FRGMNTGet in touch
Menu
FRGMNT / B2B NEWSLETTER INTELLIGENCE

Find newsletters that reach your buyers.

Explore B2B newsletter audiences, publisher rates, and sponsorship opportunities in one place.

01 / DISCOVER

Who are you trying to reach?

Select an audience, explore topics, or browse all publications.

AUDIENCE
TOPICS

Publisher directory

86 publications · Select a card to inspect the evidence
0 publications saved
FRGMNT / RESEARCH ACCESS

Unlock the publisher research.

Get free access to publisher pricing, audience insights, and research tools to make more informed media decisions.

Your email enables access and saved shortlists. No marketing subscription. Access & privacy ↗︎

We’ll email you a one-time access link.

+Number(p.impression_cpm).toLocaleString('en-US')+' CPM';if(!(Number(p.price)>0))return 'Custom pricing';const currency=p.currency||'USD';const symbol=currency==='EUR'?'€':currency==='USD'?'$':currency+' ';return symbol+Number(p.price).toLocaleString('en-US',{maximumFractionDigits:0})}; const rows=[...r.placements].sort((a,b)=>(priority[tierFor(a,r)]??6)-(priority[tierFor(b,r)]??6)||(Number(b.price)||0)-(Number(a.price)||0)).map(p=>`
${esc(tierFor(p,r))}${esc(cleanSourceLabel(p.name)||'Placement details unavailable')}
${esc(moneyFor(p))}
`).join(''); return (packages.length?'
'+(packages.length===r.placements.length?'Package pricing · multi-send or term commitments':'Additional multi-issue and custom packages')+'
':'')+rows; } const price=r=>Math.min(...r.placements.filter(p=>typeof p.price==='number'&&p.currency==='USD').map(p=>p.price)); function populateCategories(){const cats=[...new Set(DATA.map(d=>d.category))].sort();const el=$('category');el.querySelectorAll('option:not([value=""])').forEach(o=>o.remove());cats.forEach(c=>{let o=document.createElement('option');o.value=c;o.textContent=c;el.appendChild(o)})} const AUDIENCES=[{id:'ai',label:'AI & Tech',categories:['AI & Tech']},{id:'dev',label:'Developers & engineering',categories:['Developers']},{id:'security',label:'Cybersecurity & IT',categories:['Security & IT']},{id:'marketing',label:'Marketing, product & design',categories:['Marketing & Product']},{id:'founders',label:'Founders & startups',categories:['Business & Leadership']},{id:'finance',label:'Finance & operations',categories:['Business & Leadership']},{id:'business',label:'Business professionals',categories:['Business & Leadership','AI & Tech']}]; const INTERESTS=['AI & machine learning','Developer tools & infrastructure','Cybersecurity & IT','Startups & venture','Marketing & product','Data & analytics','Finance & fintech','Business & markets']; let audiencesSelected=new Set(),interestsSelected=new Set(),guidedActive=false; function audienceMatch(r,a){if(Array.isArray(r.audience_tags)&&r.audience_tags.includes(a.id))return true;if(a.id==='ai')return r.category==='AI & Tech';if(a.id==='founders')return /founder|startup|venture|entrepreneur|saas/i.test(r.name+' '+r.focus);if(a.id==='finance')return /finance|fintech|cfo|revenue operations/i.test(r.name+' '+r.focus);if(a.id==='business')return ['Business & Leadership','AI & Tech'].includes(r.category)&&!(/founder|startup|venture|fintech|crypto|cfo/i.test(r.name+' '+r.focus));return a.categories.includes(r.category)} function matchScore(r){let n=0;for(const a of AUDIENCES.filter(a=>audiencesSelected.has(a.id)))if(audienceMatch(r,a))n+=5;return n} function interestScore(r){const x=r.content_interests||{};return interestsSelected.has(x.primary)?2:interestsSelected.has(x.secondary)?1:0} function matchReason(r){const c=AUDIENCES.filter(a=>audiencesSelected.has(a.id)&&audienceMatch(r,a));return c.length?'Audience match: '+c.map(a=>a.label).join(', '):'Explore publisher-reported audience details to verify fit.'} function interestsRender(){$('interestChips').innerHTML=INTERESTS.map((name,i)=>``).join('');$('interestStatus').textContent=interestsSelected.size?interestsSelected.size+' interest'+(interestsSelected.size===1?'':'s')+' selected':'All content interests'} function scaleMatch(r){let n=r.subscribers;if(!Number.isFinite(n))return false;return $('guidedScale').value==='niche'?n<50000:$('guidedScale').value==='mid'?n>=50000&&n<500000:n>=500000} function budgetMatch(r){const p=price(r),v=$('guidedBudget').value;if(!Number.isFinite(p))return false;return v==='under10'?p<10000:v==='10to25'?p>=10000&&p<=25000:p>25000} function chipsRender(){$('audienceChips').innerHTML=AUDIENCES.map(a=>``).join('');$('audStatus').textContent=audiencesSelected.size?audiencesSelected.size+' audience'+(audiencesSelected.size===1?'':'s')+' selected':'Showing all audiences';} $('audienceChips').onclick=e=>{let b=e.target.closest('[data-aud]');if(!b)return;audiencesSelected.has(b.dataset.aud)?audiencesSelected.delete(b.dataset.aud):audiencesSelected.add(b.dataset.aud);guidedActive=audiencesSelected.size>0;chipsRender();render()}; $('clearAud').onclick=()=>{audiencesSelected.clear();interestsSelected.clear();guidedActive=false;chipsRender();interestsRender();render()}; $('interestChips').onclick=e=>{const b=e.target.closest('[data-interest]');if(!b)return;const name=INTERESTS[Number(b.dataset.interest)];interestsSelected.has(name)?interestsSelected.delete(name):interestsSelected.add(name);interestsRender();render()}; interestsRender(); $('browseAll').onclick=()=>{audiencesSelected.clear();interestsSelected.clear();guidedActive=false;chipsRender();interestsRender();render();$('resultsAnchor').scrollIntoView({behavior:'smooth',block:'start'})}; $('directoryScale').addEventListener('change',render);$('completeOnly').addEventListener('change',render); chipsRender(); let directoryView='cards'; let directorySort={key:'publication',direction:'asc'}; const directoryPrice=r=>{const values=r.placements.map(p=>Number(p.price)).filter(v=>Number.isFinite(v)&&v>0);return values.length?Math.min(...values):null}; const directoryCompare=(a,b)=>{const key=directorySort.key;let av,bv;if(key==='publication'){av=a.name;bv=b.name}else if(key==='publisher'){av=a.publisher;bv=b.publisher}else if(key==='audience'){av=a.category;bv=b.category}else if(key==='subscribers'){av=Number(a.subscribers)||null;bv=Number(b.subscribers)||null}else if(key==='open'){av=Number(a.open_rate)||null;bv=Number(b.open_rate)||null}else if(key==='price'){av=directoryPrice(a);bv=directoryPrice(b)}else{av=a.placements.length;bv=b.placements.length}const missingA=av===null||av===undefined,missingB=bv===null||bv===undefined;if(missingA!==missingB)return missingA?1:-1;const cmp=typeof av==='string'?av.localeCompare(bv):((av||0)-(bv||0));return (directorySort.direction==='asc'?1:-1)*cmp||a.name.localeCompare(b.name)}; let lastShortlistAction={id:null,time:0}; function toggleShortlist(id){const now=Date.now();if(lastShortlistAction.id===id&&now-lastShortlistAction.time<450)return false;lastShortlistAction={id,time:now};if(selected.has(id)){selected.delete(id)}else if(!explorerVerified&&selected.size>=2){openAccessGate();$('accessNote').textContent='Verify your email to save more than two publications. Your current selections will remain available.';return false}else{selected.add(id)}render();persistShortlist();return true;} // Keep audience selection distinct from directory search and refinements. $('resultsAnchor').insertAdjacentElement('afterend',$('discoverToolbar')); $('cardView').onclick=()=>{directoryView='cards';render()}; $('tableView').onclick=()=>{directoryView='table';render()}; $('directoryTable').addEventListener('click',e=>{const sort=e.target.closest('[data-directory-sort]');const save=e.target.closest('[data-table-save]');const detail=e.target.closest('[data-table-open]');if(sort){const key=sort.dataset.directorySort;directorySort={key,direction:directorySort.key===key&&directorySort.direction==='asc'?'desc':'asc'};render();return}if(save){toggleShortlist(Number(save.dataset.tableSave));return}if(detail)openDetail(Number(detail.dataset.tableOpen))}); function render(){let q=$('search').value.toLowerCase().trim(),cat=$('category').value,b=Number($('budget').value),sort=$('sort').value;let rows=DATA.filter(r=>(!q||[r.name,r.publisher,r.focus,r.category,r.functions].join(' ').toLowerCase().includes(q))&&(!cat||r.category===cat)&&(!b||price(r)<=b)&&(!$('completeOnly').checked||(Number(r.subscribers)>0||r.placements.some(p=>typeof p.price==='number'&&p.price>0)))&&(!savedOnly||selected.has(r.id))&&(!guidedActive||audiencesSelected.size===0||matchScore(r)>0)&&(!interestsSelected.size||interestScore(r)>0)&&(!$('directoryScale').value||((r.subscribers||0)>0&&($('directoryScale').value==='niche'?r.subscribers<50000:$('directoryScale').value==='mid'?r.subscribers>=50000&&r.subscribers<500000:r.subscribers>=500000)))&&true);rows.sort(directoryView==='table'?directoryCompare:(a,b)=>interestsSelected.size?interestScore(b)-interestScore(a)||a.name.localeCompare(b.name):guidedActive&&audiencesSelected.size?matchScore(b)-matchScore(a)||a.name.localeCompare(b.name):sort==='reach'?(b.subscribers||0)-(a.subscribers||0):sort==='price'?price(a)-price(b):a.name.localeCompare(b.name));$('resultCount').textContent=rows.length;$('resultNoun').textContent=rows.length===1?'publication':'publications';$('resultsTitle').textContent='Publisher directory';$('empty').hidden=rows.length>0;$('cards').innerHTML=rows.map(r=>{let priced=r.placements.filter(p=>typeof p.price==='number'&&p.price>0),values=priced.map(p=>p.price),min=values.length?Math.min(...values):null,max=values.length?Math.max(...values):null,isSaved=selected.has(r.id),sym=r.publisher.toLowerCase().includes('sifted')?'€':'$',fmt=n=>sym+Number(n).toLocaleString('en-US',{maximumFractionDigits:0}),cpmValues=priced.filter(p=>p.placement_tier!=='Package'&&!/package|bundle|multi.?send/i.test(p.name||'')).map(p=>Number(r.subscribers)>0?p.price/Number(r.subscribers)*1000:null).filter(v=>typeof v==='number'&&Number.isFinite(v)),cpmLow=cpmValues.length?Math.min(...cpmValues):null,cpmHigh=cpmValues.length?Math.max(...cpmValues):null,cpmDisplay=cpmLow===null?'N/A':cpmLow===cpmHigh?sym+cpmLow.toFixed(2):sym+cpmLow.toFixed(2)+'–'+sym+cpmHigh.toFixed(2);return `
${esc(r.publisher===r.name?'PUBLICATION':r.publisher)}
${esc(r.name)}
${esc(r.category)}

${esc(r.card_description||'Publisher research and audience information')}

Subscribers${reach(r.subscribers)}
Send frequency${esc(normalizedCadence(r.send_frequency))}
Open rate${typeof r.open_rate==='number'?Math.round(r.open_rate*100)+'%':'—'}
List price${min!==null?(min===max?fmt(min):fmt(min)+'–'+fmt(max)):'N/A'}
Subscriber CPM${r.publisher==='The Deep View'?'Not comparable · bundles':cpmDisplay}
${r.publisher==='The Deep View'?'Packages':'Placements'}${r.placements.length}
${r.subscribers>0&&priced.length?'Core data available':(r.subscribers>0||priced.length?'Partial data available':'Limited data available')}View details ↗︎
`}).join('');$('cards').hidden=directoryView==='table';$('directoryTable').hidden=directoryView!=='table'; $('cardView').classList.toggle('active',directoryView==='cards');$('tableView').classList.toggle('active',directoryView==='table'); $('cardView').setAttribute('aria-pressed',String(directoryView==='cards'));$('tableView').setAttribute('aria-pressed',String(directoryView==='table')); $('directoryTable').innerHTML=`
${[['publication','Publication'],['audience','Audience'],['subscribers','Subscribers'],['open','Open rate'],['price','List price range'],['placements','Placements']].map(([key,label])=>``).join('')}${rows.map(r=>{const vals=r.placements.map(p=>Number(p.price)).filter(v=>Number.isFinite(v)&&v>0);const low=vals.length?Math.min(...vals):null,high=vals.length?Math.max(...vals):null;const sym=r.publisher.toLowerCase().includes('sifted')?'€':'$';const f=n=>sym+n.toLocaleString('en-US',{maximumFractionDigits:0});const range=low===null?'N/A':low===high?f(low):f(low)+'–'+f(high);return ``}).join('')}
Save
${esc(r.category)}${reach(r.subscribers)}${typeof r.open_rate==='number'?Math.round(r.open_rate*100)+'%':'N/A'}${range}${r.placements.length}
`; $('shortlistBar').classList.toggle('show',selected.size>0);$('shortlistText').textContent=selected.size+' saved publication'+(selected.size===1?'':'s')+(explorerVerified?'':' · in this tab');$('shortlistOnly').classList.toggle('active',savedOnly);$('shortlistOnly').textContent=(savedOnly?'★':'☆')+' My shortlist'+(selected.size?' ('+selected.size+')':'')};function segments(title,items,raw){return demographicSection(title,raw)};function openDetail(id){let r=DATA.find(d=>d.id===id);if(!r)return;let ps=r.placements;$('modalContent').innerHTML=`
PUBLISHER RESEARCH / ${esc(r.category)}

${esc(r.name)}

Published by ${esc(r.publisher)}

Audience overview

${esc(window.FRGMNTAudience.presentationText(r.focus,'focus'))}

${segments('Geography',r.geo_segments,r.geography)}${segments('Seniority & decision-maker access',r.seniority_segments,r.seniority)}${segments('Job functions',r.function_segments,r.functions)}${segments('Company size',r.size_segments,r.company_size)}

Publisher notes

${esc(publicPublisherNotes(window.FRGMNTAudience.presentationText(r.notes,'notes')))}

`;$('overlay').classList.add('open');$('closeModal').onclick=()=>$('overlay').classList.remove('open')};$('cards').addEventListener('click',e=>{let save=e.target.closest('[data-save]'),detail=e.target.closest('[data-open]');if(save){e.stopPropagation();let id=Number(save.dataset.save);toggleShortlist(id);return}if(detail)openDetail(Number(detail.dataset.open))});$('cards').addEventListener('keydown',e=>{if((e.key==='Enter'||e.key===' ')&&e.target.matches('article[data-open]')){e.preventDefault();openDetail(Number(e.target.dataset.open))}});['category','budget','sort'].forEach(id=>$(id).addEventListener('change',render));$('search').addEventListener('input',()=>{savedOnly=false;render()});$('shortlistOnly').onclick=()=>{savedOnly=!savedOnly;render()};$('viewSaved').onclick=()=>{savedOnly=false;render();showWork('compare');document.querySelector('.workspace').scrollIntoView({behavior:'smooth'})};$('clearSaved').onclick=()=>{selected.clear();savedOnly=false;render();persistShortlist();if(workTab==='compare')showWork('compare')};$('clear').onclick=()=>{$('directoryScale').value='';$('completeOnly').checked=false;$('search').value='';$('category').value='';$('budget').value='';$('sort').value='name';savedOnly=false;render()};$('overlay').onclick=e=>{if(e.target===$('overlay'))$('overlay').classList.remove('open')};document.addEventListener('keydown',e=>{if(e.key==='Escape')$('overlay').classList.remove('open')});$('contact').onclick=()=>{window.location.href='https://frgmnt.media/contact.html#inquiry-form'}; let workTab='discover'; const aState={tableSort:'metric',tableDirection:'asc',vizSort:'cpm',vizDirection:'asc',vizMetric:'cpm',publisher:'all',publication:'all',audience:'all',tier:'Primary',metric:'cpm',order:'asc',orientation:'horizontal'}; const aPalette=['#58766c','#7594a3','#9386a8','#ba9679','#7f9a82']; const aAudienceColors=Object.fromEntries(AUDIENCES.map((a,i)=>[a.label,aPalette[i%aPalette.length]])); function aColor(category){return aAudienceColors[category]||'#82918a'} function aFmt(n,dec=0){return n===null||!Number.isFinite(n)?'N/A':Number(n).toLocaleString('en-US',{minimumFractionDigits:dec,maximumFractionDigits:dec})} function aMoney(n,cur='$'){return n===null||!Number.isFinite(n)?'N/A':cur+aFmt(n,2)} function analyticsRows(){return DATA.flatMap(pub=>(pub.placements||[]).map(p=>{ const n=Number(pub.subscribers),v=Number(p.price),o=Number(pub.open_rate); const valid=Number.isFinite(v)&&v>0&&Number.isFinite(n)&&n>0; const single=p.placement_tier==='Package'||/package|bundle|multi.?send|4 sends|week.*placement/i.test((p.name||'')+' '+(p.scope||'')); const currency=/EUR/i.test(p.currency||'')?'€':'$'; return {pub:pub.name,publisher:pub.publisher,category:pub.category,audiences:AUDIENCES.filter(a=>audienceMatch(pub,a)).map(a=>a.label),tier:tierFor(p,pub),placement:p.name,price:valid?v:null,sub:n>0?n:null,open:o>0&&o<=1?o:null,cpm:valid&&!single?v/n*1000:null,ocpm:valid&&!single&&o>0&&o<=1?v/(n*o)*1000:null,currency,bundle:single,cpcLow:null,cpcHigh:null}; })).filter(r=>r.price!==null&&r.sub!==null&&!r.bundle)} function aOptions(values,current,allLabel){return `${[...new Set(values)].filter(Boolean).sort().map(v=>``).join('')}`} function aFiltered(){return analyticsRows().filter(r=>(aState.publisher==='all'||r.publisher===aState.publisher)&&(aState.publication==='all'||r.pub===aState.publication)&&(aState.audience==='all'||r.audiences.includes(aState.audience))&&(aState.tier==='all'||r.tier===aState.tier))} function aMedian(nums){let a=nums.filter(x=>Number.isFinite(x)).sort((x,y)=>x-y);return a.length?(a.length%2?a[(a.length-1)/2]:(a[a.length/2-1]+a[a.length/2])/2):null} function analyticsShell(cost){return `
${cost?'03 / ANALYZE':'04 / VISUALIZE'}

${cost?'Cost & Performance Analysis':'Audience Reach & Value'}

${cost?'Compare what placements cost, normalized by reach and estimated opens.':'Compare audience scale and pricing efficiency across newsletter placements.'}

${cost?``:``}
${cost?`
Placement comparison
`:''}
`} function initAnalytics(cost){ const rows=analyticsRows(); const selects=[['aPublisher','publisher',[...new Set(rows.map(r=>r.publisher))],'All publishers'],['aPublication','publication',[...new Set(rows.filter(r=>aState.publisher==='all'||r.publisher===aState.publisher).map(r=>r.pub))],'All publications'],['aAudience','audience',AUDIENCES.map(a=>a.label),'All audiences'],['aTier','tier',[...new Set(rows.map(r=>r.tier))],'All tiers']]; for(const [id,key,options,all] of selects){const el=$(id);el.innerHTML=aOptions(options,aState[key],all);el.value=options.includes(aState[key])?aState[key]:'all';if(key==='tier'&&options.includes('Primary')&&aState.tier==='Primary')el.value='Primary';el.onchange=()=>{aState[key]=el.value;if(key==='publisher')aState.publication='all';initAnalytics(cost)}} if(!cost){$('aVizMetric').value=aState.vizMetric;$('aVizMetric').onchange=()=>{aState.vizMetric=$('aVizMetric').value;aState.vizSort=aState.vizMetric;renderAnalytics(false)}} if(cost){$('aMetric').value=aState.metric;$('aMetric').onchange=()=>{aState.metric=$('aMetric').value;renderAnalytics(true)};$('aOrder').value=aState.order;$('aOrder').onchange=()=>{aState.order=$('aOrder').value;aState.tableDirection=aState.order;renderAnalytics(true)};$('aHorizontal').onclick=()=>{aState.orientation='horizontal';renderAnalytics(true)};$('aVertical').onclick=()=>{aState.orientation='vertical';renderAnalytics(true)}} renderAnalytics(cost); } function renderAnalytics(cost){ const rows=aFiltered().filter(r=>r.currency==='$'); $('aLegend').innerHTML=AUDIENCES.map(a=>a.label).map(cat=>`${esc(cat)}`).join(''); const activeMetric=cost?'cpm':aState.vizMetric;const eligible=rows.filter(r=>r[activeMetric]!==null); const median=aMedian(eligible.map(r=>r[activeMetric])); $('aStats').innerHTML=`
Eligible placements${aFmt(eligible.length)}
Median ${activeMetric==='ocpm'?'open-based':'subscriber'} CPM${aMoney(median)}
Publications${new Set(eligible.map(r=>r.pub)).size}
`; if(!eligible.length){$('aChart').innerHTML='
No comparable placements match these filters. Try another tier or audience.
';$('aBottom').innerHTML='';return} if(cost){ $('aHorizontal').classList.toggle('selected',aState.orientation==='horizontal');$('aVertical').classList.toggle('selected',aState.orientation==='vertical'); const key=aState.metric, comparable=rows.filter(r=>r[key]!==null&&Number.isFinite(r[key])); const sortKey=aState.tableSort==='metric'?key:aState.tableSort;const ordered=[...comparable].sort((a,b)=>{const av=a[sortKey],bv=b[sortKey];const cmp=typeof av==='string'?av.localeCompare(bv):(av??Infinity)-(bv??Infinity);return cmp*(aState.tableDirection==='asc'?1:-1)}); const shown=ordered; if(key==='cpc'){$('aChart').innerHTML='
Estimated CPC range

Publisher-reported click benchmarks are not available for the selected placements. No CPC estimates are inferred from CPM alone.

'} else if(aState.orientation==='horizontal'){ const max=Math.max(...shown.map(r=>r[key]),1),h=60+shown.length*42,w=1100; $('aChart').innerHTML=`
${key==='price'?'List price (USD)':key==='ocpm'?'Open-based CPM (USD)':'Subscriber CPM (USD)'} · ${shown.length} placements · scroll to explore
${[0,.25,.5,.75,1].map(q=>`${aMoney(max*q)}`).join('')}${shown.map((r,i)=>{const y=44+i*42,bw=565*r[key]/max;return `${esc((r.pub.length>27?r.pub.slice(0,26)+'…':r.pub)+' · '+r.tier)}${esc(r.pub+' · '+r.tier)} — ${aMoney(r[key])}${aMoney(r[key])}`}).join('')}
`; }else{ const max=Math.max(...shown.map(r=>r[key]),1),w=Math.max(940,shown.length*145+110),h=495,left=65,bottom=330; $('aChart').innerHTML=`
${key==='price'?'List price (USD)':key==='ocpm'?'Open-based CPM (USD)':'Subscriber CPM (USD)'} · ${shown.length} placements · scroll to explore
${[0,.25,.5,.75,1].map(q=>`${aMoney(max*q)}`).join('')}${shown.map((r,i)=>{const x=left+24+i*145,bh=270*r[key]/max;return `${esc(r.pub+' · '+r.tier)} — ${aMoney(r[key])}${aMoney(r[key])}${esc(r.pub.slice(0,20))}${esc(r.pub.slice(20,40))}${esc(r.tier)}`}).join('')}
`; } $('aBottom').innerHTML=`
${ordered.slice(0,50).map(r=>``).join('')}
Publication / placement ↕List price ↕Subscribers ↕Subscriber CPM ↕Open-based CPM ↕
${esc(r.pub)}${esc(r.publisher)} · ${esc(r.tier)} · ${esc(r.placement)}${aMoney(r.price)}${aFmt(r.sub)}${aMoney(r.cpm)}${aMoney(r.ocpm)}
${ordered.length>50?`

Showing 50 of ${ordered.length} placements. Refine filters to narrow the comparison.

`:''}`; document.querySelectorAll('.a-table th[data-sort]').forEach(th=>th.onclick=()=>{const k=th.dataset.sort;aState.tableDirection=aState.tableSort===k&&aState.tableDirection==='asc'?'desc':'asc';aState.tableSort=k;renderAnalytics(true)}); }else{ const byPub=new Map();for(const r of eligible){const k=r.pub+'|'+r.tier;if(!byPub.has(k)||r[aState.vizMetric]r.sub)),medianCpm=aMedian(plotted.map(r=>r[vm])); const maxSub=Math.max(...plotted.map(r=>r.sub)),maxCpm=Math.max(...plotted.map(r=>r[vm])); const logX=n=>Math.log10(Math.max(n,1000)),xMin=Math.floor(logX(Math.min(...plotted.map(r=>r.sub)))),xMax=Math.ceil(logX(maxSub))+0.01; const xSorted=[...plotted.map(r=>r.sub)].sort((a,b)=>a-b),ySorted=[...plotted.map(r=>r[vm])].sort((a,b)=>a-b);const rank=(arr,v)=>arr.length===1?.5:(arr.findIndex(n=>n>=v))/(arr.length-1);const xx=n=>85+760*rank(xSorted,n),yy=n=>375-320*rank(ySorted,n); const mx=465,my=215; $('aChart').innerHTML=`
Median reach vs. median ${vm==='ocpm'?'open-based':'subscriber'} CPM

Each dot represents a publication and placement. Positions show relative rankings; dashed lines mark the medians.

Median audience reach${aFmt(medianSub)}
Median ${vm==='ocpm'?'open-based':'subscriber'} CPM${aMoney(medianCpm)}
Smaller reach · Higher CPMLarger reach · Higher CPMSmaller reach · Lower CPMLarger reach · Lower CPM${plotted.map(r=>`${esc(r.pub+' · '+r.tier)} | ${aFmt(r.sub)} subscribers | ${aMoney(r[vm])} ${vm==='ocpm'?'open-based':'subscriber'} CPM`).join('')}Audience size →${vm==='ocpm'?'Open-based':'Subscriber'} CPM ↑
`; const cats=AUDIENCES.map(a=>a.label).filter(cat=>plotted.some(r=>r.audiences.includes(cat))); $('aBottom').innerHTML=`
Audience economics
${cats.map(cat=>{const a=plotted.filter(r=>r.audiences.includes(cat));return ``}).join('')}
AudienceEligible placementsMedian ${vm==='ocpm'?'open-based':'subscriber'} CPMMedian reach
${esc(cat)}${a.length}${aMoney(aMedian(a.map(r=>r[vm])))}${aFmt(aMedian(a.map(r=>r.sub)))}
Placement comparison
${[...plotted].sort((a,b)=>{const k=aState.vizSort;const av=a[k],bv=b[k];const cmp=typeof av==='string'?av.localeCompare(bv):(av??Infinity)-(bv??Infinity);return cmp*(aState.vizDirection==='asc'?1:-1)}).slice(0,50).map(r=>``).join('')}
Publication ↕Audience ↕Placement ↕Subscribers ↕${vm==='ocpm'?'Open-based':'Subscriber'} CPM ↕
${esc(r.pub)}${esc(r.audiences.join(', ')||'Unclassified')}${esc(r.tier)}${aFmt(r.sub)}${aMoney(r[vm])}
`; document.querySelectorAll('.a-table th[data-vsort]').forEach(th=>th.onclick=()=>{const k=th.dataset.vsort;aState.vizDirection=aState.vizSort===k&&aState.vizDirection==='asc'?'desc':'asc';aState.vizSort=k;renderAnalytics(false)}); } } function comparePriceRange(r){ const values=(r.placements||[]).map(p=>p.price).filter(v=>typeof v==='number'&&Number.isFinite(v)&&v>0); if(!values.length)return 'N/A'; const sym=(r.publisher||'').toLowerCase().includes('sifted')?'€':'$'; const fmt=v=>sym+v.toLocaleString('en-US',{maximumFractionDigits:0}); const lo=Math.min(...values),hi=Math.max(...values); return lo===hi?fmt(lo):fmt(lo)+' – '+fmt(hi); } function showWork(tab,recordHistory=true){ if(!['discover','compare','economics','matrix'].includes(tab))tab='discover'; if(recordHistory&&workTab!==tab)history.pushState({explorerTab:tab},'',location.pathname+location.search+'#'+tab); workTab=tab; document.querySelectorAll('[data-work]').forEach(b=>{b.classList.toggle('active',b.dataset.work===tab);b.setAttribute('aria-selected',String(b.dataset.work===tab));}); const isDiscover=tab==='discover';document.querySelector('.research-notice').hidden=!isDiscover; ['discoverAudience','discoverRefinements','discoverDirectorySwitch','discoverToolbar','discoveryResults'].forEach(id=>{$(id).hidden=!isDiscover}); const panel=$('workspacePanel');panel.classList.remove('simple-analytics');panel.hidden=isDiscover;panel.classList.toggle('locked-preview',(tab==='economics'||tab==='matrix')&&!explorerVerified); if(isDiscover){panel.innerHTML='';return;} if(tab==='compare'){ let pubs=DATA.filter(r=>selected.has(r.id)); panel.innerHTML=`
02 / COMPARE

Compare your selections.

${pubs.length?pubs.length+' saved publication'+(pubs.length===1?'':'s')+'. Review their reported reach, audience focus, and starting rates side by side.':'Save publications using the star on any card to build your shortlist.'}

${pubs.length?`
${pubs.map(r=>``).join('')}
PublicationAudienceSubscribersOpen rateList price rangeResearchRemove
${esc(r.name)}${esc(r.publisher)}${esc(r.category)}${reach(r.subscribers)}${r.open_rate?Math.round(r.open_rate*100)+'%':'N/A'}${comparePriceRange(r)}

Price ranges reflect reported placement list rates, not equivalent inventory or negotiated quotes.

`:''}`; panel.querySelectorAll('[data-compare-open]').forEach(b=>b.onclick=()=>openDetail(Number(b.dataset.compareOpen))); panel.querySelectorAll('[data-compare-remove]').forEach(b=>b.onclick=()=>{selected.delete(Number(b.dataset.compareRemove));render();persistShortlist();showWork('compare')}); $('backDiscovery').onclick=()=>showWork('discover');return; } const econ=tab==='economics'; if(explorerVerified){ panel.classList.add('simple-analytics'); document.querySelector('[data-work="economics"] span').textContent='Compare placement costs'; document.querySelector('[data-work="matrix"] span').textContent='Explore audience value'; panel.innerHTML=analyticsShell(econ); initAnalytics(econ); return; } const tiles=econ?'
PublicationPublisher ASubscriber reach
List price$5,000Primary placement
Subscriber CPM$12.50Indicative economics
':'
Audience reachScalePublication comparison
PricingCPMInvestment efficiency
OpportunityFitAudience alignment
'; panel.innerHTML=`
🔒 ${econ?'ANALYZE':'VISUALIZE'} · MORE INSIGHTS

${econ?'Compare newsletter buying economics.':'See the newsletter landscape.'}

${econ?'Compare publisher pricing, audience reach, and CPM benchmarks in one place.':'Explore how publications compare on audience scale, pricing, and efficiency.'}

`; $('unlock').onclick=()=>openAccessGate(); } document.querySelectorAll('[data-work]').forEach(b=>b.onclick=()=>showWork(b.dataset.work)); window.addEventListener('popstate',()=>{if($('accessGate').open)$('accessGate').close();$('overlay').classList.remove('open');showWork(location.hash.slice(1)||'discover',false)}); let explorerVerified=false, explorerTurnstileId=null, explorerSaveTimer=null; const api=async(path,options={})=>{const r=await fetch(path,{credentials:'same-origin',cache:'no-store',...options});const v=await r.json().catch(()=>({error:'Unexpected server response'}));if(!r.ok)throw Error(v.error||'Request failed ('+r.status+')');return v}; const apiPost=(path,payload)=>api(path,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)}); function openAccessGate(){const gate=$('accessGate');if(!gate.open)gate.showModal()} async function initExplorerAuth(){ try{ const session=await api('/api/session'); if(session.verified){ // Server-authorized advanced endpoint is the authoritative gate. await api('/api/advanced'); explorerVerified=true; const saved=await api('/api/shortlist'); const byName=new Map(DATA.map(r=>[r.publisher+' / '+r.name,r.id])); if(Array.isArray(saved.publisherIds)){ for(const id of saved.publisherIds){const n=byName.get(id);if(n!==undefined)selected.add(n)} render(); } document.querySelectorAll('[data-work="economics"] span,[data-work="matrix"] span').forEach(x=>x.textContent='Verified access'); } }catch(e){console.warn('Verification/session check:',e.message)} const status=new URL(location.href).searchParams.get('verify'); if(status==='invalid'||status==='expired'){openAccessGate();$('accessNote').textContent='Your verification link expired or is invalid. Request a new link.'} if(status==='success'&&explorerVerified){$('accessNote').textContent='Email verified. Full research access enabled.'} if(status)history.replaceState({},'',location.pathname); try{ const cfg=await api('/api/config'); if(!cfg.turnstileRequired||!cfg.turnstileSiteKey){$('accessNote').textContent='Verification is not configured for this preview yet. Contact [email protected].';return} let tries=0;const draw=()=>{if(window.turnstile){explorerTurnstileId=window.turnstile.render('#accessTurnstile',{sitekey:cfg.turnstileSiteKey,theme:'light'});return}if(tries++<60)setTimeout(draw,250);else $('accessNote').textContent='Security check could not load. Disable content blockers for this site or try another browser.'};draw(); }catch(e){$('accessNote').textContent='Verification service unavailable: '+e.message} } $('accessClose').onclick=()=>$('accessGate').close(); $('accessForm').addEventListener('submit',async e=>{ e.preventDefault();const btn=$('accessForm').querySelector('[type="submit"]');btn.disabled=true;$('accessNote').textContent='Sending verification email…'; try{ if(!window.turnstile||explorerTurnstileId===null)throw Error('Security check is still loading.'); const token=window.turnstile.getResponse(explorerTurnstileId);if(!token)throw Error('Complete the security check first.'); const result=await apiPost('/api/auth/start',{email:$('accessEmail').value,consultationRequested:$('accessConsult').checked,turnstileToken:token}); $('accessNote').textContent=result.message||'Check your email for the verification link.'; }catch(err){$('accessNote').textContent=err.message} finally{btn.disabled=false;if(window.turnstile&&explorerTurnstileId!==null)window.turnstile.reset(explorerTurnstileId)} }); const shortlistDraftKey='frgmnt-v4-shortlist-draft'; function restoreShortlistDraft(){ try{const names=JSON.parse(sessionStorage.getItem(shortlistDraftKey)||'[]');if(!Array.isArray(names))return;const ids=new Map(DATA.map(r=>[r.publisher+' / '+r.name,r.id]));for(const name of names.slice(0,2)){const id=ids.get(name);if(id!==undefined)selected.add(id)}}catch(e){console.warn('Shortlist recovery unavailable:',e.message)} } function persistShortlist(){ if(!explorerVerified){try{sessionStorage.setItem(shortlistDraftKey,JSON.stringify([...selected].map(id=>DATA.find(r=>r.id===id)).filter(Boolean).map(r=>r.publisher+' / '+r.name)))}catch(e){console.warn('Shortlist recovery unavailable:',e.message)}return;} clearTimeout(explorerSaveTimer); explorerSaveTimer=setTimeout(()=>{ const ids=[...selected].map(id=>DATA.find(r=>r.id===id)).filter(Boolean).map(r=>r.publisher+' / '+r.name); apiPost('/api/shortlist',{publisherIds:ids}).catch(e=>console.warn('Shortlist sync:',e.message)); },450); } // Every shortlist mutation persists explicitly, including Compare removals. async function bootExplorer(){ try { const response=await fetch('/data/v4-publications.json',{cache:'no-store'}); if(!response.ok)throw Error('Publication data unavailable ('+response.status+')'); DATA=await response.json(); if(!Array.isArray(DATA)||DATA.length===0||DATA.some(r=>!r||typeof r.publisher!=='string'||typeof r.name!=='string'||!Array.isArray(r.placements)))throw Error('Invalid publication data'); populateCategories(); restoreShortlistDraft(); render(); await initExplorerAuth(); showWork(location.hash.slice(1)||'discover',false); history.replaceState({explorerTab:workTab},'',location.pathname+location.search+'#'+workTab); }catch(err){console.error('Explorer initialization failed',err);document.getElementById('resultsTitle').textContent='Unable to load research';document.getElementById('cards').textContent='Please refresh or contact [email protected].';} } bootExplorer();