.hero.scroll-explosion {
  min-height: 165svh;
  overflow: clip;
}

.hero.scroll-explosion .hero-grid {
  position: sticky;
  top: 78px;
  min-height: 0;
  height: calc(100svh - 78px);
  isolation: isolate;
}

.hero.scroll-explosion .hero-copy {
  position: relative;
  z-index: 7;
  opacity: 1 !important;
  transform: none !important;
  pointer-events: none;
}

.hero.scroll-explosion .hero-copy a {
  pointer-events: auto;
}

/*
 * The headline is on screen at rest so the blast has something to act on,
 * then gets thrown clear as the bomb goes off at progress .20.
 */
.hero.scroll-explosion .hero-headline {
  --headline-exit: clamp(0, calc((var(--hero-scroll-progress, 0) - .2) * 3.4), 1);
  opacity: calc(1 - var(--headline-exit));
  transform:
    translate3d(calc(var(--headline-exit) * -130px), 0, 0)
    rotate(calc(var(--headline-exit) * -4deg));
  filter: blur(calc(var(--headline-exit) * 8px));
  will-change: transform, opacity, filter;
}

/* The call to action arrives out of the clearing smoke, once it has settled. */
.hero.scroll-explosion .hero-copy .hero-actions,
.hero.scroll-explosion .hero-copy .hero-notes {
  --resolve: clamp(0, calc((var(--hero-scroll-progress, 0) - .62) * 4.4), 1);
  opacity: var(--resolve);
  transform: translateY(calc((1 - var(--resolve)) * 26px));
}

.hero.scroll-explosion .hero-art {
  position: absolute;
  inset: 0;
  z-index: 2;
  width: 100%;
  max-width: none;
  height: 100%;
  isolation: isolate;
  transform: translate3d(calc(clamp(0, calc((var(--hero-scroll-progress, 0) - .42) * 3.6), 1) * 25%), 0, 0);
  transition: none;
}

.hero-logo-stage {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: grid;
  place-items: center;
  pointer-events: none;
}

.hero-logo-stage .hero-logo {
  position: absolute;
  width: min(62vw, 720px);
  max-height: 76vh;
  margin: auto;
  transform-origin: 50% 44%;
  will-change: transform, opacity, filter;
  transition: none;
}

/*
 * The source artwork is flattened, so the animated split is traced around the
 * mascot instead of using a horizontal crop. This outline includes the fuse,
 * bomb, key, torso, both legs and both shoes while stopping above the BOOM
 * letter faces.
 */
.hero-logo-bomb-layer {
  clip-path: polygon(
    49% 5%, 56% 6%, 61% 10%, 65% 16%, 67% 24%, 66% 33%,
    63% 41%, 66% 47%, 69% 53%, 74% 58%, 78% 64%, 79% 69%,
    77% 73%, 73% 75%, 68% 74%, 63% 72%, 59% 74%, 57% 79%,
    54% 84%, 50% 87%, 46% 86%, 42% 82%, 39% 77%, 35% 74%,
    33% 69%, 34% 63%, 37% 58%, 40% 54%, 40% 49%, 37% 45%,
    34% 41%, 31% 38%, 29% 34%, 30% 29%, 34% 26%, 39% 25%,
    42% 19%, 45% 15%, 46% 9%
  );
}

/* Keep everything except the traced mascot stationary. */
.hero-logo-wordmark-layer {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpath fill='white' fill-rule='evenodd' d='M0 0H100V100H0ZM49 5L56 6 61 10 65 16 67 24 66 33 63 41 66 47 69 53 74 58 78 64 79 69 77 73 73 75 68 74 63 72 59 74 57 79 54 84 50 87 46 86 42 82 39 77 35 74 33 69 34 63 37 58 40 54 40 49 37 45 34 41 31 38 29 34 30 29 34 26 39 25 42 19 45 15 46 9Z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpath fill='white' fill-rule='evenodd' d='M0 0H100V100H0ZM49 5L56 6 61 10 65 16 67 24 66 33 63 41 66 47 69 53 74 58 78 64 79 69 77 73 73 75 68 74 63 72 59 74 57 79 54 84 50 87 46 86 42 82 39 77 35 74 33 69 34 63 37 58 40 54 40 49 37 45 34 41 31 38 29 34 30 29 34 26 39 25 42 19 45 15 46 9Z'/%3E%3C/svg%3E");
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  transform-origin: 50% 78%;
}

.hero-explosion-canvas {
  position: absolute;
  inset: 0;
  z-index: 5;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.hero-explosion-flash {
  position: absolute;
  z-index: 3;
  left: 50%;
  top: 43%;
  width: min(42vw, 390px);
  aspect-ratio: 1;
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, -50%) scale(.6);
  background: radial-gradient(circle,
    rgba(255,255,255,.95) 0 3%,
    rgba(225,207,238,.72) 10%,
    rgba(143,114,168,.35) 34%,
    transparent 69%);
  filter: blur(5px);
  mix-blend-mode: screen;
}

.hero.scroll-explosion .spin-wheel,
.hero.scroll-explosion .orbit {
  will-change: transform, opacity;
}

/*
 * Ambient rotation keeps running until the user actually scrolls, so the hero
 * is not frozen on arrival. Scroll position then takes over the transforms.
 */
.hero.scroll-explosion.scroll-engaged .spin-wheel,
.hero.scroll-explosion.scroll-engaged .orbit {
  animation-play-state: paused;
}

/* Visible on arrival: this is the label that tells Boom it is a mockup. */
.hero.scroll-explosion .prototype-stamp {
  opacity: calc(1 - clamp(0, calc((var(--hero-scroll-progress, 0) - .66) * 3), 1) * .7);
}

.hero.scroll-explosion .scroll-cue {
  top: calc(100svh - 112px);
  bottom: auto;
  z-index: 9;
  opacity: calc(1 - clamp(0, calc(var(--hero-scroll-progress, 0) * 7), 1));
  animation: scroll-cue-nudge 2.4s ease-in-out infinite;
}

.hero.scroll-explosion.scroll-engaged .scroll-cue {
  animation: none;
}

@keyframes scroll-cue-nudge {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(6px); }
}

@media (max-width: 1000px) {
  .hero.scroll-explosion {
    min-height: 155svh;
  }

  .hero.scroll-explosion .hero-grid {
    position: sticky;
    top: 78px;
    height: calc(100svh - 78px);
    min-height: 0;
    overflow: hidden;
  }

  /*
   * Single-column layouts stack, so the full-bleed absolute .hero-art used on
   * desktop lands straight on top of the copy. Put the art and the copy in
   * two real grid rows instead: logo above, words below, no overlap.
   */
  .hero.scroll-explosion .hero-grid {
    grid-template-columns: 1fr;
    grid-template-rows: minmax(0, 1fr) auto;
    align-content: stretch;
  }

  .hero.scroll-explosion .hero-art {
    position: relative;
    grid-row: 1;
    inset: auto;
    width: 100%;
    height: auto;
    min-height: 0;
    align-self: stretch;
    transform: translate3d(0, calc(clamp(0, calc((var(--hero-scroll-progress, 0) - .44) * 3.4), 1) * -12%), 0) scale(calc(1 - clamp(0, calc((var(--hero-scroll-progress, 0) - .44) * 3.4), 1) * .22));
  }

  .hero.scroll-explosion .hero-copy {
    grid-row: 2;
    align-self: end;
    padding-bottom: 8vh;
  }

  .hero-logo-stage .hero-logo {
    width: min(72vw, 480px);
    max-height: 100%;
  }

  /* The logo fills the art row now, so the stamp cannot sit under it. */
  .hero.scroll-explosion .prototype-stamp {
    top: 12px;
    right: 10px;
    bottom: auto;
  }

  /*
   * In the stacked layout these already occupy their space, so holding them
   * at opacity 0 until the smoke clears just leaves a dead gap under the
   * headline. Show the call to action from the start instead.
   */
  .hero.scroll-explosion .hero-copy .hero-actions,
  .hero.scroll-explosion .hero-copy .hero-notes {
    opacity: 1;
    transform: none;
  }
}

@media (max-width: 720px) {
  .hero.scroll-explosion {
    min-height: 150svh;
  }

  .hero.scroll-explosion .hero-grid {
    padding-top: 4px;
    padding-bottom: 0;
  }

  .hero.scroll-explosion .prototype-stamp {
    padding: 6px 8px;
    font-size: .54rem;
    letter-spacing: .14em;
  }

  .hero.scroll-explosion .hero-copy {
    padding: 0 20px 86px;
  }

  .hero-logo-stage .hero-logo {
    width: min(82vw, 420px);
    max-height: 100%;
  }

  .hero-explosion-flash {
    width: min(76vw, 340px);
  }

  /*
   * Phones previously got no scroll affordance at all, which left the opening
   * as a logo with no instruction. Centre it under the copy instead.
   */
  .hero.scroll-explosion .scroll-cue {
    display: block;
    left: 0;
    right: 0;
    top: calc(100svh - 96px);
    text-align: center;
  }

  .hero.scroll-explosion .hero-headline {
    --headline-exit: clamp(0, calc((var(--hero-scroll-progress, 0) - .2) * 3.4), 1);
    transform:
      translate3d(0, calc(var(--headline-exit) * -46px), 0)
      rotate(calc(var(--headline-exit) * -3deg));
  }

}

@media (prefers-reduced-motion: reduce) {
  .hero.scroll-explosion {
    min-height: calc(100svh - 78px);
  }

  .hero.scroll-explosion .hero-grid {
    position: relative;
    top: auto;
    height: auto;
    min-height: calc(100svh - 78px);
  }

  .hero.scroll-explosion .hero-copy {
    opacity: 1 !important;
    transform: none !important;
    pointer-events: auto;
  }

  /*
   * --hero-scroll-progress is never set on this path, so the scroll-driven
   * states would resolve to "blown away" and "not yet arrived". Show the
   * settled state instead.
   */
  .hero.scroll-explosion .hero-headline {
    opacity: 1;
    transform: none;
    filter: none;
  }

  .hero.scroll-explosion .hero-copy .hero-actions,
  .hero.scroll-explosion .hero-copy .hero-notes {
    opacity: 1;
    transform: none;
  }

  .hero.scroll-explosion .prototype-stamp {
    opacity: 1;
  }

  .hero.scroll-explosion .scroll-cue {
    opacity: 1;
    animation: none;
  }

  .hero.scroll-explosion .hero-art {
    position: relative;
    inset: auto;
    width: auto;
    height: auto;
    transform: none;
  }

  .hero-logo-stage .hero-logo {
    width: min(100%, 620px);
  }

  .hero-logo-bomb-layer,
  .hero-logo-wordmark-layer {
    clip-path: none;
    -webkit-mask-image: none;
    mask-image: none;
  }

  .hero-logo-bomb-layer {
    display: none;
  }

  .hero-explosion-canvas,
  .hero-explosion-flash {
    display: none;
  }
}