/* =========================================================================
   ParkShield — the UK journey
   -------------------------------------------------------------------------
   One fixed WebGL stage sits behind the whole document. The `.act` sections
   scroll over it and act purely as scrub track plus copy: journey.js turns
   scroll position into camera position, so nothing here animates the story
   itself. Layout only.
   ========================================================================= */

:root {
  --ink: #10151f;
  --navy: #1b3156;
  --navy-deep: #0b1425;
  --green: #00d77a;
  --green-dark: #03945c;
  --amber: #f5b84b;
  --paper: #f7f9f5;
  --white: #ffffff;
  --uk-blue: #012169;
  --uk-red: #c8102e;

  --act-pad: clamp(20px, 5vw, 88px);
  --rail: 3px;

  /*
    The fixed header's own height (18px padding + 44px button + 18px), and the
    line everything else must stay below. Act copy, the sweep panel and the
    header scrim are all sized off these two, so the clearance cannot drift
    apart from the thing it is clearing.
  */
  --header-h: 80px;
  --header-clear: 116px;
}

* {
  box-sizing: border-box;
}

html {
  /* Scroll drives the 3D camera, so smooth-scroll would fight the scrub. */
  scroll-behavior: auto;
}

body.journey {
  margin: 0;
  color: var(--white);
  background: var(--navy-deep);
  font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  line-height: 1.5;
  overflow-x: hidden;
}

a {
  color: inherit;
  text-decoration: none;
}

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

/* ---------------------------------------------------------------- stage */

/* The film is the stage; the canvas above it carries only the 3D type. */
.stage-film {
  position: fixed;
  inset: 0;
  z-index: 0;
  width: 100vw;
  height: 100vh;
  height: 100dvh;
  object-fit: cover;
  pointer-events: none;
  background: var(--navy-deep);
}

#journey-stage {
  position: fixed;
  inset: 0;
  z-index: 2;
  width: 100vw;
  height: 100vh;
  height: 100dvh;
  pointer-events: none;
}

/* Tints the stage between acts — navy at the ends, paper through the middle,
   which is how the reference moves from its dark hero into a white stat wall. */
.stage-veil {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: var(--veil-color, transparent);
  opacity: var(--veil-opacity, 0);
  transition: background 120ms linear;
}

.scroll-rail {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 30;
  width: 100%;
  height: var(--rail);
  background: rgba(255, 255, 255, 0.12);
}

.scroll-rail span {
  display: block;
  width: var(--progress, 0%);
  height: 100%;
  background: linear-gradient(90deg, var(--green), var(--amber));
}

/* --------------------------------------------------------------- header */

/*
  Content passes behind this header, and it used to pass behind nothing: the
  header was fully transparent, so the green "Get early access" pill landed on
  top of the region card and the figures panel and read as a collision.

  The acts that carry the longest camera moves are 130vh while their sticky
  inner is 100vh, so the inner necessarily un-sticks and rides up for the last
  ~100vh of the act — at full opacity, because journey.js still counts the act
  as active. Shortening the act would shorten the camera scrub, so instead the
  header carries a scrim that content fades into before it can reach the pill.

  ::before rather than a new element, and z-index -1 so it paints above the
  acts (the header's own stacking context sits at 25) but behind the brand and
  the button. --stage-scrim is set per act by journey.js from TONES, so the
  scrim follows the act's tone instead of fighting the light ones.
*/
.journey-header::before {
  content: "";
  position: absolute;
  z-index: -1;
  top: 0;
  right: 0;
  left: 0;
  /*
    Opaque through the header band so nothing can share space with the button,
    then gone by --header-clear, which is where resting copy starts — so
    content at rest is never dimmed by it.
  */
  height: var(--header-clear);
  background: linear-gradient(
    to bottom,
    var(--stage-scrim, rgba(11, 20, 37, 0.92)) 0,
    var(--stage-scrim, rgba(11, 20, 37, 0.92)) var(--header-h),
    transparent var(--header-clear)
  );
  pointer-events: none;
  transition: background 200ms linear;
}

.journey-header {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 25;
  display: grid;
  width: 100%;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 20px;
  padding: 18px var(--act-pad);
  color: var(--stage-ink, var(--white));
  transition: color 200ms linear;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-weight: 800;
}

.brand img {
  width: 40px;
  height: 40px;
  border-radius: 10px;
}

.header-route {
  justify-self: center;
  margin: 0;
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.72;
}

.header-action {
  display: inline-flex;
  min-height: 44px;
  align-items: center;
  padding: 10px 16px;
  border-radius: 8px;
  color: var(--navy-deep);
  background: var(--green);
  font-weight: 800;
  transition: transform 120ms ease, background 120ms ease;
}

.header-action:active {
  transform: scale(0.97);
  transition-duration: 80ms;
}

/* ----------------------------------------------------------------- acts */

.acts {
  position: relative;
  z-index: 10;
}

.act {
  position: relative;
  display: block;
  min-height: 100vh;
  min-height: 100dvh;
  padding-inline: var(--act-pad);
  pointer-events: none;
}

/* Extra scrub room: these acts carry the longest camera moves. */
.act-depart,
.act-regions {
  min-height: 130vh;
}

/*
  Sticky, so the copy stays vertically centred for the whole act instead of
  riding up the page and colliding with the fixed header on the way out. The
  act's own height is what provides the scrub distance.
*/
.act-inner {
  position: sticky;
  top: 0;
  display: flex;
  min-height: 100vh;
  min-height: 100dvh;
  max-width: 640px;
  flex-direction: column;
  /*
    `safe` matters. On a short viewport the sweep heading is taller than the
    padded box, and plain `center` overflows in BOTH directions — so the top of
    the copy was pushed up to y=39, under the fixed header, at every width.
    `safe center` centres while it fits and falls back to start alignment when
    it does not, so the copy can never climb above its own top padding.
  */
  justify-content: safe center;
  padding-block: var(--header-clear) 96px;
  /* Copy fades and lifts as its act takes over; journey.js sets .is-active. */
  opacity: 0;
  transform: translate3d(0, 26px, 0);
  transition: opacity 520ms ease, transform 620ms cubic-bezier(0.22, 0.61, 0.36, 1);
  pointer-events: auto;
}

.act.is-active .act-inner {
  opacity: 1;
  transform: none;
}

.act-inner.narrow {
  max-width: 820px;
}

.act-inner.split {
  display: grid;
  max-width: 1240px;
  grid-template-columns: minmax(0, 1fr) minmax(280px, 0.85fr);
  gap: clamp(32px, 6vw, 88px);
  align-content: center;
  align-items: start;
}

/* Right-hand acts sit opposite the 3D subject so they never cover it. */
.act-stats .act-inner {
  margin-left: auto;
}

/* ------------------------------------------------------------ typography */

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 16px;
  color: var(--green);
  font-size: 0.78rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.eyebrow.dark {
  color: var(--green-dark);
}

.hero-eyebrow {
  width: fit-content;
  max-width: 100%;
  padding: 10px 14px;
  border: 1px solid rgba(0, 215, 122, 0.48);
  border-radius: 999px;
  color: var(--white);
  background: rgba(7, 17, 32, 0.82);
  box-shadow: 0 12px 34px rgba(0, 0, 0, 0.28);
  line-height: 1.35;
  letter-spacing: 0.09em;
  backdrop-filter: blur(12px);
}

.flag-chip {
  width: 30px;
  height: 15px;
  border-radius: 2px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}

h1,
h2 {
  margin: 0 0 20px;
  letter-spacing: -0.01em;
}

h1 {
  font-size: clamp(2.8rem, 7vw, 6.4rem);
  line-height: 0.9;
}

h2 {
  font-size: clamp(2.2rem, 5.4vw, 4.8rem);
  line-height: 0.94;
}

/* CSS-side 3D text. The WebGL stage carries extruded type of its own; this is
   the flat-document counterpart, built from a stacked shadow ramp so the
   letterforms keep real depth at any size. */
.type-3d {
  color: var(--white);
  text-shadow:
    0 1px 0 rgba(0, 0, 0, 0.42),
    0 2px 0 rgba(0, 0, 0, 0.36),
    0 3px 0 rgba(0, 0, 0, 0.3),
    0 4px 0 rgba(0, 0, 0, 0.26),
    0 6px 0 rgba(0, 0, 0, 0.2),
    0 8px 0 rgba(0, 0, 0, 0.15),
    0 12px 28px rgba(0, 0, 0, 0.55);
}

.type-3d.light {
  color: var(--navy-deep);
  text-shadow:
    0 1px 0 rgba(11, 20, 37, 0.2),
    0 2px 0 rgba(11, 20, 37, 0.16),
    0 3px 0 rgba(11, 20, 37, 0.13),
    0 5px 0 rgba(11, 20, 37, 0.1),
    0 10px 26px rgba(11, 20, 37, 0.18);
}

.type-3d em {
  color: var(--green-dark);
  font-style: normal;
}

.lede,
.body {
  max-width: 56ch;
  margin: 0 0 24px;
  color: rgba(255, 255, 255, 0.82);
  font-size: clamp(1.02rem, 1.6vw, 1.28rem);
}

/* Acts that run over the pale middle of the journey need dark copy. */
.act-stats,
.act-pay {
  color: var(--ink);
}

.act-stats h2,
.act-pay h2 {
  color: var(--navy-deep);
}

.act-stats .body,
.act-pay .body,
.act-pay .footnote {
  color: rgba(16, 21, 31, 0.72);
}

.footnote {
  max-width: 56ch;
  margin: 28px 0 0;
  font-size: 0.9rem;
}

.payment-disclosure {
  display: grid;
  gap: 14px;
  max-width: 680px;
  margin-top: 28px;
  padding: clamp(20px, 3vw, 30px);
  border: 1px solid rgba(16, 21, 31, 0.16);
  border-left: 4px solid var(--green-dark);
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.88);
  box-shadow: 0 20px 56px rgba(11, 19, 35, 0.14);
  backdrop-filter: blur(12px);
}

.payment-disclosure .footnote {
  max-width: none;
  margin: 0;
  color: rgba(11, 20, 37, 0.86);
  font-size: clamp(0.98rem, 1.25vw, 1.08rem);
  line-height: 1.62;
}

.payment-disclosure .footnote + .footnote {
  padding-top: 14px;
  border-top: 1px solid rgba(16, 21, 31, 0.12);
  font-weight: 700;
}

/* -------------------------------------------------------------- controls */

.act-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px;
}

.button {
  display: inline-flex;
  min-height: 44px;
  align-items: center;
  justify-content: center;
  padding: 12px 20px;
  border: 1px solid transparent;
  border-radius: 8px;
  font-weight: 800;
  transition: transform 120ms ease, background 120ms ease;
}

.button.primary {
  color: var(--navy-deep);
  background: var(--green);
}

.button.ghost {
  color: var(--white);
  border-color: rgba(255, 255, 255, 0.3);
  background: rgba(255, 255, 255, 0.08);
}

.button:active {
  transform: scale(0.97);
  transition-duration: 80ms;
}

/* Three headline claims in place of the old lede paragraph. */
.lede-points {
  display: grid;
  gap: 10px;
  max-width: 30rem;
  margin: 0 0 28px;
  padding: 0;
  list-style: none;
}

.lede-points li {
  position: relative;
  padding: 11px 16px 11px 42px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  background: rgba(11, 20, 37, 0.6);
  font-size: clamp(0.98rem, 1.5vw, 1.18rem);
  font-weight: 700;
  backdrop-filter: blur(10px);
}

.lede-points li::before {
  position: absolute;
  top: 50%;
  left: 16px;
  width: 16px;
  height: 16px;
  content: "";
  border-radius: 50%;
  background: var(--green);
  box-shadow: 0 0 0 4px rgba(0, 215, 122, 0.16);
  transform: translateY(-50%);
}

/* ---------------------------------------------------------- city ticker */

.city-ticker {
  padding: clamp(20px, 3vw, 30px);
  border: 1px solid rgba(16, 21, 31, 0.14);
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.74);
  box-shadow: 0 20px 60px rgba(11, 19, 35, 0.12);
  backdrop-filter: blur(10px);
}

.ticker-label {
  margin: 0 0 6px;
  color: var(--green-dark);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* Only the city name lives here — no supporting text, per the brief. */
.ticker-city {
  margin: 0 0 22px;
  color: var(--navy-deep);
  font-size: clamp(2rem, 4.4vw, 3.4rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.02em;
}

.ticker-city.is-swapping {
  animation: cityRoll 460ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

@keyframes cityRoll {
  0% {
    opacity: 0;
    transform: translate3d(0, 14px, 0);
    filter: blur(5px);
  }
  100% {
    opacity: 1;
    transform: none;
    filter: none;
  }
}

.ticker-figures {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
  margin: 0;
}

.ticker-figures div {
  padding: 16px 18px;
  border-radius: 10px;
  background: rgba(11, 20, 37, 0.05);
}

.ticker-figures dt {
  color: var(--navy-deep);
  font-size: clamp(1.9rem, 4vw, 3rem);
  font-weight: 800;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.ticker-figures dd {
  margin: 6px 0 0;
  color: rgba(16, 21, 31, 0.66);
  font-size: 0.88rem;
}

.ticker-note {
  margin: 18px 0 0;
  color: rgba(16, 21, 31, 0.5);
  font-size: 0.76rem;
}

/* ------------------------------------------------- figures not yet verified */

/*
  No verified figure for any city. Two large em-dashes on their own read as a
  widget that failed, so the panel steps the figures back — dashed plate, the
  dash no longer competing with the city name — and says why in one line. The
  city being named is the content in this state; the counts are simply not
  claimed. Set by `syncMeasuredState()` in journey.js.
*/
.city-ticker.is-unmeasured .ticker-figures div {
  background: transparent;
  border: 1px dashed rgba(11, 20, 37, 0.18);
}

.city-ticker.is-unmeasured .ticker-figures dt {
  color: rgba(16, 21, 31, 0.32);
  font-size: clamp(1.4rem, 2.6vw, 2rem);
}

.ticker-pending {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 14px 0 0;
  color: rgba(16, 21, 31, 0.62);
  font-size: 0.8rem;
  line-height: 1.45;
}

.ticker-pending[hidden] {
  display: none;
}

.ticker-pending-dot {
  flex: none;
  width: 7px;
  height: 7px;
  margin-top: 1px;
  border-radius: 50%;
  background: var(--green-dark);
  /* A slow pulse reads as "in progress" rather than "error". */
  animation: pendingPulse 2.4s ease-in-out infinite;
}

@keyframes pendingPulse {
  0%, 100% { opacity: 0.35; }
  50% { opacity: 1; }
}

/* --------------------------------------------------- interactive text */

/*
  Headings are split into per-word spans by journey.js. Each word rises,
  unblurs and settles on a stagger when its act becomes current, and lifts
  under the pointer. `will-change` is deliberately absent — these run once per
  act, and promoting every word to its own layer costs more than it saves.
*/
[data-split] .word {
  display: inline-block;
  opacity: 0;
  transform: translate3d(0, 0.5em, 0) rotateX(-40deg);
  transform-origin: 50% 100%;
  filter: blur(6px);
  transition:
    opacity 520ms cubic-bezier(0.22, 0.61, 0.36, 1),
    transform 640ms cubic-bezier(0.22, 0.61, 0.36, 1),
    filter 520ms ease;
}

.act.is-active [data-split] .word {
  opacity: 1;
  transform: none;
  filter: none;
  transition-delay: calc(var(--i, 0) * 55ms);
}

/* Pointer response: words nearest the cursor lift toward it. */
[data-split] .word:hover {
  color: var(--green);
  transform: translate3d(0, -3px, 0) scale(1.04);
  transition-duration: 180ms;
  transition-delay: 0ms;
}

/* Body copy and list items get a lighter version of the same entrance. */
.act .body,
.act .lede,
.act .landmark-name,
.act .eyebrow,
.act .act-actions,
.act .phone-cue,
.act .city-ticker,
.act .lede-points li,
.act .operator-tiles li {
  opacity: 0;
  transform: translate3d(0, 16px, 0);
  transition:
    opacity 520ms ease,
    transform 620ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.act.is-active .body,
.act.is-active .lede,
.act.is-active .landmark-name,
.act.is-active .eyebrow,
.act.is-active .act-actions,
.act.is-active .phone-cue,
.act.is-active .city-ticker,
.act.is-active .lede-points li,
.act.is-active .operator-tiles li {
  opacity: 1;
  transform: none;
}

.act.is-active .lede-points li:nth-child(1) { transition-delay: 180ms; }
.act.is-active .lede-points li:nth-child(2) { transition-delay: 280ms; }
.act.is-active .lede-points li:nth-child(3) { transition-delay: 380ms; }
.act.is-active .act-actions { transition-delay: 460ms; }
.act.is-active .body,
.act.is-active .lede { transition-delay: 220ms; }
.act.is-active .city-ticker { transition-delay: 200ms; }

.act.is-active .operator-tiles li:nth-child(1) { transition-delay: 120ms; }
.act.is-active .operator-tiles li:nth-child(2) { transition-delay: 210ms; }
.act.is-active .operator-tiles li:nth-child(3) { transition-delay: 300ms; }
.act.is-active .operator-tiles li:nth-child(4) { transition-delay: 390ms; }

.lede-points li,
.operator-tiles li,
.rule-card li {
  transition:
    opacity 520ms ease,
    transform 300ms cubic-bezier(0.22, 0.61, 0.36, 1),
    border-color 220ms ease,
    background-color 220ms ease;
}

.lede-points li:hover,
.rule-card li:hover {
  transform: translate3d(6px, 0, 0);
  border-color: rgba(0, 215, 122, 0.55);
}

.operator-tiles li:hover {
  transform: translate3d(0, -4px, 0);
  border-color: rgba(0, 215, 122, 0.5);
}

/* ------------------------------------------------------------ phone cue */

.phone-cue {
  position: relative;
  display: inline-grid;
  grid-template-columns: 56px 1fr;
  gap: 14px;
  align-items: center;
  padding: 14px 22px 14px 14px;
  border: 1px solid rgba(0, 215, 122, 0.36);
  border-radius: 12px;
  background: rgba(11, 20, 37, 0.72);
  backdrop-filter: blur(10px);
}

.phone-cue img {
  width: 56px;
  height: 56px;
  border-radius: 14px;
}

.phone-cue strong {
  display: block;
}

.phone-cue span:not(.tap-ring) {
  color: rgba(255, 255, 255, 0.72);
  font-size: 0.9rem;
}

.tap-ring {
  position: absolute;
  top: 14px;
  left: 14px;
  width: 56px;
  height: 56px;
  border: 2px solid var(--green);
  border-radius: 14px;
  opacity: 0;
  pointer-events: none;
}

.act.is-active .tap-ring {
  animation: tapOut 1.9s ease-out infinite;
}

@keyframes tapOut {
  0% {
    transform: scale(1);
    opacity: 0.9;
  }
  70%,
  100% {
    transform: scale(1.7);
    opacity: 0;
  }
}

/* ----------------------------------------------------------- city cards */

.city-inner h2 {
  font-size: clamp(3rem, 8vw, 6.6rem);
  line-height: 0.86;
}

.nation {
  padding: 3px 9px;
  border-radius: 999px;
  color: var(--navy-deep);
  background: var(--green);
}

.landmark-name {
  margin: -8px 0 24px;
  color: rgba(255, 255, 255, 0.68);
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.02em;
}

.rule-card {
  display: grid;
  gap: 10px;
  max-width: 520px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.rule-card li {
  padding: 16px 18px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-left: 3px solid rgba(255, 255, 255, 0.3);
  border-radius: 10px;
  background: rgba(11, 20, 37, 0.66);
  color: rgba(255, 255, 255, 0.86);
  font-size: 0.98rem;
  backdrop-filter: blur(10px);
}

.rule-card span {
  display: block;
  margin-bottom: 5px;
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.rule-card .rule {
  border-left-color: var(--amber);
}

.rule-card .rule span {
  color: var(--amber);
}

.rule-card .act-taken {
  border-left-color: var(--green);
}

.rule-card .act-taken span {
  color: var(--green);
}

.rule-card .paid {
  border-left-color: var(--white);
}

.rule-card .paid span {
  color: rgba(255, 255, 255, 0.72);
}

/* Each card slides in behind the one before it as the act becomes active. */
.act.is-active .rule-card li {
  animation: cardIn 620ms cubic-bezier(0.22, 0.61, 0.36, 1) backwards;
}

.act.is-active .rule-card li:nth-child(1) {
  animation-delay: 120ms;
}
.act.is-active .rule-card li:nth-child(2) {
  animation-delay: 260ms;
}
.act.is-active .rule-card li:nth-child(3) {
  animation-delay: 400ms;
}

@keyframes cardIn {
  from {
    opacity: 0;
    transform: translate3d(18px, 8px, 0);
  }
}

/* ------------------------------------------------------- operator tiles */

.operator-tiles {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
  max-width: 900px;
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
}

.operator-tiles li {
  padding: 20px;
  border: 1px solid rgba(16, 21, 31, 0.14);
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.78);
  color: rgba(16, 21, 31, 0.7);
  font-size: 0.94rem;
}

.operator-tiles span {
  display: block;
  margin-bottom: 8px;
  color: var(--green-dark);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

/* -------------------------------------------------------------- closing */

.act-close .act-inner {
  width: min(100%, 1100px);
  max-width: 1100px;
}

.home-finale {
  display: grid;
  gap: 16px;
  width: min(100%, 700px);
  margin-bottom: 20px;
}

.journey-confidence-banner {
  position: relative;
  display: grid;
  grid-template-columns: clamp(76px, 10vw, 120px) minmax(0, 1fr);
  gap: clamp(16px, 2.5vw, 28px);
  align-items: center;
  overflow: hidden;
  padding: clamp(15px, 2vw, 22px);
  border: 1px solid rgba(166, 255, 0, 0.5);
  border-radius: 18px;
  background:
    radial-gradient(circle at 10% 20%, rgba(255, 211, 40, 0.22), transparent 28%),
    linear-gradient(120deg, rgba(6, 24, 28, 0.96), rgba(16, 67, 47, 0.9));
  box-shadow:
    0 24px 70px rgba(0, 0, 0, 0.4),
    inset 0 1px 0 rgba(255, 255, 255, 0.2),
    0 0 48px rgba(0, 215, 122, 0.13);
  opacity: 0;
  transform: translate3d(0, -20px, 0) rotateX(-5deg) scale(0.97);
  transform-origin: bottom center;
}

.journey-confidence-banner::after {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(115deg, transparent 34%, rgba(255, 255, 255, 0.16) 48%, transparent 62%);
  content: "";
  transform: translateX(-130%);
  pointer-events: none;
}

.journey-confidence-banner img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  border: 2px solid rgba(255, 255, 255, 0.62);
  border-radius: 16px;
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.38), 0 0 22px rgba(255, 204, 0, 0.25);
  object-fit: cover;
}

.journey-confidence-banner p {
  position: relative;
  z-index: 1;
  margin: 0;
  color: #f7fff9;
  font-size: clamp(1rem, 1.8vw, 1.38rem);
  font-weight: 800;
  line-height: 1.35;
  letter-spacing: -0.015em;
  text-shadow:
    0 1px 0 #b7dcc8,
    0 2px 0 #759e87,
    0 3px 0 #365d48,
    0 8px 18px rgba(0, 0, 0, 0.55),
    0 0 22px rgba(166, 255, 0, 0.15);
}

.next-trip-feature {
  overflow: hidden;
  border: 1px solid rgba(0, 215, 122, 0.38);
  border-radius: 18px;
  background: rgba(7, 15, 28, 0.86);
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.34);
  opacity: 0;
  transform: translate3d(28px, 0, 0) scale(0.97);
}

.next-trip-video {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #070f1c;
  object-fit: cover;
}

.next-trip-ribbon {
  position: relative;
  overflow: hidden;
  padding: 14px 0;
  border-top: 1px solid rgba(0, 215, 122, 0.28);
  background: linear-gradient(90deg, rgba(0, 215, 122, 0.2), rgba(166, 255, 0, 0.12));
  white-space: nowrap;
}

.next-trip-track {
  display: flex;
  width: max-content;
  will-change: transform;
}

.next-trip-track span {
  min-width: max-content;
  padding-right: 3.5rem;
  color: rgba(255, 255, 255, 0.76);
  font-size: clamp(0.88rem, 1.4vw, 1.08rem);
  letter-spacing: 0.015em;
}

.next-trip-track strong {
  color: var(--white);
}

.next-trip-track b {
  margin-left: 1rem;
  color: var(--green-bright);
  font-size: 1.2em;
}

.act-close.is-active .journey-confidence-banner,
.act-close.is-active .next-trip-feature {
  animation: finaleReveal 760ms 220ms cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
}

.act-close.is-active .next-trip-feature {
  animation-delay: 380ms;
}

.act-close.is-active .journey-confidence-banner::after {
  animation: finaleShine 1.1s 900ms ease-out forwards;
}

.act-close.is-active .next-trip-track {
  animation: nextTripForward 8s 800ms linear infinite;
}

@keyframes finaleReveal {
  to { opacity: 1; transform: none; }
}

@keyframes finaleShine {
  to { transform: translateX(130%); }
}

@keyframes nextTripForward {
  to { transform: translateX(-50%); }
}

/* -------------------------------------------------------------- noscript */

.noscript-note {
  position: relative;
  z-index: 20;
  max-width: 720px;
  margin: 0 auto;
  padding: 120px 24px;
}

/* --------------------------------------------------------- contact/footer */

.journey-contact {
  position: relative;
  z-index: 20;
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(320px, 1.1fr);
  gap: clamp(36px, 7vw, 96px);
  padding: clamp(88px, 11vw, 150px) var(--act-pad);
  color: var(--white);
  background:
    radial-gradient(circle at 82% 18%, rgba(0, 215, 122, 0.17), transparent 34%),
    linear-gradient(145deg, #0b1425, #11213c 58%, #08101e);
}

.contact-intro {
  max-width: 620px;
}

.contact-intro h2 {
  font-size: clamp(2.5rem, 5vw, 5rem);
}

.contact-intro > p:not(.eyebrow, .npp-note) {
  max-width: 48ch;
  color: rgba(255, 255, 255, 0.78);
  font-size: clamp(1rem, 1.4vw, 1.2rem);
  line-height: 1.6;
}

.contact-links {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 28px 0;
}

.contact-links a {
  padding: 10px 13px;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.07);
  font-weight: 750;
  transition: color 120ms ease, border-color 120ms ease, transform 120ms ease;
}

.contact-links a:hover,
.contact-links a:focus-visible,
.npp-note a:hover,
.npp-note a:focus-visible {
  color: var(--green);
  border-color: rgba(0, 215, 122, 0.55);
}

.contact-links a:active {
  transform: scale(0.97);
  transition-duration: 80ms;
}

.journey-address {
  margin: -10px 0 28px;
  color: rgba(255, 255, 255, 0.7);
  font-style: normal;
  line-height: 1.55;
}

.legal-identity {
  max-width: 64ch;
  margin: -8px 0 28px;
  color: rgba(255, 255, 255, 0.78);
  font-size: 0.9rem;
  line-height: 1.6;
}

.npp-note {
  max-width: 64ch;
  margin: 0;
  padding: 16px 18px;
  border-left: 3px solid var(--green);
  color: rgba(255, 255, 255, 0.72);
  background: rgba(255, 255, 255, 0.055);
  font-size: 0.86rem;
  line-height: 1.55;
}

.npp-note strong,
.npp-note a {
  color: var(--white);
}

.npp-note a {
  text-decoration: underline;
  text-underline-offset: 3px;
}

.contact-form {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px;
  align-self: start;
  padding: clamp(22px, 3vw, 34px);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.08);
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.24);
  backdrop-filter: blur(14px);
}

.contact-form label {
  display: grid;
  gap: 8px;
  color: rgba(255, 255, 255, 0.82);
  font-size: 0.8rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.contact-form input,
.contact-form textarea {
  width: 100%;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 9px;
  padding: 12px 13px;
  color: var(--white);
  background: rgba(4, 11, 23, 0.62);
  font: inherit;
  line-height: 1.4;
  text-transform: none;
}

.contact-form input:focus,
.contact-form textarea:focus {
  border-color: var(--green);
  outline: 3px solid rgba(0, 215, 122, 0.18);
}

.contact-message,
.contact-form .button,
.contact-form small {
  grid-column: 1 / -1;
}

.contact-form textarea {
  resize: vertical;
}

.contact-form .button {
  border: 0;
  cursor: pointer;
}

.contact-form small {
  color: rgba(255, 255, 255, 0.58);
  line-height: 1.45;
}

/* ------------------------------------------------------------ responsive */

@media (max-width: 900px) {
  .journey-contact {
    grid-template-columns: 1fr;
  }

  .act-inner.split,
  .operator-tiles {
    grid-template-columns: 1fr;
  }

  .ticker-figures {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .act-stats .act-inner {
    margin-left: 0;
  }

  /* The 3D subject moves to the upper half on narrow screens, so copy sits
     under it rather than beside it. */
  .act-inner {
    justify-content: flex-end;
    padding-bottom: 12vh;
  }

  .act-close .act-inner {
    justify-content: center;
    padding-top: 96px;
    padding-bottom: 5vh;
  }

  .home-finale {
    width: min(100%, 700px);
  }

  /* With copy and stage sharing the lower half, a gradient under the text is
     what guarantees contrast — the 3D behind it is not a fixed colour. */
  .act-regions::after,
  .act-depart::after {
    position: fixed;
    right: 0;
    bottom: 0;
    left: 0;
    height: 72vh;
    content: "";
    background: linear-gradient(180deg, transparent 0%, rgba(8, 15, 28, 0.82) 62%, rgba(8, 15, 28, 0.94) 100%);
    opacity: 0;
    transition: opacity 400ms ease;
    pointer-events: none;
    z-index: -1;
  }

  .act-regions.is-active::after,
  .act-depart.is-active::after {
    opacity: 1;
  }

  .header-route {
    display: none;
  }

  .journey-header {
    grid-template-columns: auto auto;
    justify-content: space-between;
  }
}

@media (max-width: 520px) {
  .hero-eyebrow {
    align-items: flex-start;
    border-radius: 14px;
    font-size: 0.7rem;
  }

  .contact-form {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 560px) {
  .header-action {
    display: none;
  }

  .journey-header {
    grid-template-columns: auto;
  }

  .rule-card li {
    font-size: 0.92rem;
  }

  .act-close .act-inner {
    padding-top: 78px;
    padding-bottom: 3vh;
  }

  .home-finale {
    gap: 10px;
    margin-bottom: 12px;
  }

  .journey-confidence-banner {
    grid-template-columns: 62px minmax(0, 1fr);
    gap: 10px;
    padding: 10px;
    border-radius: 12px;
  }

  .journey-confidence-banner img {
    border-radius: 9px;
  }

  .journey-confidence-banner p {
    font-size: 0.73rem;
    line-height: 1.35;
    text-shadow: 0 1px 0 #759e87, 0 3px 10px rgba(0, 0, 0, 0.65);
  }

  .next-trip-feature {
    border-radius: 12px;
    transform: translate3d(24px, 0, 0) scale(0.97);
  }

  .next-trip-ribbon {
    padding: 10px 0;
  }

  .next-trip-track span {
    padding-right: 2rem;
    font-size: 0.76rem;
  }

}

@media (prefers-reduced-motion: reduce) {
  .act-inner,
  [data-split] .word,
  .act .body,
  .act .lede,
  .act .landmark-name,
  .act .eyebrow,
  .act .act-actions,
  .act .phone-cue,
  .act .city-ticker,
  .act .lede-points li,
  .act .operator-tiles li {
    opacity: 1;
    filter: none;
    transform: none;
    transition: none;
  }

  .journey-confidence-banner,
  .next-trip-feature {
    opacity: 1;
    transform: none;
    animation: none;
  }

  .act-close.is-active .journey-confidence-banner::after,
  .act-close.is-active .next-trip-track {
    animation: none;
  }

  .ticker-city.is-swapping {
    animation: none;
  }

  .ticker-pending-dot {
    animation: none;
    opacity: 1;
  }

  .scroll-hint::after,
  .act.is-active .tap-ring,
  .act.is-active .rule-card li {
    animation: none;
  }

  .tap-ring {
    display: none;
  }
}

/* Seven-stage product story: translucent glass floats above the retained live journey. */
.journey-story {
  --story-progress: 0%;
  position: fixed;
  z-index: 18;
  right: clamp(16px, 3.2vw, 52px);
  bottom: clamp(82px, 10vh, 112px);
  width: min(350px, calc(100vw - 32px));
  min-height: 138px;
  perspective: 900px;
  pointer-events: auto;
}

.story-card {
  --tilt-x: 0deg;
  --tilt-y: 0deg;
  position: absolute;
  inset: 0 0 auto;
  display: grid;
  grid-template-columns: 46px 1fr;
  gap: 3px 14px;
  min-height: 112px;
  padding: 18px 20px;
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 24px;
  background: linear-gradient(135deg, rgba(7, 22, 35, 0.78), rgba(9, 54, 48, 0.48));
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.34), inset 0 1px rgba(255, 255, 255, 0.18);
  -webkit-backdrop-filter: blur(20px) saturate(145%);
  backdrop-filter: blur(20px) saturate(145%);
  opacity: 0;
  transform: translate3d(34px, 10px, -60px) rotateY(8deg);
  transition: opacity 420ms ease, transform 620ms cubic-bezier(0.2, 0.8, 0.2, 1);
  pointer-events: none;
}

.story-card::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(circle at 20% 0%, rgba(84, 255, 126, 0.2), transparent 45%);
  pointer-events: none;
}

.story-card.is-active {
  opacity: 1;
  transform: translate3d(0, 0, 0) rotateX(var(--tilt-x)) rotateY(var(--tilt-y));
  pointer-events: auto;
}

.story-count {
  grid-row: 1 / span 2;
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  color: #071a17;
  border-radius: 15px;
  background: linear-gradient(145deg, #a8ff47, #00dc7b);
  box-shadow: 0 0 30px rgba(0, 220, 123, 0.42);
  font-weight: 900;
}

.story-card strong { font-size: 1.08rem; line-height: 1.2; }
.story-card small { color: rgba(255, 255, 255, 0.76); font-size: 0.84rem; line-height: 1.35; }

.story-dots {
  position: absolute;
  top: 122px;
  right: 16px;
  left: 16px;
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 6px;
}

.story-dots i {
  height: 3px;
  overflow: hidden;
  border-radius: 99px;
  background: rgba(255, 255, 255, 0.2);
}

.story-dots i.is-active { background: #69ff38; box-shadow: 0 0 14px rgba(105, 255, 56, 0.8); }

.journey-pagination {
  position: fixed;
  z-index: 19;
  left: 50%;
  bottom: max(24px, calc(env(safe-area-inset-bottom) + 18px));
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 999px;
  background: rgba(7, 22, 35, 0.64);
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.28), inset 0 1px rgba(255, 255, 255, 0.16);
  -webkit-backdrop-filter: blur(18px) saturate(145%);
  backdrop-filter: blur(18px) saturate(145%);
  transform: translateX(-50%);
}

.page-arrow,
.page-dots button {
  border: 0;
  color: #fff;
  background: transparent;
  cursor: pointer;
}

.page-arrow {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  font-size: 1rem;
  transition: color 180ms ease, background-color 180ms ease, transform 180ms ease;
}

.page-arrow:hover:not(:disabled) { color: #071a17; background: #69ff38; transform: scale(1.08); }
.page-arrow:disabled { opacity: 0.28; cursor: default; }

.page-dots { display: flex; align-items: center; gap: 6px; }
.page-dots button {
  width: 7px;
  height: 7px;
  padding: 0;
  border-radius: 99px;
  background: rgba(255, 255, 255, 0.3);
  transition: width 220ms ease, background-color 220ms ease, box-shadow 220ms ease;
}
.page-dots button.is-active { width: 24px; background: #69ff38; box-shadow: 0 0 12px rgba(105, 255, 56, 0.72); }
.page-label { min-width: 32px; color: rgba(255, 255, 255, 0.78); font-size: 0.72rem; font-weight: 800; text-align: center; }

/* Direct-file safety: content remains readable when browser module security blocks Three.js. */
.file-preview .act-inner,
.file-preview [data-split] .word,
.file-preview .act .body,
.file-preview .act .lede,
.file-preview .act .landmark-name,
.file-preview .act .eyebrow,
.file-preview .act .act-actions,
.file-preview .act .phone-cue,
.file-preview .act .city-ticker,
.file-preview .act .lede-points li,
.file-preview .act .operator-tiles li {
  opacity: 1;
  filter: none;
  transform: none;
}

@media (max-width: 720px) {
  .journey-story {
    right: 12px;
    bottom: 76px;
    width: min(310px, calc(100vw - 24px));
  }
  .story-card { min-height: 100px; padding: 14px 16px; border-radius: 20px; }
  .story-dots { top: 110px; }
  .journey-pagination {
    bottom: max(18px, calc(env(safe-area-inset-bottom) + 14px));
    max-width: calc(100vw - 24px);
  }
  .page-label { display: inline-block; }
}

/* Keep bottom pagination visible above Codex's review bar in direct-file previews. */
.file-preview .journey-pagination {
  bottom: 104px;
}

.file-preview .journey-story {
  bottom: 174px;
}

@media (prefers-reduced-motion: reduce) {
  .story-card { transition: opacity 180ms ease; }
  .story-card.is-active { transform: none; }
}

/* The flexible-payment kicker carries more weight than a normal eyebrow. */
.act-stats .eyebrow {
  font-size: 0.94rem;
  letter-spacing: 0.14em;
}

/* ===================================================== the region slider */

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

.act-regions .act-inner {
  max-width: min(1180px, 100%);
  margin-inline: auto;
}

.region-viewport {
  overflow: hidden;
  width: 100%;
  border-radius: 18px;
}

/* One rail, four cards, driven by --region-index from journey.js. */
.region-rail {
  display: flex;
  width: 400%;
  transform: translate3d(calc(var(--region-index, 0) * -25%), 0, 0);
  transition: transform 900ms cubic-bezier(0.65, 0, 0.35, 1);
}

.region-card {
  display: grid;
  width: 25%;
  grid-template-columns: minmax(180px, 260px) minmax(0, 1fr);
  gap: clamp(20px, 4vw, 48px);
  align-items: center;
  padding: clamp(18px, 3vw, 34px);
}

.region-screen {
  margin: 0;
  perspective: 1400px;
}

.region-screen img {
  width: 100%;
  /*
    The markup carries width/height so the box is reserved before the image
    loads. Without `height: auto` the 1552 attribute becomes the used height
    and the handset stretches to full length — which is what it did.
  */
  height: auto;
  border-radius: 20px;
  box-shadow: 0 26px 70px rgba(0, 0, 0, 0.5);
  /* A full turn, then held — the 15s cycle the brief asks for. */
  animation: handsetTurn 15s cubic-bezier(0.55, 0, 0.45, 1) infinite;
  transform-style: preserve-3d;
}

@keyframes handsetTurn {
  0% { transform: rotateY(0deg); }
  20% { transform: rotateY(360deg); }
  100% { transform: rotateY(360deg); }
}

.region-copy {
  display: grid;
  gap: 12px;
}

.region-nation {
  margin: 0 0 4px;
  color: rgba(255, 255, 255, 0.72);
  font-size: 0.86rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.region-nation span {
  padding: 4px 11px;
  border-radius: 999px;
  color: var(--navy-deep);
  background: var(--green);
}

.region-line {
  margin: 0;
  padding: 14px 18px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-left: 3px solid rgba(255, 255, 255, 0.3);
  border-radius: 10px;
  background: rgba(11, 20, 37, 0.66);
  color: rgba(255, 255, 255, 0.9);
  font-size: 1rem;
  backdrop-filter: blur(10px);
}

.region-line.rule { border-left-color: var(--amber); }
.region-line.act-taken { border-left-color: var(--green); }
.region-line.paid { border-left-color: var(--white); }

.region-dots {
  display: flex;
  justify-content: center;
  gap: 9px;
  margin-top: 22px;
}

.region-dots span {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.28);
  transition: background 300ms ease, transform 300ms ease;
}

.region-dots span.is-on {
  background: var(--green);
  transform: scale(1.35);
}

@media (max-width: 900px) {
  .region-card {
    grid-template-columns: 1fr;
    justify-items: center;
  }

  .region-screen {
    max-width: 190px;
  }

  .region-line {
    font-size: 0.92rem;
  }
}

/* ============================================ brand banner behind copy */

/*
  A tinted ParkShield-green plate so these two passages stay readable over
  whatever frame of the film happens to be behind them.
*/
.act-sweep .act-inner,
.act-stats .act-inner > div:first-child {
  padding: clamp(22px, 3vw, 38px);
  border: 1px solid rgba(125, 190, 255, 0.3);
  border-radius: 16px;
  /* ParkShield navy-blue variant of the brand plate. */
  background: linear-gradient(135deg, rgba(27, 49, 86, 0.62), rgba(1, 33, 105, 0.42));
  backdrop-filter: blur(14px);
}

/* Dark plate means the stats copy inside it flips to light ink. */
.act-stats .act-inner > div:first-child h2,
.act-stats .act-inner > div:first-child .body {
  color: var(--white);
}

.act-stats .act-inner > div:first-child .body {
  color: rgba(255, 255, 255, 0.84);
}

.act-stats .act-inner > div:first-child .eyebrow {
  color: var(--green);
}

.act-sweep .act-inner {
  /* The sticky wrapper needs its own height back once it becomes a panel. */
  min-height: auto;
  align-self: center;
  margin-block: 22vh;
  /*
    As a panel this loses the padding-block above — the shorthand `padding` in
    the brand-plate rule overwrites it — so its own sticky offset has to do the
    clearing instead. Without this the panel pins at y=0 and the heading sits
    under the header at every width.
  */
  top: var(--header-clear);
}

@media (prefers-reduced-motion: reduce) {
  .region-screen img {
    animation: none;
  }

  .region-rail {
    transition: none;
  }
}
