{SPARK.map((h, i) => {
const tgt = TARGETS[targetIdx[i]];
const isWinner = WINNERS.includes(i);
// Original X (0..1 within the 16-bar strip, centered ~ 10–90%)
const baseX = 10 + (i / (SPARK.length - 1)) * 80;
// Position lerp: origin → cluster center
const x = lerp(baseX, tgt.x, easeInOut(merge));
// Height grows in phases:
// 1) rise to its spark height
// 2) climb to tall
// 3) for winners, expand to full slab; for losers, collapse
const sparkH = h / 100; // 0.14 → 0.86
const tallH = lerp(sparkH, 0.85, easeOut(tall)); // → 85% of canvas
const winnerH = lerp(tallH, 1.0, easeOut(finish)); // → 100%
const loserH = lerp(tallH, 0.0, easeInOut(merge)); // collapses to 0
const h01 = isWinner ? winnerH : loserH;
// Width: winners fatten into slabs, losers stay narrow
const baseW = 28;
const slabW = 240;
const w = isWinner
? lerp(baseW, slabW, easeOut(merge))
: baseW * (1 - easeInOut(merge));
// Tilt — winners pick up the diagonal slab clip near the end
const tilt = isWinner ? lerp(0, -6, easeOut(finish)) : 0;
// Colour blend — winners shift from spark gradient to slab gradient
const blend = isWinner ? easeOut(merge) : 0;
const grad = `linear-gradient(180deg,
color-mix(in oklab, #0FE872 ${(1-blend)*100}%, ${tgt.color1}) 0%,
color-mix(in oklab, rgba(255,186,0,0.4) ${(1-blend)*100}%, ${tgt.color2}) 100%)`;
// Opacity — losers fade as they collapse
const op = isWinner ? 1 : (1 - easeInOut(merge));
// Vertical rise from below
const baseRise = clamp01(rise * 2 - i * 0.02);
return (
);
})}
{/* Pillar labels — fade in only after winners have consolidated */}
{TARGETS.map((tg, i) => (
0{i+1}
{tg.label}
))}