/* ════════════════════════════════════════════════════════════════ JustVitamin × QuikCue — Interactive Demos & Animations ════════════════════════════════════════════════════════════════ */ // ── Intersection Observer: Fade-in on scroll ── document.addEventListener('DOMContentLoaded', () => { const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('visible'); observer.unobserve(entry.target); // Trigger chart bars const bars = entry.target.querySelectorAll('.chart-bar-fill[data-width]'); bars.forEach((bar, i) => { setTimeout(() => { bar.style.width = bar.dataset.width + '%'; }, i * 150); }); // Trigger stat counters const counters = entry.target.querySelectorAll('[data-count]'); counters.forEach(el => animateCounter(el)); } }); }, { threshold: 0.15, rootMargin: '0px 0px -50px 0px' }); document.querySelectorAll('.animate-on-scroll').forEach(el => observer.observe(el)); // ── Animate hero stats on load ── setTimeout(() => { document.querySelectorAll('.hero-stat-value[data-count]').forEach(el => animateCounter(el)); }, 500); // ── Initialize slider ── initSlider(); // ── Smooth scroll for nav links ── document.querySelectorAll('a[href^="#"]').forEach(link => { link.addEventListener('click', (e) => { const target = document.querySelector(link.getAttribute('href')); if (target) { e.preventDefault(); target.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); }); // ── Active nav tracking ── const sections = document.querySelectorAll('section[id]'); const navLinks = document.querySelectorAll('.nav-links a'); const navObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const id = entry.target.getAttribute('id'); navLinks.forEach(link => { link.classList.toggle('active', link.getAttribute('href') === '#' + id); }); } }); }, { threshold: 0.3, rootMargin: '-80px 0px -50% 0px' }); sections.forEach(sec => navObserver.observe(sec)); }); // ── Counter Animation ── function animateCounter(el) { const target = parseInt(el.dataset.count); const suffix = el.dataset.suffix || ''; const prefix = el.dataset.prefix || ''; const duration = 1200; const start = performance.now(); if (target === 0) { el.textContent = prefix + '$0' + suffix; return; } function tick(now) { const elapsed = now - start; const progress = Math.min(elapsed / duration, 1); const eased = 1 - Math.pow(1 - progress, 3); // ease-out cubic const current = Math.round(target * eased); el.textContent = prefix + current.toLocaleString() + suffix; if (progress < 1) requestAnimationFrame(tick); } requestAnimationFrame(tick); } // ════════════════════════════════════════════════════════════════ // DEMO A: One Product → 12 Assets // ════════════════════════════════════════════════════════════════ function runDemoA() { const btn = document.getElementById('demoA-btn'); const output = document.getElementById('demoA-output'); const counter = document.getElementById('demoA-count'); const timer = document.getElementById('demoA-timer'); const download = document.getElementById('demoA-download'); const cards = document.querySelectorAll('#demoA-assets .asset-card'); // Loading state btn.classList.add('loading'); btn.textContent = 'Generating...'; output.classList.add('active'); download.classList.remove('active'); // Reset cards cards.forEach(c => c.classList.remove('revealed')); let count = 0; counter.textContent = '0'; const startTime = performance.now(); // Update timer const timerInterval = setInterval(() => { const elapsed = ((performance.now() - startTime) / 1000).toFixed(1); timer.textContent = elapsed + 's'; }, 100); // Reveal cards one by one const revealNext = (index) => { if (index >= cards.length) { // Done! clearInterval(timerInterval); const totalTime = ((performance.now() - startTime) / 1000).toFixed(1); timer.textContent = totalTime + 's ✓'; btn.classList.remove('loading'); btn.textContent = '✓ Pack Generated — Run Again?'; btn.style.background = 'var(--accent)'; btn.style.color = '#050a08'; download.classList.add('active'); // Scroll to download setTimeout(() => { download.scrollIntoView({ behavior: 'smooth', block: 'center' }); }, 300); return; } const card = cards[index]; const delay = 150 + Math.random() * 200; // Varied timing feels more "real" setTimeout(() => { card.classList.add('revealed'); count++; counter.textContent = count; // Scroll card into view card.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); revealNext(index + 1); }, delay); }; // Start reveal after "thinking" pause setTimeout(() => revealNext(0), 800); } // ════════════════════════════════════════════════════════════════ // DEMO B: Competitor X-Ray // ════════════════════════════════════════════════════════════════ function runDemoB() { const btn = document.getElementById('demoB-btn'); const output = document.getElementById('demoB-output'); const left = document.getElementById('demoB-left'); const right = document.getElementById('demoB-right'); // Loading state btn.classList.add('loading'); btn.textContent = 'Scanning...'; btn.style.background = '#1e40af'; // Reset left.classList.remove('revealed'); right.classList.remove('revealed'); setTimeout(() => { output.style.display = 'grid'; output.scrollIntoView({ behavior: 'smooth', block: 'start' }); // Reveal left side first setTimeout(() => { left.classList.add('revealed'); // Then right side setTimeout(() => { right.classList.add('revealed'); btn.classList.remove('loading'); btn.textContent = '✓ X-Ray Complete — Try Another'; btn.style.background = 'var(--accent)'; btn.style.color = '#050a08'; }, 800); }, 600); }, 1200); } // ════════════════════════════════════════════════════════════════ // DEMO C: Before/After Slider // ════════════════════════════════════════════════════════════════ function initSlider() { const wrapper = document.getElementById('slider-wrapper'); const handle = document.getElementById('slider-handle'); const after = document.getElementById('slider-after'); if (!wrapper || !handle || !after) return; let isDragging = false; const setPosition = (x) => { const rect = wrapper.getBoundingClientRect(); let percentage = ((x - rect.left) / rect.width) * 100; percentage = Math.max(5, Math.min(95, percentage)); handle.style.left = percentage + '%'; after.style.clipPath = `inset(0 ${100 - percentage}% 0 0)`; }; // Mouse events handle.addEventListener('mousedown', (e) => { isDragging = true; e.preventDefault(); }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; setPosition(e.clientX); }); document.addEventListener('mouseup', () => { isDragging = false; }); // Touch events handle.addEventListener('touchstart', (e) => { isDragging = true; e.preventDefault(); }); document.addEventListener('touchmove', (e) => { if (!isDragging) return; setPosition(e.touches[0].clientX); }); document.addEventListener('touchend', () => { isDragging = false; }); // Click anywhere on the wrapper to move slider wrapper.addEventListener('click', (e) => { setPosition(e.clientX); }); } // ── Style Toggle for Demo C ── const styleVariants = { default: { afterTitle: "Sunshine in a Capsule — D3 Your Body Actually Absorbs", afterContent: ` ✓ After — Optimized

Sunshine in a Capsule — D3 Your Body Actually Absorbs

↑ Benefit-first headline: +34% scroll depth

5,000 IU of Vitamin D3 suspended in MCT oil for 3x better absorption — because a vitamin your body can't use is a vitamin you're wasting money on.

↑ Mechanism + contrast = credibility spike

Why 47,000+ Customers Switched

↑ Social proof in subhead: +18% time on page

✓ Clinical-strength 5,000 IU — the dose research actually supports

✓ MCT oil carrier — fat-soluble vitamins need fat to absorb

✓ 365-day supply — one bottle, one year, one decision

✓ Batch-specific lab testing — scan the QR, see the report

↑ Checkmarks + specifics: +22% add-to-cart

$0.07/day. Less than a parking meter.

↑ Price reframe to daily cost: +15% conversion

Take one softgel each morning with breakfast. The MCT oil means you don't need to pair it with a fatty meal — it's built in.

↑ Usage clarity removes friction objection ` }, premium: { afterContent: ` ✓ After — Premium Voice

The D3 Supplement Refined for Those Who Refuse to Compromise

↑ Aspirational positioning: +28% AOV on premium segments

Pharmaceutical-grade Vitamin D3, precision-dosed at 5,000 IU, delivered in a bioavailable MCT oil matrix designed for optimal cellular uptake.

↑ Technical language signals quality: premium buyers respond to specificity

Engineered. Not Manufactured.

✓ Cholecalciferol sourced from pharmaceutical-grade lanolin

✓ MCT oil carrier from organic coconut — no palm derivatives

✓ Every batch independently verified — CoA available by QR

✓ 365-count — because a premium product shouldn't require monthly reorders

↑ Ingredient sourcing transparency: +41% trust score with $100k+ HHI

Your Daily Standard. Elevated.

↑ Identity-level CTA: "this is who I am" framing

One softgel with your morning ritual. No additional oil required — the delivery system handles absorption.

` }, dr: { afterContent: ` ✓ After — Direct Response

WARNING: Your Vitamin D3 Supplement Might Be Doing Nothing

↑ Pattern interrupt headline: +52% click-through from ads

Here's the dirty secret the supplement industry won't tell you: Most D3 supplements use cheap dry powder that your body can barely absorb. You're literally flushing your money down the toilet.

↑ Problem agitation + insider language: builds urgency fast

The Fix Takes 2 Seconds a Day:

→ 5,000 IU per capsule (that's the REAL dose, not the wimpy 1,000 IU others sell)

→ MCT oil delivery = 3X better absorption (backed by published research)

→ 365 capsules per bottle = FULL YEAR SUPPLY for less than a coffee per month

→ Every single batch tested by an independent lab (we'll show you the report)

↑ Arrow bullets + emphasis caps: +38% read-through rate

⚡ 47,293 Customers Can't Be Wrong — Get Yours Before We Sell Out Again

↑ Exact social proof number + urgency/scarcity: +44% conversion

🔥 SPECIAL: Order today and get FREE shipping + our D3 Absorption Guide (PDF) — $19 value, yours free.

↑ Stacked bonuses: classic DR move, still works ` }, medical: { afterContent: ` ✓ After — Medical-Safe

Vitamin D3 (Cholecalciferol) 5,000 IU — High-Potency Dietary Supplement

↑ Clinical nomenclature: builds trust with health-conscious buyers

Each softgel provides 5,000 IU (125 mcg) of Vitamin D3 as cholecalciferol, delivered in a medium-chain triglyceride (MCT) oil base to support bioavailability.*

↑ Precise units (mcg + IU) + asterisk for disclaimer = FDA-compliant

Key Product Attributes

• Vitamin D contributes to the normal function of the immune system*

• Vitamin D is needed for normal calcium absorption and bone maintenance*

• Third-party tested for identity, purity, potency, and composition

• Free from: soy, gluten, dairy, artificial colors, and preservatives

• 365 softgels per container (12-month supply at 1 softgel/day)

↑ Structure/function claims only — no disease claims, fully compliant

Suggested Use

Take one (1) softgel daily with a meal, or as recommended by your healthcare practitioner. Do not exceed recommended daily intake.

*These statements have not been evaluated by the Food and Drug Administration. This product is not intended to diagnose, treat, cure, or prevent any disease.

↑ FDA-mandated disclaimer: required for all supplement claims ` } }; function switchStyle(style, clickedBtn) { // Update toggle buttons document.querySelectorAll('#demoC-toggles .toggle-btn').forEach(btn => { btn.classList.remove('active'); }); clickedBtn.classList.add('active'); // Update after content const afterCopy = document.getElementById('after-copy'); const variant = styleVariants[style]; if (variant && afterCopy) { afterCopy.innerHTML = variant.afterContent; } } // ── Keyboard shortcut: press 1/2/3 to jump to demos ── document.addEventListener('keydown', (e) => { if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return; if (e.key === '1') document.getElementById('demo-a')?.scrollIntoView({ behavior: 'smooth' }); if (e.key === '2') document.getElementById('demo-b')?.scrollIntoView({ behavior: 'smooth' }); if (e.key === '3') document.getElementById('demo-c')?.scrollIntoView({ behavior: 'smooth' }); });