Analyze Competitive Narratives

See how AI search engines describe your competitors vs your brand

Enter up to 5 competitors
Analyzing... this may take up to 90 seconds.

Executive Summary

Competitor Narratives

fetch('/api/stats').then(function(r){return r.json()}).then(function(d){var ns=document.getElementById('nav-stars');if(ns&&d.github_stars)ns.textContent='★ '+d.github_stars}).catch(function(){}); const $=id=>document.getElementById(id); const $in=$tag=>$('input[type="text"]')&&$.prototype.addEventListener.call(document.querySelector($tag),arguments[1],arguments[2]); $('btn').addEventListener('click',run); $('competitorUrls').addEventListener('keypress',e=>{if(e.key==='Enter')run()}); function isValidUrl(str){ if(/^https?:\/\/.+\..+/.test(str))return true; if(/^[^\s]+\.[a-z]{2,}(\/.*)?$/i.test(str))return true; return false; } async function run(){ const targetUrl=$('targetUrl').value.trim(); const competitorText=$('competitorUrls').value.trim(); if(!targetUrl)return; if(!competitorText)return; if(!isValidUrl(targetUrl)){$('error').textContent='Please enter a valid URL for your website';$('error').style.display='block';return} if(!/^https?:\/\//i.test(targetUrl)){$('targetUrl').value='https://'+targetUrl;targetUrl=$('targetUrl').value} const competitors=competitorText.split(',').map(u=>u.trim()).filter(u=>u&&isValidUrl(u)); if(competitors.length===0){$('error').textContent='Please enter at least one valid competitor URL';$('error').style.display='block';return} $('btn').disabled=true;$('spinner').classList.add('active');$('error').style.display='none';$('results').style.display='none'; try{ const response=await fetch('/api/analyze-competitors',{ method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({target_url:targetUrl,competitor_urls:competitors}) }); if(!response.ok){throw new Error('Analysis failed: '+response.statusText);} const data=await response.json(); render(data); $('results').style.display='block'; if(data.summary){$('summaryText').textContent=data.summary;} }catch(e){$('error').textContent=e.message;$('error').style.display='block'} finally{$('btn').disabled=false;$('spinner').classList.remove('active')} } function render(data){ const competitorList=$('competitorList'); competitorList.textContent=''; data.competitors.forEach(function(c){ const card=document.createElement('div'); card.className='narrative-card'; card.innerHTML='
'+ ''+(c.brand_name||c.url)+'Score '+(c.score||'N/A')+''+ '
'+ '
Adjectives:'+ c.dominant_adjectives.map(a=>''+a+'').join('')+ '
'+ '
Frames:'+ c.key_frames.map(f=>''+f+'').join('')+ '
'+ (c.positioning?'
\"'+c.positioning+'\"
':'')+ (c.credibility_signals.length?'

Credibility Signals

':'')+ (c.content_gaps.length?'

Content Gaps

':''); competitorList.appendChild(card); }); // Render gaps table if available if(data.competitive_gaps&&data.competitive_gaps.length>0){ const gapTableBody=$('gapTableBody'); gapTableBody.textContent=''; $('gapSection').style.display='block'; data.competitive_gaps.forEach(function(g){ const tr=document.createElement('tr'); tr.innerHTML=''+g.category+''+ ''+new URL(g.competitor_url).hostname+''+ ''+g.gap_type+''+ ''+g.target_missing+''+ ''+g.suggested_content+''; gapTableBody.appendChild(tr); }); } } document.getElementById('nav-ham').addEventListener('click',function(){ document.querySelector('.nav-links').classList.toggle('open'); this.textContent=this.textContent==='☰'?'✕':'☰'; }); document.querySelectorAll('.nav-dropdown-toggle').forEach(function(btn){ btn.addEventListener('click',function(e){ e.stopPropagation(); var dd=this.closest('.nav-dropdown'); dd.classList.toggle('open'); this.setAttribute('aria-expanded',dd.classList.contains('open')); }); }); document.addEventListener('click',function(){ document.querySelectorAll('.nav-dropdown.open').forEach(function(dd){ dd.classList.remove('open'); dd.querySelector('.nav-dropdown-toggle').setAttribute('aria-expanded','false'); }); });