/* ====================================================================
   SIGNAL — motion layer (css/motion.css)
   Loaded on every page AFTER tokens/base/marketing(/portal).
   EVERY initial hidden state is scoped under html.motion — that class
   is added at parse time by js/motion.js ONLY when JS runs and the
   user does not prefer reduced motion. No JS → fully visible, static.
   Animated properties: transform / opacity / clip-path / filter only.
   Reveal animations use `backwards` fill (no `forwards`), so once they
   finish, elements return to their normal styles — hover transforms
   and theme transitions keep their native timing.
   ==================================================================== */

/* ------------------------------ keyframes -------------------------- */

@keyframes m-fade    { from { opacity: 0; } }
@keyframes m-fade-up { from { opacity: 0; transform: translate3d(0, 18px, 0); } }
@keyframes m-rise    { from { opacity: 0; transform: translate3d(0, 36px, 0); } }
@keyframes m-down    { from { opacity: 0; transform: translate3d(0, -16px, 0); } }
@keyframes m-left    { from { opacity: 0; transform: translate3d(-32px, 0, 0); } }
@keyframes m-right   { from { opacity: 0; transform: translate3d(32px, 0, 0); } }
@keyframes m-zoom    { from { opacity: 0; transform: scale(0.95); } }
/* HUD boot — card flashes bright and settles, corner brackets snap in */
@keyframes m-boot {
  0%   { opacity: 0; transform: scale(0.962); filter: brightness(1.5) saturate(0.7); }
  62%  { opacity: 1; filter: brightness(1.13) saturate(0.92); }
  100% { opacity: 1; transform: scale(1); filter: brightness(1) saturate(1); }
}
@keyframes m-clip  { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }
@keyframes m-type  { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }
@keyframes m-row   { from { opacity: 0; transform: translate3d(0, 9px, 0); } }
@keyframes m-frame { from { opacity: 0; transform: scale(1.018); } }
@keyframes m-pop-y { from { transform: scaleY(0); } }
@keyframes m-hero-img { from { transform: scale(1.045); } }
/* blur-text (reactbits) — focus pull; bounded one-shot filter */
@keyframes m-blur { from { opacity: 0; filter: blur(12px); transform: translate3d(0, 8px, 0); } }
/* receipt print-out — top-down paper feed (no-contract ad) */
@keyframes m-print { from { clip-path: inset(0 0 101% 0); } }
@keyframes m-sweep {
  from { transform: translateY(-100%); opacity: 1; }
  to   { transform: translateY(100%);  opacity: 1; }
}

/* --------------------- scroll reveals (data-rv) --------------------
   Markup opts in: data-rv="up|rise|down|left|right|fade|zoom|boot|clip|type"
   plus optional data-rv-d="1..8" for ≤480ms group staggers.            */

html.motion [data-rv] { opacity: 0; }
html.motion [data-rv].in-view { opacity: 1; }

html.motion [data-rv="fade"].in-view  { animation: m-fade    0.50s ease-out backwards; }
html.motion [data-rv="up"].in-view    { animation: m-fade-up 0.55s cubic-bezier(0.22, 0.61, 0.36, 1) backwards; }
html.motion [data-rv="rise"].in-view  { animation: m-rise    0.60s cubic-bezier(0.18, 0.74, 0.30, 1) backwards; }
html.motion [data-rv="down"].in-view  { animation: m-down    0.50s cubic-bezier(0.22, 0.61, 0.36, 1) backwards; }
html.motion [data-rv="left"].in-view  { animation: m-left    0.60s cubic-bezier(0.16, 0.84, 0.32, 1) backwards; }
html.motion [data-rv="right"].in-view { animation: m-right   0.60s cubic-bezier(0.16, 0.84, 0.32, 1) backwards; }
html.motion [data-rv="zoom"].in-view  { animation: m-zoom    0.50s cubic-bezier(0.30, 0.90, 0.40, 1) backwards; }
html.motion [data-rv="boot"].in-view  { animation: m-boot    0.55s cubic-bezier(0.25, 0.80, 0.35, 1) backwards; }

/* photo / panel wipe — clip, not opacity (the HUD scan feel) */
html.motion [data-rv="clip"]          { opacity: 1; clip-path: inset(0 100% 0 0); }
html.motion [data-rv="clip"].in-view  { clip-path: inset(0 0 0 0); animation: m-clip 0.65s cubic-bezier(0.60, 0.05, 0.25, 1) backwards; }

/* mono eyebrows type on — blocky steps() clip, once */
html.motion [data-rv="type"].in-view  { animation: m-type 0.55s steps(22, end) backwards; }

/* hour-rail ticks light left-to-right — one stepped wipe, 25 hours */
html.motion [data-rv="rail"]          { opacity: 1; clip-path: inset(0 100% 0 0); }
html.motion [data-rv="rail"].in-view  { clip-path: inset(0 0 0 0); animation: m-clip 0.95s steps(25, end) backwards; }

/* blur-text reveal — content resolves into focus */
html.motion [data-rv="blur"].in-view  { animation: m-blur 0.70s cubic-bezier(0.22, 0.61, 0.36, 1) backwards; }

/* receipt prints top-down — clip, not opacity; the negative end insets
   keep the perforated tear edges outside the final clip */
html.motion [data-rv="print"]          { opacity: 1; clip-path: inset(0 0 101% 0); }
html.motion [data-rv="print"].in-view  { clip-path: inset(-8px -8px -10px -8px); animation: m-print 0.9s cubic-bezier(0.22, 1, 0.36, 1) backwards; }

/* group staggers — 60ms ladder, max 0.48s inside any one group */
html.motion [data-rv-d="1"].in-view { animation-delay: 0.06s; }
html.motion [data-rv-d="2"].in-view { animation-delay: 0.12s; }
html.motion [data-rv-d="3"].in-view { animation-delay: 0.18s; }
html.motion [data-rv-d="4"].in-view { animation-delay: 0.24s; }
html.motion [data-rv-d="5"].in-view { animation-delay: 0.30s; }
html.motion [data-rv-d="6"].in-view { animation-delay: 0.36s; }
html.motion [data-rv-d="7"].in-view { animation-delay: 0.42s; }
html.motion [data-rv-d="8"].in-view { animation-delay: 0.48s; }

/* 8/8 ACTIVE meter bars pop up after their host reveals */
html.motion .v-count.in-view .bars i { transform-origin: 50% 100%; animation: m-pop-y 0.35s cubic-bezier(0.16, 0.84, 0.32, 1) backwards; }
html.motion .v-count.in-view .bars i:nth-child(2) { animation-delay: 0.16s; }
html.motion .v-count.in-view .bars i:nth-child(3) { animation-delay: 0.20s; }
html.motion .v-count.in-view .bars i:nth-child(4) { animation-delay: 0.24s; }
html.motion .v-count.in-view .bars i:nth-child(5) { animation-delay: 0.28s; }
html.motion .v-count.in-view .bars i:nth-child(6) { animation-delay: 0.32s; }
html.motion .v-count.in-view .bars i:nth-child(7) { animation-delay: 0.36s; }
html.motion .v-count.in-view .bars i:nth-child(8) { animation-delay: 0.40s; }

/* ------------------ home hero — load choreography ------------------
   First element lands at ~120ms; copy settled by ~900ms.              */

html.motion .hero .eyebrow    { animation: m-type    0.50s steps(24, end)                      0.12s backwards; }
html.motion .hero__title      { animation: m-rise    0.60s cubic-bezier(0.18, 0.74, 0.30, 1)   0.18s backwards; }
html.motion .hero__sub        { animation: m-fade-up 0.50s cubic-bezier(0.22, 0.61, 0.36, 1)   0.30s backwards; }
html.motion .hero__cta        { animation: m-fade-up 0.50s cubic-bezier(0.22, 0.61, 0.36, 1)   0.40s backwards; }
html.motion .hero__telemetry  { animation: m-fade    0.44s ease-out                            0.46s backwards; }
html.motion .hero__frame      { animation: m-frame   0.70s cubic-bezier(0.20, 0.70, 0.30, 1)   0.12s backwards; }
html.motion .hero__media img  { animation: m-hero-img 0.70s cubic-bezier(0.20, 0.60, 0.30, 1) both; }

/* one-time scanline sweep across the hero on load (then parked
   off-canvas; .hero has overflow:hidden) */
html.motion .hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  opacity: 0; /* invisible until the animation starts */
  background: linear-gradient(
    180deg,
    transparent 34%,
    rgba(255, 52, 52, 0.05) 46%,
    rgba(238, 241, 246, 0.13) 50%,
    rgba(255, 52, 52, 0.05) 54%,
    transparent 66%
  );
  animation: m-sweep 0.9s cubic-bezier(0.40, 0.00, 0.60, 1) 0.25s forwards;
}

/* marketing ticker eases in with the page */
html.motion .ticker { animation: m-fade 0.55s ease-out both; }

/* ---------------- ad landing pages — hero load choreography ---------
   The welcome beat: eyebrow types on, "Glad you're here," settles,
   the site name rises under it, then the pitch, tick-list ladder,
   phone line and telemetry rail. Form card boots via data-rv.
   Copy settled by ~1.1s; scanline sweep shared with the home hero.   */

/* the eyebrow fades in while js/lp-fx.js decrypts its characters
   (reactbits decrypted-text, rebuilt vanilla) — no clip wipe on top */
html.motion .lp-hero .eyebrow     { animation: m-fade    0.40s ease-out                          0.10s backwards; }
html.motion .lp-h1__hi            { animation: m-fade-up 0.50s cubic-bezier(0.22, 0.61, 0.36, 1) 0.20s backwards; }
html.motion .lp-h1__site          { animation: m-rise    0.60s cubic-bezier(0.18, 0.74, 0.30, 1) 0.30s backwards; }
html.motion .lp-hero__sub         { animation: m-fade-up 0.50s cubic-bezier(0.22, 0.61, 0.36, 1) 0.44s backwards; }
html.motion .lp-hero .tick-list li          { animation: m-row 0.40s cubic-bezier(0.22, 0.61, 0.36, 1) backwards; }
html.motion .lp-hero .tick-list li:nth-child(1) { animation-delay: 0.54s; }
html.motion .lp-hero .tick-list li:nth-child(2) { animation-delay: 0.60s; }
html.motion .lp-hero .tick-list li:nth-child(3) { animation-delay: 0.66s; }
html.motion .lp-hero .tick-list li:nth-child(4) { animation-delay: 0.72s; }
html.motion .lp-hero__phone       { animation: m-fade    0.45s ease-out                          0.80s backwards; }
html.motion .lp-hero .hero__telemetry { animation: m-fade 0.45s ease-out                         0.90s backwards; }

/* one-time scanline sweep, same beat as the home hero */
html.motion .lp-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(
    180deg,
    transparent 34%,
    rgba(255, 52, 52, 0.05) 46%,
    rgba(238, 241, 246, 0.13) 50%,
    rgba(255, 52, 52, 0.05) 54%,
    transparent 66%
  );
  animation: m-sweep 0.9s cubic-bezier(0.40, 0.00, 0.60, 1) 0.25s forwards;
}

/* mobile sticky action bar slides up once the hero has landed */
html.motion .lp-sticky { animation: m-rise 0.50s cubic-bezier(0.16, 0.84, 0.32, 1) 1.05s backwards; }

/* grid-scan — one quiet beam pass over a grid section the first time it
   enters view (js/lp-fx.js adds .fx-swept); parks off-canvas after */
html.motion .section.grid-bg { position: relative; overflow: hidden; }
html.motion .section.grid-bg::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(180deg,
    transparent 40%, var(--signal-dim) 50%, transparent 60%);
}
html.motion .section.grid-bg.fx-swept::before {
  animation: m-sweep 1.15s cubic-bezier(0.40, 0.00, 0.60, 1) forwards;
}

/* ------------- compact page heroes (about/pricing/contact) --------- */

html.motion .page-hero .eyebrow     { animation: m-type    0.50s steps(20, end)                    0.12s backwards; }
html.motion .page-hero h1           { animation: m-rise    0.55s cubic-bezier(0.18, 0.74, 0.30, 1) 0.20s backwards; }
/* opt-in blur-text treatment for a page hero (The House file) */
html.motion .page-hero h1.fx-blur   { animation-name: m-blur; animation-duration: 0.75s; }
html.motion .page-hero .lede        { animation: m-fade-up 0.50s cubic-bezier(0.22, 0.61, 0.36, 1) 0.32s backwards; }
html.motion .page-hero__meta        { animation: m-fade    0.45s ease-out                          0.44s backwards; }

/* ----------------------- login — card entrance --------------------- */

html.motion .login-pane .eyebrow    { animation: m-type    0.45s steps(16, end)                    0.10s backwards; }
html.motion .login-pane h1          { animation: m-fade-up 0.50s cubic-bezier(0.22, 0.61, 0.36, 1) 0.16s backwards; }
html.motion .login-pane .lede       { animation: m-fade    0.45s ease-out                          0.26s backwards; }
/* :not(.shake) keeps the error shake animation in charge after a miss */
html.motion .login-card:not(.shake) { animation: m-boot    0.55s cubic-bezier(0.25, 0.80, 0.35, 1) 0.30s backwards; }
html.motion .demo-chips             { animation: m-fade-up 0.45s cubic-bezier(0.22, 0.61, 0.36, 1) 0.46s backwards; }
html.motion .login-visual img       { animation: m-hero-img 0.70s cubic-bezier(0.20, 0.60, 0.30, 1) both; }
html.motion .login-visual__content  { animation: m-fade-up 0.55s cubic-bezier(0.22, 0.61, 0.36, 1) 0.34s backwards; }

/* ------------- portal/admin — restrained tooling touches -----------
   Greeting strip settles fast; KPI cards + list rows take a quick
   3–4 item stagger; charts/rings animate to value. Nothing showy.     */

html.motion .greet-zone .eyebrow { animation: m-type    0.45s steps(20, end)                    0.08s backwards; }
html.motion .greet-zone h1       { animation: m-fade-up 0.45s cubic-bezier(0.22, 0.61, 0.36, 1) 0.12s backwards; }
html.motion .greet-zone .sysline { animation: m-fade    0.40s ease-out                          0.26s backwards; }

/* list rows + KPI cards (containers observed by motion.js) */
html.motion :is(.feed, .sess-list, .tk-list, .qa-grid, .pgrid--kpi4, .pgrid--kpi6, .m-stagger) > * { opacity: 0; }
html.motion :is(.feed, .sess-list, .tk-list, .qa-grid, .pgrid--kpi4, .pgrid--kpi6, .m-stagger).in-view > * {
  opacity: 1;
  animation: m-row 0.40s cubic-bezier(0.22, 0.61, 0.36, 1) backwards;
}
html.motion :is(.feed, .sess-list, .tk-list, .qa-grid, .pgrid--kpi4, .pgrid--kpi6, .m-stagger).in-view > *:nth-child(2) { animation-delay: 0.07s; }
html.motion :is(.feed, .sess-list, .tk-list, .qa-grid, .pgrid--kpi4, .pgrid--kpi6, .m-stagger).in-view > *:nth-child(3) { animation-delay: 0.14s; }
html.motion :is(.feed, .sess-list, .tk-list, .qa-grid, .pgrid--kpi4, .pgrid--kpi6, .m-stagger).in-view > *:nth-child(4) { animation-delay: 0.21s; }
html.motion :is(.feed, .sess-list, .tk-list, .qa-grid, .pgrid--kpi4, .pgrid--kpi6, .m-stagger).in-view > *:nth-child(n+5) { animation-delay: 0.27s; }

/* chart bars grow to value — scaleY from the baseline, no layout */
html.motion .chart__bars .bar > i {
  transform-origin: 50% 100%;
  transform: scaleY(0);
  transition: transform 0.6s cubic-bezier(0.16, 0.84, 0.32, 1), background-color 0.15s ease;
  /* per-bar transition-delay set inline by motion.js (≤40ms steps) */
}
html.motion .chart__bars.in-view .bar > i { transform: scaleY(1); }

/* progress rings sweep 0 → value (motion.js zeroes --p, restores it
   when the ring enters view; dasharray transitions to the real arc) */
html.motion .ring__bar { transition: stroke-dasharray 0.9s cubic-bezier(0.25, 0.70, 0.30, 1) 0.15s; }

/* ----------------------- safety nets ------------------------------- */

/* If the OS flips to reduced motion after load (class already on
   <html>), force everything visible and static. */
@media (prefers-reduced-motion: reduce) {
  html.motion [data-rv],
  html.motion :is(.feed, .sess-list, .tk-list, .qa-grid, .pgrid--kpi4, .pgrid--kpi6, .m-stagger) > * {
    opacity: 1 !important;
    clip-path: none !important;
    animation: none !important;
  }
  html.motion .chart__bars .bar > i { transform: none !important; transition: none !important; }
  html.motion .ring__bar { transition: none !important; }
  html.motion .hero::before, html.motion .lp-hero::before,
  html.motion .section.grid-bg::before { display: none; }
  html.motion .hero .eyebrow, html.motion .hero__title, html.motion .hero__sub,
  html.motion .hero__cta, html.motion .hero__telemetry, html.motion .hero__frame,
  html.motion .hero__media img, html.motion .ticker,
  html.motion .lp-hero .eyebrow, html.motion .lp-h1__hi, html.motion .lp-h1__site,
  html.motion .lp-hero__sub, html.motion .lp-hero .tick-list li,
  html.motion .lp-hero__phone, html.motion .lp-sticky,
  html.motion .page-hero .eyebrow, html.motion .page-hero h1,
  html.motion .page-hero .lede, html.motion .page-hero__meta,
  html.motion .login-pane .eyebrow, html.motion .login-pane h1,
  html.motion .login-pane .lede, html.motion .login-card,
  html.motion .demo-chips, html.motion .login-visual img,
  html.motion .login-visual__content,
  html.motion .greet-zone .eyebrow, html.motion .greet-zone h1,
  html.motion .greet-zone .sysline,
  html.motion .v-count .bars i {
    animation: none !important;
  }
}

/* Print gets the same treatment — nothing waits on a scroll that will
   never happen. */
@media print {
  html.motion [data-rv],
  html.motion :is(.feed, .sess-list, .tk-list, .qa-grid, .pgrid--kpi4, .pgrid--kpi6, .m-stagger) > * {
    opacity: 1 !important;
    clip-path: none !important;
    animation: none !important;
  }
  html.motion .chart__bars .bar > i { transform: none !important; transition: none !important; }
  html.motion .ring__bar { transition: none !important; }
  html.motion .hero::before, html.motion .lp-hero::before,
  html.motion .section.grid-bg::before { display: none; }
  html.motion .hero .eyebrow, html.motion .hero__title, html.motion .hero__sub,
  html.motion .hero__cta, html.motion .hero__telemetry, html.motion .hero__frame,
  html.motion .hero__media img, html.motion .ticker,
  html.motion .lp-hero .eyebrow, html.motion .lp-h1__hi, html.motion .lp-h1__site,
  html.motion .lp-hero__sub, html.motion .lp-hero .tick-list li,
  html.motion .lp-hero__phone, html.motion .lp-sticky,
  html.motion .page-hero .eyebrow, html.motion .page-hero h1,
  html.motion .page-hero .lede, html.motion .page-hero__meta,
  html.motion .login-pane .eyebrow, html.motion .login-pane h1,
  html.motion .login-pane .lede, html.motion .login-card,
  html.motion .demo-chips, html.motion .login-visual img,
  html.motion .login-visual__content,
  html.motion .greet-zone .eyebrow, html.motion .greet-zone h1,
  html.motion .greet-zone .sysline,
  html.motion .v-count .bars i {
    animation: none !important;
  }
}
