/* Motion system: reveals, staggers, the hero intro, scroll-linked depth and the returning nav.
   Every effect animates transform, opacity or clip-path only. Hidden "before" states are scoped to
   `html.js` (set inline in <head>) so the page is complete without scripts. Scroll-linked effects
   run on native scroll-driven animations where they exist; `scroll-progress.js` writes the same
   progress into `--p` everywhere else. Reduced motion turns all of it off at the end of this file. */

:root {
  --ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --duration-reveal: 900ms;
  --reveal-y: 22px;
  --stagger-step: 55ms;
  --parallax-scale: 1; /* halved on phones at the end of this file */
  --nav-h: auto; /* measured by floating-nav.js */
}

/* A physical spring for the pops (patches, chips, dots); falls back to the bezier spring */
@supports (animation-timing-function: linear(0, 1)) {
  :root {
    --ease-spring: linear(
      0, 0.009, 0.035 2.1%, 0.141, 0.281 6.7%, 0.723 12.9%, 0.938 16.7%, 1.017, 1.077, 1.121,
      1.149 24.3%, 1.159, 1.163, 1.161, 1.154 29.9%, 1.129 32.8%, 1.051 39.6%, 1.017 43.1%,
      0.991, 0.977 51%, 0.974 53.8%, 0.975 57.1%, 0.997 69.8%, 1.003 76.9%, 1.004 83.8%, 1
    );
  }
}

/* In-page anchors glide via scripts/smooth-anchors.js (native smooth scroll would fight its per-frame scrollTo) */

/* Anchor targets clear the returning nav */
[id] {
  scroll-margin-top: calc(var(--nav-h, 0px) + 1rem);
}

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

.js [data-reveal] {
  opacity: 0;
  transform: translateY(var(--reveal-y));
  transition:
    opacity 700ms var(--ease-out-expo),
    transform var(--duration-reveal) var(--ease-out-expo);
}

.js [data-reveal="fade"] {
  transform: none;
}

.js [data-reveal="scale"] {
  transform: scale(0.96);
}

/* A head whose title rises word by word only fades; the words carry the motion */
.js [data-reveal]:has([data-split]) {
  transform: none;
}

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

/* Staggered groups: the container stays put, its children arrive one after another.
   `--i` is set by reveal.js on every child. */
.js [data-reveal].stagger {
  opacity: 1;
  transform: none;
}

.js .stagger > * {
  opacity: 0;
  transform: translateY(14px);
  transition:
    opacity 600ms var(--ease-out-expo),
    transform 800ms var(--ease-out-expo);
  transition-delay: calc(var(--i, 0) * var(--stagger-step));
}

.js .is-in .stagger > *,
.js .is-in.stagger > * {
  opacity: 1;
  transform: none;
}

/* Head children: the eyebrow leads, the lede follows the title */
.js [data-reveal]:has([data-split]) > .eyebrow,
.js [data-reveal]:has([data-split]) > .band__lede,
.js [data-reveal]:has([data-split]) > .room__split {
  opacity: 0;
  transform: translateY(12px);
  transition:
    opacity 600ms var(--ease-out-expo),
    transform 800ms var(--ease-out-expo);
}

.js [data-reveal]:has([data-split]) > .band__lede,
.js [data-reveal]:has([data-split]) > .room__split {
  transition-delay: 420ms;
}

.js [data-reveal].is-in > .eyebrow,
.js [data-reveal].is-in > .band__lede,
.js [data-reveal].is-in > .room__split {
  opacity: 1;
  transform: none;
}

/* ---------- Split headlines: each word rises out of its own clip ---------- */

.word {
  display: inline-block;
  clip-path: inset(-0.25em 0 -0.35em 0);
}

.word > span {
  display: inline-block;
  transform: translateY(140%);
  transition: transform 1000ms var(--ease-out-expo);
  transition-delay: calc(120ms + var(--w, 0) * 48ms);
}

.is-in .word > span {
  transform: none;
}

/* ---------- Hero intro ---------- */

.js .intro {
  opacity: 0;
}

.js .hero__line {
  clip-path: inset(-0.12em 0 -0.28em 0);
}

.js .hero__line-inner {
  display: block;
  transform: translateY(130%);
}

.js .intro-patch {
  opacity: 0;
  transform: scale(0.4);
}

.js .brand,
.js .nav__links,
.js .nav__end {
  opacity: 0;
}

.js .brand { --i: 0; }
.js .nav__links { --i: 1; }
.js .nav__end { --i: 2; }

.is-ready .intro {
  animation: intro-rise 900ms var(--ease-out-expo) both;
  animation-delay: calc(160ms + var(--i, 0) * 130ms);
}

.is-ready .hero__line-inner {
  animation: intro-line 1100ms var(--ease-out-expo) both;
  animation-delay: calc(140ms + var(--i, 0) * 120ms);
}

.is-ready .intro-patch {
  animation: intro-pop 800ms var(--ease-spring) both;
  animation-delay: calc(760ms + var(--i, 0) * 110ms);
}

.is-ready .brand,
.is-ready .nav__links,
.is-ready .nav__end {
  animation: intro-fade 700ms var(--ease-out-expo) both;
  animation-delay: calc(var(--i, 0) * 90ms);
}

@keyframes intro-rise {
  from {
    opacity: 0;
    transform: translateY(24px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes intro-line {
  from {
    transform: translateY(130%);
  }
  to {
    transform: none;
  }
}

@keyframes intro-pop {
  from {
    opacity: 0;
    transform: scale(0.4);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes intro-fade {
  from {
    opacity: 0;
    transform: translateY(-6px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* ---------- Scroll-linked depth ----------
   Fallback path first: `--p` is the element's progress through the viewport (0 = top edge enters
   at the bottom, 1 = bottom edge leaves at the top), written by scroll-progress.js. */

[data-parallax] {
  transform: translateY(calc((var(--p, 0.5) - 0.5) * var(--parallax-range, 40px) * var(--parallax-scale)));
}

[data-drift] {
  transform: translateX(calc((var(--p, 0.5) - 0.5) * var(--drift, 56px) * var(--parallax-scale)));
}

.divider > [data-drift]:nth-child(even) {
  --drift: -56px;
}

/* The hero exits: the field sinks slower than the page, the type lifts and fades */
.site-header .quilt-field {
  transform: translateY(calc(var(--p, 0) * 28%));
}

.hero__content {
  opacity: calc(1 - var(--p, 0) * 1.35);
  transform: translateY(calc(var(--p, 0) * -72px));
}

/* Scroll progress rule under the returning nav */
.nav__progress {
  clip-path: inset(0 calc(100% - var(--scroll-p, 0) * 100%) 0 0);
}

/* Native path: the same effects on the compositor, off the main thread */
/* `?motion=fallback` adds html.motion-fallback, which switches the native path off for review. */
@supports (animation-timeline: view()) {
  :root:not(.motion-fallback) [data-parallax] {
    transform: none;
    animation: parallax-y linear both;
    animation-timeline: view();
    animation-range: cover 0% cover 100%;
  }

  :root:not(.motion-fallback) [data-drift] {
    transform: none;
    animation: drift-x linear both;
    animation-timeline: view();
    animation-range: cover 0% cover 100%;
  }

  :root:not(.motion-fallback) .site-header {
    view-timeline-name: --hero;
  }

  :root:not(.motion-fallback) .site-header .quilt-field {
    transform: none;
    animation: hero-field-exit linear both;
    animation-timeline: --hero;
    animation-range: exit 0% exit 100%;
  }

  :root:not(.motion-fallback) .hero__content {
    opacity: 1;
    transform: none;
    animation: hero-content-exit linear both;
    animation-timeline: --hero;
    animation-range: exit 0% exit 100%;
  }

  :root:not(.motion-fallback) .nav__progress {
    clip-path: inset(0 100% 0 0);
    animation: progress-stitch linear both;
    animation-timeline: scroll(root);
  }

  @keyframes parallax-y {
    from {
      transform: translateY(calc(var(--parallax-range, 40px) * var(--parallax-scale) * -0.5));
    }
    to {
      transform: translateY(calc(var(--parallax-range, 40px) * var(--parallax-scale) * 0.5));
    }
  }

  @keyframes drift-x {
    from {
      transform: translateX(calc(var(--drift, 56px) * var(--parallax-scale) * -0.5));
    }
    to {
      transform: translateX(calc(var(--drift, 56px) * var(--parallax-scale) * 0.5));
    }
  }

  @keyframes hero-field-exit {
    to {
      transform: translateY(28%);
    }
  }

  @keyframes hero-content-exit {
    to {
      opacity: -0.35;
      transform: translateY(-72px);
    }
  }

  @keyframes progress-stitch {
    to {
      clip-path: inset(0 0 0 0);
    }
  }
}

/* ---------- Returning nav ---------- */

.nav-slot {
  min-height: var(--nav-h);
}

.nav__progress {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  background-color: var(--color-plum);
  opacity: 0;
  pointer-events: none;
}

.nav--fixed {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 50;
  --space-nav-y: clamp(0.625rem, 0.4rem + 0.6vw, 0.875rem);
  background-color: color-mix(in oklab, var(--color-surface) 86%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
  backdrop-filter: blur(14px) saturate(1.3);
  box-shadow: 0 1px 0 color-mix(in oklab, var(--color-navy) 10%, transparent);
  transform: translateY(-101%);
  transition:
    transform 460ms var(--ease-out-expo),
    background-color 300ms var(--ease-out-expo);
}

.nav--fixed.nav--shown {
  transform: none;
}

.nav--fixed .nav__progress {
  opacity: 1;
}

/* The header must out-stack the bands below it while its nav floats */
.site-header:has(.nav--fixed) {
  z-index: 50;
}

/* ---------- Buttons: a lift on hover, a press on click ---------- */

.btn {
  transition:
    background-color var(--duration-normal) var(--ease-out-expo),
    color var(--duration-normal) var(--ease-out-expo),
    box-shadow var(--duration-normal) var(--ease-out-expo),
    transform var(--duration-normal) var(--ease-spring);
}

.btn:active {
  transform: scale(0.97);
  transition-duration: var(--duration-fast);
}

@media (hover: hover) {
  .btn:hover {
    transform: translateY(-2px);
  }

  .btn:hover:active {
    transform: translateY(0) scale(0.97);
  }
}

/* ---------- Phones: shallower depth ---------- */

@media (max-width: 760px) {
  :root {
    --parallax-scale: 0.5;
  }
}

/* ---------- Rendering budget: bands far below the fold lay out when they are near ---------- */

.band--room,
.band--press,
.band--partners,
.band--involve {
  content-visibility: auto;
  contain-intrinsic-size: auto 800px;
}

/* ---------- Reduced motion: everything in place, nothing moves on scroll ---------- */

@media (prefers-reduced-motion: reduce) {
  .js [data-reveal],
  .js .stagger > *,
  .js [data-reveal]:has([data-split]) > *,
  .word > span,
  .js .intro,
  .js .hero__line-inner,
  .js .intro-patch,
  .js .brand,
  .js .nav__links,
  .js .nav__end {
    opacity: 1;
    transform: none;
    transition: none;
    animation: none;
  }

  :root [data-parallax],
  :root [data-drift],
  :root .site-header .quilt-field,
  :root .hero__content {
    animation: none;
    transform: none;
    opacity: 1;
  }

  :root .nav__progress {
    animation: none;
    clip-path: inset(0 calc(100% - var(--scroll-p, 0) * 100%) 0 0);
  }

  .nav--fixed,
  .btn {
    transition: none;
  }

  .btn:hover,
  .btn:active {
    transform: none;
  }
}
