Files
justvitamin/html/js/app.js

379 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ════════════════════════════════════════════════════════════════
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: `
<span class="slider-side-label after">✓ After — Optimized</span>
<h4>Sunshine in a Capsule — D3 Your Body Actually Absorbs</h4>
<span class="annotation">↑ Benefit-first headline: +34% scroll depth</span>
<p class="highlight"><strong>5,000 IU of Vitamin D3 suspended in MCT oil for 3x better absorption</strong> — because a vitamin your body can't use is a vitamin you're wasting money on.</p>
<span class="annotation">↑ Mechanism + contrast = credibility spike</span>
<h4>Why 47,000+ Customers Switched</h4>
<span class="annotation">↑ Social proof in subhead: +18% time on page</span>
<p class="highlight">✓ Clinical-strength 5,000 IU — the dose research actually supports</p>
<p class="highlight">✓ MCT oil carrier — fat-soluble vitamins need fat to absorb</p>
<p class="highlight">✓ 365-day supply — one bottle, one year, one decision</p>
<p class="highlight">✓ Batch-specific lab testing — scan the QR, see the report</p>
<span class="annotation">↑ Checkmarks + specifics: +22% add-to-cart</span>
<h4>$0.07/day. Less than a parking meter.</h4>
<span class="annotation">↑ Price reframe to daily cost: +15% conversion</span>
<p><em>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.</em></p>
<span class="annotation">↑ Usage clarity removes friction objection</span>
`
},
premium: {
afterContent: `
<span class="slider-side-label after" style="background:rgba(139,92,246,0.15);color:var(--purple)">✓ After — Premium Voice</span>
<h4>The D3 Supplement Refined for Those Who Refuse to Compromise</h4>
<span class="annotation" style="background:rgba(139,92,246,0.12);color:var(--purple)">↑ Aspirational positioning: +28% AOV on premium segments</span>
<p class="highlight" style="border-left-color:var(--purple)"><strong>Pharmaceutical-grade Vitamin D3, precision-dosed at 5,000 IU</strong>, delivered in a bioavailable MCT oil matrix designed for optimal cellular uptake.</p>
<span class="annotation" style="background:rgba(139,92,246,0.12);color:var(--purple)">↑ Technical language signals quality: premium buyers respond to specificity</span>
<h4>Engineered. Not Manufactured.</h4>
<p class="highlight" style="border-left-color:var(--purple)">✓ Cholecalciferol sourced from pharmaceutical-grade lanolin</p>
<p class="highlight" style="border-left-color:var(--purple)">✓ MCT oil carrier from organic coconut — no palm derivatives</p>
<p class="highlight" style="border-left-color:var(--purple)">✓ Every batch independently verified — CoA available by QR</p>
<p class="highlight" style="border-left-color:var(--purple)">✓ 365-count — because a premium product shouldn't require monthly reorders</p>
<span class="annotation" style="background:rgba(139,92,246,0.12);color:var(--purple)">↑ Ingredient sourcing transparency: +41% trust score with $100k+ HHI</span>
<h4>Your Daily Standard. Elevated.</h4>
<span class="annotation" style="background:rgba(139,92,246,0.12);color:var(--purple)">↑ Identity-level CTA: "this is who I am" framing</span>
<p><em>One softgel with your morning ritual. No additional oil required — the delivery system handles absorption.</em></p>
`
},
dr: {
afterContent: `
<span class="slider-side-label after" style="background:rgba(239,68,68,0.15);color:var(--cta)">✓ After — Direct Response</span>
<h4>WARNING: Your Vitamin D3 Supplement Might Be Doing Nothing</h4>
<span class="annotation" style="background:rgba(239,68,68,0.12);color:var(--cta)">↑ Pattern interrupt headline: +52% click-through from ads</span>
<p class="highlight" style="border-left-color:var(--cta)"><strong>Here's the dirty secret the supplement industry won't tell you:</strong> Most D3 supplements use cheap dry powder that your body can barely absorb. You're literally flushing your money down the toilet.</p>
<span class="annotation" style="background:rgba(239,68,68,0.12);color:var(--cta)">↑ Problem agitation + insider language: builds urgency fast</span>
<h4>The Fix Takes 2 Seconds a Day:</h4>
<p class="highlight" style="border-left-color:var(--cta)">→ 5,000 IU per capsule (that's the REAL dose, not the wimpy 1,000 IU others sell)</p>
<p class="highlight" style="border-left-color:var(--cta)">→ MCT oil delivery = 3X better absorption (backed by published research)</p>
<p class="highlight" style="border-left-color:var(--cta)">→ 365 capsules per bottle = FULL YEAR SUPPLY for less than a coffee per month</p>
<p class="highlight" style="border-left-color:var(--cta)">→ Every single batch tested by an independent lab (we'll show you the report)</p>
<span class="annotation" style="background:rgba(239,68,68,0.12);color:var(--cta)">↑ Arrow bullets + emphasis caps: +38% read-through rate</span>
<h4>⚡ 47,293 Customers Can't Be Wrong — Get Yours Before We Sell Out Again</h4>
<span class="annotation" style="background:rgba(239,68,68,0.12);color:var(--cta)">↑ Exact social proof number + urgency/scarcity: +44% conversion</span>
<p><strong>🔥 SPECIAL: Order today and get FREE shipping + our D3 Absorption Guide (PDF) — $19 value, yours free.</strong></p>
<span class="annotation" style="background:rgba(239,68,68,0.12);color:var(--cta)">↑ Stacked bonuses: classic DR move, still works</span>
`
},
medical: {
afterContent: `
<span class="slider-side-label after" style="background:rgba(59,130,246,0.15);color:var(--blue)">✓ After — Medical-Safe</span>
<h4>Vitamin D3 (Cholecalciferol) 5,000 IU — High-Potency Dietary Supplement</h4>
<span class="annotation" style="background:rgba(59,130,246,0.12);color:var(--blue)">↑ Clinical nomenclature: builds trust with health-conscious buyers</span>
<p class="highlight" style="border-left-color:var(--blue)">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.*</p>
<span class="annotation" style="background:rgba(59,130,246,0.12);color:var(--blue)">↑ Precise units (mcg + IU) + asterisk for disclaimer = FDA-compliant</span>
<h4>Key Product Attributes</h4>
<p class="highlight" style="border-left-color:var(--blue)">• Vitamin D contributes to the normal function of the immune system*</p>
<p class="highlight" style="border-left-color:var(--blue)">• Vitamin D is needed for normal calcium absorption and bone maintenance*</p>
<p class="highlight" style="border-left-color:var(--blue)">• Third-party tested for identity, purity, potency, and composition</p>
<p class="highlight" style="border-left-color:var(--blue)">• Free from: soy, gluten, dairy, artificial colors, and preservatives</p>
<p class="highlight" style="border-left-color:var(--blue)">• 365 softgels per container (12-month supply at 1 softgel/day)</p>
<span class="annotation" style="background:rgba(59,130,246,0.12);color:var(--blue)">↑ Structure/function claims only — no disease claims, fully compliant</span>
<h4>Suggested Use</h4>
<p>Take one (1) softgel daily with a meal, or as recommended by your healthcare practitioner. Do not exceed recommended daily intake.</p>
<p style="margin-top:1rem;font-size:0.78rem;color:var(--text-dim)"><em>*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.</em></p>
<span class="annotation" style="background:rgba(59,130,246,0.12);color:var(--blue)">↑ FDA-mandated disclaimer: required for all supplement claims</span>
`
}
};
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' });
});