/* Motion: one hero sequence on load (a sunrise), and a slow parallax on the
   photos inside their figures while the page scrolls. Both only run for
   visitors without reduced motion, and the parallax only where
   scroll-driven animations are supported. The page reads the same
   without either. */

@media (prefers-reduced-motion: no-preference) {
  /* Hero on load, a sunrise over the horizon:
     1. a bordeaux sun rises from below the render's bottom edge until
        it sits on it as a half circle,
     2. it grows until it fills the frame, warming through rose to a
        light rose and fading into the photo as it goes,
     3. as it starts to grow, the header, the wordmark and the specs
        line come in.
     The figure carries this, so the photo inside stays free for the
     parallax. */
  .home-hero__media {
    position: relative;
    animation: theme-sunrise 1.9s both;
  }

  .home-hero__media::after {
    content: '';
    position: absolute;
    inset: 0;
    background: var(--theme-color-primary);
    pointer-events: none;
    animation: theme-sun-glow 1.9s linear both;
  }

  .site-header > *,
  .home-hero__bottom {
    animation: theme-settle 0.9s var(--theme-ease-signature) both;
  }

  /* These start as the sun starts to grow (32% of 1.9s), not once the
     photo has settled. */
  .site-header > * {
    animation-delay: 0.65s;
  }

  /* The clip hides the wordmark while it rises; main.js lifts it once
     the rise is done, so the springy shapes can move past its edges. */
  .home-hero__title:not([data-intro-done]) {
    overflow: clip;
  }

  /* The wordmark builds from the outside in: the outer bars first,
     the middle letter last, each sliding up out of the line. */
  .wordmark-part {
    transform-box: fill-box;
    animation: theme-rise 0.9s var(--theme-ease-signature) both;
    animation-delay: calc(0.75s + var(--theme-wordmark-step) * 70ms);
  }

  .wordmark-part--0 { --theme-wordmark-step: 0; }
  .wordmark-part--1 { --theme-wordmark-step: 1; }
  .wordmark-part--2 { --theme-wordmark-step: 2; }
  .wordmark-part--3 { --theme-wordmark-step: 3; }
  .wordmark-part--4 { --theme-wordmark-step: 4; }
  .wordmark-part--5 { --theme-wordmark-step: 5; }
  .wordmark-part--6 { --theme-wordmark-step: 6; }
  .wordmark-part--7 { --theme-wordmark-step: 7; }
  .wordmark-part--8 { --theme-wordmark-step: 8; }
  .wordmark-part--9 { --theme-wordmark-step: 9; }

  .home-hero__bottom {
    animation-delay: 1.3s;
  }

  /* The sun starts below the frame, rises until its centre sits on the
     bottom edge (a half circle on the horizon), then grows. The radius
     is a percentage of the frame's diagonal over root 2; from the bottom
     middle, 150% reaches the top corners of a frame of any shape (a
     tall phone frame needs ~127%, the limit is ~141%), so the end state
     clips nothing. */
  @keyframes theme-sunrise {
    0% {
      clip-path: circle(9% at 50% 118%);
      animation-timing-function: cubic-bezier(0.25, 0, 0.2, 1);
    }

    32% {
      clip-path: circle(9% at 50% 100%);
      animation-timing-function: cubic-bezier(0.6, 0, 0.2, 1);
    }

    100% {
      clip-path: circle(150% at 50% 100%);
    }
  }

  /* The sun rises bordeaux, warms to rose and a light rose while it
     grows, then fades into the photo. */
  @keyframes theme-sun-glow {
    0%,
    30% {
      background-color: var(--theme-color-primary);
      opacity: 1;
    }

    55% {
      background-color: var(--theme-color-secondary);
      opacity: 1;
    }

    75% {
      background-color: color-mix(in srgb, var(--theme-color-secondary) 45%, var(--theme-color-highlight));
      opacity: 0.85;
    }

    100% {
      background-color: color-mix(in srgb, var(--theme-color-secondary) 45%, var(--theme-color-highlight));
      opacity: 0;
    }
  }

  @keyframes theme-settle {
    from {
      opacity: 0;
      translate: 0 12px;
    }

    to {
      opacity: 1;
      translate: 0 0;
    }
  }

  @keyframes theme-rise {
    from {
      translate: 0 110%;
    }

    to {
      translate: 0 0;
    }
  }
}

/* Parallax: the photo is a little larger than its figure and drifts
   against the scroll while the figure crosses the screen. Scale 1.16
   leaves 8% spare on each side, the drift uses 6%. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    [data-parallax] {
      overflow: clip;
    }

    [data-parallax] img {
      scale: 1.16;
      animation: theme-parallax linear both;
      animation-timeline: view();
      animation-range: cover;
    }

    @keyframes theme-parallax {
      from {
        translate: 0 -6%;
      }

      to {
        translate: 0 6%;
      }
    }
  }
}

/* Scroll reveals after the hero, in the hero's own moves: text and rows
   settle in like the header and specs line, images fade in slightly
   enlarged and ease down to their size like the render. Each one runs
   while its element enters the screen, eased like the hero. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* Ranges are measured against the screen, not the element, so a
       short row still takes a good stretch of scrolling to settle and
       is seen doing it. */
    /* A gentle ease-out: the hero's signature curve does almost all of
       its change at the start, which on a scroll looks like no reveal. */
    [data-reveal] {
      animation: theme-settle-far cubic-bezier(0.3, 0.4, 0.4, 1) both;
      animation-timeline: view();
      animation-range: entry 0% cover 40%;
    }

    [data-reveal='image'] {
      transform-origin: 50% 60%;
      animation-name: theme-reveal-image;
      animation-range: entry 0% cover 50%;
    }

    @keyframes theme-settle-far {
      from {
        opacity: 0;
        translate: 0 40px;
      }

      to {
        opacity: 1;
        translate: 0 0;
      }
    }

    @keyframes theme-reveal-image {
      from {
        opacity: 0;
        scale: 1.06;
      }

      to {
        opacity: 1;
        scale: 1;
      }
    }
  }
}

/* Intro render: the photo is fixed (pages/home.css), so it reads the
   frame's timeline for its parallax. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .home-intro__media[data-parallax] img {
      animation-timeline: --theme-intro-media;
    }
  }
}

/* Footer: it sticks behind the page, so its parts never move through
   the screen and a view timeline would not fire. They settle in on the
   page's own scroll instead, over the last screen of it, while the page
   slides off the footer: title first, then the two columns, then the
   credits. */
@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .site-footer__title,
    .site-footer__timeline,
    .site-footer__contact,
    .site-footer__inner {
      animation: theme-settle var(--theme-ease-signature) both;
      animation-timeline: scroll(root);
    }

    .site-footer__title {
      animation-range: calc(100% - 90svh) calc(100% - 45svh);
    }

    .site-footer__timeline {
      animation-range: calc(100% - 80svh) calc(100% - 30svh);
    }

    .site-footer__contact {
      animation-range: calc(100% - 70svh) calc(100% - 20svh);
    }

    .site-footer__inner {
      animation-range: calc(100% - 50svh) 100%;
    }
  }
}

/* Browsers without scroll-driven animations (Firefox, older Safari):
   main.js marks the page and an IntersectionObserver marks each element
   once it is on screen, which plays the same settle as a transition.
   Without JavaScript nothing is hidden. */
@supports not (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    [data-reveal-mode='observer'] [data-reveal] {
      opacity: 0;
      translate: 0 40px;
      transition:
        opacity 1s var(--theme-ease-signature),
        translate 1.2s var(--theme-ease-signature);
    }

    [data-reveal-mode='observer'] [data-reveal='image'] {
      translate: none;
      scale: 1.06;
      transition:
        opacity 1.2s var(--theme-ease-signature),
        scale 1.6s var(--theme-ease-signature);
    }

    [data-reveal-mode='observer'] [data-reveal][data-revealed] {
      opacity: 1;
      translate: 0 0;
      scale: 1;
    }
  }
}

/* Titles: each rendered line is clipped (main.js wraps them), and its
   text rises up out of it, one line after the other, like the wordmark.
   Without JavaScript the title is plain text. */
.reveal-line {
  display: block;
  overflow: clip;

  /* Room for descenders inside the clip, without changing the spacing. */
  padding-bottom: 0.12em;
  margin-bottom: -0.12em;
}

.reveal-line__inner {
  display: block;
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    [data-reveal-lines] {
      view-timeline: --theme-title block;
    }

    [data-reveal-lines] .reveal-line__inner {
      animation: theme-line-up cubic-bezier(0.3, 0.4, 0.4, 1) both;
      animation-timeline: --theme-title;
      animation-range:
        entry calc(var(--theme-line-index) * 25%)
        cover calc(30% + var(--theme-line-index) * 6%);
    }

    @keyframes theme-line-up {
      from {
        translate: 0 110%;
      }

      to {
        translate: 0 0;
      }
    }
  }
}

@supports not (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    [data-reveal-mode='observer'] [data-reveal-lines] .reveal-line__inner {
      translate: 0 110%;
      transition: translate 1.1s var(--theme-ease-signature);
      transition-delay: calc(var(--theme-line-index) * 110ms);
    }

    [data-reveal-mode='observer'] [data-reveal-lines][data-revealed] .reveal-line__inner {
      translate: 0 0;
    }
  }
}

