/* ============ THE ARRIVAL: one grammar for the whole site ============

   Every page-entrance on pentinian.com speaks this vocabulary and no other.
   It grew up as three dialects, the Foyer's clock, the section sweep, the
   studies' own observer, written in separate passes and agreeing only by
   coincidence. This file is the treaty.

   The grammar:
     .rise          hidden until let up: 16px below its seat, fading in.
     .rise--far     a longer throw (26px), for large set pieces.
     .rise--blur    resolves out of blur as it rises. Headlines only;
                    blur is the voice of a name arriving, not a paragraph.
     .rise--spring  short throw with a slight overshoot. Small chips only.
     .rose          the landed state. The engine adds it, then strips .rise
                    entirely, handing transitions back to whoever owns them:
                    a chip that has arrived hovers on the chip's clock.

   The rules that make it safe, in order of importance:
     1. The hidden state lives behind .anim on <html>, written by one line in
        each page's head. No script, no .anim, no hidden state: the page
        renders as authored. This is not a nicety: six live pages once served
        a masthead over ten thousand characters of nothing because a reveal
        skipped this rule.
     2. Nothing below the fold is revealed by an IntersectionObserver. An
        observer misses an element outright when the viewport crosses it in a
        single frame (a #hash link, a restored scroll, an end-of-page keypress),
        and what it misses stays invisible forever. The engine sweeps instead:
        it asks where things are now.
     3. The first landing waits two animation frames, so the hidden state is
        actually painted before anything moves. Without the hold, a small page
        parses entirely before first paint and every element jumps instead of
        rising, a bug the Foyer's 310KB masked completely and an 8KB page
        exposed in seconds.
     4. Reduced motion is answered here, once, for every page at once.

   One scale. If a new easing or distance feels needed, the question to ask is
   why this element is not one of the four kinds above. */

:root{
  --mo-ease: cubic-bezier(.22,.72,.3,1);
  --mo-spring: cubic-bezier(.28,1.3,.5,1);
  --mo-op: .8s;    /* opacity duration   */
  --mo-tf: 1s;     /* transform duration */
  --mo-y: 16px;    /* the standard throw */
  /* The page's own inertia: the fifth kind. How hard the scroll position
     chases the wheel each frame; smaller is heavier. The distance is always
     one to one, only the catch up carries weight, so a flick settles in
     roughly an eighth of a second and nothing ever floats. */
  --mo-drag: .16;
}

.anim .rise{
  opacity:0;
  transform:translateY(var(--ry, var(--mo-y)));
  transition:opacity var(--mo-op) var(--mo-ease) var(--rd, 0s),
             transform var(--mo-tf) var(--mo-ease) var(--rd, 0s),
             filter .9s var(--mo-ease) var(--rd, 0s);
}
.anim .rise--far{--ry:26px}
.anim .rise--blur{--ry:26px; filter:blur(10px)}
.anim .rise--spring{--ry:9px;
  transition:opacity .5s ease var(--rd, 0s),
             transform .6s var(--mo-spring) var(--rd, 0s);
}
.anim .rise.rose{opacity:1; transform:none; filter:none}

@media (prefers-reduced-motion:reduce){
  .anim .rise{opacity:1 !important; transform:none !important;
    filter:none !important; transition:none !important}
}
