/* YewHo interface motion - build 20260730 */

/* premium-motion : generative ambient  (premium-motion 2.0, Phase 4)
   Dependency-light backgrounds + scroll-velocity reactivity. Pair with ambient.js.
     .md-mesh      — animated multi-radial mesh gradient (pure CSS)
     .md-dots      — interactive dot grid that ripples toward the cursor (canvas)
     .md-velocity  — element skews slightly with scroll speed, settles at rest
   All ambient motion is full-tier only; lite/off show a static gradient/grid.
   ============================================================ */

/* --- animated mesh gradient ------------------------------------------- */
.md-mesh{
  position:relative; overflow:hidden; isolation:isolate;
  background-color:#161f2c;
  background-image:
    radial-gradient(38% 45% at 18% 22%, rgba(200,16,46,.42), transparent 60%),
    radial-gradient(40% 42% at 82% 26%, rgba(58,111,208,.34), transparent 62%),
    radial-gradient(46% 50% at 30% 82%, rgba(31,169,113,.28), transparent 60%),
    radial-gradient(42% 46% at 78% 80%, rgba(214,158,20,.26), transparent 62%);
  background-size:180% 180%;
}
@media (prefers-reduced-motion: no-preference){
  :root[data-motion="full"] .md-mesh{ animation: md-mesh 22s ease-in-out infinite alternate; }
  @keyframes md-mesh{
    0%  { background-position: 0% 0%,   100% 0%,  0% 100%, 100% 100%; }
    50% { background-position: 40% 30%, 60% 20%, 30% 70%, 70% 60%; }
    100%{ background-position: 100% 100%,0% 100%,100% 0%,  0% 0%;   }
  }
}

/* --- interactive dot grid --------------------------------------------- */
.md-dots{ position:relative; }
.md-dots canvas{ position:absolute; inset:0; width:100%; height:100%; display:block; }

/* --- scroll-velocity skew --------------------------------------------- */
.md-velocity{ transform: skewY(var(--vskew,0deg)); transition: transform .35s cubic-bezier(.22,.61,.2,1); will-change: transform; }
:root[data-motion="lite"] .md-velocity,:root[data-motion="off"] .md-velocity{ transform:none !important; }

/* premium-motion : anchor-positioned tethers  (yewho-web-effects 2.4, Phase 10)
   CSS Anchor Positioning lets one element be positioned RELATIVE TO ANOTHER without
   the two being DOM siblings or hand-computed with JS — and `position-try` flips it
   automatically when it would overflow the viewport. Perfect for tooltips, rich
   dropdowns, "new" callouts and coach-marks that must stay glued to a trigger.

     .md-anchor   — the trigger; names an anchor (default --md-anc, override --anc)
     .md-tether   — the floating panel; pins under the anchor, centred, auto-flips up
                    if there's no room below. Combine with [popover] for top-layer +
                    light-dismiss for free. Tune: --anc (shared name), --gap (def 10px)

   Progressive: whole block is @supports-guarded. Where anchor positioning is absent
   the panel just needs your own fallback position (e.g. a wrapping `position:relative`
   parent) — nothing here breaks, it simply doesn't tether.
   ============================================================ */

@supports (anchor-name: --a){

  .md-anchor{ anchor-name: var(--anc, --md-anc); }

  .md-tether{
    position: absolute;
    position-anchor: var(--anc, --md-anc);
    /* default placement: below the trigger, horizontally centred on it */
    top: calc(anchor(bottom) + var(--gap,10px));
    justify-self: anchor-center;
    /* flip above / to the side when it would leave the viewport */
    position-try-order: most-height;
    position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
    max-width: min(320px, 92vw);
    /* entrance */
    opacity: 1; translate: 0 0;
    transition: opacity .22s var(--md-ease,cubic-bezier(.22,.61,.2,1)),
                translate .22s var(--md-ease,cubic-bezier(.22,.61,.2,1)),
                display .22s allow-discrete, overlay .22s allow-discrete;
  }

  /* when the tether is a popover, animate open/close via @starting-style */
  @supports (transition-behavior: allow-discrete){
    .md-tether:popover-open{ opacity:1; translate:0 0; }
    .md-tether[popover]:not(:popover-open){ opacity:0; translate:0 -6px; }
    @starting-style{ .md-tether:popover-open{ opacity:0; translate:0 -6px; } }
  }

  /* optional little arrow that also tracks the anchor's horizontal centre */
  .md-tether.arrow::before{
    content:""; position:absolute; top:-6px; left:50%; translate:-50% 0;
    width:12px; height:12px; rotate:45deg; background:inherit;
    border-left:inherit; border-top:inherit;
  }

  @media (prefers-reduced-motion: reduce){
    .md-tether{ transition-duration: 1ms; translate: 0 0 !important; }
  }
}

/* premium-motion : scroll-snap carousel  (yewho-web-effects 2.3, Phase 9)
   Pairs with carousel.js (builds dots/arrows, keeps them in sync, optional
   autoplay). Scrolling itself is 100% native scroll-snap — momentum, touch and
   trackpad feel come from the browser, not JS.

   Markup:
     <div class="md-carousel" data-autoplay="4500">   <!-- autoplay optional -->
       <div class="md-car-track">
         <div class="md-car-item">…</div> …
       </div>
     </div>
   Item width: default 100% (one per view). Set --car-w for peeking cards,
   e.g. style="--car-w:72%".
   ============================================================ */
.md-carousel{ position:relative; }
.md-car-track{ display:flex; gap:var(--car-gap,18px);
  overflow-x:auto; overflow-y:hidden; overscroll-behavior-x:contain;
  scroll-snap-type:x mandatory; -webkit-overflow-scrolling:touch;
  scrollbar-width:none; -ms-overflow-style:none;
  scroll-behavior:smooth; padding-bottom:2px; }
.md-car-track::-webkit-scrollbar{ display:none; }
@media (prefers-reduced-motion: reduce){ .md-car-track{ scroll-behavior:auto; } }
.md-car-item{ flex:0 0 var(--car-w,100%); min-width:0; scroll-snap-align:start; }

/* arrows */
.md-car-nav button{ position:absolute; top:50%; translate:0 -50%; z-index:2;
  width:42px; height:42px; border-radius:50%; border:1px solid var(--line,#E7EAEE);
  background:#fff; color:var(--ink,#111823); cursor:pointer;
  display:grid; place-items:center; font-size:16px; line-height:1;
  box-shadow:0 8px 22px rgba(17,24,35,.10);
  transition:transform .2s var(--md-ease,cubic-bezier(.22,.61,.2,1)),
             background .2s, color .2s, opacity .2s; }
.md-car-nav .car-prev{ left:-8px; } .md-car-nav .car-next{ right:-8px; }
.md-car-nav button:hover{ background:var(--red,#C8102E); color:#fff; transform:translateY(-1px); }
.md-car-nav button:disabled{ opacity:.35; pointer-events:none; }
.md-car-nav button:focus-visible{ outline:2px solid var(--red,#C8102E); outline-offset:2px; }

/* dots */
.md-car-dots{ display:flex; justify-content:center; gap:8px; margin-top:16px; }
.md-car-dots button{ width:8px; height:8px; border-radius:999px; border:0; padding:0;
  background:var(--line,#D8DDE3); cursor:pointer;
  transition:width .3s var(--md-ease,cubic-bezier(.22,.61,.2,1)), background .3s; }
.md-car-dots button[aria-current="true"]{ width:24px; background:var(--red,#C8102E); }
.md-car-dots button:focus-visible{ outline:2px solid var(--red,#C8102E); outline-offset:3px; }
@media (prefers-reduced-motion: reduce){ .md-car-dots button{ transition:none; } }

/* premium-motion : image cinema  (premium-motion 2.0, Phase 3)
   Pair with cinema.js (which adds .in on view for wipe/blur-up).
     .md-wipe      — clip-path reveal on view
     .md-blurup    — blurred → sharp on view (or on <img> load)
     .md-kenburns  — slow, subconscious zoom/drift (ambient; full tier)
     .md-duo       — duotone/greyscale → full colour on hover
   Works on <img>, background boxes, or any block.
   ============================================================ */
.md-wipe{ clip-path: inset(0 100% 0 0); }
.md-wipe.in{ transition: clip-path .9s cubic-bezier(.22,.61,.2,1); clip-path: inset(0 0 0 0); }

.md-blurup{ filter: blur(16px); transform: scale(1.03); }
.md-blurup.in{ transition: filter .8s ease, transform .8s ease; filter: blur(0); transform: none; }

.md-duo{ filter: grayscale(1) contrast(1.05); transition: filter .5s ease; }
@media (hover:hover){ .md-duo:hover{ filter: grayscale(0) contrast(1); } }

.md-kenburns{ overflow:hidden; }
.md-kenburns > *{ transform-origin:center; }
@media (prefers-reduced-motion: no-preference){
  :root[data-motion="full"] .md-kenburns > img,
  :root[data-motion="full"] .md-kenburns > .kb{ animation: md-kb 20s ease-in-out infinite alternate; }
  @keyframes md-kb{ from{ transform:scale(1) translate(0,0) } to{ transform:scale(1.12) translate(-2%,-2%) } }
}

[data-motion="off"] .md-wipe{ clip-path:none !important; }
[data-motion="off"] .md-blurup{ filter:none !important; transform:none !important; }

/* --- scroll-scrubbed image sequence (opt-in; needs frame images) --------
   <div class="md-sequence" data-frames="120" data-src="/frames/f-{n}.jpg"></div>
   cinema.js paints the frame matching scroll progress onto a <canvas>. -------- */
.md-sequence{ position:relative; }
.md-sequence canvas{ width:100%; height:auto; display:block; }

/* premium-motion : before / after compare slider  (yewho-web-effects 2.3, Phase 9)
   Pairs with compare.js. Drag / touch / keyboard — the control IS a native
   <input type="range">, so accessibility comes free.

   Markup:
     <div class="md-compare" style="--pos:50%; aspect-ratio:16/10">
       <img class="cmp-a" src="before.jpg" alt="Before">
       <div class="cmp-b"><img src="after.jpg" alt="After"></div>
       <span class="cmp-tag a">Before</span><span class="cmp-tag b">After</span>
       <input class="cmp-range" type="range" min="0" max="100" value="50"
              aria-label="Reveal comparison">
       <div class="cmp-handle" aria-hidden="true"><span></span></div>
     </div>
   ============================================================ */
.md-compare{ position:relative; overflow:hidden; border-radius:16px;
  border:1px solid var(--line,#E7EAEE); user-select:none; }
.md-compare img, .md-compare svg{ display:block; width:100%; height:100%; object-fit:cover; }
.md-compare > .cmp-a{ position:absolute; inset:0; }
.md-compare .cmp-b{ position:absolute; inset:0;
  clip-path: inset(0 0 0 var(--pos,50%)); }
.md-compare .cmp-b > *{ position:absolute; inset:0; }

/* the invisible native range covers the whole scene = free drag + keys */
.md-compare .cmp-range{ position:absolute; inset:0; width:100%; height:100%;
  margin:0; opacity:0; cursor:ew-resize; touch-action:none; z-index:3;
  -webkit-appearance:none; appearance:none; }

/* divider + handle */
.md-compare .cmp-handle{ position:absolute; top:0; bottom:0; left:var(--pos,50%);
  width:2px; margin-left:-1px; background:#fff;
  box-shadow:0 0 0 1px rgba(17,24,35,.12), 0 0 18px rgba(17,24,35,.25);
  pointer-events:none; z-index:2; }
.md-compare .cmp-handle span{ position:absolute; top:50%; left:50%;
  translate:-50% -50%; width:40px; height:40px; border-radius:50%;
  background:#fff; box-shadow:0 6px 18px rgba(17,24,35,.28);
  display:grid; place-items:center; }
.md-compare .cmp-handle span::before{ content:"\2194"; font-size:16px;
  color:var(--red,#C8102E); font-weight:700; }
.md-compare:has(.cmp-range:focus-visible) .cmp-handle span{
  outline:2px solid var(--red,#C8102E); outline-offset:2px; }

/* corner tags */
.md-compare .cmp-tag{ position:absolute; top:12px; z-index:2; pointer-events:none;
  font:600 11px/1 var(--sans,Inter,system-ui); letter-spacing:.08em;
  text-transform:uppercase; color:#fff; background:rgba(17,24,35,.55);
  padding:6px 10px; border-radius:999px; backdrop-filter:blur(4px); }
.md-compare .cmp-tag.a{ left:12px; }
.md-compare .cmp-tag.b{ right:12px; }

/* smooth glide when set by keyboard (pointer drags stay 1:1) */
.md-compare.kbd .cmp-b{ transition: clip-path .25s var(--md-ease,cubic-bezier(.22,.61,.2,1)); }
.md-compare.kbd .cmp-handle{ transition: left .25s var(--md-ease,cubic-bezier(.22,.61,.2,1)); }
@media (prefers-reduced-motion: reduce){
  .md-compare.kbd .cmp-b, .md-compare.kbd .cmp-handle{ transition:none; }
}

/* premium-motion : composable motion pack  (yewho-web-effects 2.4, Phase 10)
   Fixes the library's oldest footgun — "never stack two transform effects, they
   cancel." They cancel because they all fight over the `transform` shorthand.
   This pack uses the INDIVIDUAL transform properties (translate / rotate / scale),
   which are independent longhands, plus `animation-composition: add` for the cases
   where two effects genuinely share one axis. Result: an idle loop, a hover lift
   and a pointer tilt can all run on the SAME element without stomping each other.

     .md-afloat   — perpetual idle bob            (uses `translate` only)  [--mc-amp,--mc-fd]
     .md-adrift   — slow horizontal drift, ADDS onto float on the same axis (composition:add)
     .md-alift    — hover raise + shadow          (uses `scale`+`translate` via longhand)
     .md-apress   — press-down feedback           (uses `scale`)
     .md-atilt    — pointer/hover tilt hint       (uses `rotate`)   [--mc-tilt]
     .md-aglow    — hover brand glow              (filter/shadow only — always safe to stack)

   Stack freely, e.g.  <a class="btn md-afloat md-alift md-apress md-aglow"> — the float
   keeps bobbing while the hover lifts and the press dips, none of them cancelling.
   ============================================================ */

:root{ --mc-ease: cubic-bezier(.22,.61,.2,1); }

@supports (translate: 0px){

  /* ---- idle float — translate axis, perpetual ------------------------------ */
  .md-afloat{ animation: mc-afloat var(--mc-fd,5.5s) ease-in-out infinite; }
  @keyframes mc-afloat{
    0%,100%{ translate: 0 0; }
    50%    { translate: 0 calc(var(--mc-amp,7px) * -1); }
  }

  /* ---- horizontal drift — same axis, ADDS onto the float (needs composition) */
  @supports (animation-composition: add){
    .md-adrift{ animation: mc-adrift var(--mc-dd,11s) ease-in-out infinite;
                animation-composition: add; }
    @keyframes mc-adrift{
      0%,100%{ translate: calc(var(--mc-damp,5px) * -1) 0; }
      50%    { translate: var(--mc-damp,5px) 0; }
    }
  }

  /* ---- hover lift — scale + a small translate nudge (longhands, no clash) --- */
  @media (hover:hover){
    .md-alift{ transition: scale .28s var(--mc-ease), translate .28s var(--mc-ease),
                            box-shadow .28s var(--mc-ease); }
    .md-alift:hover{ scale: 1.03; translate: 0 -2px;
                     box-shadow: 0 16px 40px rgba(17,24,35,.18); }
  }

  /* ---- press feedback — scale only ---------------------------------------- */
  .md-apress{ transition: scale .12s var(--mc-ease); }
  .md-apress:active{ scale: .97; }

  /* ---- tilt hint — rotate axis (composes with translate/scale) ------------- */
  @media (hover:hover){
    .md-atilt{ transition: rotate .3s var(--mc-ease); }
    .md-atilt:hover{ rotate: var(--mc-tilt, 1.4deg); }
  }
}

/* ---- brand glow — filter/shadow only, always stack-safe -------------------- */
@media (hover:hover){
  .md-aglow{ transition: box-shadow .3s var(--mc-ease), filter .3s var(--mc-ease); }
  .md-aglow:hover{ box-shadow: 0 0 0 1px color-mix(in srgb,#C8102E 32%,transparent),
                               0 10px 34px color-mix(in srgb,#C8102E 30%,transparent); }
}

/* ---- governor + reduced-motion: kill the perpetual loops, keep hovers ------ */
:root[data-motion="lite"] .md-afloat, :root[data-motion="off"] .md-afloat,
:root[data-motion="lite"] .md-adrift, :root[data-motion="off"] .md-adrift{
  animation: none !important; translate: 0 0 !important;
}
@media (prefers-reduced-motion: reduce){
  .md-afloat, .md-adrift{ animation: none !important; translate: 0 0 !important; }
  .md-alift, .md-apress, .md-atilt{ transition-duration: 1ms; }
}

/* premium-motion : sticky card deck  (yewho-web-effects 2.2, Phase 8)
   CSS-only "cards stack as you scroll" scene. Each card sticks below the header;
   the next card slides up and stacks over it. On browsers with scroll timelines
   (and full motion tier), the covered card also settles back slightly — pure garnish,
   the stack works everywhere without it.

     <section class="md-deck" style="--deck-top:96px">
       <article class="md-deck-card">…</article>
       <article class="md-deck-card">…</article>
       <article class="md-deck-card">…</article>
     </section>

   Rules: every card needs its OWN solid background + radius + shadow (they overlap).
   Tune: --deck-top (stick offset, def 88px), --deck-gap (def 20px).
   Give the section normal page flow; height comes from the cards themselves.
   ============================================================ */

.md-deck{ display:grid; gap:var(--deck-gap,20px); }
.md-deck > .md-deck-card{
  position:sticky; top:var(--deck-top,88px);
  background:var(--bg,#fff); border:1px solid var(--line,#E7EAEE);
  border-radius:var(--r,12px);
  box-shadow:0 -8px 30px rgba(17,24,35,.08);
}

/* garnish: the card being covered eases back (scroll-timeline browsers, full tier) */
@supports (animation-timeline: view()){
  :root[data-motion="full"] .md-deck > .md-deck-card{
    transform-origin:center top;
    animation: md-deck-settle linear both;
    animation-timeline: view();
    animation-range: exit 0% exit 100%;
  }
  @keyframes md-deck-settle{ to{ transform:scale(.965); filter:brightness(.98); } }
}
@media (prefers-reduced-motion: reduce){
  .md-deck > .md-deck-card{ animation:none; }
}

/* premium-motion : flip cards  (premium-motion 2.0, Phase 5)
   3D card that flips to reveal a back face. Pair with flipcard.js for click mode.
     <div class="md-flip"><div class="md-flip-inner">
       <div class="md-flip-front">…</div><div class="md-flip-back">…</div>
     </div></div>
   Add data-click for click-to-flip instead of hover. Add .y for horizontal axis (default).
   ============================================================ */
.md-flip{ perspective:1200px; }
.md-flip-inner{ position:relative; transform-style:preserve-3d; transition: transform .7s cubic-bezier(.22,.61,.2,1); }
.md-flip:not([data-click]):hover .md-flip-inner,
.md-flip.flipped .md-flip-inner{ transform: rotateY(180deg); }
.md-flip.x:not([data-click]):hover .md-flip-inner,
.md-flip.x.flipped .md-flip-inner{ transform: rotateX(180deg); }
.md-flip-front,.md-flip-back{ backface-visibility:hidden; -webkit-backface-visibility:hidden; }
.md-flip-back{ position:absolute; inset:0; transform: rotateY(180deg); }
.md-flip.x .md-flip-back{ transform: rotateX(180deg); }
@media (prefers-reduced-motion: reduce){ .md-flip-inner{ transition:none; } }
[data-motion="off"] .md-flip:hover .md-flip-inner{ transform:none; }

/* premium-motion : flow diagram scenes  (yewho-web-effects 2.1, Phase 7)
   SVG "node & wire" diagrams that feel alive — flowing energy currents,
   travelling pulses, draw-on wires, and a cascade node-pop. Pairs with flow.js.
   Extracted from the SyncTech hosting pages (gateway, ecosystem, domain→brand).

   Skeleton:
     <div class="scene"><svg class="md-draw" viewBox="0 0 W H" style="max-width:900px">
        <path class="trace" d="M…"/>                            grey wires (draw on view)
        <path class="eflow" d="M…" style="animation-delay:.1s"/> red energy current
        <path class="gflow" d="M…"/>                            green ("clean/healthy") current
        <g class="nd" style="--d:.2s"><rect rx="9"/><text/></g>  a node (cascade pop, --d = delay)
        <circle r="4" fill="#C8102E"><animateMotion dur="2.4s"    a travelling pulse
          repeatCount="indefinite" path="M…"/></circle>
     </svg></div>

   Nodes are just <g class="nd"> — style the rect/text however you like (accent, glass, ghost…).
   Cascade the pop root→leaves by giving each node a bigger --d.
   Everything is governor- and reduced-motion-safe.
   ============================================================ */

.scene svg{ width:100%; height:auto; display:block; margin-inline:auto; }
.scene .nd rect{ transition:none; }

/* ---- flowing currents (a short dash that streams along a whole path) ---- */
.scene .eflow{ fill:none; stroke:#C8102E; stroke-width:3.4; stroke-linecap:round;
  stroke-dasharray:36 460; stroke-dashoffset:36; animation:md-flow 1.6s linear infinite; }
.scene .gflow{ fill:none; stroke:#1fa971; stroke-width:3.4; stroke-linecap:round;
  stroke-dasharray:36 460; stroke-dashoffset:36; animation:md-flow 1.6s linear infinite; }
@keyframes md-flow{ to{ stroke-dashoffset:-500; } }

/* ---- cascade node-pop: each box gently pulses + red glow, root→leaves ---- */
.scene .nd{ transform-box:fill-box; transform-origin:center;
  animation:md-ndpop 2.1s ease-in-out infinite; animation-delay:var(--d,0s); }
@keyframes md-ndpop{
  0%,56%,100%{ transform:scale(1); filter:none; }
  72%{ transform:scale(1.05); filter:drop-shadow(0 0 7px var(--pop, rgba(200,16,46,.5))); }
}
@media (hover:hover){ .scene .nd:hover{ animation-play-state:paused; } }

/* ---- governor / reduced-motion ---- */
:root[data-motion="lite"] .scene .eflow, :root[data-motion="off"] .scene .eflow,
:root[data-motion="lite"] .scene .gflow, :root[data-motion="off"] .scene .gflow{ animation:none; opacity:0; }
:root[data-motion="lite"] .scene .nd,   :root[data-motion="off"] .scene .nd{ animation:none; }
@media (prefers-reduced-motion: reduce){
  .scene .eflow, .scene .gflow{ animation:none; opacity:0; }
  .scene .nd{ animation:none; }
}

/* premium-motion : reactive flow layer  (yewho-web-effects 2.5, Phase 11)
   Upgrades the Phase-7 flow diagrams (flow.css/flow.js) from PASSIVE to REACTIVE with
   ZERO markup changes — just add `rx` to a `.scene` (or `data-reactive`). Pairs with
   flow2.js, which does the geometry. Everything is pointer- + full-tier gated; on
   lite/off/reduced-motion the diagram stays exactly as flow.css draws it.

     • proximity glow  — nodes brighten & swell as the cursor nears (JS sets `--near` 0..1)
     • path spotlight  — hover a node and its wires + the nodes they reach light up,
                         everything else recedes (JS adds `.lit` / scene gets `.focusing`)
     • arrival ripple  — an expanding ring at a wire's destination (JS drops `.md-arrive`)

   Proximity uses the RECT child + individual transform longhands, so it composes with —
   never fights — the existing node-pop animation on the parent `<g>` (Phase 10 lesson).
   ============================================================ */

@media (hover:hover) and (pointer:fine){
  /* ---- proximity glow (on the shape child, so the <g> node-pop is untouched) ---- */
  .scene.rx .nd rect, .scene.rx .nd .shape{
    transform-box:fill-box; transform-origin:center;
    transition: filter .18s ease, transform .18s ease;
    transform: scale(calc(1 + var(--near,0) * .05));
    filter: drop-shadow(0 0 calc(var(--near,0) * 9px) rgba(200,16,46, calc(var(--near,0) * .6)));
  }

  /* ---- path spotlight -------------------------------------------------------- */
  .scene.rx path.trace, .scene.rx .eflow, .scene.rx .gflow{ transition: opacity .22s ease; }
  .scene.rx .nd{ transition: opacity .22s ease; }
  .scene.rx.focusing path.trace:not(.lit){ opacity:.16; }
  .scene.rx.focusing .eflow:not(.lit), .scene.rx.focusing .gflow:not(.lit){ opacity:.08; }
  .scene.rx.focusing path.trace.lit{ opacity:1; }
  .scene.rx.focusing .nd:not(.lit){ opacity:.4; }
}

/* ---- arrival ripple (a <circle class="md-arrive"> injected at a sink node) ----- */
.scene .md-arrive{
  fill:none; stroke:#C8102E; stroke-width:2; pointer-events:none;
  animation: md-arrive var(--arr-dur,1.8s) ease-out infinite; animation-delay:var(--arr-d,0s);
}
@keyframes md-arrive{ 0%{ r:4; opacity:.65; } 70%,100%{ r:24; opacity:0; } }

/* ---- governor / reduced-motion: fall back to the static flow.css look --------- */
:root[data-motion="lite"] .scene.rx .nd rect, :root[data-motion="off"] .scene.rx .nd rect,
:root[data-motion="lite"] .scene.rx .nd .shape, :root[data-motion="off"] .scene.rx .nd .shape{
  transform:none !important; filter:none !important;
}
:root[data-motion="lite"] .scene .md-arrive, :root[data-motion="off"] .scene .md-arrive{ animation:none; opacity:0; }
@media (prefers-reduced-motion: reduce){
  .scene.rx .nd rect{ transform:none !important; filter:none !important; }
  .scene .md-arrive{ animation:none; opacity:0; }
}

/* premium-motion : living frames  (yewho-web-effects 2.2, Phase 8)
   Pair with frames.js (only .md-corners needs it, for the on-view trigger).

     .md-border   — rotating conic-gradient border. The element MUST have its own
                    solid background (the gradient sits behind it, showing only at
                    the edge). Radius is inherited. Tune:
                      --mdb-c1 (bright stop, def brand red)
                      --mdb-c2 (dim stop),  --mdb-dur (spin, def 4.5s), --mdb-w (edge, def 1.6px)
                    Add .glow for a soft blurred halo of the same gradient.
     .md-corners  — corner brackets (top-left + bottom-right) that draw in on view.
                    Tune: --mdc-c (color), --mdc-s (size, def 26px).
     .md-underglow— soft brand glow beneath the element; breathes on full tier.
                    Tune: --mdg-c (color).

   All perpetual loops stop under data-motion lite/off and reduced motion.
   ============================================================ */

/* ---- rotating gradient border ----
   Double-background technique (robust, no z-index games): the surface color paints
   over padding-box, the conic gradient shows only through the transparent border ring.
   IMPORTANT: the element's own background comes from --mdb-bg (default white) —
   set --mdb-bg to match your card color (e.g. --mdb-bg:#16202E on navy cards). */
@property --mdb-a { syntax:'<angle>'; initial-value:0deg; inherits:false; }
.md-border{
  position:relative;
  border:var(--mdb-w,1.6px) solid transparent;
  background:
    linear-gradient(var(--mdb-bg,#fff), var(--mdb-bg,#fff)) padding-box,
    conic-gradient(from var(--mdb-a,0deg),
      var(--mdb-c1,#C8102E) 0%,  var(--mdb-c2,rgba(200,16,46,.12)) 22%,
      var(--mdb-c2,rgba(200,16,46,.12)) 46%, var(--mdb-c1,#C8102E) 50%,
      var(--mdb-c2,rgba(200,16,46,.12)) 72%, var(--mdb-c2,rgba(200,16,46,.12)) 96%,
      var(--mdb-c1,#C8102E) 100%) border-box;
  animation: md-border-spin var(--mdb-dur,4.5s) linear infinite;
}
/* optional soft halo. NOTE: sits at z-index:-1 — if an opaque section background
   swallows it, give that section position:relative;z-index:0 so the halo paints
   just behind the card instead. */
.md-border.glow::after{
  content:""; position:absolute; inset:-2px; z-index:-1;
  border-radius:inherit; pointer-events:none;
  background:conic-gradient(from var(--mdb-a,0deg),
    var(--mdb-c1,#C8102E), transparent 30%, transparent 70%, var(--mdb-c1,#C8102E));
  filter:blur(14px); opacity:.35;
  animation: md-border-spin var(--mdb-dur,4.5s) linear infinite;
}
@keyframes md-border-spin{ to{ --mdb-a:360deg; } }

/* ---- corner brackets, draw in on view (transform-only) ---- */
.md-corners{ position:relative; }
.md-corners::before,.md-corners::after{
  content:""; position:absolute; width:var(--mdc-s,26px); height:var(--mdc-s,26px);
  pointer-events:none; opacity:0; transform:scale(.4);
  transition: transform .55s cubic-bezier(.22,.61,.2,1), opacity .4s cubic-bezier(.22,.61,.2,1);
}
.md-corners::before{ top:-3px; left:-3px; transform-origin:top left;
  border-top:2.5px solid var(--mdc-c,#C8102E); border-left:2.5px solid var(--mdc-c,#C8102E);
  border-top-left-radius:8px; }
.md-corners::after{ bottom:-3px; right:-3px; transform-origin:bottom right;
  border-bottom:2.5px solid var(--mdc-c,#C8102E); border-right:2.5px solid var(--mdc-c,#C8102E);
  border-bottom-right-radius:8px; }
.md-corners.in::before,.md-corners.in::after{ opacity:1; transform:scale(1); }
.md-corners.in:hover::before{ transform:scale(1) translate(-3px,-3px); }
.md-corners.in:hover::after{ transform:scale(1) translate(3px,3px); }
[data-motion="off"] .md-corners::before,[data-motion="off"] .md-corners::after{ opacity:1; transform:none; transition:none; }

/* ---- underglow ---- */
.md-underglow{ box-shadow:0 14px 40px -12px var(--mdg-c,rgba(200,16,46,.35)); }
.md-underglow.breathe{ animation: md-underglow 5s ease-in-out infinite; }
@keyframes md-underglow{
  0%,100%{ box-shadow:0 14px 40px -12px var(--mdg-c,rgba(200,16,46,.28)); }
  50%    { box-shadow:0 18px 52px -10px var(--mdg-c,rgba(200,16,46,.5)); }
}

/* ---- governor / reduced-motion ---- */
:root[data-motion="lite"] .md-border, :root[data-motion="off"] .md-border,
:root[data-motion="lite"] .md-border.glow::after, :root[data-motion="off"] .md-border.glow::after,
:root[data-motion="lite"] .md-underglow.breathe, :root[data-motion="off"] .md-underglow.breathe{ animation:none; }
@media (prefers-reduced-motion: reduce){
  .md-border,.md-border.glow::after,.md-underglow.breathe{ animation:none; }
  .md-corners::before,.md-corners::after{ opacity:1; transform:none; transition:none; }
}

/* premium-motion : gooey liquid pack  (yewho-web-effects 2.9, Phase 15)
   TRUE liquid, not blurred gradients. A blur + alpha-contrast ramp in an SVG
   filter makes solid shapes MERGE and SEPARATE like mercury while keeping a
   CRISP EDGE — so it works in one flat brand colour. That is the whole point:
   the old "three blurred blobs" approach expresses liquid through blur, which
   can never sit inside a flat, no-gradient brand system. This can.

     .md-goo         — gooey container; its children merge as they approach
                       (.soft = wider merge, .tight = crisper, later merge)
     .md-goo-drop    — a droplet primitive (solid circle). Tune: --drop (size)
     .md-blobs > i   — decorative blob field inside a .md-goo
     .md-goofield    — driven by goo.js: droplets travel from real source
                       elements to a real sink element and fuse on arrival

   Colour comes from --goo-c (default brand blue). No gradients anywhere.

   IMPORTANT: an element whose filter references a MISSING filter id is not
   rendered at all, so every .md-goo rule is gated on html.md-goo-ready, which
   goo.js sets after injecting the defs. Without JS you simply get un-merged
   solid shapes — never a blank hole.

   Cost: a filter forces its own rasterisation layer, so keep gooey containers
   SMALL. Never put .md-goo on a full-page or full-section wrapper.
   Lite/off/reduced-motion: shapes hold still; the filter is dropped on 'off'.
   ============================================================ */

:root{ --goo-c:#1F63FF; }

/* ---- the containers (only once the defs exist) -------------------------- */
html.md-goo-ready .md-goo      { filter:url(#md-goo-f);       }
html.md-goo-ready .md-goo.soft { filter:url(#md-goo-f-soft);  }
html.md-goo-ready .md-goo.tight{ filter:url(#md-goo-f-tight); }

/* the filter host must not clip its own blur bleed */
.md-goo{ position:relative; }

/* ---- droplet primitive -------------------------------------------------- */
.md-goo-drop{
  position:absolute; top:0; left:0;
  width:var(--drop,18px); height:var(--drop,18px);
  margin:calc(var(--drop,18px) / -2) 0 0 calc(var(--drop,18px) / -2);
  border-radius:50%; background:var(--goo-c);
  will-change:transform;
}

/* ---- decorative blob field (CSS-only) ----------------------------------- */
.md-blobs{ position:relative; }
.md-blobs > i{
  position:absolute; display:block; border-radius:50%;
  background:var(--goo-c); will-change:translate;
}
@media (prefers-reduced-motion: no-preference){
  html[data-motion="full"] .md-blobs > i{
    animation: md-blob-wander var(--blob-dur,19s) ease-in-out var(--d,0s) infinite;
  }
}
/* translate longhand, so a hover/scale on the same node never cancels it */
@keyframes md-blob-wander{
  0%,100%{ translate:0 0 }
  33%    { translate:var(--bx,14%) var(--by,-10%) }
  66%    { translate:calc(var(--bx,14%) * -.6) var(--by2,9%) }
}

/* ---- the merge scene (goo.js fills this in) ----------------------------- */
.md-goofield{
  position:absolute; inset:0; pointer-events:none; z-index:0; overflow:visible;
}
/* the pool that forms where the droplets land */
.md-goofield .md-goo-pool{
  position:absolute; top:0; left:0; border-radius:50%;
  width:var(--pool,44px); height:var(--pool,44px);
  margin:calc(var(--pool,44px) / -2) 0 0 calc(var(--pool,44px) / -2);
  background:var(--goo-c); will-change:transform;
}

/* ---- governor / reduced-motion ------------------------------------------ */
:root[data-motion="lite"] .md-blobs > i,
:root[data-motion="off"]  .md-blobs > i{ animation:none; }
/* on 'off' drop the filter entirely — no point paying for a raster layer */
:root[data-motion="off"] .md-goo{ filter:none !important; }
@media (prefers-reduced-motion: reduce){
  .md-blobs > i{ animation:none; }
}

/* premium-motion : gooey nav indicator  (yewho-web-effects 2.9, Phase 15)
   The sliding pill from tabs.css, made liquid. Two blobs — a lead and a lag —
   sit inside a .md-goo container: on a change the lead leaves first and the lag
   follows, so the ink STRETCHES between the old and new item and snaps back
   into one pill on arrival. Flat colour, crisp edge.

     <nav class="md-goonav">
       <a href="#" class="on">Hosting</a><a href="#">Email</a><a href="#">Domains</a>
     </nav>

   Tune: --nav-ink (pill colour, def brand blue)
         --nav-dur (travel, def .52s)  --nav-r (pill radius, def 999px)
         --nav-pad (ink inset around the item, def 0px)

   goonav.js measures the active item and drives the two blobs. Without JS the
   nav is just links; without goo.js the blobs still move, they simply do not
   fuse. Lite/off/reduced-motion: the pill jumps straight to the item.
   ============================================================ */

.md-goonav{
  position:relative; display:inline-flex; align-items:center; gap:2px;
  padding:var(--nav-p,5px); border-radius:var(--nav-r,999px);
}
.md-goonav > a,
.md-goonav > button{
  position:relative; z-index:2;
  display:inline-flex; align-items:center; justify-content:center;
  padding:var(--nav-ip,10px 18px);
  font-family:var(--disp,inherit); font-weight:600; font-size:var(--nav-fs,13.5px);
  color:var(--nav-fg,#16202E); background:none; border:0; border-radius:inherit;
  cursor:pointer; white-space:nowrap; text-decoration:none;
  transition:color .22s cubic-bezier(.22,.61,.2,1);
}
.md-goonav > a.on,
.md-goonav > button.on{ color:var(--nav-on,#fff); }

/* the liquid ink layer (injected by goonav.js) */
.md-goonav .md-goonav-ink{
  position:absolute; inset:0; z-index:1; pointer-events:none;
}
.md-goonav .md-goonav-ink > i{
  position:absolute; top:0; left:0; display:block;
  height:var(--ink-h,0px); width:var(--ink-w,0px);
  border-radius:var(--nav-r,999px);
  background:var(--nav-ink,#1F63FF);
  will-change:transform;
  transition:transform var(--nav-dur,.52s) cubic-bezier(.34,1.2,.4,1),
             width var(--nav-dur,.52s) cubic-bezier(.34,1.2,.4,1),
             height var(--nav-dur,.52s) cubic-bezier(.34,1.2,.4,1);
}
/* the lag blob trails, which is what creates the stretch */
.md-goonav .md-goonav-ink > i.lag{
  transition-delay:.055s;
  transition-duration:calc(var(--nav-dur,.52s) * 1.25);
}

/* ---- governor / reduced-motion: no travel, just move ---------------------- */
:root[data-motion="off"] .md-goonav .md-goonav-ink > i,
:root[data-motion="lite"] .md-goonav .md-goonav-ink > i.lag{
  transition-duration:.001s; transition-delay:0s;
}
@media (prefers-reduced-motion: reduce){
  .md-goonav .md-goonav-ink > i{ transition-duration:.001s; transition-delay:0s; }
}

/* premium-motion : diagram & storyboard styling  (yewho-web-effects 2.7, Phase 13)
   Node variants, shapes, edge labels, arrowheads and the story caption for diagrams
   generated by graph.js. Variant fills are class-driven (so a whole diagram re-themes
   from CSS); freely-coloured nodes get their fill inline from graph.js.

     .nd .shape        — the node's background (rect | pill | circle | diamond | hex)
     .nd-base/glass/accent/clean/vault/ghost — preset looks (dark-section tuned)
     .md-edge-label    — caption sitting on a wire (haloed so it reads over anything)
     .md-graph-caption — the story narration strip below the diagram
   ============================================================ */

.md-graph{ display:block; }
.md-graph .scene{ width:100%; }
.md-graph .nd text{ fill:#fff; }
.md-graph .nd .shape{ transition: filter .18s ease; }

/* preset variants (shape-agnostic: target .shape, not just rect) */
.nd-base  .shape{ fill:rgba(255,255,255,.06); stroke:rgba(255,255,255,.20); stroke-width:1.4; }
.nd-glass .shape{ fill:rgba(255,255,255,.10); stroke:rgba(255,255,255,.30); stroke-width:1.4; }
.nd-accent .shape{ fill:#C8102E; stroke:#C8102E; stroke-width:1.4; }
.nd-accent text{ fill:#fff; }
.nd-clean .shape{ fill:rgba(57,217,138,.14); stroke:rgba(57,217,138,.5); stroke-width:1.4; }
.nd-clean text{ fill:#7ef0b6; }
.nd-vault .shape{ fill:#16202E; stroke:rgba(255,133,149,.5); stroke-width:1.4; }
.nd-vault text{ fill:#fff; }
.nd-ghost .shape{ fill:rgba(255,255,255,.03); stroke:rgba(255,255,255,.14); stroke-width:1.4; }
.nd-ghost text{ fill:#aeb8c6; }

/* edge labels — a stroke halo keeps them legible over wires/nodes */
.md-graph .md-edge-label{
  fill:#8b95a5; font-family:'Plus Jakarta Sans',-apple-system,sans-serif; font-weight:600;
  paint-order:stroke; stroke:var(--graph-bg,#131c27); stroke-width:4px; stroke-linejoin:round;
}

/* story narration strip */
.md-graph-caption{ margin-top:18px; padding:14px 18px; border-radius:12px;
  background:var(--graph-cap-bg, rgba(255,255,255,.05)); border:1px solid var(--graph-cap-line, rgba(255,255,255,.1));
  font-family:'Plus Jakarta Sans',-apple-system,sans-serif; min-height:26px;
  transition:opacity .3s ease; }
.md-graph-caption b{ display:block; font-size:.95rem; color:var(--graph-cap-title,#fff); margin-bottom:2px; }
.md-graph-caption span{ font-size:.86rem; color:var(--graph-cap-text,#aeb8c6); line-height:1.5; }

/* light-section override */
.md-graph[data-theme="light"] .nd-base .shape,
.md-graph[data-theme="light"] .nd-glass .shape{ fill:#fff; stroke:#e7eaee; }
.md-graph[data-theme="light"] .nd text{ fill:#111823; }
.md-graph[data-theme="light"] .nd-accent text{ fill:#fff; }
.md-graph[data-theme="light"] .md-edge-label{ fill:#586170; stroke:#fff; }
.md-graph[data-theme="light"] .md-graph-caption{ --graph-cap-bg:#f6f7f9; --graph-cap-line:#e7eaee; --graph-cap-title:#111823; --graph-cap-text:#586170; }

/* premium-motion : grid beams backdrop  (yewho-web-effects 2.3, Phase 9)  CSS-ONLY
   A faint blueprint grid with light pulses that occasionally travel along its
   lines (the "infrastructure at work" backdrop). Works on light OR dark
   sections — set the vars.

   Markup (host must be position:relative + overflow:hidden — the class does it):
     <section class="md-gridbeams">
       <i class="gb h" style="--y:25%; --d:0s"></i>
       <i class="gb h" style="--y:75%; --d:3.4s"></i>
       <i class="gb v" style="--x:30%; --d:1.6s"></i>
       <i class="gb v" style="--x:70%; --d:5.1s"></i>
       …content (z-index:1 it yourself if needed)…
     </section>
   Tune: --gb-gap (cell size), --gb-line (grid colour), --gb-c (beam colour),
   --gb-dur (travel time, def 7s). Align --y/--x to multiples of --gb-gap for
   beams that ride exactly on a line. Full tier only; grid stays, beams stop.
   ============================================================ */
.md-gridbeams{ position:relative; overflow:hidden;
  background-image:
    linear-gradient(var(--gb-line,rgba(152,161,174,.16)) 1px, transparent 1px),
    linear-gradient(90deg, var(--gb-line,rgba(152,161,174,.16)) 1px, transparent 1px);
  background-size: var(--gb-gap,56px) var(--gb-gap,56px); }
.md-gridbeams > .gb{ position:absolute; pointer-events:none; display:block; }

.md-gridbeams > .gb.h{ top:var(--y,30%); left:-260px; width:240px; height:1px;
  background:linear-gradient(90deg, transparent, var(--gb-c,rgba(200,16,46,.75)) 55%, transparent); }
.md-gridbeams > .gb.v{ left:var(--x,30%); top:-260px; height:240px; width:1px;
  background:linear-gradient(180deg, transparent, var(--gb-c,rgba(200,16,46,.75)) 55%, transparent); }

@media (prefers-reduced-motion: no-preference){
  :root[data-motion="full"] .md-gridbeams > .gb.h{
    animation: md-gbx var(--gb-dur,7s) linear var(--d,0s) infinite; }
  :root[data-motion="full"] .md-gridbeams > .gb.v{
    animation: md-gby var(--gb-dur,7s) linear var(--d,0s) infinite; }
}
/* travel past any sane section width/height, then rest until the next lap */
@keyframes md-gbx{ 0%{ transform:translateX(0); } 62%,100%{ transform:translateX(calc(100vw + 520px)); } }
@keyframes md-gby{ 0%{ transform:translateY(0); } 62%,100%{ transform:translateY(120vh); } }

/* premium-motion : hover FX pack  (premium-motion 2.0, Phase 5)
   "Mouse-over" micro-delights. Most are pure CSS; .md-dir needs hover.js.
   All gated to @media(hover:hover) so touch devices are unaffected.
     .md-dir (+ .md-dir-fill child) — fill enters from the edge the cursor crosses
     .md-hzoom (+ .md-hcap child)   — image zooms, caption slides up on hover
     .md-link                       — animated underline sweep on a link
     .md-shine                      — glossy light sweep across a button/card
     .md-glow                       — soft brand glow on hover
     .md-swap (.a + .b children)    — old text slides up, new slides in
   ============================================================ */
@media (hover:hover){

  /* direction-aware fill */
  .md-dir{ position:relative; overflow:hidden; isolation:isolate; }
  .md-dir > *{ position:relative; z-index:1; }
  .md-dir-fill{ position:absolute; inset:0; z-index:0; background:var(--red,#C8102E);
    transform: translate(var(--dx,0), var(--dy,101%)); transition: transform .32s cubic-bezier(.22,.61,.2,1); }

  /* image hover-zoom + caption */
  .md-hzoom{ position:relative; overflow:hidden; }
  .md-hzoom img,.md-hzoom .bg{ transition: transform .55s cubic-bezier(.22,.61,.2,1); will-change:transform; }
  .md-hzoom:hover img,.md-hzoom:hover .bg{ transform: scale(1.08); }
  .md-hcap{ position:absolute; left:0; right:0; bottom:0; padding:16px; color:#fff;
    background:linear-gradient(transparent, rgba(17,24,35,.85)); transform: translateY(30%); opacity:0;
    transition: transform .4s cubic-bezier(.22,.61,.2,1), opacity .4s; }
  .md-hzoom:hover .md-hcap{ transform: translateY(0); opacity:1; }

  /* animated underline sweep */
  .md-link{ background-image: linear-gradient(currentColor, currentColor);
    background-repeat:no-repeat; background-position: left 100%; background-size: 0% 2px;
    transition: background-size .3s cubic-bezier(.22,.61,.2,1); text-decoration:none; padding-bottom:1px; }
  .md-link:hover{ background-size: 100% 2px; }

  /* glossy shine sweep */
  .md-shine{ position:relative; overflow:hidden; }
  .md-shine::after{ content:""; position:absolute; inset:0; pointer-events:none;
    background:linear-gradient(120deg, transparent 30%, rgba(255,255,255,.5) 50%, transparent 70%);
    transform: translateX(-130%); transition: transform .6s cubic-bezier(.22,.61,.2,1); }
  .md-shine:hover::after{ transform: translateX(130%); }

  /* brand glow */
  .md-glow{ transition: box-shadow .3s, transform .3s; }
  .md-glow:hover{ box-shadow: 0 0 0 4px rgba(200,16,46,.14), 0 12px 34px rgba(200,16,46,.22); }

  /* text swap */
  .md-swap{ position:relative; display:inline-grid; overflow:hidden; line-height:1.15; }
  .md-swap .a,.md-swap .b{ grid-area:1/1; transition: transform .32s cubic-bezier(.22,.61,.2,1); }
  .md-swap .b{ transform: translateY(110%); }
  .md-swap:hover .a{ transform: translateY(-110%); }
  .md-swap:hover .b{ transform: translateY(0); }
}

/* premium-motion : perpetual loop pack  (yewho-web-effects 2.2, Phase 8)
   CSS-only ambient loops — no JS needed. Complements .md-beat (scene.css) and
   .md-float (scene.js). Keep them SLOW and quiet: these are sub-conscious cues.

     .md-ring    — pulse rings emanating from the element's edge (CTA / live dot /
                   avatar). Radius is inherited, so it works on pills and circles.
                   Tune: --ring-c (color), --ring-dur (def 2.6s), --ring-s (spread, def 1.35)
     .md-breathe — soft glow that swells and settles (box-shadow; small area only).
                   Tune: --breathe-c, --breathe-dur (def 4.5s)
     .md-sway    — gentle pendulum rotate for icons / badges hanging from their top.
                   Tune: --sway-a (def 2.4deg), --sway-dur (def 5s)
     .md-blink   — status-dot blink (the classic REC dot). Tune: --blink-dur (def 1.5s)

   Stagger any of them across siblings with style="--d:.2s" like md-beat.
   All stop under data-motion lite/off and prefers-reduced-motion.
   ============================================================ */

/* ---- emanating pulse rings ---- */
.md-ring{ position:relative; }
.md-ring::before,.md-ring::after{
  content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  border:2px solid var(--ring-c,rgba(200,16,46,.55)); opacity:0;
  animation: md-ring var(--ring-dur,2.6s) cubic-bezier(.22,.61,.2,1) var(--d,0s) infinite;
}
.md-ring::after{ animation-delay: calc(var(--d,0s) + var(--ring-dur,2.6s) / 2); }
@keyframes md-ring{
  0%{ transform:scale(1); opacity:.65; }
  100%{ transform:scale(var(--ring-s,1.35)); opacity:0; }
}

/* ---- glow breathe ---- */
.md-breathe{ animation: md-breathe var(--breathe-dur,4.5s) ease-in-out var(--d,0s) infinite; }
@keyframes md-breathe{
  0%,100%{ box-shadow:0 0 0 0 rgba(200,16,46,0); }
  50%    { box-shadow:0 0 26px 2px var(--breathe-c,rgba(200,16,46,.32)); }
}

/* ---- pendulum sway ---- */
.md-sway{ transform-origin:50% -4px; display:inline-block;
  animation: md-sway var(--sway-dur,5s) ease-in-out var(--d,0s) infinite; }
@keyframes md-sway{
  0%,100%{ transform:rotate(calc(-1 * var(--sway-a,2.4deg))); }
  50%    { transform:rotate(var(--sway-a,2.4deg)); }
}

/* ---- status blink ---- */
.md-blink{ animation: md-blink var(--blink-dur,1.5s) ease-in-out var(--d,0s) infinite; }
@keyframes md-blink{ 0%,100%{ opacity:1; } 50%{ opacity:.25; } }

/* ---- governor / reduced-motion ---- */
:root[data-motion="lite"] .md-ring::before, :root[data-motion="off"] .md-ring::before,
:root[data-motion="lite"] .md-ring::after,  :root[data-motion="off"] .md-ring::after,
:root[data-motion="lite"] .md-breathe,      :root[data-motion="off"] .md-breathe,
:root[data-motion="lite"] .md-sway,         :root[data-motion="off"] .md-sway,
:root[data-motion="lite"] .md-blink,        :root[data-motion="off"] .md-blink{ animation:none; }
@media (prefers-reduced-motion: reduce){
  .md-ring::before,.md-ring::after,.md-breathe,.md-sway,.md-blink{ animation:none; }
}

/* premium-motion : micro-interaction kit  (premium-motion 2.0, Phase 4)
   Pair with micro.js. Small, high-polish state transitions for forms & buttons.
     .md-ripple   — material-style ripple from the press point
     .md-check    — SVG tick that draws itself (add .in, or via md-copy)
     .md-copy     — copy-to-clipboard button that morphs to a check
     .md-field    — input focus glow + floating label
     .md-skeleton — shimmer placeholder that crossfades to .md-content
   ============================================================ */
.md-ripple{ position:relative; overflow:hidden; }
.md-ripple .mr{ position:absolute; border-radius:50%; transform:scale(0); pointer-events:none;
  background:currentColor; opacity:.28; animation: md-ripple .6s cubic-bezier(.22,.61,.2,1); }
@keyframes md-ripple{ to{ transform:scale(2.4); opacity:0; } }

.md-check path{ stroke-dasharray:24; stroke-dashoffset:24; }
.md-check.in path{ transition: stroke-dashoffset .5s cubic-bezier(.22,.61,.2,1); stroke-dashoffset:0; }
[data-motion="off"] .md-check path{ stroke-dashoffset:0; }

.md-copy{ display:inline-flex; align-items:center; gap:8px; }
.md-copy .ic{ display:inline-grid; place-items:center; width:16px; height:16px; }
.md-copy.done{ color:#1a8f60; }

.md-field{ position:relative; }
.md-field input{ width:100%; box-sizing:border-box; padding:16px 14px 8px; border:1px solid var(--line,#e4e7ec);
  border-radius:10px; font:inherit; outline:none; background:#fff; transition:border-color .2s, box-shadow .2s; }
.md-field input:focus{ border-color:var(--red,#C8102E); box-shadow:0 0 0 4px rgba(200,16,46,.12); }
.md-field label{ position:absolute; left:14px; top:14px; color:var(--muted,#8a93a0); font-size:14px;
  pointer-events:none; transition:.2s cubic-bezier(.22,.61,.2,1); }
.md-field input:focus + label,
.md-field input:not(:placeholder-shown) + label{ top:6px; font-size:10.5px; color:var(--red,#C8102E); font-weight:700; letter-spacing:.04em; text-transform:uppercase; }

.md-skeleton > .md-content{ opacity:0; }
.md-skeleton.loaded > .md-sk{ display:none; }
.md-skeleton.loaded > .md-content{ opacity:1; transition:opacity .5s ease; }
.md-sk i{ display:block; height:12px; border-radius:6px; margin:9px 0;
  background:linear-gradient(90deg,#eef1f4,#f6f8fa,#eef1f4); background-size:200% 100%; }
@media (prefers-reduced-motion: no-preference){ .md-sk i{ animation: md-sk 1.4s infinite; }
  @keyframes md-sk{ to{ background-position:-200% 0; } } }

/* premium-motion : modern CSS engine pack  (yewho-web-effects 2.3, Phase 9)
   Zero-JS effects unlocked by 2024+ CSS. EVERYTHING here is wrapped in
   @supports / @media guards — older browsers simply get the static state.

     .md-pop        — entry/exit animation for [popover] & <dialog> via
                      @starting-style (open: rises in; close: settles out)
     details.md-acc — accordion whose height:auto ANIMATES (interpolate-size);
                      no JS height measuring ever again
     .md-path       — a glowing dot that endlessly runs around the element's own
                      border (offset-path: border-box; follows border-radius!)
                      Tune: --path-c, --path-dur (def 7s), --path-size (def 8px)
     .md-hoverdim   — hover one child, the SIBLINGS dim + shrink slightly
                      (pure :has()) — the "focus what I point at" grid
     .md-focusdim   — same but keyboard/:focus-within driven (forms, menus)

   Easing tokens (linear() — gentle settle, ≤0.6% overshoot, still "no bounce"):
     --ease-settle  — for entrances that should land with weight
     --ease-glide   — long decelerating glide for large travels
   ============================================================ */

:root{
  --md-ease: cubic-bezier(.22,.61,.2,1);
}
@supports (transition-timing-function: linear(0, 1)){
  :root{
    --ease-settle: linear(0, .38 18%, .74 36%, .93 54%, 1.006 76%, 1);
    --ease-glide:  linear(0, .55 22%, .84 44%, .96 66%, .995 84%, 1);
  }
}

/* ---- popover / dialog entrance (@starting-style) ----------------------- */
@supports (transition-behavior: allow-discrete){
  .md-pop{
    opacity:1; transform:translateY(0) scale(1);
    transition: opacity .3s var(--md-ease), transform .3s var(--md-ease),
                display .3s allow-discrete, overlay .3s allow-discrete;
  }
  .md-pop:not(:popover-open):not([open]){ opacity:0; transform:translateY(10px) scale(.985); }
  @starting-style{
    .md-pop:popover-open, dialog.md-pop[open]{ opacity:0; transform:translateY(10px) scale(.985); }
  }
  dialog.md-pop::backdrop{
    background:rgba(17,24,35,.45); backdrop-filter:blur(3px);
    transition: opacity .3s var(--md-ease), display .3s allow-discrete, overlay .3s allow-discrete;
  }
  @starting-style{ dialog.md-pop[open]::backdrop{ opacity:0; } }
  @media (prefers-reduced-motion: reduce){
    .md-pop, dialog.md-pop::backdrop{ transition-duration:1ms; }
  }
}

/* ---- animating accordion (height: auto tween) --------------------------- */
@supports (interpolate-size: allow-keywords) {
  details.md-acc{ interpolate-size: allow-keywords; }
  details.md-acc::details-content{
    height:0; overflow:clip; opacity:0;
    transition: height .42s var(--md-ease), opacity .35s var(--md-ease),
                content-visibility .42s allow-discrete;
  }
  details.md-acc[open]::details-content{ height:auto; opacity:1; }
  @media (prefers-reduced-motion: reduce){
    details.md-acc::details-content{ transition-duration:1ms; }
  }
}
details.md-acc > summary{ cursor:pointer; list-style:none; }
details.md-acc > summary::-webkit-details-marker{ display:none; }
details.md-acc > summary .md-acc-chev{ display:inline-block; transition:transform .3s var(--md-ease); }
details.md-acc[open] > summary .md-acc-chev{ transform:rotate(180deg); }

/* ---- border runner (offset-path around the element's own edge) --------- */
@supports (offset-path: border-box){
  .md-path{ position:relative; }
  .md-path::after{
    content:""; position:absolute; z-index:1; pointer-events:none;
    width:var(--path-size,8px); height:var(--path-size,8px); border-radius:50%;
    background:var(--path-c,#C8102E);
    box-shadow:0 0 10px 2px color-mix(in srgb, var(--path-c,#C8102E) 55%, transparent);
    offset-path: border-box; offset-anchor: 50% 50%;
    animation: md-path var(--path-dur,7s) linear infinite;
  }
  /* a soft comet tail — second, dimmer dot slightly behind */
  .md-path.tail::before{
    content:""; position:absolute; z-index:1; pointer-events:none;
    width:calc(var(--path-size,8px) * .62); height:calc(var(--path-size,8px) * .62);
    border-radius:50%; background:var(--path-c,#C8102E); opacity:.35; filter:blur(1px);
    offset-path: border-box; offset-anchor: 50% 50%;
    animation: md-path var(--path-dur,7s) linear infinite;
    animation-delay: calc(var(--path-dur,7s) / -60);
  }
  @keyframes md-path{ from{ offset-distance:0%; } to{ offset-distance:100%; } }
  :root[data-motion="lite"] .md-path::after, :root[data-motion="lite"] .md-path.tail::before,
  :root[data-motion="off"]  .md-path::after, :root[data-motion="off"]  .md-path.tail::before{
    animation:none; display:none;
  }
  @media (prefers-reduced-motion: reduce){
    .md-path::after,.md-path.tail::before{ animation:none; display:none; }
  }
}

/* ---- hover-dim group (:has) --------------------------------------------- */
@media (hover:hover){
  .md-hoverdim > *{ transition: opacity .32s var(--md-ease), transform .32s var(--md-ease),
                    filter .32s var(--md-ease); }
  .md-hoverdim:has(> :hover) > :not(:hover){ opacity:.45; }
  @media (prefers-reduced-motion: no-preference){
    .md-hoverdim:has(> :hover) > :not(:hover){ transform:scale(.985); }
  }
}

/* ---- focus-dim group (keyboard equivalent) ------------------------------ */
.md-focusdim > *{ transition: opacity .32s var(--md-ease); }
.md-focusdim:has(> :focus-within) > :not(:focus-within){ opacity:.5; }

/* premium-motion : mouse-parallax scene  (premium-motion 2.0, Phase 5)
   Layers drift with the cursor to fake depth. Pair with mouse.js. Fine-pointer + full tier.
     <div class="md-mouse">
       <div class="md-mouse-layer" data-depth="0.06">back</div>
       <div class="md-mouse-layer" data-depth="0.16">front</div>
     </div>
   depth = px of travel per px of cursor offset from centre.
   ============================================================ */
.md-mouse{ position:relative; }
.md-mouse-layer{ transition: transform .25s cubic-bezier(.22,.61,.2,1); will-change: transform; }
[data-motion="lite"] .md-mouse-layer,[data-motion="off"] .md-mouse-layer{ transform:none !important; }

/* premium-motion : orbit rings  (yewho-web-effects 2.3, Phase 9)  CSS-ONLY
   Icon chips orbiting a centre mark on slow concentric rings — the classic
   "everything revolves around your website" hero. Chips counter-rotate so
   they stay upright the whole way round.

   Markup:
     <div class="md-orbit" style="--orbit-size:440px">
       <div class="md-orbit-center">…logo / icon…</div>
       <div class="md-orbit-ring" style="--inset:12%; --orbit-dur:38s">
         <div class="md-orbit-item" style="--a:0deg"><span class="chip">…</span></div>
         <div class="md-orbit-item" style="--a:120deg"><span class="chip">…</span></div>
         <div class="md-orbit-item" style="--a:240deg"><span class="chip">…</span></div>
       </div>
       <div class="md-orbit-ring rev" style="--inset:30%; --orbit-dur:26s"> … </div>
     </div>
   Spread items evenly with --a. Inner rings: raise --inset. `.rev` = orbits
   the other way. Lite/off/RM: rings freeze (the spread still reads as a map).
   ============================================================ */
.md-orbit{ position:relative; width:var(--orbit-size,420px); max-width:100%;
  aspect-ratio:1; margin-inline:auto; }
.md-orbit-center{ position:absolute; top:50%; left:50%; translate:-50% -50%;
  z-index:2; display:grid; place-items:center; }

.md-orbit-ring{ position:absolute; inset:var(--inset,0);
  border:1px dashed var(--orbit-line,#DFE3E8); border-radius:50%; }
.md-orbit-item{ position:absolute; inset:0; pointer-events:none;
  transform:rotate(var(--a,0deg)); }
.md-orbit-item > *{ position:absolute; top:0; left:50%; translate:-50% -50%;
  pointer-events:auto; rotate:calc(-1 * var(--a,0deg)); }

@media (prefers-reduced-motion: no-preference){
  :root[data-motion="full"] .md-orbit-ring{
    animation: md-orbit var(--orbit-dur,36s) linear infinite; }
  :root[data-motion="full"] .md-orbit-item > *{
    animation: md-orbit-c var(--orbit-dur,36s) linear infinite; }
  :root[data-motion="full"] .md-orbit-ring.rev,
  :root[data-motion="full"] .md-orbit-ring.rev .md-orbit-item > *{
    animation-direction: reverse; }
}
@keyframes md-orbit{ to{ transform:rotate(360deg); } }
@keyframes md-orbit-c{
  from{ rotate:calc(-1 * var(--a,0deg)); }
  to  { rotate:calc(-1 * var(--a,0deg) - 360deg); }
}

/* a ready-made chip look (optional — style your own freely) */
.md-orbit .chip{ display:grid; place-items:center; width:52px; height:52px;
  border-radius:50%; background:#fff; border:1px solid var(--line,#E7EAEE);
  box-shadow:0 10px 24px rgba(17,24,35,.10); font-size:20px; }

/* premium-motion : typed custom-property pack  (yewho-web-effects 2.4, Phase 10)
   `@property` registers a custom property WITH A TYPE, which makes it animatable —
   so gradient angles, hues and positions can now tween in pure CSS (an unregistered
   `--x` can only snap). Everything is @supports-guarded; unsupported browsers get the
   static first frame.

     .md-conic     — a conic-gradient border/disc that sweeps smoothly (angle tween)
                     Tune: --mc-c1,--mc-c2 (colours), --mc-cd (dur, def 6s), --mc-cw (ring width)
     .md-auroratext— headline text whose gradient hue drifts (living, not garish)
                     Tune: --mc-h1 (base hue 348=brand red), --mc-hd (dur, def 9s)
     .md-sheen     — a soft light band that sweeps across a surface (position tween)
                     Tune: --mc-sd (dur, def 4.5s)
   ============================================================ */

@property --mc-angle{ syntax: '<angle>';      inherits: false; initial-value: 0deg; }
@property --mc-hue  { syntax: '<angle>';      inherits: false; initial-value: 0deg; }
@property --mc-sweep{ syntax: '<percentage>'; inherits: false; initial-value: 0%; }

/* ---- smoothly-rotating conic ring --------------------------------------- */
@supports (background: conic-gradient(from var(--mc-angle), red, blue)){
  .md-conic{
    position: relative; border-radius: inherit;
    background:
      linear-gradient(var(--mc-cbg,#fff), var(--mc-cbg,#fff)) padding-box,
      conic-gradient(from var(--mc-angle),
        var(--mc-c1,#C8102E), var(--mc-c2,rgba(200,16,46,.12)) 30%,
        var(--mc-c2,rgba(200,16,46,.12)) 65%, var(--mc-c1,#C8102E)) border-box;
    border: var(--mc-cw,2px) solid transparent;
    animation: mc-spin var(--mc-cd,6s) linear infinite;
  }
  @keyframes mc-spin{ to{ --mc-angle: 360deg; } }
}

/* ---- aurora gradient text (hue drift) ----------------------------------- */
@supports (background: linear-gradient(hsl(0 80% 50%), hsl(0 80% 50%))) and
          ((-webkit-background-clip: text) or (background-clip: text)){
  .md-auroratext{
    background: linear-gradient(92deg,
      hsl(calc(var(--mc-h1,348deg) + var(--mc-hue)) 72% 46%),
      hsl(calc(var(--mc-h1,348deg) + var(--mc-hue) + 40deg) 66% 40%));
    -webkit-background-clip: text; background-clip: text;
    -webkit-text-fill-color: transparent; color: transparent;
    animation: mc-huedrift var(--mc-hd,9s) ease-in-out infinite;
  }
  /* stay ON-BRAND: oscillate a narrow hue range around the base (red↔rose↔crimson),
     never a full-spectrum rainbow. Widen with --mc-hrange if you really want more. */
  @keyframes mc-huedrift{
    0%,100%{ --mc-hue: calc(var(--mc-hrange,18deg) * -1); }
    50%    { --mc-hue: var(--mc-hrange,18deg); }
  }
}

/* ---- sheen sweep across a surface --------------------------------------- */
.md-sheen{ position: relative; overflow: hidden; }
.md-sheen::after{
  content:""; position:absolute; inset:0; pointer-events:none;
  background: linear-gradient(105deg, transparent calc(var(--mc-sweep) - 18%),
              color-mix(in srgb,#fff 55%,transparent) var(--mc-sweep),
              transparent calc(var(--mc-sweep) + 18%));
  animation: mc-sheen var(--mc-sd,4.5s) var(--mc-ease,cubic-bezier(.22,.61,.2,1)) infinite;
}
@keyframes mc-sheen{ 0%{ --mc-sweep: -10%; } 55%,100%{ --mc-sweep: 120%; } }

/* ---- governor + reduced-motion: freeze on a good-looking static frame ----- */
:root[data-motion="lite"] .md-conic, :root[data-motion="off"] .md-conic,
:root[data-motion="lite"] .md-auroratext, :root[data-motion="off"] .md-auroratext,
:root[data-motion="lite"] .md-sheen::after, :root[data-motion="off"] .md-sheen::after{
  animation: none !important;
}
@media (prefers-reduced-motion: reduce){
  .md-conic, .md-auroratext, .md-sheen::after{ animation: none !important; }
}

/* premium-motion : reveal variants  (premium-motion 2.0, Phase 5)
   Entrance variants beyond the core fade-up. Pair with reveal.js (adds .in on view).
   Combine one base with one variant class:
     <div class="md-rv zoom">…</div>   scale-in
     <div class="md-rv blur">…</div>   blur-in
     <div class="md-rv flip">…</div>   3D flip-in
     <div class="md-rv rot">…</div>    tilt-in
     <div class="md-rv mask">…</div>   clip-path mask-up
     <div class="md-rv left|right|rise">…</div>  directional slide
   Respects data-stagger on the parent (reveal.js reads --d like the core engine).
   ============================================================ */
.md-rv{ opacity:0; transition: opacity .7s cubic-bezier(.22,.61,.2,1), transform .7s cubic-bezier(.22,.61,.2,1), filter .7s cubic-bezier(.22,.61,.2,1), clip-path .7s cubic-bezier(.22,.61,.2,1); transition-delay: var(--d,0ms); }
.md-rv.in{ opacity:1; transform:none; filter:none; }

.md-rv.zoom { transform: scale(.9); }
.md-rv.blur { filter: blur(14px); }
.md-rv.rise { transform: translateY(42px); }
.md-rv.left { transform: translateX(-42px); }
.md-rv.right{ transform: translateX(42px); }
.md-rv.rot  { transform: rotate(-4deg) translateY(30px); transform-origin: left bottom; }
.md-rv.flip { transform: perspective(900px) rotateX(-38deg); transform-origin: center bottom; }
.md-rv.mask { opacity:1; clip-path: inset(0 0 100% 0); }
.md-rv.mask.in{ clip-path: inset(0 0 0 0); }

[data-motion="off"] .md-rv{ opacity:1 !important; transform:none !important; filter:none !important; clip-path:none !important; }

/* premium-motion : cinematic scene pack  (premium-motion 2.0, Phase 6)
   Pair with scene.js. Ported from field use (RZEC fire-protection site).

     Embers  — spark drift over a hero / dark banner (host must be positioned):
       <div class="hero md-embers" data-max="36" data-rate="0.35" data-hue="30"></div>
       hue 30 = warm ember, 200 = cool spark; data-spawn="full" emits over whole area.

     Ghost   — giant watermark word drifting on scroll, behind a section's content:
       <section class="stats"> <div class="md-ghost light">TRUSTED</div> ...content... </section>
       put it as the FIRST child; .light = for dark sections. data-drift = px travel.

     Cue     — CSS-only scroll-down hint (no JS): <div class="md-cue"></div>

     Float   — perpetual idle bob after entrance (JS): class="md-float" data-amp="5"

     Beat    — perpetual "big & small" scale pulse (CSS only). Stagger siblings with
               style="--d:.12s" (index*step) so the pulse RUNS across them as a wave:
                 <div class="card md-beat" style="--d:0s">...</div>
               Works on HTML blocks AND SVG <g> nodes (scales from each element's own
               centre, in place). Tune: --beat (peak scale, def 1.08), --beat-dur (def 2.6s).
   ============================================================ */

/* ---- embers canvas ---- */
.md-embers{ position:relative; }
.md-embers-cv{ position:absolute; inset:0; width:100%; height:100%; pointer-events:none; z-index:1; }
.md-embers > *:not(.md-embers-cv){ position:relative; z-index:2; }

/* ---- ghost watermark ---- */
.md-ghost{
  position:absolute; top:50%; left:50%; z-index:0; pointer-events:none;
  transform:translate3d(0,0,0);
  font-family:'Montserrat',system-ui,sans-serif; font-weight:800;
  font-size:clamp(90px,20vw,300px); line-height:1; letter-spacing:-.03em;
  white-space:nowrap; margin-top:-.5em; margin-left:-50vw; width:100vw; text-align:center;
  color:rgba(31,42,55,.045);
  user-select:none;
}
.md-ghost.light{ color:rgba(255,255,255,.05); }
.md-ghost + *{ position:relative; z-index:1; }
[data-motion="off"] .md-ghost{ transform:none !important; }

/* ---- idle float ---- */
.md-float{ will-change:transform; }
[data-motion="off"] .md-float{ transform:none !important; }

/* ---- beat : perpetual scale pulse; stagger via --d for a running wave ---- */
@keyframes md-beat{ 0%,54%,100%{ transform:scale(1); } 70%{ transform:scale(var(--beat,1.08)); } }
.md-beat{ transform-box:fill-box; transform-origin:center;
  animation:md-beat var(--beat-dur,2.6s) cubic-bezier(.22,.61,.2,1) infinite; animation-delay:var(--d,0s); }
[data-motion="off"] .md-beat{ animation:none; }
@media (prefers-reduced-motion: reduce){ .md-beat{ animation:none; } }

/* ---- scroll cue (pure CSS) ---- */
.md-cue{
  position:absolute; left:50%; bottom:22px; transform:translateX(-50%); z-index:3;
  width:26px; height:42px; border:2px solid rgba(255,255,255,.55); border-radius:16px;
  pointer-events:none;
}
.md-cue::before{
  content:""; position:absolute; left:50%; top:8px; width:4px; height:8px; border-radius:2px;
  background:rgba(255,255,255,.85); transform:translateX(-50%);
  animation:md-cue-dot 1.6s cubic-bezier(.22,.61,.2,1) infinite;
}
@keyframes md-cue-dot{ 0%{opacity:0;transform:translate(-50%,0)} 20%{opacity:1} 80%{opacity:1} 100%{opacity:0;transform:translate(-50%,16px)} }
@media (prefers-reduced-motion: reduce){ .md-cue::before{ animation:none; opacity:.85; } }

/* premium-motion : native scroll-driven motion  (premium-motion 2.0, Phase 1)
   Layer this AFTER motion.css. Everything here runs on the GPU with zero
   main-thread cost via CSS scroll/view timelines, and is fully guarded:
     - only when the user allows motion
     - only when the browser supports the timeline
     - the IntersectionObserver engine in motion.js remains the fallback
   Governor tiers still apply: nothing here fires at [data-motion="off"].
   ============================================================ */

/* one easing to rule them all (matches motion.css) */
:root{ --md-ease: cubic-bezier(.22,.61,.2,1); }

/* --- reading progress bar (fixed, top of viewport) --------------------- */
/* markup: <div class="md-progress" aria-hidden="true"></div> (first in body) */
.md-progress{
  position:fixed; inset:0 0 auto 0; height:2px; z-index:120;
  transform:scaleX(0); transform-origin:0 50%;
  background:linear-gradient(90deg, var(--red-dark,#9c0c24), var(--red,#C8102E));
  pointer-events:none;
}
@media (prefers-reduced-motion:no-preference){
  @supports (animation-timeline: scroll()){
    .md-progress{ animation: md-prog linear both; animation-timeline: scroll(root block); }
    @keyframes md-prog{ from{transform:scaleX(0)} to{transform:scaleX(1)} }
  }
}

@media (prefers-reduced-motion:no-preference){
  @supports (animation-timeline: view()){

    /* --- reveal-on-view, GPU-native (drop-in for .reveal on capable UAs) - */
    /* opt in with .md-reveal so it never fights the JS .reveal engine       */
    .md-reveal{ animation: md-rv linear both; animation-timeline: view();
                animation-range: entry 0% entry 34%; }
    .md-reveal.rx{ animation-name: md-rv-x; }
    @keyframes md-rv  { from{opacity:0; transform:translateY(24px)} to{opacity:1; transform:none} }
    @keyframes md-rv-x{ from{opacity:0; transform:translateX(24px)} to{opacity:1; transform:none} }

    /* --- parallax layers: --md-par = travel (e.g. -8%) ------------------- */
    .md-par{ animation: md-par linear both; animation-timeline: view(); animation-range: cover; }
    @keyframes md-par{ from{transform:translateY(var(--md-par,-8%))}
                       to  {transform:translateY(calc(var(--md-par,-8%) * -1))} }

    /* --- pinned scroll-scrub scene -------------------------------------- */
    /* wrap:.md-pin (tall) > .md-pin-stage (sticky) > .md-pin-fill (grows)  */
    .md-pin{ position:relative; }
    .md-pin-stage{ position:sticky; top:0; height:100vh; overflow:hidden;
      display:grid; place-items:center; }
    .md-pin-fill{ transform:scaleX(0); transform-origin:0 50%;
      animation: md-fill linear both; animation-timeline: scroll(nearest);
      animation-range: contain; }
    @keyframes md-fill{ from{transform:scaleX(0)} to{transform:scaleX(1)} }

    /* --- horizontal scroll gallery: .md-hscroll > .md-htrack ------------- */
    .md-hscroll{ position:relative; }
    .md-htrack{ position:sticky; top:0; display:flex; gap:24px;
      animation: md-hx linear both; animation-timeline: scroll(nearest);
      animation-range: contain; }
    @keyframes md-hx{ from{transform:translateX(0)} to{transform:translateX(calc(-100% + 100vw))} }
  }
}

/* lite tier keeps the cheap GPU reveals/progress but no pin/parallax churn */
[data-motion="lite"] .md-par,
[data-motion="lite"] .md-htrack{ animation:none !important; transform:none !important; }
[data-motion="off"]  [class^="md-"]{ animation:none !important; transform:none !important; opacity:1 !important; }

/* premium-motion : storyboard player  (yewho-web-effects 2.8, Phase 14)
   A cinematic scene sequencer — many `.md-sb-scene` panels that crossfade one to the
   next like a short film, with a narration line, progress bar and clickable dots.
   Pairs with story-player.js. Scene visuals are your own markup (a graph.js diagram,
   SVG, or styled HTML), so the player is content-agnostic.

     <div class="md-storyboard" data-interval="4200" style="--sb-h:440px">
       <div class="md-sb-stage">
         <div class="md-sb-scene" data-cap="You launch your website."> …visual… </div>
         <div class="md-sb-scene" data-cap="Then who keeps it running?"> …visual… </div>
       </div>
       <div class="md-sb-bar"></div>
       <p class="md-sb-cap"></p>
       <div class="md-sb-dots"></div>
     </div>

   Reduced-motion / governor 'off': no autoplay — scene 1 shows, dots still let you step.
   ============================================================ */

.md-storyboard{ position:relative; --sb-ease:cubic-bezier(.22,.61,.2,1); }
.md-sb-stage{ position:relative; min-height:var(--sb-h,440px); }

/* scenes are stacked; only .active is shown */
.md-sb-scene{
  position:absolute; inset:0; display:grid; place-items:center;
  opacity:0; transform:scale(.985) translateY(8px); pointer-events:none;
  transition:opacity .7s var(--sb-ease), transform .7s var(--sb-ease);
}
.md-sb-scene.active{ opacity:1; transform:none; pointer-events:auto; }
/* scene that just left slides the other way for continuity */
.md-sb-scene.prev{ transform:scale(.985) translateY(-8px); }

/* narration line */
.md-sb-cap{
  text-align:center; margin:18px auto 0; max-width:34ch; min-height:2.4em;
  font-family:var(--disp,'Plus Jakarta Sans',sans-serif); font-weight:800;
  font-size:clamp(1.15rem,2.8vw,1.7rem); line-height:1.25; color:var(--sb-fg,#fff);
  transition:opacity .45s var(--sb-ease), transform .45s var(--sb-ease);
}
.md-sb-cap.swap{ opacity:0; transform:translateY(6px); }

/* progress bar (fills over each scene's interval) */
.md-sb-bar{ position:relative; height:3px; border-radius:3px; margin-top:20px;
  background:var(--sb-track,rgba(255,255,255,.14)); overflow:hidden; }
.md-sb-bar::after{ content:""; position:absolute; left:0; top:0; bottom:0; width:0;
  background:var(--sb-accent,#C8102E); border-radius:3px; }
.md-sb-bar.run::after{ width:100%; transition:width var(--sb-dur,4200ms) linear; }

/* dots */
.md-sb-dots{ display:flex; gap:8px; justify-content:center; margin-top:16px; }
.md-sb-dot{ width:9px; height:9px; padding:0; border:0; border-radius:50%; cursor:pointer;
  background:var(--sb-dot,rgba(255,255,255,.28)); transition:.3s var(--sb-ease); }
.md-sb-dot:hover{ background:var(--sb-dot-h,rgba(255,255,255,.5)); }
.md-sb-dot.on{ width:28px; border-radius:5px; background:var(--sb-accent,#C8102E); }

/* governor / reduced-motion: no crossfade drama, no autoplay bar */
:root[data-motion="off"] .md-sb-scene, :root[data-motion="lite"] .md-sb-scene{ transition-duration:.2s; }
@media (prefers-reduced-motion: reduce){
  .md-sb-scene{ transition:opacity .2s linear; transform:none !important; }
  .md-sb-bar{ display:none; }
  .md-sb-cap{ transition:none; }
}

/* premium-motion : scrollytelling story  (yewho-web-effects 2.3, Phase 9)
   Pairs with story.js. Steps scroll on one side; a STICKY visual on the other
   crossfades to match whichever step is at the middle of the screen — the
   classic product-features scrolly.

   Markup:
     <section class="md-story">
       <div class="md-story-visual">
         <div class="md-vis" data-vis="design">…</div>
         <div class="md-vis" data-vis="build">…</div>
         <div class="md-vis" data-vis="launch">…</div>
       </div>
       <div class="md-story-steps">
         <div class="md-step" data-vis="design"><h3>…</h3><p>…</p></div>
         <div class="md-step" data-vis="build">…</div>
         <div class="md-step" data-vis="launch">…</div>
       </div>
     </section>
   `.flip` on the section puts the visual on the right. Under 860px the visual
   pane sticks to the top at reduced height. RM keeps the swap (content), just
   without the travel.
   ============================================================ */
.md-story{ display:grid; grid-template-columns: 1fr 1fr; gap:48px; align-items:start; }
.md-story.flip .md-story-visual{ order:2; }

.md-story-visual{ position:sticky; top:90px; min-height:380px; }
.md-story-visual .md-vis{ position:absolute; inset:0; opacity:0; visibility:hidden;
  display:grid; place-items:center; }
@media (prefers-reduced-motion: no-preference){
  .md-story-visual .md-vis{
    transform:translateY(14px) scale(.985);
    transition: opacity .5s var(--md-ease,cubic-bezier(.22,.61,.2,1)),
                transform .5s var(--md-ease,cubic-bezier(.22,.61,.2,1)),
                visibility 0s linear .5s; }
  .md-story-visual .md-vis.on{ transform:none; }
}
.md-story-visual .md-vis.on{ opacity:1; visibility:visible; transition-delay:0s; }

/* steps: the active one is full ink, the rest recede */
.md-step{ padding:34px 0; border-bottom:1px solid var(--line,#EDF0F3);
  opacity:.38; transition: opacity .4s var(--md-ease,cubic-bezier(.22,.61,.2,1)); }
.md-step:last-child{ border-bottom:0; }
.md-step.on{ opacity:1; }
@media (prefers-reduced-motion: reduce){
  .md-step{ opacity:1; transition:none; }
  .md-story-visual .md-vis{ transition:none; }
}
:root[data-motion="off"] .md-step{ opacity:1; }

@media (max-width: 859px){
  .md-story{ grid-template-columns: 1fr; gap:0; }
  .md-story.flip .md-story-visual{ order:0; }
  .md-story-visual{ top:70px; min-height:240px; z-index:1;
    background:inherit; }
}

/* premium-motion : advanced SVG  (premium-motion 2.0, Phase 3)
   Pair with svgfx.js.
     .md-draw  — any <svg> whose <path>/<line>/<polyline> children draw on view
     .md-annotate (+ .circle / .strike) — a hand-drawn mark that sketches around a word
   ============================================================ */

/* hand-drawn annotations: an SVG stroke that "draws" via background-size on view */
.md-annotate{ position:relative; display:inline; padding:0 .06em .06em; }
.md-annotate::after{
  content:""; position:absolute; left:0; right:0; bottom:-.08em; height:.5em;
  background-repeat:no-repeat; background-position:left center; background-size:0% 100%;
  /* squiggly underline */
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='14' viewBox='0 0 120 14' fill='none'%3E%3Cpath d='M2 9c18-6 40 5 58-1s42-6 58 2' stroke='%23C8102E' stroke-width='3' stroke-linecap='round'/%3E%3C/svg%3E");
  pointer-events:none;
}
.md-annotate.circle::after{ bottom:-.35em; height:1.7em; left:-.2em; right:-.2em;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='60' viewBox='0 0 160 60' fill='none'%3E%3Cpath d='M80 4C40 4 8 14 8 30s34 26 72 26 70-9 70-26S128 5 92 4' stroke='%23C8102E' stroke-width='3' stroke-linecap='round'/%3E%3C/svg%3E"); }
.md-annotate.strike::after{ bottom:.42em; height:.4em;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='8' viewBox='0 0 120 8' fill='none'%3E%3Cpath d='M2 5c30-3 88-3 116 0' stroke='%23C8102E' stroke-width='3' stroke-linecap='round'/%3E%3C/svg%3E"); }
/* highlighter sweep (Phase 8): a translucent marker stroke swipes across the phrase,
   BEHIND the text. Tune color via --mark-c (def soft brand red). */
.md-annotate.mark{ z-index:0; }
.md-annotate.mark::after{
  bottom:.02em; height:.72em; left:-.12em; right:-.12em; z-index:-1; border-radius:3px;
  background-image:none; background-color:var(--mark-c,rgba(200,16,46,.16));
  background-size:auto; transform:scaleX(0); transform-origin:left center;
}
.md-annotate.mark.in::after{ transition: transform .6s cubic-bezier(.22,.61,.2,1); transform:scaleX(1); }
[data-motion="off"] .md-annotate.mark::after{ transform:scaleX(1); transition:none; }
.md-annotate.in::after{ transition: background-size .7s cubic-bezier(.22,.61,.2,1); background-size:100% 100%; }
[data-motion="off"] .md-annotate::after{ transition:none; background-size:100% 100%; }

/* draw-on paths: initial hidden state set by JS (dashoffset). Final = fully drawn. */
.md-draw path,.md-draw line,.md-draw polyline{ }

/* premium-motion : sliding-pill tabs  (yewho-web-effects 2.3, Phase 9)
   Pairs with tabs.js. A pill glides under the active tab; panels crossfade.

   Markup:
     <div class="md-tabs">
       <div class="md-tab-list" role="tablist">
         <button class="md-tab" type="button">Design</button>
         <button class="md-tab" type="button">Build</button> …
       </div>
       <div class="md-tab-panels">
         <div class="md-tab-panel">…</div>
         <div class="md-tab-panel">…</div> …
       </div>
     </div>
   (tabs.js wires all ARIA roles/ids and the pill for you)
   ============================================================ */
.md-tab-list{ position:relative; display:inline-flex; gap:4px;
  padding:4px; border-radius:999px; background:var(--soft,#F6F7F9);
  border:1px solid var(--line,#E7EAEE); }
.md-tab{ position:relative; z-index:1; border:0; background:none; cursor:pointer;
  padding:9px 18px; border-radius:999px; white-space:nowrap;
  font:600 14px/1 var(--sans,Inter,system-ui); color:var(--muted,#98A1AE);
  transition:color .25s var(--md-ease,cubic-bezier(.22,.61,.2,1)); }
.md-tab[aria-selected="true"]{ color:#fff; }
.md-tab:focus-visible{ outline:2px solid var(--red,#C8102E); outline-offset:2px; }

/* the gliding pill (tabs.js positions it via --x / --w) */
.md-tab-pill{ position:absolute; top:4px; bottom:4px; left:0; z-index:0;
  width:var(--w,0px); transform:translateX(var(--x,0px));
  border-radius:999px; background:var(--red,#C8102E);
  box-shadow:0 6px 16px rgba(200,16,46,.30);
  transition:transform .32s var(--md-ease,cubic-bezier(.22,.61,.2,1)),
             width .32s var(--md-ease,cubic-bezier(.22,.61,.2,1)); }
@media (prefers-reduced-motion: reduce){ .md-tab-pill{ transition:none; } }

/* panels: crossfade + tiny rise */
.md-tab-panels{ position:relative; margin-top:18px; }
.md-tab-panel{ display:none; }
.md-tab-panel.on{ display:block; }
@media (prefers-reduced-motion: no-preference){
  .md-tab-panel.on{ animation: md-tabin .35s var(--md-ease,cubic-bezier(.22,.61,.2,1)) both; }
  @keyframes md-tabin{ from{ opacity:0; transform:translateY(8px); } to{ opacity:1; transform:none; } }
}

/* premium-motion : text FX  (premium-motion 2.0, Phase 5)
   Pair with texts.js.
     <span class="md-type" data-text="one line|two line|three"></span>  typewriter (| = new line to cycle)
     <span class="md-rotate" data-words="secure,fast,managed"></span>    rotating words
   ============================================================ */
.md-type{ white-space:pre; }
.md-type::after{ content:""; display:inline-block; width:.6ch; height:1.05em; vertical-align:-.15em;
  background: var(--red,#C8102E); margin-left:2px; animation: md-caret 1s steps(1) infinite; }
@keyframes md-caret{ 50%{ opacity:0; } }
[data-motion="off"] .md-type::after{ display:none; }

.md-rotate{ display:inline-grid; overflow:hidden; vertical-align:bottom; }
.md-rotate > span{ grid-area:1/1; transition: transform .5s cubic-bezier(.22,.61,.2,1), opacity .5s; }
.md-rotate > span[data-st="out"]{ transform: translateY(-110%); opacity:0; }
.md-rotate > span[data-st="in"] { transform: translateY(110%); opacity:0; }
.md-rotate > span[data-st="on"] { transform: translateY(0); opacity:1; }

/* flowing gradient text (CSS-only, Phase 8) — <span class="md-gradtext">phrase</span>
   Tune: --gt-c1/--gt-c2 (stops), --gt-dur (def 6s). Static gradient on lite/off/RM. */
.md-gradtext{
  background:linear-gradient(100deg,
    var(--gt-c1,#C8102E) 0%, var(--gt-c2,#ff7a8d) 28%,
    var(--gt-c1,#C8102E) 55%, var(--gt-c2,#ff7a8d) 82%, var(--gt-c1,#C8102E) 100%);
  background-size:220% 100%;
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent; color:transparent;
  animation: md-gradtext var(--gt-dur,6s) linear infinite;
}
@keyframes md-gradtext{ to{ background-position:-220% 0; } }
:root[data-motion="lite"] .md-gradtext, :root[data-motion="off"] .md-gradtext{ animation:none; }
@media (prefers-reduced-motion: reduce){ .md-gradtext{ animation:none; } }

/* premium-motion : dimensional cards  (premium-motion 2.0, Phase 3)
   Pointer-driven 3D tilt with in-card parallax layers and a specular glare.
   Pair with tilt.js. Fine-pointer + full-tier only; degrades to a flat card.
     <article class="md-tilt" data-tilt="8">
       <div class="md-tilt-layer" style="--z:40px">…floats above…</div>
       …base content…
     </article>
   ============================================================ */
.md-tilt{
  transform: perspective(900px) rotateX(var(--rx,0deg)) rotateY(var(--ry,0deg));
  transform-style: preserve-3d;
  transition: transform .3s cubic-bezier(.22,.61,.2,1);
  position: relative; will-change: transform;
}
.md-tilt-layer{
  transform: translate3d(var(--px,0), var(--py,0), var(--z,30px));
  transition: transform .3s cubic-bezier(.22,.61,.2,1);
}
.md-tilt::after{
  content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  background: radial-gradient(300px circle at var(--gx,50%) var(--gy,50%), rgba(255,255,255,.4), transparent 60%);
  opacity:0; transition: opacity .3s; mix-blend-mode: overlay; z-index:2;
}
.md-tilt:hover::after{ opacity:1; }
@media (prefers-reduced-motion: reduce){ .md-tilt,.md-tilt-layer{ transform:none !important; transition:none; } }
:root[data-motion="lite"] .md-tilt,:root[data-motion="off"] .md-tilt{ transform:none !important; }

/* premium-motion : scroll-drawn timeline  (yewho-web-effects 2.3, Phase 9)
   Pairs with timeline.js. A vertical spine that INKS ITSELF IN as you scroll
   (native scroll-timeline where supported), while each milestone's dot pops
   and its card reveals on view (IO — works everywhere).

   Markup:
     <div class="md-timeline">
       <div class="md-tl-item">
         <div class="md-tl-dot"></div>
         <div class="md-tl-card">…heading / copy…</div>
       </div> …
     </div>
   Add .alt to the CONTAINER for alternating left/right cards on wide screens.
   Tune: --tl-c (ink colour), --tl-x (spine offset from left, def 18px).
   ============================================================ */
.md-timeline{ position:relative; padding-left:calc(var(--tl-x,18px) + 30px); }

/* grey base spine + red ink overlay */
.md-timeline::before, .md-timeline::after{
  content:""; position:absolute; top:6px; bottom:6px; left:var(--tl-x,18px);
  width:2px; border-radius:2px;
}
.md-timeline::before{ background:var(--line,#E7EAEE); }
.md-timeline::after{ background:var(--tl-c,#C8102E);
  transform:scaleY(0); transform-origin:50% 0; }
@media (prefers-reduced-motion: no-preference){
  @supports (animation-timeline: view()){
    :root:not([data-motion="off"]) .md-timeline::after{
      animation: md-tl-ink linear both;
      animation-timeline: view();
      animation-range: entry 20% exit 80%;
    }
  }
}
@keyframes md-tl-ink{ from{ transform:scaleY(0); } to{ transform:scaleY(1); } }

/* milestones */
.md-tl-item{ position:relative; padding:0 0 34px 0; }
.md-tl-item:last-child{ padding-bottom:0; }
.md-tl-dot{ position:absolute; top:4px; left:-35px; /* centred on the spine:
  item left edge sits 30px right of the spine line (which is 2px wide) */
  width:12px; height:12px; border-radius:50%; z-index:1;
  background:#fff; border:2px solid var(--line,#D8DDE3);
  transition: border-color .4s var(--md-ease,cubic-bezier(.22,.61,.2,1)),
              background .4s var(--md-ease,cubic-bezier(.22,.61,.2,1)),
              box-shadow .4s var(--md-ease,cubic-bezier(.22,.61,.2,1));
}
.md-tl-item.in .md-tl-dot{ background:var(--tl-c,#C8102E);
  border-color:var(--tl-c,#C8102E); box-shadow:0 0 0 5px rgba(200,16,46,.14); }

html.js .md-tl-card{ opacity:0; transform:translateY(16px);
  transition: opacity .55s var(--md-ease,cubic-bezier(.22,.61,.2,1)),
              transform .55s var(--md-ease,cubic-bezier(.22,.61,.2,1)); }
html.js .md-tl-item.in .md-tl-card{ opacity:1; transform:none; }

@media (prefers-reduced-motion: reduce){
  html.js .md-tl-card{ opacity:1; transform:none; transition:none; }
  .md-tl-dot{ transition:none; }
  .md-timeline::after{ transform:scaleY(1); animation:none; }
}
:root[data-motion="off"] html.js .md-tl-card,
:root[data-motion="off"] .md-tl-card{ opacity:1; transform:none; transition:none; }

/* alternating layout on wide screens */
@media (min-width: 860px){
  .md-timeline.alt{ padding-left:0; }
  .md-timeline.alt::before, .md-timeline.alt::after{ left:50%; margin-left:-1px; }
  .md-timeline.alt .md-tl-item{ width:50%; padding-right:44px; }
  .md-timeline.alt .md-tl-item .md-tl-dot{ left:auto; right:-7px; margin-left:0; }
  .md-timeline.alt .md-tl-item:nth-child(even){ margin-left:50%; padding-right:0; padding-left:44px; }
  .md-timeline.alt .md-tl-item:nth-child(even) .md-tl-dot{ right:auto; left:-5px; }
}

/* premium-motion : kinetic typography  (premium-motion 2.0, Phase 2)
   Pair with type.js. All effects are opt-in via class and reduced-motion safe
   (type.js prints final text / final weight when motion is off).
   ============================================================ */

/* --- odometer digit roll (data-odometer) ------------------------------- */
.odm{ display:inline-flex; align-items:flex-start; font-variant-numeric:tabular-nums; line-height:1; }
.odm-d{ display:inline-block; height:1em; overflow:hidden; }
.odm-reel{ display:flex; flex-direction:column; transform:translateY(0);
           transition:transform 1.15s cubic-bezier(.22,.61,.2,1); will-change:transform; }
.odm-reel > span{ height:1em; display:block; }
.odm-sep{ display:inline-block; }

/* --- gradient shimmer sweep on a phrase (.md-shimmer) ------------------ */
/* readable at every tier; the clip/transparent treatment only under full + motion */
.md-shimmer{ color: var(--red,#C8102E); }
@media (prefers-reduced-motion: no-preference){
  :root[data-motion="full"] .md-shimmer{
    background:linear-gradient(100deg, var(--red-dark,#9c0c24) 0 42%, #ff5470 50%, var(--red-dark,#9c0c24) 58% 100%);
    background-size:250% 100%;
    -webkit-background-clip:text; background-clip:text; color:transparent;
    animation: pm-shimmer 5.5s ease-in-out 1.2s infinite;
  }
  @keyframes pm-shimmer{ 0%,100%{background-position:120% 0} 45%,55%{background-position:-20% 0} }
}

/* --- variable-font weight morph on view (.md-weight) ------------------- */
.md-weight{ font-variation-settings:'wght' 340; transition: font-variation-settings .9s cubic-bezier(.22,.61,.2,1); }
.md-weight.in{ font-variation-settings:'wght' 760; }
[data-motion="off"] .md-weight{ font-variation-settings:'wght' 700; transition:none; }

/* --- decode / scramble (.md-decode) ------------------------------------ */
.md-decode{ white-space:pre-wrap; }

/* yewho typewriter — styles for typewriter.js. Ship both together. */
.tw-a11y{ position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
.tw-vis{ display:inline; }
.tw-vis::after{ content:""; display:inline-block; width:.06em; min-width:2px;
  height:1.05em; background:currentColor; margin-left:.06em; vertical-align:-.12em;
  animation:tw-caret 1s steps(1) infinite; }
@keyframes tw-caret{ 50%{ opacity:0; } }
@media (prefers-reduced-motion:reduce){ .tw-vis::after{ display:none; } }

/* premium-motion : View Transitions  (premium-motion 2.0, Phase 2)
   Cross-document (MPA) navigation morphs for same-origin static sites, plus a
   shared-element pattern so the chrome stays put while content changes.
   Progressive enhancement: unsupported browsers just navigate normally.
   Reduced-motion collapses it to a near-instant crossfade.

   Add this to the shared <head> CSS of EVERY page (same origin), then give the
   persistent chrome a stable name so it doesn't flicker between pages.
   ============================================================ */

/* enable cross-document view transitions for same-origin navigations */
@view-transition{ navigation: auto; }

/* keep the site header/chrome fixed across navigations (must be UNIQUE per page —
   name exactly one element; do NOT also name the footer logo, etc.) */
header{ view-transition-name: pm-header; }

@media (prefers-reduced-motion: no-preference){
  /* gentle content lift: old content settles up, new content rises in */
  ::view-transition-old(root){ animation: pm-vt-out .26s cubic-bezier(.22,.61,.2,1) both; }
  ::view-transition-new(root){ animation: pm-vt-in  .34s cubic-bezier(.22,.61,.2,1) both; }
  @keyframes pm-vt-out{ to  { opacity:0; transform:translateY(-8px); } }
  @keyframes pm-vt-in { from{ opacity:0; transform:translateY(10px); } }
}

/* honour reduced motion: no travel, minimal crossfade */
@media (prefers-reduced-motion: reduce){
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*){ animation-duration: 1ms !important; }
}

/* governor: on constrained devices keep the crossfade but drop the travel */
:root[data-motion="lite"] ::view-transition-old(root),
:root[data-motion="lite"] ::view-transition-new(root){ animation-name: none; }

/* --- shared-element morph pattern (opt-in) -----------------------------
   Give the SAME view-transition-name to matching elements on both pages and
   the browser tweens position/size between them (e.g. a card that becomes a
   hero). Names must be unique within each page.
     .promote{ view-transition-name: hero-media; }
   For dynamic lists, set it in JS just before navigating:
     el.style.viewTransitionName = 'hero-media';
--------------------------------------------------------------------------- */

/* premium-motion : same-document View Transitions  (yewho-web-effects 2.4, Phase 10)
   Styling for transitions driven by vt.js (`document.startViewTransition`). This is
   the IN-PAGE cousin of viewtransition.css (which handles cross-DOCUMENT/MPA morphs):
   here we animate state changes WITHIN one page — filtering a grid, swapping a tab
   panel, opening a detail view — with real shared-element morphs.

   vt.js sets `data-vt="forward|back"` on <html> for the duration of a directional
   swap; plain swaps get the calm default crossfade+lift. Give two elements the same
   `view-transition-name` (via VT.name / inline style) and they MORPH between states.
   ============================================================ */

@supports (view-transition-name: none){

  /* calm default: everything crossfades on one easing/duration ------------- */
  ::view-transition-group(*){
    animation-duration: .42s;
    animation-timing-function: cubic-bezier(.22,.61,.2,1);
  }
  /* the full-page root gets a gentle lift instead of a hard cut ------------ */
  ::view-transition-old(root){ animation: vt-out .30s cubic-bezier(.22,.61,.2,1) both; }
  ::view-transition-new(root){ animation: vt-in  .42s cubic-bezier(.22,.61,.2,1) both; }
  @keyframes vt-out{ to  { opacity:0; transform: translateY(-8px); } }
  @keyframes vt-in { from{ opacity:0; transform: translateY(10px); } }

  /* directional swaps (VT.swap(fn,{type:'forward'|'back'})) ---------------- */
  :root[data-vt="forward"]::view-transition-old(root){ animation: vt-fwd-out .34s cubic-bezier(.22,.61,.2,1) both; }
  :root[data-vt="forward"]::view-transition-new(root){ animation: vt-fwd-in  .42s cubic-bezier(.22,.61,.2,1) both; }
  :root[data-vt="back"]::view-transition-old(root){ animation: vt-back-out .34s cubic-bezier(.22,.61,.2,1) both; }
  :root[data-vt="back"]::view-transition-new(root){ animation: vt-back-in  .42s cubic-bezier(.22,.61,.2,1) both; }
  @keyframes vt-fwd-out { to  { opacity:0; transform: translateX(-4%); } }
  @keyframes vt-fwd-in  { from{ opacity:0; transform: translateX(6%);  } }
  @keyframes vt-back-out{ to  { opacity:0; transform: translateX(4%);  } }
  @keyframes vt-back-in { from{ opacity:0; transform: translateX(-6%); } }

  /* opt a NAMED shared element out of the crossfade so it purely morphs ---- */
  ::view-transition-group(.vt-morph){ animation-duration:.5s; }

  /* reduced-motion: keep the transition (for state clarity) but make it instant */
  @media (prefers-reduced-motion: reduce){
    ::view-transition-group(*),
    ::view-transition-old(*), ::view-transition-new(*){
      animation-duration: 1ms !important;
    }
  }
}

/* premium-motion : vertical ticker  (yewho-web-effects 2.3, Phase 9)  CSS-ONLY
   An endlessly rising column (testimonials, updates, log lines) with soft
   fade masks top and bottom. The vertical twin of the .mq marquee.

   Markup — DUPLICATE the content once, exactly like the marquee:
     <div class="md-vticker" style="--vt-h:340px; --vt-dur:26s">
       <div class="md-vt-track">
         <div>item 1</div><div>item 2</div><div>item 3</div>
         <div>item 1</div><div>item 2</div><div>item 3</div>   <!-- copy -->
       </div>
     </div>
   Hover pauses. Full tier only — lite/off/RM show a static scrollable list.
   ============================================================ */
.md-vticker{ overflow:hidden; height:var(--vt-h,340px); position:relative;
  -webkit-mask-image:linear-gradient(180deg, transparent, #000 14%, #000 86%, transparent);
          mask-image:linear-gradient(180deg, transparent, #000 14%, #000 86%, transparent); }
.md-vt-track{ display:flex; flex-direction:column; gap:var(--vt-gap,14px); }

@media (prefers-reduced-motion: no-preference){
  :root[data-motion="full"] .md-vt-track{
    animation: md-vt var(--vt-dur,26s) linear infinite; }
  :root[data-motion="full"] .md-vticker:hover .md-vt-track{
    animation-play-state:paused; }
}
@keyframes md-vt{
  to{ transform:translateY(calc(-50% - var(--vt-gap,14px) / 2)); }
}

/* graceful degrade: on lite/off the column simply scrolls by touch/wheel */
:root[data-motion="lite"] .md-vticker,
:root[data-motion="off"]  .md-vticker{ overflow-y:auto; scrollbar-width:thin; }

/* premium-motion : liquid section dividers  (yewho-web-effects 2.9, Phase 15)
   The library had NO section-divider vocabulary at all. These are flat-fill
   waves — the shape carries the liquid, the colour stays a single solid token,
   so they sit inside a no-gradient brand system.

     .md-wave        — a drifting wave edge; put it at the boundary of two bands
     .md-wave.flip   — points upward (use at the TOP edge of a section)
     .md-wave.still  — shape only, no drift (safe anywhere, zero cost)

   Tune: --wave-c (fill, MUST match the section the wave belongs to)
         --wave-h (height, def 64px)   --wave-dur (drift, def 18s)
         --wave-o2 (back layer opacity, def .45)
         --wv (the mask svg, if you want a different profile)

   How it works: the fill is a plain background-color and the wave is a MASK, so
   one data-URI serves every colour on the site — no per-colour SVG. Two layers
   drift at different speeds and opposite directions for depth. Motion is a
   `translate` longhand on a 200%-wide strip (GPU-composited), not an animated
   mask-position, which would repaint every frame.
   Lite/off/reduced-motion: the wave holds still, shape intact.
   ============================================================ */

.md-wave{
  --wv: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1200' height='120' preserveAspectRatio='none' viewBox='0 0 1200 120'%3E%3Cpath d='M0 120V46c103 24 204 36 303 36 149 0 246-38 394-38 99 0 200 12 303 36v40z' fill='%23000'/%3E%3C/svg%3E");
  position:relative; display:block; width:100%;
  height:var(--wave-h,64px);
  overflow:hidden; pointer-events:none;
  line-height:0;
}
.md-wave::before,
.md-wave::after{
  content:""; position:absolute; left:0; top:0;
  width:200%; height:100%;
  background:var(--wave-c,#fff);
  -webkit-mask:var(--wv) repeat-x left bottom / 50% 100%;
          mask:var(--wv) repeat-x left bottom / 50% 100%;
}
.md-wave::after{
  opacity:var(--wave-o2,.45);
  /* mirrored + slower, so the two crests never lock into one shape */
  transform:scaleX(-1);
}
@media (prefers-reduced-motion: no-preference){
  html[data-motion="full"] .md-wave::before,
  html[data-motion="lite"] .md-wave::before{
    animation: md-wave-run var(--wave-dur,18s) linear infinite;
  }
  html[data-motion="full"] .md-wave::after{
    animation: md-wave-run calc(var(--wave-dur,18s) * 1.6) linear infinite reverse;
  }
}
/* translate longhand so ::after's scaleX(-1) mirror is never cancelled */
@keyframes md-wave-run{
  from{ translate:0 0 }
  to  { translate:-50% 0 }
}

.md-wave.flip{ rotate:180deg; }
.md-wave.still::before,
.md-wave.still::after{ animation:none !important; }

/* ---- governor / reduced-motion ------------------------------------------ */
:root[data-motion="off"] .md-wave::before,
:root[data-motion="off"] .md-wave::after,
:root[data-motion="lite"] .md-wave::after{ animation:none; }
@media (prefers-reduced-motion: reduce){
  .md-wave::before,.md-wave::after{ animation:none; }
}

/* premium-motion : typography motion pack  (yewho-web-effects 2.3, Phase 9)
   Pairs with wordfx.js (splits text, adds .in on view). PLAIN TEXT only inside
   these elements — the splitter reads textContent.

     .md-words      — word-by-word cascade: each word rises out of a soft blur
                      (the keynote reveal). Tune: --wf-step (def 70ms)
     .md-chars      — per-character rise (tighter, for short accent lines)
     .md-outline    — headline starts as a hollow outline, fills with ink
                      left→right on view. Tune: --wf-fill (ink), --wf-line
     .md-letterwave — perpetual GENTLE letter bob on a short accent word
                      (armed only after the entrance settles; full tier only)

   All hidden states live under html.js — no-JS users always see text.
   ============================================================ */

/* ---- shared: split spans ---- */
.md-words .w, .md-chars .c, .md-letterwave .c{ display:inline-block; will-change:auto; }
.md-words .w{ margin-right:.28em; }
.md-words .w:last-child{ margin-right:0; }

/* ---- word cascade ---- */
html.js .md-words .w{
  opacity:0; transform:translateY(14px); filter:blur(6px);
  transition: opacity .55s var(--md-ease,cubic-bezier(.22,.61,.2,1)),
              transform .55s var(--md-ease,cubic-bezier(.22,.61,.2,1)),
              filter .55s var(--md-ease,cubic-bezier(.22,.61,.2,1));
  transition-delay: calc(var(--i,0) * var(--wf-step,70ms));
}
html.js .md-words.in .w{ opacity:1; transform:none; filter:blur(0); }
:root[data-motion="lite"] .md-words .w{ filter:none; }  /* skip blur cost on lite */

/* ---- char rise ---- */
html.js .md-chars .c{
  opacity:0; transform:translateY(.55em);
  transition: opacity .45s var(--md-ease,cubic-bezier(.22,.61,.2,1)),
              transform .45s var(--md-ease,cubic-bezier(.22,.61,.2,1));
  transition-delay: calc(var(--i,0) * 28ms);
}
html.js .md-chars.in .c{ opacity:1; transform:none; }

/* ---- outline → ink fill ---- */
.md-outline{
  color:transparent;
  -webkit-text-stroke:1.5px var(--wf-line,#98A1AE);
  background:linear-gradient(90deg, var(--wf-fill,#111823) 50%, transparent 50%);
  background-size:200% 100%; background-position:100% 0;
  -webkit-background-clip:text; background-clip:text;
}
html.js .md-outline.in{
  transition: background-position 1.1s var(--md-ease,cubic-bezier(.22,.61,.2,1)) .15s;
  background-position:0 0;
}

/* ---- perpetual letter wave (accent word) ---- */
:root[data-motion="full"] .md-letterwave.in .c{
  animation: md-lw var(--lw-dur,3.2s) ease-in-out infinite;
  animation-delay: calc(var(--i,0) * .12s + .8s);
}
@keyframes md-lw{
  0%,72%,100%{ transform:translateY(0); }
  84%{ transform:translateY(calc(-1 * var(--lw-amp,.14em))); }
}

/* ---- reduced motion: everything lands instantly, no wave ---- */
@media (prefers-reduced-motion: reduce){
  html.js .md-words .w, html.js .md-chars .c{
    opacity:1; transform:none; filter:none; transition:none;
  }
  .md-outline{ color:var(--wf-fill,#111823); -webkit-text-stroke:0;
    background:none; -webkit-background-clip:border-box; background-clip:border-box; }
  .md-letterwave .c{ animation:none !important; }
}
:root[data-motion="off"] .md-words .w, :root[data-motion="off"] .md-chars .c{
  opacity:1; transform:none; filter:none; transition:none;
}
