PageSourceSearch

https://clipbee.ai/script.js

js clipbee.ai collected 2026-10-05 12:21:44 UTC 19,650 bytes, 628 lines download raw bytes

1// ===================================
2// NAVIGATION
3// ===================================
4
5const navbar = document.querySelector('.navbar');
6const mobileMenuToggle = document.querySelector('.mobile-menu-toggle');
7const navLinks = document.querySelector('.nav-links');
8
9// Sticky navbar on scroll
10window.addEventListener('scroll', () => {
11    if (window.scrollY > 50) {
12        navbar.classList.add('scrolled');
13    } else {
14        navbar.classList.remove('scrolled');
15    }
16});
17
18// Mobile menu toggle
19if (mobileMenuToggle) {
20    mobileMenuToggle.addEventListener('click', () => {
21        navLinks.classList.toggle('active');
22        
23        // Animate hamburger menu
24        const spans = mobileMenuToggle.querySelectorAll('span');
25        if (navLinks.classList.contains('active')) {
26            spans[0].style.transform = 'rotate(45deg) translateY(7px)';
27            spans[1].style.opacity = '0';
28            spans[2].style.transform = 'rotate(-45deg) translateY(-7px)';
29        } else {
30            spans[0].style.transform = 'none';
31            spans[1].style.opacity = '1';
32            spans[2].style.transform = 'none';
33        }
34    });
35}
36
37// Close mobile menu when clicking on a link
38document.querySelectorAll('.nav-links a').forEach(link => {
39    link.addEventListener('click', () => {
40        navLinks.classList.remove('active');
41        const spans = mobileMenuToggle.querySelectorAll('span');
42        spans[0].style.transform = 'none';
43        spans[1].style.opacity = '1';
44        spans[2].style.transform = 'none';
45    });
46});
47
48// ===================================
49// SMOOTH SCROLLING
50// ===================================
51
52document.querySelectorAll('a[href^="#"]').forEach(anchor => {
53    anchor.addEventListener('click', function (e) {
54        const href = this.getAttribute('href');
55        
56        // Ignore empty hash or just # links
57        if (href === '#' || href === '#!') {
58            e.preventDefault();
59            return;
60        }
61        
62        const target = document.querySelector(href);
63        if (target) {
64            e.preventDefault();
65            const offsetTop = target.offsetTop - 80;
66            window.scrollTo({
67                top: offsetTop,
68                behavior: 'smooth'
69            });
70        }
71    });
72});
73
74// ===================================
75// FAQ ACCORDION
76// ===================================
77
78const faqItems = document.querySelectorAll('.faq-item');
79
80faqItems.forEach(item => {
81    const question = item.querySelector('.faq-question');
82    
83    question.addEventListener('click', () => {
84        const isActive = item.classList.contains('active');
85        
86        // Close all FAQ items
87        faqItems.forEach(faq => {
88            faq.classList.remove('active');
89        });
90        
91        // Open clicked item if it wasn't active
92        if (!isActive) {
93            item.classList.add('active');
94        }
95    });
96});
97
98// ===================================
99// SCROLL ANIMATIONS
100// ===================================
101
102const observerOptions = {
103    threshold: 0.1,
104    rootMargin: '0px 0px -100px 0px'
105};
106
107const observer = new IntersectionObserver((entries) => {
108    entries.forEach(entry => {
109        if (entry.isIntersecting) {
110            entry.target.style.opacity = '1';
111            entry.target.style.transform = 'translateY(0)';
112        }
113    });
114}, observerOptions);
115
116// Animate elements on scroll
117const animateOnScroll = document.querySelectorAll(`
118    .benefit-item,
119    .stat-box,
120    .reason-item,
121    .video-type-card,
122    .option-card,
123    .step-card,
124    .showcase-item,
125    .feature-card,
126    .use-case-card,
127    .testimonial-card,
128    .sample-item
129`);
130
131animateOnScroll.forEach(el => {
132    el.style.opacity = '0';
133    el.style.transform = 'translateY(30px)';
134    el.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
135    observer.observe(el);
136});
137
138// ===================================
139// COUNTER ANIMATION
140// ===================================
141
142function animateCounter(element, target, duration = 2000) {
143    const start = 0;
144    const increment = target / (duration / 16);
145    let current = start;
146    
147    const timer = setInterval(() => {
148        current += increment;
149        if (current >= target) {
150            element.textContent = target + '%';
151            clearInterval(timer);
152        } else {
153            element.textContent = Math.floor(current) + '%';
154        }
155    }, 16);
156}
157
158// Animate stat number when visible
159const statNumber = document.querySelector('.stat-number');
160if (statNumber) {
161    const statObserver = new IntersectionObserver((entries) => {
162        entries.forEach(entry => {
163            if (entry.isIntersecting && !entry.target.classList.contains('animated')) {
164                animateCounter(entry.target, 63);
165                entry.target.classList.add('animated');
166            }
167        });
168    }, { threshold: 0.5 });
169    
170    statObserver.observe(statNumber);
171}
172
173// ===================================
174// VIDEO PLAY BUTTONS
175// ===================================
176
177const playButtons = document.querySelectorAll('.play-button, .watch-demo');
178
179function openVideoModal(url) {
180    const modal = document.createElement('div');
181    modal.className = 'video-modal';
182    modal.innerHTML = `
183        <div class="modal-overlay"></div>
184        <div class="modal-content" style="position:relative;max-width:960px;width:92%;aspect-ratio:16/9;background:#000;border-radius:12px;overflow:hidden;">
185            <button class="modal-close" aria-label="Close video" style="position:absolute;top:8px;right:8px;background:rgba(0,0,0,0.6);border:none;color:#fff;font-size:28px;line-height:1;padding:6px 10px;border-radius:8px;cursor:pointer;z-index:2">×</button>
186            <iframe src="${url}" title="Demo video" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen style="width:100%;height:100%"></iframe>
187        </div>
188    `;
189    
190    Object.assign(modal.style, {
191        position: 'fixed',
192        inset: '0',
193        zIndex: '10000',
194        display: 'flex',
195        alignItems: 'center',
196        justifyContent: 'center',
197        background: 'rgba(0,0,0,0.8)'
198    });
199
200    document.body.appendChild(modal);
201
202    const close = () => {
203        modal.style.opacity = '0';
204        setTimeout(() => modal.remove(), 200);
205    };
206
207    modal.querySelector('.modal-overlay')?.addEventListener('click', close);
208    modal.querySelector('.modal-close')?.addEventListener('click', close);
209    document.addEventListener('keydown', function esc(e){
210        if (e.key === 'Escape') { close(); document.removeEventListener('keydown', esc); }
211    });
212}
213
214playButtons.forEach(button => {
215    button.addEventListener('click', (e) => {
216        e.preventDefault();
217        const url = button.getAttribute('data-video');
218        if (url) openVideoModal(url);
219    });
220});
221
222// ===================================
223// FORM VALIDATION (if you add forms later)
224// ===================================
225
226function validateEmail(email) {
227    const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
228    return re.test(email);
229}
230
231// ===================================
232// PRICING BUTTON ACTIONS
233// ===================================
234
235const buyButtons = document.querySelectorAll('.btn-primary');
236
237buyButtons.forEach(button => {
238    if (button.textContent.includes('BUY NOW') || button.textContent.includes('Get ClipBee')) {
239        button.addEventListener('click', (e) => {
240            // Here you would integrate with your payment processor
241            // For now, we'll just prevent the default action if it's not a link
242            if (button.tagName !== 'A') {
243                e.preventDefault();
244                console.log('Payment integration would go here');
245            }
246        });
247    }
248});
249
250// ===================================
251// LAZY LOADING IMAGES
252// ===================================
253
254if ('loading' in HTMLImageElement.prototype) {
255    const images = document.querySelectorAll('img[loading="lazy"]');
256    images.forEach(img => {
257        img.src = img.dataset.src || img.src;
258    });
259} else {
260    // Fallback for browsers that don't support lazy loading
261    const script = document.createElement('script');
262    script.src = 'https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.3.2/lazysizes.min.js';
263    document.body.appendChild(script);
264}
265
266// ===================================
267// PARALLAX EFFECT
268// ===================================
269
270window.addEventListener('scroll', () => {
271    const scrolled = window.pageYOffset;
272    const heroVideo = document.querySelector('.hero-video');
273    
274    if (heroVideo) {
275        heroVideo.style.transform = `translateY(${scrolled * 0.5}px)`;
276    }
277});
278
279// ===================================
280// COUNTDOWN TIMER (Optional - for urgency)
281// ===================================
282
283function startCountdown(duration, displayElement) {
284    let timer = duration;
285    const interval = setInterval(() => {
286        const hours = Math.floor(timer / 3600);
287        const minutes = Math.floor((timer % 3600) / 60);
288        const seconds = timer % 60;
289        
290        if (displayElement) {
291            displayElement.textContent = 
292                `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
293        }
294        
295        if (--timer < 0) {
296            clearInterval(interval);
297            if (displayElement) {
298                displayElement.textContent = 'Offer Expired!';
299            }
300        }
301    }, 1000);
302}
303
304// Example: Start a 24-hour countdown
305// const countdownElement = document.querySelector('.countdown-timer');
306// if (countdownElement) {
307//     startCountdown(24 * 60 * 60, countdownElement);
308// }
309
310// ===================================
311// SAMPLE GALLERY MODAL (Optional)
312// ===================================
313
314const sampleItems = document.querySelectorAll('.sample-item');
315
316sampleItems.forEach(item => {
317    item.addEventListener('click', () => {
318        const imgSrc = item.querySelector('img').src;
319        const label = item.querySelector('.sample-label').textContent;
320        
321        // Create modal
322        const modal = document.createElement('div');
323        modal.className = 'image-modal';
324        modal.innerHTML = `
325            <div class="modal-overlay"></div>
326            <div class="modal-content">
327                <button class="modal-close">&times;</button>
328                <img src="${imgSrc}" alt="${label}">
329                <p>${label}</p>
330            </div>
331        `;
332        
333        // Add styles
334        modal.style.cssText = `
335            position: fixed;
336            top: 0;
337            left: 0;
338            width: 100%;
339            height: 100%;
340            z-index: 10000;
341            display: flex;
342            align-items: center;
343            justify-content: center;
344        `;
345        
346        const overlay = modal.querySelector('.modal-overlay');
347        overlay.style.cssText = `
348            position: absolute;
349            top: 0;
350            left: 0;
351            width: 100%;
352            height: 100%;
353            background: rgba(0, 0, 0, 0.9);
354        `;
355        
356        const content = modal.querySelector('.modal-content');
357        content.style.cssText = `
358            position: relative;
359            max-width: 90%;
360            max-height: 90%;
361            z-index: 1;
362        `;
363        
364        const closeBtn = modal.querySelector('.modal-close');
365        closeBtn.style.cssText = `
366            position: absolute;
367            top: -40px;
368            right: 0;
369            background: none;
370            border: none;
371            color: white;
372            font-size: 40px;
373            cursor: pointer;
374            line-height: 1;
375        `;
376        
377        const img = modal.querySelector('img');
378        img.style.cssText = `
379            max-width: 100%;
380            max-height: 80vh;
381            border-radius: 12px;
382            display: block;
383        `;
384        
385        const p = modal.querySelector('p');
386        p.style.cssText = `
387            color: white;
388            text-align: center;
389            margin-top: 16px;
390            font-size: 1.125rem;
391        `;
392        
393        document.body.appendChild(modal);
394        
395        // Close modal
396        const closeModal = () => {
397            modal.style.opacity = '0';
398            setTimeout(() => modal.remove(), 300);
399        };
400        
401        modal.style.opacity = '0';
402        setTimeout(() => modal.style.opacity = '1', 10);
403        modal.style.transition = 'opacity 0.3s ease';
404        
405        closeBtn.addEventListener('click', closeModal);
406        overlay.addEventListener('click', closeModal);
407        
408        // Close on escape key
409        document.addEventListener('keydown', function escapeHandler(e) {
410            if (e.key === 'Escape') {
411                closeModal();
412                document.removeEventListener('keydown', escapeHandler);
413            }
414        });
415    });
416});
417
418// ===================================
419// FEATURE CARD HOVER EFFECT
420// ===================================
421
422const featureCards = document.querySelectorAll('.feature-card');
423
424featureCards.forEach(card => {
425    card.addEventListener('mouseenter', function() {
426        this.style.transform = 'translateY(-8px) scale(1.02)';
427    });
428    
429    card.addEventListener('mouseleave', function() {
430        this.style.transform = 'translateY(0) scale(1)';
431    });
432});
433
434// ===================================
435// SCROLL TO TOP BUTTON (Optional)
436// ===================================
437
438function createScrollToTop() {
439    const button = document.createElement('button');
440    button.innerHTML = '↑';
441    button.className = 'scroll-to-top';
442    button.style.cssText = `
443        position: fixed;
444        bottom: 40px;
445        right: 40px;
446        width: 50px;
447        height: 50px;
448        border-radius: 50%;
449        background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
450        color: white;
451        border: none;
452        font-size: 24px;
453        cursor: pointer;
454        opacity: 0;
455        pointer-events: none;
456        transition: all 0.3s ease;
457        z-index: 999;
458        box-shadow: 0 4px 20px rgba(99, 102, 241, 0.4);
459    `;
460    
461    document.body.appendChild(button);
462    
463    window.addEventListener('scroll', () => {
464        if (window.pageYOffset > 500) {
465            button.style.opacity = '1';
466            button.style.pointerEvents = 'all';
467        } else {
468            button.style.opacity = '0';
469            button.style.pointerEvents = 'none';
470        }
471    });
472    
473    button.addEventListener('click', () => {
474        window.scrollTo({
475            top: 0,
476            behavior: 'smooth'
477        });
478    });
479    
480    button.addEventListener('mouseenter', function() {
481        this.style.transform = 'scale(1.1) translateY(-4px)';
482        this.style.boxShadow = '0 8px 30px rgba(99, 102, 241, 0.5)';
483    });
484    
485    button.addEventListener('mouseleave', function() {
486        this.style.transform = 'scale(1) translateY(0)';
487        this.style.boxShadow = '0 4px 20px rgba(99, 102, 241, 0.4)';
488    });
489}
490
491// Initialize scroll to top button
492createScrollToTop();
493
494// ===================================
495// TESTIMONIAL ROTATION (Optional)
496// ===================================
497
498function rotateTestimonials() {
499    const testimonials = document.querySelectorAll('.testimonial-card');
500    if (testimonials.length === 0) return;
501    
502    let currentIndex = 0;
503    
504    setInterval(() => {
505        testimonials[currentIndex].style.transform = 'scale(1)';
506        currentIndex = (currentIndex + 1) % testimonials.length;
507        testimonials[currentIndex].style.transform = 'scale(1.05)';
508    }, 3000);
509}
510
511// Uncomment to enable testimonial rotation
512// rotateTestimonials();
513
514// ===================================
515// ANALYTICS TRACKING (Placeholder)
516// ===================================
517
518function trackEvent(category, action, label) {
519    // Google Analytics or other tracking
520    if (typeof gtag !== 'undefined') {
521        gtag('event', action, {
522            'event_category': category,
523            'event_label': label
524        });
525    }
526    
527    console.log(`Event tracked: ${category} - ${action} - ${label}`);
528}
529
530// Track CTA clicks
531document.querySelectorAll('.btn-primary').forEach(btn => {
532    btn.addEventListener('click', () => {
533        const text = btn.textContent.trim();
534        trackEvent('CTA', 'click', text);
535    });
536});
537
538// Track section views
539const sections = document.querySelectorAll('section[id]');
540const sectionObserver = new IntersectionObserver((entries) => {
541    entries.forEach(entry => {
542        if (entry.isIntersecting) {
543            trackEvent('Section View', 'view', entry.target.id);
544        }
545    });
546}, { threshold: 0.5 });
547
548sections.forEach(section => {
549    sectionObserver.observe(section);
550});
551
552// ===================================
553// INITIALIZATION
554// ===================================
555
556document.addEventListener('DOMContentLoaded', () => {
557    console.log('ClipBee Sales Page Loaded Successfully! 🚀');
558    
559    // Add any initialization code here
560    
561    // Smooth reveal on page load
562    document.body.style.opacity = '0';
563    setTimeout(() => {
564        document.body.style.transition = 'opacity 0.5s ease';
565        document.body.style.opacity = '1';
566    }, 100);
567
568    // Progressive enhancement: add loading="lazy" to all images if not present
569    document.querySelectorAll('img').forEach(img => {
570        if (!img.hasAttribute('loading')) {
571            img.setAttribute('loading', 'lazy');
572        }
573    });
574});
575
576// ===================================
577// PERFORMANCE OPTIMIZATION
578// ===================================
579
580// Debounce function for scroll events
581function debounce(func, wait) {
582    let timeout;
583    return function executedFunction(...args) {
584        const later = () => {
585            clearTimeout(timeout);
586            func(...args);
587        };
588        clearTimeout(timeout);
589        timeout = setTimeout(later, wait);
590    };
591}
592
593// Throttle function for scroll events
594function throttle(func, limit) {
595    let inThrottle;
596    return function() {
597        const args = arguments;
598        const context = this;
599        if (!inThrottle) {
600            func.apply(context, args);
601            inThrottle = true;
602            setTimeout(() => inThrottle = false, limit);
603        }
604    };
605}
606
607// Use throttled scroll handler for better performance
608window.addEventListener('scroll', throttle(() => {
609    // Sticky CTA visibility
610    const sticky = document.querySelector('.sticky-cta');
611    const pricing = document.querySelector('#pricing');
612    const hero = document.querySelector('.hero');
613    if (!sticky || !pricing || !hero) return;
614
615    const heroBottom = hero.getBoundingClientRect().bottom;
616    const pricingTop = pricing.getBoundingClientRect().top;
617    const viewportHeight = window.innerHeight;
618
619    // Show sticky CTA after hero leaves, hide when pricing is in view
620    if (heroBottom < 0 && pricingTop > viewportHeight * 0.5) {
621        sticky.classList.add('visible');
622    } else {
623        sticky.classList.remove('visible');
624    }
625}, 100));
626
627console.log('ClipBee - The Future of AI Stock Footage Generation 🎬');
628

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.