/* ============================================================================
   site/site-motion.css — THE MOTION, DEPTH AND MATERIAL SYSTEM
   ----------------------------------------------------------------------------
   Layered on engine/mc-ui.css and site/site.css. Loaded LAST so it can raise
   the marketing surface without any of the three being forked.

   THIS FILE DEFINES NO COLOUR, exactly like site.css. Every ink, wash, scrim
   and shadow below is a token out of engine/mc-ui.css, which is the one file in
   this tree that may define one. The gate in tools/consistency.py scans this
   file, and the number it is allowed is zero.

   THE ONE RULE THIS FILE IS BUILT AROUND
   MOTION EXPLAINS A RELATIONSHIP OR IT DOES NOT SHIP. Every animation here
   answers a question the reader already has:
     * a reveal says "this belongs to the block you just arrived at"
     * parallax says "this layer is behind that one" — depth, not decoration
     * the sticky 4-D sequence says "scroll position IS construction sequence",
       which is the literal claim the product makes about 4-D
     * a hover lift says "this is a target"
   Nothing here loops on a timer, nothing drifts, nothing pulses for attention.
   A marquee that moves while the reader is trying to read a number is the
   marketing-surface form of the defect this product refuses everywhere else:
   something confident-looking placed where information should be.

   COMPOSITOR ONLY. Every animated property below is `transform` or `opacity`.
   Nothing animates width, height, top, left, margin, padding or box-shadow —
   those force layout on every frame, and a layout in a scroll handler is a
   defect, not a preference. Where a "shadow change" is wanted on hover, the
   element carries a pseudo-element whose OPACITY is animated instead.

   OPACITY IS 0 OR 1 AND NEVER IN BETWEEN, and that is a product rule, not a
   stylistic one: consistency.py's OPACITY_MIN is 1.0 because de-emphasis by a
   fade fails contrast silently. So a resting state is opacity 0 or 1, and every
   translucent SURFACE below is one of mc-ui.css § 2b's whole tokens —
   --wash-accent / --wash-accent-2 / --wash-faint / --wash-soft / --rule-ink /
   --scrim*. A 60%-opaque panel would be the same defect one notation along.

   NO-JAVASCRIPT IS A FIRST-CLASS STATE. Every rule that hides something for a
   later reveal is scoped under `html.js`, which is set by one inline statement
   in the document head. With scripting off, nothing is hidden, nothing is
   transformed, and the page is the same page it was before this file existed.

   REDUCED MOTION IS HONOURED BY REMOVING MOTION, NOT CONTENT. § 2 collapses
   every duration and neutralises every transform. The 4-D sequence still steps
   through its phases — it just cuts instead of easing, and it is driven by
   buttons as well as by scroll. A reader who asked for less motion gets the
   same information.

   Sections: 1 motion tokens · 2 reduced motion · 3 reveal · 4 material & depth
   5 display type · 6 nav · 7 hero stage · 8 the 4-D sequence · 9 live surface
   10 marks & rules · 11 responsive · 12 print
   ============================================================================ */

/* ---- 1. motion tokens ---------------------------------------------------- */
/* Four durations, and the shortest one that reads as deliberate is 120 ms.
   Below about 100 ms a transition is perceived as a jump; above about 500 ms
   the reader is waiting for the interface. --dur-3 is the ceiling for anything
   the reader triggered; --dur-4 is only for a thing that is arriving on its
   own, where the reader is not blocked on it.

   THREE EASINGS AND A REASON FOR EACH.
   --ease-out   things ARRIVING. Fast out of the gate, settles gently: the
                reader's eye catches it already in place.
   --ease-in-out things MOVING BETWEEN two states they will return from.
   --ease-spring a single overshoot, used ONLY on a control the reader pressed,
                because an overshoot on something they did not press reads as
                instability. mc-ui.css already declares --ease for the product;
                it is reused verbatim below rather than re-spelt.

   THIS FILE OWNS THE NAME --ease-out. It declares the curve, it is the only
   consumer of it anywhere in the tree, and the name only means anything beside
   its two siblings -- a triad where one member answers to a different file is
   not a triad. brand/animus/animus.css declared a second --ease-out and links
   after this file on every site page, so every arrival here ran on that curve
   instead of this one; it is now --as-ease-out there, under its own prefix.
   An easing is a metric like a spacing step: the rule was authored around the
   motion it produces, so re-pointing it under a shared name does not restyle
   the rule, it moves it. If you want the Animus curve on this site, ask for
   --as-ease-out by name. */
:root{
  --dur-1:120ms;   /* state change on a control the pointer is already on */
  --dur-2:240ms;   /* a reveal, a lift, a colour move */
  --dur-3:420ms;   /* a panel or a stage changing what it shows */
  --dur-4:720ms;   /* a layered arrival, longest step of a stagger */

  --ease-out:cubic-bezier(.22,.61,.36,1);      /* === mc-ui.css --ease */
  --ease-in-out:cubic-bezier(.65,.05,.36,1);
  --ease-spring:cubic-bezier(.34,1.32,.64,1);

  /* Stagger. 60 ms is the smallest step where a row of cards reads as a
     sequence rather than as one late block; 5 steps × 60 ms = 300 ms, which is
     under --dur-4, so the last card still lands inside one arrival. A stagger
     longer than its own animation reads as a fault. */
  --stag:60ms;

  /* Reveal travel. 14 px is deliberately small: a reveal is a cue that content
     has arrived, not a slide show. Anything past ~24 px starts to read as the
     page being rebuilt under the reader. */
  --rise:14px;

  /* Parallax depth. These are RATIOS of scroll distance, consumed by
     site-motion.js, and they are declared here so the depth order lives beside
     the shadows that have to agree with it. Larger = further from the reader. */
  --par-near:0.04;
  --par-mid:0.10;
  --par-far:0.18;
}

/* ---- 2. reduced motion --------------------------------------------------- */
/* PLACED EARLY AND REPEATED AT THE END OF EACH SECTION IS NOT THE PATTERN HERE.
   One block, `!important`, and every animated declaration in this file is
   written so that neutralising duration and transform leaves the RESTING state
   correct — which is why every reveal's rest state is `opacity:1;transform:none`
   rather than a mid-animation value.

   scroll-behavior is included because a smooth-scrolled anchor is motion the
   reader did not ask for, and it is the one people with vestibular sensitivity
   report most often. */
@media (prefers-reduced-motion:reduce){
  html:focus-within{scroll-behavior:auto}
  *,*::before,*::after{
    animation-duration:1ms!important;
    animation-iteration-count:1!important;
    transition-duration:1ms!important;
    scroll-behavior:auto!important;
  }
  html.js [data-reveal]{opacity:1;transform:none}
  .par{transform:none!important}
  /* The stage still changes phase — it just cuts. See § 8. */
  .stage-svg{transition:none!important}
}

/* ---- 3. the reveal system ------------------------------------------------ */
/* SCOPED UNDER html.js, ALWAYS. Without scripting the selector does not match,
   nothing is hidden and the page needs no rescue. This is the single most
   important line in the file: a marketing page whose text is invisible when a
   script fails has laundered a failure into a blank, which is trap 10 wearing a
   different hat.

   The observer in site-motion.js adds `is-in`; it never removes it, so a
   revealed block cannot flicker back out when the reader scrolls up. */
html.js [data-reveal]{
  opacity:0;
  transform:translate3d(0,var(--rise),0);
  transition:opacity var(--dur-3) var(--ease-out),
             transform var(--dur-4) var(--ease-out);
  transition-delay:var(--reveal-delay,0ms);
  will-change:transform,opacity;
}
html.js [data-reveal].is-in{opacity:1;transform:none;will-change:auto}

/* Direction variants. Each one is a claim about where the thing came from, so
   they are used sparingly: a card grid rises, a side rail comes in from its own
   edge, and a stage scales up from its own centre. */
html.js [data-reveal="left"]{transform:translate3d(calc(var(--rise) * -1.4),0,0)}
html.js [data-reveal="right"]{transform:translate3d(calc(var(--rise) * 1.4),0,0)}
html.js [data-reveal="scale"]{transform:scale(.985)}
html.js [data-reveal].is-in{transform:none}

/* Stagger is set as a custom property by the observer, not by nth-child, so a
   grid that reflows to one column on a phone still staggers in visual order. */

/* ---- 4. material and depth ----------------------------------------------- */
/* THE REFERENCE IS A PRINTED DRAWING SET, NOT A GLASS DASHBOARD. Three things
   carry that: a paper grain, hairline rules with real tick marks, and shadows
   that are stacked rather than blurred.

   THE GRAIN IS A MATERIAL, NOT A THEME COLOUR — the same exemption CLAUDE.md
   grants the asphalt and steel in engine/sitelib. It is implemented as a MASK
   so the ink is still a token: the data URI supplies the alpha pattern and
   --wash-faint supplies the colour, which means the grain follows the palette
   if the palette moves. A `background-image` of coloured noise would not. */
.grain{position:relative;isolation:isolate}
.grain::before{
  content:"";position:absolute;inset:0;pointer-events:none;z-index:0;
  background-color:var(--wash-faint);
  -webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
          mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
  -webkit-mask-size:180px 180px;mask-size:180px 180px;
}
.grain>*{position:relative;z-index:1}

/* Warm light. Two washes from mc-ui.css § 2b, placed as radial pools rather
   than as a linear gradient, so the light has a source and a falloff instead of
   a direction. This is the whole difference between "warm" and "tinted". */
.lit{position:relative;overflow:hidden}
.lit::after{
  content:"";position:absolute;inset:-20% -10% auto;height:130%;pointer-events:none;z-index:0;
  background:
    radial-gradient(58% 52% at 22% 8%, var(--wash-accent), transparent 70%),
    radial-gradient(48% 48% at 88% 34%, var(--wash-accent-2), transparent 72%);
}
.lit>*{position:relative;z-index:1}

/* LAYERED SHADOW. mc-ui.css declares four whole shadows; depth here comes from
   STACKING them, not from inventing a fifth. A near shadow holds the edge, a
   far shadow puts the object above the page. Stacking is also why nothing here
   animates box-shadow: the lift on hover is a transform plus a pre-rendered
   second shadow whose opacity crosses. */
.lift{position:relative;transition:transform var(--dur-2) var(--ease-out)}
.lift::after{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  box-shadow:var(--shadow-md),var(--shadow-lg);
  opacity:0;transition:opacity var(--dur-2) var(--ease-out);
}
.lift:hover,.lift:focus-within{transform:translate3d(0,-3px,0)}
.lift:hover::after,.lift:focus-within::after{opacity:1}

/* The pillar cards get it without a class change on every one of them. */
.pcard{box-shadow:var(--shadow-sm);transition:transform var(--dur-2) var(--ease-out),
                                              border-color var(--dur-2) var(--ease-out)}
.pcard:hover,.pcard:focus-within{transform:translate3d(0,-3px,0)}

/* PARALLAX LAYERS. The transform is written by site-motion.js; everything here
   just declares that the element is a compositor layer and how deep it is. */
.par{will-change:transform}
.par-near{--par:var(--par-near)}
.par-mid{--par:var(--par-mid)}
.par-far{--par:var(--par-far)}

/* ---- 5. display typography ----------------------------------------------- */
/* CAPRASIMO IS SPENT HERE AND ONLY HERE. Inside the product it is confined to
   one h1 per page, because a chunky display face repeated over dense data is
   noise. A marketing page is the opposite case: there is one thing to say per
   screen and the type is allowed to be the thing that says it.

   The scale is fluid rather than stepped so there is no width at which a
   headline is either cramped or absurd. clamp()'s middle term is in vw, so the
   ratio between the three sizes is preserved across the whole range.

   OPTICAL TRACKING: a display face at 72px needs NEGATIVE tracking that the
   same face at 20px does not. -.022em at the top, easing to 0 at the bottom.
   Caprasimo has no italic and one weight, so hierarchy here is size and measure
   only, which is why the measures below are tight (--dm caps at 16 words). */
.d0,.d1,.d2{font-family:var(--font-display);font-weight:400;color:var(--color-text);
  line-height:1.02;margin:0;text-wrap:balance}
.d0{font-size:clamp(40px,7.2vw,86px);letter-spacing:-.022em;max-width:15ch}
.d1{font-size:clamp(30px,4.4vw,54px);letter-spacing:-.016em;max-width:19ch;line-height:1.06}
.d2{font-size:clamp(24px,3.0vw,36px);letter-spacing:-.010em;max-width:24ch;line-height:1.12}

/* A display line that carries a second, quieter half. The break is authored,
   not wrapped, so the rag is deliberate at every width the clamp produces. */
.d0 .qt,.d1 .qt,.d2 .qt{color:var(--bone-dim)}

/* The lead paragraph under a display line. Larger than body, shorter measure
   than body, because it is read in one pass and body text is not. */
.lede-xl{font-size:clamp(17px,1.5vw,21px);line-height:1.55;color:var(--bone-dim);
  max-width:56ch;margin:0 0 var(--sp-5)}
.lede-xl b,.lede-xl strong{color:var(--color-text);font-weight:700}

/* A NUMBER SET AS TYPE. Tabular figures, display face, and the unit set small
   beside it — the instrument-panel register. Used for the live figures the
   engine computes on the page, never for a decorative statistic. */
.fig-n{font-family:var(--font-display);font-size:clamp(30px,3.6vw,46px);
  line-height:1;letter-spacing:-.02em;color:var(--color-text);
  font-variant-numeric:tabular-nums;display:block}
.fig-u{font-size:11px;font-weight:700;letter-spacing:.10em;text-transform:uppercase;
  color:var(--bone-faint);display:block;margin-top:var(--sp-2)}

/* ---- 6. nav -------------------------------------------------------------- */
/* The bar tightens once the reader has left the hero. It is a transform and a
   background token, never a height change: animating height on a sticky element
   reflows every following block on every frame. */
.mnav{transition:box-shadow var(--dur-2) var(--ease-out),
                 background-color var(--dur-2) var(--ease-out)}
.mnav.is-stuck{box-shadow:var(--shadow-sm),var(--shadow-md);background:var(--color-card)}
.mnav .wrap{transition:transform var(--dur-2) var(--ease-out)}

/* Skip link — visible the moment it takes focus, and it is the first thing in
   the tab order on every page.

   position:FIXED, not absolute. An absolutely positioned skip link is pinned to
   the top of the DOCUMENT, so a reader who has scrolled and then tabs back to
   the start of the order gets a control that "appears" a thousand pixels above
   the viewport — focused, announced, and off screen. Fixed pins it to the
   viewport, which is the only position at which the affordance is true. */
.skip{position:fixed;left:var(--sp-4);top:var(--sp-2);z-index:60;
  transform:translate3d(0,-200%,0);transition:transform var(--dur-1) var(--ease-out);
  background:var(--color-card);color:var(--color-accent-800);border:1px solid var(--color-accent-400);
  border-radius:var(--radius-sm);padding:var(--sp-2) var(--sp-4);font-weight:700;font-size:13px;
  text-decoration:none;box-shadow:var(--shadow-md)}
.skip:focus{transform:translate3d(0,0,0)}

/* ---- 7. the hero stage --------------------------------------------------- */
/* The stage is where engine/sitelib draws. It is a plain <div> that receives an
   <svg> string from the product's own renderScene(), so the geometry on this
   page is generated by the same code that draws the 4-D film in the product. */
.hero-x{position:relative;border-bottom:1px solid var(--color-neutral-200);
  background:linear-gradient(150deg,var(--wash-accent),var(--wash-accent-2))}
.hero-x .wrap{padding-top:var(--sp-7);padding-bottom:var(--sp-7);position:relative}
.hero-grid{display:grid;grid-template-columns:minmax(0,1.02fr) minmax(0,1fr);
  gap:var(--sp-6);align-items:center}

/* THE GROUND POOL. renderScene() is called with no `site`, so the product's own
   dark ground plane is not drawn — it is a material for a dark 3-D canvas and
   would be a hole in a cream page. The stage supplies a warm pool of light
   instead, and the engine's own measured contact shadows land on it. */
.stage{position:relative;min-height:300px;display:flex;align-items:center;justify-content:center}
.stage::before{
  content:"";position:absolute;left:2%;right:2%;top:16%;bottom:6%;border-radius:50%;
  background:radial-gradient(58% 60% at 50% 62%, var(--wash-soft), transparent 72%);
  pointer-events:none}
.stage-svg{display:block;width:100%;height:auto;max-height:64vh;position:relative;
  transition:opacity var(--dur-3) var(--ease-out)}
.stage-svg .inst{transition:opacity var(--dur-3) var(--ease-out)}

/* The stage before the geometry engine has answered. NOT a spinner: a plain
   statement of what is being loaded, in the same ink as the rest of the page,
   so a reader whose network stalls is told rather than entertained. */
.stage-note{font-size:12.5px;line-height:1.55;color:var(--bone-dim);
  border:1px dashed var(--color-neutral-300);border-radius:var(--radius-md);
  padding:var(--sp-4);max-width:46ch;background:var(--color-card)}
.stage-note b{color:var(--color-text)}
/* A stage that FAILED says so where the drawing would have been. This is the
   marketing-surface form of trap 10, and it is the reason .stage-fail exists at
   all: an empty box on a page selling a product whose whole pitch is "a feed
   that fails says so by name" would be the single worst thing this site could
   render. It takes the status ink the product uses for exactly this. */
.stage-fail{border-style:solid;border-color:var(--st-nodata-ink);
  background:var(--st-nodata-tint);color:var(--color-text)}

/* The hero's own credential line, set under the stage. */
.stage-cap{font-size:12px;line-height:1.5;color:var(--bone-faint);margin-top:var(--sp-3);
  max-width:52ch}
.stage-cap b{color:var(--bone-dim)}

/* ---- 8. the 4-D sequence (sticky scrollytelling) ------------------------- */
/* THE ONE PLACE ON THIS SITE WHERE SCROLL DRIVES CONTENT, and it earns it: the
   relationship being explained is that scroll position IS construction
   sequence, which is the literal definition of 4-D. The stage sticks; the
   phase copy scrolls past it; the geometry re-renders per phase from the real
   catalog. Nothing about that is decoration.

   position:sticky, not a scroll-positioned transform — the browser does it on
   the compositor and there is no handler to be slow. */
.seq{position:relative;margin:var(--sp-7) 0}

/* THE ONE PLACE PARALLAX IS SPENT, and it is spent on a figure-and-ground
   relationship rather than on decoration. The reference for this whole surface
   is a printed drawing set: a ruled sheet on the table, the detail lifted off
   it. So the sequence sits over two ruled layers that travel at different
   rates — the far one barely moves, the near one tracks the reader — and the
   sticky stage above them reads as an object on a sheet rather than as a panel
   in a page. Remove the motion (§ 2 does, under prefers-reduced-motion) and the
   layers are simply two rules and the composition still holds; that is the test
   a decorative effect fails.

   Both layers are aria-hidden in the markup and pointer-events:none here: they
   carry no information, so they must not carry focus, a name, or a hit target.
   Every value is a token — --rule-ink is mc-ui.css's hairline, --wash-faint its
   lightest wash — so there is no colour here and the ruling follows the theme. */
/* THE BLEED IS THE PAGE GUTTER, NOT A PERCENTAGE, and that is a fix rather
   than a preference. The two ruled layers are decorative, so they are allowed
   to run out past the section they sit behind — but the overhang was -6% of
   the section width, which is only safe while it stays inside `.wrap`'s own
   padding. brand/animus/animus.css used to ship its own spacing scale under the
   SAME names, which moved --sp-5 from 24px to 16px, so at a 375 px viewport the
   6 % overhang became 20.6 px against 16 px of padding and the WHOLE PAGE
   scrolled sideways by 5 px — tools/siteperf.py caught it; nothing else
   would have. That scale is --as-0..10 now and --sp-5 is 24px again, so the
   20.6/16 arithmetic above is history rather than the current state. The
   fix is NOT reverted with it and must not be: expressed as the gutter it cannot overflow at any width or under
   any future spacing scale, because it is the same number the page is inset by.
   The percentage was always the defect; the collision only made it visible.
   `.seq` cannot simply clip it: .seq-sticky inside is position:sticky, and an
   ancestor with overflow:hidden silently kills sticky. */
.seq-depth{position:absolute;inset:-4% calc(-1 * var(--sp-5)) -2%;
  z-index:0;pointer-events:none;overflow:hidden}
.seq-depth>i{position:absolute;left:0;right:0;display:block}
.seq-depth .dl-far{
  top:-14%;height:128%;
  /* --wash-soft, not --rule-ink. A hairline at 35% alpha is right ON a chart,
     where it separates two fills; run full-width UNDER a column of body text it
     competes with the text it is behind. This is the depth layer's whole
     discipline: it must be legible as a sheet and invisible as an obstacle. */
  background-image:repeating-linear-gradient(to bottom,
    var(--wash-soft) 0 1px, transparent 1px 96px);
  -webkit-mask-image:linear-gradient(to bottom,transparent,var(--paper) 22%,var(--paper) 78%,transparent);
          mask-image:linear-gradient(to bottom,transparent,var(--paper) 22%,var(--paper) 78%,transparent)}
.seq-depth .dl-near{
  top:-10%;height:120%;
  background-image:repeating-linear-gradient(to right,
    var(--wash-faint) 0 1px, transparent 1px 40px);
  -webkit-mask-image:linear-gradient(to bottom,transparent,var(--paper) 30%,var(--paper) 70%,transparent);
          mask-image:linear-gradient(to bottom,transparent,var(--paper) 30%,var(--paper) 70%,transparent)}
.seq>.sechead,.seq>.prose,.seq>.seq-grid{position:relative;z-index:1}
.seq-grid{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);gap:var(--sp-6)}
.seq-sticky{position:sticky;top:88px;align-self:start;height:min(72vh,620px);
  display:flex;flex-direction:column;justify-content:center}
.seq-stage{position:relative;flex:1;display:flex;align-items:center;justify-content:center;
  border:1px solid var(--color-neutral-200);border-radius:var(--radius-lg);
  background:var(--color-card);box-shadow:var(--shadow-sm),var(--shadow-md);
  padding:var(--sp-4);overflow:hidden}
.seq-stage::before{
  content:"";position:absolute;left:6%;right:6%;top:20%;bottom:8%;border-radius:50%;
  background:radial-gradient(56% 58% at 50% 62%, var(--wash-soft), transparent 74%);
  pointer-events:none}

/* The phase readout under the stage — the instrument face. Fixed height so the
   stage above it never moves when the label changes length. */
.seq-read{display:flex;gap:var(--sp-4);align-items:baseline;justify-content:space-between;
  margin-top:var(--sp-3);padding-top:var(--sp-3);border-top:1px solid var(--color-neutral-200);
  min-height:46px}
.seq-read .pn{font-size:12px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--color-accent-800)}
.seq-read .pl{font-size:13.5px;color:var(--bone-dim);text-align:right;line-height:1.4}

/* The scrubber. Twelve ticks; the reached ones take the accent ink. This is a
   progress READOUT, not a control, so it is aria-hidden and the buttons below
   it are the keyboard path. */
.seq-bar{display:flex;gap:3px;margin-top:var(--sp-3)}
.seq-bar i{flex:1;height:3px;border-radius:2px;background:var(--color-neutral-300);
  transition:background-color var(--dur-2) var(--ease-out),transform var(--dur-2) var(--ease-out);
  transform-origin:center bottom}
.seq-bar i.on{background:var(--color-accent-500);transform:scaleY(1.8)}

/* KEYBOARD AND REDUCED-MOTION PATH. A scroll-driven sequence is unreachable
   from a keyboard and pointless under reduced motion, so the same phases are
   also a labelled list of buttons. It is not a fallback that appears when
   something fails — it is always there, always in the tab order, and the two
   drivers write the same state. */
.seq-steps{display:flex;flex-wrap:wrap;gap:var(--sp-1);margin-top:var(--sp-3)}
.seq-steps button{font:inherit;font-size:11.5px;font-weight:700;cursor:pointer;
  border:1px solid var(--color-neutral-300);background:var(--color-card);color:var(--bone-dim);
  border-radius:var(--radius-pill);padding:3px 10px;
  transition:transform var(--dur-1) var(--ease-spring),
             border-color var(--dur-1) var(--ease-out),color var(--dur-1) var(--ease-out)}
.seq-steps button:hover{border-color:var(--color-accent-400);color:var(--color-accent-800)}
.seq-steps button:active{transform:scale(.96)}
.seq-steps button[aria-current="true"]{background:var(--color-accent-100);
  border-color:var(--color-accent-500);color:var(--color-accent-800)}

/* The scrolling half: one block per phase, each one screen-ish tall. */
.seq-copy{display:flex;flex-direction:column;gap:0}
.seq-item{min-height:62vh;display:flex;flex-direction:column;justify-content:center;
  padding:var(--sp-5) 0;border-bottom:1px solid var(--color-neutral-200)}
.seq-item:last-child{border-bottom:none}
.seq-item h3{margin:0 0 var(--sp-2);font-size:20px;letter-spacing:-.01em}
.seq-item .sn{font-size:11px;font-weight:700;letter-spacing:.10em;text-transform:uppercase;
  color:var(--bone-faint);margin:0 0 var(--sp-2)}
.seq-item p{margin:0 0 var(--sp-3);font-size:15px;line-height:1.68;color:var(--bone-dim);
  max-width:52ch}
.seq-item p:last-child{margin-bottom:0}
.seq-item.is-on h3{color:var(--color-accent-800)}
.seq-item h3{transition:color var(--dur-2) var(--ease-out)}

/* ---- 9. the live product surface ----------------------------------------- */
/* A real slice of the application, running. Not a screenshot and not a mock:
   engine/mc-schedule-engine.js is loaded on this page, given a real MSPDI plan,
   and asked for a real CPM and a real Monte Carlo distribution. Everything in
   here is chrome around numbers this page computed. */
.live{border:1px solid var(--color-neutral-300);border-radius:var(--radius-lg);
  background:var(--color-card);box-shadow:var(--shadow-sm),var(--shadow-lg);
  overflow:hidden;margin:var(--sp-5) 0}
.live-bar{display:flex;align-items:center;gap:var(--sp-3);flex-wrap:wrap;
  padding:var(--sp-3) var(--sp-4);border-bottom:1px solid var(--color-neutral-200);
  background:var(--color-neutral-100)}
.live-bar .lt{font-size:12px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--bone-dim)}
.live-bar .ls{margin-left:auto;font-size:12px;color:var(--bone-faint);
  font-variant-numeric:tabular-nums}
.live-body{padding:var(--sp-4)}
.live-cols{display:grid;grid-template-columns:minmax(0,1.35fr) minmax(0,1fr);gap:var(--sp-5)}

/* The dense register. This is the product's own table density, unchanged: it is
   the point of the section that the marketing page does not loosen it. */
.live table{width:100%;border-collapse:collapse;font-size:12.5px}
.live th{text-align:left;font-weight:700;color:var(--bone-faint);font-size:10.5px;
  letter-spacing:.07em;text-transform:uppercase;padding:0 var(--sp-3) var(--sp-2) 0;
  border-bottom:1px solid var(--color-neutral-300);white-space:nowrap}
.live td{padding:5px var(--sp-3) 5px 0;border-bottom:1px solid var(--color-neutral-200);
  color:var(--bone-dim);vertical-align:baseline}
.live td.n{text-align:right;font-variant-numeric:tabular-nums;
  padding-right:0;color:var(--color-text)}
.live td.nm{color:var(--color-text);font-weight:600}
.live tr.crit td.nm{color:var(--st-critical-ink)}
.live .chip{display:inline-block;font-size:10.5px;font-weight:700;letter-spacing:.04em;
  border-radius:var(--radius-pill);padding:1px 7px;white-space:nowrap}
.live .chip.c{background:var(--st-critical-tint);color:var(--st-critical-ink)}
.live .chip.h{background:var(--st-healthy-tint);color:var(--st-healthy-ink)}

/* The histogram and the Gantt are inline SVG written by site-live.js. Both are
   sized in CSS and drawn in user units, so neither reflows on resize. */
.live svg{display:block;width:100%;height:auto}
.live-fig{margin:0 0 var(--sp-4)}
.live-fig h3{margin:0 0 var(--sp-2);font-size:12px;font-weight:700;letter-spacing:.07em;
  text-transform:uppercase;color:var(--bone-faint)}
.live-fig figcaption{font-size:12px;line-height:1.55;color:var(--bone-faint);
  margin-top:var(--sp-2);max-width:56ch}

/* THE THREE HEADLINE FIGURES. Set as type (§ 5), tabular, on a rule. */
.live-figs{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:var(--sp-4);padding:var(--sp-4) 0 0;border-top:1px solid var(--color-neutral-200);
  margin-top:var(--sp-4)}
/* Three dates side by side need a size that fits the narrowest of the three at
   the narrowest column, or one of them wraps to three lines and the row stops
   reading as a set. Smaller than the § 5 display figure for that reason alone. */
.live-figs .fig-n{font-size:clamp(19px,1.8vw,23px);line-height:1.1;text-wrap:balance}

/* A live panel that could not compute takes the same treatment as the stage:
   the refusal goes WHERE THE NUMBER WOULD HAVE BEEN, in the ink the product
   uses for a missing feed, and it names what failed. */
.live-fail{border:1px solid var(--st-nodata-ink);background:var(--st-nodata-tint);
  border-radius:var(--radius-md);padding:var(--sp-4);font-size:13.5px;line-height:1.6;
  color:var(--color-text);margin:var(--sp-3) 0}
.live-fail b{font-weight:700}

/* SVG classes — every stroke and fill below is a token, set through CSS rather
   than as a presentation attribute, so the drawing follows the theme. */
.sv-axis{stroke:var(--color-neutral-300);stroke-width:1;fill:none}
.sv-grid{stroke:var(--color-neutral-200);stroke-width:1;fill:none}
.sv-bar{fill:var(--series-1-fill);stroke:var(--series-1);stroke-width:.5}
.sv-bar-c{fill:var(--st-critical-tint);stroke:var(--st-critical-ink);stroke-width:.7}
.sv-bar-f{fill:var(--st-healthy-tint);stroke:var(--st-healthy-ink);stroke-width:.7}
.sv-mark{stroke:var(--st-critical-ink);stroke-width:1.6;fill:none}
.sv-mark-2{stroke:var(--color-accent-600);stroke-width:1.6;fill:none;stroke-dasharray:4 3}
.sv-lab{font-family:var(--font-body);font-size:9.5px;font-weight:700;fill:var(--bone-faint);
  letter-spacing:.03em}
.sv-val{font-family:var(--font-body);font-size:10.5px;font-weight:700;fill:var(--color-text);
  font-variant-numeric:tabular-nums}
.sv-ink{fill:var(--st-critical-ink)}
.sv-ink-2{fill:var(--color-accent-700)}

/* The simulation draws in one frame per chunk so the main thread is never held.
   While it runs, the bar states how far it is — as a count, not as a bar that
   moves on its own. */
.live-run{font-size:12px;color:var(--bone-faint);font-variant-numeric:tabular-nums}
/* The one sentence the whole panel exists to produce, set on a rule under the
   three figures. It states a percentile this page measured, not one it was
   told — see the header of site-live.js. */
.live-verdict{margin:var(--sp-4) 0 0;padding-top:var(--sp-3);
  border-top:1px solid var(--color-neutral-200);
  font-size:13.5px;line-height:1.6;color:var(--bone-dim)}
.live-verdict b{color:var(--color-text)}

/* A caption that belongs to the accessibility tree and not to the layout. Used
   on the live <table>, which has a visible <h3> above it but needs its own
   caption for a reader who lands on the table alone. NOT display:none — that
   removes it from the accessibility tree as well, which is the whole failure
   this class exists to avoid. */
.sr-only{position:absolute!important;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}

/* ---- 10. marks, rules and the drawing-set furniture ---------------------- */
/* A hairline with real tick marks at its ends, the way a dimension line is
   drawn on a sheet. --rule-ink is the token mc-ui.css declares for a hairline
   over an unknown fill, which is exactly this weight of line. */
.rule-dim{position:relative;height:1px;background:var(--rule-ink);margin:var(--sp-5) 0}
.rule-dim::before,.rule-dim::after{content:"";position:absolute;top:-4px;width:1px;height:9px;
  background:var(--rule-ink)}
.rule-dim::before{left:0}
.rule-dim::after{right:0}

/* A section head with an index number, set the way a drawing sheet numbers its
   details. The number is decorative in the accessibility tree (the heading
   carries the meaning) so it is marked aria-hidden in the markup. */
.sechead{display:flex;align-items:baseline;gap:var(--sp-4);margin:0 0 var(--sp-4)}
/* --color-accent-800, NOT -600, and the difference is measured rather than
   taste: accent-600 on the page background is 4.15:1, and this is 11px bold —
   small text, so the bar is 4.5:1 and it failed. accent-800 measures 7.7:1.
   The border stays at accent-300 because a border is not text and carries no
   information the number does not. */
.sechead .ix{font-family:var(--font-body);font-size:11px;font-weight:700;letter-spacing:.14em;
  color:var(--color-accent-800);border:1px solid var(--color-accent-300);border-radius:var(--radius-sm);
  padding:2px 7px;flex:0 0 auto}
.sechead h2{margin:0}

/* The claim strip: a fact set beside the thing that makes it checkable. Same
   idea as site.css's .facts, but at display scale, for the three claims the
   page is willing to lead with. */
.claims{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));
  gap:var(--sp-5);margin:var(--sp-5) 0;padding:var(--sp-5) 0;
  border-top:1px solid var(--color-neutral-300);border-bottom:1px solid var(--color-neutral-300)}
.claims .cl b{display:block;font-size:13px;color:var(--color-text);margin-bottom:var(--sp-2)}
.claims .cl p{margin:0;font-size:13px;line-height:1.6;color:var(--bone-dim)}

/* ---- 11. responsive ------------------------------------------------------ */
/* THE STICKY SEQUENCE COLLAPSES BELOW 900px, and that is a correctness decision
   rather than a layout one: on a phone the sticky stage and the copy would each
   get half a short viewport, so neither is readable and the relationship the
   motion exists to explain is lost. Below the breakpoint the stage stops
   sticking, the phases become a plain ordered list, and the buttons are the
   whole driver. Nothing is hidden. */
@media (max-width:980px){
  .hero-grid{grid-template-columns:minmax(0,1fr);gap:var(--sp-5)}
  .live-cols{grid-template-columns:minmax(0,1fr)}
}
@media (max-width:900px){
  .seq-grid{grid-template-columns:minmax(0,1fr);gap:var(--sp-4)}
  .seq-sticky{position:static;height:auto}
  .seq-stage{min-height:280px}
  .seq-item{min-height:0;padding:var(--sp-4) 0}
  .seq-copy{gap:0}
}
@media (max-width:720px){
  .stage{min-height:220px}
  .stage-svg{max-height:46vh}
  .claims{gap:var(--sp-4)}
  .seq-read{flex-direction:column;gap:var(--sp-1);align-items:flex-start}
  .seq-read .pl{text-align:left}
}

/* ---- 12. print ----------------------------------------------------------- */
/* Everything animated resolves to its resting state, the sticky stage releases,
   and the stage keeps whatever geometry it had drawn — an SVG prints. */
@media print{
  html.js [data-reveal]{opacity:1;transform:none}
  .seq-sticky{position:static;height:auto}
  .seq-item{min-height:0;page-break-inside:avoid}
  .lift::after,.lit::after,.grain::before{display:none}
  .seq-steps,.seq-bar,.skip{display:none}
  .live{box-shadow:none}
}
