diff --git a/src/client/components/trendChart.tsx b/src/client/components/trendChart.tsx
index 787cf0f..b6f884a 100644
--- a/src/client/components/trendChart.tsx
+++ b/src/client/components/trendChart.tsx
@@ -275,10 +275,14 @@ export function makeTrendChart(kit: ViewKit): (props: TrendChartProps) => ReactE
const visible: TrendBand[] = []
for (const b of props.bands) if (b.tokens > 0) visible.push(b)
if (visible.length === 0) return null
+ // Shared per-token pixel scale (CHART_H/maxTotal), not per-bar percentages: the same band's stops are
+ // identical numbers in every bar, so the browser rasterizes common strands identically (per-bar %
+ // stops quantized against each bar's own total and made sub-pixel strands flip visible/merged).
+ const pxPerToken = CHART_H / props.maxTotal
const runs: { color: string; from: number; to: number }[] = []
for (const b of visible) {
- const from = Math.round(b.off / props.total * 10000) / 100
- const to = Math.round((b.off + b.tokens) / props.total * 10000) / 100
+ const from = Math.round(b.off * pxPerToken * 100) / 100
+ const to = Math.round((b.off + b.tokens) * pxPerToken * 100) / 100
const last = runs.length > 0 ? runs[runs.length - 1] : null
if (last !== null && last.color === b.color && last.to === from) last.to = to
else runs.push({ color: b.color, from, to })
@@ -299,8 +303,8 @@ export function makeTrendChart(kit: ViewKit): (props: TrendChartProps) => ReactE
`${r.color} ${r.from}%, ${r.color} ${r.to}%`).join(', ') + ')',
+ height: `${Math.max(1, Math.round(props.total * pxPerToken))}px`,
+ background: 'linear-gradient(to top, ' + runs.map(r => `${r.color} ${r.from}px, ${r.color} ${r.to}px`).join(', ') + ')',
'--lc-i': Math.min(props.enterIndex, STAGGER_CAP),
} as CSSProperties}
onMouseMove={(e) => { props.onHit(hitAt(e).key) }}
diff --git a/tests/client/components/trendChartDna.spec.ts b/tests/client/components/trendChartDna.spec.ts
index 868c0ce..e8e0dea 100644
--- a/tests/client/components/trendChartDna.spec.ts
+++ b/tests/client/components/trendChartDna.spec.ts
@@ -52,17 +52,17 @@ describe('TrendChart DNA mode', () => {
assert.equal(dnaDivs.length, 2)
assert.equal(dnaDivs[0].style.height, `${Math.round(300 / 600 * CHART_H)}px`)
assert.equal(dnaDivs[1].style.height, `${CHART_H}px`)
- // d1: the two adjacent assistant bands coalesce into ONE blue run (50% → 100%).
+ // d1: the two adjacent assistant bands coalesce into ONE blue run (28px → 56px on the shared scale).
const g1 = dnaDivs[0].style.background
assert.ok(g1.startsWith('linear-gradient(to top, '), g1)
- assert.ok(g1.includes('var(--color-indigo-500) 0%, var(--color-indigo-500) 33.33%'), g1)
- assert.ok(g1.includes('var(--color-amber-500) 33.33%, var(--color-amber-500) 50%'), g1)
- assert.ok(g1.includes('var(--color-blue-500) 50%, var(--color-blue-500) 100%'), g1)
+ assert.ok(g1.includes('var(--color-indigo-500) 0px, var(--color-indigo-500) 18.67px'), g1)
+ assert.ok(g1.includes('var(--color-amber-500) 18.67px, var(--color-amber-500) 28px'), g1)
+ assert.ok(g1.includes('var(--color-blue-500) 28px, var(--color-blue-500) 56px'), g1)
// d2: the zero-token user band leaves no run of its own; the teal run starts at its offset.
const g2 = dnaDivs[1].style.background
- assert.ok(g2.includes('var(--color-blue-500) 33.33%, var(--color-blue-500) 46.67%'), g2)
- assert.ok(g2.includes('var(--color-green-500) 46.67%, var(--color-green-500) 63.33%'), g2)
- assert.ok(g2.includes('var(--color-teal-500) 63.33%, var(--color-teal-500) 100%'), g2)
+ assert.ok(g2.includes('var(--color-blue-500) 37.33px, var(--color-blue-500) 52.27px'), g2)
+ assert.ok(g2.includes('var(--color-green-500) 52.27px, var(--color-green-500) 70.93px'), g2)
+ assert.ok(g2.includes('var(--color-teal-500) 70.93px, var(--color-teal-500) 112px'), g2)
assert.equal(queryAll(m.container, '.lc-bar-stack').length, 0)
assert.equal(query(m.container, '.lc-axis-top').textContent, '600')
// Without an onPickBand handler a band click is a quiet no-op (the pick guard's undefined arm).