diff --git a/CLAUDE.md b/CLAUDE.md
index 6473d99..c50fab1 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -4936,6 +4936,159 @@ Ferdig og verifisert:
nøkkel, `GET /rounds/{ukjent-id}/flight-group` ga korrekt
`401 NOT_AUTHENTICATED` (ikke en rå 404 — bekrefter ruten faktisk når
FastAPI), `teeoff.no` upåvirket.
+- **Designinstruksen erstattet/formalisert + seks brukerpunkter, ALLE
+ BYGGET OG LIVE (2026-07-29):** `alternativ designinstruks.md` (fra
+ dagen før) erstattet med en revidert versjon fra bruker (allerede
+ forankret i det eksisterende token-systemet, ikke lenger i konflikt med
+ oransje/hardkodet-slate-spørsmålene) — INNHOLDET er nå slått sammen inn
+ i `DESIGN_SYSTEM.md` som den gjeldende fasiten (8-punkts rutenett,
+ `shadow-md shadow-black/8`, `font-normal` OK ved `text-base`+god
+ kontrast, `:active`-tilstander, safe-area, 16px input, `transition-all
+ duration-200`). `alternativ designinstruks.md` selv beholdt kun som
+ arkiv/historikk. **Konsistens-sveip:** det gamle svake skygge-mønsteret
+ (`shadow-sm shadow-black/5`) erstattet mekanisk på tvers av 20 filer
+ (`sed`, verifisert 0 gjenværende treff) — IKKE en fullstendig
+ strukturell gjennomgang av alle 27 skjermer, kun dette ene, trygge,
+ mekaniske mønsteret.
+ **Hurtighandlinger:** dashbordets tre knapper er nå `grid-cols-3` også
+ på mobil (var `grid-cols-1 sm:grid-cols-3`, tok unødvendig mye plass) —
+ mindre ikon/tekst/høyde for å fortsatt være lesbare i smalere kolonner.
+ **Par/stroke-index manglet på rundeleaderboardet** (`/my-rounds/{id}/
+ leaderboard`) — ny `HoleReferenceStrip` viser Hull/Par/Hcp ÉN gang
+ (banedata, identisk for alle deltakere) rett over den rangerte listen.
+ **Venner: kategorisering nå OBLIGATORISK fra vennskapet inngås**
+ (presisert av bruker: "det skal ikke finnes ukategoriserte venner") —
+ `POST /friends` og `POST /friends/{id}/accept` krever begge minst én
+ kategori (`Field(min_length=1)`), satt AV BEGGE PARTER på hvert sitt
+ naturlige tidspunkt (avsender ved sending, mottaker ved aksept) — ikke
+ en frivillig senere handling. `PUT .../categories` nekter også å sette
+ et tomt sett. Ny delt `CategoryPicker`-komponent (friends.tsx, alle
+ forhåndsvalgt — "man må heller velge bort", presisert av bruker) brukt
+ tre steder (send/godta/rediger), nekter å fjerne SISTE avkrysning.
+ Selv-helbredende sikkerhetsnett for venner fra FØR denne regelen: ny
+ advarselsbanner øverst i `/my-friends` ("N venner mangler kategori"),
+ tvinger vedkommendes kategori-panel åpent til det er løst — bekreftet
+ reelt nødvendig i produksjon (Erol hadde aldri kategorisert Tore
+ Morell, sin faste matchspill-motstander — vil nå bli fanget opp og
+ tvunget løst neste gang `/my-friends` besøkes).
+ **Synlighetsregelen for "Venner"-runder endret fra "minst én treffende
+ kategori" til "ALLE vennens kategorier må være i det synlige settet"**
+ (presisert av bruker: "'ikke vise' overstyrer 'vise'") — en venn med
+ ÉN ikke-valgt kategori ekskluderes nå helt, selv om en annen av
+ kategoriene deres er valgt. En venn med NULL kategorier vises ALDRI
+ (avklart eksplisitt med bruker via spørsmål — skal i praksis aldri
+ forekomme lenger pga. regelen over, kun en igjenværende tilstand fra
+ FØR den ble håndhevet). `_can_view_round`/`_friends_who_can_see_round`
+ (rounds.py) omskrevet til denne AND-semantikken (fra tidligere OR).
+ `new-round.tsx` sin kategori-multiselect for "Venner"-synlighet starter
+ nå med ALLE forhåndsvalgt (samme "velg heller bort"-prinsipp).
+ **"Match leaderboard" — undersøkt grundig, IKKE en backend-bug:**
+ bekreftet direkte mot ekte `teecup_db` (kun lesing) at brukerens egen
+ Tjøme-matchrunde (`d857289c-...`) hadde begge sider korrekt satt opp
+ med beregnet `playing_handicap` — `format-result`-endepunktet ville
+ altså allerede regnet ut riktig "1 UP (A)"-status. Det reelle hullet
+ var at `FormatResultPanel` (matchstatus/skins-tavle, med hull-for-hull
+ vinner/AS-visning) KUN lå under "Spillere og runde"-fanen, usynlig fra
+ "Score"-fanen der scoring naturlig skjer. Fikset ved å vise samme
+ komponent (ikke duplisert logikk) øverst på BEGGE faner.
+ **Scratch-verifisert grundig** (isolert rolle+MinIO+engangs API-
+ container, samme mønster som hele prosjektet): full kategori-håndheving
+ (422 uten kategorier ved både send/godta/rediger, 200 med), presis
+ eksklusjon-overstyrer-inklusjon-test (venn med golf_friends+close_family
+ ekskludert når kun golf_friends er synlig, inkludert når begge er
+ synlige — bekreftet mot BÅDE rå SQL og de faktiske API-endepunktene),
+ match-runde med sider satt opp fra bunnen ga korrekt "1 UP (A)".
+ Deretter en FULL, ekte nettleser-gjennomgang (Chrome DevTools, mobil
+ 390×844): 3-kolonners hurtighandlinger, matchstatus-banner synlig på
+ Score-fanen, Hull/Par/Hcp-referanserad på leaderboardet, hele venne-
+ søk→kategorivelger(alle forhåndsvalgt, avkrysning fungerer)→send-
+ flyten, og selv-helbredende-banner-flyten (simulerte en gammel
+ ukategorisert venn direkte i databasen, bekreftet banneret dukket opp
+ OG forsvant igjen etter kategorisering via UI-et).
+ **Rullet ut live 2026-07-29**, ingen migrasjon (kun eksisterende felt/
+ logikk endret), `docker compose up -d --build teecup_api
+ teecup_frontend`. Begge containere boot-et rent, `/health`/`/dashboard`
+ → 200, anonymt `POST /friends` ga korrekt `401` (ikke en rå 404 —
+ bekrefter ruten når FastAPI), `teeoff.no` upåvirket.
+
+- **Match-scorekort redesignet på tvers av appen (frittstående runde +
+ turnering), BYGGET, GRUNDIG SCRATCH-/BROWSERVERIFISERT OG LIVE
+ (2026-07-29):** brukeren delte et referansebilde av en konkurrentapp sitt
+ 1v1-matchscorekort (horisontalt rutenett, farget etter hvem som vant
+ hvert hull, løpende "Stilling"-rad AS/X UP/X&Y, navn+HCP-banner) og ba om
+ det tilsvarende for ALLE match-scorekort i TeeCup, uten direkte plagiat.
+ **Bevisst IKKE en kopi:** beholdt appens egne, allerede etablerte
+ fargespråk i stedet for referansens røde/blå -- frittstående runder bruker
+ primær/oransje (samme "side A/side B"-konvensjon som `ResultChip`/
+ `FormatResultPanel` fra tidligere runder), turnering-matcher bruker lagets
+ faktiske `team.color` (samme konvensjon som resten av turnering-UI-et).
+ Droppet bevisst referansens "Matchplay NET/Stableford NET"-fane (ga ikke
+ entydig mening for delt-ball-formater) og "Lik/Kommentar til
+ spillfeeden/Spillere"-ikonraden (en helt ny sosial funksjon, ikke en
+ scorekort-redesign -- utenfor denne rundens omfang).
+ **Kjernemekanisme, ny og delt idé (separat implementert i begge filer,
+ ikke faktisk delt kode siden filene allerede har egne lokale typer per
+ etablert konvensjon):** `computeRunning()` speiler
+ `handicap_engine.py` sin `compute_match_state()`/`describe()` presist,
+ men regnet ETT PREFIKS om gangen client-side (backend cacher i dag kun
+ SLUTT-tilstanden) -- gir en løpende AS/X UP/dormie/X&Y-status per hull i
+ stedet for kun et sluttresultat.
+ **`round-scorecard.tsx`:** `MatchProgressTable` (vertikal hull-for-hull-
+ **`round-scorecard.tsx`:** den generiske `ScoreBlock`-tabellen erstattet med `MatchScorecardGrid` -- horisontalt rutenett (Hull/
+ liste) erstattet med `MatchScorecardGrid` -- horisontalt rutenett (Hull/
+ Par/en rad per spiller-ELLER-side/Stilling), identitetsbanner (navn+HCP,
+ farget dot, løpende sentral status + "Ferdig"-merke når avgjort). Fargen
+ på scorecellen følger HVEM SOM VANT hullet (fylt sirkel), ikke over/under
+ par -- egen semantikk fra `ScoreMark` med vilje, siden dette er en match,
+ ikke en individuell runde. `ApiParticipant` fikk `playing_handicap`
+ (allerede eksponert av backend, kun en frontend-typeutvidelse).
+ **`session-scorecard.tsx`:** `HoleSummaryTable`/`OutcomeBadge`/
+ `grossPairLabel` erstattet med `TournamentMatchGrid` (samme struktur,
+ brukt av BÅDE den pågående "Vis full oversikt"-seksjonen og
+ `DecidedView`s "Hull for hull" -- én komponent, ikke to). `Unit`-typen
+ fikk `playingHandicap`.
+ **Ekte, nødvendig backend-endring:** `match_participant.playing_handicap`
+ ble beregnet og lagret siden ADR-039, men var ALDRI eksponert i
+ `MatchParticipantOut` (matches.py) -- lagt til i modellen og i begge
+ SELECT-spørringene som populerer den (`fetch_matches` og
+ `add_participant`). **Reelt funn, fikset FØR utrulling:**
+ `add_participant` sin `row` hentes FØR
+ `compute_and_store_side_handicaps()` kjører -- en naiv
+ `MatchParticipantOut(**dict(row))` ville derfor alltid returnert
+ `playing_handicap: null` for en NYLIG lagt til deltaker, selv når verdien
+ faktisk ble beregnet et øyeblikk senere i samme kall. Fikset med et
+ eksplisitt re-oppslag av `playing_handicap` RETT FØR responsen bygges.
+ **Scratch-verifisert grundig** (isolert `teecup_app_scratch`-rolle +
+ isolert scratch-MinIO + engangs API-container, alle 37 migrasjoner kjørt
+ friskt, samme mønster som resten av prosjektet): en full match bygget fra
+ bunnen for BEGGE surfacene (en frittstående match-runde med to sider, OG
+ en full turnering-scaffold -- org/bane-import/tee/tournament/to lag/
+ roster/singel-økt/match/deltakere -- bygget fra API-et for FØRSTE gang i
+ et testskript denne uken, siden frittstående runder aldri har trengt det
+ apparatet før). 8-9 hull registrert med et bevisst blandet vinn/tap/delt-
+ mønster, bekreftet at `format-result`/`scorecard` sine per-hull
+ resultater matchet forventet handicap-justert utfall.
+ **Deretter en FULL, ekte nettleser-gjennomgang** (Chrome DevTools MCP):
+ et REELT, tidkrevende miljøproblem ble diagnostisert og løst underveis --
+ React-komponentenes egne `useEffect`-datahentinger kjørte ALDRI (evig
+ lastespinner på HVER side, ikke bare de nye) når dev-serveren ble besøkt
+ via `127.0.0.1:3100` i stedet for `localhost:3100`; Next.js 16 sin
+ `allowedDevOrigins`-beskyttelse blokkerer stille dev-ressurser (HMR-
+ websocket m.m.) for det som oppfattes som et fremmed opphav, og dette
+ fikk hele klient-hydreringen til å henge uten en eneste konsollfeil.
+ Løst ved å konsekvent bruke `localhost` i stedet for `127.0.0.1` --
+ ren miljø-lærdom for fremtidige scratch-frontend-økter, ikke en bug i
+ selve appen. Etter fiksen: bekreftet BEGGE match-scorekortene visuelt,
+ i BÅDE lys og mørk modus, i BÅDE pågående (løpende "2 UP"/farget
+ Stilling-rad) og avgjort tilstand ("8 UP"/"Ferdig"-merke, cachet
+ banner), inkl. et helt nytt 2FA-oppsett fullført på fersk konto via en
+ engangs e-post-2FA-kode lest fra dev-loggen (org-eier/admin krever 2FA,
+ ADR-021) for å nå frem til turnering-siden. Ingen konsollfeil i noen av
+ rundene.
+ **Rullet ut live 2026-07-29**, bruker bekreftet eksplisitt: ingen
+ migrasjon, `docker compose up -d --build teecup_api teecup_frontend`.
+ Begge containere boot-et rent, `/health`/`/dashboard` → 200, `teeoff.no`
+ → 200 upåvirket.
Neste steg:
0a. **Spillerliste-redesign — nå FAKTISK nettleser-bekreftet
diff --git a/DESIGN_SYSTEM.md b/DESIGN_SYSTEM.md
index 0ad002d..fe1dd46 100644
--- a/DESIGN_SYSTEM.md
+++ b/DESIGN_SYSTEM.md
@@ -14,6 +14,14 @@
> trykkflater, ikke ikon-only uten tekstlabel for viktige handlinger, ikke
> avhengig av finmotorikk/skarpt syn. Enhver visuell beslutning under
> veies mot dette først.
+>
+> **2026-07-29:** dette dokumentet ble revidert og formalisert basert på
+> "alternativ designinstruks.md" (brukerens eget forslag, forsøkt først på
+> dashbordet, deretter godkjent som ny fasit). Innholdet under ER derfor nå
+> den gjeldende regelen — men er ikke nødvendigvis rullet ut på ALLE
+> skjermer ennå (se `CLAUDE.md`-status for hvor langt konsistens-sjekken har
+> kommet). Rett opportunistisk opp eldre skjermer mot dette dokumentet når
+> de likevel røres, samme mønster som tilgjengelighetsregelen over.
## Merkevare-opprinnelse
@@ -34,8 +42,11 @@ se FEATURE_BACKLOG.md. Anbefalingen der var å gjenbruke `--chart-3`
Alle farger er definert som OKLCH-variabler, med egne verdier for lys og
mørk modus (`:root` / `.dark` / `@media (prefers-color-scheme: dark)`,
alle tre holdt i synk). Bruk ALLTID token-navnene under (Tailwind-klasser
-som `bg-primary`, `text-muted-foreground` osv.) — aldri rå hex/oklch i ny
-kode.
+som `bg-primary`, `text-muted-foreground` osv.) — aldri rå hex/oklch, og
+ALDRI hardkodede Tailwind-fargeklasser som `slate-500`/`gray-100` i ny
+kode. Dette er ikke bare en stilregel: hardkodede farger bryter den
+automatiske mørk/lys-tema-logikken (se "Lyst og mørkt tema" under), som
+ellers virker helt av seg selv så lenge token-klassene brukes.
| Token | Lys modus | Rolle |
|---|---|---|
@@ -80,10 +91,14 @@ er et direkte utslag av tilgjengelighetsregelen øverst.
- `text-lg` / `text-xl` — kortoverskrifter, seksjonstitler
- `text-2xl` / `text-3xl` — sideoverskrifter, hull-header, store
resultat-tall
-- **Vekt:** `font-semibold`/`font-bold` for all vektlagt tekst,
- `font-extrabold` for tall og hovedoverskrifter. Vanlig (regular) vekt
- brukes nesten aldri i UI-kontroller — selv sekundærtekst er ofte
- `font-semibold` for lesbarhet på avstand/i sollys.
+- **Vekt (revidert 2026-07-29):** ved `text-base` (16px) og god kontrast
+ (`text-foreground`) er `font-normal` helt akseptabelt og FORETRUKKET for
+ brødtekst — unngår visuell støy av at alt roper likt. Hierarki skapes
+ primært med STØRRELSE og FARGE (kontrast), ikke bare vekt. `text-sm`
+ eller mindre (bildetekst/metadata) bør derimot ofte gis `font-medium`
+ for å kompensere for den mindre størrelsen — aldri `font-normal` under
+ `text-base`. Overskrifter/tall/primærhandlinger beholder
+ `font-bold`/`font-extrabold` som før.
- **`tabular-nums` på ALL numerisk visning** (skår, HCP, datoer,
rangeringer) — hindrer tall fra å "hoppe" i bredde når de endres.
- **Golfvis fortegn:** til-par-tall formateres ALLTID som "E" (jevnt med
@@ -93,6 +108,17 @@ er et direkte utslag av tilgjengelighetsregelen øverst.
## Avstand, hjørner og lag
+- **8-punkts rutenett (2026-07-29):** marger, padding og `gap` følger
+ 8-gangen (`p-2`=8px, `p-4`=16px, `p-6`=24px, `p-8`=32px) for
+ forutsigbar rytme. Halv-steg (`gap-1.5`, `py-2.5` osv.) unngås i nytt
+ arbeid — brukes kun der et etablert mønster (f.eks. shadcn sine egne
+ primitiver) allerede definerer det.
+- **Kort får tydeligere løft (2026-07-29):** `shadow-md shadow-black/8`
+ (opp fra `shadow-sm shadow-black/5`) på kort/paneler i lyst tema — mer
+ synlig separasjon fra bakgrunnen. Se `round-card.tsx`/
+ `tournament-card.tsx`/`install-prompt.tsx`/`dashboard.tsx` for det
+ gjeldende mønsteret; eldre skjermer som fortsatt har `shadow-sm
+ shadow-black/5` bør rettes opp neste gang de røres.
- **Hjørneradius** (fra `--radius: 0.625rem`, skalert opp via
`--radius-sm/md/lg/xl/2xl/3xl/4xl`): `rounded-xl` for felt/knapper i
tette rader, `rounded-2xl` for kort/paneler/knapper med normal vekt,
@@ -121,6 +147,43 @@ er et direkte utslag av tilgjengelighetsregelen øverst.
- Gap mellom relaterte kontroller: `gap-2`/`gap-2.5`/`gap-3` normalt,
`gap-4`/`gap-6` mellom distinkte seksjoner.
+## Interaksjon (2026-07-29)
+
+- Alle interaktive elementer skal ha en definert `:hover`- OG
+ `:active`-tilstand — `:active` (f.eks. `active:scale-[0.98]` på kort/
+ knapper, `active:opacity-70` på lenker/ikon-knapper) gir umiddelbar,
+ taktil bekreftelse på at trykket faktisk ble registrert. shadcn sin
+ `Button`-primitiv har allerede `active:translate-y-px` innebygd; egne
+ håndbygde trykkflater (kort, ``-baserte rader) må legge det til
+ eksplisitt siden de ikke arver primitivens klasser.
+- `disabled` bruker `disabled:opacity-50 disabled:pointer-events-none`
+ (allerede standard i `Input`/`Button`).
+- Bevegelse: `transition-all duration-200 ease-in-out` for state-
+ endringer på knapper/kort — kjapt og "snappy", ikke en treg nettside-
+ følelse. Ingen animasjon/fade lenger enn 300ms.
+
+## Skjemaer og inndata (mobil-først)
+
+- Tekst i ``/`