@layer motion {
  /* Everything here needs scroll-driven animations. Browsers without them,
     and visitors who prefer reduced motion, keep the static layout.
     [data-static] is only set by tests, to look at that layout in a
     browser that has scroll timelines. */
  @supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) {
      /* Header: transparent over the hero, solid once the page scrolls. */
      :root:not([data-static]) .bar {
        animation: bar-solid linear both;
        animation-timeline: scroll(root);
        animation-range: 0 45vh;
      }

      :root:not([data-static]) .bar__progress {
        display: block;
        animation: progress linear both;
        animation-timeline: scroll(root);
      }

      @keyframes bar-solid {
        from {
          border-bottom-color: transparent;
          background-color: rgb(14 7 24 / 0);
          -webkit-backdrop-filter: none;
          backdrop-filter: none;
        }
      }

      @keyframes progress {
        from { transform: scaleX(0); }
        to { transform: scaleX(1); }
      }

      /* Hero: the layers drift apart while it scrolls away. */
      :root:not([data-static]) .hero {
        view-timeline: --hero block;
      }

      :root:not([data-static]) .hero__art--back {
        animation: hero-back linear both;
        animation-timeline: --hero;
        animation-range: exit 0% exit 100%;
      }

      :root:not([data-static]) .hero__art--front {
        animation: hero-front linear both;
        animation-timeline: --hero;
        animation-range: exit 0% exit 100%;
      }

      :root:not([data-static]) .hero__copy,
      :root:not([data-static]) .modem-line {
        animation: hero-copy linear both;
        animation-timeline: --hero;
        animation-range: exit 0% exit 60%;
      }

      @keyframes hero-back {
        to { translate: 0 12vh; }
      }

      @keyframes hero-front {
        to { translate: 0 -14vh; opacity: 0.2; }
      }

      @keyframes hero-copy {
        to { translate: 0 -8vh; opacity: 0; }
      }

      /* Terminal: a tall section around a sticky stage. The terminal rises
         out of the floor, turns to the front, then the view zooms into the
         CRT and fades into the About window. */
      :root:not([data-static]) .demo {
        height: 340vh;
        view-timeline: --demo block;
      }

      :root:not([data-static]) .demo__stage {
        position: sticky;
        top: 0;
        height: 100svh;
        min-height: 0;
      }

      :root:not([data-static]) .term-zoom {
        --sx-end: calc(-13vw + 2.6em);
        --sy-end: calc(-2svh + 5.8em);
        --zoom-end: calc(
          max(tan(atan2(100vw, 28em)), tan(atan2(100svh, 21em))) * 1.04
        );
        animation: rig linear both;
        animation-timeline: --demo;
        animation-range: entry 0% contain 100%;
      }

      /* --zoom-end makes the 28em x 21em CRT cover the viewport. */
      @keyframes rig {
        0% {
          --yaw: -44deg;
          --pitch: -26deg;
          --lift: 72svh;
          --zoom: 0.78;
          --tiltk: 1;
          --sx: 0px;
          --sy: 0px;
        }
        29% {
          --yaw: -24deg;
          --pitch: -9deg;
          --lift: 0px;
          --zoom: 1;
          --sx: 0px;
          --sy: 0px;
        }
        46% {
          --yaw: -10deg;
          --pitch: -5deg;
          --tiltk: 1;
        }
        58% {
          --yaw: 0deg;
          --pitch: -1deg;
          --zoom: 1.02;
          --tiltk: 0.3;
          --sx: 0px;
          --sy: 0px;
        }
        68% {
          --yaw: 0deg;
          --pitch: 0deg;
          --zoom: 1.2;
          --tiltk: 0;
          --sx: var(--sx-end);
          --sy: var(--sy-end);
        }
        92%,
        100% {
          --yaw: 0deg;
          --pitch: 0deg;
          --lift: 0px;
          --zoom: var(--zoom-end);
          --tiltk: 0;
          --sx: var(--sx-end);
          --sy: var(--sy-end);
        }
      }

      /* The video stays out of the layout until the page has scrolled 8vh.
         With loading="lazy", Chrome 148 and newer fetch it from then on
         instead of with the first view. */
      :root:not([data-static]) .crt video {
        animation: video-on linear both;
        animation-timeline: scroll(root);
        animation-range: 0 8vh;
      }

      @keyframes video-on {
        0%, 99% { display: none; }
        100% { display: block; }
      }

      :root:not([data-static]) .tm-floor {
        animation: floor-out linear both;
        animation-timeline: --demo;
        animation-range: entry 0% contain 100%;
      }

      @keyframes floor-out {
        0%, 64% { opacity: 1; visibility: visible; }
        76% { opacity: 0; visibility: visible; }
        77%, 100% { opacity: 0; visibility: hidden; }
      }

      /* Parts that have left the frame during the zoom stop being drawn,
         which saves the rasterizer from scaling them up. */
      :root:not([data-static]) :is(.tm-kb, .tm-neck, .tm-head > .tm-f--left,
          .tm-head > .tm-f--right, .tm-head > .tm-f--top, .tm-chamfer,
          .tm-corner) {
        animation: parts-out linear both;
        animation-timeline: --demo;
        animation-range: entry 0% contain 100%;
      }

      @keyframes parts-out {
        0%, 79% { visibility: visible; }
        80%, 100% { visibility: hidden; }
      }

      :root:not([data-static]) .demo__copy {
        display: grid;
      }

      :root:not([data-static]) .demo__cap {
        grid-area: 1 / 1;
        margin: 0;
        animation: cap linear both;
        animation-timeline: --demo;
        animation-range: entry 0% contain 100%;
      }

      :root:not([data-static]) .demo__cap--b {
        animation-name: cap-b;
      }

      @keyframes cap {
        0%, 27% { opacity: 0; translate: 0 2rem; }
        33%, 44% { opacity: 1; translate: 0 0; }
        50%, 100% { opacity: 0; translate: 0 -2rem; }
      }

      @keyframes cap-b {
        0%, 47% { opacity: 0; translate: 0 2rem; }
        52%, 57% { opacity: 1; translate: 0 0; }
        62%, 100% { opacity: 0; translate: 0 -2rem; }
      }

      :root:not([data-static]) .demo__fade {
        animation: fade-in linear both;
        animation-timeline: --demo;
        animation-range: entry 0% contain 100%;
      }

      @keyframes fade-in {
        0%, 95% { opacity: 0; }
        100% { opacity: 1; }
      }

      /* The sun rises while the footer comes into view. */
      :root:not([data-static]) .dawn__sun {
        animation: sunrise linear both;
        animation-timeline: view();
        animation-range: entry 0% cover 55%;
      }

      @keyframes sunrise {
        from { translate: -50% 90%; }
        to { translate: -50% 12%; }
      }

      @media (width < 52rem) {
        :root:not([data-static]) .demo__stage {
          padding: 0;
          perspective-origin: 50% 40%;
        }

        /* A portrait screen can't show a 4:3 CRT filled to its height,
           so here it fills the width. */
        :root:not([data-static]) .term-zoom {
          --sx-end: 2.6em;
          --sy-end: calc(-10svh + 5.8em);
          --zoom-end: calc(tan(atan2(100vw, 28em)) * 1.06);
          position: absolute;
          left: 50%;
          top: 60%;
          height: 0;
          margin: 0;
        }

        :root:not([data-static]) .term-rig {
          position: static;
        }

        :root:not([data-static]) .demo__copy {
          position: absolute;
          inset: calc(var(--bar-h) + 1.5rem) var(--gutter) auto;
        }
      }
    }
  }

  @media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
      animation: none !important;
      transition: none !important;
    }

    html {
      scroll-behavior: auto;
    }

    /* The recording keeps playing even without animations, so it gives way
       to its poster and a link to the file. */
    .crt video {
      display: none;
    }

    .crt__poster {
      display: block;
    }

    .demo__file {
      position: static;
      width: auto;
      height: auto;
      margin-top: 1.5rem;
      overflow: visible;
      clip-path: none;
      white-space: normal;
      color: var(--ink-2);
      pointer-events: auto;
    }
  }
}
