/* ==========================================================================
   Beloftes — full-viewport "fruitmachine" brand-promise presentation.
   The "UX" prefix is a separate element that never moves; only the suffix
   reel (a clipped vertical list of endings) translates by one line-height
   per step, so it reads like a slot-machine cycling through UXploring /
   UXceptional / UXtensive / UXplaining. Step state is driven entirely by
   [data-promise] on the section root, same pattern as procescirkels.
   ========================================================================== */

.beloftes {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding-block: 64px;
  overflow: hidden;
}

.beloftes__inner {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 16px;
  /* Referenced by .beloftes__reel-row's own font-size, by
     .beloftes__content's margin calc below, and by .beloftes__ghost's
     own font-size (as a multiple of this), so all three always agree on
     how large the "UX" prefix currently renders. Redefined (not
     overridden per-element) at the 900px breakpoint below, so every
     consumer picks up the change together. */
  --reel-font-size: clamp(48px, 9vw, 160px);
}

/* Ghost text is positioned relative to this wrapper (sized to exactly
   match .beloftes__reel-row, since the ghost is absolute/out-of-flow)
   rather than the whole section, so its vertical center always lines up
   with the actual headline text instead of the section's own midpoint. */
.beloftes__headline {
  position: relative;
}

/* top/bottom stay tied to .beloftes__headline (its containing block) so
   the vertical centering described above is untouched, but left/width/
   transform break out to the full viewport width — .beloftes__headline
   itself is boxed in by .beloftes__inner's own .container padding, which
   was clipping this text well short of the actual viewport edges. Safe
   with 100vw here since .beloftes (the section) already has its own
   overflow:hidden, which swallows the usual 100vw/scrollbar overflow
   this trick can otherwise introduce.
   justify-content is flex-start with a dynamic margin-left — "UX" and
   its suffix (.beloftes__ghost-clip, snug against it with no gap, same
   as the foreground) move together as one rigid block, so the pair
   always reads as a single word rather than "UX" sitting at some fixed
   spot with a gap or overlap opening up next to whatever the suffix is
   currently doing. --ghost-margin-left is set by initBeloftesGhostAlign()
   in js/script.js, which measures where the suffix ends up landing
   (immediately after "UX", following naturally in flex flow) against
   the foreground's own .beloftes__reel, and shifts the whole block by
   the difference — moving "UX" is exactly what keeps it snug against
   the now-correctly-aligned suffix instead of static in place.
   font-size is derived from --reel-font-size (22/9, the ratio between
   this element's original clamp() and the reel's own — 22vw vs. 9vw —
   in the shared desktop zone where neither hits its min/max bound), not
   an independent clamp(), so the ghost stays proportional to the
   foreground text at every viewport instead of drifting: the two used to
   scale via separate clamp()s with their own, unrelated breakpoints
   (640px here, 900px for the reel) and vw coefficients, so the ratio
   between them held only on desktop and shrank on mobile, making the
   ghost look disproportionately small there. --ghost-font-size overrides
   this ratio only when initBeloftesGhostAlign() finds the 22/9 size
   isn't wide enough to still reach the viewport's right edge from the
   aligned position — pinning the suffix's start to the foreground's own
   (much smaller, once .beloftes__reel-row's margin resets at 900px)
   position fixes how much room the word has left to cover, and on
   narrow viewports the 22/9 size alone can fall short of the edge. */
.beloftes__ghost {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 100vw;
  margin-left: var(--ghost-margin-left, calc(var(--container-padding) + 242px));
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  justify-content: flex-start;
  font-family: var(--font-family);
  font-weight: 800;
  font-size: var(--ghost-font-size, calc(var(--reel-font-size) * 22 / 9));
  letter-spacing: -0.02em;
  color: transparent;
  -webkit-text-stroke: 2px rgba(188, 185, 255, 0.3);
  white-space: nowrap;
  pointer-events: none;
  z-index: 0;
}

/* Experiment: the ghost runs the same fruitmachine reel as the small
   "UX" suffix, just slower (1.4s vs. the reel's 0.7s) — a lazier, more
   ambient version of the same motion for the oversized background
   word, rather than an instant crossfade. No margin of its own — it
   follows "UX" immediately in normal flex flow (snug, no gap), same as
   the foreground; see .beloftes__ghost above for how the pair is
   shifted together to land the suffix in the right spot.
   flex-shrink:0 matters here: a flex item's automatic min-width only
   falls back to its content size when the item's own overflow is
   visible — since this has overflow:hidden, without flex-shrink:0 the
   flex parent (.beloftes__ghost, a fixed 100vw box) is free to shrink
   this item (and silently crop the word inside it) whenever "UX" plus
   the suffix's natural width exceeds the viewport, which is routinely
   the case at this font size. */
.beloftes__ghost-clip {
  height: 1em;
  overflow: hidden;
  flex-shrink: 0;
}

.beloftes__ghost-list {
  transition: transform 1.4s var(--ease);
}

.beloftes__ghost-list li {
  height: 1em;
  display: flex;
  align-items: center;
  white-space: nowrap;
}

.beloftes[data-promise="0"] .beloftes__ghost-list { transform: translateY(0); }
.beloftes[data-promise="1"] .beloftes__ghost-list { transform: translateY(-1em); }
.beloftes[data-promise="2"] .beloftes__ghost-list { transform: translateY(-2em); }
.beloftes[data-promise="3"] .beloftes__ghost-list { transform: translateY(-3em); }

.beloftes__reel-row {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: flex-start;
  font-family: var(--font-family);
  font-weight: 800;
  font-size: var(--reel-font-size);
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--color-lavender);
  /* Aligns with .page-hero__title/.page-back in styles.css (same
     values), so the reel lines up under the page-hero above — the
     ghost text is deliberately excluded, since it's a full-bleed
     background effect, not part of the aligned content column. */
  margin-left: 242px;
}

/* Aligned with the left edge of the "X" in the static "UX" prefix — not
   the "U" (.beloftes__reel-row's own offset), and not where the suffix
   reel starts either. "U" and "X" don't necessarily render at the same
   width, so this can't be a fixed fraction of the prefix's total width;
   --beloftes-content-margin is set by initBeloftesContentAlign() in
   js/script.js, which measures the prefix's actual rendered "X" position
   directly. The fallback below (the old, "U"-aligned + full-prefix-width
   formula) only matters before that first run or if JS fails to load. */
.beloftes__content {
  margin-left: var(--beloftes-content-margin, calc(242px + var(--reel-font-size) * 1.353));
}

/* Aligned with .beloftes__content (the article above it) — same
   variable, so the nav buttons sit directly under the promise
   description rather than under the "UX" prefix. */
.beloftes__nav {
  margin-left: var(--beloftes-content-margin, calc(242px + var(--reel-font-size) * 1.353));
}

@media (max-width: 1080px) {
  .beloftes__reel-row {
    margin-left: 228px;
  }
}

@media (max-width: 900px) {
  .beloftes__reel-row {
    margin-left: 0;
  }
}

.beloftes__reel {
  height: 1em;
  overflow: hidden;
}

.beloftes__reel-list li {
  height: 1em;
  display: flex;
  align-items: center;
  white-space: nowrap;
}

.beloftes__reel-list {
  transition: transform 0.7s var(--ease);
}

.beloftes[data-promise="0"] .beloftes__reel-list { transform: translateY(0); }
.beloftes[data-promise="1"] .beloftes__reel-list { transform: translateY(-1em); }
.beloftes[data-promise="2"] .beloftes__reel-list { transform: translateY(-2em); }
.beloftes[data-promise="3"] .beloftes__reel-list { transform: translateY(-3em); }

.beloftes__content {
  position: relative;
  max-width: 700px;
  min-height: 200px;
}

.beloftes__promise {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
  pointer-events: none;
}

.beloftes__promise.is-active {
  pointer-events: auto;
}

/* Label and description fade/rise independently (rather than as one
   shared block) so the description can lag slightly behind the label
   — a small stagger instead of both appearing in lockstep. */
.beloftes__label {
  display: flex;
  align-items: center;
  gap: 16px;
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
}

.beloftes__promise.is-active .beloftes__label {
  opacity: 1;
  transform: translateY(0);
}

.beloftes__label span {
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  color: var(--color-lavender);
  transition: color 0.5s var(--ease);
}

.beloftes__promise.is-active .beloftes__label span {
  color: var(--color-lime);
}

.beloftes__desc {
  font-size: clamp(18px, 1.6vw, 24px);
  font-weight: 700;
  line-height: 1.5;
  letter-spacing: -0.01em;
  color: var(--color-white);
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.5s var(--ease) 0.12s, transform 0.5s var(--ease) 0.12s;
}

.beloftes__promise.is-active .beloftes__desc {
  opacity: 1;
  transform: translateY(0);
}

.beloftes__nav {
  display: flex;
  gap: 16px;
  /* .beloftes__inner's own gap (16px) is shared by every child pair,
     including headline-to-content — pulling the nav up by half of that
     gap (rather than removing it entirely) halves just this one gap
     without touching the headline/content spacing above it. */
  margin-top: -8px;
}

.beloftes__nav-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 64px;
  flex-shrink: 0;
  border-radius: 50%;
  border: 2px solid var(--color-lavender-50);
  opacity: 1;
  visibility: visible;
  transition: border-color 0.2s var(--ease), background-color 0.2s var(--ease), opacity 0.3s var(--ease), visibility 0s linear 0s;
}

.beloftes__nav-btn:hover {
  border-color: var(--color-lavender);
  background: var(--color-bg-lavender-10);
}

/* Safari has been seen leaving this button visible at the first/last
   promise even though opacity:0 is applied — pairing it with a
   visibility transition (delayed on hide, instant on show) is the
   standard cross-browser-safe way to force an element fully out of
   rendering once faded out, rather than relying on opacity alone. */
.beloftes__nav-btn.is-hidden {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: border-color 0.2s var(--ease), background-color 0.2s var(--ease), opacity 0.3s var(--ease), visibility 0s linear 0.3s;
}

.beloftes__nav-btn--prev img {
  transform: rotate(-90deg);
}

.beloftes__nav-btn--next img {
  transform: rotate(90deg);
}

@media (max-width: 900px) {
  /* Redefines the shared variable (rather than overriding
     .beloftes__reel-row's font-size directly) so .beloftes__ghost's own
     calc() above picks up the same change automatically, keeping the
     22/9 ratio intact at this breakpoint too. */
  .beloftes__inner {
    --reel-font-size: clamp(40px, 14vw, 96px);
  }

  .beloftes__desc {
    font-size: 18px;
  }
}

@media (max-width: 640px) {
  .beloftes {
    padding-block: 40px;
  }
}
