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 COLOR = { S1:'var(--accent)', S2:'var(--viz-4)', M1:'var(--accent)', M2:'var(--viz-5)', M3:'var(--viz-4)', T1:'var(--accent)', T2:'var(--viz-5)' };
var LABELPOS = { S1:'left', S2:'left', M1:'top', M2:'top', M3:'top', T1:'right', T2:'right' };
function splitRandom(total, n){
var cuts=[];
for (var i=0;i<n-1;i++) cuts.push(R(0,total));
cuts.sort(function(a,b){ return a-b; });
var parts=[], prev=0;
cuts.forEach(function(c){ parts.push(Math.max(c-prev,0.6)); prev=c; });
parts.push(Math.max(total-prev,0.6));
return parts;
}
function genFlow(){
var s1=R(30,54), s2=R(30,54);
var s1p=splitRandom(s1,2), s2p=splitRandom(s2,2);
var m1=s1p[0], m2=s1p[1]+s2p[0], m3=s2p[1];
var m2p=splitRandom(m2,2);
var t1=m1+m2p[0], t2=m2p[1]+m3;
return {
nodeTotals:{S1:s1,S2:s2,M1:m1,M2:m2,M3:m3,T1:t1,T2:t2},
links:[
{s:'S1',t:'M1',v:s1p[0]},{s:'S1',t:'M2',v:s1p[1]},
{s:'S2',t:'M2',v:s2p[0]},{s:'S2',t:'M3',v:s2p[1]},
{s:'M1',t:'T1',v:m1},{s:'M2',t:'T1',v:m2p[0]},
{s:'M2',t:'T2',v:m2p[1]},{s:'M3',t:'T2',v:m3}
]
};
}
var cur = genFlow();
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 ribbon(x0,y0t,y0b,x1,y1t,y1b){
var xm=(x0+x1)/2;
return 'M'+x0+','+y0t+' C'+xm+','+y0t+' '+xm+','+y1t+' '+x1+','+y1t+
' L'+x1+','+y1b+' C'+xm+','+y1b+' '+xm+','+y0b+' '+x0+','+y0b+' Z';
}
function draw(){
svg.innerHTML = '';
var padL=26, padR=26, padT=14, padB=14;
var pw=W-padL-padR, ph=H-padT-padB;
var nodeW = Math.max(6, Math.min(11, W*0.022));
var grand = cur.nodeTotals.S1+cur.nodeTotals.S2;
var gap = Math.min(8, ph*0.05);
var maxGaps = 2; // widest column (M: 3 nodes) needs 2 gaps — size to that so no column overflows ph
var scale = (ph - gap*maxGaps) / grand;
var fs = Math.max(8, Math.min(11, W/110));
var cols = [ { x: padL, ids:['S1','S2'] }, { x: padL+pw/2-nodeW/2, ids:['M1','M2','M3'] }, { x: W-padR-nodeW, ids:['T1','T2'] } ];
var nodes = {};
cols.forEach(function(col){
var heights = col.ids.map(function(id){ return cur.nodeTotals[id]*scale; });
var stackH = heights.reduce(function(a,b){ return a+b; },0) + gap*(col.ids.length-1);
var y = padT + (ph-stackH)/2;
col.ids.forEach(function(id,i){
nodes[id] = { x: col.x, y:y, h: heights[i], outCur:y, inCur:y };
y += heights[i]+gap;
});
});
cur.links.forEach(function(link){
var s = nodes[link.s], t = nodes[link.t];
var thick = Math.max(link.v*scale, 0.5);
var y0t=s.outCur, y0b=s.outCur+thick; s.outCur=y0b;
var y1t=t.inCur, y1b=t.inCur+thick; t.inCur=y1b;
var path = se('path');
sa(path,{ d: ribbon(s.x+nodeW,y0t,y0b,t.x,y1t,y1b), fill: COLOR[link.s], 'fill-opacity':0.3 });
svg.appendChild(path);
});
Object.keys(nodes).forEach(function(id){
var n = nodes[id];
var rect = se('rect');
sa(rect,{ x:n.x, y:n.y, width:nodeW, height:Math.max(n.h,1), rx:2, fill:COLOR[id] });
svg.appendChild(rect);
var pos = LABELPOS[id];
var lx = pos==='left'? n.x-4 : pos==='right'? n.x+nodeW+4 : n.x+nodeW/2;
var ly = pos==='top'? n.y-4 : n.y+n.h/2+fs*0.32;
var anchor = pos==='left'? 'end' : pos==='right'? 'start' : 'middle';
var t = se('text');
sa(t,{ x:lx, y:ly, 'text-anchor':anchor, fill:'var(--fg)', 'font-size':fs, 'font-weight':700 });
t.textContent = id;
svg.appendChild(t);
});
}
function animateTo(target){
var from = cur;
var t0 = performance.now();
function step(t){
var p = Math.min(1,(t-t0)/800);
var e = 1-Math.pow(1-p,3);
var mid = { nodeTotals:{}, links: from.links.map(function(l,i){ return { s:l.s, t:l.t, v: l.v+(target.links[i].v-l.v)*e }; }) };
Object.keys(from.nodeTotals).forEach(function(k){ mid.nodeTotals[k] = from.nodeTotals[k]+(target.nodeTotals[k]-from.nodeTotals[k])*e; });
cur = mid;
draw();
if (p<1) requestAnimationFrame(step); else cur = target;
}
requestAnimationFrame(step);
}
new ResizeObserver(function(){ measure(); draw(); }).observe(svg);
measure();
draw();
setInterval(function(){ animateTo(genFlow()); }, 3800);