var svg = document.getElementById('svg');
var NS = 'http://www.w3.org/2000/svg';
function se(t){ return document.createElementNS(NS,t); }
function sa(e,o){ for (var k in o) e.setAttribute(k,o[k]); }
function R(a,b){ return a + Math.random()*(b-a); }
var CATS = [
{ name:'iOS', color:'var(--accent)' },
{ name:'Android', color:'var(--viz-4)' },
{ name:'Web', color:'var(--accent-3)' },
{ name:'Desktop', color:'var(--viz-5)' }
];
var NSAMP=50, MGRID=32;
var grid=[]; for (var g=0; g<=MGRID; g++) grid.push(g*100/MGRID);
var cur = CATS.map(function(){ return { density: grid.map(function(){ return 0.1; }), median:50 }; });
var W=300, H=200;
function measure(){
var r = svg.getBoundingClientRect();
W = Math.max(r.width,10); H = Math.max(r.height,10);
sa(svg,{ viewBox: '0 0 ' + W + ' ' + H });
}
function samples(mean,spread){
var out=[];
for (var i=0;i<NSAMP;i++){
var s=0; for (var k=0;k<4;k++) s+=Math.random();
out.push(Math.max(1,Math.min(99, mean+(s/4-0.5)*2*spread)));
}
return out;
}
function kde(xs, g, h){
return g.map(function(gv){
var s=0;
xs.forEach(function(x){ var u=(gv-x)/h; s += Math.exp(-0.5*u*u); });
return s/(xs.length*h*Math.sqrt(2*Math.PI));
});
}
function regen(){
return CATS.map(function(){
var pm = { mean:R(30,72), spread:R(9,22) };
var xs = samples(pm.mean,pm.spread);
var h = Math.max(4, pm.spread*0.5);
var d = kde(xs, grid, h);
var maxd = Math.max.apply(null, d.concat([0.001]));
var norm = d.map(function(v){ return v/maxd; });
xs.sort(function(a,b){ return a-b; });
var median = xs[Math.floor(xs.length/2)];
return { density:norm, median:median };
});
}
function draw(){
svg.innerHTML = '';
var padL=14,padR=14,padT=10,padB=18;
var pw=W-padL-padR, ph=H-padT-padB;
var n=CATS.length;
var colW = pw/n;
var halfMax = colW*0.36;
var fs = Math.max(8,Math.min(10,W/48));
CATS.forEach(function(cat,ci){
var cx = padL+colW*(ci+0.5);
var dens = cur[ci].density;
var leftPts=[], rightPts=[];
dens.forEach(function(v,gi){
var y = padT+ph-(grid[gi]/100)*ph;
leftPts.push({ x:cx-v*halfMax, y:y });
rightPts.push({ x:cx+v*halfMax, y:y });
});
var d = 'M'+leftPts[0].x.toFixed(1)+','+leftPts[0].y.toFixed(1);
leftPts.forEach(function(p){ d += ' L'+p.x.toFixed(1)+','+p.y.toFixed(1); });
for (var k=rightPts.length-1;k>=0;k--){ d += ' L'+rightPts[k].x.toFixed(1)+','+rightPts[k].y.toFixed(1); }
d += ' Z';
var shape = se('path');
sa(shape,{ d:d, fill:cat.color, 'fill-opacity':0.5, stroke:cat.color, 'stroke-width':1.4 });
svg.appendChild(shape);
var my = padT+ph-(cur[ci].median/100)*ph;
var mtick = se('line');
sa(mtick,{ x1:cx-halfMax*0.55, y1:my, x2:cx+halfMax*0.55, y2:my, stroke:'var(--fg)', 'stroke-width':2 });
svg.appendChild(mtick);
var lab = se('text');
sa(lab,{ x:cx, y:padT+ph+13, 'text-anchor':'middle', fill:'var(--muted)', 'font-size':fs, 'font-weight':600 });
lab.textContent = cat.name;
svg.appendChild(lab);
});
}
function animate(target){
var from = cur.map(function(c){ return { density:c.density.slice(), median:c.median }; });
var t0 = performance.now();
function step(t){
var p = Math.min(1,(t-t0)/850);
var e = 1-Math.pow(1-p,3);
cur = from.map(function(c,i){
return {
density: c.density.map(function(v,gi){ return v+(target[i].density[gi]-v)*e; }),
median: c.median+(target[i].median-c.median)*e
};
});
draw();
if (p<1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
new ResizeObserver(function(){ measure(); draw(); }).observe(svg);
measure();
draw();
animate(regen());
setInterval(function(){ animate(regen()); }, 4000);