/* Idle motion.
   The site already announces itself with entrance animations; this file adds the quiet
   half: a slow, low amplitude hum so a still screenshot feels considered and a page left
   open for a minute still breathes.

   Rules of the house, applied throughout:
   - every animation lives inside @media (prefers-reduced-motion: no-preference), so the
     global reduced-motion blanket in theme.css stays authoritative and never has to be fought;
   - transforms, opacity and background-position only, so nothing shifts the layout;
   - colours come from tokens in theme.css, never a raw hex;
   - nothing moves more than a handful of pixels and nothing pulses faster than ~4s.

   Keyframes are named with an idle- prefix so they cannot collide with the existing
   arrival, page-enter, hero-enter, dialog-enter, backdrop-enter, cart-bump, feedback-in,
   pick-reveal, answer-in, orbit-in, orbit-in-alt, stamp-in and discord-glint animations. */

@media (prefers-reduced-motion: no-preference) {

  /* Hero, ambient wash.
     The wide glow the hero already carries now swells and settles over 18s. Opacity only,
     so the existing gradient and its transform are left untouched. */
  .hero:before {
    animation: idle-ambient 18s ease-in-out infinite;
  }

  /* Hero, glow behind the mascot.
     A soft lime halo sitting under the hooded figure. It breathes on a 13s cycle, which
     never lines up with the 9s drift below, so the two never read as one movement.
     Painted at z-index -1, behind the artwork. */
  .hero:after {
    content: '';
    position: absolute;
    right: 2%;
    top: 12%;
    width: min(42vw, 440px);
    aspect-ratio: 1;
    z-index: -1;
    pointer-events: none;
    background: radial-gradient(circle, var(--lime-19) 0%, var(--lime-0d) 34%, transparent 66%);
    animation: idle-glow 13s ease-in-out infinite;
  }

  /* On narrow screens the hero stacks, so the halo follows the mascot down to the art
     instead of sitting behind the headline. */
  @media (max-width: 700px) {
    .hero:after {
      left: 50%;
      right: auto;
      top: auto;
      bottom: 4%;
      width: min(86vw, 360px);
      margin-left: calc(min(86vw, 360px) / -2);
    }
  }

  /* Hero, the mascot itself.
     Five pixels of travel over nine seconds, so it reads as breathing rather than floating. */
  .hero-art {
    animation: idle-float 9s ease-in-out infinite;
  }

  /* Header, the cropped mascot.
     Its eyes keep a faint light, barely changing over eight seconds. */
  .brand-crop img {
    filter: drop-shadow(0 0 3px var(--lime-19));
    animation: idle-eyes 8s ease-in-out infinite;
  }

  /* Discord banner, the icon.
     Four percent of scale and a low, neutral glow. The glyph is decoration; the sentence
     next to it never moves. */
  .discord-banner-icon {
    animation: idle-breathe 6s ease-in-out infinite;
  }

  /* Discord banner, one slow sheen.
     A narrow band of light crosses the ribbon every eighteen seconds. It is painted at
     z-index -1, beneath the copy and the artwork, so the message stays exactly as legible
     as it is at rest. */
  .join-discord-banner:after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 38%;
    z-index: -1;
    pointer-events: none;
    background: linear-gradient(100deg, transparent,
      color-mix(in srgb, var(--white) 9%, transparent) 45%,
      color-mix(in srgb, var(--white) 12%, transparent) 55%,
      transparent);
    animation: idle-sheen 18s ease-in-out infinite;
  }

  /* Stats, a shimmer across the tile.
     The light travels over the number; the number itself never moves. */
  .stat {
    position: relative;
    overflow: hidden;
  }

  .stat:after {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: linear-gradient(100deg, transparent 40%, var(--lime-12) 50%, transparent 60%);
    animation: idle-shimmer 13s ease-in-out infinite;
  }

  /* Stats, staggered so the grid does not blink in unison. */
  .stats .stat:nth-child(2):after { animation-delay: -3.25s; }
  .stats .stat:nth-child(3):after { animation-delay: -6.5s; }
  .stats .stat:nth-child(4):after { animation-delay: -9.75s; }

  /* The live dot is carried defensively: it may not exist in the current markup. */
  .live {
    animation: idle-live 5s ease-in-out infinite;
  }

  /* Primary call to action, a light travelling the border.
     The sweep is masked to a one pixel ring around the button, so it can never sit over the
     label. Older engines without mask compositing simply do not get the shimmer. */
  @supports ((mask-composite: exclude) or (-webkit-mask-composite: xor)) {
    .btn.primary:before {
      content: '';
      position: absolute;
      inset: 0;
      padding: 1px;
      border-radius: inherit;
      z-index: -1;
      pointer-events: none;
      background: linear-gradient(100deg, transparent 46%,
        color-mix(in srgb, var(--white) 80%, transparent) 50%, transparent 54%);
      background-size: 300% 100%;
      -webkit-mask: linear-gradient(var(--bg) 0 0) content-box, linear-gradient(var(--bg) 0 0);
      -webkit-mask-composite: xor;
      mask: linear-gradient(var(--bg) 0 0) content-box, linear-gradient(var(--bg) 0 0);
      mask-composite: exclude;
      animation: idle-border 9s ease-in-out infinite;
    }
  }

  /* Scroll reveal.
     The hidden state exists only for elements the script has marked with data-reveal, and
     only when motion is welcome. If the script never runs, nothing carries the attribute and
     the page is simply, fully visible. */
  [data-reveal] {
    opacity: 0;
    translate: 0 14px;
    transition: opacity .7s var(--ease), translate .7s var(--ease),
      transform .25s var(--ease), border-color .25s var(--ease), box-shadow .25s var(--ease);
    transition-delay: var(--reveal-delay, 0ms);
  }

  [data-reveal].is-revealed {
    opacity: 1;
    translate: 0 0;
  }

}

/* The reveal never fights the hover lift on the game tiles: transform is transitioned here
   on the same terms the tiles already use, so the existing hover easing is preserved. */

@keyframes idle-ambient {
  0%, 100% { opacity: .5; }
  50% { opacity: 1; }
}

@keyframes idle-glow {
  0%, 100% { opacity: .45; transform: scale(.96); }
  50% { opacity: .95; transform: scale(1.04); }
}

@keyframes idle-float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-5px); }
}

@keyframes idle-eyes {
  0%, 100% { filter: drop-shadow(0 0 3px var(--lime-19)); }
  50% { filter: drop-shadow(0 0 7px var(--lime-40)); }
}

@keyframes idle-breathe {
  0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 color-mix(in srgb, var(--white) 14%, transparent); }
  50% { transform: scale(1.04); box-shadow: 0 0 16px 1px color-mix(in srgb, var(--white) 16%, transparent); }
}

@keyframes idle-sheen {
  0% { transform: translateX(-140%); opacity: 0; }
  12% { opacity: 1; }
  58% { transform: translateX(370%); opacity: 1; }
  74% { opacity: 0; }
  100% { transform: translateX(370%); opacity: 0; }
}

@keyframes idle-shimmer {
  0% { transform: translateX(-120%); opacity: 0; }
  10% { opacity: 1; }
  45% { transform: translateX(120%); opacity: 1; }
  60% { opacity: 0; }
  100% { transform: translateX(120%); opacity: 0; }
}

@keyframes idle-live {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 var(--lime-04); }
  50% { opacity: .65; box-shadow: 0 0 0 5px var(--lime-04); }
}

@keyframes idle-border {
  0% { background-position: 10% 0; }
  55% { background-position: 90% 0; }
  100% { background-position: 90% 0; }
}
