.h-hero {
  /* The scroll the frame stays pinned for while the screenshot flies in. */
  --h-hero-scroll-distance: 90svh;
  padding: 0;
  overflow: visible;
}

.h-hero-viewport {
  position: relative;
  padding-block: calc(var(--nav-h) + var(--ox-space) * 18) calc(var(--ox-space) * 8);
  row-gap: calc(var(--ox-space) * 8);
}

.h-hero .h-runs-stage {
  aspect-ratio: auto;
  min-height: 0;
  overflow: auto;
  -webkit-mask-image: none;
  mask-image: none;
}

.h-hero .h-app {
  position: relative;
  transform: none;
}

/* A phone keeps the cropped screenshot from home.css: a fixed-height stage,
   the Runs page drawn at --h-app-z and running off the right edge, and a fade
   at the foot. The stage is wider than a phone screen, so the hero cuts the
   screenshot at the screen's edge and the page never scrolls sideways. */
.h-hero[data-h-hero-crop] {
  overflow-x: clip;
}

.h-hero[data-h-hero-crop] .h-runs-stage {
  aspect-ratio: var(--h-app-w) / var(--h-app-h);
  min-height: calc(var(--h-app-h) * var(--h-app-min) * 1px);
  overflow: clip;
  -webkit-mask-image: linear-gradient(to bottom, #000 74%, transparent);
  mask-image: linear-gradient(to bottom, #000 74%, transparent);
}

.h-hero[data-h-hero-crop] .h-app {
  position: absolute;
  /* Sits just past the stage's left edge, so a little less of it shows. */
  left: 10%;
  transform: scale(var(--h-app-z));
}

.h-hero[data-h-hero-fit] .h-runs {
  width: var(--h-hero-graphic-width);
}

.h-hero[data-h-hero-fit] .h-runs-stage {
  height: var(--h-hero-graphic-height);
  overflow: clip;
}

/* The stage cuts off the foot of the screenshot and dissolves what is left
   into the page, so no hard bottom edge shows and the page around it stays in
   view even at its closest. */
.h-hero[data-h-hero-fit] .h-runs-stage {
  -webkit-mask-image: linear-gradient(to bottom, #000 60%, transparent);
  mask-image: linear-gradient(to bottom, #000 60%, transparent);
}

.h-hero[data-h-hero-fit] .h-app {
  position: absolute;
  transform: scale(var(--h-app-z));
}

/* The pinned frame ends one bottom padding under the screenshot at its
   closest, as hero-scroll.js measures it, so the next section follows the
   screenshot up the page with no empty screen between them. The frame does
   not clip, so the screenshot at rest still runs to the bottom of the screen.
   The hero clips it instead, at the edge where the next section starts. */
.h-hero[data-h-hero-scroll] {
  height: calc(
    var(--h-hero-frame-height, 100svh) + var(--h-hero-scroll-distance)
  );
  overflow: clip;
}

.h-hero[data-h-hero-scroll] .h-hero-viewport {
  position: sticky;
  top: 0;
  box-sizing: border-box;
  height: var(--h-hero-frame-height, 100svh);
}

.h-hero[data-h-hero-scroll] .h-hero-copy {
  opacity: var(--h-hero-copy-opacity);
  transform: translate3d(0, var(--h-hero-copy-y), 0);
  will-change: transform, opacity;
}

.h-hero[data-h-hero-scroll] .h-runs {
  position: absolute;
  inset: 0 0 auto;
  margin: 0 auto;
  /* Starts face on, tips its top toward the reader as it flies in, lies face
     on again at its closest, then lifts up and off the top. hero-scroll.js
     sets every value. */
  transform: translate3d(0, var(--h-hero-graphic-y), 0) perspective(1600px)
    rotateX(var(--h-hero-graphic-tilt, 0deg)) scale(var(--h-hero-graphic-scale));
  transform-origin: center center;
  will-change: transform;
}

@media (max-width: 768px) {
  .h-hero-viewport {
    padding-top: calc(var(--nav-h) + var(--ox-space) * 8);
  }
}

@media (prefers-reduced-motion: reduce) {
  .h-hero[data-h-hero-scroll] {
    height: auto;
  }

  .h-hero[data-h-hero-scroll] .h-hero-viewport {
    position: relative;
    height: auto;
    overflow: visible;
  }

  .h-hero[data-h-hero-scroll] .h-hero-copy,
  .h-hero[data-h-hero-scroll] .h-runs {
    position: relative;
    opacity: 1;
    transform: none;
    will-change: auto;
  }
}
