'use client'; import { ArrowDown, ArrowUp } from 'lucide-react'; import Link from 'next/link'; import { t } from '@/i18n'; import { cn } from '@/lib/cn'; import { displayValue, isNum, ordinal } from '@/lib/format'; import { routes } from '@/lib/site'; import type { MetricValue } from '@/lib/types'; import type { CompareSnapshotRow, CountryLite } from '@/lib/types-compare'; import { seriesVar } from '@/components/charts/palette'; import { payloadFor } from '@/components/data/metric'; import { useProvenance } from '@/components/data/provenance-context'; import { chartHrefFor } from './snapshot-table'; import type { CompareState } from '@/lib/compare-state'; /** * Head-to-head view for exactly two countries: the indicator in the middle, one value per side, mirrored bars * proportional to the larger value, the year under each value, and a thin world-percentile track with both * countries marked (from rank / n). No "winner" language — a small glyph shows the declared direction only. */ export function HeadToHead({ rows, countries, slugs, state }: { rows: CompareSnapshotRow[]; countries: CountryLite[]; slugs: string[]; state: CompareState }) { const { open } = useProvenance(); const [a, b] = countries; if (!a || !b) return null; const visible = rows.filter((r) => r.values[a.id]?.has_data || r.values[b.id]?.has_data); if (!visible.length) return

{t('compare.snapshot.empty')}

; const openCell = (m: MetricValue, c: CountryLite, name: string) => open(payloadFor(m, { id: c.id, slug: c.slug, name: c.name, flag: c.flag }, { name })); return (
{[a, b].map((c, i) => ( {c.flag} {c.name} {c.id} ))} {t('compare.vs')}
    {visible.map((r) => { const ma = r.values[a.id]; const mb = r.values[b.id]; const va = ma?.has_data && isNum(ma.value) ? ma.value : null; const vb = mb?.has_data && isNum(mb.value) ? mb.value : null; const max = Math.max(Math.abs(va ?? 0), Math.abs(vb ?? 0)) || 1; const name = r.indicator.short_name ?? r.indicator.name ?? r.indicator.slug; const hib = r.indicator.higher_is_better; const pctOf = (m: MetricValue | undefined) => (m && isNum(m.rank_world) && isNum(m.n_world) && m.n_world > 1 ? (1 - (m.rank_world - 1) / (m.n_world - 1)) * 100 : null); const pa = pctOf(ma); const pb = pctOf(mb); return (
  1. {name} {hib != null ? ( {hib ? : } {hib ? t('compare.snapshot.hib.higher') : t('compare.snapshot.hib.lower')} ) : null} {r.indicator.unit}
    ma && openCell(ma, a, r.indicator.name ?? name)} /> mb && openCell(mb, b, r.indicator.name ?? name)} />
    {pa != null || pb != null ? (
    {pa != null ? : null} {pb != null ? : null}
    {t('compare.h2h.pctLow')} {a.name}: {pa != null ? ordinal(Math.round(pa)) : t('common.na')} · {b.name}: {pb != null ? ordinal(Math.round(pb)) : t('common.na')} {t('compare.h2h.pctTrack', { n: ma?.n_world ?? mb?.n_world ?? '' })} {t('compare.h2h.pctHigh')}
    ) : null}
  2. ); })}

{t('compare.h2h.note')}

); } function Side({ m, c, i, max, align, onOpen }: { m: MetricValue | undefined; c: CountryLite; i: number; max: number; align: 'left' | 'right'; onOpen: () => void }) { const has = !!m && m.has_data && isNum(m.value); const pct = has ? Math.max(1.5, (Math.abs(m.value!) / max) * 100) : 0; return ( ); }