(async function(){ const host=document.getElementById('timeline-chart'),org=document.getElementById('timeline-org'),cost=document.getElementById('timeline-cost'),slider=document.getElementById('timeline-month'),readout=document.getElementById('timeline-readout'); try { const response=await fetch('timeline/release-windows.json');if(!response.ok)throw Error(response.status);const rows=await response.json(); const releaseResponse=await fetch('timeline/releases.json');if(!releaseResponse.ok)throw Error(releaseResponse.status);const releases=(await releaseResponse.json()).releases; const months=[];for(let y=2022;y<=2026;y++)for(let m=1;m<=12;m++){let s=`${y}-${String(m).padStart(2,'0')}`;if(s>='2022-06'&&s<='2026-10')months.push(s)} const fmt=n=>n.toLocaleString('fr-FR',{maximumFractionDigits:1}); function render(){ const selected=rows.filter(r=>!org.value||r.organization===org.value),k=cost.value+'_musd'; const early=months.map(m=>selected.reduce((s,r)=>s+(r.first_month<=m?r[k]:0),0)),late=months.map(m=>selected.reduce((s,r)=>s+(r.last_month<=m?r[k]:0),0)); const W=1000,H=440,L=72,R=25,T=30,B=65,max=Math.max(10,early.at(-1)*1.12),x=i=>L+i*(W-L-R)/(months.length-1),y=v=>H-B-v/max*(H-T-B); // Shared smoothstep interpolation preserves every monthly value, monotonicity, // and the ordering of the two timing bounds without cubic overshoot. function curve(vals,reverse=false){ const ids=vals.map((_,i)=>i);if(reverse)ids.reverse(); return ids.map((i,j)=>{if(!j)return `M${x(i)},${y(vals[i])}`;const prev=ids[j-1],dx=(x(i)-x(prev))/3;return `C${x(prev)+dx},${y(vals[prev])} ${x(i)-dx},${y(vals[i])} ${x(i)},${y(vals[i])}`}).join(' '); } const band=curve(early)+curve(late,true).replace(/^M/,'L')+'Z'; const area=curve(late)+`L${x(months.length-1)},${y(0)}L${x(0)},${y(0)}Z`; let svg=``; for(let i=0;i<=4;i++){let v=max*i/4;svg+=`${fmt(v)}`} months.forEach((m,i)=>{if(m.endsWith('-01')||i===months.length-1)svg+=`${m}`}); svg+=`Millions USD — scénario ${cost.options[cost.selectedIndex].text.toLowerCase()}`; const n=Number(slider.value);svg+=``;host.innerHTML=svg; const monthly=releases.filter(r=>r.month===months[n]&&(!org.value||r.organization===org.value)).sort((a,b)=>b.display_priority-a.display_priority); const names=[...new Set(monthly.map(r=>r.model))]; const monthLabel=new Date(months[n]+'-01T12:00:00Z').toLocaleDateString('fr-FR',{month:'long',year:'numeric',timeZone:'UTC'}); readout.textContent=`${monthLabel} · ${names.length?names.slice(0,5).join(' · ')+(names.length>5?` · +${names.length-5} autres`:''):'Aucune sortie répertoriée ce mois-ci'}`; readout.title=names.join(' · '); slider.setAttribute('aria-valuetext',readout.textContent); } org.addEventListener('change',render);cost.addEventListener('change',render);slider.addEventListener('input',render);render(); }catch(e){host.innerHTML='Cumul central avec sensibilité au calendrier';readout.textContent='Graphique statique : le chargement interactif a échoué.'} })();