/* ========== KEYFRAME ANIMATIONS ========== */

/* Cursor blink */
@keyframes blink {
    0%, 100% { opacity: 1; }
    50% { opacity: 0; }
}

/* Pulse for current job dot */
@keyframes pulse {
    0% {
        transform: translate(-50%, -50%) scale(1);
        opacity: 0.6;
    }
    100% {
        transform: translate(-50%, -50%) scale(3);
        opacity: 0;
    }
}

/* Fade in up */
@keyframes fadeInUp {
    from {
        opacity: 0;
        transform: translateY(30px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Fade in */
@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* Slide in from left */
@keyframes slideInLeft {
    from {
        opacity: 0;
        transform: translateX(-30px);
    }
    to {
        opacity: 1;
        transform: translateX(0);
    }
}

/* Slide in from right */
@keyframes slideInRight {
    from {
        opacity: 0;
        transform: translateX(30px);
    }
    to {
        opacity: 1;
        transform: translateX(0);
    }
}

/* Scale in */
@keyframes scaleIn {
    from {
        opacity: 0;
        transform: scale(0.9);
    }
    to {
        opacity: 1;
        transform: scale(1);
    }
}

/* Float */
@keyframes float {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-10px); }
}

/* ========== SCROLL ANIMATION CLASSES ==========
   Motion is opt-in, not opt-out. Base styles are motionless and content is
   VISIBLE; the hidden pre-reveal state is added only when both hold:
     .js  — set by an inline script in <head>, so no JS means no hiding
     (prefers-reduced-motion: no-preference) — so reduced-motion means no hiding
   A blanket `*{ animation-duration: 0.01ms !important }` reset is deliberately
   avoided: it fights every animation instead of never starting one. */

@media (prefers-reduced-motion: no-preference) {

    /* Elements waiting to be revealed */
    .js .animate-on-scroll {
        opacity: 0;
        transform: translateY(30px);
        transition: opacity 0.6s ease, transform 0.6s ease;
    }

    /* Stagger delays for cards */
    .js .animate-on-scroll[data-delay="0"] { transition-delay: 0s; }
    .js .animate-on-scroll[data-delay="1"] { transition-delay: 0.1s; }
    .js .animate-on-scroll[data-delay="2"] { transition-delay: 0.2s; }
    .js .animate-on-scroll[data-delay="3"] { transition-delay: 0.3s; }
    .js .animate-on-scroll[data-delay="4"] { transition-delay: 0.4s; }
    .js .animate-on-scroll[data-delay="5"] { transition-delay: 0.5s; }
    .js .animate-on-scroll[data-delay="6"] { transition-delay: 0.6s; }

    /* Revealed by the IntersectionObserver in main.js */
    .js .animate-on-scroll.visible {
        opacity: 1;
        transform: translateY(0);
    }

    /* Hero animations — play immediately on load */
    .hero-content .hero-greeting { animation: fadeInUp 0.6s ease 0.2s both; }
    .hero-content .hero-name     { animation: fadeInUp 0.6s ease 0.4s both; }
    .hero-content .hero-title    { animation: fadeInUp 0.6s ease 0.6s both; }
    .hero-content .hero-summary  { animation: fadeInUp 0.6s ease 0.8s both; }
    .hero-content .hero-context  { animation: fadeInUp 0.6s ease 0.9s both; }
    .hero-content .hero-ctas     { animation: fadeInUp 0.6s ease 1.1s both; }
    .hero-content .hero-socials  { animation: fadeInUp 0.6s ease 1.3s both; }

    /* Profile image float */
    .profile-img-wrapper {
        animation: float 3s ease-in-out infinite;
    }
}

/* Reduced motion keeps colour/opacity affordances but drops travel and looping.
   Nothing above needs undoing — it was never applied. */
@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }
}