ControlRL / app /src /content /embeds /layer-heatmap.html
seonglae's picture
Real seaborn mako palette, clean hero badge style
7fa533d
Raw
History Blame Contribute Delete
22.7 kB
<!--
Layer Γ— Coefficient Sensitivity β€” Multi-line Chart
Shows accuracy across layers for different steering coefficients.
Data: real experiment results from DGX (compare_layers.jsonl etc.)
-->
<div class="layer-sensitivity"></div>
<style>
.layer-sensitivity {
font-family: var(--default-font-family, system-ui, -apple-system, sans-serif);
position: relative;
max-width: 760px;
margin: 0 auto;
}
.layer-sensitivity .ls-tabs {
display: flex;
gap: 4px;
margin-bottom: 8px;
flex-wrap: wrap;
}
.layer-sensitivity .ls-tab {
padding: 6px 14px;
border-radius: 8px;
border: 1.5px solid var(--border-color, rgba(0,0,0,0.12));
background: transparent;
color: var(--muted-color, #666);
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
font-family: inherit;
}
.layer-sensitivity .ls-tab:hover {
border-color: var(--text-color, #333);
color: var(--text-color, #333);
}
.layer-sensitivity .ls-tab.active {
background: var(--text-color, #222);
color: var(--page-bg, #fff);
border-color: var(--text-color, #222);
}
.layer-sensitivity .ls-mode-row {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 12px;
}
.layer-sensitivity .ls-mode-btn {
padding: 4px 12px;
border-radius: 6px;
border: 1px solid var(--border-color, rgba(0,0,0,0.12));
background: transparent;
color: var(--muted-color, #888);
font-size: 12px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
font-family: inherit;
}
.layer-sensitivity .ls-mode-btn.active {
background: oklch(from var(--text-color, #222) l c h / 0.12);
color: var(--text-color, #222);
border-color: var(--text-color, #222);
font-weight: 600;
}
.layer-sensitivity .ls-chart-wrap {
position: relative;
}
.layer-sensitivity .ls-legend {
display: flex;
align-items: center;
gap: 12px;
margin-top: 8px;
flex-wrap: wrap;
font-size: 12px;
color: var(--muted-color, #888);
}
.layer-sensitivity .ls-legend-item {
display: inline-flex;
align-items: center;
gap: 4px;
cursor: pointer;
transition: opacity 0.2s;
}
.layer-sensitivity .ls-legend-item.dimmed {
opacity: 0.25;
}
.layer-sensitivity .ls-legend-swatch {
width: 18px;
height: 3px;
border-radius: 2px;
}
.layer-sensitivity .ls-legend-baseline {
display: inline-flex;
align-items: center;
gap: 4px;
margin-left: 8px;
}
.layer-sensitivity .ls-legend-dash {
width: 18px;
height: 0;
border-top: 2px dashed var(--muted-color, #aaa);
}
.layer-sensitivity .ls-tooltip {
position: absolute;
pointer-events: none;
padding: 8px 12px;
border-radius: 10px;
font-size: 12px;
line-height: 1.5;
background: var(--page-bg, #fff);
border: 1px solid var(--border-color, rgba(0,0,0,0.12));
box-shadow: 0 4px 16px rgba(0,0,0,0.12);
opacity: 0;
transition: opacity 0.12s;
z-index: 10;
white-space: nowrap;
}
</style>
<script>
(function() {
var root = document.currentScript ? document.currentScript.parentElement.querySelector('.layer-sensitivity') : document.querySelector('.layer-sensitivity');
if (!root || root.dataset.mounted) return;
root.dataset.mounted = 'true';
/* ── Inline data from DGX experiments ── */
var DATA = {"mmlu":{"layer":{"10":{"0":0.484119,"1":0.471799,"2":0.444666,"3":0.45777,"4":0.481555,"5":0.48419,"6":0.480487,"7":0.457841,"8":0.441461,"9":0.461259,"10":0.515382,"11":0.523857,"12":0.521721,"13":0.523715,"14":0.540593,"15":0.53867,"16":0.540949,"17":0.54629,"18":0.541874,"19":0.546432,"20":0.5475,"21":0.547073,"22":0.539097,"23":0.549708,"24":0.551702,"25":0.542943},"20":{"0":0.480986,"1":0.474149,"2":0.445378,"3":0.463538,"4":0.476428,"5":0.482125,"6":0.476428,"7":0.455989,"8":0.440179,"9":0.463538,"10":0.514528,"11":0.524213,"12":0.525139,"13":0.521222,"14":0.538242,"15":0.536177,"16":0.543085,"17":0.545364,"18":0.539239,"19":0.544937,"20":0.547358,"21":0.547572,"22":0.537886,"23":0.550919,"24":0.550064,"25":0.543726},"30":{"0":0.47949,"1":0.457413,"2":0.430281,"3":0.45606,"4":0.479348,"5":0.485615,"6":0.472226,"7":0.448796,"8":0.441248,"9":0.46425,"10":0.515382,"11":0.521293,"12":0.519655,"13":0.520011,"14":0.539595,"15":0.537245,"16":0.537601,"17":0.543655,"18":0.538527,"19":0.546931,"20":0.547287,"21":0.546717,"22":0.537886,"23":0.549993,"24":0.551987,"25":0.543227},"40":{"0":0.471656,"1":0.420097,"2":0.443883,"3":0.438043,"4":0.469449,"5":0.468096,"6":0.469662,"7":0.450007,"8":0.448156,"9":0.46425,"10":0.512605,"11":0.52229,"12":0.51759,"13":0.518658,"14":0.536391,"15":0.536462,"16":0.540593,"17":0.544438,"18":0.53632,"19":0.545008,"20":0.547572,"21":0.546646,"22":0.537958,"23":0.549708,"24":0.552699,"25":0.543299},"50":{"0":0.463039,"1":0.486683,"2":0.432916,"3":0.439254,"4":0.457983,"5":0.464749,"6":0.459977,"7":0.440464,"8":0.448868,"9":0.469164,"10":0.513175,"11":0.518872,"12":0.509614,"13":0.51581,"14":0.535251,"15":0.534895,"16":0.539952,"17":0.543014,"18":0.535679,"19":0.543085,"20":0.547144,"21":0.547002,"22":0.538955,"23":0.549566,"24":0.552557,"25":0.542943},"60":{"0":0.462541,"1":0.452215,"2":0.43199,"3":0.438043,"4":0.439182,"5":0.452713,"6":0.433556,"7":0.431562,"8":0.454779,"9":0.469662,"10":0.506481,"11":0.512961,"12":0.511822,"13":0.513673,"14":0.529198,"15":0.53226,"16":0.535679,"17":0.543156,"18":0.533044,"19":0.544082,"20":0.546859,"21":0.544723,"22":0.538598,"23":0.549637,"24":0.552628,"25":0.542729},"70":{"0":0.459764,"1":0.333571,"2":0.425367,"3":0.422233,"4":0.439966,"5":0.437901,"6":0.43142,"7":0.430922,"8":0.434126,"9":0.473722,"10":0.507264,"11":0.512961,"12":0.503347,"13":0.508332,"14":0.524356,"15":0.530124,"16":0.531477,"17":0.54045,"18":0.529839,"19":0.5428,"20":0.546503,"21":0.544509,"22":0.537103,"23":0.550491,"24":0.552485,"25":0.543085},"80":{"0":0.445877,"1":0.356359,"2":0.438399,"3":0.371101,"4":0.413759,"5":0.393391,"6":0.436049,"7":0.432417,"8":0.441248,"9":0.456345,"10":0.494588,"11":0.50235,"12":0.495585,"13":0.500641,"14":0.521792,"15":0.525709,"16":0.530053,"17":0.539239,"18":0.530551,"19":0.540165,"20":0.545649,"21":0.544296,"22":0.536818,"23":0.54921,"24":0.551702,"25":0.542017},"90":{"0":0.432061,"1":0.474434,"2":0.360063,"3":0.399587,"4":0.410483,"5":0.423302,"6":0.408987,"7":0.420667,"8":0.431491,"9":0.453639,"10":0.494659,"11":0.488534,"12":0.484333,"13":0.489959,"14":0.516379,"15":0.527774,"16":0.532403,"17":0.535394,"18":0.528842,"19":0.539168,"20":0.543726,"21":0.544153,"22":0.537958,"23":0.548925,"24":0.550919,"25":0.540521},"100":{"0":0.43085,"1":0.390044,"2":0.395955,"3":0.351802,"4":0.363979,"5":0.394459,"6":0.400798,"7":0.404287,"8":0.42907,"9":0.453354,"10":0.472725,"11":0.473223,"12":0.465033,"13":0.475787,"14":0.501852,"15":0.520367,"16":0.523287,"17":0.53575,"18":0.524854,"19":0.541874,"20":0.543441,"21":0.54223,"22":0.535536,"23":0.54864,"24":0.549138,"25":0.542159}}},"mmlu_const":{"layer":{"30":{"0":0.5315,"1":0.5293,"2":0.5244,"3":0.5247,"4":0.5372,"5":0.5356,"6":0.5307,"7":0.5251,"8":0.5253,"9":0.5267,"10":0.5308,"11":0.5282,"12":0.5209,"13":0.5221,"14":0.5401,"15":0.537,"16":0.5417,"17":0.5464,"18":0.5555,"19":0.5525,"20":0.5499,"21":0.5503,"22":0.5515,"23":0.5505,"24":0.5541,"25":0.5523},"40":{"0":0.5296,"1":0.5231,"2":0.5198,"3":0.5219,"4":0.5335,"5":0.5321,"6":0.5265,"7":0.5224,"8":0.5229,"9":0.5262,"10":0.5267,"11":0.5278,"12":0.5229,"13":0.5189,"14":0.5365,"15":0.5389,"16":0.5423,"17":0.5489,"18":0.5546,"19":0.5511,"20":0.5496,"21":0.5494,"22":0.5502,"23":0.5513,"24":0.5547,"25":0.5528},"50":{"0":0.5296,"1":0.5288,"2":0.5221,"3":0.5273,"4":0.5225,"5":0.5311,"6":0.5241,"7":0.5179,"8":0.5197,"9":0.5224,"10":0.5268,"11":0.5254,"12":0.5207,"13":0.5202,"14":0.5357,"15":0.5377,"16":0.54,"17":0.5437,"18":0.555,"19":0.5513,"20":0.5504,"21":0.5504,"22":0.5514,"23":0.5508,"24":0.555,"25":0.5529},"60":{"0":0.5252,"1":0.5113,"2":0.5084,"3":0.4978,"4":0.5192,"5":0.5269,"6":0.5192,"7":0.5162,"8":0.5108,"9":0.5197,"10":0.5276,"11":0.5218,"12":0.5193,"13":0.5138,"14":0.5373,"15":0.5377,"16":0.5424,"17":0.5459,"18":0.5538,"19":0.5533,"20":0.5502,"21":0.5503,"22":0.5512,"23":0.5503,"24":0.555,"25":0.5524},"70":{"0":0.523,"1":0.5132,"2":0.5027,"3":0.4824,"4":0.5119,"5":0.5174,"6":0.5135,"7":0.5085,"8":0.5105,"9":0.5169,"10":0.5239,"11":0.518,"12":0.5089,"13":0.5135,"14":0.5344,"15":0.5344,"16":0.536,"17":0.5444,"18":0.555,"19":0.5528,"20":0.5507,"21":0.5514,"22":0.5504,"23":0.5503,"24":0.5539,"25":0.5532},"80":{"0":0.5127,"1":0.4921,"2":0.4838,"3":0.4903,"4":0.5066,"5":0.5171,"6":0.5111,"7":0.499,"8":0.5028,"9":0.5162,"10":0.5184,"11":0.5179,"12":0.5103,"13":0.5099,"14":0.5322,"15":0.5353,"16":0.5397,"17":0.5469,"18":0.5533,"19":0.5523,"20":0.5486,"21":0.5498,"22":0.5495,"23":0.5496,"24":0.5547,"25":0.5527},"90":{"0":0.5083,"1":0.5024,"2":0.4878,"3":0.4928,"4":0.4954,"5":0.5082,"6":0.5019,"7":0.4948,"8":0.4981,"9":0.5078,"10":0.5135,"11":0.5122,"12":0.5066,"13":0.5043,"14":0.5261,"15":0.5306,"16":0.5335,"17":0.54,"18":0.5542,"19":0.5518,"20":0.5482,"21":0.5503,"22":0.5523,"23":0.5502,"24":0.5539,"25":0.5518},"100":{"0":0.5042,"1":0.4222,"2":0.4591,"3":0.447,"4":0.4909,"5":0.5049,"6":0.4999,"7":0.4878,"8":0.4888,"9":0.5018,"10":0.5149,"11":0.5098,"12":0.5011,"13":0.5,"14":0.526,"15":0.5312,"16":0.5368,"17":0.5426,"18":0.5501,"19":0.5527,"20":0.5498,"21":0.5503,"22":0.5516,"23":0.5492,"24":0.5543,"25":0.5524}}},"bbq_ambig":{"layer":{"20":{"0":0.527388,"1":0.487041,"2":0.47651,"3":0.496273,"4":0.55464,"5":0.542467,"6":0.462422,"7":0.421562,"8":0.364494,"9":0.348184,"10":0.363332,"11":0.383266,"12":0.382822,"13":0.384805,"14":0.395917,"15":0.448164,"16":0.483895,"17":0.433564,"18":0.609348,"19":0.599911,"20":0.613417,"21":0.618067,"22":0.598338,"23":0.600868,"24":0.589722,"25":0.585755},"30":{"0":0.520892,"1":0.482904,"2":0.452917,"3":0.4907,"4":0.549238,"5":0.530773,"6":0.455208,"7":0.413458,"8":0.355878,"9":0.346064,"10":0.364255,"11":0.384258,"12":0.38436,"13":0.38648,"14":0.395404,"15":0.449155,"16":0.483211,"17":0.43394,"18":0.610101,"19":0.599569,"20":0.613315,"21":0.618067,"22":0.597483,"23":0.601176,"24":0.589893,"25":0.585687},"40":{"0":0.507625,"1":0.48116,"2":0.455105,"3":0.477501,"4":0.523867,"5":0.50359,"6":0.440812,"7":0.394105,"8":0.344765,"9":0.340183,"10":0.364665,"11":0.387882,"12":0.388737,"13":0.388737,"14":0.397764,"15":0.448335,"16":0.480613,"17":0.431204,"18":0.610101,"19":0.599638,"20":0.613041,"21":0.617144,"22":0.597825,"23":0.601074,"24":0.590132,"25":0.58596},"50":{"0":0.50253,"1":0.424092,"2":0.43965,"3":0.45066,"4":0.516105,"5":0.506531,"6":0.429802,"7":0.382411,"8":0.348902,"9":0.337277,"10":0.36405,"11":0.39007,"12":0.39007,"13":0.390173,"14":0.401901,"15":0.450797,"16":0.480168,"17":0.433358,"18":0.610237,"19":0.599125,"20":0.612494}}},"bbq_disambig":{"layer":{"20":{"0":0.744888,"1":0.735519,"2":0.723757,"3":0.73275,"4":0.734733,"5":0.738152,"6":0.736169,"7":0.729057,"8":0.70543,"9":0.723757,"10":0.727997,"11":0.714115,"12":0.689428,"13":0.689838,"14":0.737092,"15":0.75053,"16":0.768344,"17":0.767284,"18":0.746393,"19":0.752821,"20":0.752923,"21":0.753847,"22":0.752958,"23":0.753642,"24":0.752547,"25":0.752034},"30":{"0":0.740341,"1":0.731827,"2":0.719141,"3":0.730049,"4":0.735041,"5":0.732989,"6":0.734083,"7":0.726356,"8":0.701498,"9":0.719141,"10":0.725672,"11":0.713021,"12":0.68741,"13":0.689291,"14":0.73487,"15":0.750085,"16":0.768652,"17":0.765712,"18":0.746051,"19":0.753436,"20":0.753744,"21":0.75371,"22":0.752923,"23":0.753676,"24":0.7533,"25":0.752684},"40":{"0":0.734767,"1":0.729707,"2":0.729536,"3":0.706934,"4":0.7175,"5":0.728954,"6":0.725022,"7":0.717431,"8":0.688162,"9":0.707174,"10":0.72068,"11":0.705977,"12":0.685359,"13":0.683683,"14":0.731519,"15":0.747829,"16":0.767729,"17":0.767113,"18":0.746393}}}};
var BASELINES = {
mmlu: 0.5206,
mmlu_const: 0.5519,
bbq_ambig: 0.6017,
bbq_disambig: 0.7542
};
var TASK_LABELS = {
mmlu: 'MMLU',
mmlu_const: 'MMLU (Constrained)',
bbq_ambig: 'BBQ (Ambiguous)',
bbq_disambig: 'BBQ (Disambiguated)'
};
var currentTask = 'mmlu';
var currentMode = 'layer';
var disabledCoeffs = {};
/* Mako palette (seaborn): dark purple β†’ indigo β†’ teal β†’ mint */
var COEFF_COLORS = {
10: '#1c101d', 15: '#2c1d37', 20: '#382a54', 25: '#3f3872',
30: '#3f488e', 40: '#395d9b', 45: '#3570a0', 50: '#3483a4',
60: '#3496a9', 70: '#38a9ac', 75: '#44bcad', 80: '#5fceac',
90: '#90dbb3', 100: '#bbe7c8'
};
function coeffColor(c) { return COEFF_COLORS[c] || '#888'; }
/* ── Build DOM ── */
var tabs = document.createElement('div');
tabs.className = 'ls-tabs';
root.appendChild(tabs);
var modeRow = document.createElement('div');
modeRow.className = 'ls-mode-row';
root.appendChild(modeRow);
var chartWrap = document.createElement('div');
chartWrap.className = 'ls-chart-wrap';
root.appendChild(chartWrap);
var legend = document.createElement('div');
legend.className = 'ls-legend';
root.appendChild(legend);
var tooltip = document.createElement('div');
tooltip.className = 'ls-tooltip';
root.appendChild(tooltip);
/* ── Tabs ── */
function renderTabs() {
tabs.innerHTML = '';
Object.keys(TASK_LABELS).forEach(function(task) {
var btn = document.createElement('button');
btn.className = 'ls-tab' + (task === currentTask ? ' active' : '');
btn.textContent = TASK_LABELS[task];
btn.addEventListener('click', function() {
currentTask = task;
disabledCoeffs = {};
pickDefaultMode();
renderAll();
});
tabs.appendChild(btn);
});
}
function pickDefaultMode() {
currentMode = 'layer';
}
function renderModeButtons() {
modeRow.innerHTML = '';
}
function getActiveData() {
var td = DATA[currentTask];
if (!td) return null;
return td.layer || null;
}
/* ── Chart rendering with D3 ── */
function renderChart() {
chartWrap.innerHTML = '';
var activeData = getActiveData();
if (!activeData) {
chartWrap.innerHTML = '<div style="padding:24px;color:var(--muted-color);text-align:center;">No data</div>';
return;
}
var coeffs = Object.keys(activeData).map(Number).sort(function(a,b){return a-b;});
var layers = [];
coeffs.forEach(function(c) {
Object.keys(activeData[String(c)]).forEach(function(l) {
var ln = Number(l);
if (layers.indexOf(ln) === -1) layers.push(ln);
});
});
layers.sort(function(a,b){return a-b;});
var baseline = BASELINES[currentTask];
/* SVG dimensions */
var W = 720, H = 360;
var margin = {top: 16, right: 20, bottom: 44, left: 54};
var iw = W - margin.left - margin.right;
var ih = H - margin.top - margin.bottom;
var svg = d3.select(chartWrap).append('svg')
.attr('viewBox', '0 0 ' + W + ' ' + H)
.attr('width', '100%')
.style('display', 'block');
var g = svg.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
/* Scales */
var xScale = d3.scaleLinear().domain([d3.min(layers), d3.max(layers)]).range([0, iw]);
var allVals = [];
coeffs.forEach(function(c) {
if (disabledCoeffs[c]) return;
var cd = activeData[String(c)];
layers.forEach(function(l) {
var v = cd[String(l)];
if (v != null) allVals.push(v);
});
});
if (baseline != null) allVals.push(baseline);
var yMin = d3.min(allVals) - 0.01;
var yMax = d3.max(allVals) + 0.01;
var yScale = d3.scaleLinear().domain([yMin, yMax]).range([ih, 0]).nice();
/* Grid lines */
var yTicks = yScale.ticks(6);
yTicks.forEach(function(t) {
g.append('line')
.attr('x1', 0).attr('x2', iw)
.attr('y1', yScale(t)).attr('y2', yScale(t))
.attr('stroke', 'var(--border-color, rgba(0,0,0,0.08))')
.attr('stroke-dasharray', '2,3');
});
/* Baseline (only if defined for this task) */
if (baseline != null) {
g.append('line')
.attr('x1', 0).attr('x2', iw)
.attr('y1', yScale(baseline)).attr('y2', yScale(baseline))
.attr('stroke', 'var(--muted-color, #aaa)')
.attr('stroke-width', 1.5)
.attr('stroke-dasharray', '6,4');
g.append('text')
.attr('x', iw + 4).attr('y', yScale(baseline))
.attr('dy', '0.35em')
.attr('font-size', '10px')
.attr('fill', 'var(--muted-color, #aaa)')
.text('Base');
}
/* Line generator */
var line = d3.line()
.x(function(d){ return xScale(d.layer); })
.y(function(d){ return yScale(d.acc); })
.defined(function(d){ return d.acc != null; })
.curve(d3.curveMonotoneX);
/* Draw lines */
coeffs.forEach(function(c) {
if (disabledCoeffs[c]) return;
var cd = activeData[String(c)];
var pts = layers.map(function(l) {
return { layer: l, acc: cd[String(l)] || null };
}).filter(function(d){ return d.acc != null; });
var color = coeffColor(c);
g.append('path')
.datum(pts)
.attr('fill', 'none')
.attr('stroke', color)
.attr('stroke-width', 2)
.attr('stroke-linecap', 'round')
.attr('d', line)
.attr('opacity', 0.85);
/* Dots */
g.selectAll('.dot-c' + c)
.data(pts).enter()
.append('circle')
.attr('cx', function(d){ return xScale(d.layer); })
.attr('cy', function(d){ return yScale(d.acc); })
.attr('r', 2.5)
.attr('fill', color)
.attr('stroke', color)
.attr('stroke-width', 0.5);
});
/* Axes */
var xAxis = d3.axisBottom(xScale)
.ticks(13)
.tickFormat(function(d){ return d; });
g.append('g')
.attr('transform', 'translate(0,' + ih + ')')
.call(xAxis)
.selectAll('text')
.attr('fill', 'var(--tick-color, var(--text-color, #333))')
.attr('font-size', '11px');
g.append('text')
.attr('x', iw / 2).attr('y', ih + 36)
.attr('text-anchor', 'middle')
.attr('fill', 'var(--text-color, #333)')
.attr('font-size', '12px')
.attr('font-weight', 600)
.text('Layer');
var yAxis = d3.axisLeft(yScale)
.ticks(6)
.tickFormat(function(d){ return (d * 100).toFixed(1); });
g.append('g')
.call(yAxis)
.selectAll('text')
.attr('fill', 'var(--tick-color, var(--text-color, #333))')
.attr('font-size', '11px');
g.append('text')
.attr('transform', 'translate(-40,' + (ih / 2) + ') rotate(-90)')
.attr('text-anchor', 'middle')
.attr('fill', 'var(--text-color, #333)')
.attr('font-size', '12px')
.attr('font-weight', 600)
.text('Accuracy (%)');
/* Style axis lines */
svg.selectAll('.domain').attr('stroke', 'var(--border-color, rgba(0,0,0,0.15))');
svg.selectAll('.tick line').attr('stroke', 'var(--border-color, rgba(0,0,0,0.08))');
/* Hover interaction */
var hoverLine = g.append('line')
.attr('y1', 0).attr('y2', ih)
.attr('stroke', 'var(--text-color, #333)')
.attr('stroke-width', 0.8)
.attr('stroke-dasharray', '3,3')
.attr('opacity', 0);
var hoverRect = g.append('rect')
.attr('width', iw).attr('height', ih)
.attr('fill', 'none')
.attr('pointer-events', 'all');
hoverRect.on('mousemove', function(event) {
var coords = d3.pointer(event);
var mx = coords[0];
var layerVal = Math.round(xScale.invert(mx));
layerVal = Math.max(d3.min(layers), Math.min(d3.max(layers), layerVal));
hoverLine.attr('x1', xScale(layerVal)).attr('x2', xScale(layerVal)).attr('opacity', 1);
var html = '<div style="font-weight:600;margin-bottom:4px;">Layer ' + layerVal + '</div>';
var activeCoeffs = coeffs.filter(function(c){ return !disabledCoeffs[c]; });
activeCoeffs.forEach(function(c) {
var cd = activeData[String(c)];
var val = cd[String(layerVal)];
if (val != null) {
var deltaHtml = '';
if (baseline != null) {
var delta = val - baseline;
var sign = delta >= 0 ? '+' : '';
var dColor = delta >= 0 ? '#10b981' : '#ef4444';
deltaHtml = '<span style="color:' + dColor + ';font-size:11px;">' + sign + (delta * 100).toFixed(2) + '</span>';
}
html += '<div style="display:flex;align-items:center;gap:6px;">' +
'<span style="width:8px;height:8px;border-radius:50%;background:' + coeffColor(c) + ';flex-shrink:0;"></span>' +
'<span style="min-width:24px;">c=' + c + '</span>' +
'<span style="font-weight:600;">' + (val * 100).toFixed(2) + '%</span>' +
deltaHtml +
'</div>';
}
});
tooltip.innerHTML = html;
tooltip.style.opacity = '1';
var wrapRect = chartWrap.getBoundingClientRect();
var rootRect = root.getBoundingClientRect();
var tx = coords[0] + margin.left + 16;
var ty = coords[1] + margin.top - 10;
var tipW = tooltip.offsetWidth || 180;
if (tx + tipW > wrapRect.width) tx = coords[0] + margin.left - tipW - 12;
tooltip.style.left = tx + 'px';
tooltip.style.top = ty + 'px';
});
hoverRect.on('mouseleave', function() {
hoverLine.attr('opacity', 0);
tooltip.style.opacity = '0';
});
}
/* ── Legend ── */
function renderLegend() {
legend.innerHTML = '';
var activeData = getActiveData();
if (!activeData) return;
var coeffs = Object.keys(activeData).map(Number).sort(function(a,b){return a-b;});
coeffs.forEach(function(c) {
var item = document.createElement('span');
item.className = 'ls-legend-item' + (disabledCoeffs[c] ? ' dimmed' : '');
item.innerHTML = '<span class="ls-legend-swatch" style="background:' + coeffColor(c) + ';"></span>' +
'<span>' + c + '</span>';
item.addEventListener('click', function() {
disabledCoeffs[c] = !disabledCoeffs[c];
renderAll();
});
legend.appendChild(item);
});
/* Baseline legend entry (only if baseline exists) */
if (BASELINES[currentTask] != null) {
var bl = document.createElement('span');
bl.className = 'ls-legend-baseline';
bl.innerHTML = '<span class="ls-legend-dash"></span><span>Baseline (' + (BASELINES[currentTask] * 100).toFixed(1) + '%)</span>';
legend.appendChild(bl);
}
}
/* ── Render all ── */
function renderAll() {
renderTabs();
renderModeButtons();
renderChart();
renderLegend();
}
function waitForD3() {
if (window.d3 && typeof d3.select === 'function') {
renderAll();
} else {
setTimeout(waitForD3, 100);
}
}
waitForD3();
})();
</script>