/* Mobile motion keeps the same sequence while avoiding a new layout every frame. */
@media (max-width: 700px) and (min-height: 601px) and (prefers-reduced-motion: no-preference) {
  .js:not(.is-ready) .hero-word {
    transform: translate3d(-42px, 0, 0);
    filter: none;
  }

  .js.is-ready .hero-word {
    animation-name: hero-word-arrive-mobile;
    filter: none;
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
  }

  /* The inherited .5s durations and second-line delay retain the one-second entry. */
  @keyframes hero-word-arrive-mobile {
    from { opacity: 0; transform: translate3d(-42px, 0, 0); }
    to { opacity: 1; transform: translate3d(0, 0, 0); }
  }

  .motion .hero h1 {
    transform: translate3d(0, var(--hero-y, 0px), 0) scale(var(--hero-scale, 1));
    will-change: auto;
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
  }

  .motion .hero-scene.is-motion-active h1 {
    will-change: transform, opacity;
  }

  /* Prepare the two entry layers while the loader is still covering the page. */
  .js:not(.is-ready) .hero-word,
  .js.is-ready .hero-word:not(.is-entered) { will-change: transform, opacity; }

  .motion .newsletter h2 {
    filter: none;
    transform: translate3d(0, var(--newsletter-y, 45px), 0) scale(var(--newsletter-scale, .84));
    will-change: auto;
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
  }
  .motion .newsletter-scene.is-motion-active:not(.is-expanded) h2 { will-change: transform; }
  .motion .newsletter-scene.is-expanded h2 { transform: none; }

  /* Supported browsers bind title motion directly to native scrolling.
     Pixel ranges are measured only when the page layout changes. */
  @supports (animation-timeline: scroll(root block)) and (animation-range: 0px 1px) {
    .motion .hero h1 {
      animation: hero-zoom-mobile auto cubic-bezier(.333333, 0, .666667, 1) both,
        hero-fade-mobile auto cubic-bezier(.333333, 0, .666667, 1) both;
      animation-timeline: scroll(root block), scroll(root block);
      animation-range: var(--hero-start, 0px) var(--hero-end, 75svh),
        var(--hero-fade-start, 6svh) var(--hero-fade-end, 69svh);
    }
    .motion .newsletter-scene:not(.is-expanded) h2 {
      animation: focus-zoom-mobile auto cubic-bezier(.333333, 0, .666667, 1) both;
      animation-timeline: scroll(root block);
      animation-range: var(--focus-start, 0px) var(--focus-end, 1px);
    }
    .motion .newsletter-scene.is-expanded h2 { animation: none; transform: none; }
    @keyframes hero-zoom-mobile {
      from { transform: translate3d(0, 0, 0) scale(1); }
      to { transform: translate3d(0, -30px, 0) scale(1.28); }
    }
    @keyframes hero-fade-mobile { from { opacity: 1; } to { opacity: 0; } }
    @keyframes focus-zoom-mobile {
      from { transform: translate3d(0, 45px, 0) scale(.84); }
      to { transform: translate3d(0, 0, 0) scale(1.04); }
    }
  }

  /* Reserve the final biography space once; only its opacity and position move. */
  .motion .portal-stage {
    padding: 24px var(--gutter);
    gap: 20px;
  }

  .motion .portal-media.real-portal {
    width: min(
      calc(100vw - 2 * var(--gutter)),
      max(120px, calc(100svh - var(--header-height) - 68px - var(--bio-height, 250px)))
    );
    max-height: none;
    transform: translate3d(0, 0, 0) scale(var(--frame-scale, 1));
    will-change: auto;
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
  }

  .motion .portal-reveal {
    max-height: none;
    overflow: visible;
    transform: translate3d(0, calc(18px * (1 - var(--bio-progress, 0))), 0);
    will-change: auto;
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
  }

  .motion .portal-photo {
    transform: translate3d(0, var(--media-y, 0px), 0) scale(var(--media-scale, 1));
    will-change: auto;
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
  }

  .motion .portal-scene.is-motion-active .portal-media,
  .motion .portal-scene.is-motion-active .portal-photo {
    will-change: transform;
  }

  .motion .portal-scene.is-motion-active .portal-reveal {
    will-change: transform, opacity;
  }
}

/* Drop the completed entry filter and its animation layer after each word arrives. */
.js.is-ready .hero-word.is-entered { animation: none; opacity: 1; transform: none; filter: none; }
