← back to Costa Rica
public/stats.html
118 lines
<!doctype html>
<html lang="en" data-theme="light">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Stats — Costa Rica Directory</title>
<meta name="description" content="Live counts of Costa Rica businesses by category, vertical, and region across the directory." />
<meta property="og:type" content="website" />
<meta property="og:title" content="Costa Rica Directory — Stats" />
<meta property="og:description" content="Live counts by category, vertical, and region across 25,000+ Costa Rica listings." />
<script type="application/ld+json">
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[
{"@type":"ListItem","position":1,"name":"Home","item":"https://costarica.agentabrams.com/"},
{"@type":"ListItem","position":2,"name":"Stats","item":"https://costarica.agentabrams.com/stats"}
]}
</script>
<script>(function(){try{var t=localStorage.getItem('cr-theme');if(t)document.documentElement.setAttribute('data-theme',t);}catch(e){}})();</script>
<style>
:root{--bg:#fafaf7;--fg:#0e1015;--muted:#5a6270;--line:#e3e0d6;--card:#fff;--card-line:#ebe7da;--accent:#0a7a3a;--accent-2:#c2a85a}
[data-theme="dark"]{--bg:#0e1015;--fg:#f3f1ea;--muted:#9aa3b2;--line:#23262d;--card:#15181f;--card-line:#262932;--accent:#3ec07a;--accent-2:#d8c074}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--fg);font-family:-apple-system,BlinkMacSystemFont,'Inter','Segoe UI',Roboto,sans-serif;line-height:1.55}
a{color:inherit;text-decoration:none} a:hover{color:var(--accent)}
.nav{position:sticky;top:0;z-index:30;background:color-mix(in srgb,var(--bg) 88%,transparent);backdrop-filter:saturate(160%) blur(10px);border-bottom:1px solid var(--line)}
.nav-in{max-width:1320px;margin:0 auto;padding:14px 22px;display:flex;align-items:center;gap:18px;flex-wrap:wrap}
.brand{font-weight:700;font-size:18px} .brand .pin{color:var(--accent)}
.nav-links{display:flex;gap:14px;flex-wrap:wrap;color:var(--muted);font-size:14px}
.nav-links a.active{color:var(--fg);font-weight:600}
.ml-auto{margin-left:auto} .icon-btn{appearance:none;background:transparent;color:inherit;border:1px solid var(--line);border-radius:999px;width:36px;height:36px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer}
main{max-width:1320px;margin:0 auto;padding:28px 22px}
h1{font-size:38px;letter-spacing:-.02em;margin:6px 0 4px}
.sub{color:var(--muted);margin-bottom:22px}
.row{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-bottom:24px}
@media (max-width:760px){.row{grid-template-columns:repeat(2,1fr)}}
.stat{background:var(--card);border:1px solid var(--card-line);border-radius:14px;padding:16px}
.stat-n{font-size:30px;font-weight:800;letter-spacing:-.02em}
.stat-l{color:var(--muted);font-size:13px;text-transform:uppercase;letter-spacing:.06em;margin-top:2px}
.grid2{display:grid;grid-template-columns:1.2fr 1fr;gap:18px}
@media (max-width:900px){.grid2{grid-template-columns:1fr}}
.panel{background:var(--card);border:1px solid var(--card-line);border-radius:14px;padding:18px}
.panel h2{margin:0 0 12px;font-size:18px;letter-spacing:-.01em}
table{width:100%;border-collapse:collapse;font-size:14px}
th,td{padding:8px 6px;border-bottom:1px solid var(--line);text-align:left}
th{color:var(--muted);font-weight:600;text-transform:uppercase;font-size:11px;letter-spacing:.05em}
.bar{position:relative;background:color-mix(in srgb,var(--accent) 14%,transparent);height:8px;border-radius:999px;overflow:hidden}
.bar > span{display:block;height:100%;background:var(--accent);border-radius:999px}
.num{text-align:right;font-variant-numeric:tabular-nums}
.filter{display:flex;gap:8px;align-items:center;margin-bottom:10px}
.filter input{flex:1;background:var(--bg);color:var(--fg);border:1px solid var(--line);border-radius:8px;padding:8px 12px;font-size:13px}
.scroll{max-height:520px;overflow:auto}
</style>
</head>
<body>
<header class="nav"><div class="nav-in">
<a class="brand" href="/"><span class="pin">●</span> Costa Rica Directory</a>
<nav class="nav-links">
<a href="/">Browse</a><a href="/search">Search</a><a href="/provinces">Provinces</a><a href="/verticals">Verticals</a><a href="/stats" class="active">Stats</a><a href="/about">About</a>
</nav>
<div class="ml-auto"><button class="icon-btn" id="themeBtn" aria-label="Theme">◐</button></div>
</div></header>
<main>
<h1>Directory stats</h1>
<div class="sub" id="sub">Live counts across the Costa Rica catalog.</div>
<div class="row" id="kpis"></div>
<div class="grid2">
<section class="panel">
<h2>By vertical</h2>
<div class="filter"><input id="vfilter" placeholder="filter…" /></div>
<div class="scroll"><table id="vtable"><thead><tr><th>Vertical</th><th class="num">Count</th><th>Share</th></tr></thead><tbody></tbody></table></div>
</section>
<section class="panel">
<h2>By category</h2>
<table id="ctable"><thead><tr><th>Category</th><th class="num">Count</th><th>Share</th></tr></thead><tbody></tbody></table>
</section>
</div>
<section class="panel" style="margin-top:18px">
<h2>By region (top 30 by listings)</h2>
<div class="filter"><input id="rfilter" placeholder="filter region…" /></div>
<div class="scroll"><table id="rtable"><thead><tr><th>Region</th><th class="num">Count</th><th>Share</th></tr></thead><tbody></tbody></table></div>
</section>
</main>
<script>
const themeBtn=document.getElementById('themeBtn');
themeBtn.addEventListener('click',()=>{const cur=document.documentElement.getAttribute('data-theme')||'light';const nx=cur==='light'?'dark':'light';document.documentElement.setAttribute('data-theme',nx);try{localStorage.setItem('cr-theme',nx);}catch{}});
function bar(pct){return `<div class="bar"><span style="width:${pct.toFixed(1)}%"></span></div>`;}
function row(label,n,total){const pct=total?(n*100/total):0;return `<tr><td>${label}</td><td class="num">${n.toLocaleString()}</td><td style="width:38%">${bar(pct)}</td></tr>`;}
fetch('/api/stats').then(r=>r.json()).then(s=>{
const total=s.total||0;
document.getElementById('kpis').innerHTML=[
['Total listings',total],
['Verticals',s.by_vertical.length],
['Regions w/ listings',s.by_region.filter(r=>r.n>0).length],
['Categories',s.by_category.length]
].map(([l,n])=>`<div class="stat"><div class="stat-n">${n.toLocaleString()}</div><div class="stat-l">${l}</div></div>`).join('');
const vbody=document.querySelector('#vtable tbody');
const cbody=document.querySelector('#ctable tbody');
const rbody=document.querySelector('#rtable tbody');
function renderV(filter=''){const f=filter.trim().toLowerCase();vbody.innerHTML=s.by_vertical.filter(v=>!f||v.vertical.toLowerCase().includes(f)).map(v=>row(v.vertical,v.n,total)).join('')||`<tr><td colspan="3" style="color:var(--muted);text-align:center;padding:18px">no matches</td></tr>`;}
function renderC(){cbody.innerHTML=s.by_category.map(c=>row(c.category,c.n,total)).join('');}
function renderR(filter=''){const f=filter.trim().toLowerCase();const items=s.by_region.filter(r=>r.n>0&&(!f||r.name.toLowerCase().includes(f))).slice(0,30);rbody.innerHTML=items.map(r=>row(`<a href="/r/${r.slug}">${r.name}</a>`,r.n,total)).join('')||`<tr><td colspan="3" style="color:var(--muted);text-align:center;padding:18px">no matches</td></tr>`;}
renderV();renderC();renderR();
document.getElementById('vfilter').addEventListener('input',e=>renderV(e.target.value));
document.getElementById('rfilter').addEventListener('input',e=>renderR(e.target.value));
}).catch(e=>{document.getElementById('sub').textContent='Could not load stats: '+e.message;});
</script>
</body>
</html>