/* Build the Relationship (PURP Loyalty): who loves what, and somewhere for them to climb. The product
   pages' chrome, type, colour, hero and finale come from point-of-sale.css; this file is Loyalty's own:
   a hero film and the climb (one screen pinned above four steps, standing on a tier ladder that the
   lock-on controller in run-the-floor.mjs moves a rung at a time). It takes the page's colour names
   from there too (the light Mist page, the stage palette on the climb) and only sets its own
   components on them. */

/* ---- Hero: the directory, sorted ---- */
/* No PURP light here: the headline opens the page and the sorted directory rises into view beneath it.
   Stock's hero rides in sideways and Know's pulls back; this one climbs, like the page. */
.rel-hero h1 { animation-delay: .15s; }
.rel-hero .pos-hero__lede { animation-delay: 1.3s; }
/* The headline, the whole film and the line under it share the first screen: the navigation and bar
   (140), the headline (95), the gaps (54), the line (63) and 16px to spare leave 100svh - 388px for
   the 16:9 film. */
.rel-hero__film { width: min(var(--content), max(440px, calc((100svh - 388px) * 16 / 9))); margin: 28px auto 0; aspect-ratio: 16 / 9; box-shadow: var(--film-shadow); animation: rel-rise 1.3s var(--ease) .35s both; }
/* Its own name: point-of-sale.css's rise lifts the headline, the line and the finale, and a second
   "rise" here would replace that one for the whole page. */
@keyframes rel-rise { from { opacity: 0; transform: translateY(44px); filter: blur(6px); } to { opacity: 1; transform: none; filter: none; } }

/* ---- The climb ---- */
/* A stage (point-of-sale.css): the hero fades into it at dusk, and it runs dark into the finale. It
   clips sideways only, so the top rung's glow never widens the page and the screen still pins. */
.climb {
  --section-top: 110px;
  --fade-in: 110px;
  padding: var(--section-top) var(--gutter) 180px;
  scroll-margin-top: calc(-1 * var(--section-top));
  overflow-x: clip;
  background: linear-gradient(to bottom, var(--dusk-2), var(--dusk-3) calc(var(--fade-in) / 2), transparent var(--fade-in)), var(--ground);
  text-align: center;
}
.climb h2 { padding-bottom: .06em; font-size: clamp(40px, 5.6vw, 76px); line-height: 1.04; font-weight: 600; letter-spacing: -.02em; background: var(--headline); -webkit-background-clip: text; background-clip: text; color: transparent; }
.climb__body { width: var(--content); margin: 44px auto 0; overflow-anchor: none; }
.climb__steps { margin: 0; padding: 0; list-style: none; }

/* The screen rides pinned under the bar, standing on its ladder, while the steps' words scroll beneath
   it. The stage is the space above the screen for the next rung (40), the screen, and the space under
   it for the rung it stands on and its plaque (66). With the 240px the lit step needs below the stage,
   that leaves 100svh - --bar-space - 346px for the 16:10 film: 752x470 in a 900px window. However wide
   the window, the screen leaves room for the rails beside it. */
.climb__stage {
  --stile-gap: 44px;
  --rung-above: 40px;
  --rung-below: 66px;
  --rung-drop: 24px;
  /* No % anywhere: --pitch feeds a translateY, where % would mean the ladder's own height. */
  --film-width: min(calc(var(--content) - 2 * var(--stile-gap) - 8px), max(400px, calc((100svh - var(--bar-space) - 346px) * 1.6)));
  --film-height: calc(var(--film-width) / 1.6);
  /* From one tier rung to the next: the rung the screen stands on is --rung-drop under it, and the next
     one shows halfway up the space above it. */
  --pitch: calc(var(--film-height) + var(--rung-drop) + var(--rung-above) / 2);
  position: sticky;
  z-index: 2;
  top: var(--bar-space);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: var(--rung-above) 0 var(--rung-below);
  overflow-anchor: none;
  /* It hides the words passing under it, and its foot fades so they come out from beneath softly. */
  background: linear-gradient(180deg, var(--ground) calc(100% - 30px), transparent);
}
/* Pinned, it also covers the space above it, where the words would show through the bar's veil. */
.climb__stage::before { content: ""; position: absolute; z-index: -1; right: 0; bottom: 100%; left: 0; height: var(--bar-space); background: var(--ground); opacity: 0; }
.climb__stage.is-pinned::before { opacity: 1; }

/* The ladder: two lavender rails just outside the screen, faint minor rungs, and one tier rung for each
   step. Its foot is the first rung's line, --rung-drop under the screen, and for each step the reader
   has climbed (--lock-at) the whole ladder slides down one rung, so the step's own rung is always the
   one under the screen and the next one shows just above it. It stands in a shaft the size of the
   stage that fades out at the top and the foot. */
.climb__shaft {
  position: absolute;
  inset: 0;
  overflow: clip;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to bottom, transparent, var(--ground) 14px, var(--ground) calc(100% - 22px), transparent);
  mask-image: linear-gradient(to bottom, transparent, var(--ground) 14px, var(--ground) calc(100% - 22px), transparent);
}
.climb__ladder {
  position: absolute;
  left: 50%;
  bottom: calc(var(--rung-below) - var(--rung-drop));
  width: calc(var(--film-width) + 2 * var(--stile-gap));
  height: calc(3 * var(--pitch));
  margin: 0;
  padding: 0;
  list-style: none;
  background: repeating-linear-gradient(to top, color-mix(in srgb, var(--pdb-purp-lavender, #d9cbff) 12%, transparent) 0 1px, transparent 1px calc(var(--pitch) / 4));
  transform: translate(-50%, calc(var(--lock-at, 0) * var(--pitch)));
}
/* The rails run from just above the top rung to below the first, so at the top step they end under the
   screen: there is nowhere left to climb. */
.climb__ladder::before, .climb__ladder::after { content: ""; position: absolute; top: -10px; bottom: -48px; width: 2px; border-radius: 1px; background: color-mix(in srgb, var(--pdb-purp-lavender, #d9cbff) 34%, transparent); }
.climb__ladder::before { left: 0; }
.climb__ladder::after { right: 0; }
/* The rung the screen stands on glows champagne, the gold of the app's tier badge; the ones below it,
   already climbed, dim. The top rung carries a champagne glow that lights up behind the screen when
   the reader reaches it. The screen hides whatever is behind it, so the glow is a halo in the
   screen's own shape, standing on the rung just as the screen does: it shows only around the screen,
   and its blur is the gap between the screen and the rails, so it has faded out by the rails and the
   shaft's sides never cut it off, however narrow the window. */
.climb__rung { position: absolute; right: 0; left: 0; bottom: calc(var(--rung) * var(--pitch) - 1px); height: 2px; border-radius: 1px; background: color-mix(in srgb, var(--pdb-purp-lavender, #d9cbff) 42%, transparent); }
.climb__rung.is-passed { background: color-mix(in srgb, var(--pdb-purp-lavender, #d9cbff) 22%, transparent); }
.climb__rung.is-active { background: var(--pdb-champagne-accent, #f3c969); box-shadow: 0 0 18px 1px color-mix(in srgb, var(--pdb-champagne-accent, #f3c969) 45%, transparent); }
.climb__rung--top::after { content: ""; position: absolute; left: 50%; bottom: calc(var(--rung-drop) + 1px); width: var(--film-width); height: var(--film-height); border-radius: 18px; transform: translateX(-50%); box-shadow: 0 0 var(--stile-gap) calc(var(--stile-gap) / 4) color-mix(in srgb, var(--pdb-champagne-accent, #f3c969) 45%, transparent); opacity: 0; }
.climb__rung--top.is-active::after { opacity: 1; }

/* The screen: one 16:10 frame for every step's film, so nothing is ever cropped or letterboxed (the POS
   films are 2880x1800, and the Admin ones are filmed in a 1440x900 window at twice the size). The
   controller lets the old film go only once the new one is covering it. */
.climb__film { position: relative; z-index: 1; width: var(--film-width); }
.climb__screen { position: relative; isolation: isolate; overflow: hidden; aspect-ratio: 16 / 10; border-radius: 18px; background: var(--surface); box-shadow: var(--film-shadow); }
.climb__feed { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; }
.climb__feed.is-shown { opacity: 1; }
.climb__film > .video-toggle { z-index: 2; right: 12px; bottom: 12px; }

/* The plaque sits on the rung the screen stands on and names that tier's rule, in the app's own names;
   the old words fade as the new ones arrive. */
.climb__plaque { position: absolute; z-index: 2; left: 50%; bottom: calc(var(--rung-below) - var(--rung-drop)); transform: translate(-50%, 50%); display: inline-grid; padding: 6px 14px; border-radius: 999px; background: var(--stage-raised); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--pdb-champagne-accent, #f3c969) 55%, transparent); color: var(--type); font-size: 13px; line-height: 1.3; font-weight: 600; white-space: nowrap; }
.climb__plaque > span { grid-area: 1 / 1; transition: opacity .4s var(--ease); }
.climb__plaque > span.is-entering, .climb__plaque > span.is-leaving { opacity: 0; }

/* The steps' words: the one at the reader's line is lit. They sit the stage's leftover space apart
   (240px), so the next line rises into view as the last one slides under the screen and the film
   changes only once its words are up. Their lines are balanced, so no step ends on a word alone. */
.climb__step { display: flex; flex-direction: column; justify-content: center; align-items: center; min-height: 240px; color: var(--type-quiet); transition: color .42s var(--ease); }
.climb__step.is-active { color: var(--type); }
.climb__step h3 { font-size: clamp(26px, 2.6vw, 36px); line-height: 1.1; font-weight: 600; letter-spacing: -.015em; }
.climb__step p { max-width: 44ch; margin-top: 10px; font-size: 19px; line-height: 1.35; color: var(--type-quiet); text-wrap: balance; transition: color .42s var(--ease); }
.climb__step.is-active p { color: var(--type-muted); }

/* Up: the next film rises into the screen while the ladder slides down under it, so the film climbs. */
@keyframes climb-up { from { opacity: 0; transform: translateY(8%); } 45% { opacity: 1; } to { opacity: 1; transform: none; } }
/* Back down: the film above drops back into the screen. */
@keyframes climb-down { from { opacity: 0; transform: translateY(-8%); } 45% { opacity: 1; } to { opacity: 1; transform: none; } }
/* The new rung flares as the screen lands on it, then settles to its glow. */
@keyframes rung-lit { from { box-shadow: 0 0 34px 5px color-mix(in srgb, var(--pdb-champagne-accent, #f3c969) 80%, transparent); } }

@media (max-width: 734px) {
  .rel-hero__film { width: var(--content); margin-top: 30px; }
  .climb { --section-top: 76px; --fade-in: 94px; padding-bottom: 150px; }
  .climb__body { margin-top: 32px; }
  /* The screen takes the width less the rails beside it: 336x210 on a 390px phone, with the rails
     still inside the content width. */
  .climb__stage { --stile-gap: 9px; --rung-above: 26px; --rung-below: 54px; --rung-drop: 20px; --film-width: calc(var(--content) - 2 * var(--stile-gap) - 4px); }
  .climb__screen { border-radius: 12px; }
  .climb__film > .video-toggle { right: 8px; bottom: 8px; }
  /* The longest plaque, "PURP Insider · $500 spent · 1.25× points", fits under that screen. */
  .climb__plaque { padding: 5px 12px; font-size: 12px; }
  .climb__step { min-height: max(40svh, 220px); }
  .climb__step h3 { font-size: 24px; }
  .climb__step p { font-size: 16px; }
}

/* With motion, the ladder slides down a rung over a second as the next film rises into the screen, and
   the new rung flares as the screen lands on it. The film's rise is an animation, not a transition: a
   transition starts from the style the film had last, so after a change of direction a film already
   loaded would start from the old direction's offset. The animation starts from its own first frame
   every time, and it ends on the film's resting style inside the controller's hand-over (the old film
   is let go 600ms after the new one shows), so the film it replaces never flashes when it is let go. */
@media (prefers-reduced-motion: no-preference) {
  .climb__ladder { transition: transform 1s cubic-bezier(.3, .7, .2, 1); will-change: transform; }
  .climb__rung { transition: background-color .5s var(--ease), box-shadow .5s var(--ease); }
  .climb__rung--top::after { transition: opacity .9s var(--ease); }
  .climb__stage.is-locking .climb__rung.is-active { animation: rung-lit .9s var(--ease); }
  .climb[data-lock-dir="in"] .climb__feed.is-active.is-shown { animation: climb-up .54s cubic-bezier(.2, .8, .2, 1) both; }
  .climb[data-lock-dir="out"] .climb__feed.is-active.is-shown { animation: climb-down .54s cubic-bezier(.2, .8, .2, 1) both; }
  .climb__stage::before { transition: opacity .2s linear; }
}

/* Reduced motion: no hero rise, ladder slide, film rise or rung flare; the ladder jumps a rung, the films
   change at once, and the plaque and the steps' words change with them. */
@media (prefers-reduced-motion: reduce) {
  .rel-hero__film { animation: none; }
  .climb__step, .climb__step p, .climb__plaque > span { transition: none; }
}
