/* /assets/css/motion.css — the shared motion layer (b105 step 17; skill presive-motion).
 *
 * Pages keep their own reveal engine (.rv → .in, after the ready gate). This file adds, for blocks b105
 * introduced and for nothing that already moves:
 *   [data-motion="hero"]     children rise in sequence once html[data-ready] is set (pure CSS; the ready
 *                            gate has a hard 2.5 s timeout, so this can never leave the hero hidden)
 *   [data-motion="reveal"]   fade + rise once, when it enters the viewport (motion.js adds .is-in)
 *   [data-motion="stagger"]  the same on its direct children, 80 ms apart
 *   hover / focus-visible micro-interactions on linked cards, the landing's work machine, footer links
 *   smooth in-page anchors
 * Transform and opacity only. Durations and curves are the page tokens (fallbacks for pages without them).
 * Everything that hides or moves is inside prefers-reduced-motion: no-preference. Scroll reveals are keyed on
 * html.pv-motion, which only motion.js sets: if that script never runs, nothing is hidden.
 */
/* b111 S00: motion is gated by html.pv-anim, not by the media query alone — the class is set before first
   paint when the OS says no-preference OR the reviewer asked for it with ?motion=1. Same rules, same order;
   only the gate changed, so a reader who never touches the flag sees exactly what they saw before. */
html.pv-anim{scroll-behavior:smooth}
/* ---- hero entrance ---- */
html.pv-anim.js:not([data-ready]) [data-motion="hero"] > *{opacity:0}
html.pv-anim[data-ready] [data-motion="hero"] > *{
    animation:pv-rise var(--t-reveal) var(--ease-out) both;
  }
html.pv-anim[data-ready] [data-motion="hero"] > :nth-child(2){animation-delay:80ms}
html.pv-anim[data-ready] [data-motion="hero"] > :nth-child(3){animation-delay:160ms}
html.pv-anim[data-ready] [data-motion="hero"] > :nth-child(4){animation-delay:240ms}
html.pv-anim[data-ready] [data-motion="hero"] > :nth-child(n+5){animation-delay:320ms}
/* ---- scroll reveals ---- */
html.pv-anim .pv-motion [data-motion="reveal"],
  html.pv-anim .pv-motion [data-motion="stagger"] > *{
    transition:opacity var(--t-reveal) var(--ease-out),
               transform var(--t-reveal) var(--ease-out);
  }
html.pv-anim .pv-motion [data-motion="reveal"]:not(.is-in),
  html.pv-anim .pv-motion [data-motion="stagger"]:not(.is-in) > *{opacity:0;transform:translateY(var(--rise,28px))}
html.pv-anim .pv-motion [data-motion="stagger"] > :nth-child(2){transition-delay:var(--stagger,80ms)}
html.pv-anim .pv-motion [data-motion="stagger"] > :nth-child(3){transition-delay:calc(var(--stagger,80ms) * 2)}
html.pv-anim .pv-motion [data-motion="stagger"] > :nth-child(4){transition-delay:calc(var(--stagger,80ms) * 3)}
html.pv-anim .pv-motion [data-motion="stagger"] > :nth-child(n+5){transition-delay:calc(var(--stagger,80ms) * 4)}
/* ---- micro-interactions (hover only where hover exists; focus-visible everywhere) ---- */
html.pv-anim .svc-tile-a{
    transition:transform var(--t-fast) var(--ease),border-color var(--t-fast) var(--ease);
  }
/* b106: pickers people press again and again answer at once — colour only, --t-press (150 ms); the lift is
     for hover-capable pointers (web-design-guidelines / better-interface, b105 audit) */
html.pv-anim .svc-card-in,
  html.pv-anim .wd-opt{
    transition-property:color,background-color,border-color;
    transition-duration:var(--t-press);transition-timing-function:var(--ease);
  }
/* the machine keeps its own opacity entrance (index.html); its lift is a --t-mid transform */
html.pv-anim .wk-stage.in .wk-mb-inner{transition:opacity var(--d-4,.8s) var(--ease-out,ease),transform var(--t-mid) var(--ease-out,ease)}
html.pv-anim .svc-tile-a:focus-visible{transform:translateY(-4px)}
@media (hover:hover) and (pointer:fine){
  html.pv-anim .svc-card-in,
  html.pv-anim .wd-opt{transition-property:color,background-color,border-color,transform}
  html.pv-anim .svc-tile-a:hover{transform:translateY(-4px)}
  html.pv-anim .svc-card:hover .svc-card-in{transform:translateY(-2px)}
  html.pv-anim .wd-opt:hover{transform:translateY(-2px)}
  /* the landing's work machine answers the list: it is lifted while a project is pointed at */
  html.pv-anim .wk-show:has(.wk-item:hover) .wk-stage.in .wk-mb-inner{transform:translateY(-6px) scale(1.01)}
}
html.pv-anim .wk-show:has(.wk-item:focus-visible) .wk-stage.in .wk-mb-inner{transform:translateY(-6px) scale(1.01)}
/* footer links: the underline draws in — a scaled pseudo-element, so it stays on the compositor (b106; b105
     animated background-size, which repaints every frame) */
html.pv-anim footer.site-foot .foot-col a{position:relative;transition:color var(--t-fast) var(--ease)}
html.pv-anim footer.site-foot .foot-col a::after{
    content:"";position:absolute;left:0;right:0;bottom:0;height:1px;background:currentColor;
    transform:scaleX(0);transform-origin:left center;transition:transform var(--t-fast) var(--ease);pointer-events:none;
  }
html.pv-anim footer.site-foot .foot-col a:hover,
  html.pv-anim footer.site-foot .foot-col a:focus-visible{transform:none}
html.pv-anim footer.site-foot .foot-col a:hover::after,
  html.pv-anim footer.site-foot .foot-col a:focus-visible::after{transform:scaleX(1)}
@media (pointer:coarse){
  html.pv-anim footer.site-foot .foot-col a::after{bottom:calc(50% - .8em)}
}


@keyframes pv-rise{
  from{opacity:0;transform:translateY(var(--rise,28px))}
  to{opacity:1;transform:none}
}

@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto}
  [data-motion],[data-motion] > *{animation:none !important;transition:none !important}
}

/* b112 — a step of "How it works" that the progress line has reached. The line already says where the
   reader is; this makes the step say it too, so the fill is doing something instead of just travelling.
   Colour and opacity only: no layout, nothing to recalculate while scrolling. */
html.pv-anim .hw-item .hw-n{transition:color var(--t-fast) var(--ease-out)}
html.pv-anim .hw-item.is-passed .hw-n{color:var(--ink)}
html.pv-anim .stage-dark .hw-item.is-passed .hw-n{color:var(--white)}

/* ============================================================================================
   b115 S03 — .pv-light: the footer's lamp, made reusable.

   The footer light the owner calls perfect is `.wm-lamp{opacity:0;transition:opacity .5s var(--ease)}`
   toggled by a class (index.html:812-813). That is the whole recipe: ONE compositor property, one
   state change, no work per frame, no pointer tracking, no layout read.

   The Selected Work hover was the opposite — an additive light inside the WebGL fragment shader whose
   centre came from getBoundingClientRect() on every pointermove, each read followed by a full-canvas
   render. Same intent, a thousand times the cost, and that is the stutter.

   So the cases get the footer's recipe, scaled to the frame. The strength matches what the shader was
   adding (vec3(0.17) additive white), so the look carries over rather than being re-invented.
   ============================================================================================ */
.pv-light,
#work .work-frame {
  position: relative;
}
.pv-light::after,
#work .work-frame::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.5s var(--ease);
  background: radial-gradient(
    62% 70% at 50% 42%,
    color-mix(in srgb, var(--white) 17%, transparent),
    transparent 72%
  );
  mix-blend-mode: screen;
}
/* hover and keyboard focus reach it the same way: the row is one link */
.pv-light:hover::after,
.pv-light:focus-visible::after,
#work .work-link:hover .work-frame::after,
#work .work-link:focus-visible .work-frame::after {
  opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
  .pv-light::after,
  #work .work-frame::after {
    transition: none;
  }
}
