/* ============================================================ ApolloNvim – animations.css Web: apollo-nvim.lukan.cz All keyframes, scroll animations, stagger delays, micro-interactions and loading screen transitions. ============================================================ */ /* ──────────────────────────────────────────────────────────── 1. CORE KEYFRAMES ──────────────────────────────────────────────────────────── */ /* 1.1 fadeIn ─ simple opacity */ @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } /* 1.2 slideUp ─ rise + fade */ @keyframes slideUp { from { opacity: 0; transform: translateY(40px); } to { opacity: 1; transform: translateY(0); } } /* 1.3 slideInLeft ─ enter from left */ @keyframes slideInLeft { from { opacity: 0; transform: translateX(-60px); } to { opacity: 1; transform: translateX(0); } } /* 1.4 slideInRight ─ enter from right */ @keyframes slideInRight { from { opacity: 0; transform: translateX(60px); } to { opacity: 1; transform: translateX(0); } } /* 1.5 float ─ gentle levitation for hero images / icons */ @keyframes float { 0%, 100% { transform: translateY(0px); } 33% { transform: translateY(-10px); } 66% { transform: translateY(-6px); } } /* 1.6 pulse-glow ─ pulsing purple box-shadow */ @keyframes pulse-glow { 0%, 100% { box-shadow: 0 0 0 0 rgba(189, 147, 249, 0), 0 0 20px 2px rgba(189, 147, 249, 0.15); } 50% { box-shadow: 0 0 0 4px rgba(189, 147, 249, 0.08), 0 0 40px 8px rgba(189, 147, 249, 0.35); } } /* 1.7 typing ─ blinking cursor caret */ @keyframes typing { 0%, 100% { border-right-color: rgba(189, 147, 249, 1); } 50% { border-right-color: transparent; } } /* Typewriter text reveal (pair with .typewriter class) */ @keyframes typewriter { from { width: 0; } to { width: 100%; } } /* 1.8 spin-slow ─ slow full rotation (used by loading logo) */ @keyframes spin-slow { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } /* 1.9 glitch ─ subtle horizontal glitch for headings */ @keyframes glitch { 0% { clip-path: inset(0 0 100% 0); transform: translate(0); } 10% { clip-path: inset(10% 0 60% 0); transform: translate(-3px, 1px); } 20% { clip-path: inset(60% 0 10% 0); transform: translate(3px, -1px); } 30% { clip-path: inset(30% 0 40% 0); transform: translate(-2px, 0); } 40% { clip-path: inset(0 0 80% 0); transform: translate(2px, 1px); } 50% { clip-path: inset(80% 0 0 0); transform: translate(-1px, -2px); } 60% { clip-path: inset(20% 0 50% 0); transform: translate(1px, 0); } 70% { clip-path: inset(50% 0 20% 0); transform: translate(-2px, 1px); } 80% { clip-path: inset(5% 0 70% 0); transform: translate(2px, -1px); } 90% { clip-path: inset(70% 0 5% 0); transform: translate(0, 2px); } 100% { clip-path: inset(0 0 0 0); transform: translate(0); } } /* 1.10 rocket-launch ─ rocket rises with shrinking trail */ @keyframes rocket-launch { 0% { transform: translateY(0) scale(1); opacity: 1; filter: drop-shadow(0 8px 16px rgba(189, 147, 249, 0.6)); } 30% { transform: translateY(-20px) scale(1.05); filter: drop-shadow(0 16px 32px rgba(255, 121, 198, 0.7)); } 70% { transform: translateY(-80px) scale(0.9); opacity: 0.85; filter: drop-shadow(0 4px 8px rgba(139, 233, 253, 0.5)); } 100% { transform: translateY(-200px) scale(0.4); opacity: 0; filter: drop-shadow(0 0 0 transparent); } } /* Rocket trail (::after pseudo) */ @keyframes rocket-trail { 0% { opacity: 0.8; transform: scaleY(1) scaleX(1); } 50% { opacity: 0.5; transform: scaleY(1.4) scaleX(0.7); } 100% { opacity: 0; transform: scaleY(2) scaleX(0.3); } } /* 1.11 star-twinkle ─ random opacity blink for star dots */ @keyframes star-twinkle { 0%, 100% { opacity: 0.15; transform: scale(0.85); } 25% { opacity: 0.9; transform: scale(1.1); } 50% { opacity: 0.4; transform: scale(0.95); } 75% { opacity: 1; transform: scale(1.05); } } /* 1.12 gradient-shift ─ animated gradient background */ @keyframes gradient-shift { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } } /* ──────────────────────────────────────────────────────────── 2. ANIMATION UTILITY CLASSES ──────────────────────────────────────────────────────────── */ /* Basic named animations */ .anim-fade-in { animation: fadeIn 0.5s ease both; } .anim-slide-up { animation: slideUp 0.55s ease both; } .anim-slide-left { animation: slideInLeft 0.55s ease both; } .anim-slide-right { animation: slideInRight 0.55s ease both; } .anim-float { animation: float 4s ease-in-out infinite; } .anim-pulse-glow { animation: pulse-glow 2.5s ease-in-out infinite; } .anim-spin-slow { animation: spin-slow 20s linear infinite; } .anim-star-twinkle { animation: star-twinkle 3s ease-in-out infinite; } /* Gradient-shift background (set background-size: 200% 200% on element) */ .anim-gradient-bg { background-size: 200% 200%; animation: gradient-shift 6s ease infinite; } /* ──────────────────────────────────────────────────────────── 3. TYPEWRITER EFFECT ──────────────────────────────────────────────────────────── */ .typewriter { display: inline-block; overflow: hidden; white-space: nowrap; border-right: 2px solid; /* combine reveal + cursor blink */ animation: typewriter 2s steps(30, end) both, typing 0.7s step-end infinite; } /* Standalone cursor only (no text reveal) */ .cursor-blink { display: inline-block; width: 2px; height: 1.1em; background: #bd93f9; vertical-align: text-bottom; animation: typing 0.7s step-end infinite; } /* ──────────────────────────────────────────────────────────── 4. GLITCH EFFECT ──────────────────────────────────────────────────────────── */ .glitch-text { position: relative; display: inline-block; } /* Glitch layers via pseudo-elements */ .glitch-text::before, .glitch-text::after { content: attr(data-text); position: absolute; inset: 0; pointer-events: none; } .glitch-text::before { color: #ff79c6; animation: glitch 3.5s infinite linear alternate-reverse; animation-delay: 0.1s; opacity: 0.7; } .glitch-text::after { color: #8be9fd; animation: glitch 2.8s infinite linear alternate; animation-delay: 0.25s; opacity: 0.6; } /* On hover only */ .glitch-text--hover::before, .glitch-text--hover::after { animation-play-state: paused; } .glitch-text--hover:hover::before, .glitch-text--hover:hover::after { animation-play-state: running; } /* ──────────────────────────────────────────────────────────── 5. ROCKET LAUNCH ──────────────────────────────────────────────────────────── */ .rocket { display: inline-block; position: relative; transition: transform 0.3s ease; } .rocket--launch { animation: rocket-launch 1.2s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; } /* Trail element */ .rocket__trail { position: absolute; bottom: -8px; left: 50%; transform: translateX(-50%); width: 6px; height: 20px; border-radius: 0 0 4px 4px; background: linear-gradient(180deg, rgba(255, 121, 198, 0.8), transparent); transform-origin: top center; animation: rocket-trail 1.2s ease forwards; opacity: 0; } .rocket--launch .rocket__trail { opacity: 0.8; } /* ──────────────────────────────────────────────────────────── 6. STAR FIELD ──────────────────────────────────────────────────────────── */ .star { position: absolute; width: 3px; height: 3px; background: #f8f8f2; border-radius: 50%; animation: star-twinkle var(--duration, 3s) ease-in-out infinite; animation-delay: var(--delay, 0s); } .star--sm { width: 1.5px; height: 1.5px; } .star--md { width: 2.5px; height: 2.5px; } .star--lg { width: 4px; height: 4px; box-shadow: 0 0 4px 1px rgba(248, 248, 242, 0.4); } /* ──────────────────────────────────────────────────────────── 7. SCROLL-TRIGGERED ANIMATIONS ──────────────────────────────────────────────────────────── */ /* Base hidden state – applied by JS IntersectionObserver */ .animate-on-scroll { opacity: 0; transform: translateY(30px); transition: opacity 0.6s ease, transform 0.6s ease; } /* Revealed state – JS adds .visible class */ .animate-on-scroll.visible { opacity: 1; transform: translateY(0); } /* Directional variants */ .animate-on-scroll--left { opacity: 0; transform: translateX(-40px); transition: opacity 0.6s ease, transform 0.6s ease; } .animate-on-scroll--left.visible { opacity: 1; transform: translateX(0); } .animate-on-scroll--right { opacity: 0; transform: translateX(40px); transition: opacity 0.6s ease, transform 0.6s ease; } .animate-on-scroll--right.visible { opacity: 1; transform: translateX(0); } .animate-on-scroll--scale { opacity: 0; transform: scale(0.9); transition: opacity 0.6s ease, transform 0.6s ease; } .animate-on-scroll--scale.visible { opacity: 1; transform: scale(1); } /* ──────────────────────────────────────────────────────────── 8. STAGGER DELAYS ──────────────────────────────────────────────────────────── */ .delay-1 { animation-delay: 100ms; transition-delay: 100ms; } .delay-2 { animation-delay: 200ms; transition-delay: 200ms; } .delay-3 { animation-delay: 300ms; transition-delay: 300ms; } .delay-4 { animation-delay: 400ms; transition-delay: 400ms; } .delay-5 { animation-delay: 500ms; transition-delay: 500ms; } /* Extended stagger for large grids */ .delay-6 { animation-delay: 600ms; transition-delay: 600ms; } .delay-7 { animation-delay: 700ms; transition-delay: 700ms; } .delay-8 { animation-delay: 800ms; transition-delay: 800ms; } /* ──────────────────────────────────────────────────────────── 9. HOVER MICRO-ANIMATIONS ──────────────────────────────────────────────────────────── */ /* ── Buttons ── */ /* Primary button shine sweep */ .btn-primary { position: relative; overflow: hidden; } .btn-primary::after { content: ''; position: absolute; top: 0; left: -75%; width: 50%; height: 100%; background: linear-gradient( 120deg, transparent 20%, rgba(255, 255, 255, 0.22) 50%, transparent 80% ); transform: skewX(-20deg); transition: left 0.55s ease; pointer-events: none; } .btn-primary:hover::after { left: 135%; } /* Secondary button border glow pulse */ .btn-secondary { position: relative; } .btn-secondary::before { content: ''; position: absolute; inset: -1px; border-radius: inherit; background: linear-gradient(135deg, transparent, rgba(189, 147, 249, 0.3), transparent); opacity: 0; transition: opacity 0.3s ease; pointer-events: none; z-index: -1; } .btn-secondary:hover::before { opacity: 1; } /* ── Links ── */ .link-underline { position: relative; text-decoration: none; display: inline-block; } .link-underline::after { content: ''; position: absolute; bottom: -2px; left: 0; width: 100%; height: 1px; background: linear-gradient(90deg, #bd93f9, #ff79c6); transform: scaleX(0); transform-origin: right; transition: transform 0.35s ease; } .link-underline:hover::after { transform: scaleX(1); transform-origin: left; } /* ── Card tilt (subtle) ── */ .card-tilt { transition: transform 0.3s ease, box-shadow 0.3s ease; will-change: transform; } /* Managed by JS mousemove if desired – CSS fallback hover */ .card-tilt:hover { transform: perspective(800px) rotateX(2deg) rotateY(-2deg) translateY(-4px); } /* ── Icon bounce ── */ .icon-bounce { display: inline-block; transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1); } .icon-bounce:hover { transform: scale(1.25) translateY(-2px); } /* ── Icon spin on hover ── */ .icon-spin-hover { transition: transform 0.4s ease; display: inline-block; } .icon-spin-hover:hover { transform: rotate(180deg); } /* ── Pill/badge pop ── */ .badge { transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.2s ease; } .badge:hover { transform: scale(1.08) translateY(-1px); box-shadow: 0 4px 12px rgba(189, 147, 249, 0.3); } /* ── Nav link underline (already in style.css, reinforced here) ── */ .navbar__link { will-change: color; } /* ── Feature card shimmer border ── */ .feature-card { background-clip: padding-box; } /* ──────────────────────────────────────────────────────────── 10. ANIMATED GRADIENT HERO BACKGROUND ──────────────────────────────────────────────────────────── */ .hero-gradient-bg { background: linear-gradient( -45deg, #0d0d1a, #12122a, #1a0a2e, #0d1a2e ); background-size: 400% 400%; animation: gradient-shift 12s ease infinite; } /* ──────────────────────────────────────────────────────────── 11. LOADING SCREEN – FADE OUT ──────────────────────────────────────────────────────────── */ @keyframes loading-fade-out { 0% { opacity: 1; transform: scale(1); } 80% { opacity: 1; transform: scale(1.02); } 100% { opacity: 0; transform: scale(0.98); } } /* Trigger by adding .is-hiding to .loading-screen via JS */ .loading-screen.is-hiding { animation: loading-fade-out 0.55s ease forwards; pointer-events: none; } /* Orbiting dots spinner (used inside loading screen) */ @keyframes orbit { from { transform: rotate(0deg) translateX(28px) rotate(0deg); } to { transform: rotate(360deg) translateX(28px) rotate(-360deg); } } .loader-orbit { position: relative; width: 60px; height: 60px; } .loader-orbit__dot { position: absolute; top: 50%; left: 50%; width: 8px; height: 8px; border-radius: 50%; background: var(--purple, #bd93f9); margin: -4px 0 0 -4px; animation: orbit 1.4s linear infinite; } .loader-orbit__dot:nth-child(2) { background: var(--pink, #ff79c6); animation-delay: -0.46s; } .loader-orbit__dot:nth-child(3) { background: var(--cyan, #8be9fd); animation-delay: -0.93s; } /* ──────────────────────────────────────────────────────────── 12. REVEAL ANIMATIONS FOR PAGE SECTIONS ──────────────────────────────────────────────────────────── */ /* Used by JS: section fades in as a block */ .section-reveal { opacity: 0; transition: opacity 0.8s ease, transform 0.8s ease; transform: translateY(24px); } .section-reveal.in-view { opacity: 1; transform: translateY(0); } /* Hero content staggered entrance */ .hero__content > * { opacity: 0; animation: slideUp 0.65s ease both; } .hero__content > *:nth-child(1) { animation-delay: 0.1s; } .hero__content > *:nth-child(2) { animation-delay: 0.25s; } .hero__content > *:nth-child(3) { animation-delay: 0.4s; } .hero__content > *:nth-child(4) { animation-delay: 0.55s; } .hero__content > *:nth-child(5) { animation-delay: 0.7s; } /* ──────────────────────────────────────────────────────────── 13. PROGRESS / SKILL BARS ──────────────────────────────────────────────────────────── */ @keyframes bar-fill { from { width: 0%; } } .progress-bar { height: 4px; background: var(--border, rgba(189, 147, 249, 0.2)); border-radius: 4px; overflow: hidden; } .progress-bar__fill { height: 100%; border-radius: 4px; background: linear-gradient(90deg, var(--purple, #bd93f9), var(--pink, #ff79c6)); width: var(--progress, 0%); animation: bar-fill 1.2s cubic-bezier(0.22, 1, 0.36, 1) both; animation-delay: var(--delay, 0.2s); } /* ──────────────────────────────────────────────────────────── 14. NOTIFICATION / TOAST ENTRANCE ──────────────────────────────────────────────────────────── */ @keyframes toast-in { from { opacity: 0; transform: translateX(calc(100% + 24px)); } to { opacity: 1; transform: translateX(0); } } @keyframes toast-out { from { opacity: 1; transform: translateX(0); } to { opacity: 0; transform: translateX(calc(100% + 24px)); } } .toast { animation: toast-in 0.35s cubic-bezier(0.22, 1, 0.36, 1) both; } .toast.closing { animation: toast-out 0.3s ease forwards; } /* ──────────────────────────────────────────────────────────── 15. REDUCED MOTION OVERRIDES ──────────────────────────────────────────────────────────── */ @media (prefers-reduced-motion: reduce) { /* Disable all custom animations */ .anim-fade-in, .anim-slide-up, .anim-slide-left, .anim-slide-right, .anim-float, .anim-pulse-glow, .anim-spin-slow, .anim-star-twinkle, .anim-gradient-bg, .typewriter, .cursor-blink, .glitch-text::before, .glitch-text::after, .hero__content > *, .rocket--launch, .rocket__trail, .loader-orbit__dot, .loading-screen__bar-fill, .loading-screen__logo, .section-reveal, .progress-bar__fill, .toast, .toast.closing { animation: none !important; transition: none !important; } /* Scroll-triggered: instantly reveal instead */ .animate-on-scroll, .animate-on-scroll--left, .animate-on-scroll--right, .animate-on-scroll--scale { opacity: 1 !important; transform: none !important; transition: none !important; } /* Hero items also visible immediately */ .hero__content > * { opacity: 1 !important; } }