@@ -113,6 +113,81 @@ const base = import.meta.env.BASE_URL.replace(/\/?$/, '/');
113113 </div >
114114 </section >
115115
116+ <!-- Rocket Back to Top -->
117+ <button id =" rocket-top" aria-label =" Back to top" class =" fixed bottom-8 right-8 z-50 w-16 h-16 rounded-full bg-surface/80 backdrop-blur-md border border-phantom/40 flex items-center justify-center cursor-pointer opacity-0 translate-y-8 pointer-events-none transition-all duration-500 hover:scale-110 group" >
118+ <div class =" rocket-glow absolute -inset-2 rounded-full pointer-events-none" ></div >
119+ <svg class =" w-9 h-9 transition-transform duration-300 group-hover:-translate-y-1 relative z-10" viewBox =" 0 0 64 64" fill =" none" >
120+ <defs >
121+ <linearGradient id =" rtBody" x1 =" 32" y1 =" 4" x2 =" 32" y2 =" 48" gradientUnits =" userSpaceOnUse" >
122+ <stop offset =" 0%" stop-color =" #D68FFF" />
123+ <stop offset =" 40%" stop-color =" #9C4DCC" />
124+ <stop offset =" 100%" stop-color =" #6B2FA0" />
125+ </linearGradient >
126+ <radialGradient id =" rtPort" cx =" 50%" cy =" 40%" r =" 55%" >
127+ <stop offset =" 0%" stop-color =" #E0B0FF" stop-opacity =" 0.9" />
128+ <stop offset =" 70%" stop-color =" #9C4DCC" stop-opacity =" 0.4" />
129+ <stop offset =" 100%" stop-color =" #6B2FA0" stop-opacity =" 0.1" />
130+ </radialGradient >
131+ </defs >
132+ <path d =" M32 4 C32 4 18 18 18 40 L24 47 L40 47 L46 40 C46 18 32 4 32 4Z" fill =" url(#rtBody)" />
133+ <path d =" M32 4 C32 4 26 14 25 26 L32 6 L39 26 C38 14 32 4 32 4Z" fill =" #D68FFF" opacity =" 0.25" />
134+ <circle cx =" 32" cy =" 26" r =" 7" fill =" none" stroke =" #B44FE0" stroke-width =" 0.8" opacity =" 0.4" />
135+ <circle cx =" 32" cy =" 26" r =" 5.5" fill =" #0A0A10" stroke =" #B44FE0" stroke-width =" 1.5" />
136+ <circle cx =" 32" cy =" 26" r =" 4" fill =" url(#rtPort)" />
137+ <circle cx =" 30" cy =" 24" r =" 1.5" fill =" white" opacity =" 0.35" />
138+ <circle cx =" 33.5" cy =" 28" r =" 0.7" fill =" white" opacity =" 0.15" />
139+ <path d =" M18 36 L9 52 L18 44Z" fill =" #7B2D8E" />
140+ <path d =" M46 36 L55 52 L46 44Z" fill =" #7B2D8E" />
141+ <path d =" M18 36 L11 49 L18 42Z" fill =" #9C4DCC" opacity =" 0.3" />
142+ <path d =" M46 36 L53 49 L46 42Z" fill =" #9C4DCC" opacity =" 0.3" />
143+ <ellipse cx =" 32" cy =" 45" rx =" 8" ry =" 2.5" fill =" #5A1F7A" stroke =" #9C4DCC" stroke-width =" 0.5" opacity =" 0.7" />
144+ <line x1 =" 24" y1 =" 38" x2 =" 40" y2 =" 38" stroke =" #B44FE0" stroke-width =" 0.5" opacity =" 0.25" />
145+ <line x1 =" 22" y1 =" 42" x2 =" 42" y2 =" 42" stroke =" #B44FE0" stroke-width =" 0.5" opacity =" 0.15" />
146+ </svg >
147+ <div class =" rocket-flames absolute -bottom-4 left-1/2 -translate-x-1/2 flex gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity z-10" >
148+ <div class =" w-1 h-3 bg-gradient-to-b from-orange-300 to-orange-600 rounded-full animate-flame1 opacity-90" ></div >
149+ <div class =" w-1.5 h-5 bg-gradient-to-b from-yellow-200 to-orange-500 rounded-full animate-flame2" ></div >
150+ <div class =" w-2 h-6 bg-gradient-to-b from-white to-yellow-400 rounded-full animate-flame2" style =" animation-delay:-0.1s;" ></div >
151+ <div class =" w-1.5 h-5 bg-gradient-to-b from-yellow-200 to-orange-500 rounded-full animate-flame2" style =" animation-delay:-0.15s;" ></div >
152+ <div class =" w-1 h-3 bg-gradient-to-b from-orange-300 to-orange-600 rounded-full animate-flame3 opacity-90" ></div >
153+ </div >
154+ <div class =" absolute -bottom-10 left-1/2 -translate-x-1/2 w-4 h-10 bg-gradient-to-b from-orange-400/20 via-phantom/10 to-transparent rounded-full blur-md opacity-0 group-hover:opacity-100 transition-opacity" id =" rocket-top-exhaust" ></div >
155+ </button >
156+ <style >
157+ @keyframes flame1 { 0%,100% { height: 10px; opacity: 0.8; } 50% { height: 18px; opacity: 1; } }
158+ @keyframes flame2 { 0%,100% { height: 16px; opacity: 0.9; } 50% { height: 24px; opacity: 1; } }
159+ @keyframes flame3 { 0%,100% { height: 8px; opacity: 0.7; } 50% { height: 16px; opacity: 1; } }
160+ .animate-flame1 { animation: flame1 0.3s ease-in-out infinite; }
161+ .animate-flame2 { animation: flame2 0.25s ease-in-out infinite; }
162+ .animate-flame3 { animation: flame3 0.35s ease-in-out infinite; }
163+ #rocket-top { filter: drop-shadow(0 0 10px rgba(156,77,204,0.3)); }
164+ @keyframes rocketGlow {
165+ 0%, 100% { filter: drop-shadow(0 0 10px rgba(156,77,204,0.3)); }
166+ 50% { filter: drop-shadow(0 0 22px rgba(156,77,204,0.6)); }
167+ }
168+ #rocket-top.visible { opacity: 1; transform: translateY(0); pointer-events: auto; animation: rocketGlow 3s ease-in-out infinite; }
169+ .rocket-glow {
170+ background: radial-gradient(circle, rgba(156,77,204,0.15) 0%, transparent 70%);
171+ animation: rocketGlowRing 2s ease-in-out infinite;
172+ }
173+ @keyframes rocketGlowRing {
174+ 0%, 100% { transform: scale(1); opacity: 0.4; }
175+ 50% { transform: scale(1.3); opacity: 0.7; }
176+ }
177+ #rocket-top.launching {
178+ animation: rocketLaunch 0.8s ease-in forwards !important;
179+ }
180+ @keyframes rocketLaunch {
181+ 0% { transform: translateY(0) scale(1); filter: drop-shadow(0 0 10px rgba(156,77,204,0.4)); }
182+ 20% { transform: translateY(8px) scale(1.15); filter: drop-shadow(0 0 30px rgba(156,77,204,0.8)); }
183+ 100% { transform: translateY(-120vh) scale(0.5); opacity: 0; filter: drop-shadow(0 0 40px rgba(156,77,204,1)); }
184+ }
185+ #rocket-top.launching .rocket-flames { opacity: 1 !important; }
186+ #rocket-top.launching .rocket-flames div { height: 1.5rem !important; }
187+ #rocket-top.launching #rocket-top-exhaust { opacity: 1 !important; height: 4rem; }
188+ #rocket-top.launching .rocket-glow { transform: scale(2); opacity: 1; }
189+ </style >
190+
116191 <!-- Footer -->
117192 <footer class =" py-12 border-t border-white/5" >
118193 <div class =" max-w-7xl mx-auto px-6 flex flex-col md:flex-row items-center justify-between gap-6" >
@@ -193,6 +268,22 @@ const base = import.meta.env.BASE_URL.replace(/\/?$/, '/');
193268 window.addEventListener('scroll', () => { if (!sp) return; const h = document.documentElement.scrollHeight - window.innerHeight; sp.style.width = h > 0 ? `${(window.scrollY/h*100)}%` : '0%'; });
194269
195270 fetchChangelogs();
271+
272+ // ---- Rocket Back to Top ----
273+ const rocketBtn = document.getElementById('rocket-top');
274+ window.addEventListener('scroll', () => {
275+ if (!rocketBtn) return;
276+ if (window.scrollY > 600) rocketBtn.classList.add('visible');
277+ else rocketBtn.classList.remove('visible');
278+ });
279+ rocketBtn?.addEventListener('click', () => {
280+ rocketBtn.classList.add('launching');
281+ rocketBtn.querySelector('.rocket-flames')?.classList.add('opacity-100');
282+ window.scrollTo({ top: 0, behavior: 'smooth' });
283+ setTimeout(() => {
284+ rocketBtn.classList.remove('launching', 'visible');
285+ }, 1000);
286+ });
196287 </script >
197288
198289</Layout >
0 commit comments