/* ═══ GradeOS HW-MOTION v1 — hardware-era animation suite ═══════════════════
   Small, class-driven, reusable. Pairs with gradeos-hw-motion.js (observers,
   odometers, stagger). Brand law: transform/opacity only, one lime accent,
   prefers-reduced-motion → fully static. Include on any page:
     <link rel="stylesheet" href="/gradeos-hw-motion.css?v=1">
     <script defer src="/gradeos-hw-motion.js?v=1"></script>

   USAGE (one-liners)
   · Instrument entrance:   <div data-gx-reveal>…</div>
     variants: data-gx-reveal="left|right|scale"; container stagger:
     <section data-gx-stagger="70">…children with data-gx-reveal…</section>
     explicit delay: data-gx-delay="240" (ms)
   · Hairline sweep rule:   <div class="gx-sweep"></div>
   · Telemetry odometer:    <span data-gx-odo="12480" data-gx-odo-dp="0"
                              data-gx-odo-prefix="$" data-gx-odo-suffix="+"></span>
   · Carriage progress bar: <div class="gx-progress" data-value="72"><i></i></div>
   · Seal stamp (success):  el.classList.add('gx-stamp') once in markup,
                            then GXMotion.stamp(el) at the success moment
   · Scan-line card hover:  add class .gx-scancard (needs position context)
   · Live beacon:           <i class="gx-beacon"></i> (.ok / .warn variants)  */

/* ── instrument entrances ── */
[data-gx-reveal]{opacity:0;transform:translateY(16px);
  transition:opacity .65s cubic-bezier(.16,1,.3,1),transform .65s cubic-bezier(.16,1,.3,1)}
[data-gx-reveal="left"]{transform:translateX(-20px)}
[data-gx-reveal="right"]{transform:translateX(20px)}
[data-gx-reveal="scale"]{transform:scale(.955)}
[data-gx-reveal].gx-in{opacity:1;transform:none}

/* ── hairline sweep divider — a faint rule with a traveling lime glint.
   The glint is width:18% of the rule and travels own-width-relative, so it
   spans any container. gradeos-hw-motion.js toggles .gx-live via
   IntersectionObserver so it never animates offscreen. ── */
.gx-sweep{position:relative;height:1px;margin:28px 0;overflow:hidden;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.09) 14%,rgba(255,255,255,.09) 86%,transparent)}
.gx-sweep::after{content:"";position:absolute;top:0;bottom:0;left:0;width:18%;opacity:0;
  transform:translateX(-120%);
  background:linear-gradient(90deg,transparent,rgba(200,255,0,.7) 50%,transparent)}
/* AMBIENT LAW (2026-07-16): the glint crosses in ~3.9s then the rule RESTS
   dark for ~3.1s — a periodic survey pass, not a perpetual marquee. */
.gx-sweep.gx-live::after{animation:gxSweep 7s cubic-bezier(.45,.05,.55,.95) infinite}
@keyframes gxSweep{
  0%{transform:translateX(-120%);opacity:0}
  6%{opacity:1}
  50%{opacity:1}
  56%{transform:translateX(680%);opacity:0}
  100%{transform:translateX(680%);opacity:0}}

/* ── telemetry odometer — JS rolls the number; CSS locks the register.
   On completion the JS adds .gx-odo-lock: a tiny physical "settle" tick,
   like a gauge needle coming to rest. ── */
[data-gx-odo]{font-variant-numeric:tabular-nums;font-feature-settings:"tnum"}
[data-gx-odo].gx-odo-lock{display:inline-block;animation:gxOdoLock .45s cubic-bezier(.16,1,.3,1)}
@keyframes gxOdoLock{0%{transform:translateY(0)}35%{transform:translateY(-2px) scale(1.035)}100%{transform:none}}

/* ── live beacon — a lime point with an expanding survey ring; the LIVE
   telemetry mark for bench/ops surfaces. <i class="gx-beacon"></i>
   variants: .ok (live green) .warn (amber) — still one accent per surface. ── */
.gx-beacon{position:relative;display:inline-block;width:7px;height:7px;flex:none;
  border-radius:50%;background:#c8ff00;box-shadow:0 0 10px rgba(200,255,0,.7)}
.gx-beacon::after{content:"";position:absolute;inset:-4px;border-radius:50%;
  border:1px solid rgba(200,255,0,.8);animation:gxBeacon 2.4s cubic-bezier(.16,1,.3,1) infinite}
.gx-beacon.ok{background:#6dde8f;box-shadow:0 0 10px rgba(109,222,143,.6)}
.gx-beacon.ok::after{border-color:rgba(109,222,143,.7)}
.gx-beacon.warn{background:#ffb547;box-shadow:0 0 10px rgba(255,181,71,.6)}
.gx-beacon.warn::after{border-color:rgba(255,181,71,.7)}
@keyframes gxBeacon{0%{transform:scale(.5);opacity:.9}70%{transform:scale(2.1);opacity:0}100%{transform:scale(2.1);opacity:0}}

/* ── carriage-glide progress — the fill head carries a lime carriage glint ── */
.gx-progress{position:relative;height:4px;border-radius:2px;background:rgba(255,255,255,.07)}
.gx-progress i{position:absolute;left:0;top:0;bottom:0;width:0;border-radius:2px;
  background:linear-gradient(90deg,rgba(200,255,0,.22),#c8ff00);
  transition:width 1.25s cubic-bezier(.16,1,.3,1)}
.gx-progress i::after{content:"";position:absolute;right:-1px;top:-3px;width:2px;height:10px;
  background:#c8ff00;box-shadow:0 0 10px rgba(200,255,0,.8),0 0 24px rgba(200,255,0,.35)}

/* ── seal stamp — the passport moment: press in, settle, ring dissipates ── */
.gx-stamp{position:relative}
.gx-stamp.gx-stamped{animation:gxStampIn .6s cubic-bezier(.16,1,.3,1)}
.gx-stamp.gx-stamped::after{content:"";position:absolute;inset:-7px;border-radius:inherit;
  border:1px solid rgba(200,255,0,.75);pointer-events:none;
  animation:gxStampRing .95s cubic-bezier(.16,1,.3,1) forwards}
@keyframes gxStampIn{0%{transform:scale(1.35);opacity:0}55%{transform:scale(.965);opacity:1}100%{transform:scale(1)}}
@keyframes gxStampRing{0%{transform:scale(.72);opacity:0}22%{opacity:.95}100%{transform:scale(1.45);opacity:0}}

/* ── scan-line card hover — one hairline reads the card top→bottom.
   Set --gx-scan-h to the card's height if taller than the 260px default. ── */
.gx-scancard{position:relative;overflow:hidden}
.gx-scancard::before{content:"";position:absolute;left:0;right:0;top:0;height:1px;opacity:0;
  background:linear-gradient(90deg,transparent,rgba(200,255,0,.8),transparent);pointer-events:none}
.gx-scancard:hover::before{animation:gxCardScan .85s cubic-bezier(.45,.05,.55,.95)}
@keyframes gxCardScan{
  0%{opacity:0;transform:translateY(0)}
  12%{opacity:1}
  88%{opacity:1}
  100%{opacity:0;transform:translateY(var(--gx-scan-h,260px))}}
.gx-scancard:hover{border-color:rgba(200,255,0,.28)}

/* ── reduced motion: everything lands in its final state, no movement ── */
@media (prefers-reduced-motion:reduce){
  [data-gx-reveal]{opacity:1;transform:none;transition:none}
  .gx-sweep.gx-live::after,.gx-stamp.gx-stamped,.gx-stamp.gx-stamped::after,
  .gx-scancard:hover::before,[data-gx-odo].gx-odo-lock,.gx-beacon::after{animation:none}
  .gx-progress i{transition:none}
}
