/** Parse #RRGGBB to RGB components */ function hexToRgb(hex: string): [number, number, number] { const n = hex.replace("#", ""); if (n.length !== 6) return [0, 0, 0]; return [ Number.parseInt(n.slice(0, 2), 16), Number.parseInt(n.slice(2, 4), 16), Number.parseInt(n.slice(4, 6), 16), ]; } function rgbToHex(r: number, g: number, b: number): string { const c = (x: number) => Math.max(0, Math.min(255, Math.round(x))).toString(16).padStart(2, "0"); return `#${c(r)}${c(g)}${c(b)}`; } /** Linear mix between two hex colors (0 = a, 1 = b). */ export function mixHex(a: string, b: string, t: number): string { const u = Math.max(0, Math.min(1, t)); const [ar, ag, ab] = hexToRgb(a); const [br, bg, bb] = hexToRgb(b); return rgbToHex( ar + (br - ar) * u, ag + (bg - ag) * u, ab + (bb - ab) * u ); }