/** * Stable gradient + accent derived from a game id. Used as cover-art fallback * when the backend has no thumbnail for a game. */ export interface CoverColors { c1: string; c2: string; accent: string; angle: number; blobX: number; blobY: number; } const PALETTE: Array> = [ { c1: '#7c2d12', c2: '#1c1917', accent: '#fbbf24' }, { c1: '#1e40af', c2: '#0c1f3a', accent: '#22d3ee' }, { c1: '#15803d', c2: '#052e16', accent: '#fef08a' }, { c1: '#7f1d1d', c2: '#0a0a0a', accent: '#f97316' }, { c1: '#1d4ed8', c2: '#0c1f3a', accent: '#f59e0b' }, { c1: '#a16207', c2: '#422006', accent: '#fde047' }, { c1: '#3f3f46', c2: '#0a0a0a', accent: '#22d3ee' }, { c1: '#ea580c', c2: '#1e293b', accent: '#22d3ee' }, { c1: '#064e3b', c2: '#020617', accent: '#34d399' }, { c1: '#1e3a8a', c2: '#172554', accent: '#22d3ee' }, ]; const hash = (id: string): number => { let h = 0; for (let i = 0; i < id.length; i++) h = (h * 31 + id.charCodeAt(i)) >>> 0; return h; }; export const coverColorsFor = (id: string): CoverColors => { const h = hash(id); const base = PALETTE[h % PALETTE.length]; return { ...base, angle: 110 + (h % 60), blobX: 60 + (h % 30), blobY: 10 + ((h * 7) % 30), }; }; export const titleFontSize = ( title: string, aspect: 'box' | 'square' | 'banner', ): number => { const len = title.length; if (aspect === 'banner' || aspect === 'square') { if (len > 22) return 18; if (len > 14) return 22; return 28; } if (len > 26) return 15; if (len > 20) return 17; if (len > 14) return 21; return 26; };