/* Mathweave board engine visual states.
   All rules are below the element mounted by BoardEngine, so a page may carry
   two copies of one authored drawing without sharing state. */

/* `syncMastheadHeight()` reads the sticky public header into --masthead-h.
   A board is the page-scroll target; a working line is included so any
   future line-level scrollIntoView call clears the same masthead. */
body.lp .wbd,
body.lp .wbd [data-board-line]{
  scroll-margin-top:calc(var(--masthead-h,var(--top-h,71px)) + .5rem)
}

/* THE PUBLIC BOARD FITS BELOW ITS STICKY MASTHEAD (chapter ruling 8.18,
   public audit item 9).  Every lesson board with a written column takes it
   (first cut: an audited inventory of 22).  Above phone width they become
   the same co-visible construction as practice: figure left, writing right.
   The board owns the viewport cap, the written column owns overflow, and the
   tray remains a separate grid row so Next step and Voice never scroll away.

   The transparent tail is a reading affordance, not decoration.  Equal
   padding below the answer lets its last ink scroll wholly above the fade.
   Every rule is inside min-width:701px, so the existing phone stack is
   untouched. */
@media (min-width:701px){
  body.lp .wbd[data-wb-scroll]{
    --wb-scroll-fade:2.5rem;
    display:grid;
    grid-template-columns:minmax(0,1.45fr) minmax(0,1fr);
    grid-template-rows:minmax(0,1fr) auto;
    column-gap:var(--s4);
    align-items:start;
    min-height:0;
    max-height:calc(100vh - var(--top-h) - var(--s2) - var(--s2))
  }
  body.lp .wbd[data-wb-scroll]>.svfig,
  body.lp .wbd[data-wb-scroll]>.pfig,
  body.lp .wbd[data-wb-scroll]>.wf{
    grid-column:1;
    grid-row:1;
    position:sticky;
    top:calc(var(--top-h) + var(--s2));
    align-self:start;
    width:100%;
    min-width:0;
    margin-bottom:0
  }
  body.lp .wbd[data-wb-scroll]>.wb-body{
    grid-column:2;
    grid-row:1;
    align-self:stretch;
    min-width:0;
    min-height:0;
    height:100%;
    max-height:100%;
    overflow-x:hidden;
    overflow-y:auto;
    overscroll-behavior:contain;
    scrollbar-gutter:stable;
    scroll-behavior:smooth;
    scroll-padding-bottom:var(--wb-scroll-fade);
    padding-bottom:var(--wb-scroll-fade);
    -webkit-mask-image:linear-gradient(to bottom,#000 0,
      #000 calc(100% - var(--wb-scroll-fade)),transparent 100%);
    mask-image:linear-gradient(to bottom,#000 0,
      #000 calc(100% - var(--wb-scroll-fade)),transparent 100%)
  }
  /* The question heads the written column and sticks while the lines
     scroll (founder, 2026-09-06). */
  body.lp .wbd[data-wb-scroll]>.wb-body>.wb-stem{
    position:sticky;top:0;z-index:1;margin:0 0 .6rem;
    padding:.35rem 0 .55rem;background:#FFFDFA;
    border-bottom:1px solid var(--rule);
    font-size:var(--t-small);line-height:1.45;color:var(--ink-soft)
  }
  body.lp .wbd[data-wb-scroll]>.wb-tray{
    grid-column:1/-1;
    grid-row:2;
    position:relative;
    z-index:1
  }
}
@media (min-width:701px) and (prefers-reduced-motion:reduce){
  body.lp .wbd[data-wb-scroll]>.wb-body{scroll-behavior:auto}
}
/* PHONE TRAY: the step group takes the full row and the two buttons never
   break mid-word ("Turn voice off" ran to three lines inside a 60px group
   on every board, evening audit 2026-09-06). */
@media (max-width:700px){
  body.lp .wbd .wb-stem{display:none}
  /* The buttons keep their words whole; the tray, not the page, absorbs
     it: overflow-x:clip makes the tray's min-content zero for the board's
     grid (a nowrap without it widened the page to 461 px), and the step
     group wraps its two buttons onto a second row if the width is short. */
  body.lp .wbd{min-width:0}
  body.lp .wbd>.wb-tray{flex-wrap:wrap;row-gap:.5rem;min-width:0;
    overflow-x:clip}
  body.lp .wbd>.wb-tray .wb-step{flex:1 1 auto;min-width:0;flex-wrap:wrap}
  body.lp .wbd>.wb-tray .wb-next,
  body.lp .wbd>.wb-tray .vtog{flex:0 0 auto;white-space:nowrap}
}

/* The drawing keeps its final box from the first frame.  Only its ink waits. */
[data-board-engine] .svfig.svwait,
[data-board-engine] .pfig.svwait{visibility:hidden}

/* Staged construction.  data-s is the figure's own clock; JavaScript adds
   .on when an element is earned.  A growth element starts at its left edge. */
[data-board-engine] .svfig .st,
[data-board-engine] .pfig .st{
  opacity:0;
  transition:opacity .4s ease var(--d,0s)
}
[data-board-engine] .svfig .st.on,
[data-board-engine] .pfig .st.on{opacity:1}
[data-board-engine] .svfig .st[data-pt],
[data-board-engine] .pfig .st[data-pt]{transition:opacity .22s ease}
[data-board-engine] .svfig .st.g,
[data-board-engine] .pfig .st.g{
  transform:scaleX(.001);
  transform-origin:left center;
  transform-box:fill-box;
  transition:opacity .4s ease var(--d,0s),
    transform .5s cubic-bezier(.2,.7,.3,1) var(--d,0s)
}
[data-board-engine] .svfig .st.g.on,
[data-board-engine] .pfig .st.g.on{transform:none}

/* the mover: blue cells travelling under a purple ring once the line
   that fixes the transfer's size has landed (data-go) */
[data-board-engine] .svfig .mv{position:static;outline:none;
  transition:opacity .4s ease var(--d,0s),
    transform .7s cubic-bezier(.2,.7,.3,1) .15s
}
[data-board-engine] .svfig .mv.gone{transform:translate(var(--mx),var(--my))}
/* A zero end-gap is the one mover whose destination is a width, not an
   address: its left edge holds while the amber interval closes to zero. */
[data-board-engine] .svfig .mv.gapzero.gone{transform:scaleX(.001)}
/* THE RUN THAT LEAVES (2026-09-04, the bar-mover grammar; see `_goes`).
   `.g` grows a bar from its own left edge because a bar is BUILT from
   there. A taking is not built, it is removed, so the paper wipes across
   it from the end the quantity comes off — `.gx` from the run's right
   edge, `.gxl` from its left — and the blue underneath disappears in the
   direction of the taking. Same half-second and same curve as `.g`, so a
   departure and an arrival are the same gesture read in two directions,
   which is the whole of the vocabulary a child has to learn here. */
[data-board-engine] .svfig .st.gx{transform:scaleX(.001);transform-origin:right center;
  transform-box:fill-box;
  transition:opacity .4s ease var(--d,0s),
    transform .5s cubic-bezier(.2,.7,.3,1) var(--d,0s)}
[data-board-engine] .svfig .st.gx.gxl{transform-origin:left center}
[data-board-engine] .svfig .st.gx.on{transform:none}
/* THE BAR THAT HOLDS SAYS SO, ONCE (2026-09-04, the bar-mover grammar,
   treatment (d)). In a one-quantity-unchanged shape the teaching is a
   pair: one row changes and the OTHER DOES NOT, and "does not" is the
   half a still drawing cannot say — a ring appearing round a bar reads as
   "look here", not as "this one held". So the ring gives one small beat
   of its own on the line that licenses it, at the same half second as the
   gesture on the other row, and then is exactly the ring it was.
   IT IS THE RING THAT MOVES, NEVER THE BAR. The cells under it are the
   quantity, and a quantity that visibly swells is a quantity that
   changed, which is the opposite of what this says.
   `transform`, not `opacity`: opacity on an `.st` element is the step
   engine's own property and a second writer on it is a cascade fight
   nobody can read (the same rule `.pgb` is written under). */
[data-board-engine] .svfig .kpulse{transform-box:fill-box;transform-origin:center}
[data-board-engine] .svfig .kpulse.on{animation:kpul .5s cubic-bezier(.2,.7,.3,1) var(--d,0s) 1}
@keyframes kpul{0%{transform:scale(1)}
  45%{transform:scale(1.04)}
  100%{transform:scale(1)}}
@media (prefers-reduced-motion:reduce){[data-board-engine] .svfig .kpulse.on{animation:none}}
/* REDUCED MOTION IS THE FINAL STATE, not a slower journey. Only the
   TRANSFORM is dropped: the step engine's opacity fade is how a board
   says "this line's mark has landed" and it is not motion in the sense
   the setting is about, so it stays and the beats remain readable. The
   mover keeps its `--mx/--my` translate — that IS its final state — it
   simply arrives there without travelling. `.g` is included because it
   has had the same defect since the solve figures were written: a bar
   scaled from .001 to 1 is motion, and nothing was switching it off. */
@media (prefers-reduced-motion:reduce){
  [data-board-engine] .svfig .st.g,[data-board-engine] .svfig .st.gx{transform:none;
    transition:opacity .4s ease var(--d,0s)}
  [data-board-engine] .svfig .mv{transition:opacity .4s ease var(--d,0s)}}
/* THE PASTEL CELL AND ITS TWO INKS (2026-09-02). The hairline between
   cells was PAPER, which was legible only while the fill was saturated:
   paper on the old bar blue is 4.1:1, paper on pastel sky is 1.94:1, so
   the seam would simply have gone. It is the same ink as the bar's edge
   now, thinner and lighter — a cut inside a bar is quieter than the bar's
   own border, never louder. The border itself is `.cout`, one rect per
   run, because two neighbours both draw the edge they share and per-cell
   strokes alone put the heaviest line in the wrong place.
   The SECOND kind (2026-08-28, --kind2 in tokens.py) takes both inks on
   the same terms, so a run of pink cells reads as cut into units exactly
   as a run of blue or lilac ones does. */
[data-board-engine] .svfig .c{fill:var(--bar);stroke:var(--ink);stroke-width:var(--fig-seam);
  stroke-opacity:var(--seam-a)}
[data-board-engine] .svfig .cp{fill:var(--change);stroke:var(--ink);stroke-width:var(--fig-seam);
  stroke-opacity:var(--seam-a)}
[data-board-engine] .svfig .ck{fill:var(--kind2);stroke:var(--ink);stroke-width:var(--fig-seam);
  stroke-opacity:var(--seam-a)}
/* THE KIND THAT IS GONE (2026-09-04, the generated bars' kind palette).
   Sold, spent, taken away: it is NOT a change the other side also gets, so
   it may not take the lilac fill, and it is NOT a grouping of pieces that
   are still there, so it may not take the `grp` ring either. It DIMS TO THE
   PAPER and keeps the same dashed lilac edge `.ghost` draws round the hole
   a departing quantity leaves — one vocabulary for leaving, two states of
   it: `.ghost` is the hole, `.cg` is the amount that left, still labelled
   where it stood. Its label is INK, never `vw`: paper text on a paper fill
   is no text at all. */
[data-board-engine] .svfig .cg{fill:var(--paper);stroke:var(--change);stroke-width:2;
  stroke-dasharray:4 3}
/* THE SECOND KIND OF GONE (lead ruling 2026-09-03 19:10, on the fifteen
   `remn` questions the movers pass was blocked on). Two spends out of one
   bar are two KINDS (founder, 2026-08-28: "not the same Devi entity in
   purple"), and every departure dims to the paper (the cg treatment,
   2026-09-04). Both hold because the fill is not the only thing a gone
   block has: the FILL says it went, and the DASHED EDGE says which kind
   went — `--change` for the first taking, `--kind2` for the second. Same
   paper, same dash, same weight; only the hue differs, which is exactly
   the difference the ruling is about. Reversible: the founder may overrule
   it in the morning, and one selector is the whole of it. */
[data-board-engine] .svfig .cg2{fill:var(--paper);stroke:var(--kind2);stroke-width:2;
  stroke-dasharray:4 3}
/* the two rings that carry the CHANGE's own colour rather than ink. They
   keep it — a ring round the piece that moves is saying which KIND of
   thing moved, which is a colour role and not decoration — but a pastel
   line has to be drawn heavier than a saturated one to say the same thing
   at 2.05:1, so both go up a step (2026-09-02). */
[data-board-engine] .svfig .ghost{fill:none;stroke:var(--change);stroke-width:2;
  stroke-dasharray:4 3}
[data-board-engine] .svfig .mvring{fill:none;stroke:var(--change);stroke-width:2.8}
/* the same ring for a SECOND-KIND run — a ring points at the thing it
   rings, so it may not disagree with it about what kind that thing is. */
[data-board-engine] .svfig .mvring2{fill:none;stroke:var(--kind2);stroke-width:2.8}

/* Narrated-part lighting changes opacity only.  Colour continues to encode
   the authored teaching role and is never repurposed as focus chrome. */
[data-board-engine] .svfig svg .pp,
[data-board-engine] .pfig .pp{transition:opacity .3s ease}
[data-board-engine] .svfig.litmode .pp,
[data-board-engine] .pfig.litmode .pp{opacity:.16}
[data-board-engine] .svfig.litmode .pp.litp,
[data-board-engine] .pfig.litmode .pp.litp{opacity:1}
[data-board-engine] .svfig.noanim svg .pp,
[data-board-engine] .pfig.noanim .pp{transition:none}

/* Withheld authored content uses visibility, not display or opacity.  It
   therefore remains measurable by SVG getBBox while the lighting cascade
   remains the sole owner of opacity. */
[data-board-engine] .svfig .pp-reveal,
[data-board-engine] .pfig .pp-reveal{visibility:hidden}
[data-board-engine] .svfig .pp-reveal.rvon,
[data-board-engine] .pfig .pp-reveal.rvon{
  visibility:visible;
  animation:board-rvin .36s ease
}

/* pp-add is construction geometry.  Either its own data-step (.rvon above)
   or a MARKS-earned pp-added state can reveal it; the two routes compose. */
[data-board-engine] .svfig .pp-add,
[data-board-engine] .pfig .pp-add{visibility:hidden}
[data-board-engine] .svfig .pp-add.pp-added,
[data-board-engine] .pfig .pp-add.pp-added{
  visibility:visible;
  animation:board-rvin .36s ease
}

/* A PLACE value is created only after its zero-based working line lands. */
[data-board-engine] .svfig .pp-place,
[data-board-engine] .pfig .pp-place,
[data-board-engine] .svfig .pp-lead[data-runtime="1"],
[data-board-engine] .pfig .pp-lead[data-runtime="1"]{
  animation:board-rvin .36s ease
}
[data-board-engine] .pp-place-cell{
  font-weight:700;
  margin-left:.4em;
  white-space:nowrap;
  animation:board-rvin .36s ease
}
@keyframes board-rvin{from{opacity:0}}

/* A REVEALED PART THAT ARRIVES A PIECE AT A TIME (2026-09-04, founder on
   Volume 6.3's max-addable question: "the diagram didnt make any
   difference, nothing was really shown"). The step engine's clock is the
   WORKING LINE and that does not change: one line, one reveal. What this
   adds is an order INSIDE that one reveal, for the one shape where the
   count IS the argument — four columns of three cubes going into a well.

   THE DELAY IS DATA, NOT A CLASS. The figure writes `--acd` on each piece
   in seconds; twelve near-identical delay rules in a shared stylesheet is
   the same authoring mistake as a coordinate in an implementer's hands
   (AGENTS.md §5). `both` is load-bearing: without it the piece sits at full
   opacity through its own delay and every stagger is invisible.

   `.vkeep` is the opposite gesture — a thing the step says must NOT change.
   It pulses twice on arrival and then stays put at full strength, so it
   reads as a lock rather than as a finger (§2.3b: a POINT does not stay,
   but a CONSTRUCTION does, and a locked frame is a construction). */
[data-board-engine] .svfig .pp-reveal.rvon .ac,[data-board-engine] .pfig .pp-reveal.rvon .ac{
  animation:acrise .30s ease both;animation-delay:var(--acd,0s)}
@keyframes acrise{from{opacity:0;transform:translateY(9px)}}
[data-board-engine] .svfig .pp-reveal.rvon .vkeep,[data-board-engine] .pfig .pp-reveal.rvon .vkeep{
  animation:vkeep .6s ease 2}
@keyframes vkeep{50%{opacity:.18}}
/* THE POUR (2026-09-04, founder on Volume 6.4's relay question: "at last
   step, as the water is transferred, can we have a nice css showing
   transferring of water?").

   THE ENGINE HAS NO "GOES AWAY ON STEP N" HOOK, AND DOES NOT NEED ONE. A
   body of water that a later line empties is AUTHORED AFTER that line's own
   revealed group, so the CSS reaches it with a sibling combinator: the
   emptying step turns `rvon` and everything it names downstream drains. No
   JS, no second data channel, and the clock is still the working line.
   `wfill` rises with its own step; `wbody-*` is emptied by the step named
   in the rule; `wstream` is the arc between the two, and it does not stay.

   CLIP, NOT SCALE. `clip-path:inset()` runs a straight edge down the body
   and leaves every drawn face the shape it was; a `scaleY` squashes an
   isometric top face on the way and draws, for a second, a level the
   geometry does not mean. The percentage resolves against the element's own
   fill box, so a tank empties to its own floor and no coordinate from any
   figure reaches this stylesheet. 1.2s is the founder's own figure. */
[data-board-engine] .svfig .pp-reveal.rvon.wfill{animation:wrise 1.2s ease-in-out both}
@keyframes wrise{from{clip-path:inset(100% 0 0 0)} 98%{clip-path:inset(0 0 0 0)} to{clip-path:none}}
[data-board-engine] .svfig .wstream{opacity:0}
[data-board-engine] .svfig .pp-reveal.rvon.wstream{animation:wstream 1.2s ease both}
@keyframes wstream{0%,100%{opacity:0}22%,68%{opacity:1}}
/* AFTER the rise on purpose: Tank B rises on line 3 and empties on line 5,
   the two selectors weigh the same, and source order is what decides which
   of its two lives the board is showing. */
[data-board-engine] .svfig .pp-part-pourab.rvon ~ .wbody-a,
[data-board-engine] .svfig .pp-part-pourbc.rvon ~ .wbody-b{
  animation:wfall 1.2s ease-in-out both}
@keyframes wfall{from{clip-path:none} 2%{clip-path:inset(0 0 0 0)} to{clip-path:inset(100% 0 0 0)}}
@media (prefers-reduced-motion:reduce){
  /* final state only: the levels land, the arc never shows. `opacity` is
     restated because the lighting engine's `.svfig.litmode .pp.litp` weighs
     the same as the base `.wstream` rule and would otherwise leave the arc
     standing at full strength on a board that has no animation to take it
     away again. */
  [data-board-engine] .svfig .pp-reveal.rvon.wfill{animation:none}
  [data-board-engine] .svfig .pp-reveal.rvon.wstream{animation:none;opacity:0}
  [data-board-engine] .svfig .pp-part-pourab.rvon ~ .wbody-a,
  [data-board-engine] .svfig .pp-part-pourbc.rvon ~ .wbody-b{
    animation:none;clip-path:inset(100% 0 0 0)}}
/* THE CUT (the cube grammar). Pieces are AUTHORED APART so the ink box FIT solves from, and the authored viewBox a [data-step] figure keeps, already hold the separated state; at rest each piece is pulled back on to its join by its own vector, and the step that names the cut releases it. The cut's revealed group is authored BEFORE the pieces so the sibling combinator reaches them. Print, no-JS and reduced motion get the landed, separated figure. --csx/--csy are user units on the figure's own space. */
[data-board-engine] .svfig .csep,[data-board-engine] .pfig .csep{transform:translate(var(--csx,0px),var(--csy,0px));transition:transform .55s cubic-bezier(.2,.7,.3,1)}
[data-board-engine] .svfig .pp-cut.rvon ~ .csep,[data-board-engine] .pfig .pp-cut.rvon ~ .csep{transform:none}
@media (prefers-reduced-motion:reduce){[data-board-engine] .svfig .csep,[data-board-engine] .pfig .csep{transition:none;transform:none}}
@media (prefers-reduced-motion:reduce){
  [data-board-engine] .svfig .pp-reveal.rvon,
  [data-board-engine] .pfig .pp-reveal.rvon,
  [data-board-engine] .svfig .pp-add.pp-added,[data-board-engine] .pfig .pp-add.pp-added,
  [data-board-engine] .svfig .pp-reveal.rvon .ac,[data-board-engine] .pfig .pp-reveal.rvon .ac,
  [data-board-engine] .svfig .pp-reveal.rvon .vkeep,[data-board-engine] .pfig .pp-reveal.rvon .vkeep,
  [data-board-engine] .svfig .pp-place,
  [data-board-engine] .pfig .pp-place,
  [data-board-engine] .svfig .pp-lead[data-runtime="1"],
  [data-board-engine] .pfig .pp-lead[data-runtime="1"]{animation:none}}
/* FOLD FIRST (2026-08-28, the Angles handoff §A3). The eight angle-fold
   figures wrap the moved paper in `.pp-flap` and write their own crease
   point into `transform-origin` and the crease's unit direction into
   `--fold-ax` / `--fold-ay`, so the page needs this rule and nothing
   else — no figure list, no per-question angle. NO `animation-fill-mode`,
   the same reasoning as `.rvon` above: an animation with no fill hands
   the property straight back to the cascade when it ends, so the resting
   render is the authored, landed one. `foldFirst` adds the class on
   solution open and drops it on `animationend`, which is what makes it
   play ONCE — stepping through the working must not re-flip the paper. */
/* THE MEASURING SHEET is KIV with the construction node (founder,
   2026-08-28: "construction/measure-draw is KIV FOR P4 - removed from
   P6"). It was built and its print MEASURED — a 210mm sheet as the
   widest thing on the page, one lattice square printing at 10mm off
   `drawn._CELL`, and a calibration ruler that came out of headless
   Chrome at 100.499mm outer-edge to outer-edge against a claimed 100.000
   (its two end ticks are 0.5mm wide, so centre to centre is exact). The
   numbers are in the platform queue; the surface is not on the page.
   ONLY the fold rule below survived that ruling. */
[data-board-engine] .svfig .pp-flap{transform-box:view-box}
/* THE CURVE IS THE PAPER'S, NOT THE UI'S (2026-08-28, visual pass M1).
   cubic-bezier(.4,0,.2,1) is the standard interface ease: it dumps most
   of its travel early, so a 180-degree fold turned 152 degrees in the
   first quarter of its time and then crept through the last 12 — read as
   a snap followed by a stall, which is the opposite of how a sheet of
   paper falls. (.5,.1,.4,1) spends the turn evenly enough that the crease
   is legible the whole way over. The degrees at t = .25/.50/.75 are
   measured off the curve, not guessed. */
[data-board-engine] .svfig.foldin .pp-flap{animation:ppfold .85s cubic-bezier(.5,.1,.4,1)}
@keyframes ppfold{
  from{transform:rotate3d(var(--fold-ax),var(--fold-ay),0,180deg)}}
@media (prefers-reduced-motion:reduce){
  [data-board-engine] .svfig.foldin .pp-flap{animation:none}}
/* WHAT THE FOLD PRODUCES WAITS FOR THE FOLD (2026-08-28, visual pass M1).
   Thirty-one groups across the fold figures are marked `.pp-arrive` — the
   labels and marks that only exist BECAUSE the paper was folded — and
   nothing acted on the class, so they were on screen before the flap that
   makes them true had moved. R54 in the one place the drawing, rather
   than the working, is doing the asserting. They are hidden for the
   duration of the fold and fade back as it lands; base markup keeps them
   visible, so print, no-JS and reduced motion all show the finished
   figure, which is the same contract `.pp-flap` above lives by. */
[data-board-engine] .svfig .pp-arrive{transition:opacity .18s linear}
[data-board-engine] .svfig.foldin .pp-arrive{opacity:0;transition:none}
@media (prefers-reduced-motion:reduce){
  [data-board-engine] .svfig.foldin .pp-arrive{opacity:1}}
/* THE FOLD ON ITS LINE (2026-09-04, founder §K.4). The page already folds paper on solution open (.pp-flap, Angles ruling 2026-08-28, stands). A fold the WORKING names moves when that line lands, the way the pour and the cut do: a sibling combinator off the step's revealed group, no JS. The clock group is authored BEFORE the flap (~ reaches forward only). The piece is authored LANDED and pulled back at rest by its own half turn onto the ghost the figure already draws. Print, no-JS, reduced motion and svgcheck get the finished figure. Rotate, never tween: a rotation about an in-plane axis is the true orthographic shadow of a rigid fold. .6s on the paper's own curve (the M1 finding on .pp-flap). Two lanes because ~ reaches every later sibling. */
[data-board-engine] .svfig .pp-fold-a,[data-board-engine] .pfig .pp-fold-a,
[data-board-engine] .svfig .pp-fold-b,[data-board-engine] .pfig .pp-fold-b{transform-box:view-box;
  transform:rotate3d(var(--fold-ax,0),var(--fold-ay,1),0,180deg);
  transition:transform .6s cubic-bezier(.5,.1,.4,1)}
[data-board-engine] .svfig .pp-foldline-a.rvon ~ .pp-fold-a,[data-board-engine] .pfig .pp-foldline-a.rvon ~ .pp-fold-a,
[data-board-engine] .svfig .pp-foldline-b.rvon ~ .pp-fold-b,[data-board-engine] .pfig .pp-foldline-b.rvon ~ .pp-fold-b{
  transform:none}
/* What the fold produces waits for the fold: visibility delayed, not opacity (the lighting engine's own property). */
[data-board-engine] .svfig .pp-reveal.rvon.pp-fold-late,[data-board-engine] .pfig .pp-reveal.rvon.pp-fold-late{
  transition:visibility 0s linear .48s;animation-delay:.48s;
  animation-fill-mode:backwards}
@media (prefers-reduced-motion:reduce){
  [data-board-engine] .svfig .pp-fold-a,[data-board-engine] .pfig .pp-fold-a,
  [data-board-engine] .svfig .pp-fold-b,[data-board-engine] .pfig .pp-fold-b{transition:none;transform:none}
  [data-board-engine] .svfig .pp-reveal.rvon.pp-fold-late,[data-board-engine] .pfig .pp-reveal.rvon.pp-fold-late{
    transition:none;animation-delay:0s}}

/* Reduced motion is completed by BoardEngine.step as one static state.  The
   CSS half removes every transition in case the preference changes while a
   mounted board is open. */
@media (prefers-reduced-motion:reduce){
  [data-board-engine] .svfig .st,
  [data-board-engine] .pfig .st,
  [data-board-engine] .svfig .mv,
  [data-board-engine] .pfig .mv,
  [data-board-engine] .svfig svg .pp,
  [data-board-engine] .pfig .pp{transition:none}
  [data-board-engine] .svfig .pp-reveal.rvon,
  [data-board-engine] .pfig .pp-reveal.rvon,
  [data-board-engine] .svfig .pp-add.pp-added,
  [data-board-engine] .pfig .pp-add.pp-added,
  [data-board-engine] .svfig .pp-place,
  [data-board-engine] .pfig .pp-place,
  [data-board-engine] .svfig .pp-lead[data-runtime="1"],
  [data-board-engine] .pfig .pp-lead[data-runtime="1"],
  [data-board-engine] .pp-place-cell{animation:none}
}
