charts.js
'use strict';
/* All geometry is derived from the frozen specification fixture, never from invented history. */
window.renderStudyChart=function(id,{mini=false,uid='main'}={}){
const b=window.FOUNDATION.baseline,m=b.summary,values=[b.base.recourseManYen,b.base.nonRecourseManYen],total=values[0]+values[1],p=values[0]/total;
const colors=['#b8780a','#0b8ca3'],light=['#edbd63','#75d6e4'],dark=['#825003','#076275'];
const names=['リコース','ノンリコース'],nums=[m.recourse,m.nonRecourse];
const n=Number(id),pref='c'+uid.replace(/[^a-z0-9]/gi,'')+n;
const attr=i=>mini?`data-series="${i}"`:`data-series="${i}" data-pick="${i}" tabindex="0" role="button" aria-label="${names[i]} ${nums[i]}億円を強調"`;
const txt=(x,y,t,cls='',anchor='start')=>`<text x="${x}" y="${y}" text-anchor="${anchor}" class="${cls}">${t}</text>`;
let body='';
const defs=`<defs>${colors.map((c,i)=>`<linearGradient id="${pref}g${i}" x1="0" x2="1"><stop stop-color="${light[i]}"/><stop offset="1" stop-color="${c}"/></linearGradient>`).join('')}<filter id="${pref}s"><feDropShadow dx="0" dy="8" stdDeviation="9" flood-opacity=".10"/></filter></defs>`;
const axis=()=>[0,50,100,150,200].map(v=>`<line x1="105" x2="650" y1="${290-v*1.1}" y2="${290-v*1.1}" stroke="#dce3e4"/>${txt(82,296-v*1.1,String(v),'axis','end')}`).join('');
if(n===1){
body=axis()+[0,1].map(i=>{const x=190+i*270,h=values[i]/10000*1.1,y=290-h;return `<g ${attr(i)} filter="url(#${pref}s)"><polygon points="${x},${y} ${x+100},${y} ${x+125},${y-20} ${x+25},${y-20}" fill="${light[i]}"/><polygon points="${x+100},${y} ${x+125},${y-20} ${x+125},270 ${x+100},290" fill="${dark[i]}"/><rect x="${x}" y="${y}" width="100" height="${h}" fill="url(#${pref}g${i})" rx="2"/>${txt(x+48,y-36,nums[i],'value','middle')}${txt(x+50,329,names[i],'label','middle')}</g>`;}).join('');
}else if(n===2){
const circ=2*Math.PI*108,len=p*circ;
body=`<g transform="translate(360 160) scale(1.6 .86)"><circle cy="28" r="108" fill="none" stroke="#cfd9db" stroke-width="48"/><circle r="108" fill="none" stroke="#eff3f2" stroke-width="48"/>${[0,1].map(i=>`<circle ${attr(i)} r="108" fill="none" stroke="url(#${pref}g${i})" stroke-width="48" stroke-dasharray="${i?circ-len:len} ${i?len:circ-len}" stroke-dashoffset="${i?-len:0}" transform="rotate(-90)"/>`).join('')}</g>${txt(360,160,m.aum,'big-value','middle')}${txt(360,187,'億円','label','middle')}${txt(360,312,'厚みを添えた構成比。比率は円周で表現。','label','middle')}`;
}else if(n===3){
const h=220,h0=h*p,h1=h-h0;
body=`<rect x="65" y="60" width="28" height="220" rx="6" fill="#233e37"/>${txt(60,42,'AUM '+m.aum,'value')}`+[0,1].map(i=>{const sy=60+(i?h0:0),ey=i?230:45,hh=i?h1:h0;return `<g ${attr(i)}><path d="M93 ${sy} C300 ${sy},360 ${ey},570 ${ey} L570 ${ey+hh} C360 ${ey+hh},300 ${sy+hh},93 ${sy+hh}Z" fill="url(#${pref}g${i})" opacity=".82"/><rect x="570" y="${ey}" width="26" height="${hh}" rx="4" fill="${colors[i]}"/>${txt(618,ey+hh/2-8,names[i],'label')}${txt(618,ey+hh/2+20,nums[i],'value')}</g>`;}).join('');
}else if(n===4){
const w=600*p;
body=`<rect x="50" y="48" width="620" height="246" rx="12" fill="#e8edeb"/>${[0,1].map(i=>{const x=i?60+w:60,ww=i?600-w:w;return `<g ${attr(i)}><rect x="${x}" y="58" width="${ww}" height="226" rx="5" fill="url(#${pref}g${i})"/><path d="M${x+14} 270 L${x+ww-14} 270" stroke="#ffffff80"/>${txt(x+ww/2,150,nums[i],'white large','middle')}${txt(x+ww/2,184,names[i],'white label','middle')}</g>`;}).join('')}`;
}else if(n===5){
body=[0,1].map(i=>{const x=205+i*310,pc=i?1-p:p;return `<g ${attr(i)}><circle cx="${x}" cy="150" r="94" fill="none" stroke="#e5ece9" stroke-width="19"/><circle cx="${x}" cy="150" r="94" fill="none" stroke="url(#${pref}g${i})" stroke-width="19" pathLength="100" stroke-dasharray="${pc*100} ${100-pc*100}" stroke-linecap="round" transform="rotate(-90 ${x} 150)"/>${txt(x,150,(pc*100).toFixed(1)+'%','big-value','middle')}${txt(x,181,names[i],'label','middle')}${txt(x,295,nums[i]+' 億円','value','middle')}</g>`;}).join('');
}else if(n===6||n===10){
body=[0,50,100,150,200].map(v=>{const x=145+v*2.25;return `<line x1="${x}" x2="${x}" y1="60" y2="275" stroke="#dce3e4"/>${txt(x,316,String(v),'axis','middle')}`;}).join('')+[0,1].map(i=>{const x=145+values[i]/10000*2.25,y=105+i*130;return `<g ${attr(i)}><path d="M145 ${y} L${x} ${y}" stroke="${colors[i]}" stroke-width="${n===10?26:4}" stroke-linecap="round" opacity="${n===10?.3:1}"/><circle cx="${x}" cy="${y}" r="${n===10?22:16}" fill="url(#${pref}g${i})" stroke="white" stroke-width="4"/><line x1="${x}" x2="${x}" y1="${y+22}" y2="280" stroke="${colors[i]}" stroke-dasharray="4 5"/>${txt(x,y-34,nums[i],'value','middle')}${txt(130,y+6,names[i],'label','end')}</g>`;}).join('');
if(n===10)body=`<g class="zoom-geometry" transform="translate(360 170) scale(1) translate(-360 -170)">${body}</g><path d="M345 15h30 M360 0v30 M345 335h30 M360 320v30" stroke="#38584d" stroke-width="2"/>`;
}else if(n===7){
body=[0,1].map(i=>{const r=Math.sqrt(values[i]/total)*150,x=i?520:215;return `<g ${attr(i)}><circle cx="${x}" cy="167" r="${r}" fill="url(#${pref}g${i})" filter="url(#${pref}s)"/>${txt(x,164,nums[i],'white large','middle')}${txt(x,196,names[i],'white label','middle')}${txt(x,325,'面積 '+(values[i]/total*100).toFixed(1)+'%','label','middle')}</g>`;}).join('');
}else if(n===8){
body=Array.from({length:500},(_,k)=>{const i=k<339?0:1;return `<circle data-series="${i}" cx="${58+(k%50)*12.3}" cy="${70+Math.floor(k/50)*22}" r="4.8" fill="${colors[i]}"/>`;}).join('')+txt(360,320,'500点の構成マトリクス / 1点 = 0.2%','label','middle');
}else{
const w=580*p;
body=[0,1].map(i=>{const x=i?65+w:65,ww=i?580-w:w;return `<g ${attr(i)}><polygon points="${x},140 ${x+ww},140 ${x+ww+20},110 ${x+20},110" fill="${light[i]}"/><polygon points="${x+ww},140 ${x+ww+20},110 ${x+ww+20},190 ${x+ww},220" fill="${dark[i]}"/><rect x="${x}" y="140" width="${ww}" height="80" fill="url(#${pref}g${i})"/>${txt(x+ww/2,187,(values[i]/total*100).toFixed(1)+'%','white value','middle')}${txt(x+ww/2,275,names[i],'label','middle')}${txt(x+ww/2,303,nums[i]+' 億円','value','middle')}</g>`;}).join('');
}
return `<svg class="study-chart ${mini?'mini-chart':''}" viewBox="0 0 760 350" ${mini?'aria-hidden="true"':`role="group" aria-label="${window.STUDIES.charts[n-1].name}。リコース${m.recourse}億円、ノンリコース${m.nonRecourse}億円"`}><style>.study-chart text{font-family:Geist,'Noto Sans JP',sans-serif;fill:#22372f;font-size:17px}.study-chart .value{font-size:24px;font-weight:600}.study-chart .big-value{font-size:34px;font-weight:600}.study-chart .large{font-size:32px;font-weight:600}.study-chart .white{fill:#142e27;paint-order:stroke;stroke:#ffffffd9;stroke-width:4px;stroke-linejoin:round}.study-chart .axis{fill:#54655e;font-size:15px}</style>${defs}${body}</svg>`;
};