---
version: alpha
name: Frivio Design System
description: Frivios designsystem — flater, tekst, kant, primærknapp, radius og gradienter er egne, bevisste Frivio-valg oppå en solid 10-stegs fargeskala (gray, gray-alpha, blue, red, amber, green, teal, purple, pink; alle 10 steg, begge tema). Denne blokken er generert direkte fra app/globals.css, som er systemets eneste kilde til sannhet — verdiene speiler bølge 1 av token-kontrakten (founder-beslutninger 2026-09-10). Dark-temaet er standard (Frivio-identiteten); Light bruker de samme token-navnene med andre verdier.
generated_from: app/globals.css   # skrives av scripts/build-skill.mjs — ikke rediger for hånd
tokens:
  colors:
    dark:
      gray-100: "#1a1a1a"
      gray-200: "#1f1f1f"
      gray-300: "#292929"
      gray-400: "#2e2e2e"
      gray-500: "#454545"
      gray-600: "#878787"
      gray-700: "#8f8f8f"
      gray-800: "#7d7d7d"
      gray-900: "#a0a0a0"
      gray-1000: "#ededed"
      gray-alpha-100: "#ffffff12"
      gray-alpha-200: "#ffffff17"
      gray-alpha-300: "#ffffff21"
      gray-alpha-400: "#ffffff24"
      gray-alpha-500: "#ffffff3d"
      gray-alpha-600: "#ffffff82"
      gray-alpha-700: "#ffffff8a"
      gray-alpha-800: "#ffffff78"
      gray-alpha-900: "#ffffff9c"
      gray-alpha-1000: "#ffffffeb"
      blue-100: "#06193a"
      blue-200: "#022248"
      blue-300: "#002f62"
      blue-400: "#003771"
      blue-500: "#004287"
      blue-600: "#0090ff"
      blue-700: "#0071f6"
      blue-800: "#005fd8"
      blue-900: "#50a8ff"
      blue-1000: "#ebf6ff"
      red-100: "#330a11"
      red-200: "#440d13"
      red-300: "#5d0e17"
      red-400: "#6f101b"
      red-500: "#88151f"
      red-600: "#f32e40"
      red-700: "#f13242"
      red-800: "#e2162a"
      red-900: "#ff5e63"
      red-1000: "#ffeaed"
      amber-100: "#291800"
      amber-200: "#331b00"
      amber-300: "#4f2900"
      amber-400: "#573200"
      amber-500: "#6c4100"
      amber-600: "#e99c00"
      amber-700: "#ffb200"
      amber-800: "#ff9900"
      amber-900: "#ff9900"
      amber-1000: "#fff3d9"
      green-100: "#00250a"
      green-200: "#003110"
      green-300: "#003814"
      green-400: "#004616"
      green-500: "#00661d"
      green-600: "#009431"
      green-700: "#00ab3e"
      green-800: "#009335"
      green-900: "#00ca52"
      green-1000: "#daffe5"
      teal-100: "#00211b"
      teal-200: "#002922"
      teal-300: "#003b33"
      teal-400: "#003f35"
      teal-500: "#005f53"
      teal-600: "#009885"
      teal-700: "#00a794"
      teal-800: "#008d7d"
      teal-900: "#00c9b5"
      teal-1000: "#cefff5"
      purple-100: "#290c33"
      purple-200: "#341142"
      purple-300: "#47185e"
      purple-400: "#541a76"
      purple-500: "#642290"
      purple-600: "#9440d5"
      purple-700: "#9440d5"
      purple-800: "#7d2bba"
      purple-900: "#c472fb"
      purple-1000: "#faedff"
      pink-100: "#310d1e"
      pink-200: "#420c25"
      pink-300: "#571032"
      pink-400: "#5d0c34"
      pink-500: "#76063f"
      pink-600: "#b90056"
      pink-700: "#f12b82"
      pink-800: "#e6006e"
      pink-900: "#ff518d"
      pink-1000: "#ffeaf4"
      bg: "#000000"
      surface: "#0a0a0a"
      surface-2: "var(--color-gray-100)"
      surface-3: "var(--color-gray-200)"
      overlay: "rgb(0 0 0 / 0.4)"
      border: "var(--color-gray-alpha-400)"
      border-2: "var(--color-gray-alpha-500)"
      border-3: "var(--color-gray-alpha-600)"
      text-primary: "var(--color-gray-1000)"
      text-secondary: "var(--color-gray-900)"
      text-tertiary: "var(--color-gray-700)"
      text-quaternary: "var(--color-text-tertiary)"
      accent: "#6d9bff"
      accent-hover: "color-mix(in srgb, #6d9bff 80%, black)"
      accent-strong: "#3870e8"
      accent-strong-hover: "#1a5ae0"
      accent-light: "color-mix(in srgb, var(--color-accent) 18%, var(--color-bg))"
      accent-fg: "#ffffff"
      accent-text: "color-mix(in srgb, var(--color-accent) 72%, var(--color-text-primary))"
      accent-2: "#4b7eea"
      accent-glow: "color-mix(in srgb, var(--color-accent) 35%, transparent)"
      accent-border: "color-mix(in srgb, var(--color-accent) 35%, transparent)"
      primary-bg: "var(--color-text-primary)"
      primary-fg: "var(--color-bg)"
      primary-hover: "color-mix(in srgb, var(--color-primary-bg) 85%, var(--color-bg))"
      btn-bg: "var(--color-primary-bg)"
      btn-fg: "var(--color-primary-fg)"
      btn-bg-hover: "var(--color-primary-hover)"
      success: "var(--color-teal-700)"
      success-hover: "var(--color-teal-800)"
      success-light: "var(--color-teal-100)"
      success-border: "var(--color-teal-400)"
      success-text: "var(--color-teal-900)"
      success-solid: "var(--color-teal-900)"
      success-fg: "#000000"
      warning: "var(--color-amber-700)"
      warning-hover: "var(--color-amber-800)"
      warning-light: "var(--color-amber-100)"
      warning-border: "var(--color-amber-400)"
      warning-text: "var(--color-amber-900)"
      warning-solid: "var(--color-amber-700)"
      warning-fg: "#000000"
      error: "var(--color-red-700)"
      error-hover: "var(--color-red-800)"
      error-light: "var(--color-red-100)"
      error-border: "var(--color-red-400)"
      error-text: "var(--color-red-900)"
      error-solid: "var(--color-red-800)"
      error-fg: "#ffffff"
      neutral-light: "var(--color-gray-alpha-100)"
      akutt: "var(--color-error)"
      akutt-light: "var(--color-error-light)"
      akutt-border: "var(--color-error-border)"
      akutt-text: "var(--color-error-text)"
      hoy: "var(--color-warning)"
      hoy-light: "var(--color-warning-light)"
      hoy-border: "var(--color-warning-border)"
      hoy-text: "var(--color-warning-text)"
      middels: "var(--color-gray-700)"
      middels-light: "var(--color-gray-100)"
      middels-border: "var(--color-gray-400)"
      middels-text: "var(--color-gray-900)"
      lav: "var(--color-success)"
      lav-light: "var(--color-success-light)"
      lav-border: "var(--color-success-border)"
      lav-text: "var(--color-success-text)"
    light:
      success-fg: "#ffffff"
      gray-100: "#f2f2f2"
      gray-200: "#ebebeb"
      gray-300: "#e6e6e6"
      gray-400: "#eaeaea"
      gray-500: "#c9c9c9"
      gray-600: "#a8a8a8"
      gray-700: "#8f8f8f"
      gray-800: "#7d7d7d"
      gray-900: "#4d4d4d"
      gray-1000: "#171717"
      gray-alpha-100: "#0000000d"
      gray-alpha-200: "#00000015"
      gray-alpha-300: "#0000001a"
      gray-alpha-400: "#00000014"
      gray-alpha-500: "#00000036"
      gray-alpha-600: "#0000003d"
      gray-alpha-700: "#00000070"
      gray-alpha-800: "#00000082"
      gray-alpha-900: "#000000b3"
      gray-alpha-1000: "#000000e8"
      blue-100: "#f0f7ff"
      blue-200: "#eaf4ff"
      blue-300: "#e0efff"
      blue-400: "#cce7ff"
      blue-500: "#97ccff"
      blue-600: "#51aeff"
      blue-700: "#0070f7"
      blue-800: "#005edc"
      blue-900: "#0064e2"
      blue-1000: "#002453"
      red-100: "#ffeef0"
      red-200: "#ffe9ea"
      red-300: "#ffe4e5"
      red-400: "#ffd8d7"
      red-500: "#ffb5b6"
      red-600: "#ff6a6e"
      red-700: "#fc0035"
      red-800: "#e70022"
      red-900: "#d60020"
      red-1000: "#46000c"
      amber-100: "#fff6e1"
      amber-200: "#fff4d4"
      amber-300: "#fff1c8"
      amber-400: "#ffdd84"
      amber-500: "#ffc85e"
      amber-600: "#ffaa00"
      amber-700: "#ffb200"
      amber-800: "#ff9900"
      amber-900: "#a64f00"
      amber-1000: "#541c00"
      green-100: "#ecfdec"
      green-200: "#e5fce7"
      green-300: "#d3fad1"
      green-400: "#b9f5bc"
      green-500: "#82eb8d"
      green-600: "#4ce15e"
      green-700: "#28a948"
      green-800: "#279141"
      green-900: "#107d32"
      green-1000: "#00370d"
      teal-100: "#dffffb"
      teal-200: "#ddfef6"
      teal-300: "#ccf9f1"
      teal-400: "#b1f7ec"
      teal-500: "#52f0db"
      teal-600: "#00e2c4"
      teal-700: "#00a694"
      teal-800: "#008d7d"
      teal-900: "#007a6e"
      teal-1000: "#003d34"
      purple-100: "#f9f0ff"
      purple-200: "#f9f1ff"
      purple-300: "#f5e8ff"
      purple-400: "#f1d9ff"
      purple-500: "#dda9ff"
      purple-600: "#c77dff"
      purple-700: "#9f00f4"
      purple-800: "#8400cd"
      purple-900: "#7c00c9"
      purple-1000: "#2e004d"
      pink-100: "#ffeaf5"
      pink-200: "#ffeaf2"
      pink-300: "#ffe0eb"
      pink-400: "#ffd5e1"
      pink-500: "#fdb3cc"
      pink-600: "#f97ea7"
      pink-700: "#f22782"
      pink-800: "#e4106e"
      pink-900: "#c41562"
      pink-1000: "#460523"
      bg: "#ffffff"
      surface: "#fafafa"
      text-tertiary: "#666666"
      accent: "#4b7eea"
      accent-hover: "#3a6bd6"
      accent-strong: "#3870e8"
      accent-strong-hover: "#1a5ae0"
      accent-light: "var(--color-blue-300)"
      accent-fg: "#ffffff"
      accent-text: "color-mix(in srgb, #4b7eea 72%, var(--color-text-primary))"
      accent-2: "#6d9bff"
      accent-glow: "color-mix(in srgb, #4b7eea 35%, transparent)"
      accent-border: "color-mix(in srgb, var(--color-accent) 35%, transparent)"
      primary-bg: "var(--color-text-primary)"
      primary-fg: "var(--color-bg)"
      primary-hover: "color-mix(in srgb, var(--color-primary-bg) 85%, var(--color-bg))"
      btn-bg: "var(--color-primary-bg)"
      btn-fg: "var(--color-primary-fg)"
      btn-bg-hover: "var(--color-primary-hover)"
  print:
    paper: "#ffffff"
    canvas: "#f3f4f6"
    surface: "#f9fafb"
    border: "#e5e7eb"
    border-subtle: "#f0f1f3"
    text-strong: "#111827"
    text: "#374151"
    text-secondary: "#6b7280"
    text-muted: "#9ca3af"
    text-faint: "#b0b7c3"
    accent: "#4b7eea"
    accent-strong: "#2f62d0"
    status-danger-bg: "#fef2f2"
    status-danger-text: "#b91c1c"
    status-danger-border: "#fecaca"
    status-warning-bg: "#fffbeb"
    status-warning-text: "#b45309"
    status-info-bg: "#eff6ff"
    status-info-text: "#1d4ed8"
    status-success-bg: "#f0fdf4"
    status-success-text: "#15803d"
    shadow: "0 1px 2px rgba(16,24,40,0.04), 0 14px 36px -18px rgba(16,24,40,0.20)"
  radius:
    sm: "0.375rem"
    md: "0.75rem"
    lg: "1rem"
    full: "9999px"
  space:
    1: "0.25rem"
    2: "0.5rem"
    3: "0.75rem"
    4: "1rem"
    5: "1.25rem"
    6: "1.5rem"
    8: "2rem"
    10: "2.5rem"
    12: "3rem"
    16: "4rem"
    20: "5rem"
    24: "6rem"
    1-5: "0.375rem"
    2-5: "0.625rem"
    3-5: "0.875rem"
  shadow:
    dark:
      xs: "0 1px 2px rgba(0,0,0,0.16)"
      sm: "0 1px 2px rgba(0,0,0,0.16)"
      md: "0 1px 1px rgba(0,0,0,0.02), 0 4px 8px -4px rgba(0,0,0,0.04), 0 16px 24px -8px rgba(0,0,0,0.06)"
      lg: "0 1px 1px rgba(0,0,0,0.02), 0 8px 16px -4px rgba(0,0,0,0.04), 0 24px 32px -8px rgba(0,0,0,0.06)"
      xl: "0 1px 1px rgba(0,0,0,0.02), 0 8px 16px -4px rgba(0,0,0,0.04), 0 24px 32px -8px rgba(0,0,0,0.06)"
      border: "0 0 0 1px var(--color-gray-alpha-400)"
      menu: "var(--shadow-border), 0 1px 1px #00000005, 0 4px 8px -4px #0000000a, 0 16px 24px -8px #0000000f"
      modal: "var(--shadow-border), 0 1px 1px #00000005, 0 8px 16px -4px #0000000a, 0 24px 32px -8px #0000000f"
      tooltip: "var(--shadow-border), 0 1px 1px #00000005, 0 4px 8px #0000000a"
    light:
      xs: "0 2px 2px rgba(0,0,0,0.04)"
      sm: "0 2px 2px rgba(0,0,0,0.04)"
      md: "0 1px 2px rgba(0,0,0,0.08), 0 6px 12px -4px rgba(0,0,0,0.10), 0 20px 32px -12px rgba(0,0,0,0.14)"
      lg: "0 1px 2px rgba(0,0,0,0.06), 0 6px 12px -4px rgba(0,0,0,0.08), 0 20px 32px -12px rgba(0,0,0,0.10)"
      xl: "0 1px 2px rgba(0,0,0,0.06), 0 6px 12px -4px rgba(0,0,0,0.08), 0 20px 32px -12px rgba(0,0,0,0.10)"
  motion:
    ease-spring: "cubic-bezier(0.175, 0.885, 0.32, 1.1)"
    duration-state: "150ms"
    duration-popover: "200ms"
    duration-modal: "300ms"
  font:
    font: "var(--font-family-sans), -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif"
    font-sans: "var(--font)"
    font-heading: "var(--font)"
    font-mono: "var(--font-family-mono), ui-monospace, \"SF Mono\", Menlo, monospace"
    font-display: "var(--font-heading)"
  type:
    heading-72: "600 72px/72px"
    heading-72-ls: "-4.32px"
    heading-64: "600 64px/64px"
    heading-64-ls: "-3.84px"
    heading-56: "600 56px/56px"
    heading-56-ls: "-3.36px"
    heading-48: "600 48px/56px"
    heading-48-ls: "-2.88px"
    heading-40: "600 40px/48px"
    heading-40-ls: "-2.4px"
    heading-32: "600 32px/40px"
    heading-32-ls: "-1.28px"
    heading-24: "600 24px/32px"
    heading-24-ls: "-0.96px"
    heading-20: "600 20px/26px"
    heading-20-ls: "-0.4px"
    heading-16: "600 16px/24px"
    heading-16-ls: "-0.32px"
    heading-14: "600 14px/20px"
    heading-14-ls: "-0.28px"
    button-16: "500 16px/20px"
    button-16-ls: "normal"
    button-14: "500 14px/20px"
    button-14-ls: "normal"
    button-12: "500 12px/16px"
    button-12-ls: "normal"
    label-20: "400 20px/32px"
    label-20-ls: "normal"
    label-18: "400 18px/20px"
    label-18-ls: "normal"
    label-16: "400 16px/20px"
    label-16-ls: "normal"
    label-14: "400 14px/20px"
    label-14-ls: "normal"
    label-13: "400 13px/16px"
    label-13-ls: "normal"
    label-12: "400 12px/16px"
    label-12-ls: "normal"
    copy-24: "400 24px/36px"
    copy-24-ls: "normal"
    copy-20: "400 20px/36px"
    copy-20-ls: "normal"
    copy-18: "400 18px/28px"
    copy-18-ls: "normal"
    copy-16: "400 16px/24px"
    copy-16-ls: "normal"
    copy-14: "400 14px/20px"
    copy-14-ls: "normal"
    copy-13: "400 13px/18px"
    copy-13-ls: "normal"

---

# Frivio Design System

> **Skal du BYGGE noe med dette systemet — ikke bare slå opp en verdi?**
> Bruk skillen i stedet: **<https://www.frivio.no/frivio-ui.skill>**
>
> Dette dokumentet er spesifikasjonen (hva systemet ER). Skillen er systemet
> pakket til bruk: en `SKILL.md` med lovene, anti-mønstrene og avledningsreglene,
> pluss to droppbare filer — `frivio-tokens.css` (alle tokens + typografiklasser,
> selvforsynt med fonter) og `frivio-kit.tsx` (18 React-komponenter, ingen
> avhengigheter utenom React, ingen `tailwind.config`-endringer). Last ned,
> pakk ut i `.claude/skills/`, og systemet er tilgjengelig i et hvilket som helst
> prosjekt.
>
> Tokenene i skillen er prefikset `--frv-` (kollisjonsvern i fremmede prosjekter);
> internt i Frivio heter de `--color-*`. 1:1-mapping.

## Overview

Frivio Design System er et helhetlig token- og komponentlag for hele Frivio-appen (norsk SaaS for vedlikeholdsplanlegging i borettslag og sameier): en solid 10-stegs fargeskala, en typografisk stige og delte React-komponenter bygget oppå dem. Estetikken er minimal og høykontrast: mye luft, tilbakeholden farge, og innhold satt på nær-nøytrale flater. Prioriter lesbarhet og tilgjengelighet, og bruk farge til å signalisere tilstand og hierarki — ikke til dekorasjon.

Systemet finnes på fire flater, i denne rekkefølgen: **`app/globals.css`** er den ENESTE kilden til sannhet for hver verdi (farger i `@theme static`, typografi/spacing/radius/skygger/motion i `:root`, sammen med `.type-*`-klassene); `npm run build:skill` genererer dette dokumentet og skill-pakken (`frivio-tokens.css` + `frivio-kit.tsx`) fra den kilden; og den levende dokumentasjonssiden på **<https://design.frivio.no>** (nåbar også på `www.frivio.no/design`) viser hvert token og hver komponent i bruk, én side per komponent. Komponenter og skjermer hardkoder aldri verdier; de refererer tokenene, `.type-*`-klassene eller de delte `components/ui/`-komponentene. Endrer du en verdi i `globals.css`, oppdateres alt som bruker tokenet — inkludert dokumentasjonssiden.

Denne filen er servert RÅTT og OFFENTLIG med hensikt: den er en maskinlesbar spesifikasjon, ikke bare et menneske-dokument — andre Claude Code-agenter på andre prosjekt kan `WebFetch`/`curl` den som referanse.

Dette er Dark-temaet (Frivio-identiteten, standard). Light-temaet bruker de samme token-navnene med andre verdier og ligger i `:root[data-theme='light']`.

## Colors

Hver ikke-bakgrunnsskala går 10 steg (`100`–`1000`), og steget koder BRUK, ikke bare lyshet:

- `100` standard bakgrunn
- `200` hover-bakgrunn
- `300` aktiv bakgrunn
- `400` standard kant
- `500` hover-kant
- `600` aktiv kant
- `700` fylt flate, høy kontrast
- `800` fylt flate, hover
- `900` sekundær tekst og ikoner
- `1000` primær tekst og ikoner

`--color-bg` er sideflaten (mørkt `#000000`, lyst `#ffffff`); `surface` ligger ett steg opp (mørkt `#0a0a0a`, lyst `#fafafa`), og `surface-2`/`surface-3` er `gray-100`/`gray-200` i begge tema — kort, paneler og sidemeny. Kanter (`border`/`-2`/`-3`) peker på `gray-alpha-400`/`-500`/`-600` i begge tema; alpha-skalaen er allerede tema-satt, så kantene trenger ingen egen lys-overstyring. Solid `gray-*` holder kontrasten på enhver flate, så bruk dem til tekst og opake fyll; `gray-alpha-*` er translucent og legger seg oppå enhver bakgrunn, så den bærer kant, delelinje, overlegg og hover-tint.

**Status-oppskriften er ÉN formel per rolle** (bølge 1 av token-kontrakten, 2026-09-10): `success` → teal, `warning` → amber, `error` → red.
- Selve rollen (`--color-success` osv.) er steg `700` — en FLATE, aldri tekst.
- `-hover` (`800`) er samme flate sin hover-tilstand.
- `-light` (`100`) er bakgrunnstint til varselbokser og myke badges.
- `-border` (`400`) er kanten til den tinten.
- `-text` (`900`) er ALLTID fargen for tekst OG ikoner — de rå fargene over består kun WCAG sin 3:1-grafikkterskel, ikke 4,5:1-teksterskelen.
- `-solid` er steget som faktisk holder AA for en etikett OPPÅ den fylte flaten — ikke alltid samme steg som rollen selv: teal `700`/`800` gir bare 3–4:1 mot hvitt, så `success-solid` bruker `900`; amber og red holder AA på sitt eget steg.
- `-fg` er etiketten på `-solid`, satt per farge og ikke antatt: amber og (i mørkt tema) suksess bærer svart tekst, feil og lys-modus-suksess bærer hvit.

Samme oppskrift gjelder prioritetsdomenet: `akutt` = `error`, `hoy` = `warning`, `lav` = `success` — samme verdier, eget navnerom, så prioritet kan flytte uten å dra med seg hver feilmelding. `middels` er IKKE en alias: den er en egen, nøytral gray-basert rolle (`gray-700`/`-100`/`-400`/`-900`), fordi en alias til amber/warning gjorde «middels» og «høy» visuelt like — den underliggende skalaens mørke-tema `amber-800`/`900` er literalt samme hex.

**Tekst har tre steg:** `text-primary` (`gray-1000`), `text-secondary` (`gray-900`), `text-tertiary` (`gray-700` i mørkt tema, men en literal `#666666` i lyst — `gray-800` der gir bare 3,6:1 mot kortflaten). `text-quaternary` er en alias av `text-tertiary`, beholdt for eldre kallsteder og under utfasing — bruk `text-tertiary` i ny kode.

Aksentskalaene bærer mening: `blue` for lenker, fokus og den ene primærhandlingen; `red` for feil; `amber` for advarsel; `teal` for suksess; pluss `green`, `purple` og `pink` tilgjengelig for datavisning og innhold, aldri for UI-chrome. `accent` er Frivios egen brand-blå i samme familie som `blue`-skalaen: `#4b7eea` i lyst tema, `#6d9bff` i mørkt (lysere, fordi den mot nesten-svart ellers leser dempet). Frivio er bevisst monokromt med én blå aksent i chromet; primærknappen selv er nøytral (`primary-bg`/`primary-fg`, IKKE blå — se Components).

Gradient viser, flatt opererer: `--gradient-card`, `--gradient-hero` og `--gradient-glow` er nå rene GRÅTONE-forløp (`gray-1000` mot `surface`/`gray-800`, via `color-mix`) — ingen aksentfarge i gradientene lenger. Bruk dem KUN på flater som viser noe — hero-KPI, standard-kort, tomtilstands-heroer, dashbord-toppglød. Bruk dem ALDRI på flater man opererer i — skjema/input, modaler, popovers/menyer, listerader, tabeller, notis-bannere, knapper, sidemeny. Der holder flatt (`surface`/`surface-2` uten gradient) hierarkiet.

**`skann-*` er tema-uavhengige**, samme idé som `print.*` under: kun for AnalysisView-skanneflaten (kino-visningen under AI-analysen) — mørk uansett tema. Ikke gjenbruk i vanlig UI.

## Print (tema-uavhengig)

Utskrifts-/dokumentflater (faktura, purring, andelseierbok, skattegrunnlag, referater) skal ALLTID rendre som hvitt papir, uansett hvilket app-tema brukeren har valgt — en regning må se lik ut for alle som mottar den. Disse tokenene (`print.*` i frontmatter) lever derfor i en helt egen, tema-invariant navnerom, ALDRI under de tema-bytta `--color-*`-tokenene over. `print.accent` er kun trygg som dekor/grafikk (3.84:1 mot papir — feiler AA-tekst); bruk `print.accent-strong` for tekst og knappeflater (målt mot BÅDE papir og skjermbakgrunnen bak arket).

## Typography

Ett fontpar setter hele systemet: en proporsjonal skrift for UI og prose, og en monospace-følgesvenn for kode, data og tabulære figurer — satt én gang som `--font`/`--font-mono` (2026-09-10: tilbake til dette fontparet etter en kort ukes avstikker til Inter). `typography`-tokenene i frontmatteren bærer konkret `fontFamily`, `fontSize`, `fontWeight`, `lineHeight` og `letterSpacing`, og konsumeres via `.type-*`-klassene og `<Heading>`/`<Text>`-komponentene (klassenavnene står i `tokens.type` over):

- Headings, `heading-72` til `heading-14`, titulerer sider og seksjoner; `letterSpacing` strammes når størrelsen vokser.
- Labels, `label-20` til `label-12`, bærer enkeltlinjet, skannbar tekst: navigasjon, feltetiketter, tabellhoder, metadata.
- Copy, `copy-24` til `copy-13`, setter flerlinjet brødtekst med høyere `lineHeight`.
- Buttons, `button-16` til `button-12`, er mellomvekt-etiketter for knapper og kompakte kontroller.
- Display, `.type-display-1`/`.type-display-2`, er responsive to-stegs overskrifter KUN for landingssiden (mobil → `heading-40`/`heading-32`, fra `md` 768px → `heading-64`/`heading-48`) med vekt 500 (ikke heading-tokenenes 600 — landingssidens store flater bar 500 fra start, og klassene finnes for å bevare den looken i skalaen) — erstatter en tidligere fri `clamp()`-skalering som ikke fantes i skalaen. `display-1` er hero-H1-en, `display-2` er seksjons-H2-ene.
- Overline, `.type-overline`, er en liten uppercase eyebrow-etikett i monospace-familien med bred bokstavavstand — over en overskrift eller som seksjonsetikett, ALDRI på utskriftsdokumentenes egne etiketter, som bevisst holder seg til sitt eget sett.

`copy-14` og `label-14` dekker det meste av teksten. `-mono`-tokenene parer den monospace familien med samme metrikk; foretrekk tabulære tall når kolonner skal flukte. Sett aldri `font-size`/`line-height`/`font-weight` for hånd — bruk `.type-*`-klassen eller `<Text variant="…">`.

## Layout

Avstand følger en 4px-skala: 4, 8, 12, 16, 24, 32, 40, 64, 96px. Hold en tre-stegs rytme: 8px inne i en gruppe, 16px mellom grupper, 32–40px mellom seksjoner. Kort bruker 24px padding, 16px kompakt og 32px for hero-flater. Ett bevisst avvik: `StatCard` (KPI-kortet) bruker 20px — en kompakt mellomting, tettere enn et vanlig 24px-kort fordi et dashbord/økonomi-rutenett viser flere av dem side om side, men luftigere enn 16px-kompakt siden tallet i seg selv skal puste. `PageShell` eier sidemarg og maks-bredde (`narrow`/`content`/`wide`/`full`), og hver skjerm skal virke på både mobil og desktop.

## Elevation & Depth

Hierarki kommer fra tonale flater og kanter først, så skyggene holdes tilbakeholdne — men systemet skiller bevisst mellom to typer flater. **Flatt med vilje**: kort, listerader, sidemeny — hvilende innhold man opererer PÅ, ikke noe som svever over resten. **Elevert**: dropdown/popover og modaler/store overlegg — flytende flater uten fast plass i layouten, som MÅ lese som hevet over det de dekker. `--shadow-xs`/`--shadow-sm` bærer den første gruppen og er identiske i mørkt og lyst tema. `--shadow-md`/`--shadow-lg`/`--shadow-xl` bærer den andre.

Mørkt tema bygger den andre gruppens dybde av glass + glød + lyse kanter mot svart, så skyggen der kan holde seg lett. Lyst tema har ikke den mekanismen tilgjengelig — der er skygge den ENESTE dybdekilden — så disse tre er forskjellige per tema (founder-rettet 2026-08-20, samme dag som `--color-bg`/`--color-surface` byttet retning, se Colors-seksjonen):

**Mørkt tema** (`:root`):
- Hevede kort (`--shadow-sm`): `0 1px 2px rgba(0,0,0,0.16)`
- Popovers og menyer (`--shadow-md`): `0 1px 1px rgba(0,0,0,0.02), 0 4px 8px -4px rgba(0,0,0,0.04), 0 16px 24px -8px rgba(0,0,0,0.06)`
- Modaler og dialoger (`--shadow-lg`/`--shadow-xl`, identiske): `0 1px 1px rgba(0,0,0,0.02), 0 8px 16px -4px rgba(0,0,0,0.04), 0 24px 32px -8px rgba(0,0,0,0.06)`

**Lyst tema** (`:root[data-theme='light']`):
- Hevede kort (`--shadow-sm`): `0 2px 2px rgba(0,0,0,0.04)` — uendret, fortsatt tilbakeholden
- Popovers og menyer (`--shadow-md`): `0 1px 2px rgba(0,0,0,0.08), 0 6px 12px -4px rgba(0,0,0,0.10), 0 20px 32px -12px rgba(0,0,0,0.14)` — løftet mest, ingen scrim hjelper til med separasjonen
- Modaler og dialoger (`--shadow-lg`/`--shadow-xl`, identiske): `0 1px 2px rgba(0,0,0,0.06), 0 6px 12px -4px rgba(0,0,0,0.08), 0 20px 32px -12px rgba(0,0,0,0.10)` — løftet mindre enn md, siden modalens 40 %-scrim allerede gjør mesteparten av separasjonsjobben

Tooltips tar den letteste (`--shadow-xs`, identisk med `--shadow-sm`). Par hver elevasjon med matchende radius. Landingssidens egne `--lp-*`-tokener (neste seksjon) er en EGEN, sterkere skala for de store luftige hero-/kort-flatene der — ikke gjenbruk `--shadow-*` for dem, og ikke gjenbruk `--lp-*` inne i appens tette UI.

## Landing surface (`--lp-*`)

Et eget, landingsside-scopet sett. Grunnen til at det finnes: forsiden og appen har motsatte behov. Appen er tett arbeids-UI der dybde er støy; landingssiden er store luftige flater der de samme hårfine kantene leser som flatt papir. Å strekke `--shadow-*` for å dekke begge ville gjort appen tyngre for å redde forsiden.

**Ikke bruk noe herfra inne i appen.** Founder-linjen «Nei, bare landingssiden» (2026-08-20, om aksentknapper) gjelder hele settet.

```yaml
landing:
  # Kort og paneler på selve siden
  lp-card-bg:      { dark: gray-alpha-100, light: surface }
  lp-card-border:  { dark: gray-alpha-100, light: gray-alpha-300 }
  lp-panel-bg:     { dark: transparent,    light: surface }
  lp-panel-border: { dark: transparent,    light: gray-alpha-300 }
  lp-elev-card:    { dark: none, light: "0 1px 2px rgba(0,0,0,.04), 0 6px 16px -8px rgba(0,0,0,.10)" }
  lp-elev-panel:   { dark: none, light: "0 2px 4px rgba(0,0,0,.04), 0 16px 32px -16px rgba(0,0,0,.12)" }
  lp-elev-hero:    { dark: "0 28px 80px -32px rgba(0,0,0,.92)", light: "tre lag, sterkest i settet" }
  lp-bg-wash:      { dark: none, light: "radial aksent-tint 6% øverst på siden" }

  # Kortflaten INNE I demo-vinduene (HeroDashboard, de 7 mockupene, bevis-utdragene)
  lp-mock-bg:      { dark: "linear-gradient(180deg, gray-alpha-100, transparent)", light: surface }
  lp-mock-elev:    { dark: none, light: lp-elev-card }

  # Organiske dekor-former bak demo-vinduene (OrganiskBakgrunn.tsx)
  lp-blob-bg:      { dark: "radial closest-side, accent 42%", light: "radial closest-side, accent 30%" }
  lp-blob-opacity: { dark: 0.22, light: 0.5 }
  lp-blob-blur:    { dark: 90px, light: 80px }
```

`--lp-mock-*` finnes fordi mønsteret var håndrullet IDENTISK i 12 filer. I mørkt tema er `gray-alpha-100` hvit 5 % over svart og løfter riktig; i lyst tema er samme formel svart 5 % over `#f2f2f2`, altså mørkere enn lerretet — demo-kortene ble grå-på-grå (målt: alt innhold komposittet til `rgb(242,242,242)`, bare sidebaren var hvit). Lyst tema må SPEILE retningen, ikke arve den. Det er den samme feilen som rammet `--color-bg`/`--color-surface` i appen, og den vil ramme neste token også hvis man skriver lys-verdien ved å kopiere mørk-verdien.

`--lp-blob-*` er rene dekorasjoner (`aria-hidden`, `pointer-events: none`, bak innholdet). De finnes for å ta bort kulden fra en side som ellers bare er rette kort med hårfine kanter. Hold dem alltid under innholdet i z-rekkefølge og alltid uskarpe nok til at kanten ikke leses som en flate.

## Motion

Bruk bevegelse kun når den klargjør en endring, aldri til dekorasjon. De fleste interaksjoner skal føles umiddelbare: `0ms` er ofte det kvikkeste og beste valget. Når bevegelse genuint hjelper — å avdekke eller flytte et element — hold den kort og fysisk med easingen `cubic-bezier(0.175, 0.885, 0.32, 1.1)` (`--ease-spring`): rundt 150ms for tilstandsendringer (`--duration-state`), 200ms for popovers og tooltips (`--duration-popover`), og 300ms for overlegg og modaler (`--duration-modal`). Unngå lange, loopende eller oppmerksomhetskrevende animasjoner, og respekter `prefers-reduced-motion`.

## Shapes

`--radius-sm` 6px for hverdagsflater og kontroller (knapper, felt, brikker), `--radius-md` 12px for kort, popovers og menyer, `--radius-lg` 16px for modaler og fullskjerm-paneler (2026-09-10: tilbake til disse verdiene etter en kort ukes utvidelse til 8/14/20px). Reserver 9999px (`--radius-full`) for piller, avatarer og runde kontroller. Hold én radiusfamilie per flate i stedet for å blande runde og skarpe hjørner.

## Components

Alle delte komponenter ligger i `components/ui/` (kjernen) og `components/` (bredere mønstre), og bruker `.type-*`-klassene + `var(--color-*)`-tokenene — aldri hardkodede verdier. Hver har sin egen side med props, eksempler og tilhørende `regel/*`-er på dokumentasjonssiden:

<!-- komponentkatalog:start — GENERERT av scripts/build-skill.mjs fra app/design/components/demos/*.tsx; ikke rediger for hånd -->
- `ActionBar` — Sidens primærhandling i en rad som kleber til bunnen av rulleområdet under 640px, og står i normal flyt fra 640px — for lange sider der primærknappen ellers havner under bretten på mobil. <https://design.frivio.no/components/action-bar>
- `AddFormPanel` — Den kanoniske «legg til»-seksjonen: seksjonshode, add-trigger, liste og tomtilstand i én tilstandsmaskin som hindrer dobbel-trigger. <https://design.frivio.no/components/add-form-panel>
- `Autocomplete` — Chromet for «nedtrekk med søk»: felt + resultatliste med full piltastnavigasjon og en ekte WAI-ARIA-combobox. <https://design.frivio.no/components/autocomplete>
- `Avatar` — Rund identitetsflate — bilde eller initial-fallback. AvatarGroup stabler flere med overlapp og en «+N». <https://design.frivio.no/components/avatar>
- `Badge` — Liten pille (radius full) for ETT kort tilstandsord — prioritet, status eller en nøytral kategori. <https://design.frivio.no/components/badge>
- `Begrep` — Et fagord med forklaringen der ordet står — prikket understrek, forklaring på hover/fokus (tooltip) og trykk (panel), samme tekst begge veier. <https://design.frivio.no/components/begrep>
- `BrregSearch` — Leverandør- eller organisasjonsoppslag mot Brønnøysundregistrene: skriv et navn, velg treffet, og posten opprettes/gjenbrukes med org.nr., adresse og bransje. <https://design.frivio.no/components/brreg-search>
- `Button` — Standardknappen: sju varianter, fire størrelser, fire former, samt lenke-modus som ser ut som en knapp. <https://design.frivio.no/components/button>
- `Callout` — Visuelt skille for «dette hører til her, men er ikke selve steget/raden» — et strukturelt skille eller en tips-/varselboks, med valgfri label, ikon og én CTA. <https://design.frivio.no/components/callout>
- `Card` — Standard beholder for innhold: surface-flate, kant via box-shadow, 12px radius, ingen dropskygge i hvile. <https://design.frivio.no/components/card>
- `CenteredPage` — Skallet for beboer-/leverandør-/feilflater UTENFOR innlogget app — full høyde, sentrert kolonne, valgfri logo øverst. <https://design.frivio.no/components/centered-page>
- `Checkbox` — Et valg som er del av et skjema og bekreftes/lagres samlet — «button role="checkbox"», aldri et rått input-element med type="checkbox". <https://design.frivio.no/components/checkbox>
- `CollapsibleSection` — Skjuler en gruppe rader bak en klikkbar overskrift. Åpen/lukket-tilstand huskes per bruker via localStorage. <https://design.frivio.no/components/collapsible-section>
- `Confetti` — Feiring av at noe FULLFØRES — et konfettiteppe fra toppen, i Frivios egne tokenfarger. Ingen ekstern avhengighet. <https://design.frivio.no/components/confetti>
- `ConfirmDialog` — Erstatter native window.confirm() med en dialog i designsystemet. Kalles via useConfirm() inne i en ConfirmProvider. Bygget på Modal + ModalActions. <https://design.frivio.no/components/confirm-dialog>
- `DescriptionList` — Delt «etikett over verdi»-rutenett for et sett med korte faktafelter på en flate. <https://design.frivio.no/components/description-list>
- `Dropdown` — Én delt dropdown for «velg ett element fra en liste, se hva som er aktivt» — knapp + popup-liste bygget på Popover. <https://design.frivio.no/components/dropdown>
- `Dropzone` — En sone for når filen ER hovedhandlingen på flaten — kontoutskrift, tilstandsrapport, vedtekter. <https://design.frivio.no/components/dropzone>
- `EmptyState` — Felles tomtilstand: stiplet kort med nøytral ikonsirkel, tittel, kort forklaring og valgfri handling. <https://design.frivio.no/components/empty-state>
- `FeatureIntro` — «Psst, ny ting»-kort for en ny brukervendt evne — plasseres INLINE ved siden av funksjonen, vises én gang per nettleser. <https://design.frivio.no/components/feature-intro>
- `Field` — Metadata inne i en eksisterende flate: en liten tertiær etikett over en verdi, kort-løs. <https://design.frivio.no/components/field>
- `FormError` — Delt feilmelding for skjema/handlinger, utenfor et felt. role="alert" annonserer den for skjermleser straks den dukker opp. <https://design.frivio.no/components/form-error>
- `IconButton` — Ikon-only knapp med GARANTERT 44×44px trefflate (WCAG 2.5.5 / Apple HIG), uansett hvor lite ikonet inni er. <https://design.frivio.no/components/icon-button>
- `IconTile` — Avrundet flate med tonet bakgrunn og farget ikon — bakgrunn og ikonfarge settes SAMMEN av tonen, aldri hver for seg. <https://design.frivio.no/components/icon-tile>
- `InlineNote` — Farget venstrekant + ikon + fritekst, for en kommentar knyttet til ETT tall/resultat over. <https://design.frivio.no/components/inline-note>
- `Input` — Standard tekstfelt (Input) og flerlinjefelt (Textarea) — samme API, samme utseende og feilhåndtering. <https://design.frivio.no/components/input>
- `Kbd` — Visuell representasjon av én tastetast — mono, kant, min-bredde 20px. <https://design.frivio.no/components/kbd>
- `ListRow` — Den ene flate listeraden — brukes i en flat, delelinje-delt beholder, aldri ett kort per element. <https://design.frivio.no/components/list-row>
- `LoadBar` — Vannrett søyle som viser MENGDE i forhold til de andre radene i et sett. <https://design.frivio.no/components/load-bar>
- `Loading` — Delt lastetilstand i tre varianter — inline, blokk og panel — bygget på den delte Spinner-komponenten. <https://design.frivio.no/components/loading>
- `Markdown` — Liten, avhengighetsfri markdown-renderer for dokumentflater — bygger React-elementer direkte, ingen dangerouslySetInnerHTML. <https://design.frivio.no/components/markdown>
- `Modal` — Dialogflaten for alt du GJØR — skjema, valg, lagring, sletting. Fast bredde (480/640px) og sentrert fra 640px (sm); på mobil er modalen et bunnark i bunnen av skjermen. <https://design.frivio.no/components/modal>
- `OtpInput` — Engangskodefeltet fra innloggings- og registreringsflytene — hopper til store, sperrede sifre idet feltet får innhold. <https://design.frivio.no/components/otp-input>
- `OverflowMenu` — «…»-knappen: sekundære HANDLINGER samlet bak én trigger, bygget på Popover. <https://design.frivio.no/components/overflow-menu>
- `PageShell & PageHeader` — Sidebeholderen (marg, toppluft, maks-bredde) og sidetoppen (tittel, «i»-popover, handling) brukt på så godt som hver dashbord-side. <https://design.frivio.no/components/page-shell>
- `Pagination` — Sidenavigasjon for en delt liste — «Forrige»/«Neste» og sidetall, ellipse ved mange sider. <https://design.frivio.no/components/pagination>
- `PillTabs` — Kompakt segmentkontroll for filter/utvalg i én liste — aktiv fane vises som en invertert pille i en felles bane. <https://design.frivio.no/components/pill-tabs>
- `Popover` — Delt chrome for et ANKRET panel mot en trigger — posisjon, elevasjon, Escape, klikk-utenfor, fokusretur og mobil-ark. Materiale «meny»: bg surface, shadow-menu, min-bredde 220px. <https://design.frivio.no/components/popover>
- `Progress` — Vannrett linje som viser ANDEL av én helhet fullført (0–100) — fylt flate (default aksent) på et gray-alpha-200-spor. <https://design.frivio.no/components/progress>
- `Radio` — Radio (enkeltvalg) og RadioGroup (rovende tastaturnavigasjon) — for en LENGRE liste der hvert alternativ trenger en egen beskrivelse. Ikke for 2–4 valg (regel/velg-en-av-fa-ingen-radio). <https://design.frivio.no/components/radio>
- `SaveIndicator` — Inline kvittering ved siden av et felt som lagrer på blur/endring, uten en egen Lagre-knapp. <https://design.frivio.no/components/save-indicator>
- `SearchInput` — Ett søkefelt for hele systemet: lupe til venstre, samme høyde/kant/fokus som Input. <https://design.frivio.no/components/search-input>
- `SectionHeader` — Overskrift for en SEKSJON inne i en side — tittel, én kort forklaringssetning og en valgfri handling til høyre. <https://design.frivio.no/components/section-header>
- `Select` — Felles nedtrekksvelger med samme høyde, kant og fokusoppførsel som Input — beholder semantikken til et nativt nedtrekksfelt. <https://design.frivio.no/components/select>
- `Separator` — 1px skillelinje — horisontal eller vertikal, med en valgfri sentrert etikett. <https://design.frivio.no/components/separator>
- `Shortcuts` — Tastatursnarveier i «g, så en bokstav»-mønsteret (som GitHub/Linear) — to tastetrykk, ingen modifikatorer. <https://design.frivio.no/components/shortcuts>
- `Skeleton` — Plassholder for innhold under lasting — gray-alpha-200 med skimmer, radius-sm, `aria-hidden`. <https://design.frivio.no/components/skeleton>
- `Spinner` — Liten, roterende venteindikator — standard 20×20px, text-secondary. <https://design.frivio.no/components/spinner>
- `StatCard` — Felles KPI-kort — samme materiale som Card (bg surface, kant via box-shadow, radius-md), med tall/etikett/ikon-oppsett. <https://design.frivio.no/components/stat-card>
- `StatusDot` — Liten fargeprikk (8px) for en tilstand inline i tekst eller en rad — «på nett», «synkronisert», «venter». <https://design.frivio.no/components/status-dot>
- `StepCard` — «Ikonfirkant/nummerskive + heading-14 + copy-13» for ett steg i en kort forklarende sekvens — kortet BÆRER sin egen kant, ingen ytre Card trengs. <https://design.frivio.no/components/step-card>
- `StepIndicator` — Viser FREMDRIFT gjennom et fast antall steg — ikke et navigasjonselement, prikkene/pillene er ikke klikkbare. <https://design.frivio.no/components/step-indicator>
- `Switch` — AV/PÅ-bryter for en innstilling som trer i kraft med én gang valget er lagret. <https://design.frivio.no/components/switch>
- `Table` — Data-drevet grid for kolonner av tall som sammenlignes NEDOVER — månedsoversikt, historikk, en flerårsprognose. <https://design.frivio.no/components/table>
- `Tabs` — Understrek-faner for å bytte mellom søskenvisninger som deler scope og datamodell — erstatter AreaTabs (founder 2026-09-10: «Tabs ser mer ut som knapper»). <https://design.frivio.no/components/tabs>
- `Text & Heading` — Én kilde til all tekststil — bruker .type-*-klassene i globals.css i stedet for løse Tailwind-typografiklasser. <https://design.frivio.no/components/text>
- `Toast` — «Angre etter», ikke bare «bekreft før» — invertert kort, `useToast().vis({ tekst, tone?, handling?, varighet?, preserve?, vedLukking? })` inne i en `ToastProvider`. <https://design.frivio.no/components/toast>
- `Tooltip` — Kort, invertert hjelpetekst ved hover/fokus på ETT element — bg text-primary, tekst bg, label-13. <https://design.frivio.no/components/tooltip>
- `YearSelector` — To IconButton + et årstall — bla år for år gjennom historikk. <https://design.frivio.no/components/year-selector>
<!-- komponentkatalog:end -->

## Voice & Content

Kopi er en del av designet — presis og uten fyll. All UI-tekst er på norsk (bokmål).

- Navngi handlinger med verb + substantiv (`Lag vedlikeholdsplan`, `Slett medlem`), aldri `Bekreft`, `OK` eller et bart verb.
- Skriv feil som hva som skjedde pluss hva man gjør nå: `Analysen feilet. Filen er over 25 MB. Komprimer den eller last opp en mindre.`
- Bekreftelser navngir tingen som endret seg, dropper avsluttende punktum og sier aldri «vellykket»: `Bygg slettet`, ikke `Bygget ble slettet.`
- Tomtilstander peker på første steg: `Ingen bygg ennå. Legg til det første for å lage en plan.`
- Bruk presens partisipp med ellipse for pågår-tilstander: `Analyserer…`, `Lagrer…`.
- Bruk tall (`3 bygg`), typografiske anførselstegn og ellipse-tegnet; dropp «vennligst» og markedsføringssuperlativer.
- Skriv for styremedlemmer uten teknisk bakgrunn — rolig, hjelpsomt, forklarende. Null opplæring skal kreves.

## Do's and Don'ts

- Bruk gråskalaen til å rangere informasjon: `1000` for primær tekst, `900` for sekundær, `700` for deaktivert.
- Hold solid aksentfarge til tilstand og den ene viktigste handlingen på en flate — Frivio er monokromt med én blå aksent.
- Bruk gråtone-gradientene (`--gradient-card`/`-hero`/`-glow`) på flater som viser — hero-KPI, standard-kort, tomtilstands-heroer, dashbord-toppglød. Aldri på flater man opererer i: skjema/input, modaler, popovers/menyer, listerader, tabeller, notis-bannere, knapper, sidemeny. Gradient viser, flatt opererer.
- Hold WCAG AA-kontrast (4.5:1 for brødtekst).
- Vis fokusringen på hvert interaktivt element ved `:focus-visible`, og fjern aldri en outline uten synlig erstatning.
- Bruk typografi-tokenene (`.type-*` / `<Text>`) i stedet for å sette fontstørrelse, linjehøyde eller vekt for hånd.
- Ikke signaliser tilstand med farge alene; par den med et ikon eller en tekstetikett.
- Ikke bland runde og skarpe hjørner, eller mer enn to fontvekter, på samme flate.
- Ikke bytt `gray-*` mot `surface`/`bg`; det er separate skalaer.
- Ikke skjul knappetekst bak et brekkpunkt (`hidden sm:inline` e.l.) — en knapp med tekstinnhold beholder den på ALLE bredder; korte ordet i stedet. Ikon-only er KUN `Button shape="square"|"circle"`, som krever `aria-label`.
- Nøkkeltall stables aldri i én kolonne på mobil — minst to kolonner (`StatCardRad`).
