/*
 * Every scroll-driven effect on the page.
 *
 * The rule that matters: nothing here may hide content from a visitor who never
 * gets the JavaScript. Elements are revealed by adding a class, and the styles
 * that hide them are scoped to .js so a failed script leaves a readable page
 * rather than a blank one.
 */

/* Reveals ----------------------------------------------------------------- */

.js [data-reveal] {
  opacity: 0;
  transform: translate3d(0, var(--reveal-shift, 26px), 0);
  transition:
    opacity var(--duration-slow) var(--ease-out) var(--reveal-delay, 0ms),
    transform var(--duration-slow) var(--ease-out) var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}

.js [data-reveal].is-revealed {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

/* Once it has arrived it never moves again, so stop paying to composite it. */
.js [data-reveal].is-settled { will-change: auto; }

/*
 * Line and word reveals.
 *
 * The mask is the parent's overflow, so the inner span can start fully below the
 * baseline and rise into view rather than fading in place. `padding-bottom` on
 * the mask leaves room for descenders, which a plain overflow:hidden would clip.
 */
.line-mask,
.word-mask {
  display: block;
  overflow: hidden;
  padding-bottom: 0.12em;
  margin-bottom: -0.12em;
}

.word-mask {
  display: inline-block;
  vertical-align: top;
}

.js .line-mask > span,
.js .word-mask > span {
  display: block;
  transform: translate3d(0, 105%, 0);
  transition: transform var(--duration-slow) var(--ease-out) var(--reveal-delay, 0ms);
  will-change: transform;
}

.js .is-revealed .line-mask > span,
.js .is-revealed .word-mask > span,
.js .line-mask.is-revealed > span,
.js .word-mask.is-revealed > span {
  transform: translate3d(0, 0, 0);
}

/* Scroll-linked ----------------------------------------------------------- */

/*
 * `--progress` is written by the scroll script, 0 at the start of the element's
 * pass through the viewport and 1 at the end. Anything can read it, which is how
 * the parallax, the counters and the pinned panels all stay on one clock.
 */
[data-scroll-progress] { --progress: 0; }

.parallax {
  will-change: transform;
  transform: translate3d(0, calc((var(--progress) - 0.5) * var(--parallax-range, 60px)), 0);
}

/* Progress bar ------------------------------------------------------------ */

.read-progress {
  position: fixed;
  inset: 0 0 auto 0;
  height: 2px;
  z-index: calc(var(--z-header) + 1);
  background: var(--text);
  transform: scaleX(var(--read, 0));
  transform-origin: 0 50%;
  pointer-events: none;
  opacity: 0.55;
}

/* Preloader --------------------------------------------------------------- */

.preloader {
  position: fixed;
  inset: 0;
  z-index: calc(var(--z-overlay) + 10);
  display: grid;
  place-items: center;
  background: var(--ink);
  transition: opacity 700ms var(--ease-out), visibility 700ms;
}

.preloader.is-done {
  opacity: 0;
  visibility: hidden;
}

.preloader__count {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--text-muted);
  letter-spacing: 0.2em;
  margin-top: var(--space-m);
}

.preloader__bar {
  width: min(58vw, 24rem);
  height: 1px;
  margin-top: var(--space-s);
  background: var(--line);
  overflow: hidden;
}

.preloader__bar::after {
  content: '';
  display: block;
  height: 100%;
  width: 100%;
  background: var(--text);
  transform: scaleX(var(--load, 0));
  transform-origin: 0 50%;
  transition: transform 200ms linear;
}

/* Reduced motion ---------------------------------------------------------- */

/*
 * A visitor who asks for less motion still gets the whole page - it simply
 * arrives rather than animates. Reveals are neutralised at the source so no
 * observer is needed to un-hide them.
 */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  .js [data-reveal],
  .js [data-reveal].is-revealed {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .js .line-mask > span,
  .js .word-mask > span {
    transform: none;
    transition: none;
  }

  .parallax { transform: none; }

  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
