/* Site-wide motion (assets/motion.js and the spark in assets/thread.js), 10 October 2026.
   Loaded on every page. Colours come from the shell tokens only. */

/* How it works: the four steps on one line that fills as the strip is scrolled past.
   motion.js adds .fx; without it the steps stay the row of numbered chips. */
#how .how-steps.fx{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;position:relative;--p:0;}
#how .how-steps.fx::before,#how .how-steps.fx::after{content:"";position:absolute;top:17px;left:12.5%;right:12.5%;height:2px;border-radius:2px;background:var(--line);}
#how .how-steps.fx::after{background:var(--signal);transform-origin:left center;transform:scaleX(var(--p));box-shadow:0 0 10px color-mix(in oklch,var(--signal) 70%,transparent);}
#how .how-steps.fx li{flex-direction:column;gap:8px;border:0;padding:0;background:none;text-align:center;font-size:.88rem;color:var(--ink-soft);position:relative;z-index:1;transition:color .3s;}
#how .how-steps.fx li::before{width:34px;height:34px;font-size:.86rem;background:var(--surface);color:var(--ink-soft);border:2px solid var(--line);transition:background-color .3s,border-color .3s,color .3s,box-shadow .3s;}
#how .how-steps.fx li.on{color:var(--ink);}
#how .how-steps.fx li.on::before{background:var(--signal);border-color:var(--signal);color:var(--signal-ink);box-shadow:0 0 0 5px color-mix(in oklch,var(--signal) 18%,transparent);}
@media (max-width:560px){
  #how .how-steps.fx{grid-template-columns:1fr;gap:12px;}
  #how .how-steps.fx::before,#how .how-steps.fx::after{top:17px;bottom:17px;left:17px;right:auto;width:2px;height:auto;}
  #how .how-steps.fx::after{transform-origin:center top;transform:scaleY(var(--p));}
  #how .how-steps.fx li{flex-direction:row;text-align:left;}
}

/* Every other step list on the site lights its steps in order as it is scrolled past.
   motion.js adds .bp-seq; without it, or with less motion, every step stays fully shown. */
.bp-seq > li{transition:opacity .45s cubic-bezier(0.16,1,0.3,1),transform .45s cubic-bezier(0.16,1,0.3,1);}
.bp-seq > li:not(.on){opacity:.35;transform:translateX(-6px);}

/* The headline and the next three blocks under it wait for their rise instead of being painted
   finished first, and so do the figures that count up (one on the first screen is set to 0 before it
   is let go).
   assets/theme.js adds bp-rise in the head; motion.js removes it once the rise is
   set up, or straight away if it will not run. If motion.js never arrives, they show after 2.5s. */
html.bp-rise h1,html.bp-rise h1 + *,html.bp-rise h1 + * + *,html.bp-rise h1 + * + * + *,
html.bp-rise .bp-facts dt,html.bp-rise .fx-stat,html.bp-rise .fx-big,html.bp-rise .intro-tally li > b{visibility:hidden;animation:bp-rise-safety 0s linear 2.5s forwards;}
/* visibility, not opacity, so a block that is meant to be faint (the blog's motif) keeps its own opacity */
@keyframes bp-rise-safety{to{visibility:visible}}

/* Text kept for screen readers when the visible copy is split up to animate it. */
.bp-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;}

/* A lone 1 in any counted figure glows once instead of counting from 0 to 1. */
@media (prefers-reduced-motion: no-preference){
  .bp-glowonce{animation:bp-glowonce 1.2s cubic-bezier(0.16,1,0.3,1) 1;}
}
@keyframes bp-glowonce{0%{text-shadow:0 0 0 transparent}35%{text-shadow:0 0 18px var(--signal)}100%{text-shadow:0 0 0 transparent}}

/* The spark at the head of the circuit line (assets/thread.js). Hidden until the trace is part drawn. */
.bp-trace-spark,.bp-trace-ring{opacity:0;transition:opacity .3s;}
.bp-trace.is-live .bp-trace-spark,.bp-trace.is-live .bp-trace-ring{opacity:1;}
.bp-trace-spark{fill:var(--on-brand);filter:drop-shadow(0 0 5px var(--signal)) drop-shadow(0 0 11px var(--signal));}
.bp-trace-ring{fill:none;stroke:var(--signal);stroke-width:1.5;transform-box:fill-box;transform-origin:center;}
@media (prefers-reduced-motion: no-preference){
  .bp-trace.is-live .bp-trace-ring{animation:bp-ring 1.6s cubic-bezier(0.16,1,0.3,1) infinite;}
}
@keyframes bp-ring{from{opacity:.9;transform:scale(.6)}to{opacity:0;transform:scale(2.6)}}

/* Lenis smooth scroll, from lenis.css 1.3.26. */
html.lenis,html.lenis body{height:auto;}
.lenis:not(.lenis-autoToggle).lenis-stopped{overflow:clip;}
.lenis [data-lenis-prevent],.lenis [data-lenis-prevent-wheel],.lenis [data-lenis-prevent-touch]{overscroll-behavior:contain;}
.lenis.lenis-smooth iframe{pointer-events:none;}

/* B01, smooth scroll: while Lenis runs it moves the page itself, so the site's own CSS smooth-jump
   (scroll-behavior:smooth in the shell) is switched off and the two never fight over one scroll.
   Without Lenis (less motion, or the script never arrives) the shell's own setting still applies. */
html.lenis{scroll-behavior:auto;}

@media (prefers-reduced-motion: no-preference){
  @view-transition{navigation:auto;types:bp-page;}
}
html:active-view-transition-type(bp-page) #nav{view-transition-name:bp-nav;}
html:active-view-transition-type(bp-page) .bp-callbar{view-transition-name:bp-callbar;}
html:active-view-transition-type(bp-page):not(:active-view-transition-type(bp-morph-back))::view-transition-old(root){
  animation-duration:.16s;mix-blend-mode:normal;}
html:active-view-transition-type(bp-page):not(:active-view-transition-type(bp-morph-back))::view-transition-new(root){
  animation-duration:.16s;mix-blend-mode:normal;}
/* The header and the call pill: the new one is shown at once, so they hold still instead of
   fading. A call pill that is not there yet on the new page stays put until the page catches up. */
html:active-view-transition-type(bp-page)::view-transition-group(bp-nav),
html:active-view-transition-type(bp-page)::view-transition-group(bp-callbar),
html:active-view-transition-type(bp-page)::view-transition-old(bp-nav),
html:active-view-transition-type(bp-page)::view-transition-new(bp-nav),
html:active-view-transition-type(bp-page)::view-transition-old(bp-callbar),
html:active-view-transition-type(bp-page)::view-transition-new(bp-callbar){animation:none;}
html:active-view-transition-type(bp-page)::view-transition-old(bp-nav),
html:active-view-transition-type(bp-page)::view-transition-old(bp-callbar){opacity:0;}
html:active-view-transition-type(bp-page)::view-transition-old(bp-nav):only-child,
html:active-view-transition-type(bp-page)::view-transition-old(bp-callbar):only-child{opacity:1;}
/* M44, coming back from a case study to the examples page kept in the browser's memory: no sweep.
   assets/motion/site.js marks the change bp-morph-back and shrinks the case study (the old page,
   drawn on top here) into the card it came from. Going the other way, the card grows on the
   examples page itself before the page changes, and that page change skips the transition. */
html:active-view-transition-type(bp-morph-back)::view-transition-old(root),
html:active-view-transition-type(bp-morph-back)::view-transition-new(root){animation:none;mix-blend-mode:normal;}
html:active-view-transition-type(bp-morph-back)::view-transition-old(root){z-index:1;}

/* B46, the light and dark switch: the new colours spread out in a circle from the pressed button.
   assets/motion/site.js adds bp-vt-theme to <html> only while that transition runs, and drives the
   circle itself; the page underneath is not faded. */
html.bp-vt-theme::view-transition-old(root),
html.bp-vt-theme::view-transition-new(root){animation:none;mix-blend-mode:normal;}
html.bp-vt-theme::view-transition-new(root){z-index:2;}
/* Nothing eases its colour on its own while the circle spreads, so the new colours are captured
   finished rather than part way through a fade. */
html.bp-vt-theme *,html.bp-vt-theme *::before,html.bp-vt-theme *::after{transition:none!important;}
