/*
  The landing page's stylesheet.

  The palette is `src/theme/tokens.ts` verbatim, both themes, because the page
  walks from night to day exactly as the app's world does — the sidekick's
  lantern is the product's one idea and a page stating it in a different colour
  is stating something else.

  Two rules from the design guide carry over and are load-bearing: elevation is
  a 1px hairline border and never a shadow, and amber (1.78:1 on cream) is never
  text — `--amber-fg` is the readable weight. The one shadow allowed is a
  figure's contact shadow on a stage floor, for the reason the app's own sidekick
  screen allows it: a figure with no shadow stands on nothing.
*/

/* ── Tokens ─────────────────────────────────────────────────────────────── */

:root {
  --day-bg: #f6f0e2;
  --day-surface: #fffcf4;
  --day-ink: #2e2823;

  --night-bg: #231d24;
  --night-surface: #302830;
  --night-ink: #f3eadc;

  --amber: #efa84e;
  --amber-glow: #ffd79a;
  --amber-fg: #8d632d;        /* 4.67:1 on cream. Amber itself is 1.78:1. */
  --amber-fg-night: #efa84e;  /* 8.16:1 on the night ground. */
  --trail: #2f6a66;
  --ink-on-amber: #2a1e0c;

  --bg: var(--day-bg);
  --surface: var(--day-surface);
  --ink: var(--day-ink);
  --ink-rgb: 46 40 35;
  --accent-fg: var(--amber-fg);
  --link-fg: var(--trail);

  /*
    Where a sprite's boots sit as a fraction of its own height — the rest is
    transparent frame. All 48 renders land between 0.9625 and 0.9642 bar
    `curls@t30` at 0.9472, which is a few pixels at these sizes. Figures are
    placed and scaled about this line rather than about their box, which is what
    stops one standing in the floor while another hovers.
  */
  --boot: 0.9636;

  /*
    **iPhone 15 Pro Max, in points** (430 x 932 — 1290 x 2796 at 3x). The phone
    mocks are drawn at the real device's proportions rather than a generic
    rectangle, so a screenshot of this page and a screenshot of the app are the
    same shape.
  */
  --dev-w: 430;
  --dev-h: 932;

  --serif: Fraunces, ui-serif, Georgia, "Times New Roman", serif;
  --sans: "Inter Tight", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
}

/*
  The app's own subsets, through the /site/fonts mount. Relative URLs: only the
  HTML template gets its placeholders substituted.
*/
@font-face {
  font-family: Fraunces;
  src: url(fonts/Fraunces-Medium.ttf) format("truetype");
  font-weight: 500;
  font-display: swap;
}
@font-face {
  font-family: "Inter Tight";
  src: url(fonts/InterTight-Regular.ttf) format("truetype");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Inter Tight";
  src: url(fonts/InterTight-SemiBold.ttf) format("truetype");
  font-weight: 600;
  font-display: swap;
}

/* ── Base ───────────────────────────────────────────────────────────────── */

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 400 17px/1.65 var(--sans);
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; }

.wrap { max-width: 62rem; margin: 0 auto; padding: 0 1.5rem; }

h1, h2, h3 { font-family: var(--serif); font-weight: 500; }

h1 {
  font-size: clamp(2.1rem, 5vw, 3.4rem);
  line-height: 1.08;
  letter-spacing: -0.02em;
  margin: 0 0 1rem;
}

h2 {
  font-size: clamp(1.5rem, 3vw, 2.1rem);
  letter-spacing: -0.01em;
  margin: 0 0 1rem;
}

.lede {
  font-size: clamp(1.02rem, 1.5vw, 1.18rem);
  color: rgb(var(--ink-rgb) / 72%);
  margin: 0 0 1.75rem;
}

.micro {
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.tabular { font-variant-numeric: tabular-nums; }

/* ── The store button ───────────────────────────────────────────────────── */

.cta { margin: 0; }

.store {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  background: var(--amber);
  color: var(--ink-on-amber);
  font-size: 1.02rem;
  font-weight: 600;
  text-decoration: none;
  padding: 0.85rem 1.9rem;
  border-radius: 999px;
  border: 0;
  font-family: var(--sans);
}
.store:hover { filter: brightness(1.05); }

/*
  **The button is drawn whether or not there is a listing to point at.** Product
  owner's call: the link is filled from `APP_STORE_URL` the day the app is
  approved, and until then the page shows the control rather than a sentence
  about waiting. Without a URL it is a `<button>` with nothing bound to it — not
  an anchor to nowhere — so it cannot be middle-clicked into a dead tab, and it
  is `aria-disabled` so a screen reader is not told to activate something inert.
*/
.store:disabled { cursor: default; }
.store:disabled:hover { filter: none; }

/* ── Layout: two panes, side by side ────────────────────────────────────── */

/*
  The hero and the two phone acts are one shape: a column of words beside a
  column of art. It collapses to a stack below `--split-at`, where side-by-side
  would give each half too little.
*/
.split {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(1.5rem, 5vw, 4.5rem);
  width: 100%;
  max-width: 76rem;
  margin: 0 auto;
  padding: 0 clamp(1.25rem, 4vw, 3rem);
}

.split__text { flex: 1 1 22rem; max-width: 32rem; min-width: 0; }
.split__figure {
  flex: 0 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
}

@media (max-width: 60rem) {
  .split { flex-direction: column; gap: 2rem; }
  .split__text { flex: none; text-align: center; max-width: 34rem; }
  .split__figure { width: 100%; }
}

/* ── Section one: the hero, and a carousel that runs itself ─────────────── */

/*
  **Ordinary scrolling.** This section was a four-viewport scroll act whose
  travel rotated the cast; it is a normal section now and the rotation is on a
  timer, which is `app/(onboarding)/badges.tsx`'s carousel — the one place in
  the product this composition already exists. Scroll-driving it made the first
  thing a visitor did be a fight with the page.
*/
.hero {
  --bg: var(--night-bg);
  --surface: var(--night-surface);
  --ink: var(--night-ink);
  --ink-rgb: 243 234 220;
  --accent-fg: var(--amber-fg-night);
  background: var(--bg);
  color: var(--ink);
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding: clamp(2rem, 6vh, 4rem) 0;
}

.mark {
  width: 64px;
  height: 64px;
  border-radius: 15px;
  border: 1px solid rgb(var(--ink-rgb) / 12%);
  display: block;
  margin: 0 0 1.5rem;
}
@media (max-width: 60rem) { .mark { margin-inline: auto; } }

.hint {
  margin: 0.9rem 0 0;
  color: rgb(var(--ink-rgb) / 46%);
  font-size: 0.78rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* ── The floor every figure stands on ───────────────────────────────────── */

.floor {
  position: relative;
  width: 100%;
  min-height: 0;
}

.floor--cast {
  width: min(38rem, 92vw);
  height: min(62svh, 34rem);
  /*
    Clipped to its own pane. A figure two steps from the front is parked outside
    the frame at zero opacity, and it *transitions* there — so for the length of
    one turn it was a half-visible character sliding across the headline in the
    pane next door.
  */
  overflow: hidden;
}
@media (max-width: 60rem) {
  .floor--cast { width: 100%; height: min(46svh, 22rem); }
}

.fig {
  position: absolute;
  left: 50%;
  bottom: 8%;
  height: min(var(--figure, 100%), 84%);
  transform-origin: 50% calc(var(--boot) * 100%);
  will-change: transform, opacity;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  font: inherit;
  color: inherit;
}

/* The turn itself. `motion.ease` from the app's tokens, at its `world` weight. */
.fig,
.lineup__glow { transition: transform 620ms cubic-bezier(0.32, 0.72, 0, 1), opacity 620ms cubic-bezier(0.32, 0.72, 0, 1); }

.fig img {
  display: block;
  height: 100%;
  width: auto;
  max-width: none;
}

.fig__shadow {
  position: absolute;
  left: 50%;
  top: calc(var(--boot) * 100%);
  width: 46%;
  height: 4%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(closest-side, rgb(0 0 0 / 34%), transparent);
  pointer-events: none;
}

/*
  Light behind whoever is in focus. A radial gradient rather than a drop-shadow:
  the lantern is light coming out of the picture, a shadow is the picture
  blocking light.
*/
.lineup__glow {
  position: absolute;
  left: 50%;
  bottom: 8%;
  width: 46%;
  height: 74%;
  transform: translateX(-50%);
  background: radial-gradient(closest-side, rgb(255 215 154 / 20%), transparent 72%);
  pointer-events: none;
}

.lineup__label { text-align: center; margin-top: 0.75rem; }

.lineup__name {
  font-family: var(--serif);
  font-size: clamp(1.3rem, 2.4vw, 1.8rem);
  margin: 0 0 0.4rem;
  min-height: 1.3em;
}

.lineup__dots,
.caps__dots {
  display: flex;
  justify-content: center;
  gap: 0.45rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.lineup__dots li,
.caps__dots li {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: rgb(var(--ink-rgb) / 26%);
  transition: background 260ms ease;
}
.lineup__dots li[data-on],
.caps__dots li[data-on] { background: var(--amber); }

/* ── Scroll acts ────────────────────────────────────────────────────────── */

/*
  A tall section wrapping a sticky viewport-tall stage. Scrolling it moves the
  page normally — nothing here intercepts the wheel or calls scrollTo. The
  engine only *reads* where the stage has got to and draws that, which is what
  keeps a trackpad, a touchscreen and a scrollbar drag all behaving.
*/
.act {
  position: relative;
  height: calc(var(--travel, 3) * 100vh);
  height: calc(var(--travel, 3) * 100svh);
}

.stage {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: var(--bg);
  color: var(--ink);
}

.act--wardrobe .stage {
  --bg: var(--night-bg);
  --surface: var(--night-surface);
  --ink: var(--night-ink);
  --ink-rgb: 243 234 220;
  --accent-fg: var(--amber-fg-night);
}

.act__caption {
  margin: 0;
  font-size: clamp(0.95rem, 1.3vw, 1.05rem);
  color: rgb(var(--ink-rgb) / 70%);
}

/* ── Section two: the wardrobe ──────────────────────────────────────────── */

.act--wardrobe { --travel: 4.5; }

/*
  **The figure is 70% of the screen** (product owner's call). At that size there
  is no room left for a heading above it, which is what the overlay below is
  for: the words sit *on* the character rather than beside them.
*/
/*
  On the stage rather than on the floor, because the copy has to be placed
  against the figure's width and the copy is the floor's *sibling*. A custom
  property only travels down.

  **`48vw` is the third cap and it only binds beside the copy.** 70% of the
  screen is the figure's size; on an unusually tall, narrow desktop window that
  70% is most of the width too, and there is then nothing left for the words. It
  does not bind on any ordinary landscape window — at 1440x900 the height cap is
  still the one that wins.
*/
.act--wardrobe .stage {
  --figure: min(70svh, 92vw, 48vw);
  --card-room: 4.5rem;
  --boot-line: calc(var(--card-room) + var(--figure) * (1 - var(--boot)));
}

.act--wardrobe .floor { flex: 1; }

/*
  **Both dimensions are stated outright**, and that is the whole of it: an
  `<svg>` is a *replaced* element, so `auto` on either axis means its intrinsic
  size — which for a square viewBox is whatever the other axis is. Sizing it
  from insets fails twice over. With `top`/`bottom` and `height: auto` the box
  was over-constrained, `bottom` lost, and the cone ran 1,425px down a 757px
  stage, widening off the bottom instead of converging on the boots. Fixing only
  the height moved the fault to the other axis: `left`/`right` with `width: auto`
  gave it an intrinsic width equal to its new height, `right` lost, and the cone
  sat two-thirds of the way off to the left.
*/
.beam {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: calc(100% - var(--boot-line));
  pointer-events: none;
  z-index: 0;
}

/*
  The head glow and the floor pool are CSS rather than more of the SVG: both are
  placed off `--figure`, since the glow has to land on a face and the pool has
  to sit on the boots, and neither is a fixed fraction of the stage.
*/
.beam__head,
.beam__pool {
  position: absolute;
  left: 50%;
  pointer-events: none;
  z-index: 0;
}
.beam__head {
  bottom: calc(var(--boot-line) + var(--figure) * 0.8);
  width: calc(var(--figure) * 0.85);
  height: calc(var(--figure) * 0.55);
  transform: translate(-50%, 50%);
  background: radial-gradient(closest-side, rgb(255 215 154 / 30%), transparent);
}
.beam__pool {
  bottom: var(--boot-line);
  width: calc(var(--figure) * 0.95);
  height: calc(var(--figure) * 0.2);
  transform: translate(-50%, 50%);
  background: radial-gradient(closest-side, rgb(255 215 154 / 26%), transparent);
}

.look {
  position: absolute;
  left: 50%;
  bottom: var(--boot-line);
  margin: 0;
  transform-origin: 50% calc(var(--boot) * 100%);
  will-change: transform, opacity;
  z-index: 1;
}

.look img {
  display: block;
  height: var(--figure);
  width: auto;
  max-width: none;
  /* Its own bottom is the frame's, so push the boots onto the line. */
  margin-bottom: calc(var(--figure) * (var(--boot) - 1));
}

/*
  A figure's thrown shadow — the only part of the lighting belonging to the
  figure rather than the lamp. It leans away from the beam by how far its figure
  stands off the axis, stretches as the angle flattens, and fades as the figure
  leaves the light. Its own element so the throw does not inherit the figure's
  transform.
*/
.look__cast {
  position: absolute;
  left: 50%;
  bottom: var(--boot-line);
  width: calc(var(--figure) * 0.42);
  height: calc(var(--figure) * 0.075);
  transform: translate(-50%, 50%);
  border-radius: 50%;
  background: radial-gradient(closest-side, rgb(0 0 0 / 42%), transparent);
  pointer-events: none;
  will-change: transform, opacity;
  z-index: 0;
}

/*
  **The words sit beside the character, not on a panel over them** (product
  owner's call).

  They began centred in a half-transparent card, which was the only way to keep
  copy legible while it lay across a figure filling 70% of the stage. Moving the
  block off the figure removes that problem rather than treating it: at this size
  the character is centred and roughly a quarter of the width, so the right-hand
  third is empty ground — dark, unlit by the beam, and the highest-contrast place
  on the stage for cream text. No card, no blur, and the type steps down a size
  because it is no longer competing with the figure for the middle.
*/
.overlay {
  position: absolute;
  right: clamp(1.5rem, 4vw, 5rem);
  top: 50%;
  transform: translateY(-50%);
  /*
    Never wider than the gap between the figure's edge and the margin. A render
    is roughly 0.72 as wide as it is tall, so half of it is 0.36 of `--figure`;
    0.42 leaves a little air. Without this the column kept its 22rem on a 1100px
    window and grazed the dress.
  */
  width: min(22rem, calc(50% - var(--figure) * 0.42 - 2rem));
  z-index: 3;
  text-align: left;
}
.overlay h2 {
  font-size: clamp(1.15rem, 1.9vw, 1.55rem);
  margin-bottom: 0.5rem;
}
.overlay .act__caption { font-size: clamp(0.85rem, 1vw, 0.95rem); }

/*
  Stacked, there is no empty third to move into — the figure is most of the
  width — so the copy goes back under the boots, where the ground is dark and
  nothing is drawn.
*/
@media (max-width: 60rem) {
  .overlay {
    right: auto;
    left: 50%;
    top: auto;
    bottom: clamp(1rem, 4vh, 2.5rem);
    transform: translateX(-50%);
    width: min(30rem, 88vw);
    text-align: center;
  }

  /*
    The copy now occupies the bottom of the stage, so the floor is raised to
    stand clear of it — the boot line is measured from `--card-room`, so lifting
    that lifts the figure, its shadow and the pool together.

    **And the width cap comes off.** It exists only to keep the figure out of a
    column of text standing *beside* it; below the figure there is nothing to
    keep it out of, and at 430px wide `48vw` is 206px — which turned the subject
    of the section into a thumbnail.
  */
  .act--wardrobe .stage {
    --figure: min(70svh, 92vw);
    --card-room: 13rem;
  }
}

/*
  **One card, under the beam** — not one per look. Only the look standing in the
  light is ever legible, so eight cards were eight chances for the wrong one to
  be readable and eight boxes to hold on a line the widest outfit and the
  narrowest do not share. The engine writes the focused rung into this one.
*/
.look__card {
  position: absolute;
  left: 50%;
  /* Above, since the copy has taken the floor. It is a label, so it costs the
     figure nothing to have it overhead. */
  top: clamp(0.75rem, 3vh, 2rem);
  transform: translateX(-50%);
  width: min(20rem, 86vw);
  text-align: center;
  z-index: 2;
  pointer-events: none;
}

.look__days {
  font-family: var(--serif);
  font-size: clamp(2rem, 5vw, 2.8rem);
  line-height: 1;
  color: var(--amber);
}
.look__unit {
  display: block;
  margin-top: 0.2rem;
  color: rgb(var(--ink-rgb) / 52%);
}
.look__name {
  font-family: var(--serif);
  font-size: 1.15rem;
  margin: 0.5rem 0 0.15rem;
}
.look__note {
  margin: 0;
  font-size: 0.86rem;
  line-height: 1.4;
  color: rgb(var(--ink-rgb) / 62%);
}

/*
  **The wordmark's arrow, sunk into the corner of the two phone sections.**

  It is `assets/images/splash-arrow.png` through the mount — byte-identical to
  the `attached_assets` copy that was asked for, so there is no second file. It
  is warm amber rather than white, which is what lets one opacity work over both
  of this act's grounds: the ring section interpolates night to day underneath
  it, and a white mark would have vanished halfway through.

  Behind everything: `z-index: 0` here against the content's 1, and
  `aria-hidden` because it says nothing a reader needs.
*/
.watermark {
  position: absolute;
  /*
    Bled to the left only, and sitting on the bottom edge.

    A box hanging below its section still counts toward that section's
    `scrollHeight` — `overflow: hidden` clips what is painted, not what is
    scrollable, and a transform is no different — so a downward bleed made both
    phone sections report themselves as overflowing and masked any *real*
    overflow behind a decoration. Leftward costs nothing: scrollable overflow in
    the inline-start direction is not counted. The render's own transparent
    margin keeps the arrow off the edge without an offset.
  */
  left: -8%;
  bottom: 0;
  width: min(40rem, 54vw);
  opacity: 0.09;
  pointer-events: none;
  user-select: none;
  z-index: 0;
}

.stage > .split { position: relative; z-index: 1; }

/* ── The phone ──────────────────────────────────────────────────────────── */

/*
  **An iPhone 16 Pro Max, at its own proportions** (440 × 956pt). Height first,
  because the constraint is the viewport's; the width follows from the device
  aspect so the mock can never be a rectangle no phone has.

  Everything inside is sized in `em` off `font-size` here, so one number scales
  the whole screen with the frame — a screen authored at fixed pixels looks
  correct at one viewport height and wrong at every other.
*/
.phone {
  --phone-h: min(calc(var(--dev-h) * 1px), 82svh);
  --phone-w: calc(var(--phone-h) * var(--dev-w) / var(--dev-h));
  position: relative;
  /*
    **Portrait is structural, not arithmetic.** The width comes from
    `aspect-ratio` against a stated height rather than from a `calc()` of its
    own, so there is no expression that can fail and leave the frame to take its
    width from its contents — which is the one way a phone mock ends up wider
    than it is tall. `--phone-w` survives for the type scale and the corner
    radius, where being a little off is a blemish rather than a rotation.
  */
  height: var(--phone-h);
  aspect-ratio: var(--dev-w) / var(--dev-h);
  max-width: 100%;
  flex: none;
  border-radius: calc(var(--phone-w) * 0.125);
  /*
    **An amber edge and a glow around it.** Elevation in the *app* is a 1px
    hairline and never a shadow (design guide §2), and that rule is untouched —
    this is not elevation. It is the lantern's own light falling on the object
    the section is about, on a marketing surface where the figure beside it is
    lit the same way. The ring's amber is the one colour the product spends, so
    it is the only one this could be.
  */
  border: 1px solid rgb(239 168 78 / 38%);
  box-shadow:
    0 0 0 1px rgb(239 168 78 / 10%),
    0 0 48px rgb(239 168 78 / 20%),
    0 0 130px rgb(239 168 78 / 10%);
  background: var(--surface);
  overflow: hidden;
  font-size: calc(var(--phone-w) * 0.038);
  padding: 1.6em;
  display: flex;
  flex-direction: column;
  /* Home centres its whole stack — ring, saying, TODAY, the rows — so the mock
     does too, rather than hanging it from the top with slack underneath. */
  justify-content: center;
}

/*
  Stacked, the phone shares the stage's height with the words above it, so it is
  sized off what is *left* rather than off a fixed share of the viewport. A fixed
  fraction cannot satisfy both a tall phone and a short laptop: 62svh fits on a
  932pt screen and overflows a 617pt one, which is what pushed the reel's content
  past its own `overflow: hidden`.
*/
@media (max-width: 60rem) {
  .phone {
    --phone-h: min(calc(var(--dev-h) * 1px), max(12rem, calc(100svh - 17rem)));
  }
}

/*
  Frosted, because the world behind it is what is moving: the act interpolates
  night to day, and a solid card would hide the half of the effect that is the
  point.
*/
.phone--glass {
  background: rgb(var(--ink-rgb) / 6%);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  backdrop-filter: blur(18px) saturate(140%);
}

/* ── Section three: the ring ────────────────────────────────────────────── */

.act--ring { --travel: 3.4; }

.ring {
  position: relative;
  width: 100%;
  aspect-ratio: 1;
  flex: none;
}
.ring svg { display: block; width: 100%; height: 100%; }
.ring__sidekick {
  position: absolute;
  left: 50%;
  top: 50%;
  /* 220 of the ring's 330pt box — the app's own RING_SIDEKICK. */
  height: 66.7%;
  width: auto;
  max-width: none;
  transform: translate(-50%, -50%);
}

.readout { text-align: center; margin: 0.2em 0 0; }
.readout__n {
  display: block;
  font-family: var(--serif);
  font-size: 3.3em;
  line-height: 1;
}
.readout__u { display: block; margin-top: 0.4em; color: rgb(var(--ink-rgb) / 66%); font-size: 0.66em; }

.saying {
  font-family: var(--serif);
  font-size: 1.2em;
  text-align: center;
  margin: 0.7em 0 0;
  min-height: 2.6em;
  color: rgb(var(--ink-rgb) / 90%);
}

.overline {
  display: block;
  text-align: center;
  margin: 0.3em 0 0.5em;
  color: rgb(var(--ink-rgb) / 56%);
  font-size: 0.66em;
}

/* ── A quest row, as the app draws it ───────────────────────────────────── */

.qrow {
  display: flex;
  align-items: center;
  gap: 0.7em;
  padding: 0.65em;
  border-radius: 0.95em;
  border: 1px solid rgb(var(--ink-rgb) / 8%);
  background: rgb(var(--ink-rgb) / 4%);
  margin-bottom: 0.5em;
}
.qrow:last-child { margin-bottom: 0; }

/* The well holds the count of cycles finished today, not a glyph. */
.qrow__well {
  flex: none;
  align-self: center;
  width: 2.2em;
  height: 2.2em;
  border-radius: 0.65em;
  background: rgb(239 168 78 / 14%);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--serif);
  font-size: 1em;
  color: var(--accent-fg);
}
.qrow__body { flex: 1; min-width: 0; }
.qrow__title { display: block; font-size: 0.92em; font-weight: 500; }
.qrow__goal {
  display: block;
  font-size: 0.78em;
  color: rgb(var(--ink-rgb) / 62%);
  margin-top: 0.1em;
}
.qrow__track {
  display: block;
  height: 0.25em;
  border-radius: 0.15em;
  background: rgb(var(--ink-rgb) / 14%);
  margin-top: 0.4em;
  overflow: hidden;
}
.qrow__fill { display: block; height: 100%; background: var(--amber); border-radius: 0.15em; width: 0; }
.qrow__pay {
  flex: none;
  align-self: center;
  border-radius: 999px;
  background: rgb(239 168 78 / 14%);
  color: var(--accent-fg);
  font-family: var(--serif);
  font-size: 0.95em;
  padding: 0.15em 0.6em;
}

/* ── Section four: the other screens ────────────────────────────────────── */

/*
  **Pinned, not a scroll strip** (product owner's call). The four screens were a
  horizontally scrolling track, which asked the reader to scroll sideways inside
  a page they were already scrolling down. The phone holds still and the screen
  inside it changes, so the reader's one gesture drives everything.
*/
.act--reel { --travel: 4.4; }

/*
  **The phone on the left, the words on the right** (product owner's call) —
  the mirror of the ring section above it, so the two phone acts alternate
  rather than repeat. `row-reverse` rather than a DOM swap: the heading and its
  captions stay first in reading order, which is the order a screen reader and a
  crawler want them in.
*/
.act--reel .split { flex-direction: row-reverse; }

/* Stacked, reversing would put the phone above its own heading. */
@media (max-width: 60rem) {
  .act--reel .split { flex-direction: column; }
}

.caps {
  display: grid;
  min-height: 8.5rem;
}
.cap {
  grid-area: 1 / 1;
  margin: 0;
  will-change: opacity;
}
.cap b { display: block; font-weight: 600; font-size: 1.05rem; margin-bottom: 0.35rem; }
.cap span { font-size: 0.98rem; color: rgb(var(--ink-rgb) / 70%); }

.caps__dots { justify-content: flex-start; margin-top: 1.25rem; }
@media (max-width: 60rem) { .caps__dots { justify-content: center; } }

/* Each screen fills the phone; the engine crossfades between them. */
.screen {
  position: absolute;
  inset: 0;
  padding: 1.6em;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  will-change: opacity;
}
.screen--night {
  --ink-rgb: 243 234 220;
  --accent-fg: var(--amber-fg-night);
  background: var(--night-surface);
  color: var(--night-ink);
}

.screen__title {
  font-family: var(--serif);
  font-size: 1.7em;
  margin: 0 0 0.15em;
}
.screen__note {
  font-size: 0.8em;
  color: rgb(var(--ink-rgb) / 62%);
  margin: 0 0 0.8em;
}

/* A screen's own pinned footer, the way the app pins its one Primary. */
.screen__foot { margin-top: auto; padding-top: 0.8em; }
.screen__primary {
  display: block;
  background: var(--amber);
  color: var(--ink-on-amber);
  border-radius: 999px;
  padding: 0.8em;
  text-align: center;
  font-size: 0.95em;
  font-weight: 600;
}

.screen__sub {
  font-size: 0.85em;
  color: rgb(var(--ink-rgb) / 62%);
  margin: 0 0 1em;
}

/* The relief valve on the quest board. */
.screen > .overline { text-align: left; margin: 0.2em 0 0.4em; }

.relief {
  border-radius: 1.15em;
  border: 1px solid rgb(239 168 78 / 30%);
  background: rgb(239 168 78 / 11%);
  padding: 0.9em;
  text-align: center;
  margin-bottom: 0.9em;
}
.relief p { margin: 0 0 0.7em; font-size: 0.85em; color: rgb(var(--ink-rgb) / 78%); }
.relief b {
  display: block;
  width: 76%;
  margin: 0 auto;
  background: var(--amber);
  color: var(--ink-on-amber);
  border-radius: 999px;
  padding: 0.55em;
  font-size: 0.9em;
  font-weight: 600;
}

/* The shield, as iOS draws it: an image box, two lines, two buttons. */
.shield { display: flex; flex-direction: column; height: 100%; text-align: center; justify-content: center; }
.shield__art {
  flex: none;
  /* An em of the phone's own scale, so the figure grows with the frame. Stated
     outright rather than a percentage: nested in two flex columns a percentage
     never resolved, and the image fell back to its intrinsic height and was
     clipped at the waist. */
  height: 17em;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 0.6em;
}
.shield__art img { height: 100%; width: auto; max-width: 100%; }
.shield__t { font-family: var(--serif); font-size: 1.2em; margin: 0 0 0.3em; line-height: 1.25; }
.shield__s { font-size: 0.85em; color: rgb(var(--ink-rgb) / 62%); margin: 0 0 1em; }
.shield__b {
  border-radius: 999px;
  padding: 0.65em;
  font-size: 0.92em;
  font-weight: 600;
  margin-bottom: 0.5em;
}
.shield__b--primary { background: var(--amber); color: var(--ink-on-amber); }
.shield__b--secondary { border: 1px solid rgb(var(--ink-rgb) / 22%); }

/* A squad member row. */
.member {
  display: flex;
  align-items: center;
  gap: 0.7em;
  padding: 0.6em 0;
  border-bottom: 1px solid rgb(var(--ink-rgb) / 8%);
}
.member img { width: 2.4em; height: 2.4em; object-fit: contain; }
.member b { display: block; font-size: 0.95em; font-weight: 500; }
.member span { font-size: 0.82em; color: rgb(var(--ink-rgb) / 62%); }

/* The Me tab's streak card and rows. */
.streakcard {
  border-radius: 1.05em;
  border: 1px solid rgb(var(--ink-rgb) / 10%);
  background: rgb(var(--ink-rgb) / 4%);
  padding: 0.9em;
  text-align: center;
  margin-bottom: 0.8em;
}
.streakcard b { font-family: var(--serif); font-size: 2.6em; line-height: 1; display: block; }
.streakcard span { color: rgb(var(--ink-rgb) / 60%); font-size: 0.66em; }

.rows { list-style: none; margin: 0; padding: 0; }
.rows li {
  font-size: 0.95em;
  padding: 0.6em 0;
  border-bottom: 1px solid rgb(var(--ink-rgb) / 8%);
}
.rows li:last-child { border-bottom: 0; }

/* ── The conventional page ──────────────────────────────────────────────── */

section.plain { padding: clamp(3rem, 8vh, 5rem) 0; }
section.plain h2 { text-align: center; margin-bottom: 2rem; }

.steps {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}
.step {
  background: var(--surface);
  border: 1px solid rgb(var(--ink-rgb) / 8%);
  border-radius: 1.25rem;
  padding: 1.6rem;
}
.step h3 {
  margin: 0 0 0.5rem;
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--amber-fg);
}
.step p { margin: 0; font-size: 0.96rem; color: rgb(var(--ink-rgb) / 75%); }

/* Every figure in this table comes from lib/economy. */
.rates {
  width: 100%;
  border-collapse: collapse;
  margin-top: 1rem;
  font-size: 0.95rem;
}
.rates th, .rates td {
  text-align: left;
  padding: 0.7rem 0.5rem;
  border-bottom: 1px solid rgb(var(--ink-rgb) / 10%);
}
.rates th { font-weight: 600; }
.rates td:last-child, .rates th:last-child { text-align: right; color: var(--amber-fg); }
.rates caption {
  caption-side: bottom;
  padding-top: 0.9rem;
  font-size: 0.86rem;
  color: rgb(var(--ink-rgb) / 62%);
  text-align: left;
}

.compare-scroll { overflow-x: auto; }
.compare {
  width: 100%;
  min-width: 34rem;
  border-collapse: collapse;
  font-size: 0.93rem;
}
.compare th, .compare td {
  padding: 0.8rem 0.7rem;
  border-bottom: 1px solid rgb(var(--ink-rgb) / 10%);
  text-align: left;
  vertical-align: top;
}
.compare thead th { font-weight: 600; }
.compare tbody th { font-weight: 400; color: rgb(var(--ink-rgb) / 62%); }
.compare .ours { background: rgb(239 168 78 / 9%); }

.promise {
  background: var(--surface);
  border: 1px solid rgb(var(--ink-rgb) / 8%);
  border-radius: 1.5rem;
  padding: clamp(1.75rem, 5vw, 2.75rem);
}
.promise h2 { text-align: left; margin-bottom: 1rem; }
.promise ul { margin: 0; padding-left: 1.15rem; }
.promise li { margin-bottom: 0.7rem; color: rgb(var(--ink-rgb) / 78%); }
.promise li:last-child { margin-bottom: 0; }
.promise strong { font-weight: 600; color: var(--ink); }

.faq { max-width: 44rem; margin: 0 auto; }
.faq details {
  border-bottom: 1px solid rgb(var(--ink-rgb) / 10%);
  padding: 1rem 0;
}
.faq summary {
  cursor: pointer;
  font-weight: 600;
  font-size: 1rem;
  list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: " +"; color: var(--amber-fg); }
.faq details[open] summary::after { content: " –"; }
.faq p { margin: 0.7rem 0 0; color: rgb(var(--ink-rgb) / 76%); font-size: 0.96rem; }

section.plain .cta { text-align: center; }

footer {
  border-top: 1px solid rgb(var(--ink-rgb) / 8%);
  padding: 2.5rem 0 3.5rem;
  text-align: center;
  font-size: 0.92rem;
  color: rgb(var(--ink-rgb) / 55%);
}
footer nav {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 1.5rem;
  margin-bottom: 1.25rem;
}
footer a { color: var(--link-fg); text-decoration: none; font-weight: 600; }
footer a:hover { text-decoration: underline; }

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

/* ── Falling back ───────────────────────────────────────────────────────── */

/*
  With scripting off, or with reduced motion asked for, every act collapses to
  ordinary stacked content and nothing moves on a timer. This is not a courtesy:
  the acts hold the page's whole argument, and an argument that exists only
  inside an animation is one a crawler, a reader-mode and anybody who gets
  motion sick cannot have.
*/
.no-js .act,
.no-js .stage,
.reduced .act,
.reduced .stage {
  height: auto;
  position: static;
  overflow: visible;
}

.no-js .act,
.reduced .act { padding: clamp(2rem, 6vh, 4rem) 0; }

.no-js .floor,
.reduced .floor {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: center;
  gap: clamp(0.5rem, 3vw, 2rem);
  padding: 2rem 1.5rem;
  height: auto;
}

.no-js .fig,
.reduced .fig {
  position: static;
  transform: none !important;
  opacity: 1 !important;
  height: min(30vh, 190px);
  transition: none;
}

/*
  A look is a column here rather than a fixed box: its image carries the boot
  offset as a negative margin and its own `--figure` height, both of which are
  about standing on a shared line. Off that line they only make eight figures
  overlap, so both are dropped and the rung's own label goes underneath.
*/
.no-js .look,
.reduced .look {
  position: static;
  transform: none !important;
  opacity: 1 !important;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.no-js .look img,
.reduced .look img {
  height: min(30vh, 190px);
  margin-bottom: 0;
}

/* Decorative, and its stage is no longer a positioning context here. */
.no-js .watermark,
.reduced .watermark { display: none; }

.no-js .look__cast,
.no-js .lineup__glow,
.no-js .beam,
.no-js .beam__head,
.no-js .beam__pool,
.no-js .look__card,
.no-js .hint,
.no-js .lineup__name,
.no-js .lineup__dots,
.no-js .caps__dots,
.reduced .look__cast,
.reduced .lineup__glow,
.reduced .beam,
.reduced .beam__head,
.reduced .beam__pool,
.reduced .look__card,
.reduced .hint,
.reduced .lineup__name,
.reduced .lineup__dots,
.reduced .caps__dots { display: none; }

/* The overlay has nothing to sit over once the act is a stack. */
.no-js .overlay,
.reduced .overlay {
  position: static;
  transform: none;
  background: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border: 0;
  margin: 0 auto;
}

/* Each figure and each look names itself when the engine cannot. */
.no-js .fig::after,
.reduced .fig::after { content: attr(data-name); }
.no-js .look::after,
.reduced .look::after { content: attr(data-days); }
.no-js .fig::after,
.no-js .look::after,
.reduced .fig::after,
.reduced .look::after {
  display: block;
  text-align: center;
  margin-top: 0.4rem;
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgb(var(--ink-rgb) / 55%);
}

/*
  The ring at a real, stated fill: 0.667 of 2πr on the 180-minute scale the arc
  is drawn against, which is the 120-minute default dial — what a fresh day
  actually looks like.
*/
.no-js .ring__arc,
.reduced .ring__arc { stroke-dashoffset: 320 !important; }

/*
  **Four pinned screens become four phones.** Crossfading inside one frame needs
  an engine; without one the frame showed the first screen and hid the other
  three while all four captions still described them. So in the fallback the
  frame steps aside — the wrapper stops being a phone and each screen becomes
  one, which is the same four pictures laid out instead of stacked.
*/
.no-js .act--reel .split,
.reduced .act--reel .split { flex-direction: column; }

.no-js .act--reel .phone,
.reduced .act--reel .phone {
  --phone-w: min(22rem, 86vw);
  width: 100%;
  height: auto;
  border: 0;
  padding: 0;
  background: none;
  overflow: visible;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.5rem;
  /* The screens size themselves in em off this, as they do off the real frame. */
  font-size: calc(var(--phone-w) * 0.038);
}

.no-js .act--reel .screen,
.reduced .act--reel .screen {
  position: static;
  opacity: 1 !important;
  visibility: visible !important;
  width: var(--phone-w);
  height: auto;
  aspect-ratio: var(--dev-w) / var(--dev-h);
  border: 1px solid rgb(239 168 78 / 38%);
  box-shadow: 0 0 48px rgb(239 168 78 / 18%);
  border-radius: calc(var(--phone-w) * 0.125);
}
.no-js .caps,
.reduced .caps { display: block; min-height: 0; }
.no-js .cap,
.reduced .cap { margin-bottom: 1.25rem; }

html.no-js, html.reduced { scroll-behavior: auto; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .fig, .lineup__glow { transition: none; }
}
