/* JustVitamin × QuikCue — Live AI Demos */
const $ = s => document.querySelector(s);
const esc = s => { if (!s) return ''; const d = document.createElement('div'); d.textContent = s; return d.innerHTML; };
function setStep(id, cls, text) {
const el = $(id);
if (!el) return;
el.parentElement.className = `step ${cls}`;
el.innerHTML = text;
}
function setBtn(id, loading, text) {
const btn = $(id);
btn.disabled = loading;
btn.classList.toggle('loading', loading);
btn.textContent = text;
}
// ═══════════════════════════════════════════════════════════════
// DEMO A — One Product → 12 Assets + Images
// ═══════════════════════════════════════════════════════════════
let demoA_product = null;
async function runDemoA() {
const url = $('#demoA-url').value.trim();
if (!url) return;
setBtn('#demoA-btn', true, 'Working...');
$('#demoA-output').classList.add('hidden');
['#a-s1','#a-s2','#a-s3'].forEach(s => setStep(s, '', 'Waiting'));
// Step 1: Scrape
setStep('#a-s1', 'active', ' Scraping...');
try {
const r = await fetch('/api/scrape', {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify({url})
});
const product = await r.json();
if (product.error) throw new Error(product.error);
demoA_product = product;
setStep('#a-s1', 'done', `✓ ${esc(product.title.substring(0,35))}...`);
} catch(e) {
setStep('#a-s1', 'error', `✗ ${esc(e.message)}`);
setBtn('#demoA-btn', false, '🔴 Generate the Whole Pack');
return;
}
// Step 2: AI pack + Step 3: Images (parallel)
setStep('#a-s2', 'active', ' Gemini generating 12 assets...');
setStep('#a-s3', 'active', ' Nano Banana generating images...');
const packP = fetch('/api/generate-pack', {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify(demoA_product)
}).then(r => r.json());
const imgP = fetch('/api/generate-images', {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify(demoA_product)
}).then(r => r.json());
// Handle pack
try {
const pack = await packP;
if (pack.error) throw new Error(pack.error);
setStep('#a-s2', 'done', `✓ 12 assets generated (${pack._generation_time})`);
renderAssetPack(pack);
$('#demoA-output').classList.remove('hidden');
} catch(e) {
setStep('#a-s2', 'error', `✗ ${esc(e.message)}`);
}
// Handle images
try {
const imgs = await imgP;
if (imgs.error) throw new Error(imgs.error);
setStep('#a-s3', 'done', `✓ Images generated (${imgs._generation_time})`);
renderImages(imgs);
} catch(e) {
setStep('#a-s3', 'error', `✗ ${esc(e.message)}`);
}
setBtn('#demoA-btn', false, '✓ Done — Run Again');
}
function renderAssetPack(pack) {
const meta = $('#demoA-meta');
meta.innerHTML = `
🧠 Model: Gemini 2.5 Flash
⏱ Time: ${esc(pack._generation_time)}
📦 Product: ${esc(pack._product_title)}
`;
const grid = $('#demoA-assets');
let cards = '';
let n = 0;
// Hero angles
(pack.hero_angles || []).forEach((a, i) => {
n++;
cards += assetCard('hero', `Hero Angle`, n, `"${esc(a.headline)}"
Target: ${esc(a.target_desire)} Best for: ${esc(a.best_for)} `, i*80);
});
// PDP Copy
if (pack.pdp_copy) {
n++;
const bullets = (pack.pdp_copy.bullets||[]).map(b => `${esc(b)} `).join('');
cards += assetCard('pdp', 'PDP Copy', n, `${esc(pack.pdp_copy.headline)} `, n*80);
n++;
const faqs = (pack.pdp_copy.faq||[]).map(f => `Q: ${esc(f.q)} A: ${esc(f.a)} `).join('');
cards += assetCard('pdp', 'FAQ Block', n, faqs, n*80);
}
// Ad hooks
if (pack.ad_hooks) {
n++;
const hooks = pack.ad_hooks.map(h => `${esc(h)} `).join('');
cards += assetCard('ad', '5 Ad Hooks', n, ``, n*80);
}
// Email subjects
if (pack.email_subjects) {
n++;
const emails = pack.email_subjects.map(e => `${esc(e.subject)} ${esc(e.preview)} `).join('');
cards += assetCard('email', 'Email Subjects', n, emails, n*80);
}
// TikTok
if (pack.tiktok_script) {
n++;
const t = pack.tiktok_script;
cards += assetCard('video', 'TikTok Script', n, `
${esc(t.title)}
[0-3s] ${esc(t.hook_0_3s)}
[3-12s] ${esc(t.body_3_12s)}
[12-15s] ${esc(t.cta_12_15s)}
${esc(t.why_it_works)}
`, n*80);
}
// Blog
if (pack.blog_outline) {
n++;
const b = pack.blog_outline;
const secs = (b.sections||[]).map(s => `${esc(s)} `).join('');
cards += assetCard('blog', 'Blog Outline', n, `
${esc(b.title)}
SEO: "${esc(b.seo_keyword)}" — ${esc(b.monthly_searches)} mo/searches
`, n*80);
}
// Meta SEO
if (pack.meta_seo) {
n++;
const m = pack.meta_seo;
cards += assetCard('seo', 'Meta SEO', n, `
Title: ${esc(m.title)}
Description: ${esc(m.description)}
${m.title_chars || '?'} chars title / ${m.desc_chars || '?'} chars desc
`, n*80);
}
// Alt text
if (pack.alt_text) {
n++;
const alts = pack.alt_text.map(a => `${esc(a.image_type)}: Alt: ${esc(a.alt)} File: ${esc(a.filename)} `).join('');
cards += assetCard('a11y', 'Alt Text + Filenames', n, alts, n*80);
}
// A/B Variants
if (pack.ab_variants) {
n++;
const vars = pack.ab_variants.map(v => `${esc(v.label)}: ${esc(v.copy)} `).join('');
cards += assetCard('ad', 'A/B Variants', n, vars + 'Test all — let data pick the winner ', n*80);
}
grid.innerHTML = cards;
}
function assetCard(type, label, num, content, delay) {
return `
${label} #${num}
${content}
`;
}
function renderImages(imgs) {
const grid = $('#demoA-img-grid');
const section = $('#demoA-images');
let html = '';
const styles = [
{key:'hero', label:'Hero Banner', desc:'Nano Banana Pro', wide:true},
{key:'lifestyle', label:'Lifestyle Shot', desc:'Nano Banana'},
{key:'benefits', label:'Benefits Visual', desc:'Nano Banana Pro'},
];
styles.forEach(s => {
const data = imgs[s.key];
if (data && data.filename) {
html += `
${s.label} — ${s.desc} · ${data.model||''}
`;
}
});
if (html) {
grid.innerHTML = html;
section.classList.remove('hidden');
}
}
// ═══════════════════════════════════════════════════════════════
// DEMO B — Competitor X-Ray
// ═══════════════════════════════════════════════════════════════
async function runDemoB() {
const url = $('#demoB-url').value.trim();
if (!url) return;
setBtn('#demoB-btn', true, 'Scanning...');
$('#demoB-output').classList.add('hidden');
setStep('#b-s1', 'active', ' Scraping competitor...');
setStep('#b-s2', '', 'Waiting');
try {
const r = await fetch('/api/competitor-xray', {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify({url})
});
const data = await r.json();
if (data.error) throw new Error(data.error);
setStep('#b-s1', 'done', `✓ Scraped: ${esc((data._scrape_data?.title||'').substring(0,30))}...`);
setStep('#b-s2', 'done', `✓ Analysis complete (${data._generation_time})`);
renderXray(data);
$('#demoB-output').classList.remove('hidden');
setBtn('#demoB-btn', false, '✓ Done — Try Another');
} catch(e) {
setStep('#b-s1', 'error', `✗ ${esc(e.message)}`);
setBtn('#demoB-btn', false, '🔍 X-Ray This Competitor');
}
}
function renderXray(data) {
// Left — competitor analysis
const tactics = (data.top_5_tactics||[]).map(t =>
`${esc(t.tactic)} — ${esc(t.explanation)} `
).join('');
$('#demoB-left').innerHTML = `
❌ ${esc(data.competitor_name || 'Competitor')}
What they're really selling
${esc(data.what_theyre_selling)}
Top 5 Persuasion Tactics
${tactics}
Weakest Claim / Gap
⚠️ ${esc(data.weakest_claim)}
`;
// Right — JV improved
const hero = data.jv_hero_section || {};
const diffs = (data.differentiators||[]).map(d =>
`🎯 ${esc(d.point)} — ${esc(d.proof_idea)} `
).join('');
const donts = (data.do_not_say||[]).map(d => `${esc(d)} `).join('');
$('#demoB-right').innerHTML = `
✓ Just Vitamins — Upgraded
${esc(hero.headline)}
${esc(hero.body)}
${esc(hero.value_prop)}
3 Differentiators + Proof Ideas
⚠️ Do Not Say — Compliance
`;
}
// ═══════════════════════════════════════════════════════════════
// DEMO C — PDP Surgeon
// ═══════════════════════════════════════════════════════════════
let demoC_product = null;
let demoC_cache = {};
async function runDemoC() {
const url = $('#demoC-url').value.trim();
if (!url) return;
setBtn('#demoC-btn', true, 'Working...');
$('#demoC-output').classList.add('hidden');
demoC_cache = {};
setStep('#c-s1', 'active', ' Scraping product...');
setStep('#c-s2', '', 'Waiting');
// Step 1: Scrape
try {
const r = await fetch('/api/scrape', {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify({url})
});
const product = await r.json();
if (product.error) throw new Error(product.error);
demoC_product = product;
setStep('#c-s1', 'done', `✓ ${esc(product.title.substring(0,35))}...`);
} catch(e) {
setStep('#c-s1', 'error', `✗ ${esc(e.message)}`);
setBtn('#demoC-btn', false, '🎨 Scrape & Rewrite');
return;
}
// Step 2: AI rewrite (default style)
const active = document.querySelector('#demoC-toggles .toggle.active');
const style = active?.dataset.style || 'balanced';
await rewriteStyle(style);
setBtn('#demoC-btn', false, '✓ Done — Change URL');
}
async function switchDemoC(style, btn) {
document.querySelectorAll('#demoC-toggles .toggle').forEach(t => t.classList.remove('active'));
btn.classList.add('active');
if (!demoC_product) return;
await rewriteStyle(style);
}
async function rewriteStyle(style) {
if (demoC_cache[style]) {
renderSurgeon(demoC_product, demoC_cache[style]);
return;
}
setStep('#c-s2', 'active', ` Gemini rewriting as ${style}...`);
try {
const r = await fetch('/api/pdp-surgeon', {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify({product: demoC_product, style})
});
const result = await r.json();
if (result.error) throw new Error(result.error);
demoC_cache[style] = result;
setStep('#c-s2', 'done', `✓ ${style} rewrite (${result._generation_time})`);
renderSurgeon(demoC_product, result);
$('#demoC-output').classList.remove('hidden');
} catch(e) {
setStep('#c-s2', 'error', `✗ ${esc(e.message)}`);
}
}
function renderSurgeon(product, result) {
// Left — current
const bullets = (product.benefits||[]).map(b => `${esc(b)} `).join('');
$('#demoC-left').innerHTML = `
✕ Current PDP
${esc(product.title)}
${esc(product.subtitle)}
${esc(product.price)} ${esc(product.quantity)}
${bullets.replace(//g, ' ')}
${esc((product.description||'').substring(0,400))}...
`;
// Right — rewritten
const rBullets = (result.bullets||[]).map(b =>
`${esc(b.text)}
↑ ${esc(b.annotation)} `
).join('');
$('#demoC-right').innerHTML = `
✓ AI-Rewritten — ${esc(result.style||'balanced').toUpperCase()}
${esc(result.title)}
↑ SEO-optimised title
${esc(result.subtitle)}
${esc(result.hero_copy)}
↑ ${esc(result.hero_annotation)}
${rBullets}
⭐ ${esc(result.social_proof)}
↑ ${esc(result.social_proof_annotation)}
${esc(result.price_reframe)}
↑ ${esc(result.price_annotation)}
${esc(result.usage_instruction)}
↑ ${esc(result.usage_annotation)}
${esc(result.cta_text || 'Add to Basket')}
`;
}
// Smooth scroll nav
document.querySelectorAll('a[href^="#"]').forEach(a => {
a.addEventListener('click', e => {
const t = document.querySelector(a.getAttribute('href'));
if (t) { e.preventDefault(); t.scrollIntoView({behavior:'smooth'}); }
});
});