/* ==========================================================================
   Scroll reveal

   The mobile nav's menu animation, generalised to page content: each block
   resolves out of a defocus as it fades and rises, blocks arriving together
   stepping one after another down the page. css/nav.css holds the original
   and the reasoning behind the curve; this file reuses both so the site has
   one idea of how things arrive.

   Three rules govern the whole thing:

   1. Nothing here applies until js/reveal.js adds .js-reveal to <html> and
      tags the blocks it has chosen with [data-reveal]. A visitor with no
      JavaScript, or one whose reveal.js failed to load, gets the page with
      every block already in place. The same gate the work grid uses.

   2. The transition lives on the revealed state, never on the pending one.
      A block is hidden by a :not(.is-revealed) rule, so adding the class
      removes the declarations rather than overriding them and the block
      transitions back to whatever opacity, transform and filter it natively
      had. That matters on /work, where .index-row carries its own opacity
      for the hover dimming: a reveal that ended by asserting opacity: 1
      would win the cascade forever and the dimming would never come back.

   3. js/reveal.js strips [data-reveal] once the transition ends, so a block
      that has arrived carries no reveal styling at all. Rule 2 keeps the
      cascade honest during the animation; rule 3 makes it moot afterwards.

   Reduced motion needs no rule here. The pending state is keyed to
   [data-reveal], reveal.js declines to set it when the query matches, and
   nothing is ever hidden in the first place.
   ========================================================================== */

.js-reveal [data-reveal] {
  /* Per-block defaults, overridden by the tone rules below. A custom
     property rather than three variants of the same rule: the tones differ
     only in how far the block travels and how far out of focus it starts. */
  --reveal-blur: 4px;
  --reveal-lift: 14px;
}

/* Qualified with html rather than left bare: /work declares its hover
   dimming as .index-list:hover .index-row:not(:hover), which ties this on
   specificity and sits in a later <style> block. Without the extra element
   selector, pointing at the list would surface rows that have not arrived
   yet at 38% opacity. */
html.js-reveal [data-reveal]:not(.is-revealed) {
  opacity: 0;
  transform: translate3d(0, var(--reveal-lift), 0);
  filter: blur(var(--reveal-blur));
}

/* Same curve and the same near-enough-equal lengths as the nav menu: the
   three channels resolve as one gesture, with transform given the extra
   time because the end of its travel is the part nobody can see.

   No will-change. The nav could afford it on four links; a case page tags
   fifteen or more blocks, and promoting every one of them to its own layer
   for the whole time it sits below the fold costs more than the first frame
   of each transition does. */
html.js-reveal [data-reveal].is-revealed {
  transition:
    opacity .8s cubic-bezier(.4, 0, .2, 1),
    transform .95s cubic-bezier(.4, 0, .2, 1),
    filter .8s cubic-bezier(.4, 0, .2, 1);
}

/* --------------------------------------------------------------------------
   Fallbacks

   js/reveal.js measures both of these off the block itself — blur and travel
   scale with its type size, so a 48px heading resolves from further out of
   focus than a column of body copy, and a full-bleed image further still.
   It writes them as inline custom properties, which is also why the values
   here are only a floor: they apply for the frame before the script's own
   pass lands, and to anything it somehow could not measure.
   -------------------------------------------------------------------------- */
