1// HumGod Landing Page JavaScript 2// Interactive elements, animations, and smooth functionality 3 4document.addEventListener('DOMContentLoaded', function() { 5 // Initialize all functionality 6 initNavigation(); 7 initScrollEffects(); 8 initTestimonials(); 9 initFormTabs(); 10 initAnimations(); 11 initSmoothScrolling(); 12 initParallaxEffects(); 13}); 14 15// Navigation functionality 16function initNavigation() { 17 const navToggle = document.querySelector('.nav-toggle'); 18 const navMenu = document.querySelector('.nav-menu'); 19 const navLinks = document.querySelectorAll('.nav-link'); 20 21 // Mobile menu toggle 22 if (navToggle) { 23 navToggle.addEventListener('click', function() { 24 navToggle.classList.toggle('active'); 25 navMenu.classList.toggle('active'); 26 }); 27 } 28 29 // Close mobile menu when clicking on links 30 navLinks.forEach(link => { 31 link.addEventListener('click', function() { 32 navMenu.classList.remove('active'); 33 navToggle.classList.remove('active'); 34 }); 35 }); 36 37 // Navbar background on scroll 38 window.addEventListener('scroll', function() { 39 const navbar = document.querySelector('.navbar'); 40 if (window.scrollY > 100) { 41 navbar.style.background = 'rgba(10, 10, 10, 0.98)'; 42 } else { 43 navbar.style.background = 'rgba(10, 10, 10, 0.95)'; 44 } 45 }); 46} 47 48// Scroll effects and animations 49function initScrollEffects() { 50 const observerOptions = { 51 threshold: 0.1, 52 rootMargin: '0px 0px -50px 0px' 53 }; 54 55 const observer = new IntersectionObserver(function(entries) { 56 entries.forEach(entry => { 57 if (entry.isIntersecting) { 58 entry.target.style.opacity = '1'; 59 entry.target.style.transform = 'translateY(0)'; 60 } 61 }); 62 }, observerOptions); 63 64 // Observe elements for scroll animations 65 const animatedElements = document.querySelectorAll('.step, .feature-card, .pricing-card, .testimonial'); 66 animatedElements.forEach(el => { 67 el.style.opacity = '0'; 68 el.style.transform = 'translateY(30px)'; 69 el.style.transition = 'all 0.6s cubic-bezier(0.4, 0, 0.2, 1)'; 70 observer.observe(el); 71 }); 72} 73 74// Testimonials carousel 75function initTestimonials() { 76 const testimonials = document.querySelectorAll('.testimonial'); 77 const dots = document.querySelectorAll('.dot'); 78 let currentTestimonial = 0; 79 80 function showTestimonial(index) { 81 // Hide all testimonials 82 testimonials.forEach(testimonial => { 83 testimonial.classList.remove('active'); 84 }); 85 86 // Remove active class from all dots 87 dots.forEach(dot => { 88 dot.classList.remove('active'); 89 }); 90 91 // Show current testimonial 92 if (testimonials[index]) { 93 testimonials[index].classList.add('active'); 94 } 95 96 // Add active class to current dot 97 if (dots[index]) { 98 dots[index].classList.add('active'); 99 } 100 } 101 102 // Dot click handlers 103 dots.forEach((dot, index) => { 104 dot.addEventListener('click', function() { 105 currentTestimonial = index; 106 showTestimonial(currentTestimonial); 107 }); 108 }); 109 110 // Auto-rotate testimonials 111 setInterval(function() { 112 currentTestimonial = (currentTestimonial + 1) % testimonials.length; 113 showTestimonial(currentTestimonial); 114 }, 5000); 115} 116 117// Form tabs functionality 118function initFormTabs() { 119 const tabBtns = document.querySelectorAll('.tab-btn'); 120 const forms = document.querySelectorAll('.form'); 121 122 tabBtns.forEach(btn => { 123 btn.addEventListener('click', function() { 124 const targetTab = this.getAttribute('data-tab'); 125 126 // Remove active class from all tabs and forms 127 tabBtns.forEach(tab => tab.classList.remove('active')); 128 forms.forEach(form => form.classList.remove('active')); 129 130 // Add active class to clicked tab 131 this.classList.add('active'); 132 133 // Show corresponding form 134 const targetForm = document.getElementById(targetTab + 'Form'); 135 if (targetForm) { 136 targetForm.classList.add('active'); 137 } 138 }); 139 }); 140 141 // Form submission handlers 142 const signupForm = document.getElementById('signupForm'); 143 const loginForm = document.getElementById('loginForm'); 144 145 if (signupForm) { 146 signupForm.addEventListener('submit', function(e) { 147 e.preventDefault(); 148 handleFormSubmission('signup'); 149 }); 150 } 151 152 if (loginForm) { 153 loginForm.addEventListener('submit', function(e) { 154 e.preventDefault(); 155 handleFormSubmission('login'); 156 }); 157 } 158} 159 160// Form submission handler 161function handleFormSubmission(type) { 162 const form = document.getElementById(type + 'Form'); 163 const inputs = form.querySelectorAll('input[required]'); 164 let isValid = true; 165 166 // Basic validation
167 inputs.forEach(input => { 168 if (!input.value.trim()) { 169 isValid = false; 170 input.style.borderColor = '#ff4444'; 171 setTimeout(() => { 172 input.style.borderColor = ''; 173 }, 3000); 174 } 175 }); 176 177 if (isValid) { 178 // Show loading state 179 const submitBtn = form.querySelector('button[type="submit"]'); 180 const originalText = submitBtn.innerHTML; 181 submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Processing...'; 182 submitBtn.disabled = true; 183 184 // Simulate API call 185 setTimeout(() => { 186 submitBtn.innerHTML = '<i class="fas fa-check"></i> Success!'; 187 submitBtn.style.background = '#4CAF50'; 188 189 setTimeout(() => { 190 submitBtn.innerHTML = originalText; 191 submitBtn.disabled = false; 192 submitBtn.style.background = ''; 193 form.reset(); 194 }, 2000); 195 }, 2000); 196 } 197} 198 199// General animations 200function initAnimations() { 201 // Button hover effects 202 const buttons = document.querySelectorAll('.btn'); 203 buttons.forEach(btn => { 204 btn.addEventListener('mouseenter', function() { 205 this.style.transform = 'translateY(-2px) scale(1.02)'; 206 }); 207 208 btn.addEventListener('mouseleave', function() { 209 this.style.transform = 'translateY(0) scale(1)'; 210 }); 211 }); 212 213 // Card hover effects 214 const cards = document.querySelectorAll('.feature-card, .pricing-card, .step'); 215 cards.forEach(card => { 216 card.addEventListener('mouseenter', function() { 217 this.style.transform = 'translateY(-10px) scale(1.02)'; 218 }); 219 220 card.addEventListener('mouseleave', function() { 221 this.style.transform = 'translateY(0) scale(1)'; 222 }); 223 }); 224 225 // Floating notes animation 226 const notes = document.querySelectorAll('.note'); 227 notes.forEach((note, index) => { 228 note.addEventListener('mouseenter', function() { 229 this.style.transform = 'translateY(-30px) rotate(15deg) scale(1.2)'; 230 this.style.color = '#ff8c5a'; 231 }); 232 233 note.addEventListener('mouseleave', function() { 234 this.style.transform = ''; 235 this.style.color = ''; 236 }); 237 }); 238} 239 240// Smooth scrolling for navigation links 241function initSmoothScrolling() { 242 const navLinks = document.querySelectorAll('a[href^="#"]'); 243 244 navLinks.forEach(link => { 245 link.addEventListener('click', function(e) { 246 e.preventDefault(); 247 248 const targetId = this.getAttribute('href'); 249 const targetSection = document.querySelector(targetId); 250 251 if (targetSection) { 252 const offsetTop = targetSection.offsetTop - 80; // Account for fixed navbar 253 254 window.scrollTo({ 255 top: offsetTop, 256 behavior: 'smooth' 257 }); 258 } 259 }); 260 }); 261 262 // Hero CTA buttons 263 const heroButtons = document.querySelectorAll('.hero-buttons .btn'); 264 heroButtons.forEach(btn => { 265 btn.addEventListener('click', function(e) { 266 e.preventDefault(); 267 268 if (this.textContent.includes('Start Creating')) { 269 // Scroll to signup section 270 const signupSection = document.querySelector('#signup'); 271 if (signupSection) { 272 signupSection.scrollIntoView({ behavior: 'smooth' }); 273 } 274 } else if (this.textContent.includes('Sign Up')) { 275 // Scroll to signup section 276 const signupSection = document.querySelector('#signup'); 277 if (signupSection) { 278 signupSection.scrollIntoView({ behavior: 'smooth' }); 279 } 280 } 281 }); 282 }); 283} 284 285// Parallax effects 286function initParallaxEffects() { 287 window.addEventListener('scroll', function() { 288 const scrolled = window.pageYOffset; 289 const parallaxElements = document.querySelectorAll('.wave, .note'); 290
291 parallaxElements.forEach((element, index) => { 292 const speed = 0.5 + (index * 0.1); 293 element.style.transform = `translateY(${scrolled * speed}px)`; 294 }); 295 }); 296} 297 298// Utility functions 299function debounce(func, wait) { 300 let timeout; 301 return function executedFunction(...args) { 302 const later = () => { 303 clearTimeout(timeout); 304 func(...args); 305 }; 306 clearTimeout(timeout); 307 timeout = setTimeout(later, wait); 308 }; 309} 310 311// Loading animation for page 312window.addEventListener('load', function() { 313 document.body.style.opacity = '0'; 314 document.body.style.transition = 'opacity 0.5s ease-in-out'; 315 316 setTimeout(() => { 317 document.body.style.opacity = '1'; 318 }, 100); 319}); 320 321// Keyboard navigation support 322document.addEventListener('keydown', function(e) { 323 // ESC key closes mobile menu 324 if (e.key === 'Escape') { 325 const navMenu = document.querySelector('.nav-menu'); 326 const navToggle = document.querySelector('.nav-toggle'); 327 328 if (navMenu && navMenu.classList.contains('active')) { 329 navMenu.classList.remove('active'); 330 navToggle.classList.remove('active'); 331 } 332 } 333}); 334
335// Intersection Observer for scroll-triggered animations 336const scrollObserver = new IntersectionObserver((entries) => { 337 entries.forEach(entry => { 338 if (entry.isIntersecting) { 339 entry.target.classList.add('animate-in'); 340 } 341 }); 342}, { 343 threshold: 0.1, 344 rootMargin: '0px 0px -100px 0px' 345}); 346 347// Observe elements for animation 348document.querySelectorAll('.step, .feature-card, .pricing-card').forEach(el => { 349 scrollObserver.observe(el); 350}); 351 352// Add CSS for animation classes 353const style = document.createElement('style'); 354style.textContent = ` 355 .animate-in { 356 animation: slideInUp 0.6s ease-out forwards; 357 } 358 359 @keyframes slideInUp { 360 from { 361 opacity: 0; 362 transform: translateY(30px); 363 } 364 to { 365 opacity: 1; 366 transform: translateY(0); 367 } 368 } 369`; 370document.head.appendChild(style); 371 372// Performance optimization: Throttle scroll events 373const throttledScrollHandler = debounce(function() { 374 // Handle scroll events here if needed 375}, 16); // ~60fps 376 377window.addEventListener('scroll', throttledScrollHandler); 378 379// Add ripple effect to buttons 380function addRippleEffect() { 381 const buttons = document.querySelectorAll('.btn'); 382 383 buttons.forEach(button => { 384 button.addEventListener('click', function(e) { 385 const ripple = document.createElement('span'); 386 const rect = this.getBoundingClientRect(); 387 const size = Math.max(rect.width, rect.height); 388 const x = e.clientX - rect.left - size / 2; 389 const y = e.clientY - rect.top - size / 2; 390 391 ripple.style.width = ripple.style.height = size + 'px'; 392 ripple.style.left = x + 'px'; 393 ripple.style.top = y + 'px'; 394 ripple.classList.add('ripple'); 395 396 this.appendChild(ripple); 397 398 setTimeout(() => { 399 ripple.remove(); 400 }, 600); 401 }); 402 }); 403} 404 405// Initialize ripple effects 406addRippleEffect(); 407 408// Add CSS for ripple effect 409const rippleStyle = document.createElement('style'); 410rippleStyle.textContent = ` 411 .btn { 412 position: relative; 413 overflow: hidden; 414 } 415 416 .ripple { 417 position: absolute; 418 border-radius: 50%; 419 background: rgba(255, 255, 255, 0.3); 420 transform: scale(0); 421 animation: ripple 0.6s linear; 422 pointer-events: none; 423 } 424 425 @keyframes ripple { 426 to { 427 transform: scale(4); 428 opacity: 0; 429 } 430 } 431`; 432document.head.appendChild(rippleStyle); 433 434// Console welcome message 435console.log(` 436ðµ Welcome to HumGod! ðµ 437Transform your musical ideas into reality with AI. 438Built with â¤ï¸ for musicians everywhere. 439`); 440 441// Service Worker registration (for future PWA features) 442if ('serviceWorker' in navigator) { 443 window.addEventListener('load', function() { 444 // Service worker will be added in future updates 445 console.log('Service Worker support detected'); 446 }); 447}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.