/* =============================================================================
   ALL AMERICAN INNOVATIONS — motion.css
   Loads after site.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
   1. THE WHOLE FILE IS INSIDE ONE MEDIA BLOCK. A reader who has asked the
      device for less motion gets none of it. Not shorter: none.
   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. The compact
      letterhead keeps its full height in flow while it is stuck; the running
      head is a second, absolutely positioned line 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 drives the entrance from the
      scroll. 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), and the house durations: 120 (a
      press), 240 (a bar yielding), 600 (a section arriving), 700 (the line).
      Nothing lingers and nothing stops dead.
   5. NO NEW COLOUR, NO SHADOW.
   ========================================================================== */

@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 -- */
  @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 goes exactly as far as the reader has pulled it. The
     range is capped at 240px of travel so a tall section is not a screen of
     half-faded reading matter. */
  /* html.mo-on is added by motion.js. Without it the scroll-driven entrance
     below is pure CSS and runs with SCRIPT OFF — so a reader with no
     JavaScript got the reveal-on-scroll mannerism permanently, and got it
     from the one path nothing could pin. Nothing on screen is ever faint
     (the view() timeline sees to that), but a section fading in as it
     arrives is exactly what the taste review deleted the JS fallback for.
     Gated, a reader without script gets a still page, which is the law. */
  @supports (animation-timeline: view()) and (animation-range: entry 0% entry min(35%, 240px)) {
    html.mo-on main > section > .wrap,
    html.mo-on .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 complete, so scrolling back up never runs it
       in reverse. */
    html.mo-on main > section > .wrap.mo-in,
    html.mo-on .colophon > .wrap.mo-in { animation: none; }
  }
  /* No fallback for the browser that cannot drive the entrance from the
     scroll. The first build landed a 600ms fade on every section as it came
     in, on iOS Safari above all: the reveal-on-scroll mannerism, a reader
     made to wait for words that were already there. Where the scroll drives
     it the entrance is physical; where it cannot, there is none. */

  /* ---- 2. THE LETTERHEAD COMPRESSES into 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. motion.js measures the band and writes
     --mo-mast-h; the running head (.mo-strip) is absolutely positioned in
     that bottom 56px and made visible once the band is stuck. The flag rule
     under the band travels with it, which is the point: the running head is
     the letterhead's own foot. */
  /* On a sheet only. On a phone the thumb bar is the pinned chrome, and a
     second pinned band above it — 56px of name doing no job — was 131px of
     an 844px screen given to furniture (the taste verifier, 2026-09-02). */
  @media (min-width: 52.5rem) {
    .masthead.mo-sticky {
      position: sticky;
      top: calc(var(--mo-strip) + env(safe-area-inset-top, 0px) - var(--mo-mast-h, 0px));
      z-index: 40;
    }
  }
  .masthead.mo-sticky .masthead-in { transition: opacity 180ms var(--mo-ease); }
  .masthead.is-compact .masthead-in { opacity: .001; transition-duration: 240ms; }

  .mo-strip {
    position: absolute; left: 0; right: 0; bottom: 0; height: var(--mo-strip);
    visibility: hidden; opacity: .001;
    transition: opacity 240ms var(--mo-ease), visibility 0s 240ms;
  }
  .masthead.is-compact .mo-strip {
    visibility: visible; opacity: 1;
    transition: opacity 240ms var(--mo-ease), visibility 0s 0s;
  }
  .mo-strip-in {
    height: 100%; display: flex; align-items: center;
    justify-content: space-between; gap: var(--s4);
  }
  /* The mark first, then the name, then the number: the running head is the
     letterhead's own foot and it reads in the same order. Sized to the strip,
     not to the type, so it never pushes the 56px box. */
  .mo-mark { width: calc(11 * var(--lead)); height: calc(8 * var(--lead)); margin: 0 var(--s4) 0 0; flex: none; }
  .mo-strip-in > .mo-name { margin-right: auto; }
  .mo-name {
    margin: 0; min-width: 0;
    font-family: var(--sans); --wght: 800; font-weight: 800;
    font-size: var(--t-fine); line-height: calc(5 * var(--lead));
    letter-spacing: -.01em; text-transform: uppercase; color: var(--ink);
  }
  .mo-name-b { color: var(--ink-70); }
  /* The telephone in the strip: hidden below 840, where the fixed thumb bar
     already carries the number; shown above, where nothing else does. */
  .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);
      --wght: 800; font-weight: 800; 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; }
  }

  /* Sections 3, 4 and 5 of the first build — the yielding thumb bar, the
     drawn station line with its numerals popping in, the lifted book — are
     gone. The bar rose over the line a reader had stopped on; the numerals
     were absent for 1.3s under a heading that said "in order"; the book was
     translated twice. The ring transition on an open station and the arrival
     above are what remain. */

  /* ---- 6. THE PRESS: every button gives one pixel and steps one rung ---- */
  .aai-btn, .do, .station-btn, .bk-btn, .reach a, .compare-handle {
    transition: transform var(--t-state) var(--mo-ease),
                background-color var(--t-state) var(--mo-ease),
                border-color var(--t-state) var(--mo-ease);
  }
  .aai-btn:active, .station-btn:active, .bk-btn:active, .reach a:not(.is-lead):active,
  .compare-handle:active,
  .aai-bar .aai-btnrow:first-child .aai-btn:active {
    transform: translateY(1px); background-color: var(--sink);
  }
  .do:active, .reach .is-lead:active {
    transform: translateY(1px);
    background-color: var(--ink-70); border-color: var(--ink-70);
  }
  .colophon .reach a:not(.is-lead):active { background-color: var(--rule-on-band); }
  .colophon .reach .is-lead:active { background-color: var(--on-band-70); border-color: var(--on-band-70); }

  /* ---- 7. DRIVEN BY THE SCROLL, NOT BY A CLOCK ---------------------------
     Two of these, and only two. The catalogue of scroll-driven effects is
     mostly stunts, and this page's own law is that a gesture which coincides
     with structure the reader already perceives carries no information — it is
     decoration wearing a technique. So neither of these is an entrance and
     neither is a flourish: each one tells the reader something they cannot
     otherwise see, and each is a slow process rather than an event.

     Both run on the compositor from the scroll position itself: no scroll
     listener, no rAF, nothing on the main thread, and nothing to jank on an
     old phone. A browser without them shows the page exactly as it is now. */

  @keyframes mo-progress { from { transform: scaleX(0); } to { transform: scaleX(1); } }
  @keyframes mo-spine    { from { transform: scaleY(0); } to { transform: scaleY(1); } }

  @supports (animation-timeline: scroll(root block)) {
    /* (a) HOW FAR THROUGH. The running head is the letterhead's own foot, and
       a sheet's foot is where a reader looks to see how much is left. An ink
       line fills across it as the page is read — the sheet inking in. It rides
       the strip, so it exists exactly when the strip does: on a sheet, once
       the letterhead has compressed. On a phone the pinned chrome is the
       thumb bar, and the thumb bar is for calling, not for decorating. */
    @media (min-width: 52.5rem) {
      html.mo-on .mo-strip::after {
        content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
        background: var(--ink); transform-origin: left center; transform: scaleX(0);
        animation: mo-progress linear both;
        animation-timeline: scroll(root block);
      }
    }
  }

  @supports (animation-timeline: view()) and (animation-range: cover 0% cover 100%) {
    /* (b) HOW FAR THROUGH THE JOB. The seven steps already hang on a hairline
       spine. A second line in the accent draws down it as the reader passes,
       so the thing the section is about — that a job runs in an order, and
       this is where you are in it — is drawn rather than stated. It is the one
       place the accent earns a field on this page, and it is the only motion
       here tied to content rather than to arrival.

       The range is cover 20%..80%: the line is not finished before the last
       step is on screen, and not still empty when the first one is. */
    /* NAMED ON THE TRACK, NOT ON THE LIST. Inside a pinned stage the list does
       not move, so a view timeline on it never progresses — the house would
       freeze at whatever it was when the section pinned. The track is the thing
       that travels, and `contain` is precisely the stretch during which it
       fills the viewport, which is precisely the stretch during which the stage
       is pinned. */
    html.mo-on .path { view-timeline-name: --mo-steps; }
    html.mo-on .stations::after {
      content: ""; position: absolute; left: 1.125rem; top: 1.5rem; bottom: 1.5rem;
      border-left: 2px solid var(--accent); pointer-events: none;
      transform-origin: center top; transform: scaleY(0);
      animation: mo-spine linear both;
      animation-timeline: --mo-steps;
      animation-range: cover 20% cover 80%;
    }
  }

  /* ---- 9. THE MARK GOES UP AS THE READER GOES DOWN, AND COMES APART ON THE
     WAY BACK. The gable block and its roof are always drawn, so the mark is
     never a building site; the wing, the openings and the chimney are drawn
     from the page's own scroll position. It is one continuous, reversible
     process rather than an event: scroll down and the house is framed, roofed
     and finished; scroll up and it comes apart in the order it went together.
     The reader is never told about it and it never asks to be noticed, which
     is the only way a thing like this is allowed to be on every screen. */
  @keyframes mo-raise { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }

  @supports (animation-timeline: scroll(root block)) {
    /* THE GROUND GOES UP WITH THE PAGE. It is fixed behind everything, so it is
       the one thing on screen from the first line to the last, and it builds
       across the WHOLE scroll rather than the first screen's — ground and
       walls almost at once so the page never lands on an empty lot, then the
       roof, the wing, and the openings spread over the rest of the reading.
       Scroll back and it comes apart in the order it went together.

       It shares its ranges with nothing: the mark in the letterhead runs the
       same sequence on the same timeline, so the small house in the corner and
       the large one behind the page are always at the same stage of the same
       build. That is the whole idea — one house, drawn twice, at two sizes. */
    /* The site, the foundation and the walls are NOT animated. At scroll zero
       every animated stroke is at its start, so a house built entirely by the
       scroll leaves the first screen — the one screen every reader sees — an
       empty lot, and the mark in the letterhead a roofless box. Ground,
       footing, walls AND ROOF are simply there; the wing, the openings and the
       finish are what the reader raises. Both houses run these same three
       ranges, so the small one in the corner and the large one behind the page
       are always at the same stage of the same build. */
    html.mo-on .ghost .st-5 path,
    html.mo-on .ghost .st-6 path,
    html.mo-on .ghost .st-7 path {
      stroke-dasharray: 1; stroke-dashoffset: 1;
      animation: mo-raise linear both;
      animation-timeline: scroll(root block);
    }
    html.mo-on .ghost .st-5 path { animation-range: 0% 30%; }   /* the wing */
    html.mo-on .ghost .st-6 path { animation-range: 28% 62%; }  /* the openings */
    html.mo-on .ghost .st-7 path { animation-range: 58% 92%; }  /* the finish */

    html.mo-on .mark .mk-a path,
    html.mo-on .mark .mk-b path,
    html.mo-on .mark .mk-c path {
      stroke-dasharray: 1; stroke-dashoffset: 1;
      animation: mo-raise linear both;
      animation-timeline: scroll(root block);
    }
    html.mo-on .mark .mk-a path { animation-range: 0% 30%; }   /* the wing */
    html.mo-on .mark .mk-b path { animation-range: 28% 62%; }  /* the openings */
    html.mo-on .mark .mk-c path { animation-range: 58% 92%; }  /* the finish */
  }

  /* ---- 8. THE HOUSE IS BUILT BY THE READER'S OWN SCROLL -------------------
     The one thing on this page that is neither a photograph nor a paragraph.
     Seven groups, one per step, each drawn by stroke-dashoffset over its own
     tenth of the section's pass through the viewport — so the elevation goes
     up in the same order the job does, at exactly the rate the reader moves,
     and stops the moment they stop. It is not an entrance and it does not
     repeat: it is a single slow process, tied to the content it illustrates,
     which is the only kind of movement this page allows.

     Nothing here runs on the main thread and nothing is hidden from a reader
     who will never see it move: the waiting state lives INSIDE the @supports,
     so a browser without a view timeline draws the finished house on the first
     paint and always has. */
  @keyframes mo-build { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }

  /* The timeline is named on .stations, and .build is NOT its descendant — so
     the name only resolves inside whatever declares timeline-scope. That scope
     sat on `.path > .wrap` while the drawing was a child of the wrap. The
     moment the drawing became a SIBLING of the wrap (D30), it fell outside the
     scope, the name stopped resolving, and every stage rendered fully drawn:
     the house stopped building and nothing anywhere said so. The scope belongs
     on the common ancestor of the timeline and everything that reads it, which
     is the section. */
  @supports (animation-timeline: view()) and (timeline-scope: --mo-steps) {
    html.mo-on .path { timeline-scope: --mo-steps; }
    html.mo-on .build path {
      stroke-dasharray: 1; stroke-dashoffset: 1;
      animation: mo-build linear both;
      animation-timeline: --mo-steps;
    }
    /* Spread across the section's whole pass, and starting as soon as it
       enters: the drawing is stuck to the top for the entire seven steps now,
       so a first stage that waits until 15% left the reader looking at an
       empty lot for a quarter of the section. Ground, footing and walls are up
       by a third of the way in; the roof, the wing, the openings and the
       finish take the rest — one stage roughly per step. */
    /* EACH STAGE GETS THE SHARE OF THE TRACK IT ACTUALLY DRAWS. Equal ranges
       looked erratic and were: stage 5 is three strokes and stage 7 is
       thirteen, so equal time meant the wing snapped on and the finish crawled.
       Measured across the track in twentieths, the biggest step was 2.38x the
       average. Weighted by path count it is 1.16x — the house accrues at one
       even rate from the footings to the trim. tools/make-elevation.py prints
       these numbers; if the drawing changes, take them from it again. */
    html.mo-on .build .st-1 path { animation-range: contain 0.0% contain 9.5%; }    /* the site */
    html.mo-on .build .st-2 path { animation-range: contain 6.5% contain 17.5%; }   /* the foundation */
    html.mo-on .build .st-3 path { animation-range: contain 14.5% contain 27.5%; }  /* the walls */
    html.mo-on .build .st-4 path { animation-range: contain 24.5% contain 37.5%; }  /* the roof */
    html.mo-on .build .st-5 path { animation-range: contain 34.5% contain 43.5%; }  /* the wing */
    html.mo-on .build .st-6 path { animation-range: contain 40.5% contain 71.5%; }  /* the openings */
    html.mo-on .build .st-7 path { animation-range: contain 68.5% contain 96.0%; }  /* the finish */
  }

  /* On paper none of this exists. */
  @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; }
    .stations::after, .mo-strip::after { display: none; }
    .build path, .mark path, .ghost path { stroke-dasharray: none; stroke-dashoffset: 0; animation: none; }
    .mo-mark { display: none; }
  }
}
