/* ============================================================
   Homepage · the night room
   ------------------------------------------------------------
   The hero is a sunlit room; everything below it is one dusk room,
   and the cut between them is authored, not blended. This file owns
   that second environment and the four sections standing in it.

   The backdrop is the dusk render of the hero's own room — same white
   forms, same tree, same moon, after dark. A cool slate sky (#63697E) over a
   wet floor (#73788C → #838290) with a single warm amber column
   (#FBCD9F). Sampled across its height the render sits at L = 0.15
   → 0.27 — the exact middle of the range, where cream ink reaches
   only 3.1–4.9:1 and graphite only 3.0–4.8:1. NEITHER passes on the
   raw image. The scrim on .night__room::after is what buys cream its
   contrast (8:1+); if you lighten it, re-measure the text.

   min appears in all four sections as prerendered stills, lit in
   Blender to this same room: amber key low and left where the light
   column stands, cool sky filling from the right, his own lamp the
   warmest thing in frame. Three poses, 28 KB total — the hero
   already spends a WebGL context and 600 KB of three.js on the live
   min, and a second scene down here would buy nothing.
   ============================================================ */

/* The page's ground. landing.css sets the cave (#2A2320) for the eight
   pages that still scroll through it; this one ends in the dusk room, so
   whatever a rubber-band scroll reveals past either edge of the document
   should be that room's colour and not a brown seam. --ink stays cream. */
:root { --bg: #2F333F; }

.night {
  position: relative;
  isolation: isolate;
  /* --ink stays cream, from landing.css. The room's two poles, as sampled
     off the render: the slate sky and the one warm light in it.
     Everything else in here is a scrim or an edge, authored inline where
     it's used rather than promoted to a token nothing else would share. */
  --night-sky:   #63697E;
  --night-amber: #FBCD9F;
}

/* ---- the room itself ----------------------------------------
   One backdrop for all four sections, pinned so scrolling reads as
   moving through a single space. `fixed` rather than a per-section
   image: four copies of the same render would announce themselves
   at every section boundary. */

.night__room {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  /* clip-path gives the fixed child something to be clipped BY —
     position:fixed inside an ancestor with a filter/transform is
     contained by it, and this is the cheapest containment that
     doesn't also create a paint-heavy stacking context. */
  clip-path: inset(0);
  background: var(--night-sky);
}

/* Defocused on purpose. At full sharpness the render's moon, light ring
   and branches run straight through the headings and read as content
   competing with the copy; a few px of blur turns the same pixels into
   depth of field, which is what a room twenty metres behind you should
   look like. Scaled past the edges so the blur has pixels to sample
   instead of fading into transparency at all four sides. */
.night__room::before {
  content: '';
  position: fixed;
  inset: 0;
  height: 100lvh;   /* see canvas#scene in landing.css — same toolbar seam */
  background: url('/assets/img/min-night-room.webp') center center / cover no-repeat;
  filter: blur(5px) saturate(0.92);
  transform: scale(1.045);
}

/* The scrim. Two parts: a flat deepening so cream ink clears 4.5:1
   anywhere on the page, and a vertical gradient that keeps the floor
   (where the sections' own copy sits) darker than the sky. */
.night__room::after {
  content: '';
  position: fixed;
  inset: 0;
  height: 100lvh;
  background:
    linear-gradient(180deg,
      rgba(19, 21, 29, 0.50) 0%,
      rgba(19, 21, 29, 0.62) 46%,
      rgba(15, 17, 24, 0.74) 100%),
    radial-gradient(120% 60% at 18% 12%, rgba(251, 205, 159, 0.12), transparent 62%);
}

/* Two very slow banks, the cool counterpart of .fog in landing.css.
   They read as depth in the room's air, never as shapes. */
.night__fog {
  position: absolute;
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.42;
  will-change: transform;
}
.night__fog--1 {
  width: 68vw; height: 44vw;
  left: -16vw; top: 6%;
  background: radial-gradient(circle, rgba(251, 205, 159, 0.20), transparent 68%);
  animation: night-drift-1 92s ease-in-out infinite;
}
.night__fog--2 {
  width: 58vw; height: 40vw;
  right: -14vw; bottom: 12%;
  background: radial-gradient(circle, rgba(120, 142, 214, 0.22), transparent 68%);
  animation: night-drift-2 108s ease-in-out infinite;
}
@keyframes night-drift-1 {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  50%      { transform: translate3d(8vw, 6vh, 0) scale(1.12); }
}
@keyframes night-drift-2 {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1.08); }
  50%      { transform: translate3d(-9vw, -5vh, 0) scale(1); }
}

/* ---- shared section furniture ------------------------------- */

.night section { padding: clamp(84px, 10vh, 132px) 0; }
.night .lede { color: color-mix(in srgb, var(--ink) 82%, transparent); }
.night .section-lede { max-width: 46ch; }

/* The resin recipe from landing.css assumes it is sitting on cream. On a
   dark room the white gradients read as glossy plastic, so the panels are
   rebuilt here as thick DARK resin: a lit top edge, a cool body, and an
   internal bloom that comes from the room rather than from white.

   The body is deliberately OPAQUE-ish (~0.66). A translucent panel over
   this render is only as readable as whatever it happens to be floating
   over, and the warm light column in the middle of the room took the
   body copy down to about 2:1 — a panel has to be a ground the text can
   rely on, not a window. The blur behind it is what still reads as
   thickness; the alpha is what makes the words legible. */
.night .resin {
  /* .resin in landing.css declares itself a LIGHT-ink context — it sets
     --ink to #23211E and takes `color` from it, because on cream that is
     correct. Inheriting that here turned every color-mix(var(--ink) …)
     inside a panel into near-black on a near-black ground. A dark panel
     is a cream-ink context, so it has to say so. */
  --ink: #FAF7F2;
  --ink-soft: color-mix(in srgb, #FAF7F2 66%, transparent);
  --ink-faint: color-mix(in srgb, #FAF7F2 52%, transparent);
  color: var(--ink);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0) 30%),
    linear-gradient(158deg, rgba(52, 58, 78, 0.62), rgba(16, 18, 26, 0.74) 78%);
  border: 1px solid rgba(250, 247, 242, 0.15);
  box-shadow:
    inset 0 1.5px 0 rgba(255, 255, 255, 0.20),
    inset 0 -1px 0 rgba(255, 255, 255, 0.06),
    inset 0 0 26px 10px rgba(129, 142, 184, 0.07),
    0 18px 40px -20px rgba(8, 9, 14, 0.7);
  backdrop-filter: blur(var(--blur-resin));
  -webkit-backdrop-filter: blur(var(--blur-resin));
}

/* ══════════════════════════════════════════════════════════════
   1 · HOW IT WORKS

   A phone showing the screen for whichever step you're on, with min
   beside it. The stage is sticky and the steps scroll past it, so the
   change is driven by where the reader actually is rather than by a
   timer they may never see.

   THE SCREENS BELOW ARE PLACEHOLDERS for the real UI mockups. Each
   [data-screen] block is self-contained — the stage only ever toggles
   .is-on on it — so a real mockup drops in as a single <img> inside
   the block and the rules for that screen delete cleanly. The phone
   frame, the crossfade and the sticky behaviour are not placeholders.
   ══════════════════════════════════════════════════════════════ */

/* The grid is the SCROLL TRACK, not just a layout. Both columns are pinned
   inside it — the phone and the pebble cluster hold one eyeline together and
   neither moves — so the track needs a height of its own to travel through.
   This is the scroll budget the three steps are divided across. */
/* Heading and lede centred over the two columns — the section announces
   itself down the middle, then the work happens left and right.

   .steps__head sits INSIDE .steps__pin, so on desktop the heading is
   parked with the phone and the pebbles instead of scrolling away from
   them. A heading that drifts while the block under it is nailed down
   reads as parallax, and nothing in this section is supposed to move. */
.steps__head { text-align: center; }
.steps h2,
.steps .section-lede {
  text-align: center;
  margin-inline: auto;
}
.steps .section-lede { margin-top: var(--space-4); }

/* The track. It holds no layout of its own — its only job is to be tall
   enough to scroll through while .steps__pin stays parked in it. */
.steps__grid {
  margin-top: clamp(32px, 4.5vh, 56px);
  min-height: calc(var(--vh-fixed, 100svh) * 2.6);
}

/* Everything visible, in ONE sticky box. Both columns park and release on
   the same frame because they are the same element. */
.steps__pin {
  position: sticky;
  top: max(24px, calc(var(--vh-fixed, 100svh) * 0.5 - var(--steps-band, 590px) / 2));
  height: var(--steps-band, 590px);
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1fr);
  gap: clamp(28px, 5vw, 68px);
  align-items: start;
}
/* The pin's own vertical rhythm: heading, then the two columns. Its
   height is a token so the sticky offset above can centre it without
   measuring anything. */
/* The band is a box on mobile and nothing at all on desktop: `display:
   contents` hands its two children straight to the pin's grid, so the
   named areas below still work. */
.steps__band { display: contents; }
.steps__head { grid-area: head; }
.steps__stage { grid-area: stage; }
.steps__list { grid-area: list; }

.steps__stage {
  /* Not sticky any more — .steps__pin is. */
  display: grid;
  place-items: end center;
  height: var(--steps-band, 590px);
}

/* ---- the phone ---------------------------------------------
   Real hardware, same material language as the hero's device: a warm
   metal frame, a dark bezel, and a screen that is the only light
   source on it. Tilted a touch so it sits IN the room rather than
   floating flat on top of it. */
.pmock {
  grid-area: 1 / 1;
  align-self: start;
  position: relative;
  /* The same photographed iPhone the hero uses, inset to the same interior
     measured off the asset's alpha: 1206x2622 at (72,69) in a 1350x2760
     canvas. Height-driven with width:auto, so the hardware can never be
     stretched to a ratio it doesn't have. The drop is a filter, not a
     box-shadow, so it traces the rim rather than the element's square box. */
  height: 552px;
  width: auto;
  max-width: 100%;
  aspect-ratio: 1350 / 2760;
  transform: rotate(-2.4deg);
  filter: drop-shadow(0 34px 52px rgba(6, 7, 12, 0.7));
}
.pmock__rim {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 4;
  pointer-events: none;
}
.pmock__home {
  position: absolute;
  bottom: 3.2%; left: 50%;
  translate: -50% 0;
  width: 34%; height: 3px;
  border-radius: 4px;
  background: rgba(250, 243, 232, 0.5);
  z-index: 5;
}

/* ---- the screens ------------------------------------------- */

.pscreen {
  position: absolute;
  left: 5.333%; top: 2.5%; width: 89.333%; height: 95%;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: 14% 6% 9%;
  border-radius: 18.41% / 8.47%;
  overflow: hidden;
  background:
    radial-gradient(120% 70% at 50% -8%, rgba(201, 168, 224, 0.16), transparent 62%),
    linear-gradient(170deg, #3b3542 0%, #221f27 68%);
  color: #FAF7F2;
  font-size: 11px;
  line-height: 1.5;
  opacity: 0;
  /* Held in the layer tree but not painted or hit-tested — a screen that
     is display:none can't crossfade, and one that is only transparent
     still answers to the cursor. */
  visibility: hidden;
  transition:
    opacity var(--duration-standard) var(--ease-emergence),
    visibility 0s linear var(--duration-standard);
}
.pscreen.is-on {
  opacity: 1;
  visibility: visible;
  transition-delay: 0s;
}

.pscreen__bar {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid rgba(250, 247, 242, 0.10);
}
.pscreen__bar b { display: block; font-size: 11.5px; font-weight: var(--font-weight-bold); }
.pscreen__bar i {
  display: block;
  font-style: normal;
  font-size: 9px;
  color: rgba(250, 247, 242, 0.58);
}

/* min, in the phone: the same lit pebble, at avatar size. */
.pscreen__avatar {
  flex: 0 0 auto;
  width: 24px; height: 24px;
  border-radius: 48% 50% 44% 46%;
  background:
    radial-gradient(circle at 38% 26%, #fff6e4 0%, #e8c9a4 46%, #b58f6d 100%);
  box-shadow: 0 0 12px rgba(251, 205, 159, 0.45), inset 0 1px 1px rgba(255, 255, 255, 0.7);
}
/* 1 · the conversation — a real exported screen, so the block is just
   the image. Full-bleed: the export already includes its own background,
   and .pscreen clips it to the screen's radius. */
/* display:block, not the inherited column flex — as a flex item the
   image's height:100% still loses to flex-shrink and it stopped short of
   the bezel at both ends. Doubled class on purpose: the narrow-screen
   .pscreen padding further down would otherwise re-inset the image. */
.pscreen.pscreen--shot { display: block; padding: 0; gap: 0; }
.pscreen--shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 0;
  display: block;
}
/* The home bar is drawn light for the dark screens; over a daylight one
   it disappears. Darken it for as long as that screen is the one up. */
.pmock:has(.pscreen--shot.is-on) .pmock__home {
  background: rgba(40, 34, 30, 0.42);
}

/* 3 · the introduction */
.pinvite {
  margin-top: auto;
  padding: 13px;
  border-radius: 19px;
  background: linear-gradient(145deg, rgba(255, 237, 218, 0.13), rgba(216, 184, 235, 0.06));
  box-shadow: inset 0 1px 0 rgba(255, 250, 233, 0.18), 0 8px 22px rgba(10, 8, 16, 0.35);
}
.pinvite__who { display: flex; align-items: center; gap: var(--space-2); }
.pinvite__who b { display: block; font-size: 13px; font-weight: var(--font-weight-bold); }
.pinvite__who i {
  display: block;
  font-style: normal;
  font-size: 9px;
  color: rgba(250, 247, 242, 0.6);
}
.pinvite__face {
  flex: 0 0 auto;
  width: 30px; height: 32px;
  border-radius: 46% 50% 43% 48%;
  background: radial-gradient(circle at 34% 14%, rgba(238, 225, 214, 0.8), transparent 60%),
              linear-gradient(150deg, #a58cad, #574757);
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.55);
}
.pinvite > p {
  margin: 10px 0 0;
  font-size: 10px;
  color: rgba(250, 247, 242, 0.78);
}
/* the streets, abstracted — a couple of lines and a lit destination */
.pinvite__map {
  display: block;
  height: 56px;
  margin-top: 11px;
  border-radius: 12px;
  background:
    radial-gradient(circle at 68% 40%, rgba(251, 205, 159, 0.75) 0 2.5px, transparent 3px),
    radial-gradient(circle at 68% 40%, rgba(251, 205, 159, 0.22) 0 9px, transparent 10px),
    linear-gradient(114deg, transparent 46%, rgba(250, 247, 242, 0.14) 46.4%, rgba(250, 247, 242, 0.14) 47.6%, transparent 48%),
    linear-gradient(22deg, transparent 62%, rgba(250, 247, 242, 0.10) 62.4%, rgba(250, 247, 242, 0.10) 63.4%, transparent 64%),
    linear-gradient(160deg, #2c2833, #232028);
  box-shadow: inset 0 0 0 1px rgba(250, 247, 242, 0.07);
}
.pscreen__cta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 11px 15px;
  border-radius: 999px;
  background: linear-gradient(135deg, #ecdbf3, #bfa7d0);
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.6), 0 4px 16px rgba(201, 166, 220, 0.18);
  color: #392b40;
  font-size: 11px;
  font-weight: var(--font-weight-bold);
}
.pscreen__cta i {
  width: 11px; height: 11px;
  border-top: 1.6px solid currentColor;
  border-right: 1.6px solid currentColor;
  rotate: 45deg;
}

/* ---- the three steps ---------------------------------------------------
   One cluster, parked beside the phone and STILL. Two separate things have
   to hold for that: the list is sticky, so it doesn't ride up the screen as
   you scroll; and every row is the same fixed height, so the cluster doesn't
   re-flow when the open step changes. Scrolling changes what's inside the
   pebbles. It never changes where they are. */
.steps__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--steps-gap, 14px);
  /* Centred against the phone inside the shared sticky box. Nothing here is
     positioned or transformed: the pin above owns all of that. */
  align-content: center;
  height: 100%;
}

/* A pebble per step. `.resin` in the markup supplies the material — the same
   optical resin as the cards in "what's different" — and these rules only
   set the geometry and the open/closed states on top of it. */
.steps__item {
  border-radius: var(--radius-2xl);
  padding: var(--space-5) var(--space-6);
  /* The closed size. Both heights are FIXED values rather than content-driven
     ones, and that is what keeps the group anchored: exactly one step is ever
     open, so the cluster's total is always open + 2x closed + 2 gaps — a
     constant. The pebbles resize against each other inside a box whose outer
     edges never move. */
  height: var(--steps-closed, 88px);
  /* Inset while closed, so shrinking reads in both directions, not just
     vertically. */
  margin-inline: 16px;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-4);
  /* Centred inside the fixed box. Top-aligned left every closed pebble with
     its heading stuck to the ceiling and a hand's width of dead air beneath
     it; centred, a closed pebble reads as a complete small object. The
     pebbles themselves never move either way — only what's inside one does,
     and only the one you're on. */
  align-items: center;
  /* Everything that separates a closed pebble from the open one moves on the
     same curve and the same clock, so the cluster reads as ONE object
     re-settling rather than three boxes animating independently. */
  transition:
    height var(--duration-standard) var(--ease-emergence),
    margin var(--duration-standard) var(--ease-emergence),
    opacity var(--duration-standard) var(--ease-emergence),
    box-shadow var(--duration-standard) var(--ease-emergence);
}
/* Higher specificity than the bare `.steps__item` opacity resets further
   down the file, which exist for the old spread-out layout.

   Opacity only — no scale. A closed pebble used to sit at scale(0.975),
   which is 3px of size change on a 126px row: small, but it meant the
   pebbles' edges shifted every time the open step changed, and this cluster
   is supposed to be nailed down. Dimming says "not this one" without
   moving anything. */
.steps__grid .steps__item { opacity: 0.62; }
/* Tapping a step opens it once home-steps.js is live. No tap highlight: the
   step opening is the feedback, and the grey flash phones paint by default
   would be a second, unstyled one. */
.steps__grid.is-live .steps__item { cursor: pointer; -webkit-tap-highlight-color: transparent; }
/* The open step: taller, full width, full strength. */
.steps__grid .steps__item.is-on {
  height: var(--steps-open, 168px);
  margin-inline: 0;
  opacity: 1;
}

/* The body copy: closed is a zero-height row, open is its natural height.
   `0fr → 1fr` on a grid row is the one way to transition to an unknown
   height without measuring it in JS. The padding lives INSIDE the clipped
   child so it collapses with the text instead of leaving a gap. */
.steps__reveal {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  transition:
    grid-template-rows var(--duration-standard) var(--ease-emergence),
    opacity 260ms var(--ease-emergence);
}
.steps__reveal > p { overflow: hidden; min-height: 0; }
.steps__item.is-on .steps__reveal { grid-template-rows: 1fr; opacity: 1; }
/* Without JS nothing is ever marked current, so every step stays open —
   the section still reads as three complete steps. home-steps.js adds
   .is-live once it has taken over. */
.steps__grid:not(.is-live) .steps__reveal { grid-template-rows: 1fr; opacity: 1; }

.steps__num {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--ink) 16%, transparent);
  background: color-mix(in srgb, var(--ink) 5%, transparent);
  font-size: var(--text-base);
  font-weight: var(--font-weight-bold);
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft, #6D6861);
  transition:
    border-color var(--duration-standard) var(--ease-emergence),
    background var(--duration-standard) var(--ease-emergence),
    box-shadow var(--duration-standard) var(--ease-emergence),
    color var(--duration-standard) var(--ease-emergence);
}
/* The current step takes the room's one warm light — the same amber as
   min's lamp, so the number and the character are lit by one source. */
.steps__item.is-on .steps__copy > b { font-size: clamp(1.16rem, 1.9vw, 1.44rem); }

.steps__item.is-on .steps__num {
  border-color: rgba(251, 205, 159, 0.55);
  background: linear-gradient(180deg, rgba(251, 205, 159, 0.28), rgba(251, 189, 130, 0.11));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.3),
    0 0 20px rgba(251, 205, 159, 0.24);
  color: #FFF3E0;
}

.steps__copy > b {
  display: block;
  font-size: clamp(1.02rem, 1.5vw, 1.18rem);
  transition: font-size var(--duration-standard) var(--ease-emergence);
  font-weight: var(--font-weight-bold);
  letter-spacing: var(--tracking-tight);
  line-height: 1.15;
  margin-bottom: var(--space-3);
}
.steps__copy > p {
  margin: 0;
  max-width: 44ch;
  padding-top: var(--space-3);
  font-size: var(--text-md);
  line-height: var(--leading-normal);
  color: color-mix(in srgb, var(--ink) 84%, transparent);
}



/* ══════════════════════════════════════════════════════════════
   2 · WHAT'S DIFFERENT
   ══════════════════════════════════════════════════════════════ */

/* The scare quotes are the argument: the word "social" is the thing being
   doubted, so it is set apart rather than just quoted. */
.facts__scare {
  color: var(--night-amber);
  font-style: italic;
  opacity: 0.92;
  /* The italic's right side bearing swallows the following space, so the
     closing quote sits flush against the next word without this. */
  padding-right: 0.09em;
}

.facts__grid {
  list-style: none;
  margin: clamp(40px, 6vh, 68px) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(288px, 1fr));
  gap: var(--space-5);
  align-items: stretch;
}

.fact {
  display: flex;
  flex-direction: column;
  padding: var(--space-5) var(--space-6) var(--space-6);
  border-radius: var(--radius-2xl);
}
/* The heading is the negative claim, so it carries the card. */
.fact > b {
  display: block;
  font-size: var(--text-lg);
  font-weight: var(--font-weight-bold);
  letter-spacing: var(--tracking-tight);
  line-height: 1.2;
  margin: 0 0 var(--space-3);
}
.fact > p {
  margin: 0;
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  color: color-mix(in srgb, var(--ink) 84%, transparent);
  text-wrap: pretty;
}

/* ══════════════════════════════════════════════════════════════
   3 · QUESTIONS
   <details> does the disclosure: no JS, keyboard and screen-reader
   behaviour for free, and the answers are in the DOM for search
   engines whether or not anyone opens them.
   ══════════════════════════════════════════════════════════════ */

.faq__head .lede { margin: var(--space-4) 0 0; }

.faq__list {
  margin-top: clamp(34px, 5vh, 56px);
  display: grid;
  gap: var(--space-3);
  max-width: 820px;
}

.faq__item {
  border-radius: var(--radius-xl);
  overflow: hidden;
}

.faq__item summary {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-5) var(--space-6);
  cursor: pointer;
  font-size: var(--text-md);
  font-weight: var(--font-weight-semibold);
  letter-spacing: -0.01em;
  list-style: none;
  transition: color var(--transition-fast);
}
.faq__item summary::-webkit-details-marker { display: none; }
.faq__item summary:hover { color: #FFF3E0; }
.faq__item summary:focus-visible {
  outline: 2px solid var(--night-amber);
  outline-offset: -3px;
}

/* The marker is a plus that becomes a minus — two bars, one of which
   rotates away. Cheaper than swapping glyphs and it animates. */
.faq__item summary i {
  position: relative;
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  margin-left: auto;
  opacity: 0.66;
}
.faq__item summary i::before,
.faq__item summary i::after {
  content: '';
  position: absolute;
  inset: 50% 0 auto;
  height: 1.6px;
  border-radius: 2px;
  background: currentColor;
  translate: 0 -50%;
  transition: transform var(--duration-quick) var(--ease-emergence);
}
.faq__item summary i::after { rotate: 90deg; }
.faq__item[open] summary i { opacity: 1; color: var(--night-amber); }
.faq__item[open] summary i::after { transform: rotate(90deg); }

.faq__answer {
  padding: 0 var(--space-6) var(--space-6);
  /* the bars above the answer, so the panel reads as one object opening
     rather than two stacked ones */
  border-top: 1px solid rgba(250, 247, 242, 0.10);
  margin-top: -2px;
  padding-top: var(--space-5);
}
.faq__answer p {
  margin: 0;
  max-width: 62ch;
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  color: color-mix(in srgb, var(--ink) 86%, transparent);
}

/* ══════════════════════════════════════════════════════════════
   4 · JOIN
   ══════════════════════════════════════════════════════════════ */

.night .closer { text-align: center; }
.night .closer .shell { max-width: 720px; }
.night .closer h2 { max-width: 22ch; margin-inline: auto; }
.night .closer .lede { max-width: 48ch; margin: var(--space-6) auto 0; }
.night .closer .hero__actions {
  display: flex;
  justify-content: center;
  margin-top: clamp(28px, 4vh, 44px);
}
.night .closer .fineprint {
  margin-top: var(--space-6);
  font-size: var(--text-sm);
  color: color-mix(in srgb, var(--ink) 70%, transparent);
}
/* min leading off toward the door — the same pose as step 3, which is
   exactly what this section is asking for. */
.closer__min {
  display: block;
  width: clamp(104px, 13vw, 152px);
  height: auto;
  margin: 0 auto var(--space-6);
  filter: drop-shadow(0 18px 34px rgba(8, 9, 14, 0.55));
}

/* The dock has already stepped aside by the time the closer is on screen,
   so neither it nor the footer reserves room underneath one any more. */
.night .closer { padding-bottom: clamp(72px, 8vh, 108px); }

/* ---- footer, in the room ------------------------------------
   The room stops at the end of the last section, and the footer below it
   is painted in --bg. Left alone that boundary is a straight horizontal
   seam across a full-bleed photograph, so the room fades down into --bg
   over its final stretch and the two meet at the same colour. */
.night::after {
  content: '';
  position: absolute;
  inset: auto 0 0;
  height: 220px;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(180deg, transparent, var(--bg) 92%);
}

.footer { position: relative; z-index: 1; }
/* landing.css reserves --dock-clear (~105px) under every footer, because on
   the pages that share it a docked pill still sits at the bottom of the
   document. Here the dock has already stepped aside by the time the closer
   is on screen, so that reservation was just an empty chin. Opt-in by class
   so the eight other pages on landing.css keep theirs untouched. */
.footer--tight { padding-block: var(--space-6) var(--space-7); }

/* ══════════════════════════════════════════════════════════════
   Responsive
   ══════════════════════════════════════════════════════════════ */

@media (max-width: 900px) {
  .steps { overflow-x: clip; }
  .steps__pin { grid-template-columns: 1fr; gap: var(--space-6); }
  /* Stage above the steps. NOT sticky — .steps__pin is, and it carries the
     phone and the cluster together. When the stage kept its own sticky here
     it re-pinned itself inside the released pin, so on the way out the phone
     hung at the top of the screen while the pebbles slid away beneath it. */
  .steps__stage {
    /* Tall enough that the fade at the foot of the band starts BELOW the
       phone, with room to spare — see the note on min's alignment. */
    height: clamp(348px, 50vh, 436px);
    position: relative;
    padding-top: var(--space-3);
    place-items: start center;
    z-index: 2;
  }
  /* The band has to HIDE what scrolls under it, not tint it. A scrim alone
     doesn't: even at 0.93 the step headings still ghosted through behind
     the phone, and taking it to 1.0 would wall the room off. Blurring the
     backdrop destroys the legibility of whatever is behind instead of just
     dimming it, so the room stays and the words don't.

     On a pseudo-element, not the stage itself — `mask-image` applies to an
     element's children too, and on .steps__stage it would have faded the
     bottom of the phone along with the band. */
  .steps__stage::before {
    content: '';
    position: absolute;
    /* The BACKDROP is what needs to be full-bleed, not the stage. Sizing
       the stage itself at 100vw broke two things at once: 100vw counts the
       scrollbar, so the page gained ~35px of horizontal scroll, and the
       single-column grid track takes its width from its widest item, so
       every step's text was stretched that far past the right edge too.
       Bleeding the pseudo-element instead, with the section clipping it,
       is the same picture and costs the layout nothing. (`clip`, not
       `hidden` — clip doesn't become a scroll container, so the stage
       above it still sticks to the viewport.) */
    inset: 0 calc(50% - 50vw);
    z-index: -1;
    background: linear-gradient(180deg,
      rgba(22, 24, 33, 0.62), rgba(22, 24, 33, 0.44) 78%);
    backdrop-filter: blur(18px) brightness(0.52) saturate(0.85);
    -webkit-backdrop-filter: blur(18px) brightness(0.52) saturate(0.85);
    mask-image: linear-gradient(180deg, #000 0%, #000 74%, transparent 99%);
    -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 74%, transparent 99%);

    /* ...but only once the phone is STUCK. That is the only moment the
       band has a job: text passing behind it. Before that the stage is
       still riding up, its top edge sits out in the middle of the screen,
       and a 0.62 scrim with a brightness(0.52) backdrop starting on a
       hard line there reads as a banner of a different colour — which is
       all it was, because the only thing behind it then is the room.
       home-steps.js sets .is-parked at exactly the sticky offset. */
    opacity: 0;
    transition: opacity var(--duration-standard) var(--ease-emergence);
  }
  .steps__stage.is-parked::before { opacity: 1; }
  /* The phone scales as one object — width, height, radius and padding all
     off the same ratio — so the screens inside need no second breakpoint. */
  .pmock {
    height: clamp(292px, 76vw, 382px);
    width: auto;
  }
  .pscreen { padding: 14% 6% 9%; }
  .steps__num { width: 34px; height: 34px; font-size: var(--text-sm); }
}

@media (max-width: 620px) {
  .faq__item summary { padding: var(--space-4) var(--space-5); font-size: var(--text-base); }
  .faq__answer { padding: var(--space-4) var(--space-5) var(--space-5); }
  .fact { padding: var(--space-5) var(--space-5) var(--space-6); }
  .facts__grid { grid-template-columns: 1fr; }
}

/* 1424px of backdrop on a 390px screen is wasted bytes and wasted
   decode — the 860px cut is 12 KB and still oversamples. */
@media (max-width: 800px) {
  .night__room::before { background-image: url('/assets/img/min-night-room-mobile.webp'); }
}

/* The room stays FIXED on a phone too. Unpinning it looked like the
   cheaper option, but `position: absolute; height: 100%` measures 100% of
   .night — five thousand pixels of it — and `cover` then blows an 860x668
   render up to 6400px wide so that every section shows one flat, smeared
   8% slice of it instead of a room. The cost it was avoiding isn't there
   either: this is a fixed ELEMENT, not `background-attachment: fixed`, so
   it composites as one static layer and iOS scrolls it without repainting.

   .night__room's clip-path keeps it inside the night, which is the whole
   trick — clip-path clips fixed descendants without becoming their
   containing block, so the layer stays pinned to the viewport. */

@media (prefers-reduced-motion: reduce) {
  .night__fog { animation: none; }
  /* No scroll listener runs here, so nothing sets .is-parked — the band
     stays up rather than never appearing at all. */
  .steps__stage::before { opacity: 1; transition: none; }
  /* And no pulling the scroll somewhere the reader didn't put it. */
  html { scroll-snap-type: none; }
  .steps__item,
  .steps__reveal,
  .steps__num { transition: none; }
  /* No scroll-driven opening: all three read as one static list, with no
     runway held open for a scroll story that never plays. */
  .steps__reveal { grid-template-rows: 1fr; opacity: 1; }
  .steps__grid .steps__item { opacity: 1; transform: none; height: auto; }
  .steps__pin { position: static; height: auto; min-height: 0; }
  .steps__band { position: static; }
  .steps__list { height: auto; padding-block: 0; }
  .steps__grid { min-height: 0; }
  .pscreen { transition: none; }
  .pscreen[data-screen="1"] { opacity: 1; visibility: visible; }
  .steps__item { opacity: 1; }
}

/* Coordinated reading positions, with no negative sticky offset. */
.pscreen { transition: opacity 280ms ease, visibility 280ms; }
.faq__item summary { min-height: 64px; }
@media(max-width:900px) {
 /* Phone band + cluster have to fit ONE screen here, because the cluster is
    parked under the band rather than beside it. 52svh of phone left the
    third pebble hanging off the bottom edge. */
 /* Sized from --vh-fixed (one screen, measured once in the page head) rather
    than svh. svh moves as the browser's toolbars retract, which re-sized the
    phone mid-scroll and dropped it onto step one — most visibly when you
    scrolled past the section and came back up. */
 .steps__stage { height: 320px; }
 /* One column: the phone is pinned as a band across the TOP and the steps
    sit under it, so a scrolling cluster would slide straight behind the
    phone — the open step would be the one you can't see. Here the cluster
    parks just below the band and the grid's own height carries the scroll.
    (The desktop column is beside the phone, not under it, so it scrolls
    normally and needs none of this.) */
 /* Stacked under the phone's band rather than beside it, so it takes its
    own height and sits where the flow puts it. */
 /* An explicit gap under the phone. The frame is rotated, so its bounding
    box clears the first pebble by a hair while the visible hardware clears
    by more — too fine a margin to trust across engines. */
 .steps__list { height: auto; padding-block: 0; margin-top: var(--space-5); }
 .steps__grid { --steps-open: 178px; --steps-closed: 82px; --steps-gap: 12px; }
 .steps__item { margin-inline: 10px; }
 .steps__item { padding: var(--space-4); gap: var(--space-3); }
 .steps__copy > p { font-size: 15px; line-height: 1.5; }
 /* The scroll runway moves onto the PIN here. A sticky element can only
    travel inside its own containing block, and below 901px the thing that
    sticks is .steps__band — a child of the pin. Left on .steps__grid the
    runway was outside the band's reach and nothing stuck at all. */
 .steps__grid { margin-top: 32px; min-height: 0; }
 /* The runway itself: an explicit length of scroll under the band, for the
    band to park through. It used to be a 2.5-screen min-height on the whole
    pin, which made the runway whatever the heading and the band left over —
    ~1320px on a 390x844 phone, half a screen of swiping per step with
    nothing moving, and people took the section for frozen. 0.8 of a screen
    is ~225px per step there. */
 .steps__pin::after { content: ''; display: block; height: calc(var(--vh-fixed, 100svh) * 0.8); }
 /* Here the HEADING scrolls and the band parks. Parking the heading too
    would cost the band ~130px it doesn't have: phone + three pebbles
    already fill a phone screen. So the pin goes static and .steps__band —
    the phone and the cluster, still one box — does the sticking. */
 .steps__pin { position: static; height: auto; top: auto; }
 .steps__band { position: sticky; top: 8px; display: block; }
 .steps__head { margin-bottom: var(--space-5); text-align: center; }
 .steps__head h2 { font-size: clamp(1.7rem, 6.4vw, 2.1rem); }
 .steps .section-lede { margin-top: var(--space-2); font-size: var(--text-md); }
 .facts .section-lede { margin-bottom: 0; }
}
/* No runway where every step is already open. After the rule above, not in
   the reduced-motion block further up, or the runway wins on order. */
@media (max-width: 900px) and (prefers-reduced-motion: reduce) {
 .steps__pin::after { display: none; }
}
/* Between 600 and 700px tall the band still parks, but phone + three
   pebbles ran 16px past the fold. Everything in it gives a little. */
@media (max-width: 900px) {
 :root.is-short .steps__stage { height: 276px; }
 :root.is-short .steps__grid { --steps-open: 164px; --steps-closed: 74px; --steps-gap: 10px; }
}
@media(max-width:900px) {
 :root.is-tiny .steps__stage { height: 248px; }
 :root.is-tiny .steps__stage::before { display: none; }
 /* Nothing is pinned at this height, so nothing is scroll-driven either —
    the three pebbles just stack and stay open, each as tall as its own copy
    needs rather than all reserving room for the longest. */
 :root.is-tiny .steps__pin { position: static; height: auto; min-height: 0; }
 :root.is-tiny .steps__pin::after { display: none; }
 :root.is-tiny .steps__band { position: static; }
 :root.is-tiny .steps__list { height: auto; padding-block: 0; }
 :root.is-tiny .steps__item { height: auto; }
 :root.is-tiny .steps__grid { min-height: 0; }
 :root.is-tiny .steps__grid .steps__item { opacity: 1; transform: none; }
 :root.is-tiny .steps__reveal { grid-template-rows: 1fr; opacity: 1; }
}
@media(prefers-reduced-motion:reduce) {
 .pscreen[data-screen="1"]:not(.is-on) { opacity: 0; visibility: hidden; }
}
@media(max-width:900px) { .steps__pin { display: block; } }
@media(max-width:620px) {
 .night .closer { padding-top: 56px; }
 .closer .signup__input { min-height: 48px; font-size: 16px; }
 .faq__item summary:focus-visible { outline: 2px solid #fbcd9f; outline-offset: 3px; }
}

/* The dock steps aside only where a second CTA would be redundant: over
   the closer, which carries the same form. It used to hide over #how and
   #different too, which is most of the page — the one persistent way to
   sign up was missing for almost the whole scroll. */
body.is-near-closer .dock-cta { opacity: 0 !important; visibility: hidden; pointer-events: none; }
@media(max-width:900px) {
 /* The phone simply IS the stage: full height, width from the hardware's
    own ratio. It used to be a 552px box shrunk by `scale`, which left a
    552px hole in the layout while painting ~320px of phone — the gap is
    what dropped onto step one. Worse, the scale was chosen by
    (max-height: 740px) and the stage height by (max-height: 700px), both
    live: retracting the toolbar past either line resized the phone
    mid-scroll, which is the jump you see coming back up the section.
    Filling the stage removes the hole and both breakpoints at once. */
 /* Drawn at its DESIGN size and scaled down as one piece. Screens 2 and 3
    are real markup — radar rings, bars, notes — laid out in px against a
    268px phone; sizing the box down instead of scaling it left those
    internals at their full size and they overflowed a 121px screen.
    `scale` doesn't shrink the layout box, so the leftover 552px box is the
    hole that used to drop onto step one — the negative margin below is
    exactly that hole, 552 * (1 - scale), handed back. Scale is chosen by
    the pinned classes, never by a live height query. */
 .steps__stage .pmock { width: 268px; height: 552px; scale: .58; transform-origin: top center; margin-bottom: -222px; margin-inline: auto; }
 :root.is-short .steps__stage .pmock { scale: .50; margin-bottom: -266px; }
 :root.is-tiny  .steps__stage .pmock { scale: .45; margin-bottom: -294px; }
 .steps__stage .pscreen--shot { padding: 0; }
}
@media(max-width:900px) {
 .steps__stage::before { background: #252833; mask-image: linear-gradient(#000 0%, #000 85%, transparent); -webkit-mask-image: linear-gradient(#000 0%, #000 85%, transparent); }
}

/* ---- the two columns as one band (desktop) ------------------------------
   The phone stands in the room with no frame around it — same as it always
   did. What the band still buys is that the pebble cluster is the same
   height as the phone's stage and centred on the same line, so the section
   reads as two things of equal weight rather than a tall object beside a
   short one. */
@media (min-width: 901px) {
  .steps__pin {
    grid-template-columns: minmax(0, 0.72fr) minmax(0, 1fr);
    grid-template-rows: auto auto;
    grid-template-areas:
      "head  head"
      "stage list";
    column-gap: clamp(20px, 2.6vw, 40px);
    row-gap: clamp(16px, 3.2vh, 38px);
    /* Auto, not a fixed band: the heading row is as tall as the words in
       it, and a box that sizes to its content can never clip them. The
       offset below only has to CENTRE it, so an estimate of the heading's
       height is good enough there — being a few px off moves the whole
       block, it doesn't cut anything. */
    height: auto;
    top: max(20px, calc((var(--vh-fixed, 100svh) - var(--steps-band, 590px) - var(--steps-head-h, 190px)) / 2));
  }
  /* Both columns are the band's height, so the pebble cluster centres on
     the phone's line rather than on its own content. */
  .steps__list { height: var(--steps-band, 590px); }
  .steps__grid {
    --steps-band: 590px;
    /* One row height and one gap, so the cluster's total height is a fixed
       number and it can be centred on the phone's line without ever
       re-measuring itself. 3 rows + 2 gaps = 490px against a 590px phone. */
    --steps-open: 168px;
    --steps-closed: 88px;
    --steps-gap: 32px;
  }

}

/* A narrower desktop column wraps the longest copy onto a third line, so
   the fixed row has to be taller there or that line would be clipped. */
@media (min-width: 901px) and (max-width: 1180px) {
  .steps__grid { --steps-open: 188px; --steps-closed: 84px; --steps-gap: 26px; }
}
@media (min-width: 901px) and (max-height: 860px) {
  /* The heading now shares the viewport with the band, so the band has to
     give some of it back. The phone scales as one object — `scale` is its
     own property, so it composes with .pmock's resting rotate. */
  .steps__grid { --steps-band: 512px; --steps-head-h: 176px; }
  .steps__pin .pmock { scale: 0.92; transform-origin: bottom center; }
}
@media (min-width: 901px) and (max-height: 760px) {
  .steps__grid {
    --steps-band: 452px; --steps-head-h: 164px;
    --steps-gap: 20px; --steps-closed: 78px; --steps-open: 156px;
  }
  .steps__pin .pmock { scale: 0.8; transform-origin: bottom center; }
}
