/* =============================================================================
   REUSABLE ANIMATIONS & UTILITIES
   ============================================================================= */

/* Text Animation Classes */
/* ===================== */

/* Hide text immediately on page load */
.text-animate {
    opacity: 0;
}

/* Hide letters initially */
.text-animate .letter {
    opacity: 0;
    display: inline;  /* Changed from inline-block to prevent extra spacing */
    animation-fill-mode: forwards;
}

/* Preserve spaces */
.text-animate .letter.space {
    display: inline;
    width: 0.3em;
}

/* Letter fade-in animation */
@keyframes fadeInLetter {
    from {
        opacity: 0;
        transform: translateY(10px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Animation class applied via JavaScript */
.text-animate .letter.animate {
    animation: fadeInLetter 0.3s ease-out forwards;
}

/* Alternative slide-in animation */
@keyframes slideInLetter {
    from {
        opacity: 0;
        transform: translateX(-20px);
    }
    to {
        opacity: 1;
        transform: translateX(0);
    }
}

.text-animate.slide .letter.animate {
    animation: slideInLetter 0.3s ease-out forwards;
}

/* Scale-in animation */
@keyframes scaleInLetter {
    from {
        opacity: 0;
        transform: scale(0.5);
    }
    to {
        opacity: 1;
        transform: scale(1);
    }
}

.text-animate.scale .letter.animate {
    animation: scaleInLetter 0.3s ease-out forwards;
}

/* Rotate-in animation */
@keyframes rotateInLetter {
    from {
        opacity: 0;
        transform: rotateY(90deg);
    }
    to {
        opacity: 1;
        transform: rotateY(0deg);
    }
}

.text-animate.rotate .letter.animate {
    animation: rotateInLetter 0.4s ease-out forwards;
}


/* Loading Animations */
/* ================== */

@keyframes ellipsis {
    0% { content: ''; }
    25% { content: '.'; }
    50% { content: '..'; }
    75% { content: '...'; }
    100% { content: ''; }
}

.loading-dots::after {
    content: '';
    animation: ellipsis 2s infinite;
}


/* Fade Transitions */
/* ================ */

.fade-in {
    animation: fadeIn 0.8s ease-out forwards;
}

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

.fade-out {
    animation: fadeOut 0.8s ease-out forwards;
}

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


/* Parallax Utilities */
/* ================== */

.parallax-container {
    height: 100vh;
    overflow-y: auto;
    perspective: 1px;
    transform-style: preserve-3d;
}

.parallax-fast {
    transform: translateZ(0);
}

.parallax-medium {
    transform: translateZ(-0.3px) scale(1.3);
}

.parallax-slow {
    transform: translateZ(-0.5px) scale(1.5);
}

.parallax-very-slow {
    transform: translateZ(-1px) scale(2);
}


/* Typography Utilities */
/* ==================== */

.text-jalnan {
    font-family: 'Jalnan2', 'Arial Black', sans-serif;
}

.text-black {
    font-weight: 900;
}

.text-bold {
    font-weight: 700;
}

.letter-spacing-wide {
    letter-spacing: 1px;
}

.letter-spacing-wider {
    letter-spacing: 2px;
}

/* ============================================================================
   On-view (IntersectionObserver) utilities for scroll-triggered animations
   Works with any scroll container when paired with JS observer.
   ============================================================================ */

/* Initial hidden state */
.io-fade,
.io-fade-left,
.io-fade-right,
.io-fade-right-close,
.io-fade-right-tiny,
.io-fade-left-tiny,
.io-scale {
    opacity: 0;
    will-change: opacity, transform;
}

/* Vertical fade */
.io-fade {
    transform: translateY(24px);
    transition: opacity 600ms ease-out, transform 600ms ease-out;
}

/* Horizontal fade directions */
.io-fade-left {
    transform: translateX(calc(-60% - 80px)); /* -60% plus animation offset */
    transition: opacity 500ms ease-out, transform 500ms ease-out;
}

.io-fade-right {
    transform: translateX(80px);
    transition: opacity 500ms ease-out, transform 500ms ease-out;
}

/* Scale-in - same timing as fade-left for sync */
.io-scale {
    transform: scale(0.94);
    transition: opacity 500ms ease-out, transform 500ms ease-out;
}

/* Fade from right with small offset (layout determines final position) */
.io-fade-right-close {
    opacity: 0; /* MUST start invisible */
    transform: translateX(30px); /* Small offset from the right */
    transition: opacity 500ms ease-out, transform 500ms ease-out;
}

/* Title-specific tiny right fade */
.io-fade-right-tiny {
    opacity: 0;
    transform: translateX(10px);
    transition: opacity 400ms ease-out, transform 400ms ease-out;
}

/* Title-specific tiny left fade (left -> right) */
.io-fade-left-tiny {
    opacity: 0;
    transform: translateX(-20px); /* a little further than 10px */
    transition: opacity 450ms ease-out, transform 450ms ease-out;
}

/* Reverse fade - starts visible (after initial animation), fades out when scrolled */
.io-fade-reverse {
    transform: translateY(0);
    transition: opacity 600ms ease-out, transform 600ms ease-out;
    will-change: opacity, transform;
}

.io-fade-reverse.is-visible {
    opacity: 0 !important;
    transform: translateY(-20px) !important;
}

/* Become visible when observed */
.is-visible {
    opacity: 1 !important;
    transform: none !important;
}

/* Special-case centering removed to allow layout-driven alignment (right-aligned at 10vw) */

.character-3d.is-visible {
    transform: translateX(-60%) !important; /* Shift character left by 60% */
}
