Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
| /** | |
| * Letter grades for ACS Gini index of income inequality (household), raw 0–1 scale. | |
| * Lower Gini ⇒ more equal ⇒ better letter. Buckets are fixed (not percentile-based) so | |
| * the same value always maps to the same letter across map and scorecard. | |
| */ | |
| export type GiniLetter = 'A' | 'B' | 'C' | 'D' | 'F' | |
| export type GiniLetterGrade = { | |
| letter: GiniLetter | |
| blurb: string | |
| letterClass: string | |
| blurbClass: string | |
| chipClass: string | |
| } | |
| /** Horizontal legend strip: letter + optional “ = …” tail (scorecard / map). */ | |
| export type GiniLetterStripRow = { | |
| letter: GiniLetter | |
| letterClass: string | |
| /** e.g. " = Very equal" — omit for middle letters to match compact layout */ | |
| tail: string | |
| } | |
| export const GINI_LETTER_STRIP: readonly GiniLetterStripRow[] = [ | |
| { letter: 'A', letterClass: 'text-emerald-700', tail: ' = Very equal' }, | |
| { letter: 'B', letterClass: 'text-lime-700', tail: '' }, | |
| { letter: 'C', letterClass: 'text-amber-700', tail: '' }, | |
| { letter: 'D', letterClass: 'text-orange-700', tail: '' }, | |
| { letter: 'F', letterClass: 'text-rose-700', tail: ' = High gap' }, | |
| ] as const | |
| const BUCKETS: { letter: GiniLetter; max: number; blurb: string; letterClass: string; blurbClass: string; chipClass: string }[] = | |
| [ | |
| { | |
| letter: 'A', | |
| max: 0.43, | |
| blurb: 'Very equal', | |
| letterClass: 'text-emerald-700', | |
| blurbClass: 'text-emerald-800', | |
| chipClass: 'border-emerald-200 bg-emerald-50 text-emerald-900', | |
| }, | |
| { | |
| letter: 'B', | |
| max: 0.455, | |
| blurb: 'More equal than typical', | |
| letterClass: 'text-lime-700', | |
| blurbClass: 'text-lime-900', | |
| chipClass: 'border-lime-200 bg-lime-50 text-lime-950', | |
| }, | |
| { | |
| letter: 'C', | |
| max: 0.48, | |
| blurb: 'Typical inequality', | |
| letterClass: 'text-amber-700', | |
| blurbClass: 'text-amber-900', | |
| chipClass: 'border-amber-200 bg-amber-50 text-amber-950', | |
| }, | |
| { | |
| letter: 'D', | |
| max: 0.505, | |
| blurb: 'Less equal than typical', | |
| letterClass: 'text-orange-700', | |
| blurbClass: 'text-orange-900', | |
| chipClass: 'border-orange-200 bg-orange-50 text-orange-950', | |
| }, | |
| { | |
| letter: 'F', | |
| max: Infinity, | |
| blurb: 'High gap', | |
| letterClass: 'text-rose-700', | |
| blurbClass: 'text-rose-900', | |
| chipClass: 'border-rose-200 bg-rose-50 text-rose-950', | |
| }, | |
| ] | |
| /** Map / compact table: letter + numeric bucket hint + chip colors. */ | |
| export const GINI_CHOROPLETH_LEGEND_CHIPS: readonly { letter: GiniLetter; hint: string; chipClass: string }[] = | |
| BUCKETS.map((b) => ({ | |
| letter: b.letter, | |
| hint: b.letter === 'F' ? '> 0.505' : `≤ ${b.max}`, | |
| chipClass: b.chipClass, | |
| })) | |
| export function giniLetterGradeFromValue(gini: number | null | undefined): GiniLetterGrade | null { | |
| if (gini == null || !Number.isFinite(gini)) return null | |
| const g = gini | |
| for (const b of BUCKETS) { | |
| if (g <= b.max) { | |
| return { | |
| letter: b.letter, | |
| blurb: b.blurb, | |
| letterClass: b.letterClass, | |
| blurbClass: b.blurbClass, | |
| chipClass: b.chipClass, | |
| } | |
| } | |
| } | |
| const last = BUCKETS[BUCKETS.length - 1]! | |
| return { | |
| letter: last.letter, | |
| blurb: last.blurb, | |
| letterClass: last.letterClass, | |
| blurbClass: last.blurbClass, | |
| chipClass: last.chipClass, | |
| } | |
| } | |
| /** Short suffix for tooltips / compact values, e.g. `` · A``. */ | |
| export function giniLetterSuffix(gini: number | null | undefined): string { | |
| const m = giniLetterGradeFromValue(gini) | |
| return m ? ` · ${m.letter}` : '' | |
| } | |