/* =============================================================================
   BAR FIVE RAFTER PUBLISHING CO. — motion.css
   The finish pass, 2026-09-01. Loads after press.css and finish.css and
   owns nothing but movement. motion.js is its other half: this file holds
   every duration and easing, that file decides WHEN and says so with a
   class.

   -----------------------------------------------------------------------------
   THE LAW OF THIS FILE
   -----------------------------------------------------------------------------
   The owner's verdict on the honest, working page was "it feels very basic
   still." What it lacked was not a word or a color but finish, and the
   measure is the house's other sites: motion with physics and never a dead
   stop; depth from luminance steps and hairlines, never a shadow; one
   earned flourish; ink at rest, color as reward. The reader it is paid to
   is press.css's — often over seventy, on an iPad or a phone, and easily
   startled. So:

   1. THE WHOLE FILE IS INSIDE ONE MEDIA BLOCK. A reader who has asked the
      device for less motion gets none of it — no sticky bar, no fade, no
      drawn line, no press. Not shorter: none. press.css §15 already zeroes
      every duration under that preference; this file does not lean on it,
      because a rule that is not there cannot misfire.

   2. NOTHING HERE MOVES LAYOUT. Every state this file toggles changes
      opacity, visibility or transform and nothing else, so the page cannot
      shift under the reader's thumb when a class flips. That is why the
      compact masthead measures a layout shift of zero rather than being
      argued to be small: the letterhead keeps its full height in flow while
      it is stuck, and the running head is a second, absolutely positioned
      line that was always there, made visible.

   3. NOTHING IS HIDDEN FROM A READER WHO WILL NEVER SEE IT MOVE. The waiting
      state of every entrance is applied only by a class motion.js adds, or
      inside @supports for the browser that can drive the entrance from the
      scroll itself. Script off, or an old browser: every word is on the
      page from the first paint.

   4. ONE EASING. cubic-bezier(.2,.7,.2,1) is the curve press.css already
      gives the mock-up book. Durations from the house scale: 120 (a press),
      240 (a bar yielding), 600 (a section arriving), 700 (the line). Nothing
      lingers, and nothing stops dead.

   5. NO NEW COLOR, NO SHADOW. The only tokens named are --sink, --ink-70,
      --ink and --accent, and only where press.css already uses them for the
      same job. The desk under the book (book.js) is a gradient and stays
      the one shade on the page.
   ========================================================================== */

@media (prefers-reduced-motion: no-preference) {

  :root {
    --mo-ease: cubic-bezier(.2, .7, .2, 1);
    --mo-strip: 56px;          /* the running head: a touch target, not type */
  }

  /* ==========================================================================
     1. ARRIVAL — a section's content settles into place as it enters
     --------------------------------------------------------------------------
     Twelve pixels of rise and a fade, on the section's .wrap and never on
     the section itself, so the grounds and the rules that separate them
     stay put and only the matter set on them arrives. The hero is on screen
     at load and is therefore already there.
     ========================================================================== */
  @keyframes mo-arrive {
    from { opacity: .001; transform: translateY(12px); }
    to   { opacity: 1;    transform: none; }
  }

  /* The browser that can drive the entrance from the scroll gets that, and
     the entrance then has physics for free: it goes exactly as far as the
     reader has pulled it. The range is capped at 240px of travel because
     "entry 35%" of a section three screens tall is more than a screen of
     half-faded reading matter — .options at 390 is 2,699px, so 35% of it is
     945px, measured. min() ends the entrance at the shorter of the two, and
     the same two numbers are what motion.js waits for before pinning. */
  /* .mo-js on the root is added by motion.js and by nothing else, so this
     block is inert with script off. It was not, once: a scroll-driven
     animation is pure CSS, and the break verifier found every below-fold
     section at opacity .001 with script disabled in Chromium 152, playing in
     reverse on the way back up because nothing could pin it. Law 3 above
     was false until this gate (D45). */
  @supports (animation-timeline: view()) and (animation-range: entry 0% entry min(35%, 240px)) {
    .mo-js main > section > .wrap,
    .mo-js .colophon > .wrap {
      animation: mo-arrive var(--mo-ease) both;
      animation-timeline: view();
      animation-range: entry 0% entry min(35%, 240px);
    }
    /* Pinned by motion.js once the entrance is complete, so scrolling back
       up never runs it in reverse: a reader who goes back to check a figure
       must not watch the paragraph dissolve as it leaves the bottom. */
    .mo-js main > section > .wrap.mo-in,
    .mo-js .colophon > .wrap.mo-in { animation: none; }
  }

  /* Every other browser: motion.js marks what is below the fold as waiting
     and lands it on intersection. Only script adds .mo-wait, and only to
     what is not yet on screen, so no reader ever sees a block appear and no
     reader whose script did not run is missing one. */
  .mo-wait { opacity: .001; transform: translateY(12px); }
  /* 320ms, not the 600 of the scroll-driven range: on this path (Safari on
     the iPad) the entrance is timed rather than pulled by the scroll, and a
     timed 600ms fade is the one place a reader could be made to wait for a
     paragraph. Short enough to be an arrival, not a delay (D45). */
  .mo-wait.mo-in {
    opacity: 1; transform: none;
    transition: opacity 320ms var(--mo-ease), transform 320ms var(--mo-ease);
  }

  /* ==========================================================================
     2. THE MASTHEAD COMPRESSES — the letterhead becomes a running head
     --------------------------------------------------------------------------
     .masthead is made sticky with a NEGATIVE top: it scrolls away like any
     other band until only its bottom 56px remain, and those stick. Its
     height in flow never changes, so nothing under it moves when the class
     flips — which is the whole reason it is built this way and not as a
     header that shrinks. motion.js measures the band and writes
     --mo-mast-h; the running head (.mo-strip) is absolutely positioned in
     that bottom 56px and is simply made visible once the band is stuck.
     Before script the class is absent and the masthead is exactly what
     press.css says it is.

     env(safe-area-inset-top) is in the sum because the page is
     viewport-fit=cover: on a phone with a notch the strip must sit under
     the status bar, not behind it, and the band's own ground fills the
     inset above it, as it does at the top of the page.
     ========================================================================== */
  .masthead.mo-sticky {
    position: sticky;
    top: calc(var(--mo-strip) + env(safe-area-inset-top, 0px) - var(--mo-mast-h, 0px));
    z-index: 40;                  /* under the thumb bar's 60, over the page */
  }

  /* The letterhead dissolves rather than disappearing, and it stays in the
     accessibility tree: it is only invisible. Tab to its telephone and the
     browser scrolls to it, which un-sticks the band, which brings it back.
     240 out, 180 back — the return is racing the band as it scrolls into
     view, and a slower fade let a fast flick to the top show the letterhead
     half-drawn. */
  /* THE FADE IS SEQUENCED, NOT CROSSED, and this is the "sloppy top screen".
     The letterhead's hours line and the running head's wordmark are
     GEOMETRICALLY SUPERIMPOSED -- measured at 375px, .hours-now at viewport top
     20px height 24, .mo-name at top 18px height 20: an overlap of 18 of 20
     pixels -- and the only thing separating them was opacity. Both transitions
     ran 240ms starting together, so for a quarter of a second every time the
     reader began to scroll, two different sentences were printed over each
     other. Caught mid-flight at .masthead-in .3465 against .mo-strip .6545.
     Out first, in second: the band never carries two sentences at once. */
  .masthead.mo-sticky .masthead-in {
    transition: opacity 120ms var(--mo-ease) 120ms;
  }
  .masthead.is-compact .masthead-in {
    opacity: .001;
    transition: opacity 120ms var(--mo-ease) 0ms;
  }

  /* visibility, not display, on the strip: display would be a layout
     change, and hidden visibility also takes the duplicate telephone out of
     the tab order and the accessibility tree while the letterhead's own is
     the one that counts. The 0s visibility transition is delayed on the way
     out so the fade is seen, and immediate on the way in so it is not
     skipped. */
  .mo-strip {
    position: absolute; left: 0; right: 0; bottom: 0; height: var(--mo-strip);
    visibility: hidden; opacity: .001;
    transition: opacity 120ms var(--mo-ease) 0ms, visibility 0s 120ms;
  }
  .masthead.is-compact .mo-strip {
    visibility: visible; opacity: 1;
    /* In second: 120ms of waiting while the letterhead goes, then 120ms of
       fade. On the way back the strip leaves first, with no delay. */
    transition: opacity 120ms var(--mo-ease) 120ms, visibility 0s 0s;
  }
  .mo-strip-in {
    height: 100%; display: flex; align-items: center;
    justify-content: space-between; gap: var(--s4);
  }

  /* The running head, set as a book sets one: small, in the serif, the name
     in ink and the trade in ink-70 as the wordmark has it. 16px on a 20px
     lead so that two lines still fit the 56px strip when it has to wrap —
     and it does, once: "Bar Five Rafter Publishing Co." measures 225px at
     this size and 282px in Larger print, and a 320px screen in that mode
     has a 270px column, where it takes two lines of 25px = 50px inside the
     56. Measured. Everywhere else it is one line. */
  .mo-name {
    margin: 0; min-width: 0;
    font-family: var(--serif); --opsz: 16; --wght: 600; font-weight: 600;
    font-size: var(--t-fine); line-height: calc(5 * var(--lead));
    color: var(--ink);
  }
  .mo-name-b { color: var(--ink-70); }

  /* The telephone, in the thumb bar's face — gothic, 700, tabular, accent —
     because at 56px tall the strip is a UI line and not a place for the
     display numeral. Hidden below 840, where the fixed thumb bar already
     carries the number and a second one would be two of the same thing on
     one phone screen. Above 840 the bar is in flow at the foot of the page,
     and this is the number within reach. */
  .mo-tel { display: none; }
  @media (min-width: 52.5rem) {
    .mo-tel {
      display: inline-flex; align-items: center; flex: none;
      min-height: var(--mo-strip); padding-inline: var(--s2);
      font-family: var(--sans); --wght: 700; font-weight: 700; font-size: var(--t-ui);
      line-height: calc(7 * var(--lead));
      font-variant-numeric: tabular-nums lining-nums;
      color: var(--accent); text-decoration: none;
    }
    .mo-tel:hover { text-decoration: underline; }
    .mo-tel:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
  }

  /* ==========================================================================
     3. THE THUMB BAR YIELDS
     --------------------------------------------------------------------------
     The fixed telephone bar slides down out of the way while the reader is
     scrolling down, and comes back on the first scroll up, when they stop
     for 800ms, at either end of the page and — always — while a form field
     is focused. Those decisions are motion.js's; this is only the 240ms it
     takes.

     IT USED TO STOP AT 840, AND THAT WAS STALE. This block was written when the
     bar was in flow above 840 and nothing here was allowed to touch it. press.css
     has since made `.callbar { position: fixed }` top-level, in no media query, so
     on a desktop the bar is fixed exactly as on a phone — and it could never
     yield. At 1280 the running head's number, the page's and the bar's were on
     screen together, 3 and 4 at a time (measured 2026-09-13). It now slides away
     at every width; motion.js decides WHEN, and on a desktop that is only while
     another number is readable, never merely because the reader scrolled.
     ========================================================================== */
  .callbar.mo-yield { transition: transform 240ms var(--mo-ease); }
  .callbar.mo-yield.is-away { transform: translateY(100%); }

  /* ==========================================================================
     4. THE STATION LINE DRAWS
     --------------------------------------------------------------------------
     The rule behind the seven steps is drawn top to bottom in 700ms as the
     list comes into view, and the numerals come up with it, opacity only.
     Once: motion.js takes the classes off again when the last numeral has
     landed, so the ring transitions press.css gives the open station are
     left exactly as they were.
     ========================================================================== */
  @keyframes mo-draw { from { transform: scaleY(0); } to { transform: scaleY(1); } }
  /* The numerals fill by opacity alone and ride the line: each one comes
     up as the line reaches it, a seventh of the draw apart, so a reader
     sees a rule being drawn past seven numbers, not a build. The first cut
     grew them from .6 with a 700ms wait and a 60ms stagger after the line
     had finished; the taste verifier called that "a slide-deck build" and
     the only scale transform on the site, and it was (D45). */
  @keyframes mo-fill {
    from { opacity: .001; }
    to   { opacity: 1; }
  }
  .stations.mo-held::before,
  .stations.mo-drawn::before { transform-origin: top; }
  .stations.mo-held::before { transform: scaleY(0); }
  .stations.mo-held .station-no { opacity: .001; }
  .stations.mo-drawn::before { animation: mo-draw 700ms var(--mo-ease) both; }
  /* 50ms apart, because the line is eased: it is 42% drawn at 113ms and
     95% at 413ms, so a seventh-of-700ms stagger left the last three numerals
     coming up 300ms after the line had passed them (measured per frame by
     the break verifier). At 50ms the numeral crosses half opacity within
     ~60ms of the line reaching it, first to last. */
  .stations.mo-drawn .station-no {
    animation: mo-fill 240ms var(--mo-ease) both;
    animation-delay: calc(50ms * var(--mo-i, 0));
  }

  /* ==========================================================================
     5. THE BOOK LIFTS
     --------------------------------------------------------------------------
     The stage rises its last 16px into place as it enters. A transform on
     the perspective element composes outside the scene, so the leaves are
     untouched; the class comes off at transitionend so book.js is left
     with a stage that has no transform of its own, as it was written for.
     ========================================================================== */
  .bk-stage.mo-low  { transform: translateY(16px); }
  .bk-stage.mo-lift { transform: none; transition: transform 600ms var(--mo-ease); }

  /* ==========================================================================
     6. THE PRESS
     --------------------------------------------------------------------------
     Every button gives one pixel and steps one rung down in luminance for
     as long as it is held. 120ms, the house's own state duration. The focus
     ring is press.css's and is not touched.

     Which rung depends on the scheme the button is in AT THE MOMENT OF THE
     PRESS, not on its class, and the incident is measured: forauthors.js
     appends its own sheet after this file and turns every hovered .b5r-btn
     ink with surface type, and its lead buttons (Stop, Close) rest on ink.
     A press that stepped those to --sink would put surface type on sink —
     no button at all. So: a surface button steps to --sink; an ink button —
     a lead button, a .b5r-btn under a pointer, the letter's .do — steps to
     --ink-70. The two utilities in the reading bar are named on their own
     because press.css's four-class selector keeps them at background:none
     and would otherwise win the press as well.
     ========================================================================== */
  .b5r-btn, .letter .do, .station-btn {
    transition: transform var(--t-state) var(--mo-ease),
                background-color var(--t-state) var(--mo-ease),
                border-color var(--t-state) var(--mo-ease);
  }
  .b5r-btn:active, .station-btn:active,
  .b5r-bar .b5r-btnrow:first-child .b5r-btn:active {
    transform: translateY(1px); background-color: var(--sink);
  }
  .b5r-btn:hover:active, .b5r-btn-lead:active, .letter .do:active {
    transform: translateY(1px);
    background-color: var(--ink-70); border-color: var(--ink-70);
  }

  /* On paper none of this exists. The band prints where it stands, the
     letterhead prints whole whatever state the screen was in, and nothing
     waits for a scroll that will never come. */
  @media print {
    .masthead.mo-sticky { position: static; }
    .masthead.is-compact .masthead-in { opacity: 1; }
    .mo-strip { display: none; }
    main > section > .wrap, .colophon > .wrap,
    .stations::before, .station-no { animation: none; }
    .mo-wait, .stations.mo-held::before, .stations.mo-held .station-no,
    .bk-stage.mo-low { opacity: 1; transform: none; }
  }
}
