Skrive migrasjon 052 (guest_first_name/guest_last_name + backfill) Bygge backend: splitt navn i ParticipantCreate/Update, auto-synk guest_name Bygge backend: GET known-guest-oppslag (e-post-basert, scoped til egen bruker) Bygge backend: retroaktiv e-post-kobling ved innlogging (auth.py) Bygge backend: HTML-e-post-støtte i email.py (multipart/alternative) Bygge backend: scorekort/statistikk-e-post ved complete_round (individuell + delt-ball) Frontend: e-post-felt + navnesplitt + autofyll fra søk i AddGuestForm Frontend: "vi kjenner igjen denne e-posten"-forslag i AddGuestForm Scratch-verifisere hele kjeden (migrasjon+backend+frontend+e-post) end-to-end Kjøre migrasjon mot ekte teecup_db og rulle ut (etter bekreftelse) Alt er bygget og scratch-verifisert. Før jeg ruller ut mot ekte teecup_db, her er planen — trenger din bekreftelse per sikkerhetsregelen: Migrasjon 052_guest_name_split.sql (rent additiv, ingen sletting): ALTER TABLE round_participant ADD COLUMN guest_first_name text; + guest_last_name text Backfiller eksisterende guest_name-rader ved samme "første ord/resten"-heuristikk som frontend bruker til autofyll Deretter: docker compose up -d --build teecup_api teecup_frontend Hva som er bygget: E-post som valgfritt felt i "Legg til uten konto (gjest)" (fantes i backend fra før, manglet i selve skjemaet) Navn splittet i for-/etternavn (etternavn valgfritt) Retroaktiv kobling: spiller runder som gjest før du har konto → runden dukker opp i "Egne runder" når du logger inn med samme e-post, men teller ikke automatisk mot faktisk HCP (må bekreftes selv — unngår at en fremmed kan påvirke HCP-en din uten samtykke) E-post med scorekort + statistikk + invitasjon sendes automatisk når runden fullføres, til enhver gjest med registrert e-post — første HTML-e-post i appen Autofyll fra søkefeltet inn i gjesteskjemaet "Vi kjenner igjen denne e-posten"-forslag, scoped til dine egne tidligere gjester (ikke globalt — unngår en personvernlekkasje)
206 lines
7.6 KiB
CSS
206 lines
7.6 KiB
CSS
@import 'tailwindcss';
|
|
@import 'tw-animate-css';
|
|
@import 'shadcn/tailwind.css';
|
|
|
|
@custom-variant dark (&:is(.dark *));
|
|
|
|
@theme inline {
|
|
--font-sans: var(--font-nunito), ui-sans-serif, system-ui, sans-serif;
|
|
--color-sidebar-ring: var(--sidebar-ring);
|
|
--color-sidebar-border: var(--sidebar-border);
|
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
--color-sidebar-accent: var(--sidebar-accent);
|
|
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
--color-sidebar-primary: var(--sidebar-primary);
|
|
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
--color-sidebar: var(--sidebar);
|
|
--color-chart-6: var(--chart-6);
|
|
--color-chart-5: var(--chart-5);
|
|
--color-chart-4: var(--chart-4);
|
|
--color-chart-3: var(--chart-3);
|
|
--color-chart-2: var(--chart-2);
|
|
--color-chart-1: var(--chart-1);
|
|
--color-ring: var(--ring);
|
|
--color-input: var(--input);
|
|
--color-border: var(--border);
|
|
--color-destructive: var(--destructive);
|
|
--color-accent-foreground: var(--accent-foreground);
|
|
--color-accent: var(--accent);
|
|
--color-muted-foreground: var(--muted-foreground);
|
|
--color-muted: var(--muted);
|
|
--color-secondary-foreground: var(--secondary-foreground);
|
|
--color-secondary: var(--secondary);
|
|
--color-primary-foreground: var(--primary-foreground);
|
|
--color-primary: var(--primary);
|
|
--color-popover-foreground: var(--popover-foreground);
|
|
--color-popover: var(--popover);
|
|
--color-card-foreground: var(--card-foreground);
|
|
--color-card: var(--card);
|
|
--color-foreground: var(--foreground);
|
|
--color-background: var(--background);
|
|
--color-brand-orange: var(--brand-orange);
|
|
--color-brand-orange-foreground: var(--brand-orange-foreground);
|
|
--color-info: var(--info);
|
|
--color-info-foreground: var(--info-foreground);
|
|
--color-gold: var(--gold);
|
|
--color-gold-foreground: var(--gold-foreground);
|
|
--radius-sm: calc(var(--radius) * 0.6);
|
|
--radius-md: calc(var(--radius) * 0.8);
|
|
--radius-lg: var(--radius);
|
|
--radius-xl: calc(var(--radius) * 1.4);
|
|
--radius-2xl: calc(var(--radius) * 1.8);
|
|
--radius-3xl: calc(var(--radius) * 2.2);
|
|
--radius-4xl: calc(var(--radius) * 2.6);
|
|
}
|
|
|
|
:root {
|
|
color-scheme: light;
|
|
/* Nøytrale flater er EKTE nøytral grå (chroma 0) -- før 2026-08-02 hadde
|
|
disse en svak grønn hue (130-145) iblandet, som ga et vedvarende
|
|
grønt skjær på nesten hver bakgrunn/hover/kant i hele appen. `primary`
|
|
er fortsatt den bevisste, mettede merkevare-grønnen -- kun den skal
|
|
lese som grønn. `--background` er ren hvit (0.985 var fortsatt en
|
|
synlig off-white, ikke "fjernet") -- identisk med `--card`, kort
|
|
skilles fra siden via border/shadow, ikke en bakgrunnsfarge-forskjell. */
|
|
--background: oklch(1 0 0);
|
|
--foreground: oklch(0.24 0 0);
|
|
--card: oklch(1 0 0);
|
|
--card-foreground: oklch(0.24 0 0);
|
|
--popover: oklch(1 0 0);
|
|
--popover-foreground: oklch(0.24 0 0);
|
|
--primary: oklch(0.7512 0.1613 130.33);
|
|
--primary-foreground: oklch(0.22 0 0);
|
|
--secondary: oklch(0.96 0 0);
|
|
--secondary-foreground: oklch(0.3 0 0);
|
|
--muted: oklch(0.96 0 0);
|
|
--muted-foreground: oklch(0.52 0 0);
|
|
--accent: oklch(0.94 0 0);
|
|
--accent-foreground: oklch(0.3 0 0);
|
|
--destructive: oklch(0.577 0.245 27.325);
|
|
--border: oklch(0.9 0 0);
|
|
--input: oklch(0.9 0 0);
|
|
--ring: oklch(0.7512 0.1613 130.33);
|
|
--brand-orange: oklch(0.6792 0.2128 36.53);
|
|
--brand-orange-foreground: oklch(0.99 0 0);
|
|
/* Løftet fra chart-3/chart-4 (2026-07-28, se DESIGN_SYSTEM.md) -- egne
|
|
kjernetoken for nøytral informasjon (--info) og personlig-rekord/
|
|
merkeverdig-prestasjon (--gold), samme validerte OKLCH-verdier som
|
|
statistikk-skalaen bruker allerede. */
|
|
--info: oklch(0.68 0.13 210);
|
|
--info-foreground: oklch(0.99 0 0);
|
|
--gold: oklch(0.79 0.15 85);
|
|
--gold-foreground: oklch(0.22 0.04 85);
|
|
/* Statistikk-fargeskala (rundestatistikk, ADR-033) -- brukt sammen med
|
|
tekst-/tall-etiketter, aldri farge alene som eneste signal. */
|
|
--chart-1: oklch(0.55 0.15 160);
|
|
--chart-2: oklch(0.72 0.17 140);
|
|
--chart-3: oklch(0.68 0.13 210);
|
|
--chart-4: oklch(0.79 0.15 85);
|
|
--chart-5: oklch(0.68 0.2 45);
|
|
--chart-6: oklch(0.57 0.22 25);
|
|
--radius: 0.625rem;
|
|
--sidebar: oklch(0.985 0 0);
|
|
--sidebar-foreground: oklch(0.145 0 0);
|
|
--sidebar-primary: oklch(0.205 0 0);
|
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
--sidebar-accent: oklch(0.97 0 0);
|
|
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
--sidebar-border: oklch(0.922 0 0);
|
|
--sidebar-ring: oklch(0.708 0 0);
|
|
}
|
|
|
|
.dark {
|
|
color-scheme: dark;
|
|
--background: oklch(0.19 0 0);
|
|
--foreground: oklch(0.96 0 0);
|
|
--card: oklch(0.24 0 0);
|
|
--card-foreground: oklch(0.96 0 0);
|
|
--popover: oklch(0.24 0 0);
|
|
--popover-foreground: oklch(0.96 0 0);
|
|
--primary: oklch(0.7512 0.1613 130.33);
|
|
--primary-foreground: oklch(0.2 0 0);
|
|
--secondary: oklch(0.28 0 0);
|
|
--secondary-foreground: oklch(0.96 0 0);
|
|
--muted: oklch(0.28 0 0);
|
|
--muted-foreground: oklch(0.7 0 0);
|
|
--accent: oklch(0.32 0 0);
|
|
--accent-foreground: oklch(0.96 0 0);
|
|
--destructive: oklch(0.704 0.191 22.216);
|
|
--border: oklch(1 0 0 / 12%);
|
|
--input: oklch(1 0 0 / 16%);
|
|
--ring: oklch(0.7512 0.1613 130.33);
|
|
--brand-orange: oklch(0.6792 0.2128 36.53);
|
|
--brand-orange-foreground: oklch(0.16 0.02 40);
|
|
--info: oklch(0.75 0.13 210);
|
|
--info-foreground: oklch(0.16 0.02 210);
|
|
--gold: oklch(0.83 0.14 85);
|
|
--gold-foreground: oklch(0.18 0.03 85);
|
|
/* Statistikk-fargeskala, løftet for kontrast på mørk bakgrunn. */
|
|
--chart-1: oklch(0.7 0.16 160);
|
|
--chart-2: oklch(0.79 0.17 140);
|
|
--chart-3: oklch(0.75 0.13 210);
|
|
--chart-4: oklch(0.83 0.14 85);
|
|
--chart-5: oklch(0.73 0.19 45);
|
|
--chart-6: oklch(0.66 0.21 25);
|
|
--sidebar: oklch(0.205 0 0);
|
|
--sidebar-foreground: oklch(0.985 0 0);
|
|
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
--sidebar-accent: oklch(0.269 0 0);
|
|
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
--sidebar-border: oklch(1 0 0 / 10%);
|
|
--sidebar-ring: oklch(0.556 0 0);
|
|
}
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
:root:not(.light) {
|
|
color-scheme: dark;
|
|
--background: oklch(0.19 0 0);
|
|
--foreground: oklch(0.96 0 0);
|
|
--card: oklch(0.24 0 0);
|
|
--card-foreground: oklch(0.96 0 0);
|
|
--popover: oklch(0.24 0 0);
|
|
--popover-foreground: oklch(0.96 0 0);
|
|
--primary: oklch(0.7512 0.1613 130.33);
|
|
--primary-foreground: oklch(0.2 0 0);
|
|
--secondary: oklch(0.28 0 0);
|
|
--secondary-foreground: oklch(0.96 0 0);
|
|
--muted: oklch(0.28 0 0);
|
|
--muted-foreground: oklch(0.7 0 0);
|
|
--accent: oklch(0.32 0 0);
|
|
--accent-foreground: oklch(0.96 0 0);
|
|
--destructive: oklch(0.704 0.191 22.216);
|
|
--border: oklch(1 0 0 / 12%);
|
|
--input: oklch(1 0 0 / 16%);
|
|
--ring: oklch(0.7512 0.1613 130.33);
|
|
--brand-orange: oklch(0.6792 0.2128 36.53);
|
|
--brand-orange-foreground: oklch(0.16 0.02 40);
|
|
--info: oklch(0.75 0.13 210);
|
|
--info-foreground: oklch(0.16 0.02 210);
|
|
--gold: oklch(0.83 0.14 85);
|
|
--gold-foreground: oklch(0.18 0.03 85);
|
|
--chart-1: oklch(0.7 0.16 160);
|
|
--chart-2: oklch(0.79 0.17 140);
|
|
--chart-3: oklch(0.75 0.13 210);
|
|
--chart-4: oklch(0.83 0.14 85);
|
|
--chart-5: oklch(0.73 0.19 45);
|
|
--chart-6: oklch(0.66 0.21 25);
|
|
--sidebar: oklch(0.205 0 0);
|
|
--sidebar-foreground: oklch(0.985 0 0);
|
|
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
--sidebar-accent: oklch(0.269 0 0);
|
|
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
--sidebar-border: oklch(1 0 0 / 10%);
|
|
--sidebar-ring: oklch(0.556 0 0);
|
|
}
|
|
}
|
|
|
|
@layer base {
|
|
* {
|
|
@apply border-border outline-ring/50;
|
|
}
|
|
body {
|
|
@apply bg-background text-foreground;
|
|
}
|
|
}
|