/** Deterministic identity visuals: initials + a stable hue from the name. */ export function initials(name: string): string { const words = name.trim().split(/\s+/).filter((w) => w !== '') if (words.length === 0) return '?' const first = words[0]!.charAt(0) const second = words.length > 1 ? words[1]!.charAt(0) : '' return (first + second).toUpperCase() } /** FNV-1a over the name → hue bucket; same name always gets the same colour. */ export function avatarHue(name: string): number { let h = 0x811c9dc5 for (let i = 0; i < name.length; i++) { h ^= name.charCodeAt(i) h = Math.imul(h, 0x01000193) } return (h >>> 0) % 360 } /** Soft tinted background + readable foreground for both themes. */ export function avatarColors(name: string): { bg: string; fg: string } { const h = avatarHue(name) return { bg: `hsl(${h} 42% 50% / 0.16)`, fg: `light-dark(hsl(${h} 45% 34%), hsl(${h} 45% 74%))`, } }