"use client"

// Rundestatistikk -- dypdykk-skjerm for én frittstående runde (ADR-033),
// inspirert av (ikke kopiert fra) en skjermopptaksvideo brukeren delte av en
// konkurrent-app 2026-07-25 -- se ARCHITECTURE_DECISIONS.md for hvilke
// bevisste designvalg som skiller denne fra referansen. Presentasjon fra V0
// (zip 14), datalag skrevet om fra mock til ekte fetch mot samme
// `/rounds/{id}/participants/{id}/holes`-endepunkt som round-detail.tsx
// allerede bruker -- ingen ny backend nødvendig.

import type React from "react"
import { useEffect, useState } from "react"
import Link from "next/link"
import {
  ArrowLeft,
  BarChart3,
  ChevronDown,
  ChevronRight,
  Flag,
  Info,
  MoreHorizontal,
  MoveHorizontal,
  Target,
  Wind,
  Circle,
  Waves,
} from "lucide-react"
import { cn } from "@/lib/utils"

// --- Helpers ---------------------------------------------------------------

// Signed value with two decimals, Norwegian comma. e.g. 0.89 -> "+0,89"
function signed(n: number): string {
  const sign = n > 0 ? "+" : n < 0 ? "\u2212" : "\u00B1"
  return `${sign}${Math.abs(n).toFixed(2).replace(".", ",")}`
}

function pct(n: number): string {
  return `${Math.round(n)} %`
}

// Chart color variables (good -> bad scale defined in globals.css)
const C = {
  eagle: "var(--chart-1)",
  birdie: "var(--chart-2)",
  par: "var(--chart-3)",
  bogey: "var(--chart-4)",
  dobbel: "var(--chart-5)",
  verre: "var(--chart-6)",
  hit: "var(--chart-2)",
  miss: "var(--chart-5)",
  neutral: "var(--muted-foreground)",
} as const

// --- Collapsible card ------------------------------------------------------

function StatCard({
  title,
  icon: Icon,
  // Alle seksjonene starter ekspandert (2026-08-12, snur en tidligere
  // beslutning fra 2026-07-25 om at trekkspillet skulle vises sammenslått)
  // -- brukeren skal se alt med det samme, i stedet for å måtte åpne hver
  // seksjon manuelt. Trekkspillet finnes fortsatt, for å kunne lukke ned
  // seksjoner man ikke bryr seg om akkurat nå.
  defaultOpen = true,
  children,
}: {
  title: string
  icon: typeof BarChart3
  defaultOpen?: boolean
  children: React.ReactNode
}) {
  const [open, setOpen] = useState(defaultOpen)
  const bodyId = `sect-${title.replace(/\s+/g, "-").toLowerCase()}`
  return (
    <section className="overflow-hidden rounded-3xl border border-border bg-card shadow-md shadow-black/8">
      <h2>
        <button
          type="button"
          onClick={() => setOpen((v) => !v)}
          aria-expanded={open}
          aria-controls={bodyId}
          className="flex min-h-14 w-full items-center gap-3 bg-primary px-4 py-3 text-left text-primary-foreground"
        >
          <span className="flex size-9 shrink-0 items-center justify-center rounded-xl bg-primary-foreground/20">
            <Icon aria-hidden="true" className="size-5" />
          </span>
          <span className="flex-1 text-lg font-extrabold tracking-tight">{title}</span>
          <ChevronDown
            aria-hidden="true"
            className={cn("size-6 shrink-0 transition-transform", open && "rotate-180")}
          />
        </button>
      </h2>
      {open && (
        <div id={bodyId} className="flex flex-col gap-6 p-4 sm:p-5">
          {children}
        </div>
      )}
    </section>
  )
}

// --- Donut (conic-gradient) ------------------------------------------------

type Segment = { label: string; value: number; color: string }

function Donut({
  segments,
  centerTop,
  centerBottom,
  size = 168,
}: {
  segments: Segment[]
  centerTop: string
  centerBottom: string
  size?: number
}) {
  const total = segments.reduce((s, x) => s + x.value, 0) || 1
  let acc = 0
  const stops = segments
    .map((seg) => {
      const start = (acc / total) * 100
      acc += seg.value
      const end = (acc / total) * 100
      return `${seg.color} ${start}% ${end}%`
    })
    .join(", ")
  return (
    <div
      className="relative shrink-0"
      style={{ width: size, height: size }}
      role="img"
      aria-label={segments.map((s) => `${s.label}: ${s.value}`).join(", ")}
    >
      <div
        className="size-full rounded-full"
        style={{ background: `conic-gradient(${stops})` }}
      />
      <div className="absolute inset-[18%] flex flex-col items-center justify-center rounded-full bg-card text-center">
        <span className="text-3xl font-extrabold tabular-nums text-foreground">{centerTop}</span>
        <span className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
          {centerBottom}
        </span>
      </div>
    </div>
  )
}

function Legend({ items }: { items: Array<{ label: string; color: string; value: string }> }) {
  return (
    <ul className="flex flex-col gap-2">
      {items.map((it) => (
        <li key={it.label} className="flex items-center gap-2.5">
          <span
            aria-hidden="true"
            className="size-4 shrink-0 rounded-md border border-black/10"
            style={{ backgroundColor: it.color }}
          />
          <span className="flex-1 text-base font-semibold text-foreground">{it.label}</span>
          <span className="text-base font-bold tabular-nums text-foreground">{it.value}</span>
        </li>
      ))}
    </ul>
  )
}

// --- Category bar (percentage + raw count) ---------------------------------

function CategoryBar({
  label,
  count,
  percentage,
  color,
  max,
}: {
  label: string
  count: number
  percentage: number
  color: string
  max: number
}) {
  const width = max > 0 ? (percentage / max) * 100 : 0
  return (
    <div className="flex flex-col gap-1">
      <div className="flex items-baseline justify-between gap-2">
        <span className="text-base font-semibold text-foreground">{label}</span>
        <span className="text-sm font-bold tabular-nums text-muted-foreground">
          {pct(percentage)} <span className="text-foreground">· {count}</span>
        </span>
      </div>
      <div className="h-6 overflow-hidden rounded-lg bg-muted">
        <div
          className="h-full rounded-lg"
          style={{ width: `${Math.max(width, count > 0 ? 6 : 0)}%`, backgroundColor: color }}
        />
      </div>
    </div>
  )
}

// --- Deviation bar (signed, diverging from a center 0 line) ----------------

function DeviationBar({ label, value, max = 2 }: { label: string; value: number; max?: number }) {
  const clamped = Math.max(-max, Math.min(max, value))
  const halfPct = (Math.abs(clamped) / max) * 50
  const positive = value > 0
  // positive = over par (worse) -> orange; negative = under par (better) -> green
  const color = value > 0 ? C.dobbel : value < 0 ? C.birdie : C.neutral
  return (
    <div className="flex items-center gap-3">
      <span className="w-16 shrink-0 text-base font-semibold text-foreground">{label}</span>
      <div className="relative h-6 flex-1 rounded-lg bg-muted">
        <div className="absolute inset-y-0 left-1/2 w-px -translate-x-1/2 bg-border" aria-hidden="true" />
        <div
          className="absolute inset-y-0 rounded-lg"
          style={{
            backgroundColor: color,
            width: `${halfPct}%`,
            left: positive ? "50%" : undefined,
            right: !positive ? "50%" : undefined,
          }}
        />
      </div>
      <span className="w-16 shrink-0 text-right text-base font-bold tabular-nums text-foreground">
        {signed(value)}
      </span>
    </div>
  )
}

// --- Horizontal progress bar (single value %) ------------------------------

function ProgressRow({
  label,
  percentage,
  color = "var(--primary)",
  suffix,
}: {
  label: string
  percentage: number
  color?: string
  suffix?: string
}) {
  return (
    <div className="flex items-center gap-3">
      <span className="w-16 shrink-0 text-base font-semibold text-foreground">{label}</span>
      <div className="h-6 flex-1 overflow-hidden rounded-lg bg-muted">
        <div
          className="h-full rounded-lg"
          style={{ width: `${percentage}%`, backgroundColor: color }}
        />
      </div>
      <span className="w-20 shrink-0 text-right text-base font-bold tabular-nums text-foreground">
        {suffix ?? pct(percentage)}
      </span>
    </div>
  )
}

// --- Paired stat tiles -----------------------------------------------------

function StatTilePair({
  a,
  b,
}: {
  a: { label: string; value: string; hint?: string }
  b: { label: string; value: string; hint?: string }
}) {
  return (
    <div className="grid grid-cols-2 gap-3">
      {[a, b].map((t) => (
        <div
          key={t.label}
          className="flex flex-col gap-1 rounded-2xl border border-border bg-muted/40 p-4"
        >
          <span className="text-3xl font-extrabold tabular-nums text-foreground">{t.value}</span>
          <span className="text-sm font-semibold leading-snug text-muted-foreground text-pretty">
            {t.label}
          </span>
        </div>
      ))}
    </div>
  )
}

// --- Big number stat with info toggle --------------------------------------

function InfoStat({
  label,
  value,
  explanation,
}: {
  label: string
  value: string
  explanation: string
}) {
  const [showInfo, setShowInfo] = useState(false)
  return (
    <div className="flex flex-col gap-2 rounded-2xl border border-border bg-muted/40 p-4">
      <span className="text-4xl font-extrabold tabular-nums text-foreground">{value}</span>
      <button
        type="button"
        onClick={() => setShowInfo((v) => !v)}
        aria-expanded={showInfo}
        className="flex min-h-11 items-center gap-1.5 text-left text-base font-bold text-foreground"
      >
        {label}
        <Info aria-hidden="true" className="size-4 text-primary" />
        <span className="sr-only">Vis forklaring</span>
      </button>
      {showInfo && (
        <p className="rounded-xl bg-card p-3 text-sm leading-relaxed text-muted-foreground text-pretty">
          {explanation}
        </p>
      )}
    </div>
  )
}

// --- Mini distribution (small labelled bars) -------------------------------

function MiniDistribution({
  title,
  data,
  color,
}: {
  title: string
  data: Array<{ label: string; value: number }>
  color: string
}) {
  const max = Math.max(...data.map((d) => d.value), 1)
  return (
    <div className="flex flex-col gap-2 rounded-2xl border border-border bg-muted/40 p-3">
      <span className="text-sm font-bold text-foreground text-pretty">{title}</span>
      <div className="flex flex-col gap-1.5">
        {data.map((d) => (
          <div key={d.label} className="flex items-center gap-2">
            <span className="w-10 shrink-0 text-xs font-semibold tabular-nums text-muted-foreground">
              {d.label}
            </span>
            <div className="h-4 flex-1 overflow-hidden rounded bg-muted">
              <div
                className="h-full rounded"
                style={{ width: `${(d.value / max) * 100}%`, backgroundColor: color }}
              />
            </div>
            <span className="w-6 shrink-0 text-right text-xs font-bold tabular-nums text-foreground">
              {d.value}
            </span>
          </div>
        ))}
      </div>
    </div>
  )
}

// --- API-typer (samme form som round-detail.tsx allerede bruker) -----------

type ApiParticipant = {
  id: string
  user_id: string | null
  guest_name: string | null
  display_name: string
  is_owner: boolean
  // ADR-039 -- hvilken side deltakeren er på, null for slagspill/skins.
  round_side_id: string | null
}

type ApiSide = {
  id: string
  label: string | null
}

type ApiRound = {
  id: string
  name: string | null
  course_name_snapshot: string
  tee_name_snapshot: string
  played_at: string
  start_hole: number
  holes_planned: number
  play_format: string
  participants: ApiParticipant[]
  sides: ApiSide[]
}

// Scramble/greensome: hvilken av sidens partnere sitt utslag ble valgt
// (migrasjon 036, FEATURE_BACKLOG.md "Scramble: statistikk over utslag
// brukt per spiller"). Samme rad-form som round-detail.tsx sin ApiSideHole.
type ApiSideHole = {
  hole_number: number
  played: boolean
  score: number | null
  selected_participant_id: string | null
}

// Delt-ball-formater (foursome/greensome/scramble, ADR-039 Beslutning C)
// lagrer ÉN score PER SIDE per hull -- ingen individuell round_hole-rad
// eksisterer i det hele tatt for noen deltaker her, så all statistikken på
// denne siden (fairway/GIR/putt-fordeling osv., som forutsetter individuell
// slag-for-slag-registrering) er strukturelt umulig å vise for disse
// formatene. Vist som en forklarende melding i stedet for en stille tom/
// misvisende side (2026-07-28, funnet ved simulering av alle spillformater).
const SHARED_BALL_FORMATS = ["foursome", "greensome", "scramble_2", "scramble_4", "chapman"]
const FORMAT_LABELS: Record<string, string> = {
  foursome: "Foursome",
  greensome: "Greensome",
  scramble_2: "Scramble (2)",
  scramble_4: "Scramble (4)",
  chapman: "Chapman",
  scramble_solo: "Scramble mot enkeltspiller",
  scramble_solo_match: "Scramble mot enkeltspiller (matchspill)",
}

type ApiHole = {
  hole_number: number
  par: number
  played: boolean
  score: number | null
  putts: number | null
  tee_shot_result: "fairway" | "left" | "right" | null
  approach_result: "hit" | "long" | "short" | "left" | "right" | null
  chip_count: number | null
  bunker_shot_count: number | null
  penalty_strokes: number | null
  first_putt_distance_bucket: "<1m" | "<2m" | "<3m" | "<5m" | "<8m" | "8m+" | null
  anyway_strokes: number | null
  strokes_received: number | null
}

const dateFormatter = new Intl.DateTimeFormat("no-NO", { day: "numeric", month: "long", year: "numeric" })
const PUTT_BUCKETS = ["<1m", "<2m", "<3m", "<5m", "<8m", "8m+"] as const

function avg(values: number[]): number | null {
  if (values.length === 0) return null
  return values.reduce((s, v) => s + v, 0) / values.length
}

// --- Alle utledede tall for statistikk-skjermen, regnet ut ÉN gang fra
// rå hull-data. Ingenting her lagres -- alt beregnes ved lesing, samme
// prinsipp som "Så langt i runden" i round-detail.tsx.
function computeStats(holes: ApiHole[]) {
  const played = holes.filter((h) => h.played && h.score !== null)
  const diff = (h: ApiHole) => (h.score as number) - h.par

  const categories = [
    { label: "Eagle+", test: (d: number) => d <= -2, color: C.eagle },
    { label: "Birdie", test: (d: number) => d === -1, color: C.birdie },
    { label: "Par", test: (d: number) => d === 0, color: C.par },
    { label: "Bogey", test: (d: number) => d === 1, color: C.bogey },
    { label: "Dobbel bogey", test: (d: number) => d === 2, color: C.dobbel },
    { label: "Verre", test: (d: number) => d >= 3, color: C.verre },
  ].map((c) => {
    const count = played.filter((h) => c.test(diff(h))).length
    const percentage = played.length > 0 ? (count / played.length) * 100 : 0
    return { label: c.label, count, percentage, color: c.color }
  })

  const avgToPar = avg(played.map(diff))
  const byPar = (parValue: number) => avg(played.filter((h) => h.par === parValue).map(diff))

  const fairwayTracked = holes.filter((h) => h.par !== 3 && h.tee_shot_result !== null)
  const fairwayHit = fairwayTracked.filter((h) => h.tee_shot_result === "fairway")
  const fairwayLeft = fairwayTracked.filter((h) => h.tee_shot_result === "left")
  const fairwayRight = fairwayTracked.filter((h) => h.tee_shot_result === "right")
  const fwTotal = fairwayTracked.length || 1
  const avgToParFairwayHit = avg(fairwayHit.filter((h) => h.played && h.score !== null).map(diff))
  const avgToParFairwayMiss = avg(
    [...fairwayLeft, ...fairwayRight].filter((h) => h.played && h.score !== null).map(diff),
  )

  // GIR: samme formel som round-detail.tsx sin showGir -- krever kjent putt-tall.
  const girEligible = played.filter((h) => h.putts !== null)
  const isGir = (h: ApiHole) => (h.score as number) - (h.putts as number) <= h.par - 2
  const girHit = girEligible.filter(isGir)
  const girPct = girEligible.length > 0 ? (girHit.length / girEligible.length) * 100 : null
  const girByPar = (parValue: number) => {
    const subset = girEligible.filter((h) => h.par === parValue)
    return subset.length > 0 ? (subset.filter(isGir).length / subset.length) * 100 : null
  }
  const girWithFairway = (want: "hit" | "miss") => {
    const subset = girEligible.filter((h) =>
      want === "hit" ? h.tee_shot_result === "fairway" : h.tee_shot_result === "left" || h.tee_shot_result === "right",
    )
    return subset.length > 0 ? (subset.filter(isGir).length / subset.length) * 100 : null
  }
  const avgToParWithGir = avg(girEligible.filter(isGir).map(diff))
  const avgToParWithoutGir = avg(girEligible.filter((h) => !isGir(h)).map(diff))

  const missedGreen = holes.filter((h) => h.approach_result !== null && h.approach_result !== "hit")
  const missTotal = missedGreen.length || 1
  const missDir = (dir: "long" | "short" | "left" | "right") =>
    (missedGreen.filter((h) => h.approach_result === dir).length / missTotal) * 100

  const puttsEligible = played.filter((h) => h.putts !== null)
  const totalPutts = puttsEligible.reduce((s, h) => s + (h.putts as number), 0)
  const puttCategories = [
    { label: "1-putt", test: (p: number) => p === 1, color: C.birdie },
    { label: "2-putt", test: (p: number) => p === 2, color: C.par },
    { label: "3-putt+", test: (p: number) => p >= 3, color: C.dobbel },
  ].map((c) => {
    const count = puttsEligible.filter((h) => c.test(h.putts as number)).length
    const percentage = puttsEligible.length > 0 ? (count / puttsEligible.length) * 100 : 0
    return { label: c.label, count, percentage, color: c.color }
  })
  const puttsByPar = (parValue: number) => avg(puttsEligible.filter((h) => h.par === parValue).map((h) => h.putts as number))
  const avgPuttsWithGir = avg(puttsEligible.filter(isGir).map((h) => h.putts as number))
  const avgPuttsWithoutGir = avg(puttsEligible.filter((h) => h.putts !== null && !isGir(h)).map((h) => h.putts as number))

  const onePuttPctByBucket = PUTT_BUCKETS.map((bucket) => {
    const subset = puttsEligible.filter((h) => h.first_putt_distance_bucket === bucket)
    return { label: bucket, percentage: subset.length > 0 ? (subset.filter((h) => h.putts === 1).length / subset.length) * 100 : 0 }
  })
  const lengthByGir = (want: boolean) =>
    PUTT_BUCKETS.map((bucket) => ({
      label: bucket,
      value: girEligible.filter((h) => h.first_putt_distance_bucket === bucket && isGir(h) === want).length,
    }))

  const chipEligible = played.filter((h) => h.chip_count !== null)
  const avgChipsPerHole = avg(chipEligible.map((h) => h.chip_count as number))
  const chipCategories = [
    { label: "0 chip", test: (c: number) => c === 0, color: C.birdie },
    { label: "1 chip", test: (c: number) => c === 1, color: C.bogey },
    { label: "2+ chip", test: (c: number) => c >= 2, color: C.verre },
  ].map((c) => {
    const count = chipEligible.filter((h) => c.test(h.chip_count as number)).length
    const percentage = chipEligible.length > 0 ? (count / chipEligible.length) * 100 : 0
    return { label: c.label, count, percentage, color: c.color }
  })

  const girMissed = girEligible.filter((h) => !isGir(h))
  const scramblingPct = girMissed.length > 0 ? (girMissed.filter((h) => diff(h) <= 0).length / girMissed.length) * 100 : null
  const sandEligible = girMissed.filter((h) => h.bunker_shot_count !== null && h.bunker_shot_count >= 1)
  const sandSavePct = sandEligible.length > 0 ? (sandEligible.filter((h) => diff(h) <= 0).length / sandEligible.length) * 100 : null

  const bunkerEligible = played.filter((h) => h.bunker_shot_count !== null)
  const bunkerPerRound = bunkerEligible.reduce((s, h) => s + (h.bunker_shot_count as number), 0)
  const penaltyEligible = played.filter((h) => h.penalty_strokes !== null)
  const penaltyPerRound = penaltyEligible.reduce((s, h) => s + (h.penalty_strokes as number), 0)
  const avgToParWithBunker = avg(bunkerEligible.filter((h) => (h.bunker_shot_count as number) >= 1).map(diff))
  const avgToParWithoutBunker = avg(bunkerEligible.filter((h) => (h.bunker_shot_count as number) === 0).map(diff))
  const pctHolesWithPenalty =
    penaltyEligible.length > 0
      ? (penaltyEligible.filter((h) => (h.penalty_strokes as number) >= 1).length / penaltyEligible.length) * 100
      : null

  const anywayEligible = played.filter((h) => h.anyway_strokes !== null)
  const anywayPerRound =
    anywayEligible.length > 0 ? anywayEligible.reduce((s, h) => s + (h.anyway_strokes as number), 0) : null
  const pctHolesWithAnyway =
    anywayEligible.length > 0
      ? (anywayEligible.filter((h) => (h.anyway_strokes as number) >= 1).length / anywayEligible.length) * 100
      : null

  return {
    playedCount: played.length,
    totalStrokes: played.reduce((s, h) => s + (h.score as number), 0),
    totalToPar: played.reduce((s, h) => s + diff(h), 0),
    categories,
    avgToPar,
    byPar,
    fairwayTracked,
    fairwayHit,
    fairwayLeft,
    fairwayRight,
    fwTotal,
    avgToParFairwayHit,
    avgToParFairwayMiss,
    girEligible,
    girHit,
    girPct,
    girByPar,
    girWithFairway,
    avgToParWithGir,
    avgToParWithoutGir,
    missedGreen,
    missDir,
    totalPutts,
    puttCategories,
    puttsByPar,
    avgPuttsWithGir,
    avgPuttsWithoutGir,
    onePuttPctByBucket,
    lengthByGir,
    avgChipsPerHole,
    chipCategories,
    scramblingPct,
    sandSavePct,
    bunkerPerRound,
    penaltyPerRound,
    avgToParWithBunker,
    avgToParWithoutBunker,
    pctHolesWithPenalty,
    anywayPerRound,
    pctHolesWithAnyway,
  }
}

// Viewer-relativt (ADR-036 fase 3-utvidelsen, 2026-07-26) -- se samme
// begrunnelse i round-detail.tsx sin playerLabel().
function participantLabel(p: ApiParticipant, viewerId: string | null): string {
  return viewerId !== null && p.user_id === viewerId ? "Deg" : p.display_name
}

// --- Screen ----------------------------------------------------------------

// Scramble/greensome: "utslag brukt" per spiller (migrasjon 036,
// FEATURE_BACKLOG.md "Scramble: statistikk over utslag brukt per spiller").
// Ren klientside-telling fra allerede hentet round_hole-data (samme
// "regn ut ved lesing, ikke lagre et eget tall"-prinsipp som resten av
// denne siden) -- vises kun de hullene som faktisk har et registrert valg,
// ingen antakelse om at ALLE hull må ha ett (helt valgfritt å registrere).
function SelectedDriverSummary({ round, sideHoles }: { round: ApiRound; sideHoles: Record<string, ApiSideHole[]> }) {
  const rows = round.sides.map((side) => {
    const holes = sideHoles[side.id] ?? []
    const holesWithSelection = holes.filter((h) => h.selected_participant_id !== null)
    const counts = new Map<string, number>()
    for (const h of holesWithSelection) {
      const pid = h.selected_participant_id as string
      counts.set(pid, (counts.get(pid) ?? 0) + 1)
    }
    const sideParticipants = round.participants.filter((p) => p.round_side_id === side.id)
    const playedCount = holes.filter((h) => h.played && h.score !== null).length
    return {
      side,
      playedCount,
      registeredCount: holesWithSelection.length,
      entries: sideParticipants
        .map((p) => ({ name: p.display_name, count: counts.get(p.id) ?? 0 }))
        .sort((a, b) => b.count - a.count),
    }
  })

  if (rows.every((r) => r.registeredCount === 0)) {
    return (
      <div className="rounded-3xl border border-dashed border-border bg-card p-4 text-sm text-muted-foreground">
        Ingen har registrert hvem sitt utslag som ble brukt på noe hull ennå -- valgfritt felt i
        scoreregistreringen for {FORMAT_LABELS[round.play_format] ?? round.play_format}.
      </div>
    )
  }

  return (
    <div className="flex flex-col gap-4 rounded-3xl border border-border bg-card p-4 shadow-md shadow-black/8">
      <h2 className="text-base font-bold text-foreground">Utslag brukt</h2>
      {rows.map(({ side, playedCount, registeredCount, entries }, idx) => (
        <div key={side.id} className="flex flex-col gap-2">
          {rows.length > 1 && (
            <span className="text-sm font-semibold text-muted-foreground">
              {side.label?.trim() || `Side ${idx === 0 ? "A" : "B"}`}
            </span>
          )}
          <ul className="flex flex-col divide-y divide-border overflow-hidden rounded-xl border border-border">
            {entries.map((e) => (
              <li key={e.name} className="flex items-center justify-between gap-3 px-4 py-2.5">
                <span className="text-sm font-semibold text-foreground">{e.name}</span>
                <span className="text-base font-extrabold tabular-nums text-foreground">{e.count} utslag</span>
              </li>
            ))}
          </ul>
          <span className="text-xs text-muted-foreground">
            Registrert på {registeredCount} av {playedCount} spilte hull.
          </span>
        </div>
      ))}
    </div>
  )
}

export function RoundStats({
  roundId,
  embedded = false,
}: {
  roundId: string
  // ADR-040 Beslutning D ("det store grepet") -- Scorekort+Statistikk
  // slått sammen til ÉN side, statistikk RETT UNDER scorekortet.
  // `embedded` dropper denne komponentens egen sticky header (den delte
  // `RoundHeader` dekker det, se `RoundPageShell`) OG den redundante
  // rundesammendrag-stripen/"Se scorekort"-knappen (scorekortet ligger nå
  // allerede rett over på samme side).
  embedded?: boolean
}) {
  // Kun ÉTT <main>-landemerke tillatt per side -- når embedded (rendret
  // under RoundScorecard sin egen <main>, ADR-040 Beslutning D), bruk en
  // vanlig <div> i stedet.
  const ContentTag = embedded ? "div" : "main"
  const [round, setRound] = useState<ApiRound | null>(null)
  const [participantId, setParticipantId] = useState<string | null>(null)
  const [holes, setHoles] = useState<ApiHole[] | null>(null)
  const [error, setError] = useState<string | null>(null)
  const [viewerId, setViewerId] = useState<string | null>(null)
  // Sanntid (2026-07-26, oppfølging av ADR-027): et rent "noe endret seg"-
  // signal over WebSocket -- bumper denne, som er med i begge fetch-
  // effektene under, slik at de henter på nytt akkurat som ved
  // førstegangslasting (samme mønster som public-live.tsx).
  const [refreshKey, setRefreshKey] = useState(0)

  useEffect(() => {
    let cancelled = false
    fetch("/auth/me", { credentials: "include" })
      .then((res) => (res.ok ? res.json() : null))
      .then((data: { id: string } | null) => {
        if (!cancelled && data) setViewerId(data.id)
      })
      .catch(() => {})
    return () => {
      cancelled = true
    }
  }, [])

  useEffect(() => {
    const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"
    const socket = new WebSocket(`${protocol}//${window.location.host}/ws/rounds/${roundId}/live`)
    socket.onmessage = () => setRefreshKey((k) => k + 1)
    return () => socket.close()
  }, [roundId])

  useEffect(() => {
    let cancelled = false
    async function load() {
      const res = await fetch(`/rounds/${roundId}`, { credentials: "include" })
      if (res.status === 403 || res.status === 404) {
        if (!cancelled) setError("Denne runden finnes ikke, eller du har ikke tilgang til den.")
        return
      }
      if (!res.ok) {
        if (!cancelled) setError("Klarte ikke å hente runden. Prøv igjen om litt.")
        return
      }
      const data: ApiRound = await res.json()
      if (cancelled) return
      setRound(data)
      setParticipantId(
        (prev) =>
          prev ??
          (viewerId && data.participants.find((p) => p.user_id === viewerId)?.id) ??
          data.participants.find((p) => p.is_owner)?.id ??
          data.participants[0]?.id ??
          null,
      )
    }
    void load()
    return () => {
      cancelled = true
    }
  }, [roundId, viewerId, refreshKey])

  // Scramble/greensome: hent hver sides hull for "Utslag brukt"-sammendraget
  // under (migrasjon 036) -- samme endepunkt round-detail.tsx allerede bruker
  // for selve registreringen.
  const [sideHoles, setSideHoles] = useState<Record<string, ApiSideHole[]>>({})
  useEffect(() => {
    if (!round || !SHARED_BALL_FORMATS.includes(round.play_format)) return
    let cancelled = false
    async function loadSideHoles() {
      const entries = await Promise.all(
        round!.sides.map(async (s) => {
          const res = await fetch(`/rounds/${roundId}/sides/${s.id}/holes`, { credentials: "include" })
          const data: ApiSideHole[] = res.ok ? await res.json() : []
          return [s.id, data] as const
        }),
      )
      if (!cancelled) setSideHoles(Object.fromEntries(entries))
    }
    void loadSideHoles()
    return () => {
      cancelled = true
    }
  }, [round, roundId, refreshKey])

  useEffect(() => {
    // Delt-ball-formater har ingen individuell round_hole-rad å hente --
    // se guarden under, som viser en forklarende melding i stedet.
    if (!participantId || (round && SHARED_BALL_FORMATS.includes(round.play_format))) return
    let cancelled = false
    async function loadHoles() {
      const res = await fetch(`/rounds/${roundId}/participants/${participantId}/holes`, { credentials: "include" })
      if (!res.ok) return
      const data: ApiHole[] = await res.json()
      if (!cancelled) setHoles(data)
    }
    void loadHoles()
    return () => {
      cancelled = true
    }
  }, [roundId, participantId, round, refreshKey])

  if (error) {
    return (
      <div className="flex min-h-dvh flex-col items-center justify-center gap-4 bg-background px-5 text-center">
        <p className="text-base font-medium text-destructive">{error}</p>
        <Link href="/my-rounds" className="text-base font-semibold text-primary underline underline-offset-2">
          Tilbake til egne runder
        </Link>
      </div>
    )
  }

  if (!round) {
    return (
      <div className="flex min-h-dvh flex-col items-center justify-center bg-background">
        <div aria-hidden="true" className="size-10 animate-spin rounded-full border-4 border-primary/20 border-t-primary" />
      </div>
    )
  }

  if (SHARED_BALL_FORMATS.includes(round.play_format)) {
    return (
      <div className={cn(!embedded && "min-h-dvh bg-background")}>
        {!embedded && (
          <header className="sticky top-0 z-10 border-b border-border bg-background/95 backdrop-blur">
            <div className="mx-auto flex min-h-14 max-w-xl items-center gap-2 px-4 py-2">
              <Link
                href={`/my-rounds/${roundId}`}
                className="flex min-h-11 items-center gap-1.5 rounded-xl pr-3 text-base font-bold text-foreground transition-colors hover:text-primary"
              >
                <ArrowLeft aria-hidden="true" className="size-6" />
                Statistikk
              </Link>
            </div>
          </header>
        )}
        <ContentTag className="mx-auto flex max-w-xl flex-col gap-4 px-4 py-5">
          <SelectedDriverSummary round={round} sideHoles={sideHoles} />

          <div className="rounded-3xl border border-dashed border-border bg-card p-4 text-sm text-muted-foreground">
            Detaljert statistikk (fairwaytreff, GIR, putt-fordeling m.m.) er ikke tilgjengelig for{" "}
            {FORMAT_LABELS[round.play_format] ?? round.play_format} -- disse formatene registrerer kun ETT felles
            slagtall per side, ikke individuelle slag-for-slag-detaljer.{" "}
            <Link
              href={`/my-rounds/${roundId}/scorecard`}
              className="font-semibold text-primary underline underline-offset-2"
            >
              Se scorekortet
            </Link>{" "}
            for hull-for-hull-forløpet i stedet.
          </div>
        </ContentTag>
      </div>
    )
  }

  if (!holes) {
    return (
      <div className="flex min-h-dvh flex-col items-center justify-center bg-background">
        <div aria-hidden="true" className="size-10 animate-spin rounded-full border-4 border-primary/20 border-t-primary" />
      </div>
    )
  }

  const s = computeStats(holes)
  const maxCatPct = Math.max(...s.categories.map((c) => c.percentage), 1)

  return (
    <div className={cn(!embedded && "min-h-dvh bg-background")}>
      {!embedded && (
        <header className="sticky top-0 z-10 border-b border-border bg-background/95 backdrop-blur">
          <div className="mx-auto flex min-h-14 max-w-xl items-center gap-2 px-4 py-2">
            <Link
              href={`/my-rounds/${roundId}`}
              className="flex min-h-11 items-center gap-1.5 rounded-xl pr-3 text-base font-bold text-foreground transition-colors hover:text-primary"
            >
              <ArrowLeft aria-hidden="true" className="size-6" />
              Rundestatistikk
            </Link>
          </div>
        </header>
      )}

      <ContentTag className="mx-auto flex max-w-xl flex-col gap-4 px-4 py-5 pb-16">
        {/* Round summary strip + "Se scorekort"-knapp -- begge redundante
            når embedded (scorekortet ligger da allerede rett over på
            samme side, ADR-040 Beslutning D). */}
        {!embedded && (
          <>
            <div className="flex flex-wrap items-baseline justify-between gap-2 rounded-3xl border border-border bg-card p-4 shadow-md shadow-black/8">
              <div className="flex flex-col">
                <span className="text-xl font-extrabold tracking-tight text-foreground text-balance">
                  {round.name?.trim() || round.course_name_snapshot}
                </span>
                <span className="text-sm font-semibold text-muted-foreground">
                  {round.name?.trim() ? `${round.course_name_snapshot} · ` : ""}
                  {round.tee_name_snapshot} · {round.holes_planned} hull ·{" "}
                  {dateFormatter.format(new Date(round.played_at))}
                </span>
              </div>
              <div className="flex flex-col items-end">
                <span className="text-3xl font-extrabold tabular-nums text-foreground">{s.totalStrokes || "–"}</span>
                {s.playedCount > 0 && (
                  <span className="text-sm font-bold text-brand-orange">{signed(s.totalToPar)} til par</span>
                )}
              </div>
            </div>

            <Link
              href={`/my-rounds/${roundId}/scorecard`}
              className="flex min-h-14 items-center justify-center gap-1.5 rounded-2xl bg-primary px-5 text-base font-bold text-primary-foreground shadow-sm transition-colors hover:bg-primary/90"
            >
              Se scorekort
              <ChevronRight aria-hidden="true" className="size-5" />
            </Link>
          </>
        )}

        {/* Spillervelger, kun når runden har flere deltakere */}
        {round.participants.length > 1 && (
          <div role="tablist" aria-label="Velg spiller" className="-mx-4 overflow-x-auto px-4">
            <div className="flex gap-2">
              {round.participants.map((p) => {
                const active = p.id === participantId
                return (
                  <button
                    key={p.id}
                    type="button"
                    role="tab"
                    aria-selected={active}
                    onClick={() => {
                      setHoles(null)
                      setParticipantId(p.id)
                    }}
                    className={cn(
                      "flex min-h-11 shrink-0 items-center rounded-2xl border px-4 text-base font-bold transition-colors",
                      active
                        ? "border-primary bg-primary text-primary-foreground"
                        : "border-border bg-card text-foreground hover:bg-accent/50",
                    )}
                  >
                    {participantLabel(p, viewerId)}
                  </button>
                )
              })}
            </div>
          </div>
        )}

        {s.playedCount === 0 ? (
          <p className="rounded-3xl border border-border bg-card p-6 text-center text-base font-semibold text-muted-foreground">
            Ingen hull registrert ennå -- statistikk vises etter hvert som hull spilles.
          </p>
        ) : (
          <>
            {/* 1. Scorer */}
            <StatCard title="Scorer" icon={BarChart3}>
              <div className="flex flex-col gap-3">
                {s.categories.map((c) => (
                  <CategoryBar key={c.label} label={c.label} count={c.count} percentage={c.percentage} color={c.color} max={maxCatPct} />
                ))}
              </div>

              {s.avgToPar !== null && (
                <div className="flex flex-col items-center gap-1 rounded-2xl border border-border bg-muted/40 p-4">
                  <span className="text-sm font-bold uppercase tracking-wide text-muted-foreground">Snitt til par</span>
                  <span className="text-5xl font-extrabold tabular-nums text-brand-orange">{signed(s.avgToPar)}</span>
                  <span className="text-sm font-semibold text-muted-foreground">per hull</span>
                </div>
              )}

              {(s.byPar(3) !== null || s.byPar(4) !== null || s.byPar(5) !== null) && (
                <div className="flex flex-col gap-3">
                  <span className="text-base font-bold text-foreground">Snitt til par per hulltype</span>
                  {s.byPar(3) !== null && <DeviationBar label="Par 3" value={s.byPar(3)!} />}
                  {s.byPar(4) !== null && <DeviationBar label="Par 4" value={s.byPar(4)!} />}
                  {s.byPar(5) !== null && <DeviationBar label="Par 5" value={s.byPar(5)!} />}
                </div>
              )}
            </StatCard>

            {/* 2. Fairway */}
            {s.fairwayTracked.length > 0 && (
              <StatCard title="Fairway" icon={MoveHorizontal}>
                <div className="flex flex-col gap-2">
                  <div className="flex h-10 overflow-hidden rounded-2xl">
                    {s.fairwayLeft.length > 0 && (
                      <div
                        className="flex items-center justify-center text-sm font-bold text-white"
                        style={{ width: `${(s.fairwayLeft.length / s.fwTotal) * 100}%`, backgroundColor: C.miss }}
                      >
                        {pct((s.fairwayLeft.length / s.fwTotal) * 100)}
                      </div>
                    )}
                    {s.fairwayHit.length > 0 && (
                      <div
                        className="flex items-center justify-center text-sm font-bold text-white"
                        style={{ width: `${(s.fairwayHit.length / s.fwTotal) * 100}%`, backgroundColor: C.hit }}
                      >
                        {pct((s.fairwayHit.length / s.fwTotal) * 100)}
                      </div>
                    )}
                    {s.fairwayRight.length > 0 && (
                      <div
                        className="flex items-center justify-center text-sm font-bold text-white"
                        style={{ width: `${(s.fairwayRight.length / s.fwTotal) * 100}%`, backgroundColor: C.bogey }}
                      >
                        {pct((s.fairwayRight.length / s.fwTotal) * 100)}
                      </div>
                    )}
                  </div>
                  <div className="flex justify-between text-sm font-semibold text-foreground">
                    <span>Bom venstre</span>
                    <span>Truffet</span>
                    <span>Bom høyre</span>
                  </div>
                </div>

                {(s.avgToParFairwayHit !== null || s.avgToParFairwayMiss !== null) && (
                  <StatTilePair
                    a={{ label: "Snitt til par når fairway truffet", value: s.avgToParFairwayHit !== null ? signed(s.avgToParFairwayHit) : "–" }}
                    b={{ label: "Snitt til par når fairway bommet", value: s.avgToParFairwayMiss !== null ? signed(s.avgToParFairwayMiss) : "–" }}
                  />
                )}
              </StatCard>
            )}

            {/* 3. Greentreff (GIR) */}
            {s.girPct !== null && (
              <StatCard title="Greentreff (GIR)" icon={Target}>
                <div className="flex flex-col items-center gap-5 sm:flex-row">
                  <Donut
                    segments={[
                      { label: "Truffet green", value: s.girHit.length, color: C.hit },
                      { label: "Bommet green", value: s.girEligible.length - s.girHit.length, color: C.miss },
                    ]}
                    centerTop={pct(s.girPct)}
                    centerBottom="GIR"
                  />
                  <div className="w-full flex-1">
                    <Legend
                      items={[
                        { label: "Truffet green", color: C.hit, value: `${s.girHit.length} · ${pct(s.girPct)}` },
                        {
                          label: "Bommet green",
                          color: C.miss,
                          value: `${s.girEligible.length - s.girHit.length} · ${pct(100 - s.girPct)}`,
                        },
                      ]}
                    />
                  </div>
                </div>

                {(s.girByPar(3) !== null || s.girByPar(4) !== null || s.girByPar(5) !== null) && (
                  <div className="flex flex-col gap-3">
                    <span className="text-base font-bold text-foreground">GIR per hulltype</span>
                    {s.girByPar(3) !== null && <ProgressRow label="Par 3" percentage={s.girByPar(3)!} color={C.hit} />}
                    {s.girByPar(4) !== null && <ProgressRow label="Par 4" percentage={s.girByPar(4)!} color={C.hit} />}
                    {s.girByPar(5) !== null && <ProgressRow label="Par 5" percentage={s.girByPar(5)!} color={C.hit} />}
                  </div>
                )}

                {(s.girWithFairway("hit") !== null || s.girWithFairway("miss") !== null) && (
                  <StatTilePair
                    a={{ label: "GIR når fairway truffet", value: s.girWithFairway("hit") !== null ? pct(s.girWithFairway("hit")!) : "–" }}
                    b={{ label: "GIR når fairway bommet", value: s.girWithFairway("miss") !== null ? pct(s.girWithFairway("miss")!) : "–" }}
                  />
                )}
                {(s.avgToParWithGir !== null || s.avgToParWithoutGir !== null) && (
                  <StatTilePair
                    a={{ label: "Snitt til par med GIR", value: s.avgToParWithGir !== null ? signed(s.avgToParWithGir) : "–" }}
                    b={{ label: "Snitt til par uten GIR", value: s.avgToParWithoutGir !== null ? signed(s.avgToParWithoutGir) : "–" }}
                  />
                )}
              </StatCard>
            )}

            {/* 4. Bom-retning på green */}
            {s.missedGreen.length > 0 && (
              <StatCard title="Bom-retning på green" icon={Wind}>
                <p className="text-sm font-semibold text-muted-foreground text-pretty">
                  Retning når greenen ble bommet ({s.missedGreen.length} hull).
                </p>
                <div className="mx-auto grid w-full max-w-xs grid-cols-3 grid-rows-3 gap-2">
                  <div className="col-start-2 row-start-1">
                    <CompassCell label="Langt" percentage={s.missDir("long")} />
                  </div>
                  <div className="col-start-1 row-start-2">
                    <CompassCell label="Venstre" percentage={s.missDir("left")} />
                  </div>
                  <div className="col-start-2 row-start-2 flex items-center justify-center">
                    <div className="relative flex size-20 items-center justify-center rounded-full bg-primary/15">
                      <div className="flex size-14 items-center justify-center rounded-full bg-primary/30">
                        <Flag aria-hidden="true" className="size-7 text-primary" fill="currentColor" />
                      </div>
                    </div>
                  </div>
                  <div className="col-start-3 row-start-2">
                    <CompassCell label="Høyre" percentage={s.missDir("right")} />
                  </div>
                  <div className="col-start-2 row-start-3">
                    <CompassCell label="Kort" percentage={s.missDir("short")} />
                  </div>
                </div>
              </StatCard>
            )}

            {/* 5. Putter */}
            {s.puttCategories.some((c) => c.count > 0) && (
              <StatCard title="Putter" icon={Circle}>
                <div className="flex flex-col items-center gap-5 sm:flex-row">
                  <Donut
                    segments={s.puttCategories.map((c) => ({ label: c.label, value: c.count, color: c.color }))}
                    centerTop={String(s.totalPutts)}
                    centerBottom="putter/runde"
                  />
                  <div className="w-full flex-1">
                    <Legend
                      items={s.puttCategories.map((c) => ({
                        label: c.label,
                        color: c.color,
                        value: `${c.count} · ${pct(c.percentage)}`,
                      }))}
                    />
                  </div>
                </div>

                {(s.puttsByPar(3) !== null || s.puttsByPar(4) !== null || s.puttsByPar(5) !== null) && (
                  <div className="flex flex-col gap-3">
                    <span className="text-base font-bold text-foreground">Snitt putter per hull</span>
                    {[3, 4, 5].map(
                      (parValue) =>
                        s.puttsByPar(parValue) !== null && (
                          <ProgressRow
                            key={parValue}
                            label={`Par ${parValue}`}
                            percentage={Math.min((s.puttsByPar(parValue)! / 3) * 100, 100)}
                            color={C.par}
                            suffix={s.puttsByPar(parValue)!.toFixed(2).replace(".", ",")}
                          />
                        ),
                    )}
                  </div>
                )}

                {(s.avgPuttsWithGir !== null || s.avgPuttsWithoutGir !== null) && (
                  <StatTilePair
                    a={{ label: "Snitt putter med GIR", value: s.avgPuttsWithGir !== null ? s.avgPuttsWithGir.toFixed(2).replace(".", ",") : "–" }}
                    b={{ label: "Snitt putter uten GIR", value: s.avgPuttsWithoutGir !== null ? s.avgPuttsWithoutGir.toFixed(2).replace(".", ",") : "–" }}
                  />
                )}
              </StatCard>
            )}

            {/* 6. Putt-lengde */}
            {s.onePuttPctByBucket.some((b) => b.percentage > 0) && (
              <StatCard title="Putt-lengde" icon={BarChart3}>
                <div className="flex flex-col gap-3">
                  <span className="text-base font-bold text-foreground">Andel én-putt etter lengde på første putt</span>
                  {s.onePuttPctByBucket.map((b, i) => (
                    <ProgressRow
                      key={b.label}
                      label={b.label}
                      percentage={b.percentage}
                      color={[C.birdie, C.birdie, C.par, C.bogey, C.dobbel, C.verre][i]}
                    />
                  ))}
                </div>

                <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
                  <MiniDistribution title="Lengde når green truffet" color={C.hit} data={s.lengthByGir(true)} />
                  <MiniDistribution title="Lengde når green bommet" color={C.miss} data={s.lengthByGir(false)} />
                </div>
              </StatCard>
            )}

            {/* 7. Chip, bunker og straffeslag */}
            <StatCard title="Chip, bunker og straffeslag" icon={Waves}>
              {(s.scramblingPct !== null || s.sandSavePct !== null) && (
                <div className="grid grid-cols-2 gap-3">
                  <InfoStat
                    label="Scrambling"
                    value={s.scramblingPct !== null ? pct(s.scramblingPct) : "–"}
                    explanation="Andel hull der du reddet par eller bedre uten å treffe greenen i regulering (GIR)."
                  />
                  <InfoStat
                    label="Sand save"
                    value={s.sandSavePct !== null ? pct(s.sandSavePct) : "–"}
                    explanation="Andel bunkerhull der du kom deg opp og i hull for par eller bedre fra greenbunker."
                  />
                </div>
              )}

              {s.chipCategories.some((c) => c.count > 0) && (
                <div className="flex flex-col items-center gap-5 sm:flex-row">
                  <Donut
                    segments={s.chipCategories.map((c) => ({ label: c.label, value: c.count, color: c.color }))}
                    centerTop={s.avgChipsPerHole !== null ? s.avgChipsPerHole.toFixed(2).replace(".", ",") : "–"}
                    centerBottom="chip/hull"
                  />
                  <div className="w-full flex-1">
                    <Legend
                      items={s.chipCategories.map((c) => ({
                        label: c.label,
                        color: c.color,
                        value: `${c.count} · ${pct(c.percentage)}`,
                      }))}
                    />
                  </div>
                </div>
              )}

              <StatTilePair
                a={{ label: "Bunkerslag per runde", value: String(s.bunkerPerRound) }}
                b={{ label: "Straffeslag per runde", value: String(s.penaltyPerRound) }}
              />
              {(s.avgToParWithBunker !== null || s.avgToParWithoutBunker !== null) && (
                <StatTilePair
                  a={{ label: "Snitt til par med bunkerslag", value: s.avgToParWithBunker !== null ? signed(s.avgToParWithBunker) : "–" }}
                  b={{ label: "Snitt til par uten bunkerslag", value: s.avgToParWithoutBunker !== null ? signed(s.avgToParWithoutBunker) : "–" }}
                />
              )}
              {s.pctHolesWithPenalty !== null && (
                <div className="flex flex-col gap-1 rounded-2xl border border-border bg-muted/40 p-4">
                  <span className="text-3xl font-extrabold tabular-nums text-foreground">{pct(s.pctHolesWithPenalty)}</span>
                  <span className="text-sm font-semibold text-muted-foreground text-pretty">Andel hull med minst ett straffeslag</span>
                </div>
              )}
            </StatCard>

            {/* 8. Annet -- egen seksjon for rundens anywayslag, ETTER
                chip/bunker/straffeslag (rettet 2026-07-25 -- feilaktig slått
                sammen med den seksjonen i forrige runde). Notatfelt kommer
                trolig hit senere. */}
            {s.anywayPerRound !== null && (
              <StatCard title="Annet" icon={MoreHorizontal}>
                <StatTilePair
                  a={{ label: "Anywayslag per runde", value: String(s.anywayPerRound) }}
                  b={{
                    label: "Andel hull med anywayslag",
                    value: s.pctHolesWithAnyway !== null ? pct(s.pctHolesWithAnyway) : "–",
                  }}
                />
              </StatCard>
            )}
          </>
        )}
      </ContentTag>
    </div>
  )
}

// --- Compass cell ----------------------------------------------------------

function CompassCell({ label, percentage }: { label: string; percentage: number }) {
  return (
    <div className="flex min-h-16 flex-col items-center justify-center gap-0.5 rounded-2xl border border-border bg-muted/40 p-2 text-center">
      <span className="text-lg font-extrabold tabular-nums text-foreground">{pct(percentage)}</span>
      <span className="text-sm font-semibold text-muted-foreground">{label}</span>
    </div>
  )
}
