/* ==========================================================================
   Zenix Academy — motion
   Everything here animates transform/opacity only, so it stays on the
   compositor. Nothing animates layout properties.
   The reduced-motion block at the bottom is the single kill-switch.
   ========================================================================== */

/* ------------------------------------------------------------- keyframes -- */
@keyframes reveal-in {
  from { opacity: 0; transform: translateY(22px); }
  to   { opacity: 1; transform: none; }
}
@keyframes rise-in {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}
@keyframes fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes spin-slow  { to { transform: rotate(360deg); } }
@keyframes spin-slower{ to { transform: rotate(-360deg); } }
@keyframes float-y {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-10px); }
}
@keyframes breathe {
  0%, 100% { transform: scale(1);    opacity: .85; }
  50%      { transform: scale(1.06); opacity: 1; }
}
@keyframes sheen-slide {
  0%   { background-position:   0% 50%; }
  100% { background-position: 200% 50%; }
}
@keyframes drift-blob {
  0%, 100% { transform: translate3d(0, 0, 0) rotate(0deg); }
  50%      { transform: translate3d(-2.5%, 2%, 0) rotate(7deg); }
}
/* The pulse that answers the header's «حمّل التطبيق»: the reader is thrown back
   to the top of the page, so something has to say "these two, here". */
@keyframes dl-ring {
  0%   { opacity: 0;   transform: scale(1); }
  18%  { opacity: 1;   transform: scale(1); }
  100% { opacity: 0;   transform: scale(1.09); }
}

/* ------------------------------------------- scroll reveal (two engines) -- */
/* 1. Native scroll-driven timeline. Runs off the main thread and needs no JS,
      so the content is never hidden behind a script that failed to load. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .reveal {
      animation: reveal-in linear both;
      animation-timeline: view();
      animation-range: entry 2% cover 24%;
    }
    .reveal--stagger > * {
      animation: reveal-in linear both;
      animation-timeline: view();
      animation-range: entry 2% cover 30%;
    }
  }

  /* Below 900px .rail turns into a horizontal scroll container. Setting
     overflow-x also makes overflow-y compute to auto, so .rail becomes the
     resolved scrollport for view() — a block-axis timeline that can never
     advance, leaving the cards stuck at opacity 0. Opt its children out. */
  @media (prefers-reduced-motion: no-preference) and (max-width: 900px) {
    .rail.reveal--stagger > * {
      animation-name: none;
      animation-timeline: none;
      opacity: 1;
      transform: none;
    }
  }
}

/* 2. IntersectionObserver fallback. The hidden state is gated on `.js` so that
      with scripting off the content simply renders, fully visible. */
@supports not (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .js .reveal,
    .js .reveal--stagger > * {
      opacity: 0;
      transform: translateY(22px);
      transition: opacity .7s var(--ease-out), transform .7s var(--ease-out);
    }
    .js .reveal.is-in,
    .js .reveal--stagger.is-in > * { opacity: 1; transform: none; }

    .js .reveal--stagger.is-in > *:nth-child(1) { transition-delay: 0ms; }
    .js .reveal--stagger.is-in > *:nth-child(2) { transition-delay: 80ms; }
    .js .reveal--stagger.is-in > *:nth-child(3) { transition-delay: 160ms; }
    .js .reveal--stagger.is-in > *:nth-child(4) { transition-delay: 240ms; }
    .js .reveal--stagger.is-in > *:nth-child(5) { transition-delay: 320ms; }
    .js .reveal--stagger.is-in > *:nth-child(6) { transition-delay: 400ms; }
  }
}

/* --------------------------------------------------------- hero entrance -- */
/* Above the fold, so it plays on load rather than on scroll. */
@media (prefers-reduced-motion: no-preference) {
  .enter { animation: rise-in .85s var(--ease-out) both; }
  .d1 { animation-delay: .05s; }
  .d2 { animation-delay: .15s; }
  .d3 { animation-delay: .25s; }
  .d4 { animation-delay: .35s; }
  .d5 { animation-delay: .45s; }
  .d6 { animation-delay: .55s; }

  .hero__title .sheen { animation: sheen-slide 9s linear infinite; }
  .hero__curve        { animation: drift-blob 26s var(--ease) infinite; }
  /* .mock__phone deliberately does NOT float. An infinite transform animation
     composites the element and pins its raster scale, which softens the
     screenshot inside it. The glow is a blurred gradient with no detail to
     lose, so that one may still breathe. */
  .mock__glow         { animation: breathe 9s var(--ease) infinite; }

  .orbit__glow  { animation: breathe 7s var(--ease) infinite; }
  .orbit__chip  { animation: float-y 6s var(--ease) infinite; }
  .orbit__chip--b { animation-delay: -2s; }
  .orbit__chip--c { animation-delay: -4s; }

  .orbit__rings .ring-a { animation: spin-slow   38s linear infinite; transform-origin: 50% 50%; }
  .orbit__rings .ring-b { animation: spin-slower 56s linear infinite; transform-origin: 50% 50%; }
  .orbit__rings .ring-c { animation: spin-slow   82s linear infinite; transform-origin: 50% 50%; }
}

/* ------------------------------------------------- drawer open transition -- */
@media (prefers-reduced-motion: no-preference) {
  .drawer:not([hidden])         { animation: fade-in .22s var(--ease) both; }
  .drawer:not([hidden]) .drawer__link,
  .drawer:not([hidden]) .drawer__cta { animation: rise-in .45s var(--ease-out) both; }
  .drawer:not([hidden]) .drawer__list li:nth-child(1) .drawer__link { animation-delay: .04s; }
  .drawer:not([hidden]) .drawer__list li:nth-child(2) .drawer__link { animation-delay: .08s; }
  .drawer:not([hidden]) .drawer__list li:nth-child(3) .drawer__link { animation-delay: .12s; }
  .drawer:not([hidden]) .drawer__list li:nth-child(4) .drawer__link { animation-delay: .16s; }
  .drawer:not([hidden]) .drawer__list li:nth-child(5) .drawer__link { animation-delay: .20s; }
  .drawer:not([hidden]) .drawer__list li:nth-child(6) .drawer__link { animation-delay: .24s; }
  .drawer:not([hidden]) .drawer__cta { animation-delay: .28s; }
}

/* --------------------------------------------------------- magnetic hover -- */
/* --mx/--my are written by main.js with style.setProperty(), which CSSOM
   exempts from CSP style-src. They default to 0, so the rule is inert until
   (and unless) the script runs.

   The transform is applied only on :hover, and at .btn.magnetic specificity so
   it beats `.btn--primary:hover`'s own translateY — otherwise the lift would
   simply overwrite the magnet and nothing would move. The -2px keeps that
   lift. At rest no transform is declared here, so the `.enter` entrance
   animation owns the element uncontested. */
.magnetic { --mx: 0px; --my: 0px; }

.btn.magnetic:hover {
  transform: translate3d(var(--mx), calc(var(--my) - 2px), 0);
  transition-duration: 60ms;
}
.btn.magnetic:active { transform: translate3d(var(--mx), var(--my), 0); }

.dl__btn.magnetic:hover { transform: translate3d(var(--mx), calc(var(--my) - 2px), 0); transition-duration: 60ms; }
.dl__btn.magnetic:active { transform: translate3d(var(--mx), var(--my), 0); }
.dl__btn--primary:hover,
.dl__btn--ghost:hover { transform: translateY(-2px); }

/* ------------------------------------------------------- download pulse --- */
/* main.js adds .is-flash to the group and strips it again after ~2.4s. */
.is-flash .dl__btn::after { animation: dl-ring 1.15s var(--ease-out) 2 both; }

/* ============================================================ KILL-SWITCH == */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  html { scroll-behavior: auto; }
  .magnetic { transform: none !important; }
  /* Nothing may stay hidden when motion is off. */
  .reveal, .reveal--stagger > * { opacity: 1 !important; transform: none !important; }

  /* The download pulse still has to answer the header button, so it becomes a
     plain static ring for as long as main.js holds the class on. */
  .is-flash .dl__btn::after { opacity: 1; transform: none; }
}
