Initial: JustVitamin AI Content Engine proposal site with 3 interactive demos

This commit is contained in:
2026-03-02 19:29:47 +08:00
commit 26532ade3c
7 changed files with 2317 additions and 0 deletions

378
html/js/app.js Normal file
View File

@@ -0,0 +1,378 @@
/* ════════════════════════════════════════════════════════════════
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' });
});