diff --git a/docs/ux/README.md b/docs/ux/README.md index 754f278..cde0fe2 100644 --- a/docs/ux/README.md +++ b/docs/ux/README.md @@ -1,50 +1,22 @@ -# Kvitt UX (sprint U1) +# Kvitt UX -The UX deliverables for Kvitt v1 (SPEC §13), produced 2026-10-17. Phase 1 (U1) is here: three visual directions plus the direction-independent design documents. After the owner picks a direction, phase 2 (U2) consolidates `tokens.md`, `components.md` and `screens.md` into `design-spec.md` for the chosen direction (SPEC §13.4) and deletes what it supersedes. +The UX deliverables for Kvitt v1 (SPEC §13). Phase 1 (U1, 2026-10-17) proposed three visual directions; the owner picked **3 "Kalas", muted** on 2026-10-18. Phase 2 (U2, SPEC §13.4) is the binding design for M6 (`:core:design`) and M7a–M7f (the feature screens). -## How to read +## Binding files (U2) -1. **Look first:** open `directions/1-skargard/index.html`, `directions/2-kvitto/index.html` and `directions/3-kalas/index.html` in a browser (or the PNGs in each `png/` folder). Every screen S-1 to S-22 is there in light and dark, in Swedish, with the same household data, so the same file name compares one screen across directions. -2. **Compare and decide:** `directions/README.md` has the comparison table and the recommendation. -3. **Then the structure,** which holds whichever direction wins: +| File | What it is | Used by | +|---|---|---| +| `design-spec.md` | The design spec: tokens (colour roles light and dark, 14 category colours with containers and contrast, chart and avatar colours, type scale, spacing, shapes, elevation, motion, icons, the 14 default categories, Compose mapping), the component inventory with anatomy, sizes, states and semantics, every screen S-1 to S-22 and the notifications with layout, the binding list of states, interactions, TalkBack and 200 %, the add-expense flow in detail, open points | M6, M7a–M7f, screenshot tests | +| `strings.csv` | Every user-visible string, `key,en,sv,notes`, keys prefixed per D-017 (feature modules) or unprefixed (`:core:design`); placeholders `%1$s`, plurals as `:one` / `:other` | M6, M7 (`values/strings.xml`, `values-sv/strings.xml`) | +| `screens/` | The screen catalogue: `screens/index.html` and one PNG per frame and theme, `screens/-light.png` / `-dark.png` (200 % and English frames light only), Swedish. Every state that `design-spec.md` §4 lists has a frame | M7 (look), the owner | +| `ia.md` | Bottom navigation, screen inventory with modules and state sources, session routing, navigation map, data ownership, admin vs member | M7, M8 | +| `flows.md` | F-1 … F-11 step by step | M7, M8 | +| `category-suggestion.md` | The category suggestion rule CS-1 … CS-7 and the evidence from the household's Splitwise export | M5/M7c, the spec change UX-3 | +| `category-keywords.csv` | The built-in keyword table of CS-6 (draft of `contracts/category-keywords.csv`) | `:domain`, UX-3 | +| `proposed-decisions.md` | The owner's answers and decisions UX-1 … UX-8, with the spec text a docs agent records on `main` (numbers assigned then) | orchestrator, docs agent | -| File | What it is | -|---|---| -| `ia.md` | Bottom navigation (three destinations, argued), screen inventory S-1…S-22 with modules and state sources, session routing, navigation map, which screen owns which data, admin vs member | -| `screens.md` | Every screen: purpose, content in priority order, empty/loading/offline/error/long-content states, actions; the add-expense flow in detail | -| `flows.md` | F-1 … F-11: first run as admin, join as partner, add expense online and offline, edit the other's expense, global and per-account settle-up, recurring, archive, in-app update, remove a lost phone | -| `components.md` | The `:core:design` component list: anatomy, variants, states, semantics | -| `tokens.md` | Token names and their values in all three directions (colour roles light/dark, semantic and category colours with contrast, charts, type, spacing, shapes, elevation, motion, icons) | -| `strings.csv` | Every UI string, `key,en,sv,notes` (SPEC §13.4 format). Placeholders are Android format arguments (`%1$s`, `%d`); plurals are two rows `:one` and `:other`, which become one ``. Notification copy (SPEC §13.3, Swedish written here) has the prefix `notif_`. | -| `directions/` | The three directions, their PNGs and `export-png.sh` | -| `proposed-decisions.md` | The owner's answers (direction pick: Kalas, muted), findings, and the decisions and spec text a docs agent should record on `main` | +Precedence: SPEC rules › `design-spec.md` › PNGs; words from `strings.csv`. `design-spec.md` §1 lists where the design depends on a decision that is not in SPEC.md yet (UX-n). -## The three directions +## U1 record (not maintained) -**1 Skärgård** — the Stockholm archipelago on a calm day. Material 3 Expressive in sea blue, granite and sand, with large rounded shapes and generous whitespace. Home is a hero: "Du är skyldig Anna" above a large, narrow Roboto Flex figure and a single "Gör upp" button; accounts are a calm grouped list. The editor puts the amount in display size over an in-app keypad with Expressive key shapes. The most Android-native of the three, and the only one that can follow Material You wallpaper colours. - -**2 Kvitto** — a till receipt and a household ledger. Warm paper and ink, one ink-blue accent, every amount in IBM Plex Mono, dashed rules between groups. Home opens with a compact receipt block and then a dense ledger table; account detail reads like a bank statement; an expense is a printed receipt with an "audit trail" below; "Ni är kvitt" is a rubber stamp. The densest and calmest direction; its risk is feeling austere. - -**3 Kalas** — a crayfish party. Cream and white cards, a coral primary, very round shapes, and a solid category-colour tile on every expense so you recognise it before reading it. The balance is a seesaw (whoever owes sits lower, and the words always say it); settling up raises bunting. The editor shows the six most used categories as big tiles under the amount. The most personality; its risk is wearing thin over years of daily use. - -## Where this follows the spec instead of the U1 brief - -The owner's brief for this sprint and SPEC.md differ in places; the spec wins (CLAUDE.md › Source of truth): - -| Brief | Spec (followed) | -|---|---| -| Screen ids `S-01 …` | `S-1 … S-22` as in SPEC §13.3 | -| Bottom nav: recommend up to 4 | SPEC §13.2's three destinations kept, with the argument in `ia.md` §1 | -| `docs/ux/proposals/`, 360 dp frames, S26+ at 384 dp | `docs/ux/directions/-/index.html` at 411 dp (SPEC §13.1/§13.4); both phones are ~411 dp wide, so there is no separate S26+ layout | -| Swedish in one direction, English in the others | Swedish everywhere (§13.4), plus one English Home frame per direction to show English fits | -| `copy.md` table | `strings.csv` in the §13.4 format | -| Category suggested from the description | AC-8.6: last category used in that account on this phone, else Övrigt (a description-based suggestion would be a new rule; open question) | -| `tokens.md` "for the chosen scheme family" | Values for all three directions until the owner picks (§13.4 makes the pick precede the final spec) | - -Additions the spec did not state, flagged for U2: - -* **S-1 has a "Ditt namn" field:** AC-2.4 sends a display name with the accept call, but §13.3's S-1 lists no name field. A partner therefore types the name on S-1 and confirms it on S-2 (AC-2.10). -* **Category colour hex values** changed for contrast (`tokens.md` §4); adopting them needs a §9.7 spec change after the pick. -* **Avatar colours** for initials (AC-4.5) are defined per direction (`avatar-1`, `avatar-2`). -* **Expense save shows no "Ångra"** (an undo would be an audited create + delete that also notifies the partner). -* **Archived account detail hides the FAB** (an archived account is not offered for new expenses, AC-6.4). +`directions/` holds the three U1 directions as they were shown to the owner: `1-skargard/`, `2-kvitto/`, `3-kalas/` (each an `index.html` review page with every frame), `README.md` (comparison and recommendation) and `export-png.sh`. Token values in `design-spec.md` were copied from `3-kalas/index.html`; later changes are made in `design-spec.md` and `screens/` only. U1's direction-independent `screens.md`, `components.md` and `tokens.md` were folded into `design-spec.md` and removed. diff --git a/docs/ux/components.md b/docs/ux/components.md deleted file mode 100644 index 205cf2a..0000000 --- a/docs/ux/components.md +++ /dev/null @@ -1,182 +0,0 @@ -# Kvitt: components for `:core:design` - -Status: U1 proposal (2026-10-17), direction-independent anatomy, states and API shape. The look (colours, shapes, type) comes from the chosen direction's tokens (`tokens.md`). In U2 this list moves into `design-spec.md` with final sizes. Names are Compose function names; every component takes a `Modifier` and gets screenshot tests in light and dark, Swedish and English, at 100 % and 200 % font scale (M6). - -Rules for all components: - -* Colours and type only from `KvittTheme` (Material 3 `ColorScheme` + `KvittColors` extension + `KvittTypography`); no literal values. -* Strings only from resources (`strings.csv` keys); components that need words take them as resolved `String` parameters or take the model value and resolve inside `:core:design` (shared strings live there, §14.7). -* Touch targets ≥ 48 dp (`minimumInteractiveComponentSize`), TalkBack `contentDescription` / `semantics` as stated per component. -* Material 3 Expressive APIs (`@ExperimentalMaterial3ExpressiveApi`) are used only behind a `:core:design` wrapper so the stable fallback named below can be swapped in one place. The implementing agent confirms each API's status from the official documentation at implementation time (CLAUDE.md › Fast-moving APIs). - -## 1. Money and text - -### `MoneyText` -Formats and shows an amount in öre via `MoneyFormatter` (§9.8). Never shows a sign. -* Params: `ore: Long`, `style: AmountStyle` (`Hero`, `Large`, `Medium`, `Small`), `tone: AmountTone` (`Neutral`, `Owed`, `Owing`, `Muted`), `maxLines` (default 1; never ellipsizes: overflow wraps to 2 lines or auto-steps down one type size for `Hero`/`Large`). -* Tabular figures always (`fontFeatureSettings = "tnum"`). -* Semantics: spoken form from `MoneyFormatter.spoken(ore)`: Swedish "3 250,00 kronor", English "3,250.00 Swedish kronor" (TalkBack reads "kr" and "SEK" poorly). - -### `AmountInput` -Large amount entry for S-7, S-9, S-19. Parses per §9.6 (string → öre, never floating point). -* Params: `value: String`, `onValueChange`, `error: String?`, `size` (`Display`, `Field`). -* States: empty (hint "0,00"), typing, error (supporting text, error colour), disabled. -* Accepts `,` and `.`; displays the locale's separator; inserts no-break-space thousands grouping while typing. - -### `AmountKeypad` (only if the chosen direction uses an in-app keypad) -3 × 4 grid: 1–9, decimal separator (locale), 0, backspace (TalkBack "Radera"); keys ≥ 64 dp tall; optional "Klar". Fallback: system keyboard `KeyboardType.Decimal`. - -### `EffectLine` -The one-line effect from the viewer's side (§9.2) for an `ExpenseEffect`. -* Variants: `Lent(ore)` "Du lånade ut X" (owed tone) · `Borrowed(ore)` "Du lånade X" (owing tone) · `NotInvolved` "Inte inblandad" · `PaidForYourself` "Betalade för dig själv" (neutral). - -### `BalanceWording` -Wording + amount for a `BalanceView` (§9.3). -* Variants: `Global` ("Anna är skyldig dig", "Du är skyldig Anna", "Ni är kvitt") and `Account` (… "Kvitt"). -* Semantics: merged into one sentence. - -## 2. People - -### `PersonAvatar` -* Params: `user: User`, `size` (24, 32, 40, 56, 96 dp), `shape` (direction token; fallback `CircleShape`). -* Picture via Coil keyed by `user.id` + `pictureVersion` (AC-4.6); else initials (first letter of the first two words, uppercase, AC-4.5) on `avatar-1`/`avatar-2` chosen by a stable hash of the user id (the two colours are tokens). -* States: loading (initials shown), picture, initials, uploading (progress ring overlay). -* Semantics: the display name (or decorative when the name is next to it). - -### `PersonChip` -Avatar 24 dp + name ("Du" for the viewer). Variants: `Display`, `Selectable` (S-7 payer toggle, S-7 full-split bearer). Selected state per M3 FilterChip. - -### `PayerToggle` -Two `PersonChip`s in a connected group; one tap switches. Label "Betalt av". Semantics: "Betalt av: du. Dubbeltryck för att byta till Anna." - -## 3. Categories and accounts - -### `CategoryIcon` -Material Symbols icon (token from §9.7) on a container in `cat--container` with the icon in `cat-`. Sizes 24/32/40/56 dp. Shape per direction (fallback circle or rounded square). Decorative when a name is shown next to it; else the category name. - -### `CategoryChip` -`CategoryIcon` (24) + name. Variants: `Display`, `Selectable`. Archived variant adds "(arkiverad)". - -### `CategoryGrid` -Picker grid in S-7 and S-19: tiles of icon + name, 4 columns at 411 dp (3 at 200 %), selected with outline/check. Only active categories. - -### `CategoryIconPicker`, `ColorSwatchPicker` -S-15 editor: 30 icons (6 columns), 12 colour swatches (6 columns), each 48 dp, selected with a check mark (not colour alone). Swatches have names for TalkBack ("Grön", "Orange", …). - -### `AccountRow` -Home and pickers: name, `BalanceWording.Account`, amount, secondary "11 utgifter i oktober". Variants: `Home`, `Picker` (no count), `Archived` (badge). 200 %: amount moves under the name. - -## 4. Ledger - -### `LedgerMonthHeader` -"Oktober · 7 457,90 kr" (household spend); sticky in `LazyColumn`. Year added when not the current year. - -### `ExpenseRow` -`CategoryIcon`, description (1 line), date, `MoneyText` amount, `EffectLine`, optional account name (S-12), `SyncPendingMarker`. Heights per direction (56–88 dp). States: normal, pending, pressed. Semantics: "ICA Maxi Lindhagen, 1 247,60 kronor, 16 oktober, du lånade 623,80 kronor, väntar på synk". - -### `SettlementRow` -`payments` icon, "Anna betalade dig 800,00 kr", "· 2 konton", date, pending marker. - -### `SyncPendingMarker` -Small icon (`cloud_upload` or direction equivalent) in `pending`; label "Väntar på synk". - -## 5. Balance and sync - -### `BalanceHero` -Home's global balance (S-4) and the account header (S-5, smaller variant). -* Anatomy: both `PersonAvatar`s, `BalanceWording.Global`, `MoneyText(Hero)`, optional action button "Gör upp" / "Gör upp det här kontot". -* States: `Owed` (partner owes you: owed colours, button), `Owing` (you owe: owing colours, button), `Settled` ("Ni är kvitt"/"Kvitt", no amount, no button), `Empty` (no accounts: welcome text), `Loading` (skeleton). -* 200 %: wording and amount each take a full line; avatars may shrink to 40 dp. -* Semantics: one merged sentence + the button. - -### `SyncStatusLine` -From `SyncSummary` + connectivity. -* Variants: `Synced` ("Synkad för 3 min sedan"), `Syncing` ("Synkar…"), `Offline` ("Offline"), with suffix "· 2 ändringar väntar på synk" (plural); `Warning` (rejections or clock skew) with icon and warning colour. -* Tap → S-16 (whole line is the 48 dp target). Relative time updates every minute. - -### `UpdateBanner` -S-17 states: `Available` (text + "Uppdatera" + "Senare"), `Downloading` (determinate `LinearProgressIndicator` + percent), `Failed` (text + "Försök igen"). Fallback for Expressive wavy progress: standard linear. - -### `NeedsConnectionHint` -Inline supporting text / banner "Kräver anslutning" with `cloud_off` icon; used on every online-only control (§9.9). Variant `Banner` for whole forms (S-6, S-14, S-15, S-19). - -## 6. Split and settle - -### `SplitModeSelector` -Single-select connected buttons (Expressive `ButtonGroup`/connected toggle buttons; fallback `SingleChoiceSegmentedButtonRow`): "Kontots standard 60/40", "Egen fördelning", "Exakta belopp", "Allt på en person". Wraps to 2 rows (Swedish), 4 rows at 200 % (a vertical radio list fallback when the row cannot fit). - -### `RatioEditor` -Slider (0–100, step 1, haptic at 50) + two percent `TextField`s summing to 100 (two decimals) + preset chips (50/50, 60/40, 40/60). Used in S-6 and S-7 `ratio`. Semantics: slider "Jacobs andel, 60 procent". - -### `ExactSplitEditor` -Two money fields: the typed one editable, the other read-only "beloppet minus …"; error "Andelen är större än beloppet". - -### `FullSplitPicker` -Two `PersonChip`s: "Allt på Jacob" / "Allt på Anna". - -### `SplitPreview` -"Du 206,00 kr · Anna 206,00 kr" + the effect the row will show. Data from `ExpenseRepository.preview`; hidden when null. Optional bar showing the two shares as proportions (decorative). - -### `SplitPicker` (composite) -The S-7 split section: `SplitModeSelector` + the mode's editor + `SplitPreview`; collapsed state is a single chip "Delning: Kontots standard 50/50" which expands it in one tap. - -### `DirectionSwitcher` -S-9 "Du betalar Anna" with avatars and an arrow; swap button `swap_horiz` (TalkBack "Byt riktning"). - -### `AllocationEditor` -S-9 list of `AllocationRow`s + "Lägg till konto" + `RemainingIndicator`. -* `AllocationRow`: account name, debt line ("Du är skyldig 3 084,50 kr"), `AmountInput(Field)`, remove (`close`, "Ta bort Huset från fördelningen"), warning line (`warning` icon + "Det här reglerar mer än som är skyldigt i Japan 2027") when `overSettles`. Variant `Fixed` (per-account settle: no remove). -* `RemainingIndicator`: "Kvar att fördela 0,00 kr" (neutral/positive), "250,00 kr kvar" (warning), "250,00 kr för mycket" (error), always words + colour. - -## 7. Timeline - -### `TimelineItem` -Anatomy: actor avatar (24 dp; system icon `autorenew` for recurring, `upload_file` for import), sentence, "· 16 okt. 19:40"; vertical connector between items. -* Variants: `Create`, `Update` (one line per `FieldChange`, sentence built from field + old → new, values formatted by `MoneyFormatter`/`DateFormatter`, ids already names), `Delete`, `PendingLocal` ("Din ändring väntar på synk", pending colour, dashed connector), `System` (recurring/import). -* Field names (sv/en) in `strings.csv` (`timeline_field_*`). - -### `Timeline` -Column of `TimelineItem`, oldest first, newest last (AC-12.4), with the section title "Historik". - -## 8. Charts (`:core:design`, Vico or Canvas fallback per §6.2) - -### `ChartCard` -Container: title, optional subtitle (range), the chart, legend, and an invisible-but-spoken summary sentence (`semantics { contentDescription = summary }`). States: loaded, empty ("Inga utgifter i den här perioden"), loading (shimmer; only when the computation takes > 300 ms). - -### `StackedBarChart` -Buckets on x (month "maj" or week "v. 41"), kronor on y (whole kronor, 3–5 gridlines, compact "20 tkr" labels in sv, "20k" in en), segments coloured by group (category colour or `chart-cat-n` for accounts, stable order = legend order), 2 dp gap between segments; selected bar highlighted, others dimmed; tap → callback(bucketKey). Each bar is a focusable semantics node ("September, 41 484 kronor"). Horizontal scroll when more than ~8 buckets; newest visible. - -### `ChartLegend` -Wrapping row of swatch + label; only groups with data; static (filtering by legend is not in the spec). - -### `BreakdownBarList` -Rows: icon/swatch, name, proportional bar, amount, percent (one decimal, AC-14.10). Sorted descending. Tap → callback(groupId). 200 %: bar on its own line. - -### `FilterBar` -S-11 filters: range chip with menu (`DropdownMenu` of `RangePreset`s), three binary connected toggles. Wraps to two rows. - -## 9. Scaffolding - -### `KvittScaffold` -Top app bar (small, 64 dp; back or close), content, optional FAB slot, snackbar host, edge-to-edge insets. - -### `KvittNavigationBar` -Three items (ia.md §1). Expressive `ShortNavigationBar` (fallback `NavigationBar`). Selected icon filled. Labels always visible. - -### `AddExpenseFab` -Extended FAB "Lägg till utgift" (icon `add`), collapses to icon-only on scroll; Expressive size variants (fallback `ExtendedFloatingActionButton`). - -### `EmptyState` -Icon, title, body, optional primary action. Used by S-3, S-4, S-5, S-11, S-16, S-19. - -### `ConfirmDialog` -Title, body, confirm (destructive variant in `error`), dismiss. - -### `SectionHeader`, `SettingsRow`, `CollapsibleSection` -Settings rows (icon, title, optional trailing value/badge, chevron), section headers ("Konton" + trailing action), "Arkiverade (n)" collapse with rotation of the chevron. - -### `InviteCodeDisplay`, `InviteCodeField` -Code in three groups of four, mono/tabular, large; selectable; TalkBack spells characters ("7 K 3 M, Q X 9 D, 2 H P A"). Field variant normalises as typed (§8.3). - -### `Skeleton` -Shimmer placeholders for the first-sync loading state only. diff --git a/docs/ux/design-spec.md b/docs/ux/design-spec.md new file mode 100644 index 0000000..4dc955f --- /dev/null +++ b/docs/ux/design-spec.md @@ -0,0 +1,1207 @@ +# Kvitt: design spec (U2) + +Status: binding design spec for v1, SPEC §13.4 phase 2, written 2026-10-18 for the owner's pick: direction 3 **"Kalas", muted** (UX-1), typeface per D-020. It replaces U1's `screens.md`, `components.md` and `tokens.md`. + +--- + +## 1. How to use this file + +**Who reads it.** M6 builds `:core:design` from §2 (tokens) and §3 (components). M7a–M7f build their screens from §4 (one subsection per screen) and §5 (the add-expense flow). Every screenshot test of a feature covers exactly the states listed under "States" for its screen (SPEC §13.3: this list is the binding one). + +**Sources of truth.** + +| Question | Source | Wins over | +|---|---|---| +| Rules (limits, rounding, sync, who may do what) | `SPEC.md` | everything here | +| Look, layout, sizes, states, interaction, TalkBack structure | this file | the PNGs | +| Exact words (sv, en), keys, plurals | `strings.csv` | the quoted words here | +| Pictures of each state | `screens/index.html` and `screens/-light.png` / `-dark.png` | nothing; a PNG that disagrees with this file is fixed | +| Category suggestion rule | `category-suggestion.md` (CS-1 … CS-7), keyword table `category-keywords.csv` | — | +| Navigation, screen inventory, data ownership | `ia.md`; flows `flows.md` | — | + +Swedish words in quotes are as they appear on screen; `strings.csv` holds the final wording and the English. Keys cited in parentheses (`editor_amount`) are the keys of the current `strings.csv` (D-017 module prefixes: `onboarding_`, `home_`, `account_`, `expense_`, `settle_`, `analytics_`, `settings_`; `:core:design` keys such as `common_`, `editor_`, `ledger_`, `balance_`, `update_`, `snackbar_` carry no feature prefix). Every label quoted here has a key in `strings.csv`. `directions/3-kalas/index.html` is the U1 record the token values were copied from; it is not maintained. + +**Decisions this file builds on.** UX-1 … UX-8 (`proposed-decisions.md`) are recorded in SPEC v0.8 as D-047 … D-054 (UX-1 D-047, UX-2 D-048, UX-3 D-049, UX-4 D-050, UX-5 D-051, UX-6 D-052, UX-7 D-053, UX-8 D-054). Still open: the §9.7 hex values of D-051 are `TODO(U2)` in SPEC v0.8 and are the values of §2.3 here; UX-9 is not yet in the spec. Labels used in this file: + +| Label | What this file assumes | Where it matters here | +|---|---|---| +| UX-1 | Direction Kalas, muted palette; Roboto | all of §2 and §3 | +| UX-3 | Category suggested from the description (CS-1 … CS-7), `ExpenseRepository.suggestCategory` | §3.7 `CategoryQuickRow`, §4 S-7, §5 | +| UX-4 | S-1 has a required "Ditt namn" field on a first pairing | §4 S-1 | +| UX-5 | §9.7 colour hex values replaced by the muted values of §2.3; two new tokens `slate` and `amber` (`ColorToken.SLATE`, `ColorToken.AMBER`) | §2.3, S-15 colour swatches | +| UX-6 | No "Ångra" after saving an expense; FAB hidden on an archived account; per-account settle-up keeps the direction swap and an editable amount; avatar colours are two tokens chosen by user id | §2.5, S-5, S-7, S-9 | +| UX-8 | 14 default categories (new `housing`, `insurance`, `children`, `fees`), 33 icon tokens (new `key`, `shield`, `account_balance`), `DefaultCategoryKey` with 14 values, `CategoryIcons.ALL` with 33 | §2.13, S-7, S-11, S-15; the D-018 empty fake scenario then has 14 defaults, not 10 | +| UX-9 | Contract additions: `ExpenseEditorOptions.frequentCategoryIds` and `CategoryRepository.deletable(id)`, with fallbacks until they exist | §3.7 `CategoryQuickRow`, S-15 editor, §6 | + +UX-2 (three tabs) changes nothing; UX-7 (import mapping) has no UI. + +**Conventions used throughout.** + +* Sizes in dp, text in sp. "Gutter" = 16 dp screen side padding. Touch targets are at least 48 × 48 dp everywhere (`minimumInteractiveComponentSize`); a control drawn smaller (36 dp chip, 40 dp button) keeps a 48 dp target. +* **"Kräver anslutning"** (`common_needs_connection`): an online-only action (§9.9) while offline is shown disabled **with** that text next to it (supporting line, menu-item second line or banner), never silently disabled. Online state comes from `ConnectivityRepository.online`. +* **"Kunde inte nå servern"** (`common_could_not_reach_server`): an online call returned `OnlineResult.Failed("network")`. Snackbar on the screen that made the call, with "Försök igen" when the call can be repeated; nothing changes locally (§9.9). +* Other `OnlineResult.Failed(code)` values map to inline text when a field is at fault (`name_taken` under the name) and to a snackbar otherwise, text from `strings.csv` `error_*`. +* **Pending marker**: `cloud_upload` icon in `pending` colour, TalkBack "Väntar på synk" (`common_pending_sync`), on every row or detail whose `pending == true`. +* **Amounts**: tabular figures always; formatted by `MoneyFormatter` (§9.8: `1 234,50 kr` with U+00A0, never a minus sign); spoken by `MoneyFormatter.spoken` ("1 234,50 kronor"). Amounts never ellipsize; at large font scale they move to their own line or wrap the unit. +* **Direction** of money (owes / owed) is always words plus colour, never colour alone (§13.1). +* "Viewer" = the person holding the phone, always "du" / "Du"; the partner by display name. Wherever two people are shown side by side, the viewer is on the left. +* State names: **loaded, empty, loading, offline, error, long** (long Swedish names, 7-digit amounts, 200 % font scale) and named variants. PNGs exist for every state in §4: `screens/-light.png` and `-dark.png`; 200 % and English frames are light only. + +--- + +## 2. Tokens + +### 2.1 Material 3 colour roles + +Light and dark. Contrast is WCAG 2 against the background named; text needs ≥ 4.5:1, icons and graphics ≥ 3:1. `card` (§2.2) is the fill of most content cards. + +| Role | Light | Dark | Contrast L / D | +|---|---|---|---| +| primary | #9C5238 | #E6B5A2 | 5.3 / 9.6 on surface; 5.7 / 8.4 on card | +| on-primary | #FFFFFF | #4A1F10 | 5.7 / 7.7 on primary | +| primary-container | #F2DED4 | #6A3826 | — | +| on-primary-container | #3A1609 | #F6DCD0 | 12.5 / 7.3 | +| secondary | #5A6D52 | #BCCBB1 | 5.2 / 10.3 on surface | +| on-secondary | #FFFFFF | #27341F | 5.6 / 7.7 | +| secondary-container | #DDE6D3 | #3D4A35 | — | +| on-secondary-container | #172410 | #D9E6CD | 12.6 / 7.2 | +| tertiary | #76612F | #DCC690 | 5.6 / 10.5 on surface | +| on-tertiary | #FFFFFF | #3A2E0B | 6.0 / 7.9 | +| tertiary-container | #EEE3C6 | #55432E | — | +| on-tertiary-container | #261C04 | #F1E3C4 | 13.1 / 7.4 | +| error | #B3261E | #F2B8B1 | 6.1 / 10.2 on surface; 6.5 / 9.0 on card | +| on-error | #FFFFFF | #5F1410 | 6.5 / 7.7 | +| error-container | #F9DEDA | #73302A | — | +| on-error-container | #410E0B | #F9DEDA | 12.8 / 7.5 | +| surface | #FAF7F2 | #1B1917 | — | +| on-surface | #22201D | #ECE6DF | 15.2 / 14.1 | +| surface-variant | #E8E2D9 | #4A4540 | — | +| on-surface-variant | #4E4842 | #CDC5BC | 8.4 / 10.3 on surface; 9.0 / 9.0 on card | +| surface-container-lowest | #FFFFFF | #151311 | — | +| surface-container-low | #F5F1EB | #221F1D | — | +| surface-container | #F0EBE4 | #272422 | — | +| surface-container-high | #EAE5DD | #312E2B | — | +| surface-container-highest | #E4DED6 | #3C3835 | — | +| surface-dim | #DCD6CE | #1B1917 | — | +| surface-bright | #FAF7F2 | #423E3A | — | +| inverse-surface | #36322E | #ECE6DF | — | +| inverse-on-surface | #F6F1EA | #312E2B | 11.3 / 10.9 | +| inverse-primary | #E4B39F | #9C5238 | 6.8 / 4.6 on inverse-surface | +| outline | #7E766E | #978E86 | 4.2 / 5.5 (graphics only) | +| outline-variant | #D2CAC1 | #4A4540 | decorative only | +| scrim | #000000 at 38 % | #000000 at 38 % | — | + +Disabled content and containers follow Material 3: content `on-surface` at 38 %, container `on-surface` at 12 %. + +### 2.2 Kvitt semantic roles (`KvittColors`) + +| Role | Light | Dark | Use | Contrast L / D | +|---|---|---|---|---| +| owed | #3D6A4C | #A2CCB0 | text and graphics: the partner owes the viewer; "Du lånade ut …" | 5.8 / 9.8 on surface; 6.2 / 8.7 on card; 5.0 / 7.6 on surface-container-high | +| on-owed | #FFFFFF | #10331F | on owed | 6.2 / 7.8 | +| owed-container | #D6E7D9 | #2E4A37 | sync status tile | — | +| on-owed-container | #0F2416 | #CBE7D3 | | 12.7 / 7.4 | +| owing | #78497A | #D8B5D6 | text and graphics: the viewer owes the partner; "Du lånade …" | 6.5 / 9.6 on surface; 6.9 / 8.4 on card; 5.5 / 7.4 on surface-container-high | +| on-owing | #FFFFFF | #3C1F3C | | 6.9 / 7.9 | +| owing-container | #EEDDEC | #553A55 | | — | +| on-owing-container | #2D1430 | #F1DDEF | | 12.9 / 7.7 | +| settled | #6A5A2B | #E0CA94 | "Kvitt", seesaw plank when settled | 6.3 / 10.9 on surface; 5.3 / 5.9 on settled-container | +| settled-container | #EEE3C6 | #55432E | settled hero card | — | +| on-settled-container | #261C04 | #F1E3C4 | | 13.1 / 7.4 | +| warning | #8A5522 | #EDBE8C | over-settle, remaining-to-allocate, offline banners | 5.8 / 10.3 on surface | +| on-warning | #FFFFFF | #452B0E | | 6.2 / 7.7 | +| warning-container | #F5E1CA | #5B3F24 | warning strips and banners | — | +| on-warning-container | #2C1800 | #F6DFC6 | | 13.3 / 7.5 | +| pending | #3C6680 | #A3C7DE | pending marker, pending timeline line | 5.8 / 9.8 on surface; 6.2 / 8.7 on card | +| avatar-1 | #8A5A33 | #DDB693 | initials background, slot 1 (§2.5) | — | +| on-avatar-1 | #FFFFFF | #3A230F | | 5.8 / 7.8 | +| avatar-2 | #4A6587 | #ADC3DD | initials background, slot 2 | — | +| on-avatar-2 | #FFFFFF | #1C2F45 | | 6.0 / 7.5 | +| card | #FFFFFF | #272422 | fill of content cards, segmented list items, account cards | — | +| on-cat | #FFFFFF | #1B1917 | icon or letter on a solid category or account tile | ≥ 3.9 / ≥ 4.7 on every category colour; ≥ 4.8 / ≥ 8.2 on every chart colour | +| shadow | #3C2819 at 8 % | #000000 at 25 % | card shadow colour (§2.10) | — | + +Derived tints (fixed values, computed as the first colour mixed over the second): + +| Tint | Rule | Light | Dark | +|---|---|---|---| +| settlement-row | primary-container 28 % over card | #FBF6F3 | #3A2A23 | +| settlement-hero | primary-container 50 % over card | #F8EEEA | #482E24 | +| sync-pill-pending | pending 13 % over surface | #E1E4E3 | #2D3031 | +| pending-badge | pending 14 % over card | #E4EAED | #383B3C | +| account-header-n | chart-cat-n 12 % over card | n = 1 … 8: #F4EBE8, #ECEFEA, #F1EDE6, #EAEEF3, #EFEBF1, #E8EFEE, #F4EBEE, #EDEEEE | #3E342F, #373830, #3C362D, #353638, #3A3437, #333734, #3E3434, #383635 | + +`on-surface` on sync-pill-pending: 12.7 / 10.7; `pending` on pending-badge: 5.1 / 6.3. + +### 2.3 Category colours (§9.7 tokens; UX-5) + +Fourteen tokens. Light and dark values; the **container** is the colour mixed over `surface` at **18 %** (light) and **24 %** (dark). Category colours are never used for text. + +| Token | Light | Light container | L: vs surface / vs card / vs container / on-cat on it | Dark | Dark container | D: vs surface / vs card / vs container / on-cat on it | +|---|---|---|---|---|---|---| +| `green` | #4E7A55 | #DBE1D6 | 4.6 / 5.0 / 3.7 / 5.0 | #8FB896 | #373F35 | 7.9 / 7.0 / 4.9 / 7.9 | +| `orange` | #B26A3E | #EDDED2 | 3.9 / 4.2 / 3.2 / 4.2 | #E8AA7E | #4C3C30 | 8.7 / 7.7 / 5.3 / 8.7 | +| `brown` | #6B5244 | #E0D9D3 | 6.7 / 7.2 / 5.2 / 7.2 | #B8A090 | #413934 | 7.1 / 6.2 / 4.6 / 7.1 | +| `yellow` | #9C7C2A | #E9E1CE | 3.7 / 3.9 / 3.0 / 3.9 | #E6C67A | #4C432F | 10.6 / 9.3 / 5.9 / 10.6 | +| `blue` | #4A78A8 | #DAE0E5 | 4.3 / 4.6 / 3.5 / 4.6 | #98C0E6 | #394149 | 9.2 / 8.1 / 5.4 / 9.2 | +| `teal` | #367F82 | #D7E1DE | 4.4 / 4.7 / 3.5 / 4.7 | #8CCAC8 | #364341 | 9.5 / 8.4 / 5.6 / 9.5 | +| `purple` | #865891 | #E5DAE1 | 5.1 / 5.5 / 4.0 / 5.5 | #C59CDA | #443846 | 7.6 / 6.7 / 4.8 / 7.6 | +| `red` | #A84B45 | #EBD8D3 | 5.2 / 5.6 / 4.1 / 5.6 | #DC8F86 | #493532 | 6.9 / 6.1 / 4.5 / 6.9 | +| `pink` | #AE5D7C | #ECDBDD | 4.2 / 4.5 / 3.4 / 4.5 | #EFB0BE | #4E3D3F | 9.7 / 8.5 / 5.6 / 9.7 | +| `grey` | #6C7177 | #E0DFDC | 4.6 / 4.9 / 3.7 / 4.9 | #B0B2B3 | #3F3E3C | 8.2 / 7.2 / 5.0 / 8.2 | +| `indigo` | #45508D | #D9D9E0 | 7.1 / 7.5 / 5.4 / 7.5 | #969DD8 | #393945 | 6.8 / 6.0 / 4.4 / 6.8 | +| `lime` | #73822F | #E2E2CF | 4.0 / 4.2 / 3.2 / 4.2 | #B9CC86 | #414432 | 10.1 / 8.8 / 5.7 / 10.1 | +| `slate` (new, UX-5) | #5E2D4E | #DED3D4 | 10.0 / 10.7 / 7.3 / 10.7 | #B0719A | #3F2E36 | 4.7 / 4.2 / 3.4 / 4.7 | +| `amber` (new, UX-5) | #937D63 | #E7E1D8 | 3.7 / 3.9 / 3.0 / 3.9 | #DFCFC0 | #4A4540 | 11.5 / 10.2 / 6.2 / 11.5 | + +All values are graphics ratios (≥ 3:1 required, all pass; the tightest are light `yellow` 3.03 and `amber` 3.02 on their containers, used only for unselected picker tiles where the name is printed beside the icon). + +Use: + +| Where | Fill | Icon | +|---|---|---| +| Solid tile (ledger rows, detail hero tile, selected picker tile, chart segments, breakdown bars, legend swatch) | `cat-` | `on-cat` | +| Container tile (unselected picker tile in `CategoryQuickRow` and `CategoryGridSheet`) | `cat--container` | `cat-` | +| Expense detail hero card, drill-down header card | `cat--container` | — | +| Settings row tiles (§3.11 `SettingsRow`) | `cat--container` | `cat-` | + +### 2.4 Chart and account colours + +Accounts get `chart-cat-n` by their position in `AccountRepository.all()` (created_at ascending, archived included), n = position mod 8 + 1. The same colour is the account's tile (§3.4) on Home, the tint of its header on S-5 and its series in S-11. Categories in charts always use their own category colour. + +| Token | Light | Dark | vs card L / D (graphics) | `on-cat` on it L / D (text) | +|---|---|---|---|---| +| chart-cat-1 | #A75B40 | #E3A68E | 5.0 / 7.4 | 5.0 / 8.4 | +| chart-cat-2 | #5E7A4E | #ABC79A | 4.8 / 8.3 | 4.8 / 9.5 | +| chart-cat-3 | #8B6A2C | #D8BD7E | 5.0 / 8.4 | 5.0 / 9.6 | +| chart-cat-4 | #4C6E98 | #9EB9DC | 5.3 / 7.7 | 5.3 / 8.7 | +| chart-cat-5 | #7C5A88 | #C8A6D2 | 5.7 / 7.2 | 5.7 / 8.2 | +| chart-cat-6 | #3F7774 | #8FC2BC | 5.1 / 7.8 | 5.1 / 8.9 | +| chart-cat-7 | #A2566F | #E3A6BA | 5.1 / 7.7 | 5.1 / 8.7 | +| chart-cat-8 | #6A6F74 | #B5B9BD | 5.1 / 7.8 | 5.1 / 8.9 | +| chart-seq-1 | #F2DFD6 | #3E2C25 | 1.3 / 1.2 (fill behind a label only) | — | +| chart-seq-2 | #E2B7A5 | #6A4234 | 1.8 / 1.8 (fill behind a label only) | — | +| chart-seq-3 | #C08068 | #9A6049 | 3.2 / 3.0 | — | +| chart-seq-4 | #A75B40 | #CC8A70 | 5.0 / 5.5 | — | +| chart-seq-5 | #763A26 | #E6B5A2 | 8.7 / 8.4 | — | +| chart-grid | #E6E0D8 | #36322F | decorative | — | +| chart-axis-label | #4E4842 | #CDC5BC | 9.0 / 9.0 (text) | — | + +Stacked segments are separated by a 2 dp gap so neighbouring colours never touch. Sequential tokens are reserved (no v1 screen uses them). + +### 2.5 Avatar colours (UX-6, AC-4.5) + +Initials (first letter of the first two words of the display name, uppercase, AC-4.5) on `avatar-1` / `on-avatar-1` or `avatar-2` / `on-avatar-2`. **Slot rule:** of the household's users, the one whose id sorts first (plain string comparison of the lowercase UUID) is slot 1, the other slot 2; a household with one user has only slot 1. User ids are UUIDv7, so in practice the admin (created first) is slot 1 on both phones, and the two people never share a colour. The split bar (§3.7) uses the same two colours for the two people. + +### 2.6 Dynamic colour + +Not offered. Category, account, owed/owing and settled colours carry meaning and a wallpaper scheme cannot recolour them coherently (UX-1). The theme follows the system light/dark setting only. + +### 2.7 Typography + +Typeface: the Material 3 default (`FontFamily.Default`, Roboto on Android 16), no bundled font (D-020). Every amount style sets `fontFeatureSettings = "tnum"`; Roboto figures are already tabular, the feature is set so a different system font stays aligned. + +| Style | Size / line height (sp) | Weight | Letter spacing (sp) | Used for | +|---|---|---|---|---| +| display-large | 57 / 64 | 500 | −0.25 | unused | +| display-medium | 45 / 52 | 500 | 0 | unused | +| display-small | 36 / 44 | 600 | 0 | "Kvitt" on S-1 | +| headline-large | 32 / 40 | 600 | 0 | unused | +| headline-medium | 28 / 36 | 600 | 0 | "Ni är kvitt", S-18 title, S-22 version | +| headline-small | 24 / 32 | 600 | 0 | empty-state titles, dialog titles, "kr" after the editor amount | +| title-large | 22 / 28 | 600 | 0 | top app bar titles, section headers ("Konton") | +| title-medium | 16 / 24 | 600 | 0.15 | row titles, balance wording in the hero, card titles | +| title-small | 14 / 20 | 600 | 0.1 | month headers, group headers, banner titles | +| body-large | 16 / 24 | 400 | 0.5 | field values, detail fact values, empty-state body | +| body-medium | 14 / 20 | 400 | 0.25 | effect line, secondary row text, banner body | +| body-small | 12 / 16 | 400 | 0.4 | dates in rows, captions, row meta | +| label-large | 14 / 20 | 600 | 0.1 | buttons, "Totalt" | +| label-medium | 12 / 16 | 500 | 0.5 | field labels, account-card wording, nav labels | +| label-small | 11 / 16 | 500 | 0.5 | badges, category tile labels (11 / 14) | +| amount-hero | 44 / 52 | 700 | −0.5 | Home balance, editor amount | +| amount-large | 28 / 36 | 700 | −0.25 | detail amounts, S-5 balance, analytics total, S-9 amount field | +| amount-medium | 18 / 24 | 700 | 0 | account-card amount, allocation fields, compact editor row | +| amount-small | 15 / 20 | 600 | 0 | ledger row amounts, breakdown amounts | + +Units next to large amounts: the hero's "kr" is 24 / 30 sp weight 700 in the amount's colour; the editor's "kr" is headline-small in `on-surface-variant`. English puts "SEK" before the number (§9.8) with the same unit style. + +**Font scale.** Compose's default non-linear font scaling (Android 14+) applies: text of 24 sp and larger grows less than the system scale (about 1.5× at 200 %), smaller text grows fully. Two explicit caps, implemented by converting a dp-based size (`with(LocalDensity) { (12.dp * min(fontScale, 1.6f)).toSp() }`): bottom-navigation labels at 1.6×, the seesaw name labels at 1.15×; the account tile letter at 1.3×. Nothing else is capped. Check on the S26 in M6 that `FontFamily.Default` resolves to Roboto with tabular figures under every One UI font style (open point 4). + +### 2.8 Spacing + +Scale (`KvittSpacing`): `s1` 4, `s2` 8, `s3` 12, `s4` 16, `s5` 20, `s6` 24, `s8` 32, `s10` 40, `s12` 48 dp. + +| Measure | dp | +|---|---| +| Screen gutter | 16 | +| Gap between cards and sections in a scrolling column | 12 (S-4, S-7), 10 (detail screens, S-9, S-11, settings sub-screens) | +| Gap between items of a segmented group | 3 | +| Card padding | 16–20 (per component) | +| Row padding | 10 vertical, 16 horizontal; min height 56 (settings, detail facts) or 72 (ledger) | +| Gap between leading tile and text | 14 (rows), 12 (cards) | +| Bottom padding of a scrolling column under a FAB | 120 | +| Top app bar height | 64 | +| Bottom navigation | 64 + system navigation inset | + +### 2.9 Shapes + +M3 shape scale: extraSmall 4, small 8, medium 12, large 16, largeIncreased 20, extraLarge 28, extraLargeIncreased 32, extraExtraLarge 48, full. + +| Component | Shape | +|---|---| +| Buttons, chips, keypad keys, sync pill, badges, nav indicator (56 × 32, radius 16), connected-button outer ends | full | +| Connected-button inner corners | small 8 | +| Text fields (filled, no indicator line) | large 16, all four corners | +| Menus, info boxes inside cards | large 16 | +| Warning strips inside cards, menu items | medium 12 | +| FAB (56 dp) | largeIncreased 20 | +| Cards, balance hero, dialogs, keypad sheet top corners, flat sections | extraLarge 28 | +| Account cards, segmented-group outer corners, banners | 24 | +| Segmented-group inner corners | small 8 | +| Allocation amount field | 14 | +| Icon grid cells (S-15) | large 16 | +| Chart bars | top 10, bottom 4 | +| Category tile | `MaterialShapes.Cookie4Sided` → fallback `RoundedCornerShape(30%)` | +| Account tile | `MaterialShapes.Clover4Leaf` → fallback `RoundedCornerShape(16.dp)` | +| Large avatars (≥ 40 dp outside rows: hero, settings card, S-2, S-9, S-10, S-14), app mark, dialog icon tile, sync status tile | `MaterialShapes.Sunny` → fallback `CircleShape` | +| Empty and removed illustrations (S-18), loading indicator container | `MaterialShapes.Cookie9Sided` → fallback `CircleShape` | +| Avatars inside rows and chips (20–32 dp), colour swatches, settlement tile | `CircleShape` | + +MaterialShapes are `RoundedPolygon`s turned into a `Shape` with `toShape()`; all are wrapped in `KvittShapes` so the fallback swaps in one place. + +### 2.10 Elevation + +Tonal first. Light: content cards (`card` #FFFFFF on surface #FAF7F2) carry a soft shadow, two layers (y 1 blur 2, y 2 blur 8) in `shadow` (#3C2819 at 8 %); in Compose `Modifier.shadow(elevation = 2.dp, shape, ambientColor = shadow, spotColor = shadow)`. Dark: no visible shadow; `card` is surface-container on surface. FAB, menus and dialogs: level 3 (6 dp, Material default shadow) in both themes; the dialog scrim is #000000 at 38 %. Segmented list items and flat sections (`surface-container-low`) have no shadow. + +### 2.11 Motion + +`MaterialTheme(motionScheme = MotionScheme.expressive())`; fallback `MotionScheme.standard()` (§3.1 rule on Expressive APIs). + +| Spring | Damping ratio | Stiffness | Use | +|---|---|---|---| +| spatial fast | 0.6 | 800 | chip and tile selection, check badge pop (scale 0 → 1), keypad key press | +| spatial default | 0.8 | 380 | expanding the split section, collapsible sections, sheet open, FAB extend/collapse | +| spatial slow | 0.8 | 200 | screen-level layout changes | +| effects fast | 1.0 | 3800 | state-layer and colour of pressed controls | +| effects default | 1.0 | 1600 | colour and opacity changes (tile container → solid, card cross-fades) | +| effects slow | 1.0 | 800 | settled card cross-fade to oat | + +Named motions: + +* **Seesaw re-tilt** when the global balance changes: spring damping 0.55, stiffness 300; the hero amount counts from the old to the new value over 400 ms, `EmphasizedDecelerate`. +* **Settling to zero**: the plank levels (re-tilt spring), then the bunting drops one pennant at a time, 40 ms stagger, each on spatial fast; the card cross-fades to `settled-container` (effects slow). +* **Category selection**: the chosen tile cross-fades container → solid (effects default) and the check badge pops (spatial fast); a suggestion change (CS-7) uses the same motion without haptics. +* **Saved expense**: the editor closes; the new row is in its month group on return (no extra animation required). +* **Navigation**: pushed screens enter with a horizontal shared-axis transition (30 dp slide + fade, spatial default); tab switches fade through (effects default); S-7 and S-9 enter from the bottom (slide up 10 % + fade). +* **Reduced motion** (system "Ta bort animationer", `Settings.Global.ANIMATOR_DURATION_SCALE == 0`): all springs become critically damped and immediate, the bunting appears at once, the hero amount does not count. + +### 2.12 Icons + +Material Symbols **Rounded**, optical size 24, grade 0, shipped as vector drawables generated from the font (Apache 2.0). Two variants: + +| Variant | FILL | Weight | Used for | +|---|---|---|---| +| outline | 0 | 500 | app-bar actions, row leading icons in detail facts, chevrons, field icons | +| filled | 1 | 600 | category icons, status icons (cloud_done, warning, error), selected nav icon, banner icons, handshake, check badge (weight 700, size 14) | + +Icons are dp-sized (24 default; 18 in chips; 15–16 in row meta; 20 in buttons) and never scale with font size. + +**Category icon tokens (33, §9.7 + UX-8)** in `CategoryIcons.ALL` order: `shopping_cart`, `restaurant`, `local_cafe`, `home`, `bolt`, `water_drop`, `wifi`, `directions_car`, `train`, `local_gas_station`, `flight`, `hotel`, `luggage`, `movie`, `music_note`, `sports_esports`, `fitness_center`, `medical_services`, `medication`, `shopping_bag`, `checkroom`, `pets`, `child_care`, `school`, `card_giftcard`, `celebration`, `build`, `chair`, `subscriptions`, `category`, `key`, `shield`, `account_balance`. Each has a Swedish and English TalkBack name in `strings.csv` (`icon_`). + +**UI icons** (outline unless marked filled): + +| Icon | Where | +|---|---| +| `home`, `bar_chart`, `settings` | bottom navigation (filled when selected) | +| `add` | FAB, "Nytt konto", "Lägg till konto", "Ny kategori", "Ny återkommande" | +| `close` | editor top bars (S-6, S-7, S-9, S-15 editor, S-19 editor) | +| `arrow_back` | pushed screens | +| `more_vert` | S-5 overflow | +| `edit`, `delete` | S-8, S-10 actions; menu items | +| `check` | Spara in editors, check badge, selected chips | +| `swap_horiz` | S-9 direction swap; S-7 payer chip trailing | +| `handshake` (filled) | "Gör upp", settlement tile, confirm dialog | +| `cloud_upload` | pending marker | +| `cloud_off` | offline sync pill, "Kräver anslutning" hints and banners | +| `cloud_done` (filled) | synced pill and S-16 status | +| `sync`, `sync_problem` | "Synka nu"; S-16 status when the last attempt failed | +| `warning` (filled), `error` (filled), `info`, `check_circle` (filled) | warnings, errors, info lines, settled and "Kvar att fördela 0,00 kr" | +| `chevron_right`, `expand_more`, `expand_less` | rows, dropdown chips, collapsible sections | +| `inventory_2`, `unarchive` | archive badge and actions | +| `event`, `call_split`, `notes`, `person`, `apps`, `backspace`, `calculate`, `date_range` | editor and detail facts, "Alla", keypad delete, S-6 preview, period chip | +| `content_paste`, `link`, `dns`, `person_add`, `add_a_photo`, `photo_library` | S-1, S-2, S-3, S-14 | +| `receipt_long` (filled) | app mark, notification small icon | +| `schedule`, `pause`, `event_repeat`, `drag_indicator`, `restart_alt`, `lock` | S-3, S-19, S-8, S-15 | +| `system_update`, `download`, `downloading`, `refresh`, `install_mobile` | S-17 | +| `phonelink_erase`, `smartphone`, `block`, `devices`, `add_link`, `key`, `share`, `content_copy` | S-18, S-20, S-21, S-13 | +| `memory`, `description`, `open_in_new` | S-22, S-16 | +| `arrow_forward` | S-9, S-10 direction | + +### 2.13 Default categories (UX-8) + +Seeded by the server (SPEC AC-7.2, D-049 to D-054, SPEC v0.8; ids 11 to 14 added by server migration 0002). The localised name shows until a user renames the category. + +| Sort | `default_key` | Fixed id (SPEC §14.1) | Swedish | English | Icon | Colour | +|---|---|---|---|---|---|---| +| 10 | `groceries` | `01920000-0000-7000-8000-000000000001` | Mat & hushåll | Groceries | `shopping_cart` | `green` | +| 20 | `restaurants` | `01920000-0000-7000-8000-000000000002` | Restaurang & café | Restaurants | `restaurant` | `orange` | +| 25 | `housing` | `01920000-0000-7000-8000-000000000011` | Boende | Housing | `key` | `indigo` | +| 30 | `home` | `01920000-0000-7000-8000-000000000003` | Hem | Home | `home` | `brown` | +| 40 | `utilities` | `01920000-0000-7000-8000-000000000004` | Räkningar | Utilities | `bolt` | `yellow` | +| 45 | `insurance` | `01920000-0000-7000-8000-000000000012` | Försäkringar | Insurance | `shield` | `slate` | +| 50 | `transport` | `01920000-0000-7000-8000-000000000005` | Transport | Transport | `directions_car` | `blue` | +| 60 | `travel` | `01920000-0000-7000-8000-000000000006` | Resor | Travel | `flight` | `teal` | +| 70 | `entertainment` | `01920000-0000-7000-8000-000000000007` | Nöje | Entertainment | `movie` | `purple` | +| 80 | `health` | `01920000-0000-7000-8000-000000000008` | Hälsa | Health | `medical_services` | `red` | +| 85 | `children` | `01920000-0000-7000-8000-000000000013` | Barn | Children | `child_care` | `lime` | +| 90 | `shopping` | `01920000-0000-7000-8000-000000000009` | Shopping | Shopping | `shopping_bag` | `pink` | +| 95 | `fees` | `01920000-0000-7000-8000-000000000014` | Avgifter & skatter | Fees & taxes | `account_balance` | `amber` | +| 100 | `other` | `01920000-0000-7000-8000-00000000000a` | Övrigt | Other | `category` | `grey` | + +Every default category has its own colour; `teal` is shared with the archived example custom category "Pant" (`water_drop`) in the frames only. + +### 2.14 Compose mapping (`:core:design`) + +| Token group | Compose | +|---|---| +| §2.1 | `lightColorScheme(...)` / `darkColorScheme(...)`, role names 1:1 (`on-primary-container` → `onPrimaryContainer`) | +| §2.2–§2.5 | `@Immutable data class KvittColors(...)` with a light and a dark instance in `LocalKvittColors`; functions `category(token: ColorToken)`, `categoryContainer(token)`, `account(index: Int)` (chart-cat, 0-based position mod 8), `avatar(slot: Int)` / `onAvatar(slot)`; properties for every role of §2.2 and the derived tints | +| §2.7 | `Typography(...)` for the 15 M3 styles (default font family, weights and letter spacing above) plus `KvittTypography(amountHero, amountLarge, amountMedium, amountSmall)` in `LocalKvittTypography` | +| §2.8 | `object KvittSpacing` (or `LocalKvittSpacing`) with `s1` … `s12`, `gutter`, `rowMin`, `ledgerRowMin` | +| §2.9 | `Shapes(extraSmall … extraLarge)` plus `KvittShapes(categoryTile, accountTile, avatarLarge, illustration, card, accountCard, groupOuter, groupInner, field, fab)` in `LocalKvittShapes` | +| §2.11 | `MotionScheme` on `MaterialTheme`; named motions as constants in `KvittMotion` | +| Entry point | `KvittTheme(darkTheme: Boolean = isSystemInDarkTheme(), content)`, accessors `KvittTheme.colors`, `.typography`, `.shapes`, `.spacing` | + +`ColorToken` gains `SLATE` and `AMBER` (UX-5) and `DefaultCategoryKey` gains `HOUSING`, `INSURANCE`, `CHILDREN`, `FEES` (UX-8); `KvittColors.category` is exhaustive over the enum. + +--- + +## 3. Components (`:core:design`) + +### 3.1 Rules for every component + +* Colours, type, shapes and spacing only from `KvittTheme` (§2.14); no literal values in feature modules. +* Strings from resources. Shared strings live in `:core:design`; components take resolved `String`s for feature-specific words. +* Every component takes a `Modifier`, has light/dark, Swedish/English and 100 %/200 % font-scale screenshot tests in M6 (SPEC M6), and keeps 48 dp touch targets. +* **Expressive APIs** (`@ExperimentalMaterial3ExpressiveApi`) are used only behind a `:core:design` wrapper so the stable fallback named in the component swaps in at one place. The implementing agent confirms each API's status and signature in the official documentation at implementation time (CLAUDE.md › Fast-moving APIs). Summary: + +| Expressive | Where | Stable fallback | +|---|---|---| +| `MaterialShapes` (Cookie4Sided, Clover4Leaf, Sunny, Cookie9Sided) via `toShape()` | category, account, avatar and illustration tiles | `RoundedCornerShape` / `CircleShape` (§2.9) | +| `ShortNavigationBar` (64 dp) | bottom navigation | `NavigationBar` (80 dp) | +| `ButtonGroup` (connected) | analytics toggles, ratio presets, full-split person picker | `SingleChoiceSegmentedButtonRow` | +| Expressive slider look (thick track, thin handle) | `SplitSlider` | `Slider` with custom track and thumb | +| `LoadingIndicator` (shape morph) | first sync on Home | `CircularProgressIndicator` | +| `LinearWavyProgressIndicator` | update download | `LinearProgressIndicator` | +| Medium extended FAB (56 dp, radius 20) | "Lägg till utgift", "Ny återkommande" | `ExtendedFloatingActionButton` | +| Button sizes small (40) and medium (56) | all buttons | `Button` with `heightIn` | +| `MotionScheme.expressive()` | theme | `MotionScheme.standard()` | +| Segmented list items (grouped shapes) | ledger, settings, devices, facts | `Surface` per item with the §2.9 group shapes | + +* **Semantics.** Rows merge their children into one node (`Modifier.semantics(mergeDescendants = true)`) with the default click action (no custom click labels); icon-only controls always have a `contentDescription`; decorative tiles, the seesaw, split bars next to their numbers and illustrations are cleared (`clearAndSetSemantics {}` or `contentDescription = null`). TalkBack text is Swedish or English per app language and reads amounts with `MoneyFormatter.spoken`. + +### 3.2 Money and words + +**`MoneyText(ore: Long, style: AmountStyle, tone: AmountTone, unit: Boolean = true)`** +* `AmountStyle`: `Hero`, `Large`, `Medium`, `Small` → the four amount styles of §2.7. `AmountTone`: `Neutral` (on-surface), `Owed`, `Owing`, `Muted` (on-surface-variant), `Settled`. +* Never a sign. Never ellipsized: one line; when it does not fit, `Hero` and `Large` put the unit on a second line; others wrap at the unit. +* Semantics: `MoneyFormatter.spoken(ore)`. + +**`EffectLine(effect: ExpenseEffect)`** (§9.2), body-medium: +* `Lent(ore)` "Du lånade ut 206,00 kr" in `owed` (`effect_lent`) · `Borrowed(ore)` "Du lånade 623,80 kr" in `owing` (`effect_borrowed`) · `NotInvolved` "Inte inblandad" · `PaidForYourself` "Betalade för dig själv" in `on-surface-variant`. + +**`BalanceWording(netOre: Long, partner: User, scope: Global | Account)`** (§9.3): "Anna är skyldig dig" (`owed`), "Du är skyldig Anna" (`owing`), "Ni är kvitt" (global, `settled`) / "Kvitt" (account, `settled`). Used by `BalanceHero`, `AccountCard`, `AccountBalanceHeader`. + +### 3.3 People + +**`PersonAvatar(user: User, size: Dp, shape: AvatarShape = Circle)`** +* Sizes in use: 20, 24, 28, 32, 40, 46, 52, 72, 112, 128. +* Picture via Coil keyed by `user.id` + `pictureVersion` (AC-4.6), cropped to the shape; else initials on the slot colour (§2.5), initials size 44 % of the avatar (capped at 1.3× font scale). `Sunny` shape for the large standalone uses of §2.9, else circle. +* States: picture, initials (also while the picture loads), **uploading** (a 4 dp `CircularProgressIndicator` in `primary` drawn around the avatar, picture or initials at 60 % opacity). +* Semantics: the display name; decorative (`null`) when the name is printed beside it. + +**`AvatarPlaceholder(size)`**: the empty partner seat of S-3: circle, 2 dp dashed `outline` border, `surface-container-high` fill, `person_add` icon at 50 % of the size in `on-surface-variant`. TalkBack "Partner saknas". + +### 3.4 Categories and accounts + +**`CategoryTile(category: Category, size: Dp, style: Solid | Container, archived: Boolean = false)`** +* Shape Cookie4Sided (fallback 30 % rounded). Sizes: 32 (breakdown), 36 (S-15 list, S-16, compact editor row), 40 (ledger rows), 52 (editor quick row and sheet), 56 (S-12 header), 60 (S-8 hero), 64 (S-15 editor preview). +* Icon: filled Material Symbol at 52 % of the tile (`iconRatio`), colours per §2.3. +* `archived`: saturation 25 %, opacity 75 %. +* Semantics: decorative when the category name is printed next to it (always, except in the quick row where the label is the tile's own text). + +**`AccountTile(name: String, colorIndex: Int, size: Dp = 44, muted: Boolean = false)`** +* Clover4Leaf (fallback 16 dp rounded) filled with `chart-cat-n` (§2.4); the first grapheme of the name, uppercase, title 18 sp weight 700 in `on-cat` (capped 1.3×). `muted` (archived): opacity 70 %. Sizes 32, 36, 40, 44. Always decorative. + +**`AccountCard(summary: AccountSummary, colorIndex: Int, onClick)`** (S-4) +* Card, radius 24, `card` fill and shadow, padding 14 vertical / 16 horizontal, min height 76, gap 14. +* Left: `AccountTile` 44. Middle: name (title-medium, 1 line, ellipsis) with an "Arkiverat" badge (`inventory_2` 14) for archived accounts; below it body-small `on-surface-variant` "11 utgifter i oktober" (`home_account_expenses_month`, plural; current month name) or "Inga utgifter i oktober". Right column, right-aligned: wording in label-medium and `MoneyText(Medium)` both in `owed`/`owing`; when `netOre == 0`: `check_circle` (filled, 20) + "Kvitt" in title-medium `settled`, no amount. +* Archived card: card opacity 92 %, tile muted. +* Long and 200 %: the right column wraps to its own line under the name, right-aligned (flex-wrap); the name keeps one line with ellipsis at 100 % and two lines at ≥ 1.5×. +* Semantics: one node "Vardag, Anna är skyldig dig 1 184,50 kronor, 11 utgifter i oktober" (archived: "…, arkiverat"), role button. + +**`CategoryQuickRow`, `CategoryGridSheet`**: see §3.7 (editor fields). + +**`CategoryIconPicker(selected: String, color: ColorToken)`** (S-15): grid of the 33 icon tokens, 7 columns, gap 5, cells 48 dp high, radius 16. Unselected: `surface-container-high` fill, outline icon in `on-surface-variant`. Selected: `cat-` fill, filled icon in `on-cat`. Single-select radio group; each cell TalkBack = the icon's name (`icon_`), state "vald". + +**`ColorSwatchPicker(selected: ColorToken)`** (S-15): 14 circles of 44 dp in a 7-column grid, gap 8, each the solid category colour. Selected: `check` icon 22 in `on-cat` plus a 3 dp `surface` ring and a 2 dp `on-surface` ring outside it (selection never by colour alone). TalkBack: colour name ("Grön", "Plommon", "Sand", … `color_`), state "vald". + +### 3.5 Ledger (D-019; used by S-5, S-12, S-16) + +**`SegmentedGroup`**: vertical group of items 3 dp apart; outer corners 24, inner 8; item fill `card` (default) or `surface-container-low` (`soft`). Light: the group casts the card shadow once. + +**`LedgerMonthHeader(period: String, householdSpendOre: Long)`**: title-small "Oktober · 7 457,90 kr" (`ledger_month_total`), padding 12 top / 8 sides / 2 bottom; year appended when not the current year ("September 2025 · …"). Sticky (`stickyHeader`) on S-5 with a `surface` background. Semantics heading. + +**`ExpenseRow(row: LedgerRow.ExpenseRow, onClick)`** +* Item of a `SegmentedGroup`, min height 72, padding 10 / 16, gap 14. +* `CategoryTile` 40 solid · centre: description (title-medium, 1 line, ellipsis), `EffectLine` below · right column: `MoneyText(Small)`, below it body-small `on-surface-variant` date ("16 okt.", §9.8) preceded by the pending marker (15 dp) when `pending`. +* 200 %: the right column moves under the centre column, left-aligned with the text, so the description gets the full width (still 1 line, ellipsis) and the amount is never cut. +* Semantics: one node "ICA Maxi Lindhagen, 1 247,60 kronor, 16 oktober, du lånade 623,80 kronor, väntar på synk" (category name added after the description: "…, Mat & hushåll, …"). + +**`SettlementRow(row: LedgerRow.SettlementRow, onClick)`** +* Same item geometry; fill `settlement-row` tint (§2.2). +* Tile: 40 dp circle `primary-container` with filled `handshake` in `on-primary-container`. Centre: "Anna betalade dig 800,00 kr" / "Du betalade Anna 800,00 kr" (title-medium, `ledger_settlement_*`), plus " · 2 konton" when `accountCount > 1`; second line "Betalning" body-medium muted. Right: date with the pending marker. +* Semantics: "Anna betalade dig 800,00 kronor, 2 konton, 6 oktober". + +**`SyncPendingMarker(size = 15.dp)`**: `cloud_upload` in `pending`, `contentDescription` "Väntar på synk" when standalone; merged into the row sentence otherwise. + +### 3.6 Balance, sync and banners + +**`BalanceHero(state: HeroState, onSettle)`** (S-4; seesaw) +* Card, radius 28, padding 16 top / 20 sides / 20 bottom, content centred. +* **Seesaw** (decorative), 250 × 92 dp: plank 220 × 8 (radius 4) pivoting at its centre on a fulcrum triangle 36 × 22 in `outline-variant`; the viewer's avatar (46, Sunny) on the left end, the partner's on the right end, 88 dp from the centre; name labels "Du" and "Anna" (12 sp weight 600, `on-surface-variant`, capped 1.15×) outside each avatar. **The side of the person who owes sits lower**: tilt 9°; plank colour `owing` (viewer owes), `owed` (partner owes), `settled` (level, 0°), `outline` (waiting, 12° down on the viewer's side). +* Below: wording (title-medium, `owed`/`owing`), amount (`amount-hero` + "kr" at 24 sp 700, same colour), then "Gör upp" (filled, small 40 dp, filled `handshake` icon; `home_settle_up`) 14 dp below. +* States: + +| State | Content | +|---|---| +| `Owed(net, partner)` | "Anna är skyldig dig", amount in `owed`, plank tilted toward the partner, "Gör upp" | +| `Owing(net, partner)` | "Du är skyldig Anna", amount in `owing`, plank tilted toward the viewer, "Gör upp" | +| `Settled` | card fill `settled-container`, text `on-settled-container`, bunting along the top (30 dp high: a curved `outline` line with 14 pennants in category colours green, orange, yellow, blue, teal, purple, red, pink, lime, orange, green, blue, yellow, pink), top padding 28, level plank, "Ni är kvitt" (headline-medium), "Ingen är skyldig något just nu." (body-medium); no amount, no button | +| `Waiting` (S-3) | partner seat = `AvatarPlaceholder`, label "Partner", plank `outline`; content per S-3 | +| `Loading` (S-4 first sync) | no seesaw: `LoadingIndicator` 56 dp in a Cookie9 `primary-container` tile, "Hämtar era utgifter…" (title-medium), "Första synken hämtar allt som finns på servern. Det tar bara en stund." (body-medium muted); padding 28 / 20 | + +* Long: a 7-digit amount ("1 234 567,89 kr") fits one line at 100 %; at 200 % the hero amount grows about 1.5× and the unit wraps to its own line; wording wraps, never ellipsizes. +* Semantics: the card is one node, `contentDescription` = "Du är skyldig Anna 3 250,00 kronor" / "Anna är skyldig dig …" / "Ni är kvitt"; the button is a separate node "Gör upp". + +**`AccountBalanceHeader(detail: AccountDetailState, colorIndex: Int, onSettle, onUnarchive)`** (S-5) +* Card, radius 28, padding 18 / 20 / 20, gap 10, fill `account-header-n` (§2.2). +* Row: `AccountTile` 40 · "50 / 50 · Jacob och Anna" (title-medium; default shares in percent, viewer first, `account_default_split`) with a thin `SplitBar` (8 dp) below · "Arkiverat" badge when archived. +* Balance: wording (title-medium) above `MoneyText(Large)`, both in `owed`/`owing`; zero: `check_circle` 28 + "Kvitt" in headline-small `settled`. +* Actions, left-aligned: "Gör upp det här kontot" (filled, `handshake`) when `netOre != 0` (archived accounts too: they can still be settled, AC-6.4); for an archived account also "Återställ från arkiv" (tonal, `unarchive`), placed first. +* Semantics: one node "Vardag, standarddelning 50 procent du, 50 procent Anna, Anna är skyldig dig 1 184,50 kronor"; button separate. + +**`SyncStatusPill(summary: SyncSummary, online: Boolean, onClick)`** (S-4) +* Centred pill, min height 40 (48 target), padding 6 / 10 / 6 / 14, radius full, body 13 sp weight 500; leading icon 18, trailing `chevron_right` 18. +* Variants: synced: `surface-container-high` fill, `cloud_done` (filled) in `owed`, "Synkad för 3 min sedan" (`sync_synced_relative`, §9.8 relative time, refreshed every minute) · running: `sync` icon, "Synkar…" · pending: suffix " · 2 ändringar väntar på synk" (`sync_pending`, plural) · offline: `sync-pill-pending` fill, `cloud_off` in `pending`, "Offline · 2 ändringar väntar på synk" (or "Offline" alone) · warning: `warning-container` fill, `warning` icon, "1 ändring synkades inte" (`sync_rejected`, plural) or "Telefonens klocka går fel" (`sync_clock_wrong_short`) · failed: "Synk misslyckades · försök igen om en stund" in the warning style. +* Never a blocking banner. Whole pill is one button: TalkBack "Synkstatus: ". + +**`Banner(kind: Lantern | Error | Warning | Neutral, icon, title, body, actions)`**: radius 24, padding 14 / 16 / 10, gap 6; icon 24 (filled) + title (title-small) + body (body-medium); actions right-aligned, text buttons in the banner's content colour. Lantern = `tertiary-container`, Error = `error-container`, Warning = `warning-container`, Neutral = `surface-container-high`. `role` status (alert for Error). + +**`UpdateBanner(state: UpdateState, onUpdate, onLater, onRetry)`** (S-4, S-22): `Available`: Lantern, `system_update`, "Version 1.2 finns", "Nyheter och rättningar · 8,4 MB" (size from `UpdateInfo.sizeBytes`), actions "Senare" (text) and "Uppdatera" (tertiary filled, `download`); offline: "Uppdatera" disabled and the body reads "Kräver anslutning". `Downloading`: Lantern, `downloading`, "Laddar ner… 64 %", `LinearWavyProgressIndicator` in `tertiary` (determinate, track `tertiary` at 28 %), no actions; TalkBack progress "Laddar ner version 1.2, 64 procent". `Failed`: Error, `error`, "Nedladdningen misslyckades. Försök igen." with "Senare" and "Försök igen" (error filled, `refresh`). `NeedsInstallPermission`: Available look; "Uppdatera" opens the S-17 dialog. + +**`NeedsConnectionBanner(body: String)`**: Warning banner, `cloud_off`, title "Kräver anslutning", screen-specific body. **`NeedsConnectionHint`**: supporting line under a control: `cloud_off` 16 + "Kräver anslutning" in body-small weight 600 `on-surface-variant`, padding 4 / 16. + +### 3.7 Expense editor fields (D-019; used by S-7 and S-19) + +**`AmountDisplay(text: String, focused: Boolean, error: String?)`** +* Centred column, padding 10 top / 6 bottom: label "Belopp" (label-medium muted; `error` colour on error) above the amount (`amount-hero`, tabular) with a 3 dp `primary` caret when focused, then "kr" (headline-small muted) on the same baseline; wraps the unit under the number when it does not fit. +* Empty shows "0,00" in `on-surface-variant` as a hint, not a value. +* The text is what the user typed, grouped with U+00A0 thousands separators while typing ("1 060", "1 060,5"); the decimal separator is the locale's (`,` sv, `.` en). +* Semantics: "Belopp, 412 kronor" (empty: "Belopp, 0,00", the hint `editor_amount_hint`); error appended. Tapping it focuses it and shows the keypad. + +**`AmountKeypad(onKey, onDelete, onClear, compact: Boolean)`** +* Sheet at the bottom of S-7 and S-19: `surface-container`, top corners 28, padding 12 / 12 and bottom = 8 + navigation-bar inset; 3-column grid, gap 8. +* Keys `1`–`9`, decimal key, `0`, delete. Each key: min height 52 (46 in `compact`, used while the split section is open), radius full, `surface-container-highest`, label 24 sp weight 600 (capped 1.5×). Delete key: `primary-container` fill, `backspace` 26 in `on-primary-container`. +* Decimal key shows `,` (sv) or `.` (en); TalkBack "Decimaltecken". Delete: TalkBack "Radera" (`editor_keypad_delete`); long-press or the TalkBack custom action "Rensa" clears the field. +* Input rules per §9.6, enforced key by key: digits; one decimal separator (a second is ignored); at most two decimals (a third digit is ignored); no sign; a value above 10 000 000,00 kr is not accepted (the key is ignored with a reject haptic); a leading `0` followed by a digit replaces the `0`; decimal first gives "0,". Parsing to öre is string-exact (never floating point). +* Each key gives `HapticFeedbackType.KeyboardTap`. The keypad drives the editor's money and percent fields: the amount, the exact-share field and the two ratio percent fields (percent: max 100, two decimals). +* Visibility: shown while a keypad field has focus; hidden while the description or notes field has focus (the system keyboard shows instead) or nothing has focus. Back with the keypad visible clears focus and hides it (it does not close the editor). +* 200 %: keys grow with their labels; the keypad keeps its four rows and the content above scrolls. + +**`DescriptionField`**: filled text field (radius 16, `surface-container-highest`, no indicator, 2 dp `primary` inset outline when focused, 2 dp `error` on error), label "Beskrivning", single line, 1–100 code points, capitalisation `Sentences`, IME action `Done` (§4 S-7). Error supporting text with filled `error` icon. + +**`CategoryQuickRow(options, selected: CategoryId, onPick, onAll)`** (UX-3) +* A 6-column grid, gap 4: five category items and "Alla". Item: `CategoryTile` 52 (Container style; Solid when selected) above a label (11 / 14 sp weight 500 `on-surface-variant`, up to 2 lines, centred, no ellipsis; selected: `on-surface` weight 700). +* Selected tile: solid, plus a check badge: 20 dp circle in `on-surface` with `check` (14, filled, weight 700) in `surface`, offset to the tile's top-right (−6 / −8 dp), with a 2 dp `surface` ring. +* "Alla": tile in `surface-container-highest` with `apps` 26 in `on-surface-variant`, label "Alla"; opens `CategoryGridSheet`. TalkBack "Alla kategorier". +* **Which five**: the five active categories used most often in the selected account (count of non-deleted expenses on the phone, ties by `sort_order`), filled up from `sort_order` when fewer than five have been used; shown in that order. If the selected category (suggested, default or picked in the sheet) is not among them, it takes the fifth place. The order does not change while the editor is open except for that fifth place. The usage list is `ExpenseEditorOptions.frequentCategoryIds` (UX-9, pending); until it exists the five are the selected category followed by the first active categories in `sort_order`. +* 200 % (font scale ≥ 1.5): 3 columns, two rows, row gap 12. +* Semantics: radio group "Kategori"; each item "Nöje, vald" / "Mat & hushåll"; picking sets the editor's "picked by hand" flag (CS-2). + +**`CategoryGridSheet`**: modal bottom sheet "Välj kategori": all active categories in `sort_order`, 4 columns (3 at ≥ 1.5×), items as in the quick row; tapping picks and closes the sheet. + +**`EditorChip`** (the chips row under the category row): `FlowRow`, gap 6. Chip: min height 36 (48 target), radius full, `surface-container-high`, padding 4 / 10 / 4 / 8, label 14 / 18 sp weight 500 with an optional key prefix in `on-surface-variant` weight 600 ("Betalt av ", "Delning "), leading 18 dp element, trailing 18 dp icon. Selected (the split section is open): `secondary-container`. Chips: + +| Chip | Leading | Label | Trailing | Tap | +|---|---|---|---|---| +| Konto | 12 dp dot in the account's chart colour | "Vardag" | `expand_more` | opens `AccountPickerSheet` | +| Datum (S-7 only) | `event` | "Idag" / "Igår" / "14 okt." (§9.8 list format) | `expand_more` | Material `DatePickerDialog`, range per §9.6 | +| Betalt av | payer avatar 24 | "Betalt av Du" / "Betalt av Anna" | `swap_horiz` | toggles between the two users (AC-8.3), no picker | +| Delning | `call_split` | "Delning Kontots standard 50/50" / "Egen fördelning 70/30" / "Exakta belopp" / "Allt på Anna" | `expand_more` / `expand_less` | opens or closes `SplitSection` in place and scrolls it into view | + +TalkBack: "Konto: Vardag. Dubbeltryck för att byta konto"; "Datum: idag"; "Betalt av: du. Dubbeltryck för att byta till Anna" (`editor_paid_by_toggle`); "Delning: kontots standard 50/50". + +**`AccountPickerSheet`**: modal bottom sheet "Välj konto": active accounts in created order (AC-6.4: archived not offered), each row `AccountTile` 40 + name + the account's wording and amount (body-small, coloured), check on the selected one; tap picks and closes. + +**`SplitBar(viewerPercent: Float, thin: Boolean, error: Boolean, knob: Boolean)`**: two rounded segments (radius full) separated by a 3 dp gap (6 dp with a knob): left = viewer in the viewer's avatar colour, right = partner in the partner's avatar colour, widths proportional; height 16 (8 thin); `error`: left segment `error`. Decorative next to printed numbers; otherwise `contentDescription` "Du 60 procent, Anna 40 procent". + +**`SplitSlider`** (S-6, ratio mode): `SplitBar` 16 dp with a knob (4 × 44 dp, radius 2, `on-surface`) at the split point; drag in steps of 1 percentage point with a haptic tick at 50; semantics slider labelled with the left person's share ("Jacobs andel", `editor_ratio_slider`) and value text "Jacob 60 procent, Anna 40 procent" (`editor_ratio_spoken`) and ± 1 adjust actions. + +**`SplitPreview(shares: List?)`**: a 72 dp thin `SplitBar` then body-medium "Du **206,00 kr** · Anna **206,00 kr**" (amounts weight 700, tabular). Data only from `ExpenseRepository.preview` (the UI never computes shares); hidden while it returns null. Semantics "Du 206,00 kronor, Anna 206,00 kronor". + +**`SplitSection(mode, …)`**: flat section (`surface-container-low`, radius 28, padding 16, gap 12): +* Header row: "Delning" (title-small) + `expand_less` icon button (TalkBack "Fäll ihop delning"). +* Mode chips: four single-select filter chips, wrapping: "Kontots standard 50/50" (live ratio of the selected account, `editor_split_mode_default`), "Egen fördelning", "Exakta belopp", "Allt på en person"; selected = `secondary-container` with `check`. Semantics radio group "Delning". +* `default`: `SplitBar` 16 + `SplitPreview` text. +* `ratio` (`RatioEditor`): the two names with avatars 32 at either end, `SplitSlider`, two percent fields side by side ("Jacob" / "Anna", suffix "%", keypad-driven, two decimals, typing one sets the other to 100 minus it), presets as a `ButtonGroup` "40/60 · 50/50 · 60/40" (viewer first), then `SplitPreview`. +* `exact` (`ExactSplitEditor`): one row per person: avatar 40 + money field (label = name, suffix "kr", value `amount-medium`). The viewer's field is typed by default; the other is read-only (transparent fill, 1 dp `outline-variant` border) showing "beloppet minus …" as the computed value, or "—" while it cannot be computed, with trailing caption "räknas ut". Tapping the read-only field makes it the typed one (AC-8.7: one typed, the other derived). Typed share > amount: field in error with "Andelen är större än beloppet" (`error_exact_share_too_large`) and an `error-container` box "Jacob 1 200,00 kr av 1 060,00 kr" / "140,00 kr för mycket" (title-small); Spara disabled. +* `full` (`FullSplitPicker`): label "Vem står för hela beloppet?" (label-large muted), a two-button `ButtonGroup` (min height 52; avatar 28 + name each), `SplitBar` 16 fully in the bearer's colour, `SplitPreview`. + +**`NotesField`**: collapsed as a text button "Lägg till anteckning" (`notes` icon); expanded: filled multi-line field "Anteckningar", 3–8 visible lines, 0–1000 code points, newlines allowed, counter "120 / 1000" as supporting text. + +**`CompactEditorSummary`**: when the `AmountDisplay` has scrolled out of view, a row pinned under the top app bar: `CategoryTile` 36 + description (title-medium, ellipsis) over the category name (body-small muted) + `MoneyText(Medium)`; padding 6 / 20 / 10; 1 dp `outline-variant` bottom border. Tap scrolls to the top and focuses the amount. + +### 3.8 Settle up components (S-9, S-10) + +**`DirectionCard(payer, recipient, onSwap)`**: card, padding 14 / 14 / 14 / 18, gap 12: payer avatar 40 (Sunny) → `arrow_forward` (on-surface-variant) → recipient avatar 40; label "Riktning" (label-medium muted) over "Du betalar Anna" / "Anna betalar dig" (title-medium); trailing tonal icon button `swap_horiz` (TalkBack "Byt riktning", `settle_swap`). Semantics: card text "Riktning: du betalar Anna" + the button. + +**`AllocationRow(line: AllocationLine, colorIndex, fixed: Boolean, onAmount, onRemove)`**: `SegmentedGroup` item, padding 12 / 14, gap 8: +* Line 1: `AccountTile` 40 · name (title-medium, ellipsis) over the payer's position in body-small muted: "Skuld 3 084,50 kr" (the payer owes there, `settle_row_debt`); otherwise the account balance wording from the payer's side, "Anna är skyldig dig 1 184,50 kr" (`balance_*_amount`), or "Kvitt" (`balance_account_settled`) · amount field (148 dp wide, min height 48, radius 14, `surface-container-highest`, right-aligned `amount-medium` + "kr"; system decimal keyboard) · remove icon button `close` (TalkBack "Ta bort Huset från fördelningen", `settle_row_remove`). `fixed`: the amount is plain `MoneyText(Medium)` (it follows the settlement amount) and there is no remove button. +* Over-settle (`overSettles`): field border 2 dp `warning`; under line 1 a strip (`warning-container`, radius 12, padding 8 / 12): filled `warning` 20 + "Det här reglerar mer än som är skyldigt i Japan 2027" (`settle_over_settle_warning`). +* 200 %: name and position on line 1, the amount field full width on line 2, the remove button at the end of line 1. + +**`RemainingIndicator(remainingOre)`**: row, radius 16, padding 10 / 14, gap 10: zero → `surface-container-high`, filled `check_circle` in `owed`, "Kvar att fördela" + "0,00 kr"; positive → `warning-container`, filled `error`, "Kvar att fördela" + "250,00 kr"; negative → `warning-container`, filled `error`, "Kvar att fördela" + "250,00 kr för mycket" (`common_too_much`). Words always say which; semantics "Kvar att fördela 250,00 kronor". Live region polite. + +### 3.9 Timeline (S-8, S-10) + +**`Timeline(lines: List)`**: header "Historik" (title-small, `timeline_title`) then a card (padding 16 / 16 / 16 / 12) with items oldest first, newest last (AC-12.4): +* Item: a 24 dp gutter with a 12 dp dot in `secondary` and a 2 dp `outline-variant` connector to the next item; text body-medium. Sentence per `FieldChange`: "Anna skapade den här · 16 okt. 14:02"; "Du ändrade beloppet från 1 147,60 kr till 1 247,60 kr · 16 okt. 19:40"; "Anna ändrade kategorin från Övrigt till Mat & hushåll · 16 okt. 20:05"; "Anna tog bort den här · 17 okt. 09:12"; recurring source "Skapad automatiskt (återkommande) · 25 sep. 06:00"; import source "Importerad från Splitwise · …". Several changes in one entry are one line each with the same time. Values formatted with `MoneyFormatter`/`DateFormatter`; field names from `timeline_field_*`. +* `pendingLocal`: hollow dot (2 dp `pending` ring), text in `pending` weight 600 with a 16 dp `cloud_upload` before it: "Din ändring väntar på synk" (AC-12.5); always last. +* Semantics: each item one node; the header is a heading. + +### 3.10 Charts (S-11; Vico or the Canvas fallback, §6.2) + +**`ChartCard(title, content)`**: card, padding 18 / 16 / 16, title title-medium; an invisible summary sentence as `contentDescription` of the chart area. + +**`StackedBarChart(buckets, groups, onBucket)`** +* Plot 170 dp high, y-axis gutter 44 dp on the left: gridlines (1 dp `chart-grid`) at 3–4 round values with labels in whole kronor (10 / 12 sp, `chart-axis-label`, sv "15 000", en "15,000"); x labels under each bar (11 / 14 sp: "maj", "jun" or the week number "41" with the caption "Vecka" under the axis). +* One bar per bucket (every bucket of the range, empty ones as zero, AC-14.7), gap 10, top corners 10, bottom 4; segments stacked in legend order with 2 dp gaps; segment colour per group (category colour or account chart colour). +* More buckets than fit at a minimum bar width of 16 dp: horizontal scroll, scrolled to the newest bucket on first show. At 200 %, x labels are drawn for every second bucket when they would overlap. +* Tap a bar → `onBucket(key)` (S-12). Each bar is a focusable semantics node: "September, 41 484 kronor, Resor största kategori". +* Chart summary (TalkBack): "Utgifter över tid, : högst i september med 41 484 kronor, lägst i maj med 24 710 kronor" (`analytics_summary_over_time`). + +**`ChartLegend(groups)`**: wrapping row, gap 4 / 12; 10 dp swatch (radius 3) + label (12 / 16 sp weight 500 `on-surface-variant`). Only groups with data. Decorative for TalkBack (the bars speak their totals). + +**`BreakdownList(rows, total, onRow)`** (AC-14.6 charts 2 and 3): rows sorted by amount descending, padding 8 / 0, gap 6: tile 32 (`CategoryTile` or `AccountTile`) + name (title-small, 1 line, ellipsis; "Arkiverat" badge for archived accounts) + right column `MoneyText(Small)` over the percent (body-small muted, one decimal, half-up, "34,8 %", AC-14.10); below, a 10 dp bar track (`surface-container-highest`, radius full) indented 44 dp, filled to amount / largest amount in the group colour. Row is one node "Boende, 62 000 kronor, 34,8 procent"; click → `onRow(id)`. + +**`AnalyticsFilterBar(filter, onChange)`**: row 1: period chip (`date_range`, label "Senaste 6 mån" / "Anpassad · 1–30 nov.", `expand_more`; TalkBack "Period: senaste 6 månaderna") + `ButtonGroup` "Månad | Vecka" (min width 170, fills the rest); row 2: `ButtonGroup` "Hushållet | Min del" and `ButtonGroup` "Kategori | Konto", half width each. Connected buttons: min height 40, label-large weight 600, unselected `surface-container-high` / `on-surface-variant`, selected `secondary` / `on-secondary` with full radius. 200 %: one control per row. Semantics: each group a radio group named "Indelning", "Mått", "Gruppera efter". + +**`TotalCard(totalOre, caption)`**: card, padding 16 / 20: "Totalt" (label-large muted; "Totalt · Min del" for my share) over `MoneyText(Large)` over the range caption (body-small muted, "maj–okt 2026 · Hushållet"). + +### 3.11 Scaffolding and common controls + +**`KvittScaffold`**: edge-to-edge; top app bar slot, content, optional FAB, optional bottom bar, snackbar host above the FAB and navigation bar; status-bar inset handled by the app bar. + +**`KvittTopAppBar(title, navigation: Back | Close | None, actions)`**: 64 dp, `surface` (scrolled: `surface-container`), navigation icon button 48 (`arrow_back` TalkBack "Tillbaka" / `close` TalkBack "Avbryt"), title title-large 1 line ellipsis (pad 16 when there is no navigation icon), optional subtitle body-small muted. Top destinations (S-4) show the wordmark instead of a title: a 22 dp Sunny `primary` dot with a filled `receipt_long` (15) in `on-primary`, then "Kvitt" 24 sp weight 700 `primary` (capped 1.5×); semantics heading "Kvitt". + +**`KvittNavigationBar(selected)`**: Expressive `ShortNavigationBar` (fallback `NavigationBar`), `surface-container`, items 64 dp high: indicator pill 56 × 32 (`primary-container` when selected) with the icon (filled when selected), label label-medium (selected: `on-surface` weight 700) capped at 1.6×. Items: "Översikt" `home`, "Statistik" `bar_chart`, "Inställningar" `settings` (`nav_*`). Labels always visible. + +**`AddExpenseFab(expanded: Boolean)`**: medium extended FAB, 56 dp, radius 20, `primary` / `on-primary`, `add` 24 + "Lägg till utgift" (16 / 22 sp weight 600), 16 dp from the right edge and above the navigation bar; collapses to the 56 dp icon-only FAB while the list scrolls down and at font scale ≥ 1.5; TalkBack "Lägg till utgift" in both forms. Same component with "Ny återkommande" on S-19. + +**Buttons**: radius full, label-large weight 600, icon 20. Small 40 dp (padding 8 / 20), medium 56 dp (padding 12 / 26, 16 sp, icon 24). Kinds: filled (`primary`), tonal (`secondary-container`), tertiary (`tertiary`), outlined (1 dp `outline-variant`, `on-surface-variant`), text (`primary`), danger text (`error`), error filled (`error` / `on-error`). + +**Badge**: radius full, padding 2 / 10, label-small weight 600; neutral (`surface-container-highest` / `on-surface-variant`), lantern (`tertiary-container`), pending (`pending-badge` / `pending`). Icon 14. + +**`SectionHeader(title, action)`**: min height 48, padding 8 / 4 / 0 / 8, title title-large (heading semantics), optional trailing text button. + +**`CollapsibleRow(title, count, expanded)`**: min height 48, padding 6 / 8, `inventory_2` + "Arkiverade (1)" (title-small) + `expand_more`/`expand_less`; semantics button with state "utfälld"/"hopfälld". + +**`FactRow(icon, label, value)`** (detail facts in S-8, S-10, S-22): `SegmentedGroup` item, min height 56, padding 10 / 16, gap 14: outline icon in `on-surface-variant`, label (label-medium muted) over value (body-large). Merged node "Datum: fredag 16 oktober 2026". + +**`SettingsRow(icon, color: ColorToken, title, subtitle, enabled)`**: `SegmentedGroup` item, min height 56: tile 40 (Cookie4, `cat--container` fill, icon 22 in `cat-`), title (title-medium) over subtitle (body-small muted), trailing `chevron_right`. Merged node, role button. + +**`EmptyState(illustration, title, body, action)`**: flat section (`surface-container-low`, radius 28, padding 28 / 24), centred, gap 10: decorative illustration (three rotated category tiles, ~170 × 110 dp), title headline-small, body body-large muted, optional medium button. + +**`ConfirmDialog(icon, title, body, confirm, dismiss, destructive)`**: `surface-container-high`, radius 28, padding 24, gap 14, 24 dp from the screen sides; centred 52–56 dp Sunny icon tile (destructive: `error-container` / `on-error-container`; otherwise `secondary-container` or `tertiary-container`); title headline-small centred; body body-medium muted; buttons right-aligned (dismiss text, confirm filled or error filled). Scrim 38 %. + +**`KvittMenu`**: `surface-container`, radius 16, padding 6, min width 244, max 300, level 3. Items min height 48, padding 12, radius 12, icon + body-large; a disabled item shows its reason as a second line (body-small) at 50 % opacity and is still announced with that reason. + +**`Snackbar`**: `inverse-surface`, text `inverse-on-surface`, action `inverse-primary`, radius 16; 4 s without an action, 10 s with one. + +**`InviteCodeDisplay(code)`**: three groups of four on `surface` chips (radius 14, padding 6 / 10), 24 / 32 sp weight 700, letter spacing 1.5, wrapping centred; selectable. TalkBack spells it per group: "7 K 3 M, Q X 9 D, 2 H P A". + +**`InviteCodeField`**: filled field "Inbjudningskod", value title-large, uppercase, inserts the hyphens and normalises per §8.3 while typing (`O` → `0`, `I`/`L` → `1`), keyboard `Ascii` with `Characters` capitalisation; trailing "Klistra in" text button (`content_paste`), which also accepts a full invite link and fills the server from it. + +**`Skeleton`**: `surface-container-highest` blocks, radius 12 (pills full), no shimmer under reduced motion; only for the first-sync and online-fetch loading states. + +--- + +## 4. Screens + +Each screen: layout top to bottom, the **binding states** (exactly the frames of the screen catalogue; PNG `screens/-light.png` and `-dark.png`, "light only" where noted), interactions, TalkBack, and 200 %. Modules and navigation are in `ia.md`. + +### S-1 Pairing (`:feature:onboarding`) + +**Layout** (no app bar, no navigation; scrolling column, gutter 16, gap 14; bottom bar with the button): +1. Brand block, centred, padding 28 / 0 / 12: app mark 88 dp (Sunny, `primary`, filled `receipt_long` 44 in `on-primary`), "Kvitt" (display-small, `primary`), "Delade utgifter för två" (title-medium muted, `common_tagline`). +2. `InviteCodeField` with "Klistra in". +3. "Ditt namn" field (UX-4; 1–40, §9.6; capitalisation `Words`), required on a first pairing. +4. Help line: `info` 20 + body-medium muted "Koden får du av Jacob: från en länk eller från Inställningar › Bjud in partner." (`onboarding_code_help`; the partner's own name is unknown before pairing, so the generic `onboarding_code_help_generic` is used when the app was not opened from a link). +5. Server row, collapsed (`surface-container-low`, radius 16, min height 56): `dns` + "Serveradress" (label-medium muted) over the URL (body-medium, wraps anywhere) + `expand_more`; tap expands it into an editable field (keyboard `Uri`). It opens expanded when a link carried a server different from the release default. +6. Bottom bar, padding 12 / 16: "Para ihop" (filled medium 56, full width, `link` icon). + +**States** + +| Frame | State | What differs | +|---|---|---| +| `S-1-pairing` | loaded | opened from Anna's invite link: code "7K3M-QX9D-2HPA" and server filled, "Ditt namn" = "Anna", nothing focused, "Para ihop" enabled | +| `S-1-pairing-loading` | loading | button shows a 20 dp progress indicator in `on-primary` and "Parar ihop…" (`onboarding_pairing`) and is not clickable; all fields read-only (transparent fill, 1 dp `outline-variant` border); "Klistra in" hidden | +| `S-1-pairing-error` | error | `invite_invalid`: the code field in error with "Koden fungerar inte. Den kan ha gått ut eller redan använts. Be om en ny." (`onboarding_error_invite_invalid`) under it; button enabled again | +| `S-1-pairing-again` | variant Unrecognised (§14.4.8; viewer Jacob) | the tagline is replaced by "Para ihop telefonen igen" (headline-small) and "Dina utgifter finns kvar på telefonen." (body-large muted, `onboarding_again_*`); no "Ditt namn" field; generic help line | + +**Interactions** +* Code typing: uppercase, hyphens inserted after 4 and 8, normalised per §8.3; "Para ihop" enabled when the code has 12 characters, the name is non-blank (first pairing) and the server is a valid URL. IME `Next` from code to name, `Done` on the name runs "Para ihop". +* "Klistra in": reads the clipboard; a 12-character code or an invite link (`kvitt://invite?…` or `https://…/i/`) fills code (and server). +* Other errors, all inline under the field at fault, button enabled again: network "Kunde inte nå servern. Kontrollera adressen och anslutningen." (under the server row, which expands); `rate_limited` "För många försök. Vänta en stund och försök igen." (under the code); `http://` in release (AC-2.8) "Adressen måste börja med https://" (server). Offline: the button stays enabled and gives the network error (pairing never queues). +* Success: `pair(serverUrl, code, displayName, deviceName = Build.MODEL)` → partner pairing goes to S-2 (AC-2.10), otherwise S-4 in its loading state; after the first full sync the system notification-permission prompt (AC-2.9), preceded by nothing (the rationale string `home_notification_rationale` is used only if Android asks for a rationale). + +**TalkBack**: the paste button reads its text "Klistra in" (`onboarding_code_paste`); the code field reads the value group by group; the server row "Serveradress, https://icehack.asuscomm.com:8443, dubbeltryck för att ändra"; errors are announced (live region assertive). + +**200 %**: single column scrolls; the code value wraps after a group; the brand block's title grows ~1.5×; the bottom button stays pinned. + +### S-2 Partner profile step (`:feature:onboarding`) + +**Layout** (app bar without title or navigation; column gap 12; bottom bar): +1. Centred intro, padding 24 / 4 / 4: Jacob's avatar 72 (Sunny), "Du går med i Jacobs hushåll" (headline-small, `onboarding_profile_joining`), "Så här ser Jacob dig i Kvitt. Du kan ändra det senare under Inställningar › Profil." (body-large muted). +2. Card, padding 22 / 20, centred, gap 14: own avatar 112 (Sunny; initials on the own slot colour until a picture exists) with a 40 dp `primary` circle badge holding `add_a_photo` 20 at its bottom-right (3 dp `card` ring); "Lägg till bild" tonal button (`photo_library`; "Byt bild" when a picture exists, plus a text button "Ta bort bild"); "Ditt namn" field prefilled from the pairing call. +3. Bottom bar: "Fortsätt" (filled medium, full width). + +**States** + +| Frame | State | What differs | +|---|---|---| +| `S-2-profile-step` | loaded | Anna's initials, name "Anna" focused | +| `S-2-profile-step-loading` | loading | a picture was picked and is uploading: the avatar shows it at 60 % with the progress ring (§3.3); "Byt bild" and "Fortsätt" disabled until the upload ends | +| `S-2-profile-step-error` | error | upload refused or network: snackbar "Bilden kunde inte användas" (`profile_picture_failed`, with the reason for `payload_too_large` / `unsupported_media_type` / `image_invalid`, or "Kunde inte nå servern"); avatar back to initials; "Fortsätt" enabled | + +**Interactions**: "Lägg till bild" opens the Android photo picker (images only, AC-4.3); upload starts immediately (`setPicture`). "Fortsätt" saves the name if changed (`setDisplayName`; failure → snackbar, stays) and goes to S-4; blank name → inline "Skriv ett namn" and "Fortsätt" disabled. Back is not offered (the pairing is done). + +**TalkBack**: the avatar badge is part of the button "Lägg till bild"; avatar while uploading "Laddar upp bilden". + +**200 %**: column scrolls, avatar stays 112. + +### S-3 Waiting for partner (`:feature:onboarding`, shown in the Home destination) + +**Layout** (wordmark app bar; bottom navigation with Översikt selected; no FAB): +1. Hero card in the `Waiting` state (§3.6), padding 28 / 22 / 24: seesaw with the empty seat, "Väntar på din partner" (headline-small), "Kvitt behöver er båda innan ni kan börja. Bjud in din partner med en länk." (body-large muted), "Bjud in partner" (filled medium, `person_add`). +2. When an invite is pending: a one-item group: `schedule` + "Inbjudan skickad" (title-small) over "Inbjudan gäller till mån 19 okt. 21:40" (`onboarding_waiting_invite_expires`). +3. Body-medium muted line: "Konton, utgifter och betalningar blir tillgängliga när din partner har gått med." + +**States** + +| Frame | State | What differs | +|---|---|---| +| `S-3-waiting` | loaded | pending invite row shown | +| `S-3-waiting-offline` | offline | "Bjud in partner" disabled with `NeedsConnectionHint` "Kräver anslutning" under it; the pending invite row is not shown (invites are online only) | + +**Interactions**: "Bjud in partner" → S-21 (it creates a `partner` invite there). When the partner joins, the next sync replaces this content with S-4 (empty). Statistik shows its empty state; Inställningar works. + +**TalkBack**: hero one node "Väntar på din partner. Kvitt behöver er båda innan ni kan börja."; the seesaw is decorative. + +### S-4 Home (`:feature:home`) + +**Layout** (wordmark app bar; column gap 12, bottom padding 120; bottom navigation; FAB): +1. `UpdateBanner` when `HomeState.update` / `UpdateState` is not `None` (S-17). +2. `BalanceHero` from `HomeState.global`. +3. `SyncStatusPill` from `HomeState.sync` and connectivity. +4. `SectionHeader` "Konton" with "Nytt konto" (text button, `add`). +5. `AccountCard` per active account, created order (AC-6.6). +6. `CollapsibleRow` "Arkiverade (n)" when archived accounts exist; expanded in place to their `AccountCard`s (AC-6.4); collapsed by default, state kept for the session. +7. `AddExpenseFab`. + +**States** + +| Frame | State | What differs | +|---|---|---| +| `S-4-home` | loaded | "Du är skyldig Anna 3 250,00 kr"; Vardag, Huset, Japan 2027; "Arkiverade (1)" collapsed | +| `S-4-home-settled` | variant settled + archived expanded | hero `Settled` with bunting, accounts "Kvitt", archived Gotland 2026 visible with badge; no "Gör upp" | +| `S-4-home-empty` | empty | partner joined, no accounts: a card with both avatars (44, overlapping by 10) "Du och Anna är med" / "Hushållet är klart. Nu behövs ett konto."; then `EmptyState` (pile of three solid tiles: groceries, travel, restaurants) "Skapa ert första konto", "Ett konto samlar utgifter som ni delar på samma sätt, till exempel Vardag, Huset eller en resa. Ni kan ha hur många ni vill.", button "Nytt konto" (medium, `add`); no sync pill, no FAB (AC-8.6 needs an account) | +| `S-4-home-loading` | loading (first sync only) | hero `Loading`, a 40 × 200 dp pill skeleton, "Konton" header without action, three skeleton account cards; no FAB | +| `S-4-home-offline` | offline + update banner | pill "Offline · 2 ändringar väntar på synk"; "Nytt konto" disabled with "Kräver anslutning" as its supporting text; banner `Available` with "Uppdatera" disabled ("Kräver anslutning") | +| `S-4-home-200` | long, 200 %, light only | app bar `surface-container` (scrolled 90 dp), hero amount ~1.5× with "kr" on its own line, account amounts under the names, FAB icon-only | +| `S-4-home-en` | English, light only | "You owe Anna", "SEK 3,250.00" (unit before the number), "Synced 3 min ago", "Accounts", "New account", "Add expense" | + +**Interactions** +* FAB → S-7 new (account per AC-8.6). Hidden while no active account exists (only archived accounts: hidden too, UX-6). +* "Gör upp" → S-9 global. Account card → S-5. "Nytt konto" → S-6 (offline: disabled). Pill → S-16. Banner per S-17. +* Returning from S-7 shows the S-7 snackbar here; from S-9 "Betalningen sparades" (`snackbar_payment_saved`). +* After the first full sync after pairing, the notification permission is asked once (AC-2.9). +* Pull-to-refresh is not offered (sync runs on its own triggers, AC-15.3). + +**TalkBack**: hero as §3.6; pill as §3.6; account cards as §3.4; "Arkiverade (1), hopfälld"; FAB "Lägg till utgift"; navigation items "Översikt, flik 1 av 3, vald". + +**200 %**: see `S-4-home-200`; every element reflows, nothing truncates except account names (two lines then ellipsis). + +### S-5 Account detail (`:feature:account`) + +**Layout** (app bar: back, account name, `more_vert`; column gap 12; FAB): +1. `AccountBalanceHeader`. +2. Ledger: per month a `LedgerMonthHeader` and a `SegmentedGroup` of `ExpenseRow` / `SettlementRow` in AC-8.10 order; settlement amounts are the allocation to this account (AC-11.7). +3. `AddExpenseFab` (account preset to this one); hidden for archived accounts (UX-6). + +Overflow menu (`KvittMenu`): "Ändra konto" (`edit`) → S-6; "Arkivera" (`inventory_2`) or "Återställ från arkiv" (`unarchive`); "Ta bort konto" (`delete`) enabled only when `deletable`, otherwise disabled with "Kontot kan inte tas bort så länge det har utgifter, betalningar eller återkommande utgifter" (`account_delete_blocked`). + +**States** + +| Frame | State | What differs | +|---|---|---| +| `S-5-account` | loaded | Vardag: "Anna är skyldig dig 1 184,50 kr", October and September groups, the SL-kort row pending | +| `S-5-account-menu` | variant: overflow menu open | menu over the content; "Ta bort konto" disabled with its reason | +| `S-5-account-archived` | variant archived | Gotland 2026: "Arkiverat" badge, "Kvitt", "Återställ från arkiv" (tonal) in the header, no FAB | +| `S-5-account-empty` | empty | new account "Sommarstugan": header "Kvitt" without a settle button; `EmptyState` "Inga utgifter än" / "Lägg till den första utgiften i Sommarstugan." (no illustration, no button); FAB shown | +| `S-5-account-offline` | offline | menu open: all three items disabled, each with "Kräver anslutning" as its second line; rows, "Gör upp det här kontot" and FAB work | +| `S-5-account-error` | error | after "Arkivera" while the server is unreachable: snackbar "Kunde inte nå servern" with "Försök igen"; account unchanged | +| `S-5-account-long` | long | "Sommarstugan i Österåker 2027" ellipsized in the app bar; rows with long descriptions ellipsized at one line; an amount of 1 234 567,89 kr and a month total of 7 digits shown in full | + +**Interactions** +* Row → S-8 / S-10. "Gör upp det här kontot" → S-9 per account. FAB → S-7 with this account. +* "Arkivera" / "Återställ från arkiv": no confirmation (reversible); success → back to S-4 with snackbar "Vardag arkiverades" + "Ångra" (`account_archived_snackbar`; "Ångra" unarchives) / stays with "Vardag återställdes"; network failure → `S-5-account-error`. +* "Ta bort konto": `ConfirmDialog` (destructive) "Ta bort kontot Vardag?" / "Det går inte att ångra." → "Ta bort"; success → S-4; `account_not_empty` → snackbar "Kontot har fortfarande utgifter, betalningar eller återkommande utgifter". + +**TalkBack**: overflow "Fler alternativ"; header one node (§3.6); month headers are headings; rows §3.5; disabled menu items read with their reason. + +**200 %**: header wording and amount wrap; rows per §3.5 (amount under the text). + +### S-6 Account editor (`:feature:account`) + +**Layout** (app bar: close, "Nytt konto" / "Ändra konto", "Spara" small filled; column gap 14): +1. Name field "Namn" (1–60, focused on open for a new account; capitalisation `Sentences`). +2. Card "Standarddelning", padding 18, gap 14: subtitle "Gäller nya utgifter i kontot. Varje utgift kan ändra sin egen delning." (`account_editor_split_hint`; this line also carries AC-6.3 when editing); the two people (avatar 32 + name, viewer left); `SplitSlider`; two percent fields ("Jacob", "Anna", suffix "%", system decimal keyboard, two decimals, the other follows as 100 minus); `ButtonGroup` presets "40/60 · 50/50 · 60/40"; preview box (`surface-container-high`, radius 16, padding 12 / 14, `calculate` icon): "Av 100,00 kr: Jacob 60,00 kr, Anna 40,00 kr" (`account_editor_preview`). + +**States** + +| Frame | State | What differs | +|---|---|---| +| `S-6-account-editor` | loaded | new "Sommarstugan", 60 / 40 | +| `S-6-account-editor-offline` | offline | `NeedsConnectionBanner` "Konton sparas direkt på servern. Anslut telefonen till internet för att skapa kontot." on top; field, card (45 % opacity) and "Spara" disabled | +| `S-6-account-editor-error` | error | `name_taken`: name field in error with "Det finns redan ett konto som heter Vardag" (`account_editor_name_taken`); "Spara" enabled again | + +**Interactions**: "Spara" enabled when the name is non-blank and the percentages sum to 100; shows a progress indicator inside the button while saving; success → back (new: to S-5 of the new account; edit: to S-5); network → snackbar "Kunde inte nå servern" + "Försök igen". Close with changes → `ConfirmDialog` "Slänga ändringarna?" "Släng" / "Fortsätt redigera". + +**TalkBack**: slider "Jacobs andel, Jacob 60 procent, Anna 40 procent" (`editor_ratio_slider`, `editor_ratio_spoken`); preset buttons "60 procent du, 40 procent Anna". + +**200 %**: percent fields stack vertically; presets wrap. + +### S-7 Expense editor (`:feature:expense`) + +Full screen (§13.2), works offline (AC-8.2). The flow and the category rule are in §5. + +**Layout** (app bar: close, "Ny utgift" / "Ändra utgift", "Spara" small filled with `check`; scrolling column gap 12, padding 16 bottom; keypad at the bottom while a keypad field has focus): +1. `AmountDisplay` (focused on open for a new expense). +2. `DescriptionField` "Beskrivning". +3. `CategoryQuickRow` (five tiles + "Alla"). +4. Chips row: Konto · Datum · Betalt av · Delning (§3.7). +5. `SplitPreview` (when the split section is closed). +6. `SplitSection` (when open: after a tap on Delning, or always when the mode is not `default` in edit mode). +7. `NotesField` ("Lägg till anteckning", or the field when notes exist). +8. Edit mode: "Ta bort utgift" (danger text button, `delete`) at the end. +9. `AmountKeypad`; `CompactEditorSummary` pinned under the app bar when the amount has scrolled away. + +**States** + +| Frame | State | What differs | +|---|---|---| +| `S-7-editor` | loaded (new, ten-second flow) | "412", "Systembolaget", category Nöje selected by the suggestion (no caption, CS-7), Vardag, Idag, Betalt av Du, Kontots standard 50/50, preview "Du 206,00 kr · Anna 206,00 kr", keypad up | +| `S-7-editor-split` | variant: exact split with error | compact summary row (Ullared, Shopping, 1 060,00 kr); Delning chip selected; split section open with "Exakta belopp"; Jacob's field 1 200,00 kr in error, Anna's read-only "—"; error box "140,00 kr för mycket"; compact keypad; "Spara" disabled | +| `S-7-editor-full` | variant: full split + notes (edit mode) | "Ändra utgift", SL-kort 30 dagar, Anna, Transport, 14 okt., "Allt på en person" with Anna as bearer, notes expanded, "Ta bort utgift" | +| `S-7-editor-edit` | variant edit mode | "Ändra utgift" for ICA Maxi Lindhagen (1 247,60 kr, Mat & hushåll, Vardag, 16 okt., Betalt av Anna); nothing focused, keypad hidden; "Ta bort utgift" visible at the end | +| `S-7-editor-200` | long, 200 %, light only | amount and "Spara" visible, quick row in 3 columns, the middle scrolls, keypad keys grow | + +**Interactions** +* Defaults (new): account per AC-8.6; category per §5 (UX-3: suggestion, falling back to AC-8.6); date today; payer the viewer (AC-8.3); mode `default`. From S-5 the account is that account. +* Changing the account: mode `default` re-copies the account's ratio (AC-8.8); the suggestion re-runs (CS-3) unless the category was picked by hand. +* "Spara" enabled when the amount is > 0, the description is non-blank and `preview` is not null; tap → `save`. `SaveResult.Invalid` maps each `FieldError` inline: amount `required` "Skriv ett belopp", out of range "Beloppet måste vara mellan 0,01 kr och 10 000 000,00 kr"; description `required` "Skriv en beskrivning", too long; date out of range "Datumet ligger för långt fram"; `exact_sum_mismatch` / exact share > amount "Andelen är större än beloppet". Focus moves to the first field in error. +* `Saved` (new) → back to the opener with snackbar "Utgiften sparades" (online) / "Sparad. Synkas när du är online." (offline), no "Ångra" (UX-6). `Saved` (edit) → back to S-8, no snackbar (the timeline shows the pending line). +* Description IME `Done`: saves when valid; otherwise hides the keyboard and focuses the first invalid field. +* Close or back with changes: `ConfirmDialog` "Slänga utgiften?" (new) / "Slänga ändringarna?" (edit) with "Släng" / "Fortsätt redigera"; without changes it closes directly. +* "Ta bort utgift" (edit): `ConfirmDialog` (destructive, `delete`) "Ta bort utgiften?" / "Den försvinner för er båda." → "Ta bort"; then back past S-8 to its opener with snackbar "Utgiften togs bort". +* Editing an expense the other person created: same editor, no warning. +* Not reachable without an active account (no FAB). + +**TalkBack**: close "Avbryt"; "Spara"; amount, keypad, quick row, chips, split section per §3.7; the compact row "Ullared, Shopping, 1 060,00 kronor, dubbeltryck för att ändra beloppet". Errors announced. + +**200 %**: `S-7-editor-200`; "Spara" always in the app bar; chips wrap to several rows; split section scrolls. + +### S-8 Expense detail (`:feature:expense`) + +**Layout** (app bar: back, no title, `edit` "Ändra" and `delete` "Ta bort" icon buttons; column gap 10): +1. Hero card (fill `cat--container`, padding 20, gap 12): `CategoryTile` 60 + description (title-large, wraps) over "Mat & hushåll · Vardag" (body-medium muted); amount `MoneyText(Large)` with the pending marker (20) after it when pending; badge "Återkommande: Hyra" (lantern, `event_repeat`) when `recurringTemplateName != null` (AC-8.11). +2. Facts (`FactRow`s): Datum (`event`, "fredag 16 oktober 2026"); Betalt av (`person`, avatar 24 + name, "Du" for the viewer); Delning (`call_split`, mode label "Kontots standard 50/50" / "Egen fördelning 70/30" / "Exakta belopp" / "Allt på Anna", a thin `SplitBar`, "Du 623,80 kr · Anna 623,80 kr"); Anteckning (`notes`, full text) only when notes exist. +3. `Timeline`. + +**States** + +| Frame | State | What differs | +|---|---|---| +| `S-8-expense` | loaded + pending | ICA Maxi Lindhagen, notes, timeline with create, two changes and "Din ändring väntar på synk" | +| `S-8-expense-recurring` | variant recurring | Hyra september, Boende · Huset, badge "Återkommande: Hyra", timeline "Skapad automatiskt (återkommande) · 25 sep. 06:00" | +| `S-8-expense-deleted` | variant deleted | Neutral banner on top: filled `delete` in `error` + "Anna tog bort den här utgiften · 17 okt. 09:12" + "Den räknas inte längre i saldon eller statistik."; hero and facts at 62 % opacity, tile desaturated; no "Ändra" / "Ta bort" | +| `S-8-expense-missing` | empty | `ExpenseRepository.detail` emits null: `EmptyState` (Cookie9 tile, `sync_problem`) "Utgiften finns inte på telefonen än" / "Den kan finnas på servern. Synka för att hämta den." with "Synka nu" (tonal, `sync`); no actions in the app bar | + +**Interactions**: "Ändra" → S-7 edit. "Ta bort" → the delete dialog of S-7, then back with snackbar "Utgiften togs bort". "Synka nu" → `SyncRepository.syncNow()`; the screen fills in when the row arrives. Opened from a deletion notification → deleted state (AC-13.6 opens Home for deletions; the deleted state also shows when the row is deleted while open). + +**TalkBack**: icon buttons "Ändra utgift" (`expense_editor_edit`), "Ta bort utgift" (`expense_editor_delete`); hero one node "ICA Maxi Lindhagen, Mat & hushåll, Vardag, 1 247,60 kronor, väntar på synk"; facts per `FactRow`; timeline items one node each. + +**200 %**: facts wrap; hero description wraps. + +### S-9 Settle up (`:feature:settle`) + +**Layout** (app bar: close, "Gör upp" / "Gör upp Huset" (per account) / "Ändra betalning" (editing); column gap 10, padding bottom 20; bottom bar): +1. `DirectionCard`. +2. Amount field "Belopp" (filled, value `amount-large`, suffix "kr" title-large muted; system decimal keyboard; §9.6 input rules). +3. Header "Fördelning" (title-small) and a `SegmentedGroup` of `AllocationRow`s (`SettleState.lines`). +4. `RemainingIndicator` (global variant only). +5. "Lägg till konto" (text, `add`) and, after a row was edited, "Föreslå igen" (text, `refresh`). +6. Info line (global variant, when an account where the payer is owed is not in the rows, §9.4): `info` 18 + body-small muted "Vardag lämnas som det är, eftersom Anna är skyldig dig där." (`settle_untouched_partner_owes`; "… eftersom du är skyldig Anna där." is `settle_untouched_you_owe`). +7. Chip "Datum Idag" (date picker) and field "Anteckning" (0–200, placeholder "Till exempel Swish"). +8. Bottom bar (`surface`, 1 dp `outline-variant` top line, padding 10 / 16): "Bekräfta" (filled medium, full width, `check`), enabled only when `canConfirm`. + +**States** + +| Frame | State | What differs | +|---|---|---| +| `S-9-settle` | loaded (proposal) | "Du betalar Anna", 3 250,00 kr; Huset 3 084,50, Japan 2027 165,50; "Kvar att fördela 0,00 kr"; info line about Vardag | +| `S-9-settle-adjusted` | variant over-settle | Huset 1 750,00, Japan 2027 1 500,00 with the warning border and strip; remaining 0,00 | +| `S-9-settle-remaining` | variant sum mismatch | Huset 2 000,00, Japan 2027 1 000,00; remaining "250,00 kr" in warning style; "Bekräfta" disabled | +| `S-9-settle-account` | variant per account | "Gör upp Huset": one fixed row (no remove, amount follows), no remaining, no "Lägg till konto", no info line; direction swap and amount editable (UX-6) | +| `S-9-confirm-dialog` | variant confirm dialog | over `S-9-settle-adjusted`: dialog "Bekräfta betalningen", "Du betalar Anna 3 250,00 kr idag, lördag 17 oktober 2026.", the allocations (name + amount per line), the warning strip per warned row, "Avbryt" / "Bekräfta" | +| `S-9-settle-empty` | empty | global balance zero: amount empty (hint "0,00"), no rows; in place of the rows a body-medium muted line "Skriv ett belopp så föreslår Kvitt en fördelning."; no remaining indicator; "Bekräfta" disabled | +| `S-9-settle-200` | long, 200 %, light only | each allocation row stacks name and debt over a full-width amount field; direction card wraps its text; bottom button stays pinned | + +**Interactions** +* Defaults from `SettlementRepository.start` (AC-11.3, AC-11.4; editing: AC-11.6). +* Editing the amount calls `recompute` with `allocations = null` (a fresh proposal) until the user edits a row; afterwards it keeps the rows and only updates the remaining amount; "Föreslå igen" re-proposes. Swapping the direction re-proposes. Per account: the single row follows the amount. +* "Lägg till konto" → bottom sheet of all non-deleted accounts not yet listed (archived with badge, AC-11.3); picking adds a row with an empty amount, focused. +* Remove → the row disappears and the remaining amount updates. +* "Bekräfta": with any `overSettles` row → the confirm dialog (AC-11.5) → "Bekräfta" saves; without warnings it saves directly. `Saved` → back to S-4 or S-5 with snackbar "Betalningen sparades". `Invalid` → inline under the amount or the rows (`settle_error_allocation_sum`, `settle_error_allocation_positive`, …). +* Close with changes → `ConfirmDialog` "Slänga ändringarna?" (`common_discard_title`) "Släng" / "Fortsätt redigera". +* Works offline unchanged. + +**TalkBack**: swap "Byt riktning"; remove "Ta bort Huset från fördelningen"; row fields labelled "Belopp för Huset"; the remaining indicator is a polite live region; dialog title announced. + +**200 %**: `S-9-settle-200`. + +### S-10 Settlement detail (`:feature:settle`) + +**Layout** (app bar: back, "Betalning", `edit` "Ändra", `delete` "Ta bort"; column gap 10): +1. Hero card (fill `settlement-hero`, padding 22 / 20, centred, gap 8): payer avatar 52 (Sunny) → `arrow_forward` 28 → recipient avatar 52; "Anna betalade dig" (title-medium); `MoneyText(Large)` with the pending marker when pending. +2. Facts: Datum ("tisdag 6 oktober 2026"), Anteckning (when present). +3. "Fördelning" header and a group: one row per allocation (`AccountTile` 36, name title-medium, `MoneyText(Small)`). +4. `Timeline`. + +**States** + +| Frame | State | What differs | +|---|---|---| +| `S-10-settlement` | loaded | Anna → Jacob 800,00 kr, Vardag, "Swish" | +| `S-10-settlement-deleted` | variant deleted | Neutral banner "Anna tog bort den här betalningen · 17 okt. 09:12" + "Den räknas inte längre i saldona."; content at 62 % opacity; no "Ändra" / "Ta bort"; timeline ends with "Anna tog bort den här · 17 okt. 09:12" | + +**Interactions**: "Ändra" → S-9 editing. "Ta bort" → `ConfirmDialog` (destructive) "Ta bort betalningen?" / "Saldona räknas om för er båda." → back with snackbar "Betalningen togs bort". A missing settlement uses the S-8 missing layout ("Betalningen finns inte på telefonen än"); no frame. + +**TalkBack**: hero one node "Anna betalade dig 800,00 kronor"; icon buttons "Ändra betalning", "Ta bort betalning". + +### S-11 Analytics (`:feature:analytics`) + +**Layout** (app bar "Statistik" without navigation icon; column gap 10; bottom navigation with Statistik selected; no FAB): +1. `AnalyticsFilterBar` (defaults AC-14.2–AC-14.5: Senaste 6 mån, Månad, Hushållet, Kategori). +2. `TotalCard`. +3. `ChartCard` "Utgifter över tid": `StackedBarChart` grouped by the group-by toggle, `ChartLegend`. +4. `ChartCard` "Per kategori": `BreakdownList` of categories. +5. `ChartCard` "Per konto": `BreakdownList` of accounts (archived included with badge, AC-14.9). + +**States** + +| Frame | State | What differs | +|---|---|---| +| `S-11-analytics` | loaded | maj–okt 2026, household, by category; legend of the categories with data; "Per kategori" begins | +| `S-11-analytics-scrolled` | loaded, scrolled | app bar `surface-container`; the end of "Per kategori" and the whole "Per konto" | +| `S-11-analytics-week` | variant week / my share / by account | "Anpassad · 24 aug.–17 okt.", Vecka, Min del, Konto; eight weekly bars by account with "Vecka" caption; "Per konto" | +| `S-11-analytics-empty` | empty | "Anpassad · 1–30 nov."; total 0,00 kr; `EmptyState` (three `other` container tiles) "Inga utgifter i den här perioden" / "Välj en annan period, eller lägg till en utgift med ett datum i perioden." + "Byt period" (tonal, `date_range`); charts hidden, filters stay | +| `S-11-analytics-200` | long, 200 %, light only | filter controls one per row; total amount ~1.5×; chart labels thinned; breakdown names ellipsize, amounts and percents whole | + +**Interactions** +* Period chip → menu of the presets (AC-14.2: "Denna månad", "Senaste 3 mån", "Senaste 6 mån", "Senaste 12 mån", "I år", "All tid", "Anpassad…"); "Anpassad…" opens the Material date range picker; "Använd" is enabled only with both dates (AnalyticsState note in §14.2.4). +* Toggles re-query immediately; the filter survives tab switches within the session. +* Bar → S-12 for that bucket (all groups); breakdown row → S-12 for that category or account over the range (AC-14.8). +* Settlements are never counted (AC-14.6). Works offline. + +**TalkBack**: chart summary sentences as §3.10; each bar and row a node; toggles are radio groups. + +**200 %**: `S-11-analytics-200`. + +### S-12 Analytics drill-down (`:feature:analytics`) + +**Layout** (app bar: back, title formatted from the drill-down arguments: "Mat & hushåll, oktober 2026" / "Vardag, v. 41" / "Oktober 2026" / "Mat & hushåll, maj–okt 2026"; one line, ellipsis; column gap 10): +1. Header card (fill `cat--container` for a category, `account-header-n` for an account, `primary-container` for a whole bucket; padding 18 / 20): tile 56 (`CategoryTile`, `AccountTile`, or a Cookie4 `primary` tile with `bar_chart` for a bucket) + "Totalt · Hushållet" / "Totalt · Min del" (label-large muted) over `MoneyText(Large)` over "4 utgifter" (body-small muted, plural). +2. `SegmentedGroup` of `ExpenseRow`s, newest first. + +**States** + +| Frame | State | +|---|---| +| `S-12-drilldown` | loaded (Mat & hushåll, oktober 2026) | + +**Interactions**: row → S-8. If the data changed and nothing is left: the header shows 0,00 kr and the line "Inga utgifter längre" replaces the list (no frame). + +**TalkBack**: header one node "Mat & hushåll, oktober 2026, totalt 3 910,00 kronor, 4 utgifter". + +### S-13 Settings hub (`:feature:settings`) + +**Layout** (app bar "Inställningar"; column gap 12; bottom navigation with Inställningar selected): +1. Household card (padding 16 / 18, gap 12; role lines only, no device name: `SessionState` carries none): viewer row: avatar 52 (Sunny) + name (title-medium) over "Du · Administratör" (`settings_role_admin`) or "Du" (`settings_role_member`) in body-small muted; 1 dp divider; partner row: avatar 52 + name over "Partner" (`common_partner`) or "Admin" (`settings_role_admin_other`). +2. Group 1: `SettingsRow`s "Profil" (`person`, orange, "Namn och bild"), "Kategorier" (`category`, green, "14 aktiva"), "Återkommande utgifter" (`event_repeat`, brown, "3 aktiva, 1 pausad" / "Inga"). +3. Group 2: "Enheter" (`devices`, blue, no subtitle: the list is online only); admin only: "Bjud in partner" (`person_add`, teal) while the partner has not joined, else "Para ihop en enhet" (`add_link`, teal); "Synkstatus" (`sync`, purple, the pill's text, e.g. "Synkad för 3 min sedan" / "2 väntar på synk"); "Om Kvitt" (`info`, grey, "Version 1.1" / "Uppdatering finns"). + +**States** + +| Frame | State | What differs | +|---|---|---| +| `S-13-settings` | loaded (admin) | Jacob "Du · Administratör", Anna "Partner"; "Para ihop en enhet" shown | +| `S-13-settings-member` | variant member | Anna's phone: Anna "Du" first, Jacob "Admin"; no invite or pairing row (AC-2.2) | + +**Interactions**: each row pushes its screen (S-14, S-15, S-19, S-20, S-21, S-16, S-22). Rows to online-only screens stay enabled; those screens show their offline state. + +**TalkBack**: household card two nodes ("Jacob, du, administratör"; "Anna, partner"); rows "Kategorier, 14 aktiva, knapp". + +### S-14 Profile (`:feature:settings`) + +**Layout** (app bar: back, "Profil", "Spara" small filled; column gap 14): +1. Centred, padding 20 / 0 / 10: own avatar 128 (Sunny); buttons "Byt bild" (tonal, `photo_library`; "Lägg till bild" without a picture) and "Ta bort bild" (text, `delete`; only with a picture). +2. Field "Namn" (1–40) with supporting text "Så här ser Anna dig i Kvitt." (partner's name). + +**States** + +| Frame | State | What differs | +|---|---|---| +| `S-14-profile` | loaded | Jacob, picture, name focused | +| `S-14-profile-loading` | loading | a new picture uploading: progress ring on the avatar, both picture buttons disabled | +| `S-14-profile-offline` | offline | Warning banner "Kräver anslutning. Profilen kan bara ändras när telefonen är online." (`settings_profile_offline_banner`); buttons, field and "Spara" disabled | + +**Interactions**: picture changes apply on success without "Spara" (AC-4.3); "Ta bort bild" asks nothing (reversible by adding again). "Spara" enabled when the name changed and is valid; saves with `setDisplayName`; success → back (no snackbar). Failures: snackbar per S-2 (picture) or "Kunde inte nå servern" + "Försök igen". + +**TalkBack**: avatar = the viewer's name; while uploading "Laddar upp bilden". + +### S-15 Categories (`:feature:settings`) + +**List layout** (app bar: back, "Kategorier", `add` icon button "Ny kategori"; column gap 4): +1. `SegmentedGroup` of active categories in `sort_order`: row min height 56, padding 4 / 12 / 4 / 8, gap 12: drag handle (`drag_indicator`, 32 × 48 target), `CategoryTile` 36 solid, name (title-medium; custom name or localised default), `chevron_right`. +2. `CollapsibleRow` "Arkiverade (n)" and, expanded, a group of archived categories (no drag handle, 32 dp spacer, muted tile). + +**Editor layout** (pushed; app bar: close, "Ändra kategori" / "Ny kategori", "Spara"; column gap 10): +1. Row: live preview `CategoryTile` 64 + name field "Namn" (1–40; for a default category an empty field means "use the default name"); for a default category under the field "Standardnamn: Mat & hushåll" (body-small muted) and, when a custom name exists, "Återställ namn" (text, `restart_alt`, AC-7.4). +2. "Ikon" (title-small) + `CategoryIconPicker` (33). +3. "Färg" + `ColorSwatchPicker` (14). +4. Footer row: "Arkivera" (outlined, `inventory_2`) or "Återställ från arkiv" (outlined, `unarchive`); delete: default category → `lock` 16 + "Kan inte tas bort: standardkategori" (body-small muted, `settings_category_delete_blocked_default_short`); custom unused → "Ta bort kategori" (danger text, `delete`); custom in use → disabled "Ta bort kategori" with "Kategorin används av utgifter eller återkommande utgifter. Arkivera den i stället." (`settings_category_delete_blocked_in_use`); the state comes from `CategoryRepository.deletable(id)` (UX-9, pending), see open point 2 for the behaviour until then. + +**States** + +| Frame | State | What differs | +|---|---|---| +| `S-15-categories` | loaded | the 14 defaults in sort order | +| `S-15-categories-scrolled` | loaded, scrolled | the end of the list, "Arkiverade (1)" expanded with Pant | +| `S-15-categories-offline` | offline | no drag handles; a `NeedsConnectionHint` line "Kräver anslutning för att ändra" above the list; "Ny kategori" disabled; rows not clickable, no chevrons | +| `S-15-category-editor` | variant editor (default renamed) | "Matvaror" over "Standardnamn: Mat & hushåll" with "Återställ namn"; `shopping_cart`, green; "Arkivera"; "Kan inte tas bort: standardkategori" | +| `S-15-category-editor-custom` | variant editor, custom archived | "Pant", `water_drop`, teal; no default-name line; "Återställ från arkiv"; "Ta bort kategori" disabled with "Kategorin används av utgifter eller återkommande utgifter och kan inte tas bort." (`settings_category_delete_blocked_in_use_archived`; an active category uses `settings_category_delete_blocked_in_use`, which adds "Arkivera den i stället.") | + +**Interactions** +* Reorder: long-press a row or drag its handle; on drop `reorder(ids)`; failure → snackbar "Kunde inte nå servern" and the old order returns. TalkBack custom actions "Flytta upp" / "Flytta ned" on each active row. +* Row → editor. "Spara" (online) → `update` / `create`; success → back. "Återställ namn" sets the field empty and the preview name to the default (saved with "Spara", `name = null`). +* "Arkivera" / "Återställ från arkiv" act immediately (online) and return to the list with a snackbar ("Pant arkiverades"). "Ta bort kategori" → `ConfirmDialog` "Ta bort kategorin Pant?" → `delete`; `category_in_use` → the disabled reason inline. + +**TalkBack**: drag handle "Flytta Mat & hushåll" (`settings_categories_drag_handle`); add "Ny kategori"; icon and colour pickers per §3.4; preview tile decorative. + +**200 %**: icon grid stays 7 columns (icons do not scale); colour grid 7 columns; the footer wraps. + +### S-16 Sync status (`:feature:home`) + +**Layout** (app bar: back, "Synkstatus"; column gap 10): +1. When `clockSkew`: Error banner (`schedule`) "Telefonens klocka går fel. Rätta den och synka igen." / "Klockan ligger mer än 10 minuter före servern. Dina ändringar sparas på telefonen tills dess." with "Datum och tid" (text, `open_in_new`, opens the system date settings). +2. Status card (padding 20, gap 14): 52 dp Sunny tile (`owed-container` + filled `cloud_done`; `surface-container-highest` + `sync_problem` when the last attempt failed or was refused) + "Synkad för 3 min sedan" / "Synkar…" / "Offline" (title-medium) over "Senast lyckade synk 13:59" (body-small muted). +3. "Synka nu" (filled medium, full width, `sync`); disabled with a progress indicator while running; offline disabled with "Kräver anslutning". +4. "Väntande ändringar (2)" header + group of pending rows: `CategoryTile` 36 (settlements: the settlement tile), description (title-medium, ellipsis), "Väntar på synk" with `cloud_upload` 15 in `pending` (body-small), `MoneyText(Small)`; tap → S-8 / S-10. Rows carry no change-kind tag ("Ny" / "Ändrad" / "Borttagen" of U1): `SyncStatusState.pending` is a list of `LedgerRow`s without one, and the tag is not needed for v1. +5. "Ändringar som inte synkades" header + one outlined card per rejection (1 dp `outline-variant`, padding 16, gap 10): 36 dp tile (`receipt_long` for an expense, `handshake` for a settlement, on `surface-container-highest`) + summary (title-medium) + reason text per §14.4.6 with the time ("Din ändring ersattes av en nyare ändring från Anna · 17 okt. 08:31", body-medium) + a body-small muted consequence line ("Annas version gäller. Öppna utgiften om du vill göra din ändring igen."); actions "Öppna" (text) and "Ta bort från listan" (tonal). + +**States** + +| Frame | State | What differs | +|---|---|---| +| `S-16-sync` | loaded | synced 3 min ago, two pending rows, one `stale` rejection (Babette) | +| `S-16-sync-clock` | variant clock skew | clock banner first; status "Synkad för 2 h sedan" / "Senaste försöket nekades: klockan" with `sync_problem`; pending rows; no rejection list | +| `S-16-sync-empty` | empty | status card "Allt är synkat" (`home_sync_status_all_synced`) over "Senast lyckade synk 13:59"; "Synka nu"; no pending or rejection sections | + +**Interactions**: "Öppna" opens the entity's current version (S-8 / S-10; deleted or missing states apply); "Ta bort från listan" → `dismiss` (works offline). The rows update live while a sync runs. + +**TalkBack**: status card one node; rejection card one node plus its two buttons, read with their visible text ("Öppna", "Ta bort från listan", `home_sync_status_dismiss`). + +### S-17 Update available (`:feature:home`) + +Not a screen: the `UpdateBanner` on S-4 (and S-22) and one dialog. + +**Dialog "Tillåt installation"** (shown once, before the first install, when `UpdateState.NeedsInstallPermission`): `ConfirmDialog` with a 56 dp Sunny `tertiary-container` tile (`install_mobile`), title "Tillåt installation", body "Kvitt uppdateras direkt från er server, inte från Play Butik. Därför frågar Android en gång om Kvitt får **Installera okända appar**." and "Slå på **Tillåt från den här källan** och gå sedan tillbaka till Kvitt. Uppdateringen fortsätter av sig själv.", buttons "Senare" / "Fortsätt" (`arrow_forward`, opens the system setting for Kvitt). + +**States** + +| Frame | State | What differs | +|---|---|---| +| `S-17-update` | variant first-time dialog | the dialog over S-4 with the `Available` banner | +| `S-17-update-progress` | loading (downloading) | banner `Downloading` 64 % | +| `S-17-update-failed` | error | banner `Failed` | + +**Interactions**: "Uppdatera" → `install()`; after the system setting, returning to the app continues; verified download → system installer (out of the app's hands). "Senare" hides the banner until the next cold start. Offline: "Uppdatera" disabled with "Kräver anslutning". Debug builds never show it (AC-16.5). + +**TalkBack**: progress announced as a percentage every 10 %; dialog title read on open. + +### S-18 Device removed (`:feature:onboarding`) + +**Layout** (no app bar, no navigation): centred column, padding top 80, gap 14: Cookie9 tile 112 (`error-container`, `phonelink_erase` 52 in `on-error-container`); "Den här telefonen har tagits bort" (headline-medium); "Åtkomsten togs bort och allt Kvitt-data på telefonen har raderats." (body-large muted); "Vill du använda Kvitt igen behöver du en ny inbjudningskod." (body-medium muted). Bottom bar: "Para ihop igen" (filled medium, full width, `link`). + +**States** + +| Frame | State | +|---|---| +| `S-18-removed` | loaded | + +**Interactions**: "Para ihop igen" → `acknowledgeRevoked()` → S-1 (first pairing: with the name field). Back closes the app. + +### S-19 Recurring expenses (`:feature:settings`) + +**List layout** (app bar: back, "Återkommande utgifter"; column gap 10; FAB "Ny återkommande"): +* `SegmentedGroup` of templates (row min 72, top-aligned, padding 14 / 16): `CategoryTile` 40 (muted when paused) · description (title-medium, ellipsis) over "Huset · Anna betalar" (account · payer, body-medium muted) over "Varje månad den 25:e" and "Nästa 25 okt." (body-small muted, two lines; `settings_recurring_every_month_on`, `settings_recurring_next`) or, when paused, a "Pausad" lantern badge (`pause`) + "Varje månad den 12:e" · `MoneyText(Small)` right (70 % opacity when paused). + +**Editor layout** (pushed; app bar: close, "Ny återkommande utgift" / "Återkommande utgift", "Spara"; column gap 12): `AmountDisplay` (+ keypad when focused; focused on open for a new template), `DescriptionField`, `CategoryQuickRow` (no suggestion, open point 3), chips Konto · Betalt av · Delning (no Datum), `SplitSection` when open; then a flat section "Schema" (padding 16, gap 10): "Dag i månaden" field (1–31, number keyboard) and "Första månad" (dropdown; a month picker dialog titled with the field label, with a year row and 12 month chips; months before the allowed start disabled, AC-9.8 default) side by side (1 : 1.5); "Sista månad (valfritt)" (placeholder "Ingen slutmånad"); a switch row "Pausad" with "Skapas kl. 06:00 den 25:e varje månad" (body-small muted). Day 29–31: hint "I kortare månader används sista dagen". Editing: hint "Ändringen gäller kommande utgifter." (AC-9.4); after un-pausing: "Månader som passerat under pausen skapas inte i efterhand." (§9.5). Edit mode: "Ta bort" (danger text) at the end. + +**States** + +| Frame | State | What differs | +|---|---|---| +| `S-19-recurring` | loaded | Hyra, Bahnhof bredband, Netflix, Spotify Family (paused) | +| `S-19-recurring-empty` | empty | `EmptyState` "Inga återkommande utgifter" / "Hyra, el och streaming kan läggas in automatiskt varje månad." + "Ny återkommande utgift" (medium, `add`); no FAB | +| `S-19-recurring-offline` | offline | Warning banner "Kräver anslutning. Återkommande utgifter kan bara ändras när telefonen är online." (`settings_recurring_offline_banner`) above the list; FAB disabled (`on-surface` 12 % fill, 38 % content); rows not clickable, list readable | +| `S-19-recurring-editor` | variant editor | Hyra 12 400,00 kr, Boende, Huset, Betalt av Anna, Kontots standard 60/40, day 25, from september 2026 | + +**Interactions**: FAB or empty-state button → editor (new). Row → editor. "Spara" (online) → `create` / `update`; success → back; network → snackbar. "Ta bort" → `ConfirmDialog` "Ta bort Hyra?" / "Utgifter som redan skapats finns kvar." → `delete`. Close with changes → discard dialog. + +**TalkBack**: row one node "Hyra, 12 400,00 kronor, Huset, Anna betalar, varje månad den 25:e, nästa 25 oktober" (paused: "pausad"); FAB "Ny återkommande utgift". + +### S-20 Devices (`:feature:settings`) + +**Layout** (app bar: back, "Enheter"; column gap 10): `SegmentedGroup` of devices (row min 72, top-aligned, padding 16): tile 40 (Cookie4, `cat-blue` container, `smartphone` in `cat-blue`) · name (title-medium) + "Den här enheten" lantern badge for the current one · avatar 20 + owner name (body-medium muted) · "Parad 3 okt. · Senast sedd nyss · Kvitt 1.1" (body-small muted) · for revocable devices "Ta bort åtkomst" (danger text, `block`). Revoked devices are not listed. Footer: "Du kan inte ta bort åtkomsten för den telefon du använder just nu." (body-small muted). Admin sees both users' devices, a member their own (AC-3.1). + +**States** + +| Frame | State | What differs | +|---|---|---| +| `S-20-devices` | loaded | Galaxy S26 (Jacob, this device), Galaxy S26+ (Anna) | +| `S-20-devices-revoke` | variant confirm dialog | `ConfirmDialog` (destructive, `phonelink_erase`) "Ta bort åtkomst för Galaxy S26+?" / "Annas telefon loggas ut och allt Kvitt-data på den raderas. Anna kan para ihop den igen med en ny inbjudningskod." / "Avbryt", "Ta bort åtkomst" | +| `S-20-devices-loading` | loading | two skeleton rows while `list()` runs | +| `S-20-devices-offline` | offline | no list; centred `cloud_off` 48 + "Kräver anslutning för att visa enheter" (title-medium) | + +**Interactions**: revoke → dialog → `revoke(id)` → the row updates; failure → snackbar. Network failure on open → the offline layout with "Kunde inte nå servern" and "Försök igen". + +### S-21 Invites (`:feature:settings`, admin only) + +**Layout** (app bar: back, "Bjud in partner" while the partner has not joined, else "Para ihop en enhet"; column gap 10): +1. Before an invite is created in this visit: card with a body-medium explanation and the create buttons: "Bjud in partner" (filled, `person_add`, only while one user exists, AC-2.1) or, after the partner joined, "För mig" and "För Anna" (tonal, `add_link`; AC-2.2). +2. After creation, in its place: a `tertiary-container` card (padding 20, gap 12): `key` (filled) + "Ny enhet för Jacob" (`settings_invites_new_device_for`) / "Inbjudan till din partner" (`settings_invites_new_partner`) (title-medium); "Inbjudningskod" (label-medium); `InviteCodeDisplay`; the link (body-small, centred, wraps anywhere); `schedule` + "Gäller till tis 20 okt. 14:02"; "Dela" (tertiary, `share`; Android share sheet with `settings_invites_share_message`) and "Kopiera kod" (outlined in `on-tertiary-container`, `content_copy`; snackbar "Koden kopierades"). Under the card: "Öppna länken på den nya telefonen, eller skriv in koden i Kvitt. Koden fungerar en gång." (body-medium muted). +3. "Väntande inbjudningar" header + group: tile (`key`, `cat-yellow` container) + "Enhet · Jacob" / "Partner" (title-medium) over "Skapad idag 14:02 · Gäller till tis 20 okt. 14:02" (body-small muted) + "Avbryt" (text). Rows created earlier have no code ("Koden visas bara när den skapas"). + +**States** + +| Frame | State | What differs | +|---|---|---| +| `S-21-invites` | loaded | created device invite for Jacob; one pending row | +| `S-21-invites-offline` | offline | Warning banner "Kräver anslutning. Inbjudningar kan bara skapas och visas när telefonen är online." (`settings_invites_offline_banner`); create buttons disabled; no pending list | +| `S-21-invites-error` | error | after a create: snackbar "Kunde inte nå servern" with "Försök igen"; no card | + +**Interactions**: create → `create(kind, targetUser)`; `household_full` → snackbar "Din partner har redan gått med" (`error_household_full`). "Avbryt" on a pending row → `ConfirmDialog` "Avbryta inbjudan?" → `cancel(id)`. Leaving the screen discards the shown code (it cannot be shown again). + +**TalkBack**: code spelled per group (§3.11); "Dela" (`settings_invites_share`), "Kopiera kod" (`settings_invites_copy_code`); pending row "Enhet för Jacob, gäller till tisdag 20 oktober 14:02" + "Avbryt inbjudan". + +### S-22 About (`:feature:settings`) + +**Layout** (app bar: back, "Om Kvitt"; column gap 10): centred app mark 96 (Sunny, `primary`, `receipt_long` 48), "Kvitt 1.1 (11)" (headline-medium), "Delade utgifter för två" (body-medium muted); `UpdateBanner` when an update is available; group of `FactRow`s: Server (`dns`, URL wraps), Serverversion (`memory`, "1.1.0" or "Okänd" when `/health` failed), "Licenser för öppen källkod" (`description`, chevron → the licences screen); "Sök efter uppdatering" (tonal medium, `system_update`, centred; hidden in debug builds, AC-16.5). + +**States** + +| Frame | State | +|---|---| +| `S-22-about` | loaded | + +**Interactions**: "Sök efter uppdatering" → `check()` with progress in the button; result: the banner appears, or snackbar "Du har den senaste versionen"; network → "Kunde inte nå servern". + +### Notifications (`:app` builds them, copy in `:core:design`) + +Built locally after the sync per AC-13.5; channel "Aktivitet" (`notif_channel_activity`). Small icon: the monochrome `receipt_long` app mark; accent colour `primary`; large icon: the actor's avatar when cached (else none). Title and body per §13.3 in the phone's language (`notif_*`), e.g. "Anna lade till en utgift" / "ICA Maxi Lindhagen · 1 247,60 kr · Vardag"; "Anna registrerade en betalning" / "Anna betalade dig 800,00 kr". Generic fallback without amounts when the sync fails. Tap per AC-13.6. + +| Frame | State | +|---|---| +| `N-notifications` | the notification shade with two notifications | + +--- + +## 5. The add-expense flow (S-7) in detail + +The most important flow (§13.1). It must need at most: open app, tap add, type amount, type description, tap save; account and category are defaulted (AC-8.6, and UX-3 for the category). + +### 5.1 The ten-second path (binding) + +| Step | User | Screen | +|---|---|---| +| 1 | Taps "Lägg till utgift" on S-4 (or S-5) | S-7 slides up: amount focused, keypad up, all defaults set (Konto per AC-8.6 or the S-5 account; category = the step-4 default of CS-5; Datum "Idag"; Betalt av "Du"; Delning "Kontots standard …"). "Spara" disabled | +| 2 | Types `4` `1` `2` on the keypad | "412" in the amount; `preview` computed; "Spara" still disabled (no description) | +| 3 | Taps "Beskrivning", types "Systembolaget" | keypad slides out, the Swedish system keyboard in; on every change the suggestion re-runs (§5.3) and the quick row selects Nöje; "Spara" enabled | +| 4 | Taps "Spara" (app bar) or the keyboard's Done | `save` → `Saved`; the editor closes; S-4 shows "Utgiften sparades" (offline: "Sparad. Synkas när du är online."); balances already include it (AC-10.4); sync after 2 s (AC-15.3) | + +One tap each, never more, for the common overrides from step 3 on: payer (tap "Betalt av" toggles), category (tap a tile), split (tap "Delning" opens the section; one more tap picks a mode). Nothing else needs touching. + +### 5.2 Keypad and keyboard + +* The keypad (§3.7) owns the amount, the exact-share field and the ratio percent fields; the system keyboard owns the description and the notes. Only one of the two is visible. +* Focus order: amount → description → (notes when expanded). Tapping the description while the keypad is up swaps keypad for keyboard in one motion (keypad slides down 150 ms while the IME animates in; the content keeps the description visible above the IME with `imePadding`). +* Tapping outside any field hides both. +* The keypad never inserts invalid input (§9.6); the amount needs no separate validation message for format, only for "required" and the range. + +### 5.3 Category suggestion (UX-3, CS-1 … CS-7) + +* **Scope (CS-1):** new expenses only. In edit mode (`S-7-editor-edit`, `S-7-editor-full`) the saved category stays and nothing is suggested. +* **Manual flag (CS-2):** `categoryPickedByHand = false` on open. Tapping any tile in `CategoryQuickRow` or `CategoryGridSheet` sets it true, also when the tile was already selected. It is never reset in this editor session. +* **Runs (CS-3)** while the flag is false, on every description change and every account change, by calling `ExpenseRepository.suggestCategory(accountId, description)` (UX-3; local, no network); debounce 150 ms so typing stays smooth. A non-null result becomes the selected category; null (no suggestion possible) leaves the step-4 default (CS-5 step 4; for an empty description, CS-7). +* **Order (CS-5):** history exact → history first token → keyword table → default; "most often", ties by most recent. The UI does not know which step answered. +* **What the user sees (CS-7):** the Kategori selection simply moves to the suggested tile (container → solid cross-fade and check badge, §2.11); **no caption, no marker, no snackbar**. If the suggested category is not among the five tiles it takes the fifth place (§3.7). TalkBack does not announce suggestion changes (no live region); the selected tile reads "vald" when focused. +* Clearing the description returns the selection to the step-4 default (CS-7). +* A wrong guess costs one tap: the right tile, or "Alla" and the right category. + +### 5.4 Split override + +* Closed state: the "Delning" chip shows the current mode and ratio ("Delning Kontots standard 60/40"); `SplitPreview` under the chips shows the shares. +* One tap on the chip opens `SplitSection` in place (spatial default spring) and scrolls it into view; the chip turns `secondary-container` with `expand_less`; the keypad switches to its compact height when a keypad field in the section gets focus. +* Picking a mode is one tap; "Allt på en person" then needs the bearer (one tap; default the partner when the viewer pays, else the viewer — the person who did not pay is the usual reason for "all on one"); "Egen fördelning" starts at the account's ratio; "Exakta belopp" starts with the viewer's field empty and focused. +* Closing the section keeps the chosen mode. Changing the account in mode `default` re-copies its ratio (AC-8.8); in other modes the split is kept. +* The preview is always `ExpenseRepository.preview(draft)`; when it returns null the preview hides and "Spara" is disabled. + +### 5.5 Edit mode + +Same editor with "Ändra utgift", fields loaded from `load(id)`, nothing focused and the keypad hidden (`S-7-editor-edit`); tapping the amount shows the keypad. The split section is open when the mode is not `default`. Notes are expanded when present. "Ta bort utgift" at the end. Save returns to S-8. + +--- + +## 6. Open points + +1. **Quick-row usage data (UX-9).** `CategoryQuickRow` needs `ExpenseEditorOptions.frequentCategoryIds` (proposed in `proposed-decisions.md` UX-9: per active account, up to five active categories by use count in that account, ties by `sort_order`, from the Room copy). Until it lands the row uses the fallback of §3.7. +2. **Category deletability (UX-9).** `S-15-category-editor-custom` needs `CategoryRepository.deletable(id)` (UX-9). Until it lands, "Ta bort kategori" is enabled for custom categories and `Failed("category_in_use")` shows the in-use reason inline after the tap. +3. **Recurring editor suggestion.** CS-1 limits the suggestion to new expenses; S-19's editor uses the AC-8.6-style default (last category of the account, else Övrigt) and no suggestion (category-suggestion.md open point 3). +4. **System font on One UI.** `FontFamily.Default` may resolve to a One UI font style instead of Roboto; M6 checks on the owner's S26 that amounts stay tabular and weights 500/600/700 are distinct (D-020 stands either way). +5. **Expressive API status.** Every Expressive API of §3.1 is checked against the current Material 3 release when M6 pins it; any that is not usable takes its listed fallback without a design change. + +Resolved in the U2 alignment pass: the catalogue frames follow this file (payer chip `swap_horiz`, ratio presets 40/60 · 50/50 · 60/40, allocation remove buttons, S-13 "14 aktiva" without an "Enheter" subtitle and without a device name, S-16 rows "Väntar på synk", S-12 count caption), and every label quoted here has a `strings.csv` key (including the split-section collapse label, the keypad's "Rensa" action, the S-10 missing state and the category delete confirmation). diff --git a/docs/ux/directions/export-png.sh b/docs/ux/directions/export-png.sh index 0c59f99..ed8cea7 100755 --- a/docs/ux/directions/export-png.sh +++ b/docs/ux/directions/export-png.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash # Export every phone frame of one direction to /png/.png at 2x. -# Usage: docs/ux/directions/export-png.sh docs/ux/directions/- [frame-id ...] +# Usage: [OUT_DIR=] docs/ux/directions/export-png.sh docs/ux/directions/- [frame-id ...] +# OUT_DIR (optional) is where the PNGs go; default /png. # Needs Google Chrome (macOS path below or CHROME=...). The page must implement ?list=1 and ?frame=. set -euo pipefail dir="$(cd "$1" && pwd)"; shift @@ -13,8 +14,9 @@ if [ "$#" -gt 0 ]; then ids=("$@"); else | sed -n '/
/,/<\/pre>/p' | sed -e 's/<[^>]*>//g' | grep -v '^[[:space:]]*$')
 fi
 [ "${#ids[@]}" -gt 0 ] || { echo "no frames listed by $page?list=1" >&2; exit 1; }
-mkdir -p "$dir/png"
+out="${OUT_DIR:-$dir/png}"
+mkdir -p "$out"
 for id in "${ids[@]}"; do
-  run --window-size=411,891 --force-device-scale-factor=2 --screenshot="$dir/png/$id.png" "$page?frame=$id"
+  run --window-size=411,891 --force-device-scale-factor=2 --screenshot="$out/$id.png" "$page?frame=$id"
 done
-echo "exported ${#ids[@]} frames to $dir/png"
+echo "exported ${#ids[@]} frames to $out"
diff --git a/docs/ux/flows.md b/docs/ux/flows.md
index 0e07d3f..435bd32 100644
--- a/docs/ux/flows.md
+++ b/docs/ux/flows.md
@@ -1,6 +1,6 @@
 # Kvitt: flows
 
-Status: U1 proposal (2026-10-17). Step-by-step flows with screen ids (`ia.md`, `screens.md`). Rules referenced by spec section; nothing here redefines them. Data from the shared mock household (Jacob = owner/admin, Anna = partner; today lördag 17 oktober 2026).
+Status: U1 (2026-10-17), updated in U2 (2026-10-18) for the owner's decisions (category suggestion UX-3 with no caption and "most often" history; 14 default categories UX-8). Step-by-step flows with screen ids (`ia.md`, `design-spec.md` §4). Rules referenced by spec section; nothing here redefines them. Data from the shared mock household (Jacob = owner/admin, Anna = partner; today lördag 17 oktober 2026).
 
 Notation: **User** does · *System* does · `S-n` screen · ⟶ navigation.
 
@@ -18,7 +18,7 @@ Notation: **User** does · *System* does · `S-n` screen · ⟶ navigation.
 8. **Jacob** taps "Dela" ⟶ Android share sheet ⟶ sends the message to Anna (SMS, Signal, …). Back ⟶ `S-3` now shows the expiry.
 9. When Anna has joined (F-2), the next sync replaces `S-3` with `S-4` empty state "Skapa ert första konto" ⟶ **Jacob** taps "Nytt konto" ⟶ `S-6` "Vardag", 50/50 ⟶ Spara ⟶ `S-4` with the account and the FAB.
 
-Error branches: `invite_invalid` / network / `rate_limited` inline on `S-1` (screens.md S-1). Lost every phone later: `kvitt-server pair-device` (AC-1.6) then F-1 steps 2–5.
+Error branches: `invite_invalid` / network / `rate_limited` inline on `S-1` (`design-spec.md` S-1). Lost every phone later: `kvitt-server pair-device` (AC-1.6) then F-1 steps 2–5.
 
 ## F-2 Join as spouse (US-2, AC-2.4, AC-2.10)
 
@@ -28,13 +28,13 @@ Error branches: `invite_invalid` / network / `rate_limited` inline on `S-1` (scr
 4. *Full sync* ⟶ `S-4` loading ⟶ notification permission prompt ⟶ `S-4` empty "Skapa ert första konto" (or Jacob's accounts, if he created any after she joined).
 5. On Jacob's phone the next sync (or push-triggered sync) removes `S-3`.
 
-## F-3 Add an expense online (US-3, AC-8.6) — the ten-second flow
+## F-3 Add an expense online (US-3, AC-8.6, UX-3) — the ten-second flow
 
 1. **Jacob** opens Kvitt ⟶ `S-4`.
-2. Taps FAB "Lägg till utgift" ⟶ `S-7` with amount focused, keypad up; defaults: Konto Vardag (last used), Kategori (last used in Vardag, else Övrigt), Datum Idag, Betalt av Du, Delning Kontots standard 50/50.
+2. Taps FAB "Lägg till utgift" ⟶ `S-7` with amount focused, keypad up; defaults: Konto Vardag (last used), Kategori (last used in Vardag, else Övrigt, until a description exists), Datum Idag, Betalt av Du, Delning Kontots standard 50/50. The category row shows the five categories used most in Vardag plus "Alla".
 3. Types `412`.
-4. Taps Beskrivning, types "Systembolaget".
-5. (optional, one tap each) taps the Kategori chip ⟶ grid ⟶ Nöje; taps Betalt av to switch to Anna; taps Delning to open the split section.
+4. Taps Beskrivning, types "Systembolaget". *While he types, the category is suggested from the description (`category-suggestion.md` CS-5: the category used most often for this description in the household's history, else the keyword table, else the default) and Nöje becomes selected; no caption or marker (CS-7).*
+5. (optional, one tap each) a wrong guess: taps the right category tile (or "Alla" ⟶ sheet ⟶ category); from then on the suggestion no longer changes the category (CS-2); taps Betalt av to switch to Anna; taps Delning to open the split section.
 6. Taps "Spara" ⟶ *saved locally, pending* ⟶ back to `S-4`, snackbar "Utgiften sparades"; Vardag's wording/amount and the global hero update immediately (AC-10.4).
 7. *2 s later, sync pushes (AC-15.3)*; the sync line returns to "Synkad nyss"; *the server pushes `expense_created` to Anna's phone*, which syncs and shows "Jacob lade till en utgift · Systembolaget · 412,00 kr · Vardag".
 
@@ -51,7 +51,7 @@ Error branches: `invite_invalid` / network / `rate_limited` inline on `S-1` (scr
 
 1. **Jacob** gets a notification "Anna lade till en utgift · ICA Maxi Lindhagen · 1 147,60 kr · Vardag" ⟶ taps ⟶ `S-8` (after sync).
 2. Timeline: "Anna skapade den här · 16 okt. 14:02". He sees the receipt says 1 247,60.
-3. Taps "Ändra" ⟶ `S-7` "Ändra utgift" with all fields loaded; amount focused.
+3. Taps "Ändra" ⟶ `S-7` "Ändra utgift" with all fields loaded, nothing focused, keypad hidden; the category is kept (no suggestion when editing, CS-1). He taps the amount; the keypad appears.
 4. Changes 1 147,60 to 1 247,60 ⟶ "Spara" ⟶ back to `S-8`; timeline gains a last line "Din ändring väntar på synk".
 5. *Sync* ⟶ the pending line is replaced by "Du ändrade beloppet från 1 147,60 kr till 1 247,60 kr · 16 okt. 19:40".
 6. *Anna's phone* gets "Jacob ändrade en utgift · ICA Maxi Lindhagen · 1 247,60 kr"; her `S-8` shows the same timeline (her view: "Jacob ändrade beloppet …").
@@ -80,7 +80,7 @@ Variant: "Lägg till konto" ⟶ picker (all non-deleted accounts, archived inclu
 ## F-8 Create a recurring expense (US-10, FR-9)
 
 1. **Jacob** ⟶ Inställningar `S-13` ⟶ "Återkommande utgifter" `S-19` ⟶ "Ny".
-2. Editor: amount 12 400,00; description "Hyra"; Konto Huset; Kategori Hem; Betalt av Anna (one tap); Delning Kontots standard 60/40; Dag i månaden 25; Första månad: *defaults to oktober 2026 because the 25th is still ahead (AC-9.8)*; Sista månad empty.
+2. Editor: amount 12 400,00; description "Hyra"; Konto Huset; Kategori Boende; Betalt av Anna (one tap); Delning Kontots standard 60/40; Dag i månaden 25; Första månad: *defaults to oktober 2026 because the 25th is still ahead (AC-9.8)*; Sista månad empty.
 3. Spara (online) ⟶ list row "Hyra · 12 400,00 kr · Huset · Varje månad den 25:e · Nästa: 25 okt.".
 4. *On 25 Oct at 06:00 the server creates the expense* ⟶ both phones get "Återkommande utgift tillagd · Hyra · 12 400,00 kr · Huset" (AC-9.6) ⟶ `S-8` shows "Återkommande: Hyra" and "Skapad automatiskt (återkommande) · 25 okt. 06:00".
 5. Offline at step 3 ⟶ Spara disabled with "Kräver anslutning".
diff --git a/docs/ux/ia.md b/docs/ux/ia.md
index 35616a8..22217b7 100644
--- a/docs/ux/ia.md
+++ b/docs/ux/ia.md
@@ -1,10 +1,10 @@
 # Kvitt: information architecture
 
-Status: U1 proposal (2026-10-17). Binding inputs: SPEC §13.2 (navigation model), §13.3 (screens S-1 to S-22), §14.2.4 (UI contract), §6.2 (module ownership). Screen ids are the spec's (`S-1` … `S-22`), not renumbered.
+Status: U1 (2026-10-17), updated in U2 (2026-10-18) for the owner's decisions (`proposed-decisions.md`: three tabs kept, UX-2; category suggestion, UX-3; 14 default categories, UX-8). Look and states of each screen: `design-spec.md`. Binding inputs: SPEC §13.2 (navigation model), §13.3 (screens S-1 to S-22), §14.2.4 (UI contract), §6.2 (module ownership). Screen ids are the spec's (`S-1` … `S-22`), not renumbered.
 
 ## 1. Bottom navigation
 
-**Recommendation: keep the spec's three destinations.**
+**Decided (owner, 2026-10-18, UX-2): the spec's three destinations.**
 
 | Destination | sv | en | Icon (Material Symbols) | Start screen |
 |---|---|---|---|---|
@@ -18,10 +18,10 @@ Why three and not four:
 
 1. **Home already is the account list.** The second most important question ("who owes whom", §13.1) is answered per account on Home (AC-10.2). A separate "Accounts" destination would split the global balance from its breakdown and add a tap to the most common path (Home → account → expense).
 2. **The add flow is the FAB, not a tab.** A tab "Add" would be a navigation item that is really an action; Material 3 discourages that, and the FAB is reachable with the thumb on both phones.
-3. **No fourth destination exists in the spec.** The only candidate that would earn the slot is an **Activity** feed (the other person's latest changes across all accounts, i.e. the audit log as a list). It would be useful together with push notifications (US-13), but it is a new feature, out of scope for the UX sprint (§13.5). It is listed as an owner question in the report, not designed.
+3. **No fourth destination exists in the spec.** The only candidate that would earn the slot is an **Activity** feed (the other person's latest changes across all accounts, i.e. the audit log as a list). It would be useful together with push notifications (US-13), but it is a new feature, out of scope for the UX sprint (§13.5). The owner did not take it for v1 (UX-2).
 4. Three items keep Swedish labels whole: "Inställningar" (13 characters) fits at 200 % font scale in a three-item bar but not in a four- or five-item bar of 411 dp.
 
-Navigation bar style per direction (Expressive "short navigation bar" 64 dp or classic `NavigationBar` 80 dp) is stated on each direction's review page.
+Navigation bar style: Expressive `ShortNavigationBar` (64 dp; fallback `NavigationBar`), `design-spec.md` §3.11.
 
 ## 2. Screen inventory
 
@@ -35,7 +35,7 @@ Kind: **top** = bottom-nav destination; **pushed** = full screen with top app ba
 | S-4 | Översikt / Home | top | `:feature:home` | bottom nav; notification for deletions and `sync_batch` | `HomeRepository` → `HomeState` |
 | S-5 | Konto (account detail) | pushed | `:feature:account` | S-4 account row | `AccountRepository.detail` → `AccountDetailState` |
 | S-6 | Nytt konto / Ändra konto | pushed | `:feature:account` | S-4 "Nytt konto"; S-5 overflow "Ändra konto" | `AccountRepository` |
-| S-7 | Ny utgift / Ändra utgift | pushed, full screen (§13.2) | `:feature:expense` | FAB on S-4 or S-5; S-8 "Ändra"; S-19 editor reuses its fields | `ExpenseRepository.editorOptions`, `preview`, `save` |
+| S-7 | Ny utgift / Ändra utgift | pushed, full screen (§13.2) | `:feature:expense` | FAB on S-4 or S-5; S-8 "Ändra"; S-19 editor reuses its fields | `ExpenseRepository.editorOptions`, `preview`, `save`, `suggestCategory` (UX-3) |
 | S-8 | Utgift (expense detail) | pushed | `:feature:expense` | ledger rows (S-5, S-12, S-16); notification | `ExpenseRepository.detail` → `ExpenseDetailState` |
 | S-9 | Gör upp / Settle up | pushed | `:feature:settle` | S-4 "Gör upp" (global); S-5 "Gör upp det här kontot" (per account); S-10 "Ändra" | `SettlementRepository.start`, `recompute`, `save` → `SettleState` |
 | S-10 | Betalning (settlement detail) | pushed | `:feature:settle` | settlement rows; notification | `SettlementRepository.detail` |
@@ -43,7 +43,7 @@ Kind: **top** = bottom-nav destination; **pushed** = full screen with top app ba
 | S-12 | Statistik, urval (drill-down) | pushed | `:feature:analytics` | tap on a bar or a breakdown row in S-11 (AC-14.8) | `AnalyticsRepository.drillDown` |
 | S-13 | Inställningar / Settings | top | `:feature:settings` | bottom nav | `ProfileRepository.household`, `SessionRepository` (role) |
 | S-14 | Profil | pushed | `:feature:settings` | S-13 | `ProfileRepository` |
-| S-15 | Kategorier (list + editor) | pushed (editor pushed from list) | `:feature:settings` | S-13 | `CategoryRepository` |
+| S-15 | Kategorier (list + editor; 14 default categories, UX-8) | pushed (editor pushed from list) | `:feature:settings` | S-13 | `CategoryRepository` |
 | S-16 | Synkstatus / Sync status | pushed | `:feature:home` | S-4 sync line; S-13 "Synkstatus" | `SyncRepository.status` |
 | S-17 | Uppdatering (banner + dialogs) | banner on S-4, dialogs | `:feature:home` | S-4 when `UpdateState != None`; S-22 "Sök efter uppdatering" | `UpdateRepository` |
 | S-18 | Telefonen har tagits bort / Device removed | flow, start when `Revoked` | `:feature:onboarding` | any 401 `device_revoked` (AC-3.3) | `SessionRepository.acknowledgeRevoked` |
@@ -101,7 +101,7 @@ After saving in S-7 the app returns to where the editor was opened from (S-4 or
 | Own display name and picture | S-14 (and S-2 once for the partner) | S-4 hero, S-13, all avatars, timelines | no ("Kräver anslutning") |
 | Partner's name and picture | partner's own S-14 | everywhere | — |
 | Accounts (name, default ratio, archived, deleted) | S-6 (create, edit), S-5 overflow (archive, unarchive, delete) | S-4, S-7 picker (active only), S-9 rows and "Lägg till konto" (all), S-11/S-12, S-19 picker | no |
-| Categories | S-15 | S-7 grid (active only), S-8, ledger rows, S-11/S-12, S-19 | no |
+| Categories | S-15 | S-7 quick row and "Alla" sheet (active only; selected by the suggestion of UX-3 for new expenses), S-8, ledger rows, S-11/S-12, S-19 | no |
 | Recurring templates | S-19 | S-8 ("Återkommande: Hyra") | no |
 | Expenses | S-7 (create, edit), S-8 (delete) | S-4 (counts, balances), S-5, S-11/S-12, S-16 (pending) | **yes** |
 | Settlements | S-9 (create, edit), S-10 (delete) | S-4/S-5 balances and rows, S-16 (pending) | **yes** |
diff --git a/docs/ux/proposed-decisions.md b/docs/ux/proposed-decisions.md
index 131cefe..c19729c 100644
--- a/docs/ux/proposed-decisions.md
+++ b/docs/ux/proposed-decisions.md
@@ -1,5 +1,7 @@
 # UX sprint: owner answers, findings and proposed spec changes
 
+**Recorded on `main`:** UX-1 … UX-8 became D-047 … D-054 in SPEC v0.8 (`7fb9f6f`), with the category contract v2 (`6b60ef7`). The two new colour tokens were named `slate` (insurance) and `amber` (fees) there; this file uses those names. Still open for a docs agent: the §9.7 hex values of UX-5 (`TODO(U2)` in SPEC v0.8; final values below) and UX-9.
+
 The UX sprint runs in the owner's own session, which has no orchestrator. This file is how its results reach the main line. **No numbers are assigned here:** the decision ids and the spec version are handed out when a docs agent records these (CLAUDE.md › Numbering; next free at the time of writing: D-044, spec v0.6). Ids `UX-1` … are local labels only.
 
 Every entry has: the owner's answer or the finding, the proposed decision text for `docs/decisions.md`, and the spec text to change.
@@ -18,6 +20,7 @@ Every entry has: the owner's answer or the finding, the proposed decision text f
 | History match: most recent or most often? | **Most often.** |
 | Import granularity | Splitwise's categories are finer than Kvitt's ten defaults (several "Övrigt" under different groups); map them better. "Hemförsäkring" must not end up in Övrigt. |
 | Which new default categories? | **All four proposed:** Boende, Försäkringar, Barn, Avgifter & skatter. |
+| U2 design spec, screen catalogue and strings (2026-10-18) | **Approved** ("I approve everything"). |
 
 ## UX-1 Direction: Kalas, muted
 
@@ -39,7 +42,7 @@ Every entry has: the owner's answer or the finding, the proposed decision text f
   * AC-8.6: the category default becomes "the suggestion of §9.11, which falls back to the current rule".
   * New §9.11 "Category suggestion": the text CS-1 to CS-7 of `category-suggestion.md`.
   * New contract file `contracts/category-keywords.csv` (columns `keyword,match,default_key,source`; draft `docs/ux/category-keywords.csv`, 148 entries, none mapping to `other`), listed in §14.8.
-  * §14.2.4: `ExpenseRepository` gains `suspend fun suggestCategory(accountId: AccountId?, description: String): CategoryId?` (computed in `:data` from Room and the keyword table; the editor calls it on description and account changes while the category was not picked by hand).
+  * §14.2.4: `ExpenseRepository` gains `suspend fun suggestCategory(accountId: AccountId?, description: String): CategoryId?` (computed in `:data` from Room and the keyword table; the editor calls it on description and account changes while the category was not picked by hand). The editor's category row also needs `ExpenseEditorOptions.frequentCategoryIds` (UX-9).
   * §14.5: a pure Kotlin function `suggestCategory(description, history, keywords, activeCategories, fallback)` in `:domain` with test vectors (Kotlin only, like analytics).
 
 ## UX-7 Finer Splitwise import mapping (§10.8)
@@ -81,17 +84,18 @@ Every entry has: the owner's answer or the finding, the proposed decision text f
 | **`housing`** | **Housing** | **Boende** | **`key`** | **`indigo`** | **25** |
 | `home` | Home | Hem | `home` | `brown` | 30 |
 | `utilities` | Utilities | Räkningar | `bolt` | `yellow` | 40 |
-| **`insurance`** | **Insurance** | **Försäkringar** | **`shield`** | **new token (UX-5)** | **45** |
+| **`insurance`** | **Insurance** | **Försäkringar** | **`shield`** | **`slate`** (new token, UX-5) | **45** |
 | `transport` | Transport | Transport | `directions_car` | `blue` | 50 |
 | `travel` | Travel | Resor | `flight` | `teal` | 60 |
 | `entertainment` | Entertainment | Nöje | `movie` | `purple` | 70 |
 | `health` | Health | Hälsa | `medical_services` | `red` | 80 |
 | **`children`** | **Children** | **Barn** | **`child_care`** | **`lime`** | **85** |
 | `shopping` | Shopping | Shopping | `shopping_bag` | `pink` | 90 |
-| **`fees`** | **Fees & taxes** | **Avgifter & skatter** | **`account_balance`** | **new token (UX-5)** | **95** |
+| **`fees`** | **Fees & taxes** | **Avgifter & skatter** | **`account_balance`** | **`amber`** (new token, UX-5) | **95** |
 | `other` | Other | Övrigt | `category` | `grey` | 100 |
 
-* **Contract consequences** (one landing, numbered by the docs agent): AC-7.2 table; §9.7 icon set gains `key`, `shield`, `account_balance` (33 tokens) and the colour set gains two tokens (UX-5); §14.1 seed rows with four new fixed ids and a server schema **migration to version 2** inserting them (existing databases, including the development stack, get them on upgrade); `DefaultCategoryKey` and `ColorToken` in §14.2.4 gain the new values and `CategoryIcons.ALL` the new icons; the OpenAPI enums; default-name strings in both languages (`strings.csv`); test vectors that enumerate categories, if any. The Room schema is unchanged (category rows arrive through sync).
+* **Contract consequences** (one landing, numbered by the docs agent): AC-7.2 table; §9.7 icon set gains `key`, `shield`, `account_balance` (33 tokens) and the colour set gains `slate` and `amber` (UX-5); §14.1 seed rows with four new fixed ids and a server schema **migration to version 2** inserting them (existing databases, including the development stack, get them on upgrade); `DefaultCategoryKey` and `ColorToken` in §14.2.4 gain the new values and `CategoryIcons.ALL` the new icons; the OpenAPI enums; default-name strings in both languages (`strings.csv`); test vectors that enumerate categories, if any. The Room schema is unchanged (category rows arrive through sync).
+* **Fakes:** the D-018 empty scenario of `:core:fakes` (§6.2 "Fakes") then has exactly the **14** default categories, not ten; the §6.2 sentence changes with it.
 * **Order matters for existing milestones:** M3 (server) and M5 (`:data`) already landed with ten defaults, so this is a contract change against code on `main`, not only the spec.
 
 ## UX-4 S-1 asks for the display name
@@ -102,9 +106,26 @@ Every entry has: the owner's answer or the finding, the proposed decision text f
 
 ## UX-5 Category colour values (§9.7)
 
-* **Finding:** several provisional §9.7 values fail contrast as icon colours on tinted containers (light orange, yellow, lime).
-* **Decision:** replace the §9.7 hex values with the muted Kalas values, and add two colour tokens for the new categories of UX-8 (values and names from the Kalas revision in progress; filled in when it reports). Existing token names unchanged.
-* **Spec:** §9.7 table values.
+* **Finding:** several provisional §9.7 values fail contrast as icon colours on tinted containers (light orange, yellow, lime), and the bright set tires the eye over years of daily use (owner, UX-1).
+* **Decision:** replace the §9.7 hex values with the muted Kalas values below and add two colour tokens, `slate` (Försäkringar) and `amber` (Avgifter & skatter), for the new categories of UX-8. Existing token names unchanged. Light `brown` is #6B5244, darker than the first muted draft (#7A5F50), so it stays apart from `amber`. Values from `design-spec.md` §2.3, which also gives each token's container (the colour at 18 % over surface in light, 24 % in dark) and the contrast ratios (every value ≥ 3:1 as a graphic against surface, card and its container).
+* **Spec:** the §9.7 colour table becomes (14 tokens; the server's validation list and `ColorToken` gain `slate` and `amber`):
+
+| Token | Light | Dark | §9.7 today (light / dark) |
+|---|---|---|---|
+| `green` | `#4E7A55` | `#8FB896` | `#2E7D32` / `#81C784` |
+| `orange` | `#B26A3E` | `#E8AA7E` | `#E65100` / `#FFB74D` |
+| `brown` | `#6B5244` | `#B8A090` | `#6D4C41` / `#BCAAA4` |
+| `yellow` | `#9C7C2A` | `#E6C67A` | `#F9A825` / `#FFF176` |
+| `blue` | `#4A78A8` | `#98C0E6` | `#1565C0` / `#90CAF9` |
+| `teal` | `#367F82` | `#8CCAC8` | `#00796B` / `#80CBC4` |
+| `purple` | `#865891` | `#C59CDA` | `#6A1B9A` / `#CE93D8` |
+| `red` | `#A84B45` | `#DC8F86` | `#C62828` / `#EF9A9A` |
+| `pink` | `#AE5D7C` | `#EFB0BE` | `#AD1457` / `#F48FB1` |
+| `grey` | `#6C7177` | `#B0B2B3` | `#546E7A` / `#B0BEC5` |
+| `indigo` | `#45508D` | `#969DD8` | `#283593` / `#9FA8DA` |
+| `lime` | `#73822F` | `#B9CC86` | `#827717` / `#DCE775` |
+| **`slate`** | **`#5E2D4E`** | **`#B0719A`** | new |
+| **`amber`** | **`#937D63`** | **`#DFCFC0`** | new |
 
 ## UX-6 Smaller UX rules found during U1 (no owner question needed)
 
@@ -113,12 +134,48 @@ Every entry has: the owner's answer or the finding, the proposed decision text f
 | Saving an expense shows "Utgiften sparades" without "Ångra" (an undo would be an audited create + delete that also notifies the partner). | §13.3 S-7 |
 | Account detail of an archived account hides the FAB (archived accounts are not offered for new expenses, AC-6.4); settling stays available. | §13.3 S-5 |
 | The per-account settle-up keeps the direction swap (§9.4: per-account settle-ups go either way) and lets the amount be edited (AC-11.4 "defaults to"); its one account row is fixed. | §13.3 S-9 |
-| Avatar initials colours are two tokens `avatar-1`, `avatar-2`; the slot is a stable function of the user id (AC-4.5). | AC-4.5 |
+| Avatar initials colours are two tokens `avatar-1`, `avatar-2`; the slot is a stable function of the user ids: slot 1 for the household user whose id sorts first (string comparison), slot 2 for the other, so the two never share a colour and the admin (created first, UUIDv7) is slot 1 on both phones (`design-spec.md` §2.5). | AC-4.5 |
 | `strings.csv` keys are renamed in U2 to the feature-module prefixes of D-017 (`onboarding_`, `home_`, `account_`, `expense_`, `settle_`, `analytics_`, `settings_`; shared keys without a feature prefix). | none (U2 work) |
 
+## UX-9 Contract additions needed by the design
+
+* **Finding (U2):** two binding states of `design-spec.md` need values that the UI contract (§14.2.4) does not carry. Both are computed on the phone from Room; no server or sync change.
+* **Decision (proposed):** add the two members below to `:core:model`, implement them in `:data` and `:core:fakes` (every fake scenario computes them from its own data). Recorded together with UX-3, since (1) is part of the same editor change.
+
+**(1) `ExpenseEditorOptions.frequentCategoryIds`**
+
+* **Why / where:** S-7's category row (and S-19's editor) shows five large tiles plus "Alla": the five categories used most in the selected account, so the usual category is one tap away even when the suggestion (UX-3) guesses wrong (`design-spec.md` §3.7 `CategoryQuickRow`; frames `S-7-editor`, `S-7-editor-200`, `S-19-recurring-editor`).
+* **Spec text (§14.2.4):**
+
+```kotlin
+data class ExpenseEditorOptions(
+    val me: User, val partner: User, val accounts: List, val categories: List,
+    val defaultAccountId: AccountId?, val defaultCategoryFor: Map, val today: String,
+    val frequentCategoryIds: Map>,   // per active account: up to 5 active categories, most used first
+)
+```
+
+  Reading: for each active account, the active (non-archived, non-deleted) categories ordered by the number of non-deleted expenses in that account that use them, descending; ties by `sort_order` ascending; at most 5 entries; categories with no use in the account are left out (the list may be shorter or empty). Counted over every expense in the phone's Room copy (both users, imported and recurring included), so both phones show the same order after sync; it is not a per-phone history like AC-8.6's "last used on this phone". The flow re-emits when expenses or categories change; the editor reads it once when it opens (and on an account change) so tiles do not move while the user looks at them.
+* **UI rule (design, not contract):** the row shows the account's list, filled up to five from the remaining active categories in `sort_order`; the selected category (suggested, defaulted or picked in "Alla") takes the fifth place when it is not among them.
+* **Fallback until it exists:** the row shows the selected category first, then the first active categories in `sort_order` up to five.
+
+**(2) `CategoryRepository.deletable(id)`**
+
+* **Why / where:** the category editor shows "Ta bort kategori" disabled with the reason before any tap when deletion is not allowed (AC-7.6), the same way the account menu uses `AccountDetailState.deletable` (`design-spec.md` §4 S-15; frame `S-15-category-editor-custom`).
+* **Spec text (§14.2.4, `CategoryRepository`):**
+
+```kotlin
+fun deletable(id: CategoryId): Flow   // false for a default category (defaultKey != null) or one used by a non-deleted expense or non-deleted recurring template (AC-7.6)
+```
+
+  Computed from Room. The server stays the authority: `delete` can still return `Failed("category_is_default")` or `Failed("category_in_use")` (for example when the partner's new expense has not synced yet), and the UI shows the same reason inline. The editor uses `deletable` with the category's `defaultKey` to pick the text: default → `settings_category_delete_blocked_default_short`; in use and archived → `settings_category_delete_blocked_in_use_archived`; in use and active → `settings_category_delete_blocked_in_use`.
+* **Fallback until it exists:** for a custom category "Ta bort kategori" is enabled; after a tap, `Failed("category_in_use")` shows the in-use reason inline under the button (default categories need no data: `defaultKey` already says it).
+
+* **Spec:** §14.2.4 as above (contract change: spec version bump and decision entry, numbered by the docs agent); `:core:fakes` and `:data` implement both in the same change (CLAUDE.md › The UI contract is the contract).
+
 ## Status
 
 * U1: delivered (`d27f92e` rebased on `main`), direction picked.
 * Done: muted Kalas revision (approved); keyword list and replay for 14 categories and "most often".
-* In progress: Kalas mocks with the 14 categories and two new colour tokens; 
-* Next: owner approves the muted palette → U2 `design-spec.md`, renamed `strings.csv`, per-screen PNGs, and the final values for UX-3, UX-5 and UX-8.
+* U2 (2026-10-18): `design-spec.md` (binding design), `strings.csv` with D-017 prefixes, the screen catalogue `screens/` (80 frames, light and dark); the final values of UX-5 above; UX-9 found while writing the design spec.
+* Next: a docs agent records UX-1 … UX-9 on `main` (decision numbers and spec version from the orchestrator).
diff --git a/docs/ux/screens.md b/docs/ux/screens.md
deleted file mode 100644
index a1d7477..0000000
--- a/docs/ux/screens.md
+++ /dev/null
@@ -1,266 +0,0 @@
-# Kvitt: screens
-
-Status: U1 proposal (2026-10-17), direction-independent. What each screen contains, in which order, in which states, and what each action does. How it looks is the chosen direction's job (`directions/`); the exact words are in `strings.csv`. Rules (rounding, allocation order, sync, limits) are the spec's and are referenced, never restated as new rules.
-
-Conventions:
-
-* **States** per SPEC §13.3: loaded, empty, loading (first sync only), offline (with pending changes), error (a failed online action), long content (long Swedish names, 7-digit amounts, 200 % font scale).
-* "Kräver anslutning" = the action is disabled while offline per §9.9, with that text as a supporting line or tooltip, never a silent disabled control.
-* "Kunde inte nå servern" = an online action failed with a network error; nothing changed locally (§9.9). Shown as a snackbar on the screen that triggered it, with "Försök igen" when the action is repeatable.
-* Server error codes (§14.3.2) map to inline text where a field is at fault (`name_taken` on the name field) and to a snackbar otherwise. Copy keys in `strings.csv` prefix `error_`.
-* Amounts: tabular figures, right-aligned in lists, never ellipsized; at 200 % they move under the description instead. Direction is always words plus colour (§13.1).
-* Pending-sync marker: a small `cloud_upload` (or `schedule`) icon with TalkBack label "Väntar på synk" on every row and detail whose `pending == true`.
-* Every screen reads local data, so after the first sync there is no loading state except where noted.
-
----
-
-## S-1 Pairing (Para ihop)
-
-**Purpose:** connect this phone to the household with an invite code (US-1, US-2, AC-2.6, AC-2.7).
-
-Content, in priority order:
-1. App name "Kvitt" and the one-line purpose "Delade utgifter för två".
-2. Invite code field: 12 characters, displayed and typed in groups of 4 (`7K3M-QX9D-2HPA`); input is uppercased and normalised per §8.3 as the user types (hyphens inserted automatically, `O`→`0`, `I`/`L`→`1`). Trailing "Klistra in" button (reads the clipboard; accepts a full link and extracts the code and server).
-3. Explanation: "Koden får du av Jacob: från en länk eller från Inställningar › Bjud in partner." For the admin's first run: "Koden skrivs ut när servern sätts upp."
-4. "Ditt namn" field (1–40, §9.6), required on a first pairing: AC-2.4 sends the display name with the accept call and the app cannot know the invite kind beforehand (the server ignores it for `device` invites). Hidden in the "Para ihop telefonen igen" variant. The partner confirms it again on S-2.
-5. Server address field, prefilled `https://icehack.asuscomm.com:8443` (release), collapsed under "Serveradress" as a secondary row since it rarely changes; expanded automatically when the link carried a different server.
-6. Primary button "Para ihop".
-
-States:
-* **Loaded/empty:** code field focused, keyboard open (uppercase letters and digits).
-* **From link:** both fields filled, focus on "Para ihop".
-* **Loading:** button shows progress "Parar ihop…", fields read-only. After success: full sync runs (S-4 loading state), then the notification permission prompt (AC-2.9).
-* **Error** (inline under the code field, button enabled again): `invite_invalid` "Koden fungerar inte. Den kan ha gått ut eller redan använts. Be Jacob om en ny." · network "Kunde inte nå servern. Kontrollera adressen och anslutningen." · `rate_limited` "För många försök. Vänta en stund och försök igen." · `http://` in release (AC-2.8) "Adressen måste börja med https://" on the server field.
-* **Unrecognised variant** (§14.4.8): header "Para ihop telefonen igen" and a line "Dina utgifter finns kvar på telefonen."
-* **Offline:** the button stays enabled; tapping it gives the network error (pairing never queues).
-* **Long content:** single column, scrolls; at 200 % the code field wraps to two lines of groups.
-
-Actions: Para ihop (primary); Klistra in. No FAB, no bottom nav.
-
-## S-2 Partner profile step (Din profil)
-
-**Purpose:** the partner confirms their name and optionally adds a picture before entering the app (AC-2.10).
-
-Content: 1. "Du går med i Jacobs hushåll" with Jacob's avatar and name; 2. own avatar (initials on `avatar-2` until a picture is chosen) with "Lägg till bild" (Android photo picker, AC-4.3); 3. display name field prefilled from the pairing call (1–40 characters, §9.6); 4. "Fortsätt".
-
-States: **uploading picture** (progress on the avatar; Continue waits); **error** `image_invalid` / `unsupported_media_type` / `payload_too_large` as a snackbar "Bilden kunde inte användas" with the reason; network "Kunde inte nå servern" — the user may continue without the picture; **validation** empty name blocks Continue with "Skriv ett namn".
-
-Actions: Fortsätt (primary, saves name if changed); Lägg till bild / Byt bild / Ta bort bild.
-
-## S-3 Waiting for partner (Väntar på din partner)
-
-**Purpose:** the admin's Home until the partner has joined (AC-5.1).
-
-Content: illustration-free empty state on the Home destination: title "Väntar på din partner"; body "Kvitt behöver er båda innan ni kan börja. Bjud in din partner med en länk."; if an invite is pending: "Inbjudan gäller till mån 19 okt. 21:40"; primary "Bjud in partner" (→ S-21, creates a `partner` invite; if one is pending S-21 shows it and offers a new one). Bottom nav visible; no FAB; no balance; no "Nytt konto".
-
-States: **offline** "Bjud in partner" disabled with "Kräver anslutning". **Partner joins** while the screen is open: the next sync replaces it with S-4 (empty state "Skapa ert första konto").
-
-## S-4 Home (Översikt)
-
-**Purpose:** answer "who owes whom" in one glance and start the two most common actions: add an expense and settle up (US-6, AC-10.1, AC-10.2).
-
-Content, in priority order:
-1. **Update banner** (only when `UpdateState` is `Available`, `Downloading`, `Failed` or `NeedsInstallPermission`; S-17).
-2. **Balance hero:** both avatars; wording per §9.3 from the viewer's side: "Anna är skyldig dig", "Du är skyldig Anna", or "Ni är kvitt"; the amount (`amount-hero`) on its own line; "Gör upp" button when the global balance ≠ 0. TalkBack reads one sentence: "Du är skyldig Anna 3 250,00 kronor".
-3. **Sync line**, small, under the hero: "Synkad för 3 min sedan" (relative time per §9.8); appended "· 2 ändringar väntar på synk" when `pendingCount > 0`; "Offline" prefix when offline; "Synkar…" while running; warning style with "· 1 ändring synkades inte" when `rejectedCount > 0` or "Telefonens klocka går fel" when `clockSkew`. Tap → S-16. Never a blocking banner.
-4. **Konton** section header with trailing "Nytt konto" (text button; "Kräver anslutning" offline).
-5. **Active accounts** in `created_at` order (AC-6.6): name (ellipsize at one line, 2 lines at 200 %), wording ("Anna är skyldig dig", "Du är skyldig Anna", "Kvitt"), amount (omitted when Kvitt), "11 utgifter i oktober" (plural, current month name). Tap → S-5.
-6. **"Arkiverade (1)"** collapsed row; expands in place to the archived accounts with an "Arkiverat" badge (AC-6.4).
-7. FAB "Lägg till utgift" (extended, collapses to icon-only while scrolling down; TalkBack "Lägg till utgift").
-
-States:
-* **Loading** (first sync after pairing only): hero and rows as skeletons, text "Hämtar era utgifter…"; the FAB is hidden until the first sync finishes or fails (then the local, possibly empty, state is shown).
-* **Empty** (partner joined, no accounts): no hero amount ("Ni är kvitt" is not meaningful yet: hero shows both avatars and "Välkommen, Anna och Jacob"), empty state "Skapa ert första konto" with one line ("Ett konto samlar utgifter som hör ihop, till exempel Vardag eller Japan 2027.") and the primary button "Nytt konto". No FAB (AC-8.6 needs an account).
-* **Settled:** hero "Ni är kvitt", no amount, no "Gör upp".
-* **Offline:** everything works; sync line "Offline · 2 ändringar väntar på synk"; "Nytt konto" disabled with "Kräver anslutning".
-* **Error:** not applicable (local data); a failed sync shows as the sync line in warning style ("Synk misslyckades · försök igen om en stund"), never a dialog.
-* **Long content:** account names up to 60 characters ellipsize in one line (two at 200 %); 7-digit amounts ("1 234 567,89 kr") fit the hero on one line at 100 % and wrap under the wording at 200 %.
-
-Actions: FAB → S-7 (account defaulted per AC-8.6); Gör upp → S-9 global; account row → S-5; Nytt konto → S-6; sync line → S-16; banner → S-17.
-
-## S-5 Account detail (Konto)
-
-**Purpose:** one account's balance and its ledger; settle just this account (AC-10.3, US-8).
-
-Content:
-1. Top app bar: account name (ellipsize), overflow menu: "Ändra konto" (→ S-6), "Arkivera" / "Återställ från arkiv", "Ta bort konto" (enabled only when `deletable`; otherwise disabled with the reason "Kontot kan inte tas bort så länge det har utgifter, betalningar eller återkommande utgifter"). All three "Kräver anslutning" offline.
-2. Header: default split "50 / 50 · Jacob och Anna" (names in percent order, viewer as "du" is not used here; names keep the line unambiguous); "Arkiverat" badge if archived; balance wording and amount (§9.3, account wording "Kvitt"); "Gör upp det här kontot" when non-zero.
-3. Ledger grouped by month, newest first: sticky month header "Oktober · 7 457,90 kr" (household spend of the month, settlements excluded); rows ordered per AC-8.10:
-   * **Expense row:** category icon on its colour container, description (one line), date ("16 okt."), amount (right), effect line (§9.2: "Du lånade ut 432,17 kr" in owed colour, "Du lånade 623,80 kr" in owing colour, "Inte inblandad" / "Betalade för dig själv" neutral), pending marker. Tap → S-8.
-   * **Settlement row:** `payments` icon, "Anna betalade dig 800,00 kr" or "Du betalade Anna 800,00 kr", "· 2 konton" when more than one allocation (AC-11.7), date, pending marker. Tap → S-10.
-4. FAB "Lägg till utgift" (account preset to this account).
-
-States: **empty** (new account) "Inga utgifter än" + "Lägg till den första utgiften i Vardag." (FAB still there); **archived** badge, "Återställ från arkiv" in the header as a tonal button, FAB still offered? **No**: archived accounts are hidden from the account picker for new expenses (AC-6.4), so the FAB is hidden on an archived account; settling stays available; **offline** rows work, overflow actions disabled; **error** archive/delete failures as snackbar (`account_not_empty` "Kontot har fortfarande utgifter, betalningar eller återkommande utgifter"); **long content** month totals and row amounts at 7 digits stay on one line at 100 %; at 200 % the amount moves to the line under the description.
-
-Actions: FAB → S-7; Gör upp det här kontot → S-9 (one fixed row); row → S-8/S-10; overflow actions; archive asks no confirmation (reversible) and shows a snackbar "Vardag arkiverades" with "Ångra"; delete asks "Ta bort kontot Vardag? Det går inte att ångra." (soft delete on the server, but not restorable from the app).
-
-## S-6 Account editor (Nytt konto / Ändra konto)
-
-**Purpose:** create or edit an account's name and default split (AC-6.1 to AC-6.3). Online only.
-
-Content: 1. Name field (1–60, unique: `name_taken` → "Det finns redan ett konto som heter Vardag"); 2. "Standarddelning": a slider (0–100 % in steps of 1, snapping to 50) plus two percent fields "Jacob %" and "Anna %" that always sum to 100 (typing in one updates the other; two decimals allowed, comma or point); quick presets as chips "50/50", "60/40", "40/60"; 3. preview "Av 100,00 kr: Jacob 60,00 kr, Anna 40,00 kr"; 4. editing only: hint "Ändringen gäller nya utgifter. Befintliga utgifter behåller sin delning." (AC-6.3); 5. "Spara" (top app bar action and/or bottom button, per direction).
-
-States: **offline** all fields disabled, banner "Kräver anslutning. Konton kan bara ändras när telefonen är online." , Spara disabled; **saving** progress on Spara; **error** network snackbar; validation inline.
-
-Actions: Spara; Avbryt (back; asks "Slänga ändringarna?" only if something changed).
-
-## S-7 Expense editor (Ny utgift / Ändra utgift) — the add-expense flow
-
-**Purpose:** record an expense in ten seconds (SPEC §13.1): open app, tap add, type amount, type description, tap save. Full screen, not a sheet (§13.2). Works offline (AC-8.2).
-
-### Layout, top to bottom
-
-1. **Top app bar:** close (`close`, TalkBack "Avbryt"), title "Ny utgift" / "Ändra utgift", trailing "Spara" text button (enabled when valid). Edit mode adds overflow "Ta bort".
-2. **Amount** (focus on open): display-size `amount-large`, tabular, with "kr" suffix; input rules §9.6 (digits, one `,` or `.`, max two decimals, no sign, max 10 000 000,00). Empty shows "0,00" as a hint in `on-surface-variant`, not as a value.
-3. **Description** field "Beskrivning" (1–100), single line, IME action "Spara" (saves when valid, otherwise moves focus to the first invalid field).
-4. **Defaults row**: one chip per field, each showing its current value, each one tap to change:
-   * **Konto:** "Vardag" (AC-8.6: last used on this phone if still active, else the oldest active). Opens a picker of active accounts (bottom sheet list with balances). Changing the account in mode `default` re-copies the ratio (AC-8.8) and changes the category default only if the user has not picked a category yet.
-   * **Kategori:** icon + name of the default (AC-8.6: last used in that account on this phone if active, else Övrigt). Opens the category grid (icon + colour + name, active categories, `sort_order`).
-   * **Datum:** "Idag" (also "Igår", else "16 okt."); opens the Material date picker; range per §9.6.
-   * **Betalt av:** "Du" or "Anna" with the avatar; **one tap toggles** between the two users (no picker: there are exactly two) (AC-8.3).
-   * **Delning:** "Kontots standard 50/50" — **one tap opens the split section** in place (below the chips) and scrolls it into view.
-5. **Split section** (collapsed by default; expanded when the mode is not `default` or the user tapped Delning):
-   * Mode chooser (single-select connected buttons / segmented, wraps to two rows in Swedish): "Kontots standard 60/40" · "Egen fördelning" · "Exakta belopp" · "Allt på en person".
-   * `ratio`: same control as S-6 (slider + two percent fields summing to 100).
-   * `exact`: two money fields; the user types one, the other is read-only "amount minus typed" (AC-8.7). Typed > amount → inline error "Andelen är större än beloppet" and Save disabled.
-   * `full`: choose the bearer: "Jacob" / "Anna" (two avatar toggles), independent of the payer.
-   * **Live preview** for all modes, from `ExpenseRepository.preview` (the domain computes it; the UI never computes shares): "Du 206,00 kr · Anna 206,00 kr", plus the effect line that the row will show ("Du lånade ut 206,00 kr"). Hidden while `preview` returns null.
-6. **Anteckningar:** collapsed as "Lägg till anteckning"; expands to a multi-line field (0–1000).
-7. **Keypad / keyboard:** while the amount has focus, the numeric input is shown at the bottom (each direction states whether it is an in-app keypad with a guaranteed "," key or the system keyboard with `KeyboardType.Decimal`). Focus moves to the description with "Nästa"/IME or by tapping it; the system text keyboard replaces the keypad.
-8. Edit mode only, at the end: a destructive "Ta bort utgift". (Who created the expense is shown on S-8 as the first timeline line; `ExpenseDraft` carries no creator.)
-
-### The ten-second path (binding interaction)
-
-| Step | User does | Screen does |
-|---|---|---|
-| 1 | Taps FAB on S-4 | S-7 opens with amount focused, keypad up, all defaults set |
-| 2 | Types `412` | Amount shows "412", Save still disabled (description missing) |
-| 3 | Taps "Beskrivning", types "Systembolaget" | Text keyboard; Save enabled |
-| 4 | Taps "Spara" (app bar) or IME "Spara" | Saves locally (`SaveResult.Saved`), closes, snackbar "Utgiften sparades" (no "Ångra": an undo would be a create plus a delete, both audited and pushed to the partner), the new row is visible on return; sync runs after 2 s (AC-15.3) |
-
-The split override is one tap from step 3 (the Delning chip). Payer change is one tap. Nothing else needs touching for the common case.
-
-### States
-
-* **Validation** (inline, from `SaveResult.Invalid` field errors, mapped by token): amount missing "Skriv ett belopp"; amount out of range "Beloppet måste vara mellan 0,01 kr och 10 000 000,00 kr"; description empty "Skriv en beskrivning"; date out of range "Datumet ligger för långt fram"; exact mismatch as above.
-* **Offline:** no difference; after saving, the snackbar says "Sparad. Synkas när du är online."
-* **No accounts:** the editor is not reachable (no FAB). **Only archived accounts:** same (FAB hidden; AC-6.4).
-* **Edit mode for an expense the other user created:** same editor, no warning; the save records the change in the timeline ("Du ändrade …").
-* **Discard:** close with changes asks "Slänga utgiften?" (Släng / Fortsätt redigera).
-* **Long content:** 7-digit amount scales down one type step rather than wrapping; at 200 % the chips wrap to several rows and the split section scrolls; Spara stays in the app bar so it is always reachable.
-
-## S-8 Expense detail (Utgift)
-
-**Purpose:** everything about one expense and who changed what (US-13, AC-12.4).
-
-Content: 1. amount (large) and description; category chip; 2. facts list: Konto, Datum ("fredag 16 oktober 2026"), Betalt av (avatar + name), Delning (mode label, e.g. "Kontots standard 50/50", "Egen fördelning 70/30", "Exakta belopp", "Allt på Anna") and each user's share ("Du 623,80 kr · Anna 623,80 kr"), the effect line; "Återkommande: Hyra" when `recurringTemplateName != null` (AC-8.11); notes (full text); 3. **Historik** timeline, oldest first, newest last (AC-12.4): actor avatar, sentence, "· 16 okt. 19:40"; create line "Anna skapade den här"; update lines one per changed field ("Du ändrade beloppet från 1 147,60 kr till 1 247,60 kr"; split changes as "Du ändrade delningen från 50/50 till 70/30"); recurring source "Skapad automatiskt (återkommande)"; import source "Importerad från Splitwise"; pending last line "Din ändring väntar på synk" (AC-12.5).
-
-Actions: "Ändra" (→ S-7 edit), "Ta bort" (confirm "Ta bort utgiften? Den försvinner för er båda." → soft delete, returns to the previous screen with snackbar "Utgiften togs bort").
-
-States: **deleted** (opened from a deletion notification or the row was deleted while open): banner "Anna tog bort den här utgiften · 17 okt. 09:12", content muted, no Edit/Delete; **pending** marker next to the amount; **missing** (entity not in the database yet): "Utgiften finns inte på telefonen än" with "Synka nu"; **offline** fully readable, edit and delete allowed.
-
-## S-9 Settle up (Gör upp)
-
-**Purpose:** record a payment between the two users and spread it over accounts (US-7, US-8, AC-11.3 to AC-11.6). Works offline.
-
-Content, in order:
-1. **Direction:** "Du betalar Anna" with a swap control "Byt riktning" (TalkBack). Default per AC-11.3 / AC-11.4.
-2. **Amount** (large money field), default = absolute global (or account) balance, empty when zero.
-3. **Fördelning** list, one row per account from `SettleState.lines`: account name; "Skuld 3 084,50 kr" (the payer's debt in that account, `payerNetOre`, words: "Du är skyldig 3 084,50 kr" / "Anna är skyldig dig 1 184,50 kr" when the payer is owed there); editable allocated amount; per-row remove; over-settle warning row text "Det här reglerar mer än som är skyldigt i Japan 2027" (AC-11.5) in warning colour with `warning` icon.
-4. **"Kvar att fördela 0,00 kr"** live indicator; when negative: "250,00 kr för mycket"; colour plus words.
-5. **"Lägg till konto"** → picker of all non-deleted accounts not yet listed (active and archived, AC-11.3).
-6. Info line when an account is left non-zero because the payer is owed there (§9.4 consequence): "Vardag påverkas inte: där är Anna skyldig dig 1 184,50 kr."
-7. Datum (Idag), Anteckning (0–200).
-8. **Bekräfta** (primary, enabled only when `canConfirm`), Avbryt.
-
-Behaviour: editing the total amount re-runs the proposal (`recompute` with `allocations = null`) unless the user has edited a row, in which case it keeps the rows and updates "Kvar att fördela" (the user can tap "Föreslå igen" to re-run). Swap direction re-runs the proposal. Confirming with any warned row shows a dialog repeating the warning(s) (one line per warned row, the same text as on the row: "Det här reglerar mer än som är skyldigt i Japan 2027") with "Bekräfta ändå" / "Ändra".
-
-Per-account variant (from S-5): one fixed row for that account (not removable), no "Lägg till konto", no info line.
-
-States: **no accounts** cannot occur (button hidden); **zero balance** amount empty, rows appear after typing; **editing** title "Ändra betalning", proposal computed with this settlement excluded (AC-11.6); **offline** identical; **long content** at 200 % each row stacks name / debt / field vertically.
-
-## S-10 Settlement detail (Betalning)
-
-Content: "Anna betalade dig 800,00 kr" headline with both avatars and an arrow from payer to recipient; Datum ("tisdag 6 oktober 2026"); Anteckning; **Fördelning** per account with amounts; Historik timeline as in S-8 (diff lines "Du ändrade beloppet …", "… ändrade fördelningen"). Actions: Ändra (→ S-9 editing), Ta bort (confirm "Ta bort betalningen? Saldona räknas om för er båda."). States: deleted, pending, missing as S-8.
-
-## S-11 Analytics (Statistik)
-
-**Purpose:** how much the household spends, when, on what and in which account (US-11, FR-14).
-
-Content:
-1. **Filters** (sticky under the app bar, wrap on two rows in Swedish):
-   * Period chip "Senaste 6 mån" → menu with the presets of AC-14.2 (Denna månad, Senaste 3 mån, Senaste 6 mån, Senaste 12 mån, I år, All tid, Anpassad…); Anpassad opens the Material date range picker; the resolved range is shown under the chips ("1 maj – 17 okt. 2026").
-   * Three binary toggles (connected buttons): Månad / Vecka · Hushållet / Min del · Kategori / Konto.
-2. **Totalt** for the range ("178 211,40 kr"), with the measure in words ("Hushållets utgifter" / "Din del").
-3. **Utgifter över tid:** stacked bars, one per bucket (all buckets, empty ones as zero, AC-14.7), stacked by group (category colours or account colours), y-axis in kronor (no decimals on the axis), x-axis "maj, jun …" or "v. 35"; legend below (group colour + name, only groups present); tap a bar → S-12 for that bucket; horizontally scrollable when there are more buckets than fit (weeks), newest at the right and scrolled into view.
-4. **Per kategori:** horizontal bars sorted descending, each row: icon, name, bar, amount, percent (one decimal, half-up, AC-14.10); tap → S-12.
-5. **Per konto:** same form; archived accounts included with "Arkiverat" badge (AC-14.9).
-
-Grouping applies to chart 1 only; charts 2 and 3 are always shown (AC-14.6). Settlements never count (AC-14.6).
-
-Accessibility: each chart has a content description that summarises it in one sentence ("Utgifter över tid, sex månader: högst i september med 41 484 kronor, lägst i maj med 24 710 kronor") and the bars are focusable one by one ("September: 41 484 kronor, Resor största kategori"). Breakdown rows are ordinary list items.
-
-States: **empty** "Inga utgifter i den här perioden" with "Byt period" (charts hidden, filters remain); **loading** none (local); **offline** identical; **long content** category names ellipsize in breakdown rows, amounts never; at 200 % the breakdown row puts the bar on its own line.
-
-No FAB on Statistik.
-
-## S-12 Analytics drill-down
-
-Content: title naming the filter ("Mat & hushåll, oktober 2026", "v. 41, Vardag", "Japan 2027, senaste 6 mån"), the measure ("Hushållet"/"Min del") as subtitle, total, then expense rows exactly as in S-5 (with the account name added to the row's secondary line, since rows come from several accounts), newest first. Tap → S-8. Empty cannot occur (only reachable from a non-zero number); if data changed meanwhile: "Inga utgifter längre".
-
-## S-13 Settings hub (Inställningar)
-
-Content: 1. household card: own avatar, name, "Du · Administratör" / "Du"; partner's avatar and name ("Anna · Partner"); 2. rows with leading icons and chevrons: Profil (`person`), Kategorier (`category`), Återkommande utgifter (`event_repeat`), Enheter (`devices`), Bjud in partner (`person_add`, admin before join) or Para ihop en enhet (`qr_code`/`add_link`, admin after join), Synkstatus (`sync`, with trailing "2 väntar" when pending), Om Kvitt (`info`, trailing "Uppdatering finns" when available). Rows that lead to online-only screens stay enabled; the screens themselves show their offline state.
-
-## S-14 Profile (Profil)
-
-Content: large avatar with "Byt bild" and "Ta bort bild" (when a picture exists); display name field (1–40); Spara. Picture changes apply immediately on success (no Save needed); name needs Spara. States: offline (all disabled, "Kräver anslutning"), uploading (progress over the avatar), error codes as in S-2.
-
-## S-15 Categories (Kategorier)
-
-**List:** active categories in `sort_order`: drag handle (long-press to drag, plus "Flytta upp"/"Flytta ned" TalkBack actions), icon on colour container, name (custom name or localised default), chevron; "Arkiverade (n)" collapsed section; app bar action "Ny kategori". Reorder saves on drop (online; offline: drag handles hidden and a line "Kräver anslutning för att ändra").
-
-**Editor** (pushed): name field (1–40; for a default category, empty name means "use the default name"; "Återställ namn" button shown when a custom name exists, AC-7.4); live preview chip; **icon grid** of the 30 icons of §9.7 (6 per row at 411 dp, 48 dp targets, selected with outline + check); **colour swatches** of the 12 tokens (each 48 dp, selected with check, TalkBack names "Grön", "Orange" …); Spara; overflow "Arkivera" / "Återställ från arkiv"; "Ta bort kategori" only when allowed, otherwise disabled with the reason: default "Standardkategorier kan inte tas bort, bara arkiveras", in use "Kategorin används av utgifter eller återkommande utgifter. Arkivera den i stället."
-
-## S-16 Sync status (Synkstatus)
-
-Content: 1. status headline: "Synkad för 3 min sedan" / "Synkar…" / "Offline"; 2. clock-skew warning card when `clockSkew` ("Telefonens klocka går fel. Rätta den och synka igen.") — shown first; 3. "Väntande ändringar (2)" list of pending ledger rows (as in S-5, with "ny"/"ändrad"/"borttagen" tag), tap → S-8/S-10; 4. "Ändringar som inte synkades" list from `rejections`: summary (description · amount), the reason text per §14.4.6, the time; tap opens the current version (read-only S-8/S-10); "Ta bort från listan" per item; 5. "Synka nu" (primary; disabled with progress while running; offline "Kräver anslutning").
-
-Empty: "Allt är synkat" with the last sync time.
-
-## S-17 Update available (Uppdatering)
-
-* **Banner on S-4** (top of content, dismissible for the session): "Version 1.2 finns" + "Uppdatera" / "Senare".
-* **First time only**, before downloading (`NeedsInstallPermission` or not yet granted): dialog "Tillåt installation" — "Android frågar en gång om Kvitt får installera appar. Välj Tillåt från den här källan och gå tillbaka." — buttons "Fortsätt" (opens the system setting) / "Avbryt".
-* **Downloading:** banner shows "Laddar ner… 64 %" with a linear progress indicator (determinate); the app stays usable.
-* **Verified:** the system installer appears (out of our hands).
-* **Failed** (AC-16.4): banner "Nedladdningen misslyckades. Försök igen." + "Försök igen".
-* **Offline:** "Uppdatera" disabled with "Kräver anslutning".
-
-## S-18 Device removed (Telefonen har tagits bort)
-
-Full screen, no navigation: icon `phonelink_erase`; title "Den här telefonen har tagits bort"; body "Åtkomsten togs bort och allt Kvitt-data på telefonen har raderats. Be Jacob om en ny kod för att para ihop den igen." (member) / "…Skapa en ny kod på servern med kvitt-server pair-device." (admin, rare); primary "Para ihop igen" → S-1.
-
-## S-19 Recurring expenses (Återkommande utgifter)
-
-**List:** rows: description, amount, account, "Varje månad den 25:e", "Nästa: 25 okt." (`next_occurrence_date`) or "Pausad" badge, payer avatar; app bar "Ny"; empty state "Inga återkommande utgifter" + "Hyra, el och streaming kan läggas in automatiskt varje månad." + "Ny återkommande utgift".
-
-**Editor** (pushed): the S-7 fields without date (amount, description, Konto (active accounts), Kategori, Betalt av, Delning, Anteckningar) plus "Dag i månaden" (1–31 picker; for 29–31 a hint "I kortare månader används sista dagen"), "Första månad" (month picker, default per AC-9.8, back to 12 months), "Sista månad (valfritt)", "Pausad" switch; Spara; overflow "Ta bort" (confirm "Ta bort Hyra? Utgifter som redan skapats finns kvar."). Hint under the fields when editing: "Ändringen gäller kommande utgifter." (AC-9.4). Online only (offline state as S-6).
-
-## S-20 Devices (Enheter)
-
-Rows per AC-3.1: device name, owner avatar and name, "Parad 3 okt.", "Senast sedd nyss", "Kvitt 1.1", "Den här enheten" tag; trailing "Ta bort" (not on the current device). Confirmation dialog: "Ta bort Galaxy S26+? Telefonen loggas ut och dess Kvitt-data raderas nästa gång den ansluter." → "Ta bort" / "Avbryt". States: loading (online fetch: progress), offline ("Kräver anslutning" + nothing listed; this list is not stored locally), error `cannot_revoke_current_device` cannot be triggered from the UI.
-
-## S-21 Invites (Bjud in partner / Para ihop en enhet)
-
-Admin only. Content: actions "Bjud in partner" (only while one user exists, AC-2.1) and "Para ihop en enhet" with a choice "För mig" / "För Anna" (AC-2.2). **Created invite:** the code very large in groups of 4 (`7K3M-QX9D-2HPA`, mono/tabular, selectable), the link, "Gäller till tis 20 okt. 14:02", "Dela" (Android share sheet with a Swedish message: "Gå med i vårt hushåll i Kvitt:  Koden är 7K3M-QX9D-2HPA."), "Kopiera kod". **Pending invites** list: kind ("Partner", "Enhet för Anna"), created, expiry, "Avbryt" (confirm). The code is only visible right after creation (the server stores hashes); later rows say "Koden visas bara när den skapas". States: offline ("Kräver anslutning"), errors `household_full`, network.
-
-## S-22 About (Om Kvitt)
-
-Rows: "Version 1.1 (11)", "Server: https://icehack.asuscomm.com:8443", "Serverversion 1.1.0" (or "Okänd" when `/health` failed), "Öppen källkod-licenser" (→ the standard licences screen), button "Sök efter uppdatering" (→ check; result as S-17 banner or a snackbar "Du har den senaste versionen"; debug builds: hidden, AC-16.5).
-
----
-
-## Notifications (not screens)
-
-Built locally per AC-13.5 and §13.3 (copy in `strings.csv`, prefix `notif_`). Channel "Aktivitet". Small icon: the app's monochrome icon. Large icon: actor avatar if cached. Tap targets per AC-13.6.
diff --git a/docs/ux/screens/index.html b/docs/ux/screens/index.html
new file mode 100644
index 0000000..a07440d
--- /dev/null
+++ b/docs/ux/screens/index.html
@@ -0,0 +1,1396 @@
+
+
+
+
+
+Kvitt — skärmar (U2)
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/ux/strings.csv b/docs/ux/strings.csv
index 64d757b..f521f19 100644
--- a/docs/ux/strings.csv
+++ b/docs/ux/strings.csv
@@ -1,465 +1,558 @@
 key,en,sv,notes
-common_save,Save,Spara,
-common_cancel,Cancel,Avbryt,
-common_delete,Delete,Ta bort,
-common_edit,Edit,Ändra,
-common_close,Close,Stäng,TalkBack label of the close icon
-common_back,Back,Tillbaka,TalkBack label of the back icon
-common_continue,Continue,Fortsätt,
-common_retry,Try again,Försök igen,
-common_more_options,More options,Fler alternativ,TalkBack label of the overflow icon
-common_you,You,Du,"the viewer as a name, sentence start"
-common_you_lower,you,du,the viewer inside a sentence
-common_today,Today,Idag,
-common_yesterday,Yesterday,Igår,
-common_needs_connection,Needs connection,Kräver anslutning,§9.9
-common_could_not_reach_server,Could not reach the server,Kunde inte nå servern,§9.9
-common_offline,Offline,Offline,
-common_pending_sync,Waiting to sync,Väntar på synk,TalkBack label of the pending marker
-common_archived,Archived,Arkiverat,badge on an archived account
-common_archived_category,archived,arkiverad,suffix on an archived category chip
-common_discard_title,Discard changes?,Slänga ändringarna?,
-common_discard,Discard,Släng,
-common_keep_editing,Keep editing,Fortsätt redigera,
-common_undo,Undo,Ångra,
-common_amount_spoken,%1$s Swedish kronor,%1$s kronor,TalkBack form of an amount; %1$s = number without currency
-common_kr_suffix,SEK,kr,suffix/prefix shown in the amount field
-common_ratio,%1$s/%2$s,%1$s/%2$s,"ratio label, percents without % sign, e.g. 60/40"
-common_percent,%1$s %%,%1$s %%,"percent with one decimal, e.g. 23,4 %"
-common_user_split,%1$s %2$s,%1$s %2$s,"name + amount in a split preview: ""Du 206,00 kr"""
-common_separator_dot, · , · ,list separator
-common_and,%1$s and %2$s,%1$s och %2$s,
-nav_home,Home,Översikt,
-nav_analytics,Analytics,Statistik,
-nav_settings,Settings,Inställningar,
-fab_add_expense,Add expense,Lägg till utgift,FAB on S-4 and S-5
-balance_partner_owes_you,%1$s owes you,%1$s är skyldig dig,%1$s partner name; amount shown separately
-balance_you_owe_partner,You owe %1$s,Du är skyldig %1$s,
-balance_partner_owes_you_amount,%1$s owes you %2$s,%1$s är skyldig dig %2$s,one-line form and TalkBack sentence
-balance_you_owe_partner_amount,You owe %1$s %2$s,Du är skyldig %1$s %2$s,
-balance_all_settled,All settled up,Ni är kvitt,"global, §9.3"
-balance_account_settled,Settled up,Kvitt,"account screens and rows, §9.3"
-effect_lent,You lent %1$s,Du lånade ut %1$s,
-effect_borrowed,You borrowed %1$s,Du lånade %1$s,
-effect_not_involved,Not involved,Inte inblandad,
-effect_paid_for_yourself,Paid for yourself,Betalade för dig själv,
-settlement_row_partner_paid_you,%1$s paid you %2$s,%1$s betalade dig %2$s,
-settlement_row_you_paid_partner,You paid %1$s %2$s,Du betalade %1$s %2$s,
-settlement_row_accounts:one,%d account,%d konto,"plural one; shown as ""· 2 konton"" only when > 1"
-settlement_row_accounts:other,%d accounts,%d konton,plural other
-sync_synced_relative,Synced %1$s,Synkad %1$s,%1$s = relative time below
-time_just_now,just now,nyss,
-time_minutes_ago,%d min ago,för %d min sedan,
-time_hours_ago,%d h ago,för %d h sedan,
-sync_syncing,Syncing…,Synkar…,
-sync_pending:one,%d change waiting to sync,%d ändring väntar på synk,plural one
-sync_pending:other,%d changes waiting to sync,%d ändringar väntar på synk,plural other
-sync_rejected:one,%d change was not synced,%d ändring synkades inte,plural one
-sync_rejected:other,%d changes were not synced,%d ändringar synkades inte,plural other
-sync_failed_line,Sync failed · trying again soon,Synk misslyckades · försöker igen snart,
-sync_clock_wrong_short,Your phone's clock is wrong,Telefonens klocka går fel,
-pairing_app_name,Kvitt,Kvitt,not translated
-pairing_tagline,Shared expenses for two,Delade utgifter för två,
-pairing_code_label,Invite code,Inbjudningskod,
-pairing_code_paste,Paste,Klistra in,
-pairing_name_label,Your name,Ditt namn,
-pairing_code_help,You get the code from %1$s: from a link or from Settings › Invite partner.,Koden får du av %1$s: från en länk eller från Inställningar › Bjud in partner.,%1$s admin name when known from the link; else use pairing_code_help_generic
-pairing_code_help_generic,"You get the code from a link, or it is printed when the server is set up.","Koden får du från en länk, eller så skrivs den ut när servern sätts upp.",
-pairing_server_label,Server address,Serveradress,
-pairing_pair,Pair,Para ihop,
-pairing_pairing,Pairing…,Parar ihop…,
-pairing_again_title,Pair this phone again,Para ihop telefonen igen,§14.4.8
-pairing_again_body,Your expenses are still on this phone.,Dina utgifter finns kvar på telefonen.,
-pairing_error_invite_invalid,This code does not work. It may have expired or already been used. Ask for a new one.,Koden fungerar inte. Den kan ha gått ut eller redan använts. Be om en ny.,
-pairing_error_network,Could not reach the server. Check the address and your connection.,Kunde inte nå servern. Kontrollera adressen och anslutningen.,
-pairing_error_rate_limited,Too many attempts. Wait a while and try again.,För många försök. Vänta en stund och försök igen.,
-pairing_error_https,The address must start with https://,Adressen måste börja med https://,AC-2.8
-pairing_error_code_length,The code has 12 characters,Koden har 12 tecken,
-pairing_error_name_required,Enter your name,Skriv ditt namn,
-pairing_notification_rationale,Kvitt tells you when %1$s adds or changes something.,Kvitt säger till när %1$s lägger till eller ändrar något.,shown before the system permission prompt (AC-2.9)
-profile_step_title,Your profile,Din profil,
-profile_step_joining,You are joining %1$s's household,Du går med i %1$ss hushåll,sv genitive: name + s (no apostrophe); names ending in s get no extra s (handled in code: Jonas hushåll)
-profile_add_picture,Add picture,Lägg till bild,
-profile_change_picture,Change picture,Byt bild,
-profile_remove_picture,Remove picture,Ta bort bild,
-profile_name_label,Display name,Visningsnamn,
-profile_name_required,Enter a name,Skriv ett namn,
-profile_picture_failed,The picture could not be used,Bilden kunde inte användas,
-profile_picture_too_large,The picture is too large (max 5 MB),Bilden är för stor (högst 5 MB),payload_too_large
-profile_picture_unsupported,"Use a JPEG, PNG or WebP picture","Använd en bild i JPEG, PNG eller WebP",unsupported_media_type
-profile_picture_invalid,The picture could not be read,Bilden kunde inte läsas,image_invalid
-profile_offline_banner,Needs connection. Your profile can only be changed online.,Kräver anslutning. Profilen kan bara ändras när telefonen är online.,
-waiting_title,Waiting for your partner,Väntar på din partner,
-waiting_body,Kvitt needs both of you before you can start. Invite your partner with a link.,Kvitt behöver er båda innan ni kan börja. Bjud in din partner med en länk.,
-waiting_invite_expires,Invite valid until %1$s,Inbjudan gäller till %1$s,"%1$s weekday + date + time, e.g. mån 19 okt. 21:40"
-waiting_invite_partner,Invite partner,Bjud in partner,
-home_accounts,Accounts,Konton,
-home_new_account,New account,Nytt konto,
-home_archived_section,Archived (%d),Arkiverade (%d),
-home_settle_up,Settle up,Gör upp,
-home_account_expenses_month:one,%1$d expense in %2$s,%1$d utgift i %2$s,plural one; %2$s month name (lowercase in sv)
-home_account_expenses_month:other,%1$d expenses in %2$s,%1$d utgifter i %2$s,plural other
-home_account_no_expenses_month,No expenses in %1$s,Inga utgifter i %1$s,
-home_loading,Getting your expenses…,Hämtar era utgifter…,first sync only
-home_empty_welcome,"Welcome, %1$s and %2$s","Välkommen, %1$s och %2$s",
-home_empty_title,Create your first account,Skapa ert första konto,
-home_empty_body,"An account collects expenses that belong together, for example Everyday or Japan 2027.","Ett konto samlar utgifter som hör ihop, till exempel Vardag eller Japan 2027.",
-home_expense_saved,Expense saved,Utgiften sparades,
-home_expense_saved_offline,Saved. Syncs when you're online.,Sparad. Synkas när du är online.,
-home_settlement_saved,Payment saved,Betalningen sparades,
-account_default_split,%1$s · %2$s,%1$s · %2$s,"""50 / 50 · Jacob och Anna""; %1$s ratio, %2$s names with common_and"
-account_settle_this,Settle this account,Gör upp det här kontot,
-account_month_total,%1$s · %2$s,%1$s · %2$s,"month header: ""Oktober · 7 457,90 kr"""
-account_edit,Edit account,Ändra konto,
-account_archive,Archive,Arkivera,
-account_unarchive,Restore from archive,Återställ från arkiv,
-account_delete,Delete account,Ta bort konto,
-account_delete_blocked,"The account cannot be deleted while it has expenses, payments or recurring expenses","Kontot kan inte tas bort så länge det har utgifter, betalningar eller återkommande utgifter",AC-6.5; also error account_not_empty
-account_delete_confirm_title,Delete the account %1$s?,Ta bort kontot %1$s?,
-account_delete_confirm_body,This cannot be undone.,Det går inte att ångra.,
-account_archived_snackbar,%1$s was archived,%1$s arkiverades,
-account_unarchived_snackbar,%1$s was restored,%1$s återställdes,
-account_empty_title,No expenses yet,Inga utgifter än,
-account_empty_body,Add the first expense in %1$s.,Lägg till den första utgiften i %1$s.,
-account_editor_new,New account,Nytt konto,
-account_editor_edit,Edit account,Ändra konto,
-account_editor_name,Name,Namn,
-account_editor_default_split,Default split,Standarddelning,
-account_editor_percent_of,%1$s %%,%1$s %%,"label of a percent field: ""Jacob %"""
-account_editor_preview,Of %1$s: %2$s,Av %1$s: %2$s,"""Av 100,00 kr: Jacob 60,00 kr, Anna 40,00 kr"""
-account_editor_ratio_change_hint,The change applies to new expenses. Existing expenses keep their split.,Ändringen gäller nya utgifter. Befintliga utgifter behåller sin delning.,AC-6.3
-account_editor_offline_banner,Needs connection. Accounts can only be changed online.,Kräver anslutning. Konton kan bara ändras när telefonen är online.,
-account_editor_name_required,Enter a name,Skriv ett namn,
-account_editor_name_taken,There is already an account called %1$s,Det finns redan ett konto som heter %1$s,name_taken
-account_editor_ratio_sum,The two parts must add up to 100 %,De två delarna måste bli 100 %,bp_sum_not_10000
-expense_editor_new,New expense,Ny utgift,
-expense_editor_edit,Edit expense,Ändra utgift,
-expense_editor_amount,Amount,Belopp,
-expense_editor_amount_hint,"0,00","0,00","hint; en shows 0.00 (built from the formatter, not this string)"
-expense_editor_description,Description,Beskrivning,
-expense_editor_account,Account,Konto,
-expense_editor_category,Category,Kategori,
-expense_editor_date,Date,Datum,
-expense_editor_paid_by,Paid by,Betalt av,
-expense_editor_paid_by_toggle,Paid by %1$s. Double-tap to switch to %2$s.,Betalt av %1$s. Dubbeltryck för att byta till %2$s.,TalkBack
-expense_editor_split,Split,Delning,
-split_mode_default,Account default %1$s,Kontots standard %1$s,"%1$s ratio, e.g. 60/40"
-split_mode_ratio,Custom ratio,Egen fördelning,
-split_mode_exact,Exact amounts,Exakta belopp,
-split_mode_full,All on one person,Allt på en person,
-split_full_on,All on %1$s,Allt på %1$s,
-split_exact_rest,%1$s gets the rest,%1$s får resten,read-only field of the other user
-split_preview_effect,This expense: %1$s,Den här utgiften: %1$s,%1$s an effect line
-expense_editor_notes,Notes,Anteckningar,
-expense_editor_add_note,Add note,Lägg till anteckning,
-expense_editor_delete,Delete expense,Ta bort utgift,
-expense_editor_discard_title,Discard the expense?,Slänga utgiften?,
-expense_editor_keypad_delete,Delete,Radera,TalkBack of the keypad backspace
-expense_editor_keypad_decimal,Decimal separator,Decimaltecken,TalkBack
-expense_editor_next,Next,Nästa,IME action on the amount
-expense_editor_pick_account,Choose account,Välj konto,
-expense_editor_pick_category,Choose category,Välj kategori,
-error_amount_required,Enter an amount,Skriv ett belopp,required on amount
-error_amount_out_of_range,The amount must be between %1$s and %2$s,Beloppet måste vara mellan %1$s och %2$s,amount_out_of_range
-error_description_required,Enter a description,Skriv en beskrivning,
-error_description_too_long,At most %d characters,Högst %d tecken,
-error_date_out_of_range,The date is too far ahead or back,Datumet ligger för långt fram eller bak,§9.6
-error_exact_share_too_large,The share is larger than the amount,Andelen är större än beloppet,"AC-8.7, exact_sum_mismatch"
-error_ratio_invalid,The parts must add up to 100 %,Delarna måste bli 100 %,"bp_out_of_range, bp_sum_not_10000"
-error_generic_invalid,This could not be saved. Check the fields.,Det gick inte att spara. Kontrollera fälten.,fallback for other domain tokens
-expense_detail_title,Expense,Utgift,
-expense_detail_account,Account,Konto,
-expense_detail_date,Date,Datum,
-expense_detail_paid_by,Paid by,Betalt av,
-expense_detail_split,Split,Delning,
-expense_detail_notes,Notes,Anteckning,
-expense_detail_recurring,Recurring: %1$s,Återkommande: %1$s,AC-8.11
-expense_detail_delete_title,Delete the expense?,Ta bort utgiften?,
-expense_detail_delete_body,It disappears for both of you.,Den försvinner för er båda.,
-expense_detail_deleted_snackbar,Expense deleted,Utgiften togs bort,
-expense_detail_deleted_banner,%1$s deleted this expense · %2$s,%1$s tog bort den här utgiften · %2$s,"%1$s actor (Du/Anna), %2$s date+time"
-expense_detail_missing,This expense is not on the phone yet,Utgiften finns inte på telefonen än,
-sync_now,Sync now,Synka nu,
-timeline_title,History,Historik,
-timeline_created,%1$s created this,%1$s skapade den här,"%1$s actor: ""Anna"" or common_you"
-timeline_deleted,%1$s deleted this,%1$s tog bort den här,
-timeline_changed,%1$s changed %2$s from %3$s to %4$s,%1$s ändrade %2$s från %3$s till %4$s,%2$s a timeline_field_* (definite form)
-timeline_changed_added,%1$s added %2$s: %3$s,%1$s lade till %2$s: %3$s,old value empty (e.g. notes)
-timeline_changed_removed,%1$s removed %2$s,%1$s tog bort %2$s,new value empty
-timeline_recurring,Created automatically (recurring),Skapad automatiskt (återkommande),source recurring
-timeline_import,Imported from Splitwise,Importerad från Splitwise,source import
-timeline_pending,Your change is waiting to sync,Din ändring väntar på synk,AC-12.5
-timeline_time,· %1$s,· %1$s,"""· 16 okt. 19:40"""
-timeline_field_account,the account,kontot,
-timeline_field_category,the category,kategorin,
-timeline_field_payer,the payer,betalaren,
-timeline_field_amount,the amount,beloppet,
-timeline_field_description,the description,beskrivningen,
-timeline_field_notes,the notes,anteckningen,
-timeline_field_date,the date,datumet,
-timeline_field_split_mode,the split,delningen,split_mode and splits are shown as one change when both differ
-timeline_field_splits,the shares,andelarna,
-timeline_field_from,who paid,vem som betalade,settlement from_user_id
-timeline_field_to,who received,vem som fick,settlement to_user_id
-timeline_field_note,the note,anteckningen,settlement note
-timeline_field_allocations,the allocation,fördelningen,
-settle_title,Settle up,Gör upp,
-settle_title_edit,Edit payment,Ändra betalning,
-settle_you_pay,You pay %1$s,Du betalar %1$s,
-settle_partner_pays,%1$s pays you,%1$s betalar dig,
-settle_swap,Swap direction,Byt riktning,TalkBack and tooltip
-settle_amount,Amount,Belopp,
-settle_allocation,Allocation,Fördelning,
-settle_row_debt_you_owe,You owe %1$s,Du är skyldig %1$s,payer is the viewer
-settle_row_debt_partner_owes,%1$s owes you %2$s,%1$s är skyldig dig %2$s,
-settle_row_debt_payer_owes,%1$s owes %2$s,%1$s är skyldig %2$s,payer is the partner
-settle_row_settled,Settled up,Kvitt,
-settle_row_remove,Remove %1$s from the allocation,Ta bort %1$s från fördelningen,TalkBack
-settle_over_settle_warning,This settles more than is owed in %1$s,Det här reglerar mer än som är skyldigt i %1$s,AC-11.5
-settle_remaining,Left to allocate %1$s,Kvar att fördela %1$s,
-settle_over_allocated,%1$s too much,%1$s för mycket,
-settle_add_account,Add account,Lägg till konto,
-settle_propose_again,Suggest again,Föreslå igen,
-settle_untouched_info,%1$s is not affected: there %2$s.,%1$s påverkas inte: där %2$s.,"%2$s e.g. ""är Anna skyldig dig 1 184,50 kr"" — build with settle_untouched_owes_you"
-settle_untouched_owes_you,%1$s owes you %2$s,är %1$s skyldig dig %2$s,"fragment for settle_untouched_info (sv word order after ""där"")"
-settle_date,Date,Datum,
-settle_note,Note,Anteckning,
-settle_confirm,Confirm,Bekräfta,
-settle_confirm_dialog_title,Confirm the payment?,Bekräfta betalningen?,
-settle_confirm_anyway,Confirm anyway,Bekräfta ändå,
-settle_change,Change,Ändra,
-error_allocation_sum,The accounts must add up to the amount,Kontona måste bli lika med beloppet,allocation_sum_mismatch
-error_allocation_positive,"Each account needs at least 0,01","Varje konto behöver minst 0,01 kr",allocation_not_positive
-error_no_allocations,Add at least one account,Lägg till minst ett konto,no_allocations
-error_too_many_allocations,At most 50 accounts,Högst 50 konton,too_many_allocations
-error_note_too_long,At most %d characters,Högst %d tecken,
-settlement_detail_title,Payment,Betalning,
-settlement_detail_delete_title,Delete the payment?,Ta bort betalningen?,
-settlement_detail_delete_body,Balances are recalculated for both of you.,Saldona räknas om för er båda.,
-settlement_detail_deleted_banner,%1$s deleted this payment · %2$s,%1$s tog bort den här betalningen · %2$s,
-settlement_detail_deleted_snackbar,Payment deleted,Betalningen togs bort,
-analytics_title,Analytics,Statistik,
-analytics_period,Period,Period,
-range_this_month,This month,Denna månad,
-range_last_3_months,Last 3 months,Senaste 3 mån,
-range_last_6_months,Last 6 months,Senaste 6 mån,
-range_last_12_months,Last 12 months,Senaste 12 mån,
-range_this_year,This year,I år,
-range_all_time,All time,All tid,
-range_custom,Custom…,Anpassad…,
-analytics_range_dates,%1$s – %2$s,%1$s – %2$s,
-granularity_month,Month,Månad,
-granularity_week,Week,Vecka,
-measure_household,Household,Hushållet,
-measure_my_share,My share,Min del,
-group_by_category,Category,Kategori,
-group_by_account,Account,Konto,
-analytics_total_household,Household spending,Hushållets utgifter,
-analytics_total_my_share,Your share,Din del,
-analytics_over_time,Spending over time,Utgifter över tid,
-analytics_by_category,By category,Per kategori,
-analytics_by_account,By account,Per konto,
-analytics_week_label,W%1$d,v. %1$d,x-axis week label
-analytics_axis_thousands,%1$sk,%1$s tkr,y-axis compact
-analytics_empty,No expenses in this period,Inga utgifter i den här perioden,
-analytics_change_period,Change period,Byt period,
-analytics_summary_over_time,"Spending over time, %1$s: highest in %2$s with %3$s, lowest in %4$s with %5$s.","Utgifter över tid, %1$s: högst i %2$s med %3$s, lägst i %4$s med %5$s.",TalkBack summary of chart 1
-analytics_summary_breakdown,%1$s: largest is %2$s with %3$s (%4$s).,%1$s: störst är %2$s med %3$s (%4$s).,TalkBack summary of charts 2 and 3
-analytics_bar_spoken,%1$s: %2$s,%1$s: %2$s,TalkBack per bar
-drilldown_empty,No expenses any more,Inga utgifter längre,
-drilldown_title_category_period,"%1$s, %2$s","%1$s, %2$s","""Mat & hushåll, oktober 2026"""
-settings_title,Settings,Inställningar,
-settings_role_admin,You · Admin,Du · Administratör,
-settings_role_member,You,Du,
-settings_partner,%1$s · Partner,%1$s · Partner,
-settings_profile,Profile,Profil,
-settings_categories,Categories,Kategorier,
-settings_recurring,Recurring expenses,Återkommande utgifter,
-settings_devices,Devices,Enheter,
-settings_invite_partner,Invite partner,Bjud in partner,
-settings_pair_device,Pair a device,Para ihop en enhet,
-settings_sync_status,Sync status,Synkstatus,
-settings_about,About Kvitt,Om Kvitt,
-settings_pending_badge,%d waiting,%d väntar,
-settings_update_badge,Update available,Uppdatering finns,
-categories_title,Categories,Kategorier,
-categories_new,New category,Ny kategori,
-categories_archived,Archived (%d),Arkiverade (%d),
-categories_reorder_offline,Needs connection to change,Kräver anslutning för att ändra,
-categories_move_up,Move up,Flytta upp,TalkBack custom action
-categories_move_down,Move down,Flytta ned,TalkBack custom action
-category_editor_new,New category,Ny kategori,
-category_editor_edit,Edit category,Ändra kategori,
-category_editor_name,Name,Namn,
-category_editor_reset_name,Reset name,Återställ namn,AC-7.4
-category_editor_icon,Icon,Ikon,
-category_editor_color,Colour,Färg,
-category_editor_archive,Archive,Arkivera,
-category_editor_unarchive,Restore from archive,Återställ från arkiv,
-category_editor_delete,Delete category,Ta bort kategori,
-category_delete_blocked_default,"Default categories cannot be deleted, only archived","Standardkategorier kan inte tas bort, bara arkiveras",category_is_default
-category_delete_blocked_in_use,The category is used by expenses or recurring expenses. Archive it instead.,Kategorin används av utgifter eller återkommande utgifter. Arkivera den i stället.,category_in_use
-category_name_required,Enter a name,Skriv ett namn,
-category_default_groceries,Groceries,Mat & hushåll,AC-7.2
-category_default_restaurants,Restaurants,Restaurang & café,
-category_default_home,Home,Hem,
-category_default_utilities,Utilities,Räkningar,
-category_default_transport,Transport,Transport,
-category_default_travel,Travel,Resor,
-category_default_entertainment,Entertainment,Nöje,
-category_default_health,Health,Hälsa,
-category_default_shopping,Shopping,Shopping,
-category_default_other,Other,Övrigt,
-color_green,Green,Grön,"TalkBack of colour swatches, §9.7 tokens"
-color_orange,Orange,Orange,
-color_brown,Brown,Brun,
-color_yellow,Yellow,Gul,
-color_blue,Blue,Blå,
-color_teal,Teal,Blågrön,
-color_purple,Purple,Lila,
-color_red,Red,Röd,
-color_pink,Pink,Rosa,
-color_grey,Grey,Grå,
-color_indigo,Indigo,Indigo,
-color_lime,Lime,Lime,
-icon_shopping_cart,Shopping cart,Kundvagn,"TalkBack of icon picker, §9.7 tokens"
-icon_restaurant,Restaurant,Restaurang,
-icon_local_cafe,Café,Kafé,
-icon_home,Home,Hem,
-icon_bolt,Electricity,El,
-icon_water_drop,Water,Vatten,
-icon_wifi,Internet,Internet,
-icon_directions_car,Car,Bil,
-icon_train,Train,Tåg,
-icon_local_gas_station,Fuel,Bränsle,
-icon_flight,Flight,Flyg,
-icon_hotel,Hotel,Hotell,
-icon_luggage,Luggage,Bagage,
-icon_movie,Film,Film,
-icon_music_note,Music,Musik,
-icon_sports_esports,Games,Spel,
-icon_fitness_center,Fitness,Träning,
-icon_medical_services,Health care,Vård,
-icon_medication,Medicine,Medicin,
-icon_shopping_bag,Shopping bag,Shoppingkasse,
-icon_checkroom,Clothes,Kläder,
-icon_pets,Pets,Husdjur,
-icon_child_care,Children,Barn,
-icon_school,School,Skola,
-icon_card_giftcard,Gift,Present,
-icon_celebration,Party,Fest,
-icon_build,Repairs,Reparationer,
-icon_chair,Furniture,Möbler,
-icon_subscriptions,Subscriptions,Prenumerationer,
-icon_category,Other,Övrigt,
-sync_status_title,Sync status,Synkstatus,
-sync_status_all_synced,Everything is synced,Allt är synkat,
-sync_status_pending_title,Pending changes (%d),Väntande ändringar (%d),
-sync_status_tag_new,new,ny,
-sync_status_tag_changed,changed,ändrad,
-sync_status_tag_deleted,deleted,borttagen,
-sync_status_rejected_title,Changes that were not synced,Ändringar som inte synkades,
-sync_status_dismiss,Remove from list,Ta bort från listan,
-sync_reject_stale,Your change was replaced by a newer change from %1$s,Din ändring ersattes av en nyare ändring från %1$s,§14.4.6
-sync_reject_deleted,This was deleted before your change arrived,Den här togs bort innan din ändring kom fram,
-sync_reject_validation,This change was not valid and was not saved,Ändringen var inte giltig och sparades inte,
-sync_reject_reference,Its account or category no longer exists,Dess konto eller kategori finns inte längre,
-sync_reject_clock_skew,Your phone's clock is wrong. Fix it and sync again,Telefonens klocka går fel. Rätta den och synka igen,
-sync_status_clock_warning,Your phone's clock is wrong. Fix it and sync again.,Telefonens klocka går fel. Rätta den och synka igen.,§9.10
-update_available,Version %1$s available,Version %1$s finns,
-update_install,Update,Uppdatera,
-update_later,Later,Senare,
-update_downloading,Downloading… %1$d %%,Laddar ner… %1$d %%,
-update_failed,"Download failed, try again",Nedladdningen misslyckades. Försök igen.,AC-16.4
-update_permission_title,Allow installation,Tillåt installation,
-update_permission_body,Android asks once whether Kvitt may install apps. Choose Allow from this source and go back.,Android frågar en gång om Kvitt får installera appar. Välj Tillåt från den här källan och gå tillbaka.,AC-16.3; Android setting name differs per locale
-update_up_to_date,You have the latest version,Du har den senaste versionen,
-update_check,Check for update,Sök efter uppdatering,
-removed_title,This phone has been removed,Den här telefonen har tagits bort,
-removed_body_member,Access was removed and all Kvitt data on the phone has been deleted. Ask %1$s for a new code to pair it again.,Åtkomsten togs bort och allt Kvitt-data på telefonen har raderats. Be %1$s om en ny kod för att para ihop den igen.,
-removed_body_admin,Access was removed and all Kvitt data on the phone has been deleted. Create a new code on the server to pair it again.,Åtkomsten togs bort och allt Kvitt-data på telefonen har raderats. Skapa en ny kod på servern för att para ihop den igen.,
-removed_pair_again,Pair again,Para ihop igen,
-recurring_title,Recurring expenses,Återkommande utgifter,
-recurring_new,New,Ny,
-recurring_every_month_on,Every month on day %1$d,Varje månad den %1$d:e,"sv ordinal: 1:a, 2:a, otherwise :e — use recurring_every_month_on_1_2 for 1 and 2"
-recurring_every_month_on_1_2,Every month on day %1$d,Varje månad den %1$d:a,"for days 1, 2, 21, 22, 31 (sv ordinal -a)"
-recurring_next,Next: %1$s,Nästa: %1$s,
-recurring_paused,Paused,Pausad,
-recurring_empty_title,No recurring expenses,Inga återkommande utgifter,
-recurring_empty_body,"Rent, electricity and streaming can be added automatically every month.","Hyra, el och streaming kan läggas in automatiskt varje månad.",
-recurring_empty_action,New recurring expense,Ny återkommande utgift,
-recurring_editor_new,New recurring expense,Ny återkommande utgift,
-recurring_editor_edit,Edit recurring expense,Ändra återkommande utgift,
-recurring_day_of_month,Day of month,Dag i månaden,
-recurring_day_clamp_hint,In shorter months the last day is used,I kortare månader används sista dagen,days 29–31
-recurring_first_month,First month,Första månad,
-recurring_last_month,Last month (optional),Sista månad (valfritt),
-recurring_paused_switch,Paused,Pausad,
-recurring_resume_hint,Months that passed while paused are not created afterwards.,Månader som passerat under pausen skapas inte i efterhand.,§9.5
-recurring_edit_hint,The change applies to upcoming expenses.,Ändringen gäller kommande utgifter.,AC-9.4
-recurring_delete_title,Delete %1$s?,Ta bort %1$s?,
-recurring_delete_body,Expenses already created stay.,Utgifter som redan skapats finns kvar.,
-recurring_offline_banner,Needs connection. Recurring expenses can only be changed online.,Kräver anslutning. Återkommande utgifter kan bara ändras när telefonen är online.,
-error_end_before_start,The last month cannot be before the first,Sista månaden kan inte vara före den första,
-error_start_too_early,At most 12 months back,Högst 12 månader bakåt,
-devices_title,Devices,Enheter,
-devices_paired,Paired %1$s,Parad %1$s,
-devices_last_seen,Last seen %1$s,Senast sedd %1$s,
-devices_app_version,Kvitt %1$s,Kvitt %1$s,
-devices_this_device,This device,Den här enheten,AC-3.1
-devices_revoke,Remove,Ta bort,
-devices_revoke_title,Remove %1$s?,Ta bort %1$s?,
-devices_revoke_body,The phone is signed out and its Kvitt data is deleted the next time it connects.,Telefonen loggas ut och dess Kvitt-data raderas nästa gång den ansluter.,
-devices_offline,Needs connection to show devices,Kräver anslutning för att visa enheter,
-invites_title_partner,Invite partner,Bjud in partner,
-invites_title_device,Pair a device,Para ihop en enhet,
-invites_for_me,For me,För mig,
-invites_for_partner,For %1$s,För %1$s,
-invites_valid_until,Valid until %1$s,Gäller till %1$s,
-invites_share,Share,Dela,
-invites_copy_code,Copy code,Kopiera kod,
-invites_code_copied,Code copied,Koden kopierades,
-invites_share_message,Join our household in Kvitt: %1$s The code is %2$s.,Gå med i vårt hushåll i Kvitt: %1$s Koden är %2$s.,
-invites_pending,Pending invites,Väntande inbjudningar,
-invites_kind_partner,Partner,Partner,
-invites_kind_device,Device for %1$s,Enhet för %1$s,
-invites_code_hidden,The code is only shown when it is created,Koden visas bara när den skapas,
-invites_cancel,Cancel invite,Avbryt inbjudan,
-invites_cancel_title,Cancel the invite?,Avbryta inbjudan?,
-error_household_full,Your partner has already joined,Din partner har redan gått med,household_full
-about_title,About Kvitt,Om Kvitt,
-about_version,Version %1$s (%2$d),Version %1$s (%2$d),
-about_server,Server,Server,
-about_server_version,Server version %1$s,Serverversion %1$s,
-about_server_version_unknown,Unknown,Okänd,
-about_licences,Open-source licences,Öppen källkod-licenser,
-error_forbidden,You are not allowed to do this,Du får inte göra det här,forbidden
-error_not_found,This no longer exists,Det här finns inte längre,not_found
-error_household_incomplete,Your partner needs to join first,Din partner behöver gå med först,household_incomplete
-error_account_not_empty,"The account still has expenses, payments or recurring expenses","Kontot har fortfarande utgifter, betalningar eller återkommande utgifter",account_not_empty
-error_rate_limited,Too many attempts. Wait a while and try again.,För många försök. Vänta en stund och försök igen.,rate_limited
-error_server,Something went wrong on the server. Try again later.,Något gick fel på servern. Försök igen senare.,"internal, unavailable, not_implemented"
-error_app_too_old,Update Kvitt to continue,Uppdatera Kvitt för att fortsätta,protocol_unsupported
-error_validation,The server did not accept the values,Servern godtog inte värdena,validation_failed without a mapped field
-notif_channel_activity,Activity,Aktivitet,§14.6
-notif_expense_created_title,%1$s added an expense,%1$s lade till en utgift,
-notif_expense_created_body,%1$s · %2$s · %3$s,%1$s · %2$s · %3$s,description · amount · account
-notif_expense_updated_title,%1$s changed an expense,%1$s ändrade en utgift,
-notif_expense_updated_body,%1$s · %2$s,%1$s · %2$s,description · amount
-notif_expense_deleted_title,%1$s deleted an expense,%1$s tog bort en utgift,
-notif_expense_deleted_body,%1$s · %2$s,%1$s · %2$s,description · amount
-notif_settlement_created_title,%1$s recorded a payment,%1$s registrerade en betalning,
-notif_settlement_body,%1$s paid %2$s %3$s,%1$s betalade %2$s %3$s,"payer, recipient (dig/you when the viewer), amount"
-notif_settlement_body_to_you,%1$s paid you %2$s,%1$s betalade dig %2$s,recipient is the viewer
-notif_settlement_body_from_you,You paid %1$s %2$s,Du betalade %1$s %2$s,payer is the viewer
-notif_settlement_updated_title,%1$s changed a payment,%1$s ändrade en betalning,
-notif_settlement_deleted_title,%1$s deleted a payment,%1$s tog bort en betalning,
-notif_settlement_deleted_body,%1$s,%1$s,amount
-notif_recurring_created_title,Recurring expense added,Återkommande utgift tillagd,
-notif_recurring_created_body,%1$s · %2$s · %3$s,%1$s · %2$s · %3$s,description · amount · account
-notif_sync_batch_title:one,%1$s made %2$d change,%1$s gjorde %2$d ändring,plural one
-notif_sync_batch_title:other,%1$s made %2$d changes,%1$s gjorde %2$d ändringar,plural other
-notif_sync_batch_body,Open Kvitt to see them,Öppna Kvitt för att se dem,
-notif_generic_title_actor,%1$s changed something in Kvitt,%1$s ändrade något i Kvitt,sync failed
-notif_generic_title,Kvitt,Kvitt,"sync failed, actor unknown"
-notif_generic_body,Open Kvitt to see it,Öppna Kvitt för att se det,
+app_name,Kvitt,Kvitt,module=app; translatable=false; the only :app string (D-017)
+common_save,Save,Spara,module=core:design
+common_cancel,Cancel,Avbryt,module=core:design
+common_delete,Delete,Ta bort,module=core:design
+common_edit,Edit,Ändra,module=core:design
+common_close,Close,Stäng,module=core:design; TalkBack label of the close icon
+common_back,Back,Tillbaka,module=core:design; TalkBack label of the back icon
+common_continue,Continue,Fortsätt,module=core:design
+common_name,Name,Namn,"module=core:design; name field label in S-6, S-14, S-15"
+common_name_required,Enter a name,Skriv ett namn,module=core:design; required on a name field
+common_retry,Try again,Försök igen,module=core:design
+common_more_options,More options,Fler alternativ,module=core:design; TalkBack label of the overflow icon
+common_you,You,Du,"module=core:design; the viewer as a name, sentence start"
+common_you_lower,you,du,module=core:design; the viewer inside a sentence
+common_today,Today,Idag,module=core:design
+common_yesterday,Yesterday,Igår,module=core:design
+common_needs_connection,Needs connection,Kräver anslutning,module=core:design; §9.9
+common_could_not_reach_server,Could not reach the server,Kunde inte nå servern,module=core:design; §9.9
+common_offline,Offline,Offline,module=core:design
+common_pending_sync,Waiting to sync,Väntar på synk,module=core:design; TalkBack label of the pending marker
+common_archived,Archived,Arkiverat,module=core:design; badge on an archived account
+common_archived_category,archived,arkiverad,module=core:design; suffix on an archived category chip
+common_discard_title,Discard changes?,Slänga ändringarna?,module=core:design
+common_discard,Discard,Släng,module=core:design
+common_keep_editing,Keep editing,Fortsätt redigera,module=core:design
+common_amount_spoken,%1$s Swedish kronor,%1$s kronor,module=core:design; TalkBack form of an amount; %1$s = number without currency
+common_kr_suffix,SEK,kr,module=core:design; suffix/prefix shown in the amount field
+common_ratio,%1$s/%2$s,%1$s/%2$s,"module=core:design; ratio label, percents without % sign, e.g. 60/40"
+common_percent,%1$s %%,%1$s %%,"module=core:design; percent with one decimal, e.g. 23,4 %"
+common_user_split,%1$s %2$s,%1$s %2$s,"module=core:design; name + amount in a split preview: ""Du 206,00 kr"""
+common_separator_dot, · , · ,module=core:design; list separator
+common_and,%1$s and %2$s,%1$s och %2$s,module=core:design
+common_separator_comma,", ",", ",module=core:design; list separator in TalkBack sentences and summaries
+common_partner,Partner,Partner,"module=core:design; role word for the member: S-3 empty seat, S-13 partner row"
+common_invite_partner,Invite partner,Bjud in partner,"module=core:design; S-3 button, S-13 row, S-21 title"
+common_date_spoken,Date: %1$s,Datum: %1$s,module=core:design; TalkBack of the date chip in S-7 and S-9
+nav_home,Home,Översikt,module=core:design
+nav_analytics,Analytics,Statistik,module=core:design
+nav_settings,Settings,Inställningar,module=core:design
+nav_label,Navigation,Navigering,module=core:design; TalkBack of the navigation bar
+fab_add_expense,Add expense,Lägg till utgift,module=core:design; FAB on S-4 and S-5
+balance_partner_owes_you,%1$s owes you,%1$s är skyldig dig,module=core:design; %1$s partner name; amount shown separately
+balance_you_owe_partner,You owe %1$s,Du är skyldig %1$s,module=core:design
+balance_partner_owes_you_amount,%1$s owes you %2$s,%1$s är skyldig dig %2$s,module=core:design; one-line form and TalkBack sentence
+balance_you_owe_partner_amount,You owe %1$s %2$s,Du är skyldig %1$s %2$s,module=core:design
+balance_all_settled,All settled up,Ni är kvitt,"module=core:design; global, §9.3"
+balance_account_settled,Settled up,Kvitt,"module=core:design; account screens and rows, §9.3"
+balance_all_settled_body,Nobody owes anything right now.,Ingen är skyldig något just nu.,module=core:design; S-4 hero when settled
+effect_lent,You lent %1$s,Du lånade ut %1$s,module=core:design
+effect_borrowed,You borrowed %1$s,Du lånade %1$s,module=core:design
+effect_not_involved,Not involved,Inte inblandad,module=core:design
+effect_paid_for_yourself,Paid for yourself,Betalade för dig själv,module=core:design
+ledger_settlement_partner_paid_you,%1$s paid you %2$s,%1$s betalade dig %2$s,module=core:design
+ledger_settlement_you_paid_partner,You paid %1$s %2$s,Du betalade %1$s %2$s,module=core:design
+ledger_settlement_accounts:one,%d account,%d konto,"module=core:design; plural one; shown as ""· 2 konton"" only when > 1"
+ledger_settlement_accounts:other,%d accounts,%d konton,module=core:design; plural other
+sync_synced_relative,Synced %1$s,Synkad %1$s,module=core:design; %1$s = relative time below
+time_just_now,just now,nyss,module=core:design
+time_minutes_ago,%d min ago,för %d min sedan,module=core:design
+time_hours_ago,%d h ago,för %d h sedan,module=core:design
+time_today_at,today %1$s,idag %1$s,"module=core:design; %1$s time, e.g. ""Skapad idag 14:02"""
+sync_syncing,Syncing…,Synkar…,module=core:design
+sync_pending:one,%d change waiting to sync,%d ändring väntar på synk,module=core:design; plural one
+sync_pending:other,%d changes waiting to sync,%d ändringar väntar på synk,module=core:design; plural other
+sync_rejected:one,%d change was not synced,%d ändring synkades inte,module=core:design; plural one
+sync_rejected:other,%d changes were not synced,%d ändringar synkades inte,module=core:design; plural other
+sync_failed_line,Sync failed · trying again soon,Synk misslyckades · försöker igen snart,module=core:design
+sync_clock_wrong_short,Your phone's clock is wrong,Telefonens klocka går fel,module=core:design
+sync_line_spoken,Sync status: %1$s,Synkstatus: %1$s,module=core:design; TalkBack of the sync line; %1$s the visible line
+common_wordmark,Kvitt,Kvitt,"module=core:design; translatable=false; same text as app_name; wordmark in the top bars of S-1, S-3, S-4"
+common_tagline,Shared expenses for two,Delade utgifter för två,"module=core:design; S-1 under the wordmark, S-22 under the version"
+common_invite_code,Invite code,Inbjudningskod,"module=core:design; S-1 field label, S-21 code card"
+profile_add_picture,Add picture,Lägg till bild,"module=core:design; avatar editor, S-2 and S-14"
+profile_change_picture,Change picture,Byt bild,module=core:design
+profile_remove_picture,Remove picture,Ta bort bild,module=core:design
+profile_picture_failed,The picture could not be used,Bilden kunde inte användas,module=core:design
+profile_picture_too_large,The picture is too large (max 5 MB),Bilden är för stor (högst 5 MB),module=core:design; payload_too_large
+profile_picture_unsupported,"Use a JPEG, PNG or WebP picture","Använd en bild i JPEG, PNG eller WebP",module=core:design; unsupported_media_type
+profile_picture_invalid,The picture could not be read,Bilden kunde inte läsas,module=core:design; image_invalid
+profile_picture_uploading,Uploading picture…,Laddar upp bilden…,"module=core:design; avatar editor progress, S-2 and S-14; TalkBack reads it without the ellipsis"
+snackbar_expense_saved,Expense saved,Utgiften sparades,module=core:design
+snackbar_expense_saved_offline,Saved. Syncs when you're online.,Sparad. Synkas när du är online.,module=core:design
+snackbar_payment_saved,Payment saved,Betalningen sparades,module=core:design
+ledger_month_total,%1$s · %2$s,%1$s · %2$s,"module=core:design; LedgerMonthHeader: ""Oktober · 7 457,90 kr"""
+editor_percent_of,%1$s %%,%1$s %%,"module=core:design; label of a percent field: ""Jacob %""; RatioEditor in S-6 and S-7"
+editor_percent_suffix,%,%,module=core:design; formatted=false; suffix of a percent field
+editor_amount,Amount,Belopp,module=core:design
+editor_amount_hint,"0,00","0,00","module=core:design; hint; en shows 0.00 (built from the formatter, not this string)"
+editor_description,Description,Beskrivning,module=core:design
+editor_account,Account,Konto,module=core:design
+editor_account_spoken,Account: %1$s,Konto: %1$s,module=core:design; TalkBack of the account chip
+editor_category,Category,Kategori,module=core:design
+editor_category_all,All,Alla,"module=core:design; last tile of the category quick row (S-7, S-19)"
+editor_category_all_spoken,All categories,Alla kategorier,module=core:design; TalkBack of editor_category_all
+editor_paid_by,Paid by,Betalt av,module=core:design
+editor_paid_by_toggle,Paid by %1$s. Double-tap to switch to %2$s.,Betalt av %1$s. Dubbeltryck för att byta till %2$s.,module=core:design; TalkBack
+editor_split,Split,Delning,module=core:design
+editor_split_mode_default,Account default %1$s,Kontots standard %1$s,"module=core:design; %1$s ratio, e.g. 60/40"
+editor_split_mode_ratio,Custom ratio,Egen fördelning,module=core:design
+editor_split_mode_exact,Exact amounts,Exakta belopp,module=core:design
+editor_split_mode_full,All on one person,Allt på en person,module=core:design
+editor_split_full_on,All on %1$s,Allt på %1$s,module=core:design
+editor_split_full_question,Who pays the whole amount?,Vem står för hela beloppet?,module=core:design; full mode prompt
+editor_split_exact_computed,calculated,räknas ut,module=core:design; trailing text of the other person's read-only field in exact mode
+editor_split_exact_of,%1$s %2$s of %3$s,%1$s %2$s av %3$s,"module=core:design; exact-mode error box: name, share, amount"
+editor_split_half_each,Half each,Hälften var,module=core:design; TalkBack of the share bar at 50/50
+editor_split_collapse,Collapse split,Fäll ihop delning,module=core:design; TalkBack of the control that collapses the split section (design-spec)
+editor_ratio_spoken,"%1$s %2$s percent, %3$s %4$s percent","%1$s %2$s procent, %3$s %4$s procent","module=core:design; TalkBack value of the share bar and the ratio slider: name, percent, name, percent"
+editor_ratio_slider,%1$s's share,%1$ss andel,"module=core:design; TalkBack label of the ratio slider (S-6, S-7); sv genitive as onboarding_profile_joining"
+editor_split_preview_effect,This expense: %1$s,Den här utgiften: %1$s,module=core:design; %1$s an effect line
+editor_notes,Notes,Anteckningar,module=core:design
+editor_add_note,Add note,Lägg till anteckning,module=core:design
+editor_keypad_delete,Delete,Radera,module=core:design; TalkBack of the keypad backspace
+editor_keypad_decimal,Decimal separator,Decimaltecken,module=core:design; TalkBack
+editor_keypad,Number keys,Sifferknappar,module=core:design; TalkBack of the in-app keypad group
+editor_keypad_clear,Clear,Rensa,module=core:design; TalkBack custom action on the keypad backspace: clears the amount (design-spec)
+editor_next,Next,Nästa,module=core:design; IME action on the amount
+editor_pick_account,Choose account,Välj konto,module=core:design
+editor_pick_category,Choose category,Välj kategori,module=core:design
+error_amount_required,Enter an amount,Skriv ett belopp,module=core:design; required on amount
+error_amount_out_of_range,The amount must be between %1$s and %2$s,Beloppet måste vara mellan %1$s och %2$s,module=core:design; amount_out_of_range
+error_description_required,Enter a description,Skriv en beskrivning,module=core:design
+error_too_long:one,At most %d character,Högst %d tecken,"module=core:design; plural one; §9.6 length limits (names, descriptions, notes)"
+error_too_long:other,At most %d characters,Högst %d tecken,module=core:design; plural other
+error_date_out_of_range,The date is too far ahead or back,Datumet ligger för långt fram eller bak,module=core:design; §9.6
+error_exact_share_too_large,The share is larger than the amount,Andelen är större än beloppet,"module=core:design; AC-8.7, exact_sum_mismatch"
+error_ratio_invalid,The parts must add up to 100 %,Delarna måste bli 100 %,"module=core:design; formatted=false; bp_out_of_range, bp_sum_not_10000; S-6 and S-7 ratio"
+error_generic_invalid,This could not be saved. Check the fields.,Det gick inte att spara. Kontrollera fälten.,module=core:design; fallback for other domain tokens
+snackbar_expense_deleted,Expense deleted,Utgiften togs bort,module=core:design
+timeline_title,History,Historik,module=core:design
+timeline_created,%1$s created this,%1$s skapade den här,"module=core:design; %1$s actor: ""Anna"" or common_you"
+timeline_deleted,%1$s deleted this,%1$s tog bort den här,module=core:design
+timeline_changed,%1$s changed %2$s from %3$s to %4$s,%1$s ändrade %2$s från %3$s till %4$s,module=core:design; %2$s a timeline_field_* (definite form)
+timeline_changed_added,%1$s added %2$s: %3$s,%1$s lade till %2$s: %3$s,module=core:design; old value empty (e.g. notes)
+timeline_changed_removed,%1$s removed %2$s,%1$s tog bort %2$s,module=core:design; new value empty
+timeline_recurring,Created automatically (recurring),Skapad automatiskt (återkommande),module=core:design; source recurring
+timeline_import,Imported from Splitwise,Importerad från Splitwise,module=core:design; source import
+timeline_pending,Your change is waiting to sync,Din ändring väntar på synk,module=core:design; AC-12.5
+timeline_time,· %1$s,· %1$s,"module=core:design; ""· 16 okt. 19:40"""
+timeline_field_account,the account,kontot,module=core:design
+timeline_field_category,the category,kategorin,module=core:design
+timeline_field_payer,the payer,betalaren,module=core:design
+timeline_field_amount,the amount,beloppet,module=core:design
+timeline_field_description,the description,beskrivningen,module=core:design
+timeline_field_notes,the notes,anteckningen,module=core:design
+timeline_field_date,the date,datumet,module=core:design
+timeline_field_split_mode,the split,delningen,module=core:design; split_mode and splits are shown as one change when both differ
+timeline_field_splits,the shares,andelarna,module=core:design
+timeline_field_from,who paid,vem som betalade,module=core:design; settlement from_user_id
+timeline_field_to,who received,vem som fick,module=core:design; settlement to_user_id
+timeline_field_note,the note,anteckningen,module=core:design; settlement note
+timeline_field_allocations,the allocation,fördelningen,module=core:design
+common_too_much,%1$s too much,%1$s för mycket,"module=core:design; S-7 exact-amount error box, S-9 remaining indicator; %1$s amount"
+snackbar_payment_deleted,Payment deleted,Betalningen togs bort,module=core:design
+category_default_groceries,Groceries,Mat & hushåll,module=core:design; AC-7.2
+category_default_restaurants,Restaurants,Restaurang & café,module=core:design
+category_default_housing,Housing,Boende,module=core:design; AC-7.2 (UX-8)
+category_default_home,Home,Hem,module=core:design
+category_default_utilities,Utilities,Räkningar,module=core:design
+category_default_insurance,Insurance,Försäkringar,module=core:design; AC-7.2 (UX-8)
+category_default_transport,Transport,Transport,module=core:design
+category_default_travel,Travel,Resor,module=core:design
+category_default_entertainment,Entertainment,Nöje,module=core:design
+category_default_health,Health,Hälsa,module=core:design
+category_default_children,Children,Barn,module=core:design; AC-7.2 (UX-8)
+category_default_shopping,Shopping,Shopping,module=core:design
+category_default_fees,Fees & taxes,Avgifter & skatter,module=core:design; AC-7.2 (UX-8)
+category_default_other,Other,Övrigt,module=core:design
+color_green,Green,Grön,"module=core:design; TalkBack of colour swatches, §9.7 tokens"
+color_orange,Orange,Orange,module=core:design
+color_brown,Brown,Brun,module=core:design
+color_yellow,Yellow,Gul,module=core:design
+color_blue,Blue,Blå,module=core:design
+color_teal,Teal,Blågrön,module=core:design
+color_purple,Purple,Lila,module=core:design
+color_red,Red,Röd,module=core:design
+color_pink,Pink,Rosa,module=core:design
+color_grey,Grey,Grå,module=core:design
+color_indigo,Indigo,Indigo,module=core:design
+color_lime,Lime,Lime,module=core:design
+color_slate,Plum,Plommon,"module=core:design; §9.7 token `slate` (D-054); the label names the visible colour, not the token"
+color_amber,Sand,Sand,"module=core:design; §9.7 token `amber` (D-054); the label names the visible colour, not the token"
+icon_shopping_cart,Shopping cart,Kundvagn,"module=core:design; TalkBack of icon picker, §9.7 tokens"
+icon_restaurant,Restaurant,Restaurang,module=core:design
+icon_local_cafe,Café,Kafé,module=core:design
+icon_home,Home,Hem,module=core:design
+icon_bolt,Electricity,El,module=core:design
+icon_water_drop,Water,Vatten,module=core:design
+icon_wifi,Internet,Internet,module=core:design
+icon_directions_car,Car,Bil,module=core:design
+icon_train,Train,Tåg,module=core:design
+icon_local_gas_station,Fuel,Bränsle,module=core:design
+icon_flight,Flight,Flyg,module=core:design
+icon_hotel,Hotel,Hotell,module=core:design
+icon_luggage,Luggage,Bagage,module=core:design
+icon_movie,Film,Film,module=core:design
+icon_music_note,Music,Musik,module=core:design
+icon_sports_esports,Games,Spel,module=core:design
+icon_fitness_center,Fitness,Träning,module=core:design
+icon_medical_services,Health care,Vård,module=core:design
+icon_medication,Medicine,Medicin,module=core:design
+icon_shopping_bag,Shopping bag,Shoppingkasse,module=core:design
+icon_checkroom,Clothes,Kläder,module=core:design
+icon_pets,Pets,Husdjur,module=core:design
+icon_child_care,Children,Barn,module=core:design
+icon_school,School,Skola,module=core:design
+icon_card_giftcard,Gift,Present,module=core:design
+icon_celebration,Party,Fest,module=core:design
+icon_build,Repairs,Reparationer,module=core:design
+icon_chair,Furniture,Möbler,module=core:design
+icon_subscriptions,Subscriptions,Prenumerationer,module=core:design
+icon_category,Other,Övrigt,module=core:design
+icon_key,Key,Nyckel,module=core:design; §9.7 token (UX-8)
+icon_shield,Shield,Sköld,module=core:design; §9.7 token (UX-8)
+icon_account_balance,Bank,Bank,module=core:design; §9.7 token (UX-8)
+update_available,Version %1$s available,Version %1$s finns,module=core:design
+update_details,New features and fixes · %1$s,Nyheter och rättningar · %1$s,"module=core:design; banner subtitle; %1$s APK size from size_bytes (§10.6), e.g. 8,4 MB"
+update_install,Update,Uppdatera,module=core:design
+update_later,Later,Senare,module=core:design
+update_downloading,Downloading… %1$d %%,Laddar ner… %1$d %%,module=core:design
+update_downloading_spoken,Downloading version %1$s,Laddar ner version %1$s,module=core:design; TalkBack of the progress bar
+update_failed,"Download failed, try again",Nedladdningen misslyckades. Försök igen.,module=core:design; AC-16.4
+update_permission_title,Allow installation,Tillåt installation,module=core:design
+update_permission_body,"Kvitt updates straight from your own server, not from the Play Store, so Android asks once whether Kvitt may Install unknown apps. Turn on Allow from this source and come back to Kvitt. The update continues by itself.","Kvitt uppdateras direkt från er server, inte från Play Butik. Därför frågar Android en gång om Kvitt får Installera okända appar. Slå på Tillåt från den här källan och gå sedan tillbaka till Kvitt. Uppdateringen fortsätter av sig själv.",module=core:design; AC-16.3; bold the two Android setting names with ; Android's own labels per locale
+error_household_full,Your partner has already joined,Din partner har redan gått med,module=core:design; household_full
+error_forbidden,You are not allowed to do this,Du får inte göra det här,module=core:design; forbidden
+error_not_found,This no longer exists,Det här finns inte längre,module=core:design; not_found
+error_household_incomplete,Your partner needs to join first,Din partner behöver gå med först,module=core:design; household_incomplete
+error_rate_limited,Too many attempts. Wait a while and try again.,För många försök. Vänta en stund och försök igen.,module=core:design; rate_limited
+error_server,Something went wrong on the server. Try again later.,Något gick fel på servern. Försök igen senare.,"module=core:design; internal, unavailable, not_implemented"
+error_app_too_old,Update Kvitt to continue,Uppdatera Kvitt för att fortsätta,module=core:design; protocol_unsupported
+error_validation,The server did not accept the values,Servern godtog inte värdena,module=core:design; validation_failed without a mapped field
+notif_channel_activity,Activity,Aktivitet,module=core:design; §14.6
+notif_expense_created_title,%1$s added an expense,%1$s lade till en utgift,module=core:design
+notif_expense_created_body,%1$s · %2$s · %3$s,%1$s · %2$s · %3$s,module=core:design; description · amount · account
+notif_expense_updated_title,%1$s changed an expense,%1$s ändrade en utgift,module=core:design
+notif_expense_updated_body,%1$s · %2$s,%1$s · %2$s,module=core:design; description · amount
+notif_expense_deleted_title,%1$s deleted an expense,%1$s tog bort en utgift,module=core:design
+notif_expense_deleted_body,%1$s · %2$s,%1$s · %2$s,module=core:design; description · amount
+notif_settlement_created_title,%1$s recorded a payment,%1$s registrerade en betalning,module=core:design
+notif_settlement_body,%1$s paid %2$s %3$s,%1$s betalade %2$s %3$s,"module=core:design; payer, recipient (dig/you when the viewer), amount"
+notif_settlement_body_to_you,%1$s paid you %2$s,%1$s betalade dig %2$s,module=core:design; recipient is the viewer
+notif_settlement_body_from_you,You paid %1$s %2$s,Du betalade %1$s %2$s,module=core:design; payer is the viewer
+notif_settlement_updated_title,%1$s changed a payment,%1$s ändrade en betalning,module=core:design
+notif_settlement_deleted_title,%1$s deleted a payment,%1$s tog bort en betalning,module=core:design
+notif_settlement_deleted_body,%1$s,%1$s,module=core:design; amount
+notif_recurring_created_title,Recurring expense added,Återkommande utgift tillagd,module=core:design
+notif_recurring_created_body,%1$s · %2$s · %3$s,%1$s · %2$s · %3$s,module=core:design; description · amount · account
+notif_sync_batch_title:one,%1$s made %2$d change,%1$s gjorde %2$d ändring,module=core:design; plural one
+notif_sync_batch_title:other,%1$s made %2$d changes,%1$s gjorde %2$d ändringar,module=core:design; plural other
+notif_sync_batch_body,Open Kvitt to see them,Öppna Kvitt för att se dem,module=core:design
+notif_generic_title_actor,%1$s changed something in Kvitt,%1$s ändrade något i Kvitt,module=core:design; sync failed
+notif_generic_title,Kvitt,Kvitt,"module=core:design; sync failed, actor unknown"
+notif_generic_body,Open Kvitt to see it,Öppna Kvitt för att se det,module=core:design
+onboarding_code_paste,Paste,Klistra in,module=onboarding
+onboarding_code_placeholder,XXXX-XXXX-XXXX,XXXX-XXXX-XXXX,module=onboarding; translatable=false; placeholder of the empty code field
+onboarding_name_label,Your name,Ditt namn,module=onboarding
+onboarding_code_help,You get the code from %1$s: from a link or from Settings › Invite partner.,Koden får du av %1$s: från en länk eller från Inställningar › Bjud in partner.,module=onboarding; %1$s admin name when known from the link; else use onboarding_code_help_generic
+onboarding_code_help_generic,"You get the code from a link, or it is printed when the server is set up.","Koden får du från en länk, eller så skrivs den ut när servern sätts upp.",module=onboarding
+onboarding_server_label,Server address,Serveradress,module=onboarding
+onboarding_pair,Pair,Para ihop,module=onboarding
+onboarding_pairing,Pairing…,Parar ihop…,module=onboarding
+onboarding_again_title,Pair this phone again,Para ihop telefonen igen,module=onboarding; §14.4.8
+onboarding_again_body,Your expenses are still on this phone.,Dina utgifter finns kvar på telefonen.,module=onboarding
+onboarding_error_invite_invalid,This code does not work. It may have expired or already been used. Ask for a new one.,Koden fungerar inte. Den kan ha gått ut eller redan använts. Be om en ny.,module=onboarding
+onboarding_error_network,Could not reach the server. Check the address and your connection.,Kunde inte nå servern. Kontrollera adressen och anslutningen.,module=onboarding
+onboarding_error_rate_limited,Too many attempts. Wait a while and try again.,För många försök. Vänta en stund och försök igen.,module=onboarding
+onboarding_error_https,The address must start with https://,Adressen måste börja med https://,module=onboarding; AC-2.8
+onboarding_error_code_length,The code has 12 characters,Koden har 12 tecken,module=onboarding
+onboarding_error_name_required,Enter your name,Skriv ditt namn,module=onboarding
+onboarding_profile_title,Your profile,Din profil,module=onboarding
+onboarding_profile_joining,You are joining %1$s's household,Du går med i %1$ss hushåll,module=onboarding; sv genitive: name + s (no apostrophe); names ending in s get no extra s (handled in code: Jonas hushåll)
+onboarding_profile_hint,This is how %1$s sees you in Kvitt. You can change it later under Settings › Profile.,Så här ser %1$s dig i Kvitt. Du kan ändra det senare under Inställningar › Profil.,module=onboarding; %1$s the admin's name
+onboarding_waiting_title,Waiting for your partner,Väntar på din partner,module=onboarding
+onboarding_waiting_body,Kvitt needs both of you before you can start. Invite your partner with a link.,Kvitt behöver er båda innan ni kan börja. Bjud in din partner med en länk.,module=onboarding
+onboarding_waiting_invite_sent,Invite sent,Inbjudan skickad,module=onboarding; pending invite row title
+onboarding_waiting_invite_expires,Invite valid until %1$s,Inbjudan gäller till %1$s,"module=onboarding; %1$s weekday + date + time, e.g. mån 19 okt. 21:40"
+onboarding_waiting_footer,"Accounts, expenses and payments become available when your partner has joined.","Konton, utgifter och betalningar blir tillgängliga när din partner har gått med.",module=onboarding
+onboarding_waiting_partner_missing,Partner has not joined,Partner saknas,module=onboarding; TalkBack of the empty seat
+onboarding_removed_title,This phone has been removed,Den här telefonen har tagits bort,module=onboarding
+onboarding_removed_body,Access was removed and all Kvitt data on the phone has been deleted.,Åtkomsten togs bort och allt Kvitt-data på telefonen har raderats.,module=onboarding; AC-3.3
+onboarding_removed_new_code,To use Kvitt again you need a new invite code.,Vill du använda Kvitt igen behöver du en ny inbjudningskod.,module=onboarding
+onboarding_removed_pair_again,Pair again,Para ihop igen,module=onboarding
+home_notification_rationale,Kvitt tells you when %1$s adds or changes something.,Kvitt säger till när %1$s lägger till eller ändrar något.,"module=home; shown on Home before the system permission prompt, after the first full sync (AC-2.9, ia.md §3)"
+home_accounts,Accounts,Konton,module=home
+home_new_account,New account,Nytt konto,module=home
+home_archived_section,Archived (%d),Arkiverade (%d),module=home
+home_settle_up,Settle up,Gör upp,module=home
+home_account_expenses_month:one,%1$d expense in %2$s,%1$d utgift i %2$s,module=home; plural one; %2$s month name (lowercase in sv)
+home_account_expenses_month:other,%1$d expenses in %2$s,%1$d utgifter i %2$s,module=home; plural other
+home_account_no_expenses_month,No expenses in %1$s,Inga utgifter i %1$s,module=home
+home_loading,Getting your expenses…,Hämtar era utgifter…,module=home; first sync only
+home_loading_body,The first sync fetches everything on the server. It only takes a moment.,Första synken hämtar allt som finns på servern. Det tar bara en stund.,module=home; first sync only
+home_empty_welcome,You and %1$s are in,Du och %1$s är med,module=home; %1$s partner name; S-4 empty state card
+home_empty_subtitle,The household is ready. Now you need an account.,Hushållet är klart. Nu behövs ett konto.,module=home
+home_empty_title,Create your first account,Skapa ert första konto,module=home
+home_empty_body,"An account collects expenses that you share the same way, for example Everyday, The house or a trip. You can have as many as you like.","Ett konto samlar utgifter som ni delar på samma sätt, till exempel Vardag, Huset eller en resa. Ni kan ha hur många ni vill.",module=home
+home_sync_status_sync_now,Sync now,Synka nu,module=home
+home_sync_status_title,Sync status,Synkstatus,module=home
+home_sync_status_all_synced,Everything is synced,Allt är synkat,module=home
+home_sync_status_all_synced_body,All changes are on the server.,Alla ändringar finns på servern.,module=home; followed by the sync line
+home_sync_status_last_success,Last successful sync %1$s,Senast lyckade synk %1$s,module=home; %1$s time or list date and time
+home_sync_status_pending_title,Pending changes (%d),Väntande ändringar (%d),module=home
+home_sync_status_tag_new,New,Ny,module=home; pending row: home_sync_status_row_pending(tag)
+home_sync_status_tag_changed,Changed,Ändrad,module=home
+home_sync_status_tag_deleted,Deleted,Borttagen,module=home
+home_sync_status_row_pending,%1$s · waiting to sync,%1$s · väntar på synk,module=home; %1$s a home_sync_status_tag_*
+home_sync_status_rejected_title,Changes that were not synced,Ändringar som inte synkades,module=home
+home_sync_status_dismiss,Remove from list,Ta bort från listan,module=home
+home_sync_status_open,Open,Öppna,module=home; opens the rejected change read-only
+home_sync_reject_stale,Your change was replaced by a newer change from %1$s,Din ändring ersattes av en nyare ändring från %1$s,module=home; §14.4.6
+home_sync_reject_stale_help,%1$s's version applies. Open the expense if you want to make your change again.,%1$ss version gäller. Öppna utgiften om du vill göra din ändring igen.,module=home; sv genitive as onboarding_profile_joining
+home_sync_reject_stale_help_payment,%1$s's version applies. Open the payment if you want to make your change again.,%1$ss version gäller. Öppna betalningen om du vill göra din ändring igen.,module=home; as home_sync_reject_stale_help
+home_sync_reject_deleted,This was deleted before your change arrived,Den här togs bort innan din ändring kom fram,module=home
+home_sync_reject_validation,This change was not valid and was not saved,Ändringen var inte giltig och sparades inte,module=home
+home_sync_reject_reference,Its account or category no longer exists,Dess konto eller kategori finns inte längre,module=home
+home_sync_reject_clock_skew,Your phone's clock is wrong. Fix it and sync again,Telefonens klocka går fel. Rätta den och synka igen,module=home
+home_sync_status_clock_warning,Your phone's clock is wrong. Fix it and sync again.,Telefonens klocka går fel. Rätta den och synka igen.,module=home; §9.10
+home_sync_status_clock_body,The clock is more than 10 minutes ahead of the server. Your changes stay on the phone until it is fixed.,Klockan ligger mer än 10 minuter före servern. Dina ändringar sparas på telefonen tills dess.,module=home; §14.4.6 clock_skew
+home_sync_status_open_date_settings,Date & time,Datum och tid,module=home; opens the system date settings
+home_sync_status_last_attempt_clock,Last attempt refused: the clock,Senaste försöket nekades: klockan,module=home
+account_default_split,%1$s · %2$s,%1$s · %2$s,"module=account; ""50 / 50 · Jacob och Anna""; %1$s ratio, %2$s names with common_and"
+account_default_split_spoken,Default split: %1$s,Standarddelning: %1$s,module=account; TalkBack; %1$s editor_ratio_spoken
+account_settle_this,Settle this account,Gör upp det här kontot,module=account
+account_edit,Edit account,Ändra konto,module=account
+account_archive,Archive,Arkivera,module=account
+account_unarchive,Restore from archive,Återställ från arkiv,module=account
+account_delete,Delete account,Ta bort konto,module=account
+account_delete_blocked,"The account cannot be deleted while it has expenses, payments or recurring expenses","Kontot kan inte tas bort så länge det har utgifter, betalningar eller återkommande utgifter",module=account; AC-6.5; also the error account_not_empty
+account_delete_confirm_title,Delete the account %1$s?,Ta bort kontot %1$s?,module=account
+account_delete_confirm_body,This cannot be undone.,Det går inte att ångra.,module=account
+account_archived_snackbar,%1$s was archived,%1$s arkiverades,module=account
+account_unarchived_snackbar,%1$s was restored,%1$s återställdes,module=account
+account_empty_title,No expenses yet,Inga utgifter än,module=account
+account_empty_body,Add the first expense in %1$s.,Lägg till den första utgiften i %1$s.,module=account
+account_editor_new,New account,Nytt konto,module=account
+account_editor_edit,Edit account,Ändra konto,module=account
+account_editor_default_split,Default split,Standarddelning,module=account
+account_editor_preview,Of %1$s: %2$s,Av %1$s: %2$s,"module=account; ""Av 100,00 kr: Jacob 60,00 kr, Anna 40,00 kr"""
+account_editor_split_hint,Applies to new expenses in the account. Each expense can change its own split.,Gäller nya utgifter i kontot. Varje utgift kan ändra sin egen delning.,module=account; AC-6.3; S-6 under the split control (new and edit)
+account_editor_offline_body,Accounts are saved straight to the server. Connect the phone to the internet to create the account.,Konton sparas direkt på servern. Anslut telefonen till internet för att skapa kontot.,module=account; §9.9; under the title common_needs_connection; new account
+account_editor_offline_body_edit,Accounts are saved straight to the server. Connect the phone to the internet to save the change.,Konton sparas direkt på servern. Anslut telefonen till internet för att spara ändringen.,module=account; §9.9; edit mode
+account_editor_name_taken,There is already an account called %1$s,Det finns redan ett konto som heter %1$s,module=account; name_taken
+expense_editor_new,New expense,Ny utgift,module=expense
+expense_editor_edit,Edit expense,Ändra utgift,module=expense
+expense_editor_date,Date,Datum,module=expense
+expense_editor_delete,Delete expense,Ta bort utgift,module=expense
+expense_editor_discard_title,Discard the expense?,Slänga utgiften?,module=expense
+expense_detail_title,Expense,Utgift,module=expense
+expense_detail_account,Account,Konto,module=expense
+expense_detail_date,Date,Datum,module=expense
+expense_detail_paid_by,Paid by,Betalt av,module=expense
+expense_detail_split,Split,Delning,module=expense
+expense_detail_notes,Notes,Anteckning,module=expense
+expense_detail_recurring,Recurring: %1$s,Återkommande: %1$s,module=expense; AC-8.11
+expense_detail_delete_title,Delete the expense?,Ta bort utgiften?,module=expense
+expense_detail_delete_body,It disappears for both of you.,Den försvinner för er båda.,module=expense
+expense_detail_deleted_banner,%1$s deleted this expense · %2$s,%1$s tog bort den här utgiften · %2$s,"module=expense; %1$s actor (Du/Anna), %2$s date+time"
+expense_detail_deleted_body,It no longer counts in balances or analytics.,Den räknas inte längre i saldon eller statistik.,module=expense; under the deleted banner
+expense_detail_missing,This expense is not on the phone yet,Utgiften finns inte på telefonen än,module=expense
+expense_detail_missing_body,It may have just been added on another phone. Sync to fetch it.,Den kan ha lagts till nyss på en annan telefon. Synka för att hämta den.,module=expense; S-8 opened from a notification before the sync
+settle_title,Settle up,Gör upp,module=settle
+settle_title_edit,Edit payment,Ändra betalning,module=settle
+settle_title_account,Settle %1$s,Gör upp %1$s,module=settle; per-account variant; %1$s account name
+settle_you_pay,You pay %1$s,Du betalar %1$s,module=settle
+settle_partner_pays,%1$s pays you,%1$s betalar dig,module=settle
+settle_swap,Swap direction,Byt riktning,module=settle; TalkBack and tooltip
+settle_direction,Direction,Riktning,module=settle; label above settle_you_pay / settle_partner_pays
+settle_amount,Amount,Belopp,module=settle
+settle_empty_settled,You are all settled up right now.,Ni är kvitt just nu.,module=settle; S-9 when nothing is owed
+settle_empty_hint,Enter an amount and Kvitt suggests an allocation.,Skriv ett belopp så föreslår Kvitt en fördelning.,module=settle; S-9 when nothing is owed
+settle_allocation,Allocation,Fördelning,module=settle
+settle_row_debt,Debt %1$s,Skuld %1$s,module=settle; allocation row when the account's debt runs in the payment's direction; otherwise balance_*_amount or balance_account_settled
+settle_row_remove,Remove %1$s from the allocation,Ta bort %1$s från fördelningen,module=settle; TalkBack
+settle_over_settle_warning,This settles more than is owed in %1$s,Det här reglerar mer än som är skyldigt i %1$s,module=settle; AC-11.5
+settle_remaining,Left to allocate,Kvar att fördela,"module=settle; label; the amount is shown next to it (neutral, or warning colour when not 0)"
+settle_add_account,Add account,Lägg till konto,module=settle
+settle_untouched_partner_owes,"%1$s is left as it is, because %2$s owes you there.","%1$s lämnas som det är, eftersom %2$s är skyldig dig där.","module=settle; %1$s account name(s) joined with common_and, %2$s partner"
+settle_untouched_you_owe,"%1$s is left as it is, because you owe %2$s there.","%1$s lämnas som det är, eftersom du är skyldig %2$s där.",module=settle; as settle_untouched_partner_owes
+settle_propose_again,Suggest again,Föreslå igen,module=settle
+settle_date,Date,Datum,module=settle
+settle_note,Note,Anteckning,module=settle
+settle_note_hint,For example Swish,Till exempel Swish,module=settle; placeholder of the note field
+settle_confirm,Confirm,Bekräfta,module=settle
+settle_confirm_dialog_title,Confirm the payment,Bekräfta betalningen,module=settle
+settle_confirm_body_you_pay,You pay %1$s %2$s %3$s.,Du betalar %1$s %2$s %3$s.,"module=settle; %1$s partner, %2$s amount, %3$s settle_confirm_date_today or settle_confirm_date"
+settle_confirm_body_partner_pays,%1$s pays you %2$s %3$s.,%1$s betalar dig %2$s %3$s.,module=settle; as settle_confirm_body_you_pay
+settle_confirm_date_today,"today, %1$s","idag, %1$s",module=settle; %1$s detail date
+settle_confirm_date,dated %1$s,med datum %1$s,module=settle; %1$s detail date
+settle_error_allocation_sum,The accounts must add up to the amount,Kontona måste bli lika med beloppet,module=settle; allocation_sum_mismatch
+settle_error_allocation_positive,"Each account needs at least 0,01","Varje konto behöver minst 0,01 kr",module=settle; allocation_not_positive
+settle_error_no_allocations,Add at least one account,Lägg till minst ett konto,module=settle; no_allocations
+settle_error_too_many_allocations,At most 50 accounts,Högst 50 konton,module=settle; too_many_allocations
+settle_detail_title,Payment,Betalning,module=settle; S-10 title; also the type label on a settlement row in S-5
+settle_detail_partner_paid_you,%1$s paid you,%1$s betalade dig,module=settle; S-10 header; amount shown below
+settle_detail_you_paid_partner,You paid %1$s,Du betalade %1$s,module=settle; S-10 header
+settle_detail_delete_title,Delete the payment?,Ta bort betalningen?,module=settle
+settle_detail_delete_body,Balances are recalculated for both of you.,Saldona räknas om för er båda.,module=settle
+settle_detail_deleted_banner,%1$s deleted this payment · %2$s,%1$s tog bort den här betalningen · %2$s,module=settle
+settle_detail_deleted_body,It no longer counts in the balances.,Den räknas inte längre i saldona.,module=settle; under the deleted banner
+settle_detail_missing,This payment is not on the phone yet,Betalningen finns inte på telefonen än,module=settle; S-10 opened from a notification before the sync
+settle_detail_missing_body,It may have just been added on another phone. Sync to fetch it.,Den kan ha lagts till nyss på en annan telefon. Synka för att hämta den.,module=settle; as expense_detail_missing_body
+analytics_title,Analytics,Statistik,module=analytics
+analytics_period,Period,Period,module=analytics
+analytics_period_spoken,Period: %1$s,Period: %1$s,module=analytics; TalkBack of the period chip
+analytics_range_this_month,This month,Denna månad,module=analytics
+analytics_range_last_3_months,Last 3 months,Senaste 3 mån,module=analytics
+analytics_range_last_6_months,Last 6 months,Senaste 6 mån,module=analytics
+analytics_range_last_12_months,Last 12 months,Senaste 12 mån,module=analytics
+analytics_range_this_year,This year,I år,module=analytics
+analytics_range_all_time,All time,All tid,module=analytics
+analytics_range_custom,Custom…,Anpassad…,module=analytics
+analytics_range_custom_dates,Custom · %1$s,Anpassad · %1$s,module=analytics; period chip with a custom range; %1$s short date range
+analytics_range_dates,%1$s – %2$s,%1$s – %2$s,module=analytics
+analytics_granularity_month,Month,Månad,module=analytics
+analytics_granularity_week,Week,Vecka,module=analytics
+analytics_measure_household,Household,Hushållet,module=analytics
+analytics_measure_my_share,My share,Min del,module=analytics
+analytics_group_by_category,Category,Kategori,module=analytics
+analytics_group_by_account,Account,Konto,module=analytics
+analytics_total,Total,Totalt,module=analytics; total card label; subtitle range · measure
+analytics_weeks:one,%d week,%d vecka,module=analytics; plural one; total card subtitle in week granularity
+analytics_weeks:other,%d weeks,%d veckor,module=analytics; plural other
+analytics_over_time,Spending over time,Utgifter över tid,module=analytics
+analytics_chart_spoken,%1$s per %2$s and %3$s,%1$s per %2$s och %3$s,"module=analytics; TalkBack label of chart 1: measure, granularity, group-by (toggle labels in lower case)"
+analytics_by_category,By category,Per kategori,module=analytics
+analytics_by_account,By account,Per konto,module=analytics
+analytics_week_label,week %1$d,vecka %1$d,module=analytics; ISO week in TalkBack sentences and the S-12 title; the x-axis shows the bare number
+analytics_axis_thousands,%1$sk,%1$s tkr,"module=analytics; compact y-axis label, only when whole kronor do not fit"
+analytics_empty,No expenses in this period,Inga utgifter i den här perioden,module=analytics
+analytics_empty_body,"Choose another period, or add an expense dated within it.","Välj en annan period, eller lägg till en utgift med ett datum i perioden.",module=analytics
+analytics_change_period,Change period,Byt period,module=analytics
+analytics_summary_over_time,"Spending over time, %1$s: highest in %2$s with %3$s, lowest in %4$s with %5$s.","Utgifter över tid, %1$s: högst i %2$s med %3$s, lägst i %4$s med %5$s.",module=analytics; TalkBack summary of chart 1
+analytics_summary_breakdown,%1$s: largest is %2$s with %3$s (%4$s).,%1$s: störst är %2$s med %3$s (%4$s).,module=analytics; TalkBack summary of charts 2 and 3
+analytics_bar_spoken,%1$s: %2$s,%1$s: %2$s,module=analytics; TalkBack per bar
+analytics_drilldown_empty,No expenses any more,Inga utgifter längre,module=analytics
+analytics_drilldown_title,"%1$s, %2$s","%1$s, %2$s","module=analytics; ""Mat & hushåll, oktober 2026"""
+analytics_drilldown_expenses:one,%d expense,%d utgift,module=analytics; plural one; S-12 total card subtitle
+analytics_drilldown_expenses:other,%d expenses,%d utgifter,module=analytics; plural other
+settings_profile_offline_banner,Needs connection. Your profile can only be changed online.,Kräver anslutning. Profilen kan bara ändras när telefonen är online.,module=settings
+settings_title,Settings,Inställningar,module=settings
+settings_role_admin,You · Admin,Du · Administratör,module=settings; S-13 own row when the viewer is the admin
+settings_role_member,You,Du,module=settings; S-13 own row when the viewer is the member
+settings_role_admin_other,Admin,Admin,module=settings; S-13 the admin's row seen by the member; the member's row seen by the admin is common_partner
+settings_profile,Profile,Profil,module=settings
+settings_profile_summary,Name and picture,Namn och bild,module=settings; S-13 row subtitle
+settings_profile_hint,This is how %1$s sees you in Kvitt.,Så här ser %1$s dig i Kvitt.,module=settings; S-14; %1$s partner
+settings_categories,Categories,Kategorier,module=settings
+settings_categories_summary:one,%d active,%d aktiv,module=settings; plural one; S-13 row subtitle
+settings_categories_summary:other,%d active,%d aktiva,module=settings; plural other
+settings_recurring,Recurring expenses,Återkommande utgifter,module=settings
+settings_recurring_summary_active:one,%d active,%d aktiv,"module=settings; plural one; S-13 row subtitle, joined with common_separator_comma"
+settings_recurring_summary_active:other,%d active,%d aktiva,module=settings; plural other
+settings_recurring_summary_paused:one,%d paused,%d pausad,module=settings; plural one; shown when > 0
+settings_recurring_summary_paused:other,%d paused,%d pausade,module=settings; plural other
+settings_devices,Devices,Enheter,module=settings
+settings_devices_summary:one,%d device,%d enhet,module=settings; plural one; S-13 row subtitle
+settings_devices_summary:other,%d devices,%d enheter,module=settings; plural other
+settings_pair_device,Pair a device,Para ihop en enhet,module=settings
+settings_sync_status,Sync status,Synkstatus,module=settings
+settings_about,About Kvitt,Om Kvitt,module=settings
+settings_about_summary,Version %1$s,Version %1$s,module=settings; S-13 row subtitle; version name
+settings_pending_badge,%d waiting,%d väntar,module=settings
+settings_update_badge,Update available,Uppdatering finns,module=settings
+settings_categories_title,Categories,Kategorier,module=settings
+settings_categories_new,New category,Ny kategori,module=settings
+settings_categories_archived,Archived (%d),Arkiverade (%d),module=settings
+settings_categories_reorder_offline,Needs connection to change,Kräver anslutning för att ändra,module=settings
+settings_categories_move_up,Move up,Flytta upp,module=settings; TalkBack custom action
+settings_categories_move_down,Move down,Flytta ned,module=settings; TalkBack custom action
+settings_categories_drag_handle,Move %1$s,Flytta %1$s,module=settings; TalkBack of the drag handle
+settings_category_editor_new,New category,Ny kategori,module=settings
+settings_category_editor_edit,Edit category,Ändra kategori,module=settings
+settings_category_archived_badge,Archived,Arkiverad,module=settings; badge in the editor of an archived category
+settings_category_editor_reset_name,Reset name,Återställ namn,module=settings; AC-7.4
+settings_category_editor_default_name,Default name: %1$s,Standardnamn: %1$s,module=settings; AC-7.4
+settings_category_editor_icon,Icon,Ikon,module=settings
+settings_category_editor_color,Colour,Färg,module=settings
+settings_category_editor_archive,Archive,Arkivera,module=settings
+settings_category_editor_unarchive,Restore from archive,Återställ från arkiv,module=settings
+settings_category_editor_delete,Delete category,Ta bort kategori,module=settings
+settings_category_delete_title,Delete the category %1$s?,Ta bort kategorin %1$s?,module=settings; confirmation dialog; %1$s category name
+settings_category_delete_body,It disappears from the list for both of you. This cannot be undone.,Den försvinner från listan för er båda. Det går inte att ångra.,module=settings; confirmation dialog body
+settings_category_delete_blocked_default,"Default categories cannot be deleted, only archived","Standardkategorier kan inte tas bort, bara arkiveras",module=settings; category_is_default
+settings_category_delete_blocked_default_short,Cannot be deleted: default category,Kan inte tas bort: standardkategori,module=settings; inline reason under the disabled delete
+settings_category_delete_blocked_in_use,The category is used by expenses or recurring expenses. Archive it instead.,Kategorin används av utgifter eller återkommande utgifter. Arkivera den i stället.,module=settings; category_in_use
+settings_category_delete_blocked_in_use_archived,The category is used by expenses or recurring expenses and cannot be deleted.,Kategorin används av utgifter eller återkommande utgifter och kan inte tas bort.,"module=settings; category_in_use, shown for an archived category"
+settings_about_up_to_date,You have the latest version,Du har den senaste versionen,module=settings
+settings_about_check_update,Check for update,Sök efter uppdatering,module=settings
+settings_recurring_title,Recurring expenses,Återkommande utgifter,module=settings
+settings_recurring_new,New recurring,Ny återkommande,module=settings; S-19 FAB
+settings_recurring_every_month_on,Every month on day %1$s,Varje månad den %1$s,module=settings; %1$s = settings_recurring_day_ordinal_a or _e
+settings_recurring_day_ordinal_e,%1$d,%1$d:e,module=settings; sv ordinal for every day not covered by _a
+settings_recurring_day_ordinal_a,%1$d,%1$d:a,"module=settings; sv ordinal for days 1, 2, 21, 22, 31"
+settings_recurring_next,Next %1$s,Nästa %1$s,module=settings; %1$s list date
+settings_recurring_row_you_pay,%1$s · You pay,%1$s · Du betalar,module=settings; list row; %1$s account name
+settings_recurring_row_partner_pays,%1$s · %2$s pays,%1$s · %2$s betalar,"module=settings; list row; %1$s account, %2$s partner"
+settings_recurring_paused,Paused,Pausad,module=settings
+settings_recurring_empty_title,No recurring expenses,Inga återkommande utgifter,module=settings
+settings_recurring_empty_body,"Rent, electricity and streaming can be added automatically every month.","Hyra, el och streaming kan läggas in automatiskt varje månad.",module=settings
+settings_recurring_empty_action,New recurring expense,Ny återkommande utgift,module=settings
+settings_recurring_editor_new,New recurring expense,Ny återkommande utgift,module=settings
+settings_recurring_editor_edit,Recurring expense,Återkommande utgift,module=settings; S-19 editor title when editing
+settings_recurring_schedule,Schedule,Schema,module=settings; editor section title
+settings_recurring_day_of_month,Day of month,Dag i månaden,module=settings
+settings_recurring_day_clamp_hint,In shorter months the last day is used,I kortare månader används sista dagen,module=settings; days 29–31
+settings_recurring_first_month,First month,Första månad,module=settings
+settings_recurring_last_month,Last month (optional),Sista månad (valfritt),module=settings
+settings_recurring_no_end,No last month,Ingen slutmånad,module=settings; placeholder of the last-month field
+settings_recurring_paused_switch,Paused,Pausad,module=settings
+settings_recurring_created_at,Created at 06:00 on day %1$s every month,Skapas kl. 06:00 den %1$s varje månad,module=settings; §9.5; %1$s = settings_recurring_day_ordinal_a or _e
+settings_recurring_resume_hint,Months that passed while paused are not created afterwards.,Månader som passerat under pausen skapas inte i efterhand.,module=settings; §9.5
+settings_recurring_edit_hint,The change applies to upcoming expenses.,Ändringen gäller kommande utgifter.,module=settings; AC-9.4
+settings_recurring_delete_title,Delete %1$s?,Ta bort %1$s?,module=settings
+settings_recurring_delete_body,Expenses already created stay.,Utgifter som redan skapats finns kvar.,module=settings
+settings_recurring_offline_banner,Needs connection. Recurring expenses can only be changed online.,Kräver anslutning. Återkommande utgifter kan bara ändras när telefonen är online.,module=settings
+settings_recurring_error_end_before_start,The last month cannot be before the first,Sista månaden kan inte vara före den första,module=settings
+settings_recurring_error_start_too_early,At most 12 months back,Högst 12 månader bakåt,module=settings
+settings_devices_title,Devices,Enheter,module=settings
+settings_devices_paired,Paired %1$s,Parad %1$s,module=settings
+settings_devices_last_seen,Last seen %1$s,Senast sedd %1$s,module=settings
+settings_devices_app_version,Kvitt %1$s,Kvitt %1$s,module=settings
+settings_devices_this_device,This device,Den här enheten,module=settings; AC-3.1
+settings_devices_revoke,Remove access,Ta bort åtkomst,module=settings; row button and confirm button (AC-3.2)
+settings_devices_cannot_revoke_current,You cannot remove access for the phone you are using right now.,Du kan inte ta bort åtkomsten för den telefon du använder just nu.,module=settings; AC-3.2
+settings_devices_revoke_title,Remove access for %1$s?,Ta bort åtkomst för %1$s?,module=settings; %1$s device name
+settings_devices_revoke_body_own,The phone is signed out and all Kvitt data on it is deleted. You can pair it again with a new invite code.,Telefonen loggas ut och allt Kvitt-data på den raderas. Du kan para ihop den igen med en ny inbjudningskod.,module=settings; one of the viewer's own other devices
+settings_devices_revoke_body_partner,%1$s's phone is signed out and all Kvitt data on it is deleted. %1$s can pair it again with a new invite code.,%1$ss telefon loggas ut och allt Kvitt-data på den raderas. %1$s kan para ihop den igen med en ny inbjudningskod.,module=settings; admin revoking the partner's device; sv genitive as onboarding_profile_joining
+settings_devices_offline,Needs connection to show devices,Kräver anslutning för att visa enheter,module=settings
+settings_devices_loading,Loading devices…,Hämtar enheter…,module=settings
+settings_invites_title_device,Pair a device,Para ihop en enhet,module=settings
+settings_invites_new_device_for,New device for %1$s,Ny enhet för %1$s,module=settings; created invite card title
+settings_invites_new_partner,Invite for your partner,Inbjudan till din partner,module=settings; created partner invite card title
+settings_invites_create_body,Create a one-time code to pair another phone. The code is valid for 72 hours.,Skapa en engångskod för att para ihop en telefon till. Koden gäller i 72 timmar.,module=settings; S-21 create card
+settings_invites_for_me,For me,För mig,module=settings
+settings_invites_for_partner,For %1$s,För %1$s,module=settings
+settings_invites_valid_until,Valid until %1$s,Gäller till %1$s,module=settings
+settings_invites_share,Share,Dela,module=settings
+settings_invites_copy_code,Copy code,Kopiera kod,module=settings
+settings_invites_code_copied,Code copied,Koden kopierades,module=settings
+settings_invites_instructions,"Open the link on the new phone, or enter the code in Kvitt. The code works once.","Öppna länken på den nya telefonen, eller skriv in koden i Kvitt. Koden fungerar en gång.",module=settings
+settings_invites_share_message,Join our household in Kvitt: %1$s The code is %2$s.,Gå med i vårt hushåll i Kvitt: %1$s Koden är %2$s.,module=settings
+settings_invites_pending,Pending invites,Väntande inbjudningar,module=settings
+settings_invites_none,No pending invites,Inga väntande inbjudningar,module=settings
+settings_invites_offline_banner,Needs connection. Invites can only be created and shown online.,Kräver anslutning. Inbjudningar kan bara skapas och visas när telefonen är online.,module=settings; §9.9
+settings_invites_kind_partner,Partner,Partner,module=settings
+settings_invites_kind_device,Device · %1$s,Enhet · %1$s,module=settings; pending invite row; %1$s the user the device is for
+settings_invites_created,Created %1$s,Skapad %1$s,module=settings; %1$s time_today_at or list date and time
+settings_invites_code_hidden,The code is only shown when it is created,Koden visas bara när den skapas,module=settings
+settings_invites_cancel,Cancel invite,Avbryt inbjudan,module=settings; TalkBack of the Avbryt (common_cancel) button on a pending invite row
+settings_invites_cancel_title,Cancel the invite?,Avbryta inbjudan?,module=settings
+settings_about_title,About Kvitt,Om Kvitt,module=settings
+settings_about_version,Kvitt %1$s (%2$d),Kvitt %1$s (%2$d),module=settings; version name and code
+settings_about_server,Server,Server,module=settings
+settings_about_server_version,Server version,Serverversion,module=settings; label; value from /health
+settings_about_server_version_unknown,Unknown,Okänd,module=settings
+settings_about_licences,Open-source licences,Licenser för öppen källkod,module=settings
diff --git a/docs/ux/tokens.md b/docs/ux/tokens.md
deleted file mode 100644
index 6f27bf4..0000000
--- a/docs/ux/tokens.md
+++ /dev/null
@@ -1,223 +0,0 @@
-# Kvitt: design tokens
-
-Status: U1 proposal (2026-10-17). One token **set of names** (binding once the owner picks a direction) with **values for all three directions** (`directions/1-skargard`, `2-kvitto`, `3-kalas`), so `:core:design` can be built from whichever is chosen. After the pick, U2 copies the chosen column into `design-spec.md` and deletes the other two here (one source of truth). Each direction's review page recomputes every contrast ratio below in JavaScript and marks failures; the values here were checked there.
-
-Contrast targets (SPEC §13.1): text ≥ 4.5:1 against the surface it sits on; icons and graphics ≥ 3:1; nothing conveyed by colour alone.
-
-## 1. How tokens map to Compose
-
-| Token group | Compose home in `:core:design` | Notes |
-|---|---|---|
-| Material 3 colour roles (§2) | `lightColorScheme(...)` / `darkColorScheme(...)` | names map 1:1 (`on-primary-container` → `onPrimaryContainer`) |
-| Kvitt semantic roles (§3) | `KvittColors` data class in a `CompositionLocal`, light and dark instance | `owed*`, `owing*`, `settled*`, `warning*`, `pending`, `avatar*` |
-| Category colours (§4) | `KvittColors.category(ColorToken)` and `.categoryContainer(ColorToken)` | `ColorToken` enum from `:core:model` (§14.2.4) |
-| Chart colours (§5) | `KvittColors.chartCategorical[0..7]`, `chartSequential[0..4]`, `chartGrid`, `chartAxisLabel` | |
-| Type (§6) | `Typography(...)` + `KvittTypography(amountHero, amountLarge, amountMedium, amountSmall)` | amounts always `fontFeatureSettings = "tnum"` |
-| Spacing (§7) | `object KvittSpacing { val s1 = 4.dp … }` | |
-| Shapes (§8) | `Shapes(extraSmall…extraLarge)` + `KvittShapes` for the Expressive sizes and custom shapes | |
-| Elevation, motion (§9) | `MotionScheme` (Expressive) or durations/easings constants | |
-
-Dynamic colour (Material You): offered only by Skärgård, as an off-by-default option that replaces §2 only; §3–§5 keep their values in every case.
-
-## 2. Material 3 colour roles
-
-`L` = light, `D` = dark.
-
-| Token | Skärgård L | Skärgård D | Kvitto L | Kvitto D | Kalas L | Kalas D |
-|---|---|---|---|---|---|---|
-| primary | #1A6476 | #8ED0E2 | #23408E | #B3C3FF | #B23A1C | #FFB59F |
-| on-primary | #FFFFFF | #00363F | #FFFFFF | #112A6A | #FFFFFF | #5E1604 |
-| primary-container | #C2E8F3 | #0E4D5C | #DCE3F6 | #2B4590 | #FFDBD0 | #8A2A10 |
-| on-primary-container | #0B4C5C | #C2E8F3 | #14295E | #DDE3FF | #3D0B00 | #FFDBD0 |
-| secondary | #4E5F69 | #B6C9D4 | #4E5466 | #C2C6D8 | #4B6A2A | #B2D38A |
-| on-secondary | #FFFFFF | #20333B | #FFFFFF | #2B3040 | #FFFFFF | #1F3701 |
-| secondary-container | #D3E3EE | #374952 | #DFE2EB | #3B4050 | #D3EFAE | #344F15 |
-| on-secondary-container | #374851 | #D3E5F0 | #1B2030 | #DEE1F1 | #112000 | #D3EFAE |
-| tertiary | #7B5C24 | #E8C48A | #73552A | #DCC39A | #7A5900 | #F2C14E |
-| on-tertiary | #FFFFFF | #422C00 | #FFFFFF | #3E2D10 | #FFFFFF | #402D00 |
-| tertiary-container | #F7DFB4 | #5D4410 | #F1E2C8 | #574425 | #FFDF8E | #5A3F00 |
-| on-tertiary-container | #5A410E | #FFDEA8 | #2E1F06 | #F7E1BD | #261A00 | #FFDF8E |
-| error | #B3261E | #F2B8B5 | #B0261B | #FFB4A8 | #BA1A1A | #FFB4AB |
-| on-error | #FFFFFF | #601410 | #FFFFFF | #690004 | #FFFFFF | #690005 |
-| error-container | #F9DEDC | #8C1D18 | #F9DCD6 | #8E1E14 | #FFDAD6 | #93000A |
-| on-error-container | #8C1D18 | #F9DEDC | #5A0E08 | #FFDAD4 | #410002 | #FFDAD6 |
-| surface | #F6FAFC | #0F1417 | #F7F4EC | #1C1A17 | #FFF8F1 | #1D1415 |
-| on-surface | #171C1F | #DEE3E6 | #1D1B17 | #ECE6DA | #231918 | #F1DFDB |
-| surface-variant | #DBE4E8 | #40484C | #E7E2D6 | #4A463E | #F5DED6 | #53433F |
-| on-surface-variant | #40484C | #BFC8CC | #58534A | #C0B9AB | #53433F | #D8C2BB |
-| surface-container-lowest | #FFFFFF | #0A0F11 | #FFFDF8 | #161512 | #FFFFFF | #170F10 |
-| surface-container-low | #F0F4F7 | #171C1F | #F2EEE5 | #211F1B | #FFF1E8 | #251B1C |
-| surface-container | #EAEFF2 | #1B2124 | #EDE9DF | #26241F | #FAEADF | #2A1F20 |
-| surface-container-high | #E4E9EC | #252B2E | #E6E1D6 | #302D28 | #F4E4D8 | #352A2A |
-| surface-container-highest | #DEE3E6 | #303639 | #E0DBCF | #3B3832 | #EEDED2 | #403434 |
-| surface-dim | #D6DBDE | #0F1417 | #D9D4C8 | #1C1A17 | #E6D6CB | #1D1415 |
-| surface-bright | #F6FAFC | #353A3D | #FBF9F3 | #42403A | #FFF8F1 | #463A3A |
-| inverse-surface | #2C3134 | #DEE3E6 | #32302B | #ECE6DA | #392E2C | #F1DFDB |
-| inverse-on-surface | #EDF1F4 | #2C3134 | #F4F0E7 | #32302B | #FFEDE7 | #392E2C |
-| inverse-primary | #8ED0E2 | #1A6476 | #B3C3FF | #23408E | #FFB59F | #B23A1C |
-| outline | #70787C | #899296 | #7E786C | #8D877A | #85736E | #A08C87 |
-| outline-variant | #BFC8CC | #40484C | #D3CDC0 | #48443C | #D8C2BB | #53433F |
-| scrim | #000000 (40 %) | #000000 | #000000 (42 %) | #000000 | #000000 (38 %) | #000000 |
-
-Key contrast (text role vs surface, L / D): on-surface 16.4 / 14.3 · 15.6 / 14.0 · 16.3 / 14.0; on-surface-variant 8.9 / 10.9 · 6.9 / 8.9 · 8.9 / 10.6; primary 6.4 / 10.8 · 8.7 / 10.1 · 5.7 / 10.6. `outline` is used for graphics only (3.99–5.84).
-
-Direction-specific extra aliases: Kvitto `paper` (= surface-container-lowest in light, surface-container-high in dark; the receipt surface). Kalas `card` (#FFFFFF / #2A1F20) and `on-cat` (#FFFFFF / #1D1415, icon colour on solid category tiles).
-
-## 3. Kvitt semantic roles
-
-| Token | Use | Skärgård L | D | Kvitto L | D | Kalas L | D |
-|---|---|---|---|---|---|---|---|
-| owed | text/graphic: partner owes you | #00695C | #7DD8C5 | #2C6A38 | #92CC9A | #1B6B45 | #8BD8A8 |
-| on-owed | | #FFFFFF | #00382F | #FFFFFF | #0E3818 | #FFFFFF | #003920 |
-| owed-container | | #C2EDE4 | #005047 | #DCEBD8 | #1E3A24 | #C4EFD2 | #0F5233 |
-| on-owed-container | | #00443B | #A3F2E0 | #123A1C | #BFE8C4 | #00210F | #A7F5C3 |
-| owing | text/graphic: you owe partner | #A33E1C | #FFB59E | #A23B25 | #F2A890 | #7E2F84 | #F2AEF0 |
-| on-owing | | #FFFFFF | #5E1700 | #FFFFFF | #4F1708 | #FFFFFF | #4C0F53 |
-| owing-container | | #FFDBCF | #7E2C0E | #F6DDD3 | #4B2418 | #F9D7F6 | #66226C |
-| on-owing-container | | #792A0B | #FFDBCF | #4D170A | #FFDACD | #33003A | #FFD6F8 |
-| settled | "Kvitt" / "Ni är kvitt" | #4E5F69 | #B6C9D4 | #58534A | #C0B9AB | #6E5500 | #F2D45C |
-| settled-container | | #DCE5EA | #2C3B43 | #E6E1D6 | #302D28 | #FFE38F | #5A3F00 |
-| on-settled-container | | #36464F | #D3E5F0 | #2A2823 | #E5DFD2 | #241A00 | #FFE38F |
-| warning | over-settle, sync warnings | #855700 | #F5BD62 | #875400 | #EBC06C | #8A4D00 | #FFB86E |
-| on-warning | | #FFFFFF | #452B00 | #FFFFFF | #3E2900 | #FFFFFF | #4A2800 |
-| warning-container | | #FFDEAD | #633F00 | #F7E3BF | #4D3708 | #FFDDBA | #6A3B00 |
-| on-warning-container | | #633F00 | #FFDEAD | #3B2600 | #FCE3AE | #2C1600 | #FFDDBA |
-| pending | sync-pending marker | #7B5C24 | #E8C48A | #875400 | #EBC06C | #00658F | #86CFFF |
-| avatar-1 | initials background, user slot 1 | #C2E8F3 | #0E4D5C | #3F5E4C | #A5C9B0 | #9A4A00 | #FFB870 |
-| on-avatar-1 | | #0B4C5C | #C2E8F3 | #FFFFFF | #10291A | #FFFFFF | #2E1500 |
-| avatar-2 | initials background, user slot 2 | #F2DDB8 | #5D4410 | #6B4C8A | #CDB4E8 | #2C5F9E | #A8C8FF |
-| on-avatar-2 | | #5A400E | #F7DFB4 | #FFFFFF | #2F1B45 | #FFFFFF | #002F64 |
-
-All owed/owing text values ≥ 5.6:1 on surface in both themes in all directions. Owed/owing hue pairs are chosen to stay distinct for red-green colour blindness (Skärgård sea-green vs coral, Kvitto green vs brick with a square marker, Kalas green vs purple); the words carry the meaning anyway. Avatar slot: a stable function of the user id (e.g. the last hex digit's parity), so each person keeps one colour on both phones (AC-4.5).
-
-## 4. Category colours (SPEC §9.7 token names)
-
-Use: the icon colour on its container (`cat-` on `cat--container`), chart segments, Kalas solid tiles (`on-cat` icon on `cat-`). Never text in light themes. Container rule: Skärgård 16 % (L) / 22 % (D) of the colour over surface; Kvitto 16 % / 24 %; Kalas 18 % / 24 %.
-
-Baseline values (used unchanged by Kvitto and Kalas, and by Skärgård in dark):
-
-| Token | Light | Dark | Skärgård light override |
-|---|---|---|---|
-| green | #2E7D32 | #81C784 | #2A732E |
-| orange | #B84A00 | #FFB74D | #A84300 |
-| brown | #795548 | #C8B4AC | — |
-| yellow | #8A6A00 | #F2D45C | #7D6000 |
-| blue | #1565C0 | #90CAF9 | — |
-| teal | #00796B | #80CBC4 | #006F62 |
-| purple | #7B1FA2 | #D1A0DB | — |
-| red | #C62828 | #EF9A9A | — |
-| pink | #AD1457 | #F48FB1 | — |
-| grey | #5F6B73 | #B0BEC5 | #58646C |
-| indigo | #3949AB | #A5AEE0 | — |
-| lime | #5B6B00 | #C5D86D | — |
-
-Measured: icon vs its container ≥ 3.7:1 (light) and ≥ 4.8:1 (dark) in every direction; colour vs surface ≥ 4.6:1 (light) and ≥ 7.7:1 (dark). These differ from the provisional hex values in SPEC §9.7 (orange, brown, yellow, purple, grey, indigo, lime in light; brown, yellow, purple, indigo, lime in dark): adopting them is a spec change (decisions entry + spec version bump) that the orchestrator makes after the pick (SPEC §13.4 U2).
-
-Per-direction container hex values are listed on each review page (computed, not hand-typed).
-
-## 5. Chart colours
-
-| Token | Skärgård L | Skärgård D | Kvitto L | Kvitto D | Kalas L | Kalas D |
-|---|---|---|---|---|---|---|
-| chart-cat-1 | #1A6476 | #8ED0E2 | #23408E | #B3C3FF | #C2462A | #FF9F86 |
-| chart-cat-2 | #A8741A | #E8C48A | #B4602A | #F0A673 | #4F7A2B | #A9D37F |
-| chart-cat-3 | #5B5FA8 | #B8BCF4 | #2D7A6C | #7DCAB8 | #8C6200 | #F2C14E |
-| chart-cat-4 | #4D8559 | #9BD3A3 | #7B4E9E | #CDA8EE | #2D63AE | #9CC2FF |
-| chart-cat-5 | #A0526E | #EDA9C1 | #7F7225 | #D6C66B | #8A3F92 | #E2A6EA |
-| chart-cat-6 | #2F86A0 | #5FB6D0 | #AE3A5C | #F29CB6 | #00796F | #7FD3C8 |
-| chart-cat-7 | #7A6A58 | #CDB9A1 | #4D6A7A | #A6BFCD | #B0306A | #FF9EC3 |
-| chart-cat-8 | #66737C | #AEBDC8 | #73552A | #DCC39A | #5B6670 | #B7C3CC |
-| chart-seq-1 | #D3ECF3 | #1E3A42 | #D6DDF2 | #2A3352 | #FBD8CC | #4A2A23 |
-| chart-seq-2 | #9FD3E2 | #245A68 | #A9B8E3 | #3C4C80 | #F4A68E | #7A3B2C |
-| chart-seq-3 | #62AFC5 | #2F7C8F | #7890CF | #5A70B0 | #E3765A | #B5563C |
-| chart-seq-4 | #2E869E | #5EADC3 | #4865B1 | #8499DC | #C24A2E | #E8826A |
-| chart-seq-5 | #165A6B | #A3DDEC | #23408E | #B3C3FF | #8E2A14 | #FFB59F |
-| chart-grid | #DEE3E6 | #303639 | #E3DED2 | #37342E | #EBDACF | #3E3233 |
-| chart-axis-label | #40484C | #BFC8CC | = on-surface-variant | = on-surface-variant | #53433F | #D8C2BB |
-
-Rules: categorical colours are assigned to **accounts** in `created_at` order (Vardag 1, Huset 2, Gotland 2026 3, Japan 2027 4, cycling after 8); **categories** always use their own category colour, so a category looks the same in lists and charts. Sequential ramps are for single-series views (a highlighted bucket, "Min del" totals); seq-1 and seq-2 are backgrounds only (< 3:1). Stacked segments are separated by a 2 dp gap in the surface colour so adjacent similar hues stay distinct.
-
-## 6. Typography
-
-| Direction | Typeface(s) | Licence | Amounts |
-|---|---|---|---|
-| Skärgård | Roboto Flex (variable: wght, wdth, opsz) | OFL 1.1 | Roboto Flex with `tnum`; hero uses wdth 84 |
-| Kvitto | IBM Plex Sans (text), IBM Plex Mono (amounts, captions) | OFL 1.1 | Plex Mono (tabular by nature) |
-| Kalas | Nunito | OFL 1.1 | Nunito (default figures tabular, verified in renders) |
-
-Type scale (sp / line height / weight):
-
-| Style | Skärgård | Kvitto | Kalas |
-|---|---|---|---|
-| display-large | 57/64 400 | 57/64 400 (unused) | 57/64 800 |
-| display-medium | 45/52 400 | 45/52 400 (unused) | 45/52 800 |
-| display-small | 36/44 400 | 36/44 400 (unused) | 36/44 800 |
-| headline-large | 32/40 500 | 30/38 500 | 32/40 800 |
-| headline-medium | 28/36 500 | 26/34 500 | 28/36 800 |
-| headline-small | 24/32 500 | 22/30 500 | 24/32 800 |
-| title-large | 22/28 500 | 20/28 500 | 22/28 800 |
-| title-medium | 16/24 560 | 16/24 500 | 16/24 700 |
-| title-small | 14/20 560 | 14/20 600 | 14/20 800 |
-| body-large | 16/24 400 | 16/24 400 | 16/24 500 |
-| body-medium | 14/20 400 | 14/20 400 | 14/20 500 |
-| body-small | 12/16 400 | 12/16 400 | 12/16 600 |
-| label-large | 14/20 560 | 14/20 500 | 14/20 800 |
-| label-medium | 12/16 560 | 12/16 500 | 12/16 700 |
-| label-small | 11/16 560 | 11/16 500 Mono caps, tracking 0.08 em | 11/16 700 |
-| amount-hero | 60/64 720 wdth 84, decimals and kr at 50 % | 44/52 500 Mono | 44/52 900 |
-| amount-large | 32/40 650 wdth 92 | 28/36 500 Mono | 28/36 900 |
-| amount-medium | 16/24 620 | 15/22 500 Mono | 18/24 800 |
-| amount-small | 14/20 520 | 12/16 400 Mono | 15/20 800 |
-
-Font scale: sp follows Android's non-linear scaling (Android 14+, Compose default): text ≥ 24 sp grows less than smaller text, so a 60 sp amount does not double at 200 %. The 200 % frames approximate this. Amounts never ellipsize: they wrap or step down one size.
-
-## 7. Spacing (dp)
-
-All three use one 4 dp scale: `space-1` 4, `space-2` 8, `space-3` 12, `space-4` 16, `space-5` 20, `space-6` 24, `space-8` 32, `space-10` 40, `space-12` 48, `space-16` 64 (Kvitto does not use 40 and 64). Screen gutter 16 in all directions.
-
-| Measure | Skärgård | Kvitto | Kalas |
-|---|---|---|---|
-| card padding | 20 | 20 (receipt) | 20 |
-| home account row | ~72 (card row) | 60 | ~76 (card) |
-| ledger row | 72 | 60 | 72 |
-| settings row | 56 | 56 | 56 |
-| keypad key | 64 | 52 | 52 |
-| gap between grouped items | 2 | rule (1 dp dashed) | 12 |
-
-## 8. Shapes (corner radius, dp)
-
-M3 shape tokens: extraSmall 4, small 8, medium 12, large 16, largeIncreased 20, extraLarge 28, extraLargeIncreased 32, extraExtraLarge 48, full.
-
-| Component | Skärgård | Kvitto | Kalas |
-|---|---|---|---|
-| buttons, chips | full | small 8 | full |
-| text fields | large 16 (outlined) | extraSmall 4 | large 16 (filled, no indicator) |
-| cards | extraLarge 28 | none (receipt zig-zag edge, 10 dp teeth) | 24–28 |
-| balance hero | extraLargeIncreased 32 | top corners 8, zig-zag bottom | extraLarge 28 |
-| grouped/segmented lists | outer 20, inner 4 | none, rules | outer 24, inner 8 |
-| category icon container | medium 12 | extraSmall 4 | MaterialShapes Cookie4Sided (fallback 30 %) |
-| avatars | MaterialShapes Cookie9/12 (fallback circle) | full | MaterialShapes Sunny (fallback circle) |
-| FAB | largeIncreased 20 | medium 12 | largeIncreased 20 |
-| dialogs | extraLarge 28 | large 16 | extraLarge 28 |
-| keypad keys | full, morph to 16 on press | small 8 | full |
-
-## 9. Elevation and motion
-
-| | Skärgård | Kvitto | Kalas |
-|---|---|---|---|
-| elevation model | tonal (surface-container levels); 2 dp shadow on menus, 3 dp on FAB | almost none: rules and tones; shadow only on menus, FAB, dialogs | tonal + soft warm shadow on light cards (0 1 2, 0 2 8, 10 % brown); no shadows in dark |
-| motion scheme | `MotionScheme.expressive()` springs: spatial fast 0.6/800, default 0.8/380, slow 0.8/200; effects 1.0/3800, 1600, 800 | standard easing curves: receipt print reveal 350 ms emphasizedDecelerate, number roll 250 ms per digit, "Kvitt" stamp 200 ms, push shared-axis X 300 ms, tabs fade-through 200 ms | `MotionScheme.expressive()`; seesaw re-tilt spring 0.55/300; amount count-up 400 ms emphasizedDecelerate; bunting drop with 40 ms stagger when settled |
-| stable fallback | `MotionScheme.standard()` / tween 200–500 ms emphasized | (already stable) | `MotionScheme.standard()` |
-| reduced motion | everything except fades snaps | everything becomes a 150 ms crossfade | springs critically damped |
-
-## 10. Icons
-
-Material Symbols (Apache 2.0), as a bundled variable font or vector drawables generated from it.
-
-| | Skärgård | Kvitto | Kalas |
-|---|---|---|---|
-| style | Rounded | Sharp | Rounded |
-| weight / grade / optical size | 400 / 0 / 24 | 300 / 0 / 20–24 | 500–600 / 0 / 24 |
-| fill | 1 for selected nav item and category icons, else 0 | 0, except the selected nav item | 1 for category icons and selected nav item |
-
-Category icons: the 30 tokens of SPEC §9.7. Other icons used by all directions: `home`, `bar_chart`, `settings`, `add`, `close`, `arrow_back`, `more_vert`, `edit`, `delete`, `swap_horiz`, `payments`, `cloud_upload` / `cloud_off` / `cloud_done`, `sync`, `warning`, `info`, `history`, `event_repeat`, `devices`, `person_add`, `content_paste`, `share`, `content_copy`, `drag_indicator`, `archive` / `unarchive`, `backspace`, `system_update`, `phonelink_erase`.