/* Modern Animations & Effects */

/* Scroll-triggered animations */
@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(30px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes slideInLeft {
  from {
    opacity: 0;
    transform: translateX(-30px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes slideInRight {
  from {
    opacity: 0;
    transform: translateX(30px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

/* Gradient mesh animation */
@keyframes gradientShift {
  0%, 100% {
    background-position: 0% 50%;
  }
  50% {
    background-position: 100% 50%;
  }
}

@keyframes float {
  0%, 100% {
    transform: translateY(0px);
  }
  50% {
    transform: translateY(-20px);
  }
}

@keyframes pulse {
  0%, 100% {
    opacity: 1;
    transform: scale(1);
  }
  50% {
    opacity: 0.8;
    transform: scale(1.05);
  }
}

@keyframes shimmer {
  0% {
    background-position: -1000px 0;
  }
  100% {
    background-position: 1000px 0;
  }
}

/* Scroll reveal classes */
html.has-js [data-animate] {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1), transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
  will-change: opacity, transform;
}

/* Directional variants */
html.has-js [data-animate="left"] {
  transform: translateX(-30px);
}

html.has-js [data-animate="right"] {
  transform: translateX(30px);
}

html.has-js [data-animate="scale"] {
  transform: scale(0.92);
}

html.has-js [data-animate].scroll-reveal {
  opacity: 1;
  transform: none;
}

/* animations.js adds .is-revealed once the reveal transition has settled. The
   compositor hint is only worth paying for while the reveal is in flight —
   held past that, every revealed element keeps a layer for an animation that
   already finished. */
html.has-js [data-animate].is-revealed {
  will-change: auto;
}

/* Failsafe. [data-animate] starts hidden and only animations.js brings it
   back, so a module that fails to load or parse would strand the page blank
   with nothing left to recover it. This restores the revealed state from CSS
   alone, one second behind the 3s safety net in animations.js — so when the
   module is alive it is always beaten to the punch and the reveal looks
   exactly as before, and a real reveal removes the animation before it can
   start. Deliberately not applied to .stagger-item: that class is added by
   animations.js itself, so a module that never runs leaves nothing hidden,
   and firing this on unstaggered items would reveal below-fold rows before
   the visitor ever scrolls to them. */
@keyframes revealFailsafe {
  to {
    opacity: 1;
    transform: none;
  }
}

html.has-js [data-animate]:not(.scroll-reveal) {
  animation: revealFailsafe 1ms linear 4s forwards;
}

.scroll-reveal-delay-1 {
  transition-delay: 0.1s;
}

.scroll-reveal-delay-2 {
  transition-delay: 0.2s;
}

.scroll-reveal-delay-3 {
  transition-delay: 0.3s;
}

.scroll-reveal-delay-4 {
  transition-delay: 0.4s;
}

/* Animated gradient backgrounds */
.gradient-animated {
  background: linear-gradient(
    135deg,
    rgba(93, 173, 226, 0.25) 0%,
    rgba(93, 173, 226, 0.18) 30%,
    rgba(255, 255, 255, 0.06) 55%,
    rgba(58, 143, 196, 0.14) 78%,
    rgba(93, 173, 226, 0.22) 100%
  );
  background-size: 200% 200%;
  /* Matches the animation's first and last keyframe, so the resting state is
     the same picture whether the animation runs, stops, or never starts. */
  background-position: 0% 50%;
  /* Three passes, not infinite: background-position is not compositable, so
     this repaints the full-width hero forever on a laptop battery for a
     flourish nobody is still watching at 45s. It ends on the frame it started
     from — the first 45s are pixel-identical to the infinite version. */
  animation: gradientShift 15s ease 3;
}

/* Floating elements */
.float-element {
  animation: float 6s ease-in-out infinite;
}

/* Pulsing CTA */
.pulse-cta {
  animation: pulse 2s ease-in-out infinite;
}

/* Hover lift effect */
.hover-lift {
  transition: transform 240ms cubic-bezier(0.16, 1, 0.3, 1), box-shadow 240ms cubic-bezier(0.16, 1, 0.3, 1);
}

.hover-lift:hover {
  transform: translateY(-8px);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.10), 0 18px 44px rgba(0, 0, 0, 0.55), 0 0 80px rgba(93, 173, 226, 0.10);
}

/* Hover scale effect */
.hover-scale {
  transition: transform 0.3s ease;
}

.hover-scale:hover {
  transform: scale(1.05);
}

/* Shimmer effect for loading/emphasis */
.shimmer {
  background: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.3) 50%,
    rgba(255, 255, 255, 0) 100%
  );
  background-size: 1000px 100%;
  animation: shimmer 3s infinite;
}

/* Stagger animation for card grids */
html.has-js .stagger-item {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1), transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}

html.has-js .stagger-item.is-staggered {
  opacity: 1;
  transform: none;
}

/* Reduce motion for accessibility.
   Scope note: this block owns the declarative half only. The rotating headline,
   the carousel, and the count-up are driven by timers, which no media query can
   reach — main.js and animations.js read the same query and gate themselves.
   The rule below is the catch-all for anything animated that they don't know
   about; the named rules after it say what we actually intend. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }

  /* The decorative loops in this file, stopped on purpose rather than left to
     the catch-all above. .gradient-animated rests on its own
     background-position, which is the frame the animation starts from. */
  .gradient-animated,
  .float-element,
  .pulse-cta,
  .shimmer {
    animation: none;
  }

  /* Keep opacity transitions (non-motion) but make them instant.
     No transform means no layer to promote, so don't ask for one. */
  html.has-js [data-animate] {
    transition: opacity 0.01ms !important;
    transform: none !important;
    will-change: auto;
  }

  html.has-js .stagger-item {
    transition: opacity 0.01ms !important;
    transform: none !important;
  }

  .hover-lift:hover,
  .hover-scale:hover {
    transform: none !important;
  }
}
