/* ============================================================================
 * MOTION LAYER — single source of truth for movement on adolfjon.uz
 * ----------------------------------------------------------------------------
 * The contract: ONE duration scale, ONE easing set, ONE travel distance, used
 * by every page. A product card rising on /store and a table row rising on
 * /store/orders run the identical numbers, so walking between pages never
 * feels like walking into a different product.
 *
 * Durations are inherited from what /store already shipped (--tf .14s /
 * --tm .24s in store.php) so the existing shop does not suddenly feel slower
 * or faster than everything built around it.
 *
 * Nothing here moves unless <html> carries `.mo` — set by a one-line inline
 * script in header.php. Without JS the hiding rules never apply and every
 * page renders fully visible. See also the reduced-motion kill switch at the
 * bottom, which is the LAST rule in the file so it wins unconditionally.
 * ========================================================================== */

:root{
  /* --- duration scale ----------------------------------------------------
   * 1 = touch response, colour swaps      2 = hover lift, borders
   * 3 = enter / reveal / accordion        4 = hero beats, sequences          */
  --mo-1:.14s;
  --mo-2:.24s;
  --mo-3:.38s;
  --mo-4:.55s;

  /* --- easing ------------------------------------------------------------
   * out: everything entering or settling. io: things that travel both ways
   * (carousels, drawers). pop: restrained overshoot — 1.35, not 1.7; a
   * confirmation should feel solid, not rubbery.                            */
  --mo-out:cubic-bezier(.22,1,.36,1);
  --mo-io:cubic-bezier(.4,0,.2,1);
  --mo-pop:cubic-bezier(.34,1.35,.5,1);

  /* --- travel ------------------------------------------------------------ */
  --mo-rise:14px;   /* how far an entering element climbs                    */
  --mo-lift:-4px;   /* how far a hovered card floats                         */
  --mo-press:.975;  /* pressed scale — matches .ns-card:active on /store     */
  --mo-step:55ms;   /* stagger gap — matches store.php's col*.055s           */
}

/* ============================================================================
 * 1. ENTER — the reveal primitives
 * Elements start hidden ONLY under html.mo, so a JS failure can never leave
 * the page blank. motion.js adds .mo-in; it also has a failsafe that reveals
 * everything unconditionally after 1.6s.
 * ========================================================================== */

html.mo .mo-rise,
html.mo .mo-fade,
html.mo .mo-scale,
html.mo .mo-slide-l,
html.mo .mo-slide-r{
  opacity:0;
  transition:opacity var(--mo-3) var(--mo-out),transform var(--mo-3) var(--mo-out);
  transition-delay:var(--mo-d,0ms);
  will-change:opacity,transform;
}
html.mo .mo-rise   {transform:translateY(var(--mo-rise))}
html.mo .mo-scale  {transform:scale(.96)}
html.mo .mo-slide-l{transform:translateX(-18px)}
html.mo .mo-slide-r{transform:translateX(18px)}

html.mo .mo-rise.mo-in,
html.mo .mo-fade.mo-in,
html.mo .mo-scale.mo-in,
html.mo .mo-slide-l.mo-in,
html.mo .mo-slide-r.mo-in{
  opacity:1;
  transform:none;
  will-change:auto;
}

/* Stagger. motion.js writes --mo-i (the child's index) on each child of a
 * .mo-stagger container; the delay is computed here so the rhythm is defined
 * in one place instead of being hardcoded per page. Capped at 10 steps
 * (550ms) — past that a visitor is waiting, not being delighted. */
html.mo .mo-stagger > *{--mo-d:calc(min(var(--mo-i,0),10) * var(--mo-step))}

/* Page enter. Pure CSS animation, not a JS class — it ends on a visible
 * state, so it cannot strand content.
 *
 * Opacity only, and that is load-bearing: a transform animation on <main>
 * makes <main> the containing block for every position:fixed descendant,
 * because a filling transform animation counts as "has a transform". On
 * landing.php that anchored #liteModal (the LITE download popup) to a
 * ~9500px-tall <main> instead of the viewport, so its centred dialog sat
 * ~4400px below the fold while its inset:0 backdrop blurred the page —
 * a tap on "Bepul sinab ko'ring" produced blurred glass and no dialog.
 * The same trap applies to the landing lightbox and the back-to-top button.
 * Machines with prefers-reduced-motion never saw it: the kill switch at the
 * bottom of this file strips the animation, which is why it read as a
 * phones-only bug. Keep this keyframe transform-free. */
html.mo main{animation:moPageIn var(--mo-3) var(--mo-out) both}
@keyframes moPageIn{from{opacity:0}to{opacity:1}}

/* --- [data-reveal-group] ------------------------------------------------
 * /dashboard, /wallet, /token, /store/cart, /store/product and
 * /profile_edit all carry this attribute on a container, expecting its
 * children to cascade in. The rules that made it work only ever existed in
 * template/assets/css/header.css — a stale copy of the stylesheet that the
 * site does not load — so on production the attribute has been inert.
 *
 * Supporting it here revives all six pages without touching their markup,
 * and binds them to the same tokens as everything else so they cascade at
 * the site's speed rather than the old copy's .5s. The container itself is
 * never hidden; only its children are, so a group that the observer never
 * reaches still shows its wrapper.
 *
 * Two observers can mark these groups: header.php has had its own since
 * before this file existed (it adds `.rv-in`), and motion.js adds `.mo-in`.
 * Both are honoured. Keying on only one would mean a group is hidden until
 * that particular observer happens to fire, and only motion.js carries the
 * 1.6s failsafe — so accepting either is what keeps content from being
 * stranded if one of them is ever removed.                                */
html.mo [data-reveal-group] > *{
  opacity:0;transform:translateY(var(--mo-rise));
  transition:opacity var(--mo-3) var(--mo-out),transform var(--mo-3) var(--mo-out);
}
html.mo [data-reveal-group].mo-in > *,
html.mo [data-reveal-group].rv-in > *{opacity:1;transform:none}
html.mo [data-reveal-group].mo-in > *:nth-child(1),
html.mo [data-reveal-group].rv-in > *:nth-child(1){transition-delay:0ms}
html.mo [data-reveal-group].mo-in > *:nth-child(2),
html.mo [data-reveal-group].rv-in > *:nth-child(2){transition-delay:calc(1 * var(--mo-step))}
html.mo [data-reveal-group].mo-in > *:nth-child(3),
html.mo [data-reveal-group].rv-in > *:nth-child(3){transition-delay:calc(2 * var(--mo-step))}
html.mo [data-reveal-group].mo-in > *:nth-child(4),
html.mo [data-reveal-group].rv-in > *:nth-child(4){transition-delay:calc(3 * var(--mo-step))}
html.mo [data-reveal-group].mo-in > *:nth-child(5),
html.mo [data-reveal-group].rv-in > *:nth-child(5){transition-delay:calc(4 * var(--mo-step))}
html.mo [data-reveal-group].mo-in > *:nth-child(6),
html.mo [data-reveal-group].rv-in > *:nth-child(6){transition-delay:calc(5 * var(--mo-step))}
html.mo [data-reveal-group].mo-in > *:nth-child(n+7),
html.mo [data-reveal-group].rv-in > *:nth-child(n+7){transition-delay:calc(6 * var(--mo-step))}

/* ============================================================================
 * 2. INTERACT — press, hover, focus
 * Press feedback is unconditional (touch devices get it too). Hover effects
 * are gated behind a real pointer so they never stick on a phone after a tap.
 * ========================================================================== */

.mo-press{transition:transform var(--mo-1) var(--mo-out)}
.mo-press:active{transform:scale(var(--mo-press))}

.mo-lift{transition:transform var(--mo-2) var(--mo-out),border-color var(--mo-2) var(--mo-out),box-shadow var(--mo-2) var(--mo-out)}

/* Shine sweep — the diagonal gloss /store already uses on product cards.
 * Host needs position:relative and overflow:hidden. */
.mo-shine{position:relative;overflow:hidden}
.mo-shine::after{
  content:'';position:absolute;inset:0;z-index:4;pointer-events:none;
  background:linear-gradient(108deg,transparent 38%,rgba(255,255,255,.055) 50%,transparent 62%);
  transform:translateX(-120%);
  border-radius:inherit;
}

@media(hover:hover) and (pointer:fine){
  .mo-lift:hover{
    transform:translateY(var(--mo-lift));
    border-color:var(--bdh)!important;
    box-shadow:0 12px 32px rgba(0,0,0,.42);
  }
  .mo-shine:hover::after{transform:translateX(120%);transition:transform var(--mo-4) var(--mo-io)}
  .mo-zoom img{transition:transform var(--mo-4) var(--mo-out)}
  .mo-zoom:hover img{transform:scale(1.05)}
  .mo-nudge{transition:transform var(--mo-2) var(--mo-out)}
  .mo-nudge:hover{transform:scale(1.08)}
}

/* Focus ring. Keyboard users get the same quality of feedback as the mouse. */
.mo-press:focus-visible,.mo-lift:focus-visible{
  outline:2px solid var(--ac);outline-offset:2px;
  transition:outline-offset var(--mo-1) var(--mo-out);
}

/* ============================================================================
 * 3. STATE — things the page says back to you
 * ========================================================================== */

/* Breathing. For anything genuinely pending — an unpaid order, a job still
 * running. Deliberately slow and low-contrast: it must read as "alive", not
 * as "alarm". */
.mo-pulse{animation:moPulse 2.6s ease-in-out infinite}
@keyframes moPulse{0%,100%{opacity:1}50%{opacity:.55}}

/* Attention dot. One ring, expanding and fading. */
.mo-ping{position:relative}
.mo-ping::after{
  content:'';position:absolute;inset:0;border-radius:inherit;
  border:1px solid currentColor;
  animation:moPing 2s var(--mo-out) infinite;
  pointer-events:none;
}
@keyframes moPing{0%{transform:scale(1);opacity:.7}70%,100%{transform:scale(1.9);opacity:0}}

/* Rejection. 6px, three oscillations, done in 300ms. */
.mo-shake{animation:moShake .3s var(--mo-io) both}
@keyframes moShake{
  0%,100%{transform:translateX(0)}
  20%{transform:translateX(-6px)}
  40%{transform:translateX(5px)}
  60%{transform:translateX(-3px)}
  80%{transform:translateX(2px)}
}

/* One-shot confirmation flashes. Colour washes in and drains away. */
.mo-flash-ok{animation:moFlashOk 1.1s var(--mo-out) both}
@keyframes moFlashOk{0%{background-color:rgba(53,188,104,.22)}100%{background-color:transparent}}
.mo-flash-er{animation:moFlashEr 1.1s var(--mo-out) both}
@keyframes moFlashEr{0%{background-color:rgba(217,80,80,.22)}100%{background-color:transparent}}

/* Pop. For a counter that just changed, a badge that just incremented. */
.mo-pop{animation:moPop var(--mo-3) var(--mo-pop) both}
@keyframes moPop{0%{transform:scale(1)}40%{transform:scale(1.28)}100%{transform:scale(1)}}

/* Spinner and skeleton — lifted verbatim from store.php's vocabulary so a
 * loading state looks the same wherever it appears. */
.mo-spin{animation:moSpin .8s linear infinite}
@keyframes moSpin{to{transform:rotate(360deg)}}

.mo-skel{
  background:linear-gradient(90deg,var(--sf) 25%,rgba(255,255,255,.07) 50%,var(--sf) 75%)!important;
  background-size:200% 100%!important;
  animation:moSkel 1.5s ease-in-out infinite!important;
}
@keyframes moSkel{0%{background-position:200% 0}100%{background-position:-200% 0}}

/* SVG line drawing — the checkmark on a successful token activation, the
 * connector between numbered steps. Host sets stroke-dasharray/offset to the
 * path length; motion.js measures it. */
.mo-draw{stroke-dasharray:var(--mo-len,100);stroke-dashoffset:var(--mo-len,100)}
.mo-draw.mo-in{animation:moDraw var(--mo-4) var(--mo-out) forwards}
@keyframes moDraw{to{stroke-dashoffset:0}}

/* Height accordion. Set --mo-h to the measured scrollHeight before opening. */
.mo-collapse{overflow:hidden;height:0;opacity:0;transition:height var(--mo-3) var(--mo-io),opacity var(--mo-2) var(--mo-out)}
.mo-collapse.mo-open{height:var(--mo-h,auto);opacity:1}

/* Fly-to-cart ghost. Positioned and animated entirely from motion.js. */
.mo-ghost{
  position:fixed;z-index:20050;pointer-events:none;border-radius:10px;
  object-fit:cover;box-shadow:0 12px 32px rgba(0,0,0,.5);
  transition:transform var(--mo-4) var(--mo-io),opacity var(--mo-4) var(--mo-io);
}

/* Numbers that change. tabular-nums keeps the column from twitching while a
 * count-up runs — without it every digit change reflows the row. */
.mo-num{font-variant-numeric:tabular-nums}

/* ============================================================================
 * 4. MOBILE BUDGET
 * Phones get the same vocabulary at a shorter reach. Long travel and big
 * shadows are what drop frames on budget Android, not the transitions.
 * ========================================================================== */
@media(max-width:768px){
  :root{--mo-rise:10px;--mo-step:40ms;--mo-3:.32s;--mo-4:.45s}
  html.mo .mo-stagger > *{--mo-d:calc(min(var(--mo-i,0),8) * var(--mo-step))}
}

/* ============================================================================
 * 5. KILL SWITCH — last rule in the file, wins over everything above.
 * A visitor who asks the OS for reduced motion gets a completely static site,
 * not a slightly calmer one. Content still renders: the reveal primitives are
 * forced visible rather than left at opacity 0.
 * ========================================================================== */
@media(prefers-reduced-motion:reduce){
  html.mo .mo-rise,html.mo .mo-fade,html.mo .mo-scale,
  html.mo .mo-slide-l,html.mo .mo-slide-r,
  html.mo [data-reveal-group] > *{
    opacity:1!important;transform:none!important;transition:none!important;
  }
  html.mo main{animation:none!important}
  .mo-press,.mo-lift,.mo-shine::after,.mo-zoom img,.mo-nudge,
  .mo-collapse,.mo-ghost,.mo-num{transition:none!important}
  .mo-press:active,.mo-lift:hover,.mo-nudge:hover,.mo-zoom:hover img{transform:none!important}
  .mo-shine::after{display:none}
  .mo-pulse,.mo-ping::after,.mo-shake,.mo-flash-ok,.mo-flash-er,
  .mo-pop,.mo-spin,.mo-skel,.mo-draw.mo-in{animation:none!important}
  .mo-draw{stroke-dashoffset:0}
  .mo-collapse.mo-open{height:auto}
}
