/* --- Customer-facing base ---
   Design direction: a trustworthy vault for receipts/warranties, not a marketing site or a
   playful consumer app. Deep navy carries trust and brand recognition in the header on every
   visit, unchanged across light/dark; a single consistent indigo accent (not the old day-of-year
   rotation) carries every interactive/primary-action moment, in the same spirit as the modern,
   confident SaaS look of trafex.io / login.properit.se / stripe.com. Plain confident sans-serif
   throughout, thin borders over heavy shadows, generous whitespace, solid (non-gradient) buttons.

   All colour lives in tokens below so the whole site — including this file's own later rules —
   only ever reads var(--x); the light block is the default, :root[data-theme="dark"] is an
   explicit user choice (persisted in localStorage, see theme-toggle.js), and the
   prefers-color-scheme block covers "auto" (no explicit choice stored) by mirroring the same dark
   values whenever the OS itself is in dark mode. */

/* --- Startsida-only typeface (STARTSIDA-SPEC.md) — self-hosted, not loaded from Google Fonts:
   this app has no third-party network dependency anywhere else (the wordmark logo is outlined to
   vector paths for the same reason, see wwwroot/img/wordmark-*.svg), so a webfont request would
   have been a new external call and a new CSP surface for one page. Files are subsetted to just
   the Latin + Swedish/Nordic characters actually used (å/ä/ö/Å/Ä/Ö etc.), ~7-8KB each. Scoped to
   body.startsida-fonts (set only by Index.cshtml) — every other page keeps the app's own system
   font stack (Peter, 2026-08-16: "ser inte ut som referensen", confirmed the gap was the
   typeface, chose self-hosting over Google Fonts CDN or staying on system fonts). */
@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/archivo-400.woff2") format("woff2");
}

@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/fonts/archivo-500.woff2") format("woff2");
}

@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/fonts/archivo-600.woff2") format("woff2");
}

@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/fonts/archivo-700.woff2") format("woff2");
}

@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("/fonts/archivo-800.woff2") format("woff2");
}

@font-face {
  font-family: "DM Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/dm-mono-400.woff2") format("woff2");
}

@font-face {
  font-family: "DM Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/fonts/dm-mono-500.woff2") format("woff2");
}

body.startsida-fonts {
  font-family: "Archivo", -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

* {
  box-sizing: border-box;
}

/* Reserves the scrollbar's width on every page load, whether or not that page's content is
   actually tall enough to need one — without this, a page short enough to fit on screen (no
   scrollbar) renders its centered content ~7-8px wider than a page that scrolls (scrollbar takes
   space from the viewport), which visibly shifts the header sideways every time navigation moves
   between a scrolling and a non-scrolling page (e.g. "Add receipt" → "My account"). */
html {
  scrollbar-gutter: stable;
}

:root {
  /* Warm cream/paper family, not cool blue-gray — see DESIGNGRANSKNING.md #1. A cold canvas made
     the navy read as muddy and the category dots read as louder than intended; every neutral
     below is warm-grey (brown undertone), never blue-grey. */
  --ink: #12143a;
  --ink-muted: #7c7a6c;
  --hairline: #e6e2d6;
  --surface: #ffffff;
  --surface-alt: #f2efe5;
  --border-inner: #f0ede3;
  --canvas: #faf8f3;
  --navy: #12143a;
  /* Was #0a0b28 — darkening an already near-black colour further is imperceptible (Peter,
     2026-08-28: "'Lägg till kvitto' ... har inte heller hover effekt"). Lightening instead, same
     direction dark theme's own --navy-hover already takes and to the identical value, gives a
     hover state that's actually visible against both --navy itself and the page behind it. */
  --navy-hover: #1f3563;
  /* Uppercase mono labels (stat-label, badge, etc.) — one tick lighter/warmer than --ink-muted's
     general secondary-text role, so labels recede a bit further than body-weight muted text. */
  --text-label: #8a8878;
  /* Between --ink and --ink-muted — for content that's more prominent than a caption but
     shouldn't read as primary text either (category chips, etc.). */
  --text-2: #4a4838;
  /* Zero-value numbers and inactive icon states — reads as "there's genuinely nothing here" (see
     .stat-value-zero), distinct from --ink-muted's "there's data, it's just not primary". */
  --muted-value: #a3a091;
  /* Sampled from the real logo (symbol-reversed-white.svg's dot) rather than an arbitrary indigo —
     see logo-assets/. --accent-hover keeps the same darken ratio the old #4f46e5 -> #4338ca pair
     used (~85% of base). */
  --accent: #5b5fe0;
  --accent-hover: #4548b8;
  --accent-ink: #ffffff;
  /* Same indigo value in both themes, unlike --accent (which pales for dark-mode legibility as
     inline text/focus rings) — for a FILLED button that needs to stay a vivid "press me" pop
     against a dark page rather than fade into a pale-lavender fill. See .receipts-add-button's
     dark-theme override. */
  --accent-solid: #5b5fe0;
  --accent-solid-hover: #4548b8;
  /* Every primary button's colour (generic button/input[submit]/a.auth-submit, see that rule's own
   comment) — navy here, --accent-solid in dark mode (see :root[data-theme="dark"] below), same
   split .receipts-add-button already used. A token, not a per-theme selector override, specifically
   so the theme switch never has to raise that rule's specificity — see the rule's own comment for
   why that mattered (Peter, 2026-08-29). */
  --primary-bg: var(--navy);
  --primary-bg-hover: var(--navy-hover);
  --primary-ink: #ffffff;
  --active-bg: var(--navy);
  --active-ink: #ffffff;
  --active-overlay: rgba(255, 255, 255, 0.25);
  --danger: #b3261e;
  --danger-bg: #fdf1f0;
  --danger-border: #f0cfc9;
  --success: #15803d;
  --success-bg: #f0fdf4;
  --success-border: #bbf7d0;
  --shadow-rgb: 18, 33, 59;
  --overlay-weak: rgba(18, 33, 59, 0.06);
  --overlay-medium: rgba(18, 33, 59, 0.08);
  --focus-ring: rgba(88, 94, 221, 0.22);
  /* List-row hover — one tick darker than --surface-alt/--canvas, used where a row needs to read
     as hoverable inside an already-tinted panel (space switcher, quick-jump — see
     design_handoff_spaces §4) rather than the plain --canvas most hover states already use. */
  --list-hover: #f4f2ec;
  /* Pending/attention notice — a Viewer-role banner or a "Väntar" invite chip
     (design_handoff_spaces §4's "Varning/observera"). Deliberately amber, not --danger — nothing
     is wrong, it's just informational/waiting. */
  --pending-border: #b9a24a;
  --pending-bg: #f7f0d8;
  --pending-ink: #7a6417;
  color-scheme: light;
  /* Landing page's three dark-surface levels (STARTSIDA-SPEC.md "Tre mörka ytnivåer, aldrig en")
     — the deadline band, the cards inside it, and the deeper Pris/CTA band all need to read as
     distinct depths, not one flat navy. */
  --landing-band-bg: #12143a;
  --landing-band-card-bg: #1d2150;
  --landing-band-card-border: #2f3369;
  --landing-step-border: #dfdaca;
  /* The footer is the one surface that never flips with the theme — always the page's deepest
     level, page < .landing-cta-band < footer. Fixed literals instead of --ink/--ink-muted/etc
     (which are dark-on-light in light mode and would be invisible here), defined once at :root
     rather than scoped to .site-footer so nothing needs re-deriving them per theme block — that's
     the actual point of moving them up here (Claude Design, 2026-08-20). */
  --footer-ink: #faf8f3;
  --footer-ink-muted: rgba(250, 248, 243, 0.72);
  --footer-ink-faint: rgba(250, 248, 243, 0.55);
  --footer-hairline: rgba(250, 248, 243, 0.12);
  /* Rceep Plus's tier colour in the plan chip/legend (Kontoadministration §5a/§5c, Claude Design
     2026-08-28) — Free/Spaces/Custom all already map onto existing tokens (--surface-alt/--accent/
     --ink respectively), Plus's olive-gold is the one genuinely new hue in that four-tier legend. */
  --tier-plus-bg: #8a7a3f;
  --tier-plus-ink: #ffffff;
}

:root[data-theme="dark"] {
  --ink: #faf8f3;
  --ink-muted: rgba(250, 248, 243, 0.62);
  --hairline: rgba(250, 248, 243, 0.1);
  --surface: #1a1d45;
  --surface-alt: rgba(250, 248, 243, 0.08);
  --border-inner: rgba(250, 248, 243, 0.06);
  --canvas: #0f1330;
  --navy-hover: #1f3563;
  --text-label: rgba(250, 248, 243, 0.48);
  --text-2: rgba(250, 248, 243, 0.78);
  --muted-value: rgba(250, 248, 243, 0.34);
  /* Same logo-sampled hue as the light theme (symbol-primary-navy.svg's dot) — already almost
     identical to the old #818cf8, which is why dark mode barely changes with this swap. */
  --accent: #a9abf5;
  --accent-hover: #c3c4f8;
  --accent-ink: #12143a;
  /* --accent-solid itself doesn't change between themes (see its own token comment) — only which
     token --primary-bg points at does, navy → the still-vivid accent-solid. */
  --primary-bg: var(--accent-solid);
  --primary-bg-hover: var(--accent-solid-hover);
  --primary-ink: var(--ink);
  --active-bg: var(--accent);
  --active-ink: var(--accent-ink);
  --active-overlay: rgba(20, 18, 58, 0.18);
  --danger: #ff8783;
  --danger-bg: #34191b;
  --danger-border: #5c2b2a;
  --success: #6ee0a8;
  --success-bg: #10321f;
  --success-border: #1f5138;
  --shadow-rgb: 0, 0, 0;
  --overlay-weak: rgba(255, 255, 255, 0.06);
  --overlay-medium: rgba(255, 255, 255, 0.1);
  --focus-ring: rgba(126, 139, 255, 0.32);
  --list-hover: rgba(250, 248, 243, 0.06);
  --pending-border: #a68a3a;
  --pending-bg: #3a3116;
  --pending-ink: #e8d488;
  color-scheme: dark;
  --landing-band-bg: #171a3c;
  --landing-band-card-bg: #1f2350;
  --landing-band-card-border: #2e3268;
  --landing-step-border: var(--hairline);
  --tier-plus-bg: #b39c52;
  --tier-plus-ink: #1f1a0a;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --ink: #faf8f3;
    --ink-muted: rgba(250, 248, 243, 0.62);
    --hairline: rgba(250, 248, 243, 0.1);
    --surface: #1a1d45;
    --surface-alt: rgba(250, 248, 243, 0.08);
    --border-inner: rgba(250, 248, 243, 0.06);
    --canvas: #0f1330;
    --navy-hover: #1f3563;
    --text-label: rgba(250, 248, 243, 0.48);
    --text-2: rgba(250, 248, 243, 0.78);
    --muted-value: rgba(250, 248, 243, 0.34);
    --accent: #a9abf5;
    --accent-hover: #c3c4f8;
    --accent-ink: #12143a;
    --primary-bg: var(--accent-solid);
    --primary-bg-hover: var(--accent-solid-hover);
    --primary-ink: var(--ink);
    --active-bg: var(--accent);
    --active-ink: var(--accent-ink);
    --danger: #ff8783;
    --danger-bg: #34191b;
    --danger-border: #5c2b2a;
    --success: #6ee0a8;
    --success-bg: #10321f;
    --success-border: #1f5138;
    --shadow-rgb: 0, 0, 0;
    --overlay-weak: rgba(255, 255, 255, 0.06);
    --overlay-medium: rgba(255, 255, 255, 0.1);
    --focus-ring: rgba(126, 139, 255, 0.32);
    --list-hover: rgba(250, 248, 243, 0.06);
    --pending-border: #a68a3a;
    --pending-bg: #3a3116;
    --pending-ink: #e8d488;
    color-scheme: dark;
    --landing-band-bg: #171a3c;
    --landing-band-card-bg: #1f2350;
    --landing-band-card-border: #2e3268;
    --landing-step-border: var(--hairline);
    --tier-plus-bg: #b39c52;
    --tier-plus-ink: #1f1a0a;
  }
}

/* Never removed without a replacement (DESIGNGRANSKNING.md #29/#60) — the browser's own default
   focus ring differs wildly across engines and themes; this one is themed, consistent, and
   always visible. :focus-visible (not :focus) so it only shows for keyboard/assistive-tech
   navigation, not every mouse click. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* One place to name the two easing curves the login-transition already established
   (DESIGNGRANSKNING.md #31), so any future motion in the app reaches for these instead of
   picking a new curve each time. Not applied broadly yet — almost nothing in the app animates
   beyond the login transition and a couple of hover fades, which are short/subtle enough that
   plain `ease` is indistinguishable from a custom curve. */
:root {
  --ease-in: cubic-bezier(0.33, 1, 0.68, 1);
  --ease-out: cubic-bezier(0.6, 0, 0.3, 1);
}

/* A global kill switch (DESIGNGRANSKNING.md #31) — every transition/animation in the app collapses
   to effectively-instant for anyone with the OS-level "reduce motion" preference set, rather than
   relying on each individual animated component remembering to check for it. The login-transition
   and queue-spinner already had their own explicit reduced-motion handling before this existed
   (see their own @media blocks) — this is the broader safety net underneath those specific cases,
   not a replacement for them. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

body {
  margin: 0;
  font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  /* Un-set, body text inherited the browser's ~1.15-1.2 UA default — comfortable for a single
     line of UI chrome, but cramped once actual paragraphs (FAQ answers, privacy policy, auth
     subtext) run past one line. 1.55 is the same reading-comfort figure most type systems settle
     on for body copy; headings override back down to something tighter below, since a big bold
     line doesn't need the same breathing room. */
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: var(--ink);
  background: var(--canvas);
  transition: background-color 0.15s ease, color 0.15s ease;
  /* Digits keep a fixed width everywhere — amounts, dates, counters — so a column of numbers
     never jitters sideways as its content changes (DESIGNGRANSKNING.md #32). Harmless on
     non-numeric text; only affects how digit glyphs themselves are drawn. */
  font-variant-numeric: tabular-nums;
}

/* Marketing pages (Index landing + Faq/Privacy/Cookies — set via ViewData["Marketing"], see
   _Layout.cshtml) share the exact same --canvas/--surface tokens the app uses now, not a separate
   colder gradient — the whole point of Fas 1's palette work was one consistent product, and a
   visibly different "storefront" mood undid that the moment someone logged in
   (DESIGNGRANSKNING-2.md #39, #80). */
body.marketing-bg {
  min-height: 100dvh;
  background: var(--canvas);
}

.marketing-bg .card {
  box-shadow: 0 16px 40px -24px rgba(var(--shadow-rgb), 0.4);
}

/* Auth pages (login/register/forgot-reset password) vertically centre .auth-card between the
   header and footer instead of leaving it stranded in the top third with empty space below
   (DESIGNGRANSKNING-PUBLIK.md #48) — flex on body rather than a fixed min-height calc, so it
   still centres correctly regardless of exactly how tall the header/footer end up being. */
body.auth-page {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
}

body.auth-page .hero,
body.auth-page .site-footer {
  flex-shrink: 0;
}

body.auth-page main.page {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* --- Landing page — rebuilt per design_handoff_startsida/STARTSIDA-SPEC.md (2026-08-16). Same
   asymmetric-grid section rhythm and three-value vertical padding (6rem/6.5rem/6.75rem ≈ the
   spec's 96/104/108px) as the reference, adapted to this codebase's own tokens (they already map
   1:1 to the spec's — --navy is --ink, --cream is --canvas, --accent is --accent, etc.) and its
   existing system-font stack rather than loading Archivo/DM Mono from Google Fonts: this app has
   no external font dependency anywhere else (the wordmark itself is outlined to vector paths for
   the same reason, see wwwroot/img/wordmark-*.svg), and a marketing-only web-font request would
   be a new CSP/privacy surface for one page. Where the spec calls for "DM Mono", the existing
   ui-monospace numeric stack (already used for amounts/badges elsewhere) stands in instead. Each
   section breaks out of .page's max-width via the same negative-margin full-bleed technique
   .hero (the header) uses one level up, then re-applies its own 1320px inner width to match the
   rest of the site (not the spec's 1200px, since .page/.hero-inner/.site-footer-inner already
   settled on 1320px — a landing page narrower than its own header/footer would look accidental). */
.landing-section-bleed {
  width: 100vw;
  margin-left: calc(50% - 50vw);
}

.landing-inner {
  max-width: 1320px;
  margin: 0 auto;
  padding: 0 1.5rem;
}

/* --- 1. Hero — grid 1.05fr/0.95fr, copy left, product-preview card right (spec §Hero). --- */
.landing-hero {
  padding: 4rem 0 6rem;
}

.landing-hero-grid {
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: 4.5rem;
  align-items: center;
}

.landing-hero-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1.5rem;
}

.landing-eyebrow {
  font-family: "DM Mono", ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.8125rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-label);
}

.landing-eyebrow-dark {
  /* Used on the two dark bands only ("Hela poängen"/deadline band, "Pris"/cta band), which never
     flip with the theme — var(--accent) paled to a barely-there lavender in light mode against
     that fixed-dark background (3.0:1, below AA), because it's tuned for a light *surface* like
     everywhere else --accent is used. Hardcoded to the dark-theme accent value unconditionally
     instead (Peter, 2026-08-20). */
  color: #a9abf5;
}

.landing-title {
  margin: 0;
  /* Fixed 78px per spec, not a vw-scaled clamp() — Archivo 800 at full size is what gives the
     hero its scale; capping it early made it read as a generic headline (Peter, 2026-08-16). */
  font-size: 4.875rem;
  font-weight: 800;
  line-height: 0.98;
  letter-spacing: -0.04em;
  text-wrap: balance;
}

.landing-subtitle {
  margin: 0;
  max-width: 30em;
  font-size: 1.1875rem;
  line-height: 1.55;
  color: var(--text-2);
  text-wrap: pretty;
}

.landing-cta {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  padding-top: 0.25rem;
}

.landing-cta a.auth-submit, .landing-cta a.secondary-button {
  width: auto;
  min-width: 160px;
}

.landing-trust-row {
  display: flex;
  align-items: center;
  gap: 1.1rem;
  flex-wrap: wrap;
  padding-top: 0.4rem;
  font-size: 0.8125rem;
  color: var(--text-label);
}

.landing-trust-dot {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--muted-value);
}

/* --- Hero product-preview card — a static illustration of the real list view
   (DESIGNGRANSKNING-PUBLIK.md #36), reusing the actual .status-badge chips from Mina kvitton
   rather than a bespoke pill, so the one piece of "product" shown on the homepage matches what a
   new user will actually see. Flagged as illustrative (aria-hidden), not presented as a
   screenshot: swap for a real one once it exists. The offset cream shape behind it is the spec's
   only decorative flourish (§Hero) — everywhere else color carries meaning, not ornament. */
.landing-hero-preview-wrap {
  position: relative;
}

.landing-hero-preview-shape {
  position: absolute;
  inset: 1.75rem -1.25rem -1.75rem 1.25rem;
  border-radius: 20px;
  background: var(--surface-alt);
}

.landing-preview {
  position: relative;
  border: 1px solid var(--hairline);
  border-radius: 18px;
  background: var(--surface);
  box-shadow: 0 24px 60px -28px rgba(var(--shadow-rgb), 0.28);
  overflow: hidden;
}

.landing-preview-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.1rem 1.35rem;
  border-bottom: 1px solid var(--border-inner);
  font-weight: 700;
  font-size: 0.9375rem;
}

.landing-preview-header-note {
  font-family: "DM Mono", ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-label);
  font-weight: 500;
}

/* Decorative-only echo of the real countdown row (ReceiptsCountdown.cshtml's .countdown-row-*) —
   deliberately its own rule set, not a reuse, so this card never inherits the real view's layout
   changes. Only one row carries --countdown-row-urgent; the other two keep their own protection-
   type colour (warranty/statutory), same "urgency beats type, but only when it's actually urgent"
   split the real view uses — three orange rows with no type label would lose the "three different
   clocks" argument this page exists to make. */
.landing-countdown-row {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  padding: 0.9rem 1.35rem;
  border-bottom: 1px solid var(--border-inner);
}

.landing-countdown-row:last-of-type {
  border-bottom: none;
}

.landing-countdown-badge {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 9px;
  font-family: "DM Mono", ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--countdown-openpurchase);
  background: var(--countdown-openpurchase-bg);
}

.landing-countdown-row-warranty .landing-countdown-badge {
  color: var(--countdown-warranty);
  background: var(--countdown-warranty-bg);
}

.landing-countdown-row-statutory .landing-countdown-badge {
  color: var(--countdown-statutory);
  background: var(--countdown-statutory-bg);
}

.landing-countdown-row-urgent .landing-countdown-badge {
  color: var(--countdown-urgent);
  background: color-mix(in srgb, var(--countdown-urgent) 14%, var(--surface));
}

.landing-countdown-main {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  min-width: 0;
  flex: 1 1 auto;
}

.landing-countdown-merchant {
  font-weight: 600;
  font-size: 0.875rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.landing-countdown-sub {
  font-size: 0.75rem;
  color: var(--text-2);
}

.landing-countdown-progress {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.landing-countdown-bar {
  height: 5px;
  border-radius: 3px;
  background: var(--surface-alt);
  overflow: hidden;
}

.landing-countdown-bar-fill {
  display: block;
  height: 100%;
  border-radius: 3px;
  background: var(--countdown-openpurchase);
}

.landing-countdown-row-warranty .landing-countdown-bar-fill {
  background: var(--countdown-warranty);
}

.landing-countdown-row-statutory .landing-countdown-bar-fill {
  background: var(--countdown-statutory);
}

.landing-countdown-row-urgent .landing-countdown-bar-fill {
  background: var(--countdown-urgent);
}

/* Two fixed widths for three fixed decorative rows — a discrete class per value rather than an
   inline style="width: X%", since the response's CSP is style-src 'self' with no unsafe-inline
   (see Program.cs) and silently drops inline style="" attributes entirely, no console error.
   Width = share of the window still remaining (daysLeft / totalDays, "andel kvar" — not andel
   förbrukad), same semantics the real view uses — an empty bar means "almost expired", not
   "urgent". The hero previously filled these in the opposite direction (an almost-empty bar for a
   row with plenty of time left), teaching visitors the wrong reading before they ever open the
   app. 5 of 14 dagar and 8 of 24 mån both round to 35%; 2 of 3 år rounds to 65%. */
.landing-countdown-bar-fill-35 {
  width: 35%;
}

.landing-countdown-bar-fill-65 {
  width: 65%;
}

.landing-countdown-days {
  font-family: "DM Mono", ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.75rem;
  color: var(--text-2);
}

.landing-countdown-row-urgent .landing-countdown-days {
  color: var(--countdown-urgent);
}

.landing-preview-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.9rem 1.35rem;
  font-size: 0.75rem;
  color: var(--muted-value);
}

.landing-preview-footer a {
  font-weight: 600;
}

/* --- 2. Deadline band — the app's one dark content section and its strongest argument (spec
   §Deadline-sektion): three "clocks" (öppet köp / omvänd bevisbörda / reklamationsrätt), each a
   different countdown, which is the whole reason the product exists. --- */
.landing-deadline-band {
  background: var(--landing-band-bg);
  color: #faf8f3;
}

.landing-deadline-inner {
  /* padding-top/bottom, not shorthand "padding: 6rem 0" — that shorthand set left/right to 0 too,
     which silently overwrote .landing-inner's own horizontal padding on this same element (equal
     specificity, later rule in the file wins outright, CSS doesn't merge the two). Desktop hid it
     (max-width+auto-margins still centered the box), mobile didn't — content ran edge to edge with
     no margin at all (Peter, 2026-08-16). Same fix applied to every .landing-inner-combined rule
     below. */
  padding-top: 6rem;
  padding-bottom: 6rem;
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: 4.5rem;
  align-items: start;
}

.landing-deadline-copy {
  display: flex;
  flex-direction: column;
  gap: 1.35rem;
}

.landing-deadline-copy h2 {
  margin: 0;
  color: #faf8f3;
  font-size: clamp(1.75rem, 2.6vw, 2.375rem);
  line-height: 1.08;
  letter-spacing: -0.03em;
}

.landing-deadline-copy p {
  margin: 0;
  max-width: 34em;
  font-size: 1.0625rem;
  line-height: 1.6;
  color: rgba(250, 248, 243, 0.66);
}

.landing-clocks {
  display: flex;
  flex-direction: column;
  gap: 0.875rem;
}

.landing-clock {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  padding: 1.5rem 1.65rem;
  border: 1px solid var(--landing-band-card-border);
  border-radius: 15px;
  background: var(--landing-band-card-bg);
}

.landing-clock-ring {
  flex: 0 0 auto;
  width: 58px;
  height: 58px;
  border-radius: 50%;
  border: 2px solid currentColor;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: "DM Mono", ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.8125rem;
  font-weight: 500;
}

/* Decreasing ring brightness per clock (spec: #A9ABF5 / #8A8DC8 / #6B6FA8) — öppet köp reads as
   the most urgent/vivid, reklamationsrätt (the longest, least time-pressured window) the quietest,
   against the --landing-band-card-bg surface behind them. */
.landing-clock:nth-of-type(1) .landing-clock-ring {
  color: #a9abf5;
}

.landing-clock:nth-of-type(2) .landing-clock-ring {
  color: #8a8dc8;
}

.landing-clock:nth-of-type(3) .landing-clock-ring {
  color: #6b6fa8;
}

.landing-clock-copy {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  min-width: 0;
}

.landing-clock-title {
  font-size: 1.0625rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: #faf8f3;
}

.landing-clock-body {
  font-size: 0.875rem;
  line-height: 1.5;
  color: rgba(250, 248, 243, 0.66);
}

/* --- 3. Så funkar det — three step cards, big muted index number (spec §Så funkar det). --- */
.landing-steps-section {
  padding-top: 6rem;
  padding-bottom: 6rem;
}

.landing-steps-heading-row {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 2.5rem;
  margin-bottom: 2.5rem;
}

.landing-steps-heading-row h2 {
  margin: 0;
  max-width: 12em;
}

.landing-steps-tagline {
  font-size: 1rem;
  color: var(--text-label);
  max-width: 26em;
  text-align: right;
}

.landing-steps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.25rem;
}

.landing-step {
  min-width: 0;
  border: 1px solid var(--landing-step-border);
  border-top: 3px solid var(--ink);
  border-radius: 3px 3px 17px 17px;
  background: var(--surface);
  box-shadow: 0 18px 40px -30px rgba(18, 20, 58, 0.35);
  padding: 2rem 1.75rem 2.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  min-height: 250px;
}

/* A small mono eyebrow-style label, not a big pale digit — a large numeral read as decorative
   filler rather than a step marker (STARTSIDA-SPEC.md "Avvikelser" #2). */
.landing-step-number {
  font-family: "DM Mono", ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.9375rem;
  letter-spacing: 0.12em;
  font-weight: 500;
  line-height: 1;
  color: var(--accent);
}

.landing-step h3 {
  margin: 0;
  font-size: 1.375rem;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.landing-step p {
  margin: 0;
  color: var(--text-2);
  font-size: 0.9375rem;
  line-height: 1.6;
  text-wrap: pretty;
}

/* --- 3b. Presentkort — a small standalone strip, one flex row (icon + copy), not a grid of
   cards like the trust-grid below — deliberately lighter weight than the sections either side. --- */
.landing-giftcard-band {
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  background: var(--surface-alt);
}

.landing-giftcard-inner {
  padding-top: 3.25rem;
  padding-bottom: 3.25rem;
  display: flex;
  align-items: center;
  gap: 2rem;
}

.landing-giftcard-icon {
  flex: 0 0 auto;
  width: 58px;
  height: 58px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--accent);
  border: 1px solid var(--hairline);
  background: var(--surface);
}

.landing-giftcard-copy {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  max-width: 42em;
}

.landing-giftcard-copy h2 {
  margin: 0;
  font-size: clamp(1.375rem, 1.8vw, 1.625rem);
  letter-spacing: -0.02em;
}

.landing-giftcard-copy p {
  margin: 0;
  font-size: 1rem;
  line-height: 1.6;
  color: var(--text-2);
}

/* --- 4. Integritet och säkerhet — cream-alt band, lines above/below (spec §5). --- */
.landing-privacy-band {
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  background: var(--surface-alt);
}

.landing-privacy-inner {
  padding-top: 5.5rem;
  padding-bottom: 5.5rem;
  display: grid;
  grid-template-columns: 0.9fr 1.1fr;
  gap: 4.5rem;
  align-items: start;
}

.landing-privacy-copy {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
}

.landing-privacy-copy h2 {
  margin: 0;
  font-size: clamp(1.6rem, 2.3vw, 2rem);
}

.landing-privacy-copy p {
  margin: 0;
  max-width: 30em;
  font-size: 1.0625rem;
  line-height: 1.6;
  color: var(--text-2);
}

.landing-trust-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2rem;
}

.landing-trust-grid > div {
  min-width: 0;
  padding-top: 1.1rem;
  border-top: 2px solid var(--ink);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.landing-trust-grid h3 {
  font-size: 1rem;
  font-weight: 700;
  color: var(--ink);
  margin: 0;
}

.landing-trust-grid p {
  margin: 0;
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--text-2);
  max-width: none;
  text-wrap: pretty;
}

/* --- 5. Vanliga frågor — grid 0.8fr/1.2fr, accordion right (spec §6). Kept as native
   <details>/<summary> rather than a bespoke JS accordion — every other disclosure control in this
   app (overflow menus, the "Rätta uppgifter" panel) already uses the same no-JS-required pattern,
   and a one-off custom accordion just for this page would be the odd one out. --- */
.landing-faq-section {
  padding-top: 6rem;
  padding-bottom: 6rem;
  display: grid;
  grid-template-columns: 0.8fr 1.2fr;
  gap: 4.5rem;
  align-items: start;
}

/* Not a lone heading in an empty column (spec "Avvikelser" #3) — eyebrow + H2 + a line of support
   copy with a real contact address. Was position:sticky ("pinned in view as the visitor scrolls
   through the questions") — dropped: it read as broken rather than deliberate ("skrollar inte som
   övriga texter", Peter, 2026-08-16), and pinning a short three-line block while a much taller
   accordion scrolls past it isn't worth that confusion for what's a minor polish detail. */
.landing-faq-copy {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.landing-faq-section h2 {
  margin: 0;
}

.landing-faq-copy p {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--text-2);
  max-width: 26em;
}

.landing-faq-list {
  border-top: 1px solid var(--hairline);
}

.landing-faq-item {
  border-bottom: 1px solid var(--hairline);
}

.landing-faq-item summary {
  padding: 1.5rem 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  font-weight: 600;
  font-size: 1.1875rem;
  letter-spacing: -0.015em;
  color: var(--ink);
}

.landing-faq-item summary::-webkit-details-marker {
  display: none;
}

.landing-faq-sign {
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 1px solid var(--line-strong, var(--hairline));
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.0625rem;
  color: var(--text-2);
}

.landing-faq-item[open] .landing-faq-sign {
  transform: rotate(45deg);
}

.landing-faq-item p {
  margin: 0;
  padding: 0 3.5rem 1.6rem 0;
  font-size: 1rem;
  line-height: 1.65;
  color: var(--text-2);
  max-width: none;
  text-wrap: pretty;
}

/* --- 6. Pris + CTA — merged into one dark band, not two sections (spec §7 — "de ska inte vara
   två sektioner"). --- */
.landing-cta-band {
  /* Three surface levels, footer always deepest: page (--canvas) < this band (--landing-band-bg,
     theme-flipping) < footer (#08091a, fixed — see .site-footer). Was --landing-deep-bg here too
     — in dark mode that's darker than the page itself, so the band read as a hole punched in the
     page instead of a block; it also matched the footer's own background near-exactly in light
     mode (#12143a vs #0c0e24 read as one flat dark field, not two levels — Peter, 2026-08-20),
     which is why the footer's tone was pushed further down instead of the two sharing a token. */
  background: var(--landing-band-bg);
  color: #faf8f3;
}

/* A lighter band against a dark page needs an edge or it reads as a light leak — light mode
   doesn't need this (dark-on-light already has a natural edge). */
:root[data-theme="dark"] .landing-cta-band {
  border-top: 1px solid var(--landing-band-card-border);
  border-bottom: 1px solid var(--landing-band-card-border);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .landing-cta-band {
    border-top: 1px solid var(--landing-band-card-border);
    border-bottom: 1px solid var(--landing-band-card-border);
  }
}

.landing-pricing-inner {
  padding-top: 5.5rem;
  padding-bottom: 5.5rem;
}

.landing-pricing-heading {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  max-width: 38em;
  margin: 0 auto 3rem;
  text-align: center;
}

.landing-pricing-heading h2 {
  margin: 0;
  color: #faf8f3;
  font-size: clamp(1.75rem, 2.6vw, 2.375rem);
  letter-spacing: -0.03em;
}

.landing-pricing-heading p {
  margin: 0;
  font-size: 1.0625rem;
  line-height: 1.6;
  color: rgba(250, 248, 243, 0.66);
}

.landing-pricing-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1.25rem;
  align-items: stretch;
}

.landing-pricing-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1.75rem 1.5rem;
  border: 1px solid var(--landing-band-card-border);
  border-radius: 15px;
  background: var(--landing-band-card-bg);
}

/* The one self-service tier meant to catch the most eyes — same accent-bordered "this one" pattern
   a pricing table needs somewhere, applied to Rceep Plus specifically since it's the mass-market
   upsell. Free/Spaces/Custom all look identical to each other, deliberately not competing for the
   same attention. */
.landing-pricing-card-highlight {
  border-color: #a9abf5;
  background: #232766;
}

.landing-pricing-card-badge {
  position: absolute;
  top: -0.7rem;
  left: 1.5rem;
  padding: 0.25rem 0.65rem;
  border-radius: 999px;
  background: #a9abf5;
  color: #12143a;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.landing-pricing-card-name {
  font-size: 0.9375rem;
  font-weight: 700;
  color: #faf8f3;
}

.landing-pricing-card-price {
  font-size: 2rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: #faf8f3;
}

.landing-pricing-card-price-unit {
  font-size: 1rem;
  font-weight: 500;
  color: rgba(250, 248, 243, 0.66);
}

.landing-pricing-card-price-monthly {
  font-size: 0.875rem;
  font-weight: 500;
  color: rgba(250, 248, 243, 0.5);
}

.landing-pricing-card-price-billing {
  margin-top: -0.7rem;
  font-size: 0.75rem;
  color: rgba(250, 248, 243, 0.5);
}

.landing-pricing-card-price-note {
  margin-top: -0.65rem;
  font-size: 0.8125rem;
  color: rgba(250, 248, 243, 0.62);
}

.landing-pricing-card-features {
  flex: 1;
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}

.landing-pricing-card-features li {
  padding-left: 1.35rem;
  position: relative;
  font-size: 0.875rem;
  line-height: 1.45;
  color: rgba(250, 248, 243, 0.82);
}

.landing-pricing-card-features li::before {
  content: "✓";
  position: absolute;
  left: 0;
  color: #a9abf5;
  font-weight: 700;
}

.landing-pricing-card-cta {
  padding: 0.85rem 1.5rem;
  border-radius: 10px;
  background: transparent;
  border: 1px solid rgba(250, 248, 243, 0.3);
  color: #faf8f3;
  font-size: 0.9375rem;
  font-weight: 700;
  text-align: center;
}

.landing-pricing-card-cta:hover {
  background: rgba(250, 248, 243, 0.08);
  color: #faf8f3;
}

.landing-pricing-card-cta-highlight {
  background: #faf8f3;
  border-color: #faf8f3;
  color: var(--navy);
}

.landing-pricing-card-cta-highlight:hover {
  background: #ffffff;
  color: var(--navy);
}

@media (max-width: 900px) {
  .landing-hero-grid, .landing-deadline-inner, .landing-privacy-inner, .landing-faq-section {
    grid-template-columns: 1fr;
    gap: 2.5rem;
  }

  .landing-pricing-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  .landing-steps-heading-row {
    flex-direction: column;
    align-items: flex-start;
  }

  .landing-steps-tagline {
    text-align: left;
  }

  .landing-giftcard-inner {
    flex-direction: column;
    align-items: flex-start;
    gap: 1.25rem;
  }

  /* The desktop offset (-1.25rem right) was sized for the space next to the hero's second grid
     column — once the grid collapses to one column here, that same negative inset ate almost all
     of .landing-inner's own right padding, leaving the card just a few px from the viewport edge
     (Peter, 2026-08-16: "vissa delar får inte plats... saknas lite marginal"). A smaller, near-
     symmetric offset keeps the depth effect without crowding the edge. */
  .landing-hero-preview-shape {
    inset: 1rem -0.375rem -1rem 0.75rem;
  }
}

@media (max-width: 720px) {
  .landing-hero {
    padding: 2.5rem 0 3.5rem;
  }

  .landing-title {
    font-size: 2.25rem;
  }

  .landing-steps-section, .landing-faq-section, .landing-privacy-inner, .landing-deadline-inner, .landing-pricing-inner, .landing-giftcard-inner {
    padding-top: 3.5rem;
    padding-bottom: 3.5rem;
  }

  .landing-steps, .landing-trust-grid {
    grid-template-columns: 1fr;
  }

  .landing-pricing-grid {
    grid-template-columns: 1fr;
  }
}

h1, h2 {
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1.25;
  color: var(--ink);
}

/* A global reset so any plain <a> in page content (not just the specially-styled nav/footer/
   pagination links below, which all use class selectors that already beat this tag rule) reads as
   a deliberate, branded link instead of the browser's default blue-and-underlined look. */
a {
  color: var(--accent);
  text-decoration: none;
  font-weight: 600;
}

a:hover {
  text-decoration: underline;
  color: var(--accent-hover);
}

h1 {
  font-size: 1.65rem;
  margin: 0 0 1.25rem;
  text-wrap: balance;
}

/* Avoids a lone short word stranded on its own last line (an orphan) in longer paragraphs —
   DESIGNGRANSKNING-PUBLIK.md #62. Unsupported browsers just fall back to normal wrapping. */
p {
  text-wrap: pretty;
}

/* Uppercase reads fine as a short section label ("Rader", "Påminnelser") but shouts when a page
   (e.g. FAQ) uses h2 for full sentence-length questions — so this styles the label case, not the
   transform; content decides its own casing. */
h2 {
  font-size: 1rem;
  margin: 2rem 0 0.85rem;
  color: var(--ink-muted);
  letter-spacing: -0.005em;
  font-weight: 700;
}

.hero {
  position: relative;
  /* Matches the page body, not a fixed navy bar — light in light theme, dark in dark theme (see
     :root/:root[data-theme="dark"] tokens above). The wordmark and every nav control below read
     off var(--ink)/var(--ink-muted) instead of hardcoded white so they stay legible in both. */
  background: var(--canvas);
  color: var(--ink);
  border-bottom: 1px solid var(--hairline);
  /* 21px top+bottom + the 30px logo below = 72px header, the target from DESIGNGRANSKNING.md #3
     (was closer to 110px once the badge's inherited 1.55 body line-height and generous padding
     were accounted for). */
  padding: 1.3125rem 2rem;
}

/* Sticky only on the public site (Peter, 2026-09-11) — visitors reach CTA/"Logga in" without
   scrolling back up, which matters most on the long scrollable marketing/auth pages this class
   scopes to (see .page's own body.marketing-bg rule above). Left off the signed-in app header on
   purpose: that surface already spends its scroll budget on the fixed bottom-tab-bar (mobile) and
   isn't part of what prompted this. Unlike the FAQ column's reverted sticky (see
   .landing-faq-copy's comment) this is the conventional persistent-nav pattern, not a short block
   pinned beside much taller scrolling content, so it doesn't carry the same "reads as broken"
   risk — the existing --canvas background + hairline border already gives it a clean edge against
   whatever scrolls underneath, no extra scroll-shadow JS needed. */
body.marketing-bg .hero {
  position: sticky;
  top: 0;
  z-index: 30;
}

.hero-inner {
  position: relative;
  /* Above body.nav-menu-open::after's scrim (z-index: 19) — .hero-inner establishes its own
     stacking context, so without this the whole header (hamburger panel included) would render
     BENEATH the scrim regardless of .nav-menu-panel's own higher z-index, since that z-index is
     only resolved relative to this context, not the page root. */
  z-index: 20;
  max-width: 1320px;
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  /* center, not baseline — baseline let the brand's text baseline and the nav row's pill/icon
     controls (which have no natural text baseline) drift out of vertical alignment with each
     other; center keeps brand, tagline and every nav control on one shared vertical midline. */
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 2rem;
}

/* Plain layout row now, not a link itself — holds .brand-link plus the beta/env badges (see
   _Layout.cshtml). Badges moved out of the link on purpose (Peter, 2026-09-02): they used to sit
   inside it and pick up its hover underline/navigate-on-click, neither of which makes sense for a
   status label. */
.brand {
  display: inline-flex;
  align-items: center;
}

/* The actual link (to "/" — the landing page, or a signed-in user's capture page once IndexModel
   redirects them there), now scoped to just the logo images. */
.brand-link {
  display: inline-flex;
  align-items: center;
}

/* width auto keeps wordmark-white.svg's own aspect ratio, never distorts. 30px cap height is
   ~2.1x the nav text's 14px (DESIGNGRANSKNING.md #3) — big enough to read as the brand mark,
   not so big it dwarfs the controls sitting right next to it. See wordmark-white.svg itself for
   the icon/text proportions (icon height matches the wordmark's own cap height, not tied to this
   value). */
.brand-logo {
  display: block;
  height: 30px;
  width: auto;
}

/* Light theme (default): navy wordmark, matching the now-light header background. */
.brand-logo-dark {
  display: none;
}

:root[data-theme="dark"] .brand-logo-light {
  display: none;
}

:root[data-theme="dark"] .brand-logo-dark {
  display: block;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .brand-logo-light {
    display: none;
  }

  :root:not([data-theme]) .brand-logo-dark {
    display: block;
  }
}

/* Non-production environment indicator (see _Layout.cshtml) — a quiet outline pill, not a filled
   amber one. It used to be the loudest thing on the page and read as part of the brand, sitting
   right next to the logo; an outline in the same neutral border/label tokens as everything else
   in the header keeps it legible without competing with the wordmark for attention. */
.env-badge {
  display: inline-flex;
  align-items: center;
  margin-left: 0.6rem;
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid var(--hairline);
  background: transparent;
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  line-height: 1.2;
  text-transform: uppercase;
  color: var(--text-label);
  white-space: nowrap;
}

/* Slightly higher-contrast border/text than the generic --hairline/--text-label dark values —
   the badge needs to stay readable as a deliberate "not production" signal even against the
   busier dark surface, not fade to the same near-invisible border every other hairline uses. */
:root[data-theme="dark"] .env-badge {
  border-color: rgba(250, 248, 243, 0.22);
  color: #a3a091;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .env-badge {
    border-color: rgba(250, 248, 243, 0.22);
    color: #a3a091;
  }
}

/* Same quiet outline-pill treatment as .env-badge right above, but in the brand accent color
   instead of the neutral hairline/label tokens — a deliberate small distinction so it reads as
   "about the product" rather than "about the deploy target" next to env-badge, without being any
   louder. --accent already carries its own light/dark values (see :root tokens above), so no
   separate dark-mode override is needed here the way .env-badge needs one for its neutral colors. */
.beta-badge {
  display: inline-flex;
  align-items: center;
  margin-left: 0.3rem;
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid var(--accent);
  background: transparent;
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  line-height: 1.2;
  text-transform: uppercase;
  color: var(--accent);
  white-space: nowrap;
}

/* --- Space context switcher (design_handoff_spaces §5.1) ---
   Replaces the space-switching entries that used to live inside .nav-menu/.user-menu. Sits in
   .hero-inner right after the brand/env badge, separated by its own 1px divider — a permanent,
   always-visible control rather than something buried in the account menu. Renders nothing for a
   user with no spaces at all (see _SpaceContextSwitcher.cshtml's own early return). */
.space-switcher-divider {
  width: 1px;
  height: 22px;
  background: var(--hairline);
  margin: 0 0.2rem;
  flex-shrink: 0;
}

.space-switcher {
  position: relative;
  /* Same Chromium <details> quirk as .user-menu below (see its comment) — pin the height so
     opening the panel (position:absolute, out of flow) doesn't grow the whole header. 38px is
     the trigger's own 24px avatar plus its 6px top/bottom padding plus 1px border each side
     (Peter, 2026-08-25: this partial was added after that fix and never got it).
     Deliberately NOT display:flex + align-items:center the way .user-menu does it below — with
     this specific element, adding those two properties on top of the fixed height introduced a
     SECOND bug: opening the panel then shifted the trigger itself upward by 6px (confirmed via
     isolated testing — flex + a fixed height on a <details> containing a position:absolute panel
     makes Chromium re-run cross-axis alignment against the panel's unclamped content height while
     [open], even though the box's own rendered height stays correctly pinned at 38px). Plain block
     flow leaves the summary sitting at the top of the box regardless of [open], which needs no
     centering here anyway — 38px is only 0.67px taller than the trigger's own natural height. */
  height: 38px;
}

.space-switcher-trigger {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 6px 12px 6px 8px;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 999px;
  box-shadow: 0 1px 2px rgba(var(--shadow-rgb), 0.05);
  cursor: pointer;
  list-style: none;
  user-select: none;
}

.space-switcher-trigger::-webkit-details-marker {
  display: none;
}

.space-switcher-trigger svg {
  color: var(--text-label);
  flex-shrink: 0;
}

/* Shared avatar chip — color + initials, used everywhere a space is referenced (switcher, quick-
   jump, /Spaces, space detail, upload target picker). Radius ~25% of the box per the doc's own
   "Radie ≈ 25% av sidan" rule, not a fixed px value, so it scales consistently across the 24/30/
   32/36/46px sizes those views use. */
.space-avatar {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  border-radius: 25%;
  color: #fff;
  font-weight: 700;
  line-height: 1;
}

.space-avatar-sm {
  width: 24px;
  height: 24px;
  font-size: 10px;
}

.space-avatar-row {
  width: 30px;
  height: 30px;
  font-size: 11px;
}

/* border-radius is already percentage-based on .space-avatar itself, so these three size
   variants only need to set width/height/font-size — no per-size radius override needed. */
.space-avatar-md {
  width: 36px;
  height: 36px;
  font-size: 12.5px;
}

.space-avatar-lg {
  width: 46px;
  height: 46px;
  font-size: 15px;
}

.space-avatar-preview {
  width: 38px;
  height: 38px;
  font-size: 12.5px;
}

.space-switcher-name {
  font-weight: 600;
  font-size: 0.84375rem;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 160px;
}

.space-switcher-count {
  font-size: 0.6875rem;
  font-weight: 500;
  color: var(--text-label);
}

.space-switcher-panel {
  position: absolute;
  top: calc(100% + 0.5rem);
  left: 0;
  width: 400px;
  max-width: 90vw;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 14px;
  box-shadow: 0 18px 44px rgba(var(--shadow-rgb), 0.18);
  z-index: 25;
  overflow: hidden;
}

.space-switcher-search {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 12px 14px 8px;
  padding: 8px 11px;
  background: var(--canvas);
  border-radius: 9px;
  color: var(--text-label);
}

.space-switcher-search svg {
  flex-shrink: 0;
}

.space-switcher-search input {
  flex: 1;
  border: none;
  background: none;
  font-size: 0.78125rem;
  color: var(--ink);
  outline: none;
}

.space-switcher-group {
  padding: 4px 0;
}

.space-switcher-eyebrow {
  padding: 8px 14px 4px;
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.59375rem;
  font-weight: 500;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--text-label);
}

.space-switcher-row {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 8px 14px;
  color: inherit;
}

.space-switcher-row:hover {
  background: var(--list-hover);
  text-decoration: none;
  color: inherit;
}

.space-switcher-row.current {
  background: var(--list-hover);
}

.space-switcher-row-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.space-switcher-row-name {
  font-weight: 600;
  font-size: 0.8125rem;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.space-switcher-row-sub {
  font-size: 0.6875rem;
  color: var(--text-label);
}

.space-switcher-row-count {
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.6875rem;
  font-weight: 500;
  color: var(--text-label);
}

.space-switcher-check {
  color: var(--accent);
  font-weight: 700;
  font-size: 0.75rem;
}

.space-switcher-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 11px 14px;
  border-top: 1px solid var(--hairline);
  background: var(--canvas);
  font-size: 0.71875rem;
  color: var(--ink-muted);
}

.space-switcher-footer-actions {
  display: flex;
  gap: 10px;
}

.space-switcher-footer a {
  font-weight: 600;
  color: var(--ink);
}

.space-switcher-footer-create {
  color: var(--accent) !important;
}

/* Mobile equivalent of the topbar pill (design_handoff_spaces §5.8) — same partial rendered a
   second time in .page instead of .hero, hidden here and shown only under the mobile media query
   below (which also hides the desktop .space-switcher pill instead). */
.space-switcher-mobile-row {
  display: none;
}

.bottom-tab-bar {
  display: none;
}

/* Upload page's swap-in for the context switcher (design_handoff_spaces §5.5) — "man navigerar
   inte här, man väljer mål". */
.site-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem 1.35rem;
  /* .hero-inner's justify-content: space-between only has two flex items (.brand, .site-nav) to
     distribute — fine as long as both fit on one line. Below ~720px the nav's own content (page
     links + theme + language + account, before the hamburger collapse kicks in at 640px) is wide
     enough to wrap .site-nav onto its own line under the brand, where it's the sole item on that
     line — and per the flexbox spec, space-between with a single item degrades to flex-start, so
     it reads as stuck to the left instead of right (same root cause the .site-footer-inner mobile
     fix addresses for the footer, see below). margin-left: auto is a more robust fix than a
     breakpoint-specific override: an auto margin always consumes the line's leftover space, so
     .site-nav stays flush right whether it's sharing the line with .brand or wrapped alone. */
  margin-left: auto;
}

.site-nav a {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  color: var(--ink-muted);
  text-decoration: none;
  font-weight: 500;
  font-size: 0.875rem;
  /* 0.25rem, not 0.4rem — at 0.4rem this was the tallest thing in the header row (~34.5px vs the
     30px every other control was brought down to, see .nav-menu/.lang-menu/.theme-menu/.user-menu's
     comment), which silently reopened the same header-height bug through a different element. */
  padding: 0.25rem 0.7rem;
  border-radius: 6px;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.site-nav a svg {
  flex-shrink: 0;
  opacity: 0.85;
}

.site-nav a:hover {
  color: var(--ink);
  background: var(--overlay-weak);
  text-decoration: none;
}

/* A plain text link, not a filled pill — the landing page's hero already has the real call to
   action ("Skapa konto", filled). A second filled CTA up here competed with it and, on the
   landing page itself, drew the eye before the page even said what the product does
   (DESIGNGRANSKNING-PUBLIK.md #41). */
.site-nav a.login-link {
  color: var(--text-2);
  font-weight: 600;
}

.site-nav a.login-link:hover {
  background: var(--overlay-weak);
  color: var(--ink);
}

/* A plain <form>/<button> inside the nav needs to look like the surrounding links, not like a
   default HTML button. */
.site-nav .nav-form {
  margin: 0;
}

/* Account menu — a details/summary dropdown so "who am I logged in as, and where's account
   settings" works without any JS, matching the no-JS-by-default approach the rest of the nav
   already takes (langswitch, logout form). */
.user-menu {
  position: relative;
  /* Chromium gives a <details> element's own content box a few extra px whenever [open] is set,
     even though the dropdown panel is position:absolute and shouldn't need any of that room —
     without a pinned height here, opening the menu visibly grows the whole navy header by that
     amount. Fixed height stops that; the panel still overlays fine since it's out of flow.
     30px, matching .avatar exactly (was 2.5rem/40px — see .nav-menu/.lang-menu/.theme-menu's own
     comment for why that was the actual cause of the header-height complaint). */
  height: 1.875rem;
  /* <details> lays its (in-flow) summary out top-aligned by default, not centered within this
     fixed height — display:flex here centers it instead. The dropdown panel is position:absolute
     so it's already out of flow and unaffected by becoming a flex item too. */
  display: flex;
  align-items: center;
}

.user-menu summary {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  list-style: none;
  cursor: pointer;
  /* No padding — the 30px .avatar circle is already the full visual target; padding here pushed
     this summary past .user-menu's own 30px box, the same tallest-child bug as the other header
     triggers (see .nav-menu/.lang-menu/.theme-menu's comment). */
  padding: 0;
  margin: 0;
  border-radius: 999px;
  transition: background-color 0.15s ease;
}

.user-menu summary::-webkit-details-marker {
  display: none;
}

.user-menu summary::after {
  content: "";
  width: 0;
  height: 0;
  flex-shrink: 0;
  margin-right: 0.15rem;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid currentColor;
  color: var(--ink-muted);
}

/* Solid navy regardless of theme (--navy has no dark-theme override — same stable-across-themes
   reasoning as .receipts-add-button) rather than a translucent-white fill, which relied on
   sitting on top of the header's old fixed navy background and would go invisible now that the
   header itself can be light. */
.avatar {
  width: 30px;
  height: 30px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--navy);
  color: #ffffff;
  font-weight: 700;
  font-size: 0.8rem;
  line-height: 1;
  overflow: hidden;
}

.avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.user-menu:hover summary, .user-menu[open] summary {
  background: var(--overlay-weak);
  /* The generic `details[open] summary { margin-bottom }` rule further down (written for the
     collapsible Rader/category sections) matches every <details>/<summary> on the page, including
     this one, at equal CSS specificity — since it comes later in the file it would otherwise win
     and visibly bump the avatar up a few px each time this menu opens. Needs the [open] compound
     selector here (not just a plain `margin: 0` on the base rule above) to out-specificity it. */
  margin-bottom: 0;
}

.user-menu-email {
  padding: 0.3rem 0.7rem 0.6rem;
  margin-bottom: 0.2rem;
  border-bottom: 1px solid var(--hairline);
  color: var(--ink-muted);
  font-size: 0.78rem;
  word-break: break-all;
}

.user-menu-panel {
  position: absolute;
  right: 0;
  top: calc(100% + 0.4rem);
  min-width: 210px;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(var(--shadow-rgb), 0.18);
  padding: 0.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  z-index: 20;
}

.user-menu-panel a, .user-menu-panel .link-button,
.nav-menu-panel .link-button {
  color: var(--ink);
  text-align: left;
  padding: 0.55rem 0.7rem;
  border-radius: 6px;
  font-weight: 500;
  font-size: 0.875rem;
  width: 100%;
  display: block;
}

.user-menu-panel a:hover, .user-menu-panel .link-button:hover,
.nav-menu-panel .link-button:hover {
  background: var(--canvas);
  text-decoration: none;
  color: var(--ink);
}

.user-menu-panel .nav-form, .nav-menu-panel .nav-form {
  margin: 0;
}

.nav-menu-divider {
  height: 1px;
  background: var(--hairline);
  margin: 0.35rem 0.2rem;
}

/* The space switcher's active entry (Personligt or one specific Space) — bold + a checkmark, so
   the menu doubles as a "where am I" indicator without needing to open Mina kvitton first. */
.nav-menu-current {
  font-weight: 700;
}

/* Labels a Viewer's own entry (e.g. a revisor invited read-only into a client's space) — muted,
   not a full nav link colour, so it reads as a qualifier rather than competing with the space name. */
.nav-menu-role-tag {
  color: var(--ink-muted);
  font-weight: 400;
  font-size: 0.85em;
}

.nav-links-inline {
  display: flex;
  align-items: center;
  gap: 1.75rem;
  margin-right: 0.5rem;
}

.nav-links-inline a {
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--text-2);
}

.nav-links-inline a:hover {
  color: var(--ink);
}

@media (max-width: 640px) {
  .nav-links-inline {
    display: none;
  }
}

/* Hamburger nav (page links) and language dropdown share the account menu's shape/sizing —
   position:relative + a pinned height (same Chromium <details> quirk as .user-menu above), a
   pill summary, and a white overlay panel — so all three dropdown triggers in the header read as
   one consistent control instead of three different widgets.
   30px, matching .brand-logo/.avatar — this is the actual fix for the header-too-tall complaint
   raised three times (DESIGNGRANSKNING.md #3, DESIGNGRANSKNING-PUBLIK.md #59, DESIGNGRANSKNING-2.md
   #65): the logo's own CSS was already correct (30px) every time, but .hero's flex row height is
   set by its TALLEST child, and these triggers were 40px — 10px taller than the logo — which is
   what actually dragged the header to ~82px regardless of the logo number. 21px padding + 30px
   tallest-child = 72px, for real this time (verified via getBoundingClientRect, not just the CSS
   value, 2026-08-16). */
.nav-menu, .lang-menu, .theme-menu {
  position: relative;
  height: 1.875rem;
  /* Same reasoning as .user-menu above — centers the summary within the fixed-height box instead
     of leaving it top-aligned. */
  display: flex;
  align-items: center;
}

.nav-menu {
  display: none;
}

.theme-menu {
  order: 2;
}

.lang-menu {
  order: 3;
}

.user-menu, .login-link {
  order: 4;
}

.nav-menu {
  order: 5;
}

.nav-menu summary, .lang-menu summary, .theme-menu summary {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  list-style: none;
  cursor: pointer;
  color: var(--ink-muted);
  margin: 0;
  border-radius: 999px;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.nav-menu summary, .theme-menu summary {
  width: 1.875rem;
  height: 1.875rem;
  justify-content: center;
  border-radius: 8px;
}

/* Same font-size/weight as the regular nav links (.site-nav a) — this used to be smaller, bolder
   and letter-spaced for no functional reason, which just read as an inconsistent second design
   system living in the same header. Explicit height (not just vertical padding) matches the
   fixed square the theme toggle right next to it uses, so the two stay visually aligned
   (Peter, 2026-08-16) — both were 2.5rem/40px until a later pass (also 2026-08-16) found that
   40px, not the logo, was what was actually dragging the whole header past its 72px target
   (DESIGNGRANSKNING.md #3/DESIGNGRANSKNING-PUBLIK.md #59/DESIGNGRANSKNING-2.md #65); 1.875rem/30px
   keeps this fix and that one both true at once. */
.lang-menu summary {
  height: 1.875rem;
  padding: 0.3rem 0.55rem;
  font-size: 0.875rem;
  font-weight: 500;
}

/* Mono/uppercase, same family as .stat-label and every other small caption token in the app —
   the trigger reads as a code, not a word, at every width now (DESIGNGRANSKNING.md #30/#61). */
.lang-name-code {
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  letter-spacing: 0.04em;
}

.nav-menu summary::-webkit-details-marker, .lang-menu summary::-webkit-details-marker,
.theme-menu summary::-webkit-details-marker {
  display: none;
}

.nav-menu:hover summary, .nav-menu[open] summary,
.lang-menu:hover summary, .lang-menu[open] summary,
.theme-menu:hover summary, .theme-menu[open] summary {
  color: var(--ink);
  background: var(--overlay-weak);
  /* See .user-menu[open] summary above — same generic `details[open] summary { margin-bottom }`
     rule leaks in here too and needs the same specificity-matched override. */
  margin-bottom: 0;
}

.nav-menu-panel, .lang-menu-panel, .theme-menu-panel {
  position: absolute;
  top: calc(100% + 0.4rem);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(var(--shadow-rgb), 0.18);
  padding: 0.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  z-index: 20;
}

.nav-menu-panel {
  /* Right-anchored, not left — the hamburger sits at the far right of the header (rightmost of
     all nav controls), so a panel growing left from its right edge is what stays on-screen;
     growing right from its left edge would run straight off the viewport. */
  right: 0;
  min-width: 220px;
  /* Slightly rounder than the other header dropdowns — matches the card family radius (see
     .stat-card etc.), since at mobile widths this panel is the page's primary navigation, not a
     small icon popover (DESIGNGRANSKNING-2.md #69). */
  border-radius: 13px;
  z-index: 25;
}

/* Dimming scrim behind the hamburger menu — added by header-menus.js as a body class rather than
   a dedicated element, since <details> has no native ::backdrop the way <dialog> does
   (DESIGNGRANSKNING-2.md #69). Sits above page content, below the panel itself (z-index 25). */
body.nav-menu-open::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 19;
  background: rgba(15, 19, 48, 0.55);
}

.lang-menu-panel, .theme-menu-panel {
  right: 0;
  min-width: 150px;
}

.nav-menu-panel a, .lang-menu-panel a, .theme-menu-panel button {
  color: var(--ink);
  padding: 0.55rem 0.7rem;
  border-radius: 6px;
  font-weight: 500;
  font-size: 0.875rem;
}

.nav-menu-panel a, .lang-menu-panel a {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.nav-menu-panel a svg {
  flex-shrink: 0;
  color: var(--ink-muted);
}

.nav-menu-panel a:hover, .lang-menu-panel a:hover, .theme-menu-panel button:hover {
  background: var(--canvas);
  text-decoration: none;
  color: var(--ink);
}

.lang-menu-panel a.active, .theme-menu-panel button.active {
  color: var(--accent);
  font-weight: 700;
  background: var(--canvas);
}

/* The Free-tier upsell in the account menu (avatar + hamburger) — same accent treatment as the
   "active" language/theme choice above, borrowed for the same reason: it's the one row in an
   otherwise neutral list that should draw the eye. */
.user-menu-panel .upgrade-link, .nav-menu-panel .upgrade-link {
  color: var(--accent);
  font-weight: 700;
}

/* .theme-menu-panel button reuses .nav-menu-panel a's shape but is a real <button>, not a link —
   undo the global button tag rule's own background/color/min-height/full-width defaults so it
   reads identically to the <a> options next to it (the language picker). */
.theme-menu-panel button {
  background: none;
  border: none;
  min-height: 0;
  width: 100%;
  text-align: left;
  cursor: pointer;
}

.theme-menu summary svg {
  display: none;
}

.theme-menu:not([data-current="dark"]):not([data-current="auto"]) summary .icon-theme-light,
.theme-menu[data-current="dark"] summary .icon-theme-dark,
.theme-menu[data-current="auto"] summary .icon-theme-auto {
  display: block;
}

.link-button {
  background: none;
  border: none;
  padding: 0.4rem 0.7rem;
  border-radius: 6px;
  color: var(--ink-muted);
  font: inherit;
  font-weight: 500;
  font-size: 0.875rem;
  cursor: pointer;
  text-decoration: none;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.link-button:hover {
  color: var(--ink);
  background: var(--overlay-weak);
  filter: none;
}

.page {
  max-width: 1320px;
  margin: 2rem auto 3rem;
  padding: 0 1.5rem;
}

/* On marketing/auth pages .page is immediately followed by .site-footer — its own 3rem bottom
   margin left a strip of --canvas showing through between the last section and the footer,
   breaking the "footer is always the deepest surface, no gap" rule (Claude Design, 2026-08-19).
   Not an issue on app pages, since the footer isn't rendered there at all. */
body.marketing-bg .page {
  margin-bottom: 0;
}

.site-footer {
  /* The footer is the one surface that doesn't flip with the theme: it's always the page's
     deepest level (page < .landing-cta-band < footer — see .landing-cta-band's own comment). Its
     --footer-* tokens are defined once at :root (not here, not per [data-theme] block) since
     that's the whole point of them being fixed literals in the first place — see :root's own
     comment. Deliberately a plain hex, not var(--landing-band-bg) or any other themed token: it
     needs to read as visibly deeper than the band above it in BOTH themes, not just coincidentally
     different (Peter, 2026-08-20 — #0c0e24 vs the band's #12143a in light mode was close enough to
     read as one flat dark field). Exact value sampled from the actual Claude Design reference
     (korrekt_footer.png) — #08091a, this rule's first attempt, was close but read as too close to
     plain black instead of a deep navy. */
  background: #0b0d22;
  color: var(--footer-ink-muted);
  border-top: 1px solid var(--footer-hairline);
  padding: 4.5rem 1.5rem 2rem;
  margin-top: 0;
}

.site-footer-columns {
  max-width: 1320px;
  margin: 0 auto 2rem;
  display: grid;
  grid-template-columns: 1.5fr repeat(3, 1fr);
  gap: 1.5rem;
}

.site-footer-column {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.site-footer-column a {
  color: var(--footer-ink-muted);
  font-size: 0.85rem;
  text-decoration: none;
}

.site-footer-column a:hover {
  color: var(--footer-ink);
  text-decoration: underline;
}

.site-footer-heading {
  margin: 0 0 0.2rem;
  color: var(--footer-ink-faint);
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* Full contrast, not muted — on the footer's own navy this is the page's last brand impression,
   not a "logo also happens to be here" afterthought (the old muted --hairline treatment was
   specifically for standing out less against a light background, which no longer applies once
   the footer is always dark — Claude Design, 2026-08-19). */
.site-footer-brand {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}

/* NOT the header's own 30px .brand-logo size — measured directly against korrekt_footer.png
   (bar-height vs. the "PRODUKT" column heading's cap-height, ratio ~2.1:1 in the reference) rather
   than assumed equal to the header, since the footer sits in a denser, smaller-type context than
   the header does. 30px read as oversized there (Peter, 2026-08-20, second pass after the
   icon+text version read as too small). */
.site-footer-brand-logo {
  display: block;
  height: 24px;
  width: auto;
}

/* Product-description line filling the brand column, which otherwise held only the wordmark
   while the three link columns beside it were full (Claude Design, 2026-08-19). Reuses the
   product's own existing one-sentence description — not new copy to approve. */
.site-footer-tagline {
  margin: 0.75rem 0 0;
  max-width: 22em;
  color: var(--footer-ink-faint);
  font-size: 0.85rem;
  line-height: 1.6;
  text-wrap: pretty;
}

.site-footer-inner {
  max-width: 1320px;
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.5rem 1.5rem;
  padding-top: 2rem;
  border-top: 1px solid var(--footer-hairline);
  color: var(--footer-ink-faint);
  font-size: 0.8rem;
}

.site-footer-inner a {
  color: inherit;
}

.footer-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
}

.footer-nav a {
  color: var(--footer-ink-muted);
  text-decoration: none;
}

.footer-nav a:hover {
  color: var(--footer-ink);
  text-decoration: underline;
}


.card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 12px;
  padding: 1.75rem;
}

/* Billing/Upgrade.cshtml's Free/Rceep Plus/Rceep Spaces comparison — plain .card children, so this
   only supplies the grid + equal-height layout, not a competing card style. align-items: stretch
   (the grid default, restated here so it's not silently relying on nobody overriding it) makes
   every card in a row match the tallest one's height (Peter, 2026-08-28: "rutorna lika höga i
   dator-läge") — combined with .plan-grid .card being a column flexbox and .plan-card-footer's
   margin-top: auto below, so it's the trailing badge/button that absorbs the extra space, not a
   stretched gap in the middle of the card. */
.plan-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1.25rem;
  align-items: stretch;
}

.plan-grid .card {
  display: flex;
  flex-direction: column;
}

.plan-card-footer {
  margin-top: auto;
  padding-top: 0.75rem;
}

.plan-price {
  margin: 0.5rem 0 0;
  font-size: 1.75rem;
  font-weight: 800;
  letter-spacing: -0.02em;
}

.plan-price-unit {
  font-size: 1rem;
  font-weight: 500;
  color: var(--text-label);
}

.plan-features {
  margin: 0.75rem 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.plan-features li {
  padding-left: 1.35rem;
  position: relative;
  font-size: 0.9rem;
  color: var(--text-2);
}

.plan-features li::before {
  content: "✓";
  position: absolute;
  left: 0;
  color: var(--accent);
  font-weight: 700;
}

/* Same selected-state treatment .stat-card[aria-pressed="true"] already uses elsewhere — "this is
   what you have" reads consistently across the app, not just here (Peter, 2026-08-27). */
.plan-card-current {
  border-color: var(--accent);
  background: var(--surface-alt);
}

/* One consistent "you already have this" signal, reused across Free/Plus/Spaces instead of each
   card inventing its own phrasing/placement — that mismatch (a plain hint-text line on one card,
   a full sentence on another) was the actual cause of the cards feeling unrelated to each other. */
.plan-current-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin-bottom: 0.75rem;
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
  background: var(--success-bg);
  color: var(--success);
  font-size: 0.8125rem;
  font-weight: 600;
}

.plan-current-badge::before {
  content: "✓";
}

@media (max-width: 640px) {
  .plan-grid {
    grid-template-columns: 1fr;
  }
}

/* Long-form reading pages (FAQ, Privacy, Cookies) — the site-wide h2 is deliberately a small
   muted label for compact UI section dividers ("Säkerhet", "Rader"), which reads as
   under-emphasized/decorative for what are actually the main scannable headings of a text-heavy
   page (an FAQ question, a policy section). This scopes a proper sub-heading treatment — real
   ink colour, a size that actually outranks body text, a touch more breathing room — to just
   these pages instead of changing h2 everywhere else it's already right for. */
/* The card used to span the page's full (now 1320px) width with only its 68ch paragraphs
   actually filling the left half — the right half sat empty for the page's whole height, which
   read as a layout bug rather than a choice (DESIGNGRANSKNING-PUBLIK.md #54). Capping the card
   itself to a reading-width column is option A from that review: simple, and correct for a
   handful of legal/FAQ pages that don't need a sidebar. */
.content-page {
  max-width: 760px;
  margin-left: auto;
  margin-right: auto;
}

.content-page h2 {
  font-size: 1.15rem;
  margin: 2rem 0 0.6rem;
  color: var(--ink);
  letter-spacing: -0.01em;
}

.content-page h2:first-of-type {
  margin-top: 1.5rem;
}

.content-page p {
  margin: 0 0 1rem;
  max-width: 68ch;
}

/* The cookie table's own cell/header look already comes from the shared .page table rule below
   (border, padding, uppercase th) — checked live before touching this, it already wasn't unstyled.
   What actually was missing: a horizontal-scroll container so the three-column table doesn't force
   the card wider than the viewport at 390px. */
.content-page-table-scroll {
  overflow-x: auto;
  /* Otherwise a paragraph right after the table (e.g. SupportedCountries.cshtml's closing
     "öppet köp"-explainer) sits flush against its bottom border — table/.page table td have no
     margin of their own, so this scroll wrapper is the one place to add breathing room without
     a one-off class on every page that happens to end a table this way. */
  margin-bottom: 1.5rem;
}

/* Header labels never wrap (Peter, 2026-09-08: forced multi-line wrapping — e.g. a translated
   header like Italian "Inversione dell'onere della prova" breaking across 3-4 lines — "isn't
   quite good", prefers letting the table scroll horizontally instead). Left unset on td: the
   Finland/Netherlands InformationalNote is a genuine full-sentence paragraph and should still wrap
   like normal prose rather than force one absurdly long line — only the short header labels and
   numeric cells ever need this. */
.content-page-table-scroll th {
  white-space: nowrap;
}

/* Icon-only external-source link (SupportedCountries.cshtml's table) — same muted-until-hover
   treatment as other secondary affordances, just without a text label; aria-label/title on the
   anchor itself carry the accessible name and hover tooltip. */
.source-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-muted);
}

.source-link:hover {
  color: var(--ink);
}

/* Vanliga frågor page — a real accordion, first question open, instead of six-plus walls of text
   shown all at once (DESIGNGRANSKNING-PUBLIK.md #55). <details>/<summary> keeps page search
   (Ctrl+F) and printing working with no extra JS beyond faq.js's anchor-opens-the-answer bit. */
.faq-list {
  margin-top: 1rem;
}

.faq-item {
  border-bottom: 1px solid var(--border-inner);
}

.faq-item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.125rem 0;
  font-size: 1rem;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  list-style: none;
}

.faq-item summary::-webkit-details-marker {
  display: none;
}

.faq-item summary::after {
  content: "+";
  flex-shrink: 0;
  width: 1.2rem;
  color: var(--ink-muted);
  font-weight: 400;
  font-size: 1.2rem;
  text-align: center;
}

.faq-item[open] summary::after {
  content: "\2212";
}

.faq-item p {
  margin: 0 0 1.125rem;
  max-width: 68ch;
  color: var(--ink-muted);
}

.auth-card {
  max-width: 400px;
  margin: 2.5rem auto;
  /* Own shadow, not inherited from .marketing-bg .card — Account pages no longer set
     ViewData["Marketing"] (Peter, 2026-08-19: they're app.rceep.ai pages, not marketing chrome),
     so this can't ride on that rule anymore without losing the card's lift against the page. */
  box-shadow: 0 16px 40px -24px rgba(var(--shadow-rgb), 0.4);
}

/* A whisper, not a shout — four kinds of emphasis at once (bold, uppercase, indigo, letter-
   spaced) on a line that's meant to sit quietly under the real heading was too loud
   (DESIGNGRANSKNING-PUBLIK.md #46). Mono, same family as every other small caption token in the
   app, ordinary weight. */
.auth-eyebrow {
  margin: 0 0 0.5rem;
  color: var(--text-label);
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.6875rem;
  font-weight: 400;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}

.auth-card h1 {
  margin-bottom: 0.35rem;
}

.auth-subtext {
  margin: 0 0 1.5rem;
  color: var(--ink-muted);
  font-size: 0.9rem;
}

/* Third-party sign-in gets its own pill shape (a near-universal convention for OAuth buttons)
   so it reads as "connect to an outside account" rather than the app's own primary action. */
.oauth-button {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.65rem;
  width: 100%;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--hairline);
  border-radius: 999px;
  padding: 0.6rem 1.2rem;
  margin-bottom: 0.6rem;
}

.oauth-button:hover {
  background: var(--canvas);
  filter: none;
}

.oauth-button svg {
  flex-shrink: 0;
}

.auth-divider {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 1.4rem 0;
  color: var(--ink-muted);
  font-size: 0.78rem;
}

.auth-divider::before, .auth-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--hairline);
}

.password-field {
  position: relative;
}

.password-field input {
  padding-right: 2.75rem;
}

.toggle-password {
  position: absolute;
  right: 0.35rem;
  top: 50%;
  transform: translateY(-50%);
  background: none;
  border: none;
  min-height: 0;
  padding: 0.4rem;
  color: var(--ink-muted);
}

.toggle-password:hover {
  color: var(--ink);
  background: none;
  filter: none;
}

/* The icon mirrors the field's current state (open eye = visible, crossed eye = hidden), not the
   action the click performs — swapped from an earlier "shows what the click does" convention
   after live testing on Login/Register showed the icon reading backwards in practice (Peter,
   2026-08-16): the crossed eye was appearing while the password was actually showing. */
.toggle-password .icon-eye {
  display: none;
}

.toggle-password[aria-pressed="true"] .icon-eye {
  display: block;
}

.toggle-password[aria-pressed="true"] .icon-eye-off {
  display: none;
}

.auth-switch {
  margin: 1.5rem 0 0;
  text-align: center;
  color: var(--ink-muted);
  font-size: 0.88rem;
}

.auth-legal-note {
  margin: 0.75rem 0 0;
  text-align: center;
  color: var(--ink-muted);
  font-size: 0.8125rem;
}

/* Full width so the primary action reads as one bar in line with the inputs/OAuth button above it,
   instead of a small button stranded on the left. */
.auth-submit {
  display: flex;
  width: 100%;
  gap: 0.55rem;
}

/* Submitting state — disabled + spinner + a "Loggar in …"-style label swapped in by auth.js, so a
   slow request reads as in-progress rather than a form that silently did nothing
   (DESIGNGRANSKNING-PUBLIK.md #51). */
.btn-spinner {
  display: none;
  width: 15px;
  height: 15px;
  border: 2px solid rgba(255, 255, 255, 0.4);
  border-top-color: #fff;
  border-radius: 50%;
  animation: auth-spin 0.6s linear infinite;
}

.auth-submit.is-loading {
  pointer-events: none;
  opacity: 0.85;
}

.auth-submit.is-loading .btn-spinner {
  display: inline-block;
}

@keyframes auth-spin {
  to {
    transform: rotate(360deg);
  }
}

.account-card {
  max-width: 460px;
}

.account-email {
  color: var(--ink-muted);
  margin: -0.75rem 0 0;
  font-size: 0.95rem;
}

/* Groups a related set of controls (a heading + description + action) into its own bordered box —
   used on the account overview page so "Security" / "My data" / "Delete account" read as three
   distinct settings panels instead of a flat stack of headings and links, and on the receipt
   detail page for the same reason (Kategori, Rader, Påminnelser, Dela kvitto). */
.settings-section {
  /* A tint distinct from the white card behind it (--canvas, not --surface) is what actually
     separates these into visible panels — a border alone read as barely-there hairlines on the
     same white background it was drawn on. */
  background: var(--canvas);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  padding: 1.1rem;
  margin-top: 1.25rem;
}

/* Without this, panels ending in a <p> (e.g. Kategori's hint text) had visibly more bottom
   breathing room than panels ending in a <table>/<form>/<details> — the paragraph's own default
   browser margin doesn't collapse away against the panel's own bottom padding, so it just added
   on top, making otherwise-identical-looking panels sit at two different heights.
   :not(dialog) matters (Peter, 2026-09-07): a <dialog> opened via showModal() gets the same
   `open` attribute a <details> does, but it's `position: fixed` and centered via the browser's
   own `margin: auto` — this rule's `margin-bottom: 0` was overriding just that one side of the
   auto-centering (the other three sides stayed auto), so any confirm dialog that happened to be
   the last element in its .settings-section (e.g. "Exportera mina data", "Radera alla mina
   kvitton") rendered pinned near the bottom of the viewport instead of centered. A dialog is
   never in normal document flow while open, so it was never meant to be covered by this
   trailing-margin cleanup in the first place. */
.settings-section > *:not(dialog):last-child {
  margin-bottom: 0;
}

/* Direct child ">" matters on the receipt detail page: the Rader section nests its own h2 three
   levels down inside <details><summary>, and that heading needs to stay .details-summary-heading's
   plain inline text (flowing next to the disclosure triangle), not turn into an icon+flex row —
   a descendant selector here would incorrectly reach that nested h2 too. */
.settings-section > h2 {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin-top: 0;
}

.settings-section-icon {
  flex-shrink: 0;
}

.danger-zone {
  border-color: var(--danger-border);
}

.danger-zone h2 {
  color: var(--danger);
}

/* Shape (padding/radius/weight/etc.) is shared by class OR tag so a <button> and an
   <a class="secondary-button"> (e.g. "Sök" next to "Rensa") always match — without the class
   selectors here, an <a class="secondary-button"> matched none of this and rendered with no
   padding/radius at all, which is exactly why "Sök" and "Rensa" used to look inconsistent.
   Colour is kept in separate, class-based rules below so a class always wins over the tag-only
   default regardless of selector order (a class selector's specificity beats a bare-tag one).
   Same shape as .receipts-add-button now too (font-size/padding/radius) — used to be its own,
   larger shape (0.9rem/0.6rem 1.2rem/8px/44px min-height), which is exactly why "Lägg till kvitto"
   looked like a visibly different kind of button next to every other primary action (Peter,
   2026-08-29: "Exportera och Lägg till kvitto har inte samma typsnitt" — same font, genuinely
   different size). No min-height override any more either — .receipts-add-button never had one
   and its own padding already lands at a comparable ~40px, so adding one back here would just
   reintroduce a mismatch. */
button, input[type="submit"], .secondary-button, .delete-button, a.auth-submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 9px;
  padding: 0.625rem 1rem;
  font-size: 0.8125rem;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.1s ease;
}

/* Same colour as .receipts-add-button too (--primary-bg/-hover/-ink, defined per theme at :root
   alongside --navy/--accent-solid — see those tokens' own block), not the separate indigo
   --accent-solid-always treatment this used to have. Peter asked for these to match explicitly,
   twice (2026-08-29: "Ha 'Lägg till kvitto' som referens både ljust och mörkt tema"). Deliberately
   kept at the SAME specificity as the original var(--accent) rule (bare `button`, no :not() guard)
   — an earlier version of this fix added :not(.secondary-button):not(.delete-button) to stop the
   dark-theme override beating those two classes' own colour, but every :not() argument counts
   toward specificity too, so it also beat .link-button, .toggle-password, .space-card-pin, and
   every other single-class "background: none" button reset in the app (~20 of them) — exactly the
   regression it was trying to prevent, just against a wider set of classes. Pushing the theme
   switch into the token itself instead avoids the specificity fight entirely: this selector never
   needs a :root[data-theme="dark"] prefix, so it never outranks any of those classes. */
button, input[type="submit"], a.auth-submit {
  background: var(--primary-bg);
  color: var(--primary-ink);
}

button:hover, input[type="submit"]:hover, a.auth-submit:hover {
  background: var(--primary-bg-hover);
  filter: none;
  text-decoration: none;
}

.secondary-button {
  background: none;
  color: var(--ink);
  border: 1px solid var(--hairline);
}

/* --hairline, not --canvas — a .secondary-button turns up on every kind of background in this app
   (the plain page canvas itself, a white .card/.auth-card, a tinted .settings-section panel), and
   --canvas matches at least one of those exactly (the plain page) or nearly (a white card), making
   the hover state invisible there. --hairline reads as a real, visible change against all three
   without needing a context-specific override per container (Peter, 2026-08-29: "Tvåfaktorautenti-
   sering", "Mitt abonnemang", "Mitt space" "Bjud in"/"Redigera space", "Kontoadministration" —
   this one rule was the actual root cause behind all of those). */
.secondary-button:hover {
  background: var(--hairline);
  filter: none;
}

/* .settings-section's own background IS --canvas, and its border IS --hairline (Kategori's "Spara
   kategori", Dela kvitto's "Skapa delningslänk"/"Kopiera"/"Återkalla" etc.) — --surface reads as
   the button visibly lifting off the tinted panel, a more deliberate effect than just blending
   into its own border colour the way the general rule above would here. */
.settings-section .secondary-button:hover {
  background: var(--surface);
}

/* An <a class="secondary-button"> (e.g. "Rensa") is still an <a> — without this, the global
   a:hover rule's underline (specificity 0,1,1) beats the shape rule's non-hover text-decoration:
   none (0,1,0), so anchor-styled buttons alone would grow an underline no <button> ever gets. This
   selector's specificity (0,2,0) beats a:hover regardless of source order. */
/* color repeated here too, not just text-decoration — a.secondary-button/.page-link/.delete-button
   never redeclared color on their own :hover rules below, so the global "a:hover { color: var(
   --accent-hover) }" reset (specificity 0,1,1, a plain element+pseudo) beat their own BASE color
   (specificity 0,1,0, lower since it has no :hover of its own) outright — every one of these as an
   <a> (not a <button>) turned blue-purple on hover, including .delete-button losing its red danger
   colour entirely (Peter, 2026-08-29: "Mitt abonnemang" "Hantera dina spaces" — same root cause
   found live via computed styles, not just the two named here). */
a.secondary-button:hover, a.page-link:hover {
  text-decoration: none;
  color: var(--ink);
}

a.delete-button:hover {
  text-decoration: none;
  color: var(--danger);
}

/* A row-level action shouldn't look like the page's primary call to action. */
.delete-button {
  background: none;
  color: var(--danger);
  border: 1px solid var(--danger-border);
  padding: 0.35rem 0.85rem;
  font-size: 0.85rem;
  font-weight: 500;
  min-height: 40px;
}

.delete-button:hover {
  background: var(--danger-bg);
  filter: none;
}

pre#result {
  white-space: pre-wrap;
  word-break: break-word;
  background: var(--canvas);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  padding: 1rem;
  margin-top: 1.25rem;
  font-size: 0.85rem;
}

/* --- Forms (auth pages, capture form) --- */

form label {
  display: block;
  margin-bottom: 0.9rem;
  font-weight: 600;
  font-size: 0.82rem;
  color: var(--ink-muted);
}

form label input:not([type="checkbox"]), form label select {
  display: block;
  width: 100%;
  margin-top: 0.35rem;
  padding: 0.6rem 0.8rem;
  font-family: inherit;
  /* Explicit 16px, not the inherited ~13px (form label's own 0.82rem) — iOS Safari auto-zooms the
     page when a focused input's font-size is under 16px, and in a standalone PWA that zoomed-in
     scale then carries over into whatever page a form submit navigates to next (login redirecting
     to /receipts, a filter submitting as a GET, etc.), stuck until the user manually reloads
     (Peter, 2026-08-19 — reported specifically on the login form). Keeping the input's font-size
     at/above the threshold means iOS never zooms in the first place, so there's nothing to carry
     over. */
  font-size: 1rem;
  font-weight: 400;
  color: var(--ink);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  min-height: 44px;
  background: var(--surface);
  transition: border-color 0.1s ease, box-shadow 0.1s ease;
}

form label input:not([type="checkbox"]):focus, form label select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

/* Browsers' default placeholder gray doesn't adapt to theme and reads too dim against the dark
   surface color — pin it to the theme's own muted-text token instead (Firefox also dims
   placeholders by default, hence the explicit opacity: 1). */
form label input::placeholder {
  color: var(--ink-muted);
  opacity: 1;
}

.checkbox-label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 400;
}

/* The system checkbox next to a fully restyled form read as an unfinished corner of the page
   (DESIGNGRANSKNING-PUBLIK.md #50) — same border/radius language as the text inputs above it. */
.checkbox-label input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 18px;
  height: 18px;
  margin: 0;
  flex-shrink: 0;
  border: 1.5px solid var(--hairline);
  border-radius: 4px;
  background: var(--surface);
  position: relative;
  cursor: pointer;
}

.checkbox-label input[type="checkbox"]:checked {
  background: var(--navy);
  border-color: var(--navy);
}

.checkbox-label input[type="checkbox"]:checked::after {
  content: "";
  position: absolute;
  left: 5px;
  top: 1px;
  width: 5px;
  height: 9px;
  border: solid #fff;
  border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}

.checkbox-label input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Kom ihåg mig + Glömt lösenord? on one row instead of stacked (which broke the natural
   field → link → checkbox → button reading order, DESIGNGRANSKNING-PUBLIK.md #49). */
.auth-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin: -0.5rem 0 1.25rem;
  font-size: 0.85rem;
}

.auth-row .checkbox-label {
  margin: 0;
}

.form-error {
  color: var(--danger);
  background: var(--danger-bg);
  border: 1px solid var(--danger-border);
  border-radius: 8px;
  padding: 0.75rem 1rem;
  margin: 0 0 1rem;
}

/* A staging-only process note ("this legal copy hasn't been reviewed yet"), not an error — a
   quiet neutral row, not a red alarm box competing with the page's whole reason for existing
   (DESIGNGRANSKNING-PUBLIK.md #33). Only ever rendered outside production, see the pages that
   use it. */
.draft-notice {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  align-items: baseline;
  min-height: 32px;
  padding: 0.4rem 0.85rem;
  margin: 0 0 1.25rem;
  border-radius: 8px;
  border: 1px solid var(--hairline);
  background: var(--surface-alt);
  color: var(--text-label);
  font-size: 0.8125rem;
}

.draft-notice strong {
  color: var(--ink);
}

.content-page-updated {
  color: var(--ink-muted);
  font-size: 0.8125rem;
  margin-top: -0.5rem;
}

.form-error li {
  margin-left: 1rem;
}

/* Capture page heading row — plain h1 plus the mobile-only "Mina kvitton" shortcut (hidden here,
   shown at @media max-width:640px below). margin moved off the h1 onto the row itself so the row
   lays out as one line instead of the h1's own bottom margin only pushing empty space under it
   while the shortcut pill sits taller/shorter beside it. */
/* Narrow and centered, not a full-bleed card (DESIGNGRANSKNING-2.md #70) — this page is one
   focused action, not a form with fields to scan left-to-right. Vertical centering (as the doc
   literally asks) isn't done here on purpose: it would look right for the empty dropzone but
   fights a growing queue list below it, which needs to grow downward from a fixed top, not
   recentre itself every time a row is added. */
.capture-page {
  max-width: 720px;
  margin: 0 auto;
}

/* --- "Sparas i" target picker (design_handoff_spaces §5.5) — the upload page's single space
   control. The list expands out of this same card (upload-target.js toggles [hidden]), never a
   side panel. --- */
.upload-target-card {
  margin-bottom: 1rem;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  overflow: hidden;
}

.upload-target-card-open {
  border-color: rgba(var(--shadow-rgb), 0.14);
  box-shadow: 0 12px 30px rgba(var(--shadow-rgb), 0.1);
}

.upload-target-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.8rem 0.9rem;
}

.upload-target-card-open .upload-target-row {
  border-bottom: 1px solid var(--hairline);
}

.upload-target-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.upload-target-eyebrow {
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.53125rem;
  font-weight: 500;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--text-label);
}

.upload-target-name {
  font-weight: 700;
  font-size: 0.875rem;
  color: var(--ink);
}

.upload-target-toggle {
  flex-shrink: 0;
}

.upload-target-list {
  display: flex;
  flex-direction: column;
}

/* The [hidden] attribute only works via the UA stylesheet's [hidden]{display:none} — an author
   rule setting display on the same element (.upload-target-list above) silently wins over it
   regardless of specificity (author origin beats user-agent origin even on a tie), which made
   upload-target.js's hidden toggle inert until this explicit override existed. */
.upload-target-list[hidden] {
  display: none;
}

.upload-target-option {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  width: 100%;
  padding: 0.65rem 0.9rem;
  background: none;
  border: none;
  text-align: left;
  min-height: 0;
}

.upload-target-option:hover {
  background: var(--list-hover);
}

.upload-target-option.current {
  background: var(--list-hover);
}

.upload-target-option-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.upload-target-option-name {
  font-weight: 600;
  font-size: 0.8125rem;
  color: var(--ink);
}

.upload-target-option-sub {
  font-size: 0.6875rem;
  color: var(--text-label);
}

.upload-target-footer {
  padding: 0.7rem 0.9rem;
  border-top: 1px solid var(--hairline);
  background: var(--canvas);
  font-size: 0.71875rem;
  line-height: 1.5;
  color: var(--ink-muted);
}

.upload-target-free-note {
  margin: 0 0 1rem;
  font-size: 0.75rem;
  line-height: 1.6;
  color: var(--ink-muted);
}

/* Dimmed while the target picker is open (design_handoff_spaces §5.5) — the dropzone isn't the
   thing being interacted with right now. */
.capture-dropzone-dimmed {
  opacity: 0.5;
}

/* --- Dropzone (capture form) — a real drop target, not a bare native <input type="file"> sitting
   on the page (DESIGNGRANSKNING-2.md #70). The <input> itself can't be restyled directly in any
   browser, so it stays visually hidden (kept focusable/keyboard-usable, not display:none) and the
   <label for="..."> buttons inside the zone act as its visible triggers. --- */

.capture-dropzone {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.4rem;
  min-height: 160px;
  padding: 1.25rem 1.5rem;
  border: 2px dashed #dcd7c8;
  border-radius: 13px;
  color: var(--hairline);
  transition: border-color 0.15s ease, background-color 0.15s ease;
}

/* Shorter than the icon's own default 48px — this box is deliberately compact now (queued/
   processing receipts appear directly below it, see #queue in Capture.cshtml, and used to get
   pushed out of view — Peter, 2026-08-28) so the brand mark shouldn't be the tallest thing in it. */
.capture-dropzone .brand-mark-icon {
  width: 32px;
  height: 32px;
}

:root[data-theme="dark"] .capture-dropzone {
  border-color: rgba(250, 248, 243, 0.18);
}

.capture-dropzone-active {
  border-color: var(--accent);
  background: var(--surface-alt);
}

.capture-dropzone-title {
  margin: 0.75rem 0 0;
  font-size: 1.125rem;
  font-weight: 700;
  color: var(--ink);
}

.capture-actions-desktop {
  margin-top: 1.25rem;
}

.capture-actions-mobile {
  display: none;
  flex-direction: column;
  align-items: stretch;
  gap: 0.6rem;
  width: 100%;
  max-width: 280px;
  margin-top: 1.25rem;
}

.capture-actions-mobile label {
  justify-content: center;
}

.capture-dropzone-hint {
  margin: 1.25rem 0 0;
  font-size: 0.75rem;
  color: var(--text-label);
}

.file-input-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.file-input-hidden:focus-visible + .hint-text,
.file-picker label:has(+ .file-input-hidden:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* --- Capture queue (Lägg till kvitton) --- */

.capture-queue-header {
  display: flex;
  justify-content: flex-end;
  margin-top: 1.25rem;
}

/* Same recurring gotcha as .search-clear[hidden]/.correct-fields-form[hidden] — an author rule's
   `display: flex` beats the browser's built-in `[hidden] { display: none }` UA rule regardless of
   specificity, so toggling `hidden` on/off from JS silently stopped hiding this element the moment
   it got its own `display` declaration above. */
.capture-queue-header[hidden] {
  display: none;
}

.capture-queue {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 1.25rem;
}

.queue-item {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.65rem 1rem;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  background: var(--canvas);
}

/* A real preview of what's being uploaded (DESIGNGRANSKNING-2.md #70) — only populated when the
   row was created from a live upload (a File object is in hand); rows rebuilt from server state
   on page load have no local file to read, so this stays an empty placeholder square for those. */
.queue-item-thumb {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: 6px;
  overflow: hidden;
  background: var(--surface-alt);
}

.queue-item-thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.queue-item-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
  font-size: 0.9rem;
}

.queue-item-status {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.45rem;
  font-size: 0.85rem;
  text-align: right;
}

.queue-item-done {
  background: var(--success-bg);
  border-color: var(--success-border);
}

.queue-item-failed {
  background: var(--danger-bg);
  border-color: var(--danger-border);
}

.queue-item-failed .queue-item-status {
  color: var(--danger);
}

/* Same shape as .info-button (the "?" next to a reminder's date on the receipt page) — a small
   round tap target with its own background/border, not just a bare glyph, so there's an actual
   button-sized hit area on mobile rather than just the visual width of an "×" character. Danger-
   tinted by default (used on a Failed row), since this one removes something rather than just
   explaining it — but a Completed row is green, not red, so it's re-themed with the success
   palette below rather than showing a jarring red × on a "this went fine" row. Explicit
   min-height/padding/background override the global `button` shape rule the same way .info-button
   and .category-badge-label already have to (see their comments) — a bare class here would
   otherwise inherit the 44px-tall, indigo-hover default. */
.queue-item-dismiss {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  min-height: 0;
  padding: 0;
  margin-left: 0.15rem;
  border-radius: 999px;
  border: 1px solid var(--danger-border);
  background: none;
  color: var(--danger);
  font-size: 0.85rem;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
}

.queue-item-dismiss:hover {
  background: var(--danger-border);
}

.queue-item-done .queue-item-dismiss {
  border-color: var(--success-border);
  color: var(--success);
}

.queue-item-done .queue-item-dismiss:hover {
  background: var(--success-border);
}

/* Active (Pending/Processing) rows get a spinning ring so it's visually obvious the system is
   still working on them, not just static "Bearbetar..." text that could look stalled. */
.queue-item-spinner {
  display: inline-block;
  width: 0.8rem;
  height: 0.8rem;
  border-radius: 50%;
  border: 2px solid var(--overlay-medium);
  border-top-color: var(--accent);
  animation: queue-item-spin 0.8s linear infinite;
  flex-shrink: 0;
}

@keyframes queue-item-spin {
  to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  .queue-item-spinner {
    animation: none;
  }
}

/* --- Search/filter forms (Mina kvitton) --- */

/* Always visible now (DESIGNGRANSKNING.md #15) — only .search-options-panel inside it is still
   collapsible (toggled by "Filtrera", see Receipts.cshtml/receipts-search.js). Named grid area
   (not flex) purely so .search-options-panel/.filter-chips can sit on their own row below
   .filter-form without extra wrapper markup. */
.receipts-toolbar {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-areas:
    "form"
    "chips"
    "panel";
  row-gap: 0.75rem;
  margin-top: 1.25rem;
  margin-bottom: 1.5rem;
}

.receipts-toolbar .filter-form {
  grid-area: form;
  margin-bottom: 0;
}

.receipts-toolbar .filter-chips {
  grid-area: chips;
}

.receipts-toolbar .search-options-panel {
  grid-area: panel;
}

/* The persistent "which date filter is active" reminder (DESIGNGRANSKNING.md #17) — shown
   whenever a date range is set, independent of whether the collapsible panel itself is open. */
.filter-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.3rem 0.4rem 0.3rem 0.75rem;
  border-radius: 999px;
  background: var(--surface-alt);
  color: var(--text-2);
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
}

.filter-chip a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 50%;
  color: var(--text-2);
  text-decoration: none;
  font-size: 1rem;
  line-height: 1;
}

.filter-chip a:hover {
  background: var(--overlay-medium);
  color: var(--ink);
}

.filter-chips-clear-all {
  display: inline-flex;
  align-items: center;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--ink-muted);
}

.filter-chips-clear-all:hover {
  color: var(--accent);
}

.filter-form {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.75rem 1rem;
  margin-bottom: 1.5rem;
}

.filter-form label {
  margin-bottom: 0;
  flex: 1 1 160px;
}

.filter-form label input {
  min-width: 0;
}

.filter-form button, .filter-form a.secondary-button {
  flex: 0 0 auto;
}

/* Search icon (left) + clear/filter-toggle buttons (right), same absolute-positioned-inside-a-
   relative-wrapper pattern as .password-field's show/hide toggle. Wider than before — this field
   now owns filtering too, not just free text, so it reads as the whole toolbar rather than a
   narrow, purely-text search box. */
.search-field {
  position: relative;
  max-width: 480px;
}

/* Fully self-contained — deliberately duplicates the global "form label input:not([type=checkbox])"
   rule's border/radius/height/background/font rather than relying on it, because that global rule
   never actually matches here: it requires the input to be a DESCENDANT of a <label>, but this
   field's <label> is a visually-hidden sibling (id/for association, not wrapping — see
   Receipts.cshtml), so the input silently fell back to the browser's raw unstyled default (2px
   inset grey border, no radius, no min-height) despite every other input in the app looking
   correct. Same fix applies to .date-range-fields input below, which has the same gap for a
   different reason (form="receiptFilterForm" links it for submission but doesn't make it a CSS
   descendant of <form>). */
.filter-form .search-field > input {
  display: block;
  width: 100%;
  padding: 0.6rem 4.3rem 0.6rem 2.5rem;
  font-family: inherit;
  font-size: 1rem; /* see "form label input"'s own comment on the 16px iOS-zoom threshold */
  font-weight: 400;
  color: var(--ink);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  min-height: 44px;
  background: var(--surface);
  transition: border-color 0.1s ease, box-shadow 0.1s ease;
}

.filter-form .search-field > input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.filter-form .search-field > input::placeholder {
  color: var(--ink-muted);
  opacity: 1;
}

.search-icon {
  position: absolute;
  left: 0.85rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--ink-muted);
  pointer-events: none;
}

/* Clear (×) sits left of the filter toggle. Clears BOTH the search text and the date range — the
   one universal "clear" affordance for this form, so the options panel itself doesn't need its
   own separate Rensa button. */
.search-clear {
  position: absolute;
  right: 2.6rem;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  min-height: 0;
  padding: 0;
  background: none;
  border: none;
  border-radius: 50%;
  color: var(--ink-muted);
}

/* Without this, the rule above's own `display: flex` (needed to center the × icon while visible)
   beats the browser's built-in `[hidden] { display: none }` UA rule, so toggling the `hidden`
   attribute via JS stopped actually hiding the button. */
.search-clear[hidden] {
  display: none;
}

.search-clear:hover {
  background: var(--overlay-medium);
  color: var(--ink);
}

/* Filter icon lives INSIDE the search field now, at its far right edge — one control that owns
   both search and filtering, not a separate "Filtrera" button elsewhere in the header pointing at
   a panel that visually belonged to neither. */
.search-filter-toggle {
  position: absolute;
  right: 0.4rem;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  min-height: 0;
  padding: 0;
  background: none;
  border: none;
  border-radius: 6px;
  color: var(--ink-muted);
}

.search-filter-toggle:hover, .search-filter-toggle[aria-expanded="true"] {
  background: var(--overlay-medium);
  color: var(--ink);
}

/* Small dot instead of a filled/coloured button state — enough to say "a filter is active"
   without competing with the search icon on the opposite edge of the same field. */
.search-filter-toggle.has-active-filter {
  color: var(--accent);
}

.search-filter-toggle.has-active-filter::after {
  content: "";
  position: absolute;
  top: 2px;
  right: 2px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  border: 1.5px solid var(--surface);
}

/* Sits OUTSIDE <form class="filter-form"> as its own block (see Receipts.cshtml) so it folds out
   in normal document flow directly under the search bar, pushing the table down, rather than
   floating over the page as a detached card — this was a deliberate move away from the previous
   position:absolute dropdown/modal approach. Its own From/To inputs still submit as part of the
   one combined GET request via the form="receiptFilterForm" HTML attribute. */
.search-options-panel {
  /* Left-aligns under its trigger (the search field), matching its width exactly and sitting
     close enough to read as physically attached to it, not a separate floating card — no shadow
     of its own, same border colour/radius as the field above it ("i exakt fältets bredd, visuellt
     fäst vid det"). Spans both columns of .receipts-toolbar's grid (see
     grid-template-areas there), so it always sits on its own full-width row regardless of where
     that row falls between the search field and the counter at a given breakpoint. */
  margin-bottom: 1.5rem;
  width: 100%;
  max-width: 480px;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  padding: 1rem;
}

.search-options-panel[hidden] {
  display: none;
}

.search-options-panel-header {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  color: var(--ink-muted);
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 0.9rem;
}

.search-options-panel-header span {
  flex: 1 1 auto;
}

.search-options-close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  min-height: 0;
  padding: 0;
  background: none;
  border: none;
  border-radius: 50%;
  color: var(--ink-muted);
}

.search-options-close:hover {
  background: var(--overlay-medium);
  color: var(--ink);
}

.date-quick-picks {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-bottom: 0.9rem;
}

.date-quick-pick {
  padding: 0.35rem 0.7rem;
  border-radius: 999px;
  border: 1px solid var(--hairline);
  background: var(--surface);
  color: var(--ink-muted);
  font-size: 0.78rem;
  font-weight: 500;
  min-height: 0;
}

.date-quick-pick:hover {
  border-color: var(--accent);
  color: var(--ink);
}

.date-range-fields {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.date-range-fields label {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  margin-bottom: 0;
  font-size: 0.76rem;
  font-weight: 600;
  color: var(--ink-muted);
}

.date-range-fields label input {
  display: block;
  width: 100%;
  min-width: 0;
  padding: 0.5rem 0.55rem;
  font-family: inherit;
  font-size: 1rem; /* see "form label input"'s own comment on the 16px iOS-zoom threshold */
  font-weight: 400;
  color: var(--ink);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  min-height: 40px;
  background: var(--surface);
  transition: border-color 0.1s ease, box-shadow 0.1s ease;
}

.date-range-fields label input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

/* The native picker icon otherwise renders in whatever color the OS chooses (often stark black),
   clashing with a themed input in dark mode — dim it to match the field's own muted tone
   (DESIGNGRANSKNING.md #16). Firefox has no equivalent pseudo-element; its native icon is left
   as-is there. */
.date-range-fields label input::-webkit-calendar-picker-indicator {
  opacity: 0.55;
  filter: var(--date-icon-filter, none);
}

:root[data-theme="dark"] .date-range-fields label input::-webkit-calendar-picker-indicator {
  filter: invert(1);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .date-range-fields label input::-webkit-calendar-picker-indicator {
    filter: invert(1);
  }
}

.date-range-sep {
  align-self: flex-end;
  padding-bottom: 0.75rem;
  color: var(--ink-muted);
  flex-shrink: 0;
}

/* Visible only to assistive tech / still a real, focusable, submittable button in the DOM —
   receipts-search.js auto-submits the form, this is the no-JS/Enter-key fallback so the search
   still works without a visible button, matching what was asked for. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Inverse of .no-print — invisible on screen, shown only when printing (see .print-branding in
   the @media print block below for the one current use). Genuinely display:none (not the
   visually-hidden clip trick above): this has no reason to be reachable by assistive tech on
   screen, since it's the same information already visible in the header/branding elsewhere. */
.print-only {
  display: none;
}

/* --- Category badges (Mina kvitton list + receipt detail picker) ---
   Best-practice tag/label styling (GitHub issue labels, Linear, Notion): a mostly-neutral pill
   with a small colored dot for quick visual scanning, rather than filling the whole pill with a
   saturated color per category — a full rainbow of solid-colored pills reads as busy/childish and
   fights this app's otherwise restrained navy-and-neutral palette. The dot still carries the same
   distinct-per-category color; only where it lives changed. */

/* No more per-category dot — a full rainbow of 10+ saturated dots read as random/childish and
   competed with the urgency color-coding on the complaint-right column right next to it
   (DESIGNGRANSKNING.md #13, option A). Category is metadata, not status: one quiet neutral chip
   style for every category now, same as any other tag in the app. */
.category-badge {
  display: inline-flex;
  align-items: center;
  padding: 0.25rem 0.65rem;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  white-space: nowrap;
  background: var(--surface-alt);
  color: var(--text-2);
}

/* The selected category on the receipt detail picker — filled navy, same convention as
   .page-link.active — stands out from the neutral unselected ones instead of every option
   competing for attention via its own color. */
.category-badge.active {
  background: var(--active-bg);
  color: var(--active-ink);
}

/* --- View switcher (Alla kvitton / Nedräkning) — replaces the old
   icon-only Lista/Kalender toggle: the calendar icon promised a month grid that no longer exists,
   and both segments being wordless meant "nedräkning" as a concept was introduced nowhere at all.
   Now the ACTIVE segment carries its own word (inactive stays icon-only, with aria-label carrying
   the name); the countdown segment also carries a notification count so switching views has a
   reason, not just a settings toggle sitting there. */
.view-switcher {
  display: inline-flex;
  gap: 4px;
  padding: 5px;
  background: var(--surface-alt);
  border-radius: 999px;
}

:root[data-theme="dark"] .view-switcher {
  border: 1px solid var(--hairline);
}

.view-switcher-segment {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  height: 40px;
  width: 40px;
  padding: 0;
  border-radius: 999px;
  color: var(--ink-muted);
  text-decoration: none;
  transition: background-color 0.15s ease, color 0.15s ease, width 0.15s ease;
}

.view-switcher-segment svg {
  display: block;
  flex-shrink: 0;
}

.view-switcher-segment:hover {
  color: var(--ink);
  text-decoration: none;
}

.view-switcher-segment.active {
  width: auto;
  padding: 0 12px 0 14px;
  background: var(--surface);
  color: var(--ink);
  box-shadow: 0 1px 3px rgba(var(--shadow-rgb), 0.12);
}

.view-switcher-label {
  font-size: 0.875rem;
  font-weight: 600;
  white-space: nowrap;
}

/* Always in the DOM (see _ViewSwitcher.cshtml) so the mobile override below can show it on the
   inactive segment too — hidden here on desktop, where only the active segment carries a word. */
.view-switcher-segment:not(.active) .view-switcher-label {
  display: none;
}

/* Flows as a third flex child after the label, not position:absolute on top of the text (spec's
   own explicit instruction) — so it never overlaps the word at narrow widths. */
.view-switcher-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 19px;
  height: 19px;
  padding: 0 6px;
  border-radius: 999px;
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.6875rem;
  font-weight: 600;
  color: #faf8f3;
  background: #c2521f;
}

:root[data-theme="dark"] .view-switcher-badge {
  color: #0e1024;
  background: #e5794a;
}

@media (max-width: 640px) {
  /* Full width, both segments keep their word — restored after a same-day round trip: a compact
     icon-only version was tried per an earlier live note, then explicitly rejected in favour of
     this exact layout (Peter, 2026-08-16, reference image: "BARA IKONER — UNDVIK... Sparar 240 px
     som ingen behöver, och tar bort det enda stället där ordet 'nedräkning' lärs ut. Träffytan blir
     dessutom 44 px istället för halva skärmen."). Both segments equal width, 44px tall, label
     always visible on both. */
  .view-switcher {
    width: 100%;
  }

  .view-switcher-segment {
    flex: 1 1 0;
    width: auto;
    height: 44px;
    padding: 0 12px !important;
  }

  .view-switcher-segment .view-switcher-label {
    display: inline !important;
  }
}

/* --- Pagination --- */

.pagination {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 1.25rem;
}

.page-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.25rem;
  min-height: 2.25rem;
  padding: 0 0.5rem;
  border-radius: 8px;
  border: 1px solid var(--hairline);
  color: var(--ink);
  text-decoration: none;
  font-size: 0.85rem;
  font-weight: 600;
}

/* --hairline, not --canvas — .pagination sits directly on the plain page, same invisible-hover
   trap .secondary-button had (see its own comment above) since the page's own background IS
   --canvas. */
.page-link:hover {
  background: var(--hairline);
}

.page-link.active {
  background: var(--active-bg);
  border-color: var(--active-bg);
  color: var(--active-ink);
}

/* --- Whole-receipt category edit --- */

.category-form {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  flex-wrap: wrap;
}

/* Fully self-contained, same reasoning as the receipts-page search field (see .filter-form
   .search-field > input): this <input> sits directly in a <form>, not inside a <label>, so the
   global "form label input:not([type=checkbox])" rule never matches it — it was silently falling
   back to the browser's raw default text-field appearance (no border-radius, mismatched border)
   instead of the app's actual input style. --surface (not --canvas) so it visibly stands out
   against the tinted .settings-section panel it now sits inside. */
.category-input {
  min-height: auto;
  padding: 0.5rem 0.7rem;
  font-size: 0.9rem;
  font-family: inherit;
  color: var(--ink);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  background: var(--surface);
  transition: border-color 0.1s ease, box-shadow 0.1s ease;
}

.category-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.category-input::placeholder {
  color: var(--ink-muted);
  opacity: 1;
}

.category-form .secondary-button {
  min-height: auto;
  padding: 0.5rem 0.9rem;
}

/* Needs room for real category words ("Restaurang", "Presentkort"). */
.receipt-category-input {
  width: 16rem;
  max-width: 100%;
}

/* --- Share-link duration picker — matches the sizing of every other button/input on the page
   (previously reused .category-form, whose deliberately-smaller controls made the select and the
   "Skapa delningslänk" button look mismatched next to each other). --- */
.share-duration-form {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-top: 0.75rem;
}

/* "Giltig i" reads as a caption sitting inline right before the dropdown, on the same row as the
   "Skapa delningslänk" button — not stacked above it. The global "form label select" rule forces
   select to display:block/width:100% (right for a normal form field sitting alone in a column),
   which is what pushed it onto its own line here; both overrides below need to out-specificity
   that rule ("form label select" = 3 elements, (0,0,0,3)) — one class + one element beats it. */
.share-duration-form label {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0;
}

.share-duration-form label select {
  display: inline-block;
  width: auto;
  /* The global "form label select" rule also adds margin-top: 0.35rem, meant to space a stacked
     label-caption from the field below it — with "Giltig i" now sitting inline instead (see
     .share-duration-form label above), that margin just inflated this row's height beyond the
     select's own 44px, throwing off vertical alignment with "Skapa delningslänk" next to it. */
  margin-top: 0;
}

/* --- Share link (click-to-copy) --- */

/* Same "no <label> ancestor, global input rule never matches" gap as .category-input above — this
   one sits in a plain <td>, not a label, so it also needs its own complete border/radius. --surface
   (not --canvas) so it stays visible as its own field against the tinted .settings-section panel
   the share-link table now lives inside. min-height: 44px (not auto) matches every other input in
   the app — this one used to sit visibly shorter than the rest. Right padding clears the copy
   icon button overlaid inside the field (see .share-link-copy below) now that "Kopiera" is no
   longer a separate button next to it. */
.share-link-input {
  width: 100%;
  min-height: 44px;
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.8rem;
  color: var(--ink);
  padding: 0.6rem 2.6rem 0.6rem 0.7rem;
  cursor: pointer;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  background: var(--surface);
}

/* Copy button lives inside the field itself (right edge), same absolute-positioned-inside-a-
   relative-wrapper pattern as the receipts search field's clear (×) button — lets the input take
   the full row width instead of splitting it with a separate "Kopiera" button next to it. */
.share-link-row {
  position: relative;
}

.share-link-copy {
  position: absolute;
  right: 0.4rem;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  min-height: 0;
  padding: 0;
  background: none;
  border: none;
  border-radius: 50%;
  color: var(--ink-muted);
}

.share-link-copy:hover {
  background: var(--overlay-medium);
  color: var(--ink);
}

.share-link-copy .check-icon {
  display: none;
  color: var(--success);
}

.share-link-copy.copied .copy-icon {
  display: none;
}

.share-link-copy.copied .check-icon {
  display: block;
}

/* --- 2FA setup --- */

.qr-code {
  max-width: 220px;
  /* Centered, not flush against the card's left edge — a square graphic like this reads as
     misplaced/half-cut-off sitting where left-aligned body text normally starts, unlike the text
     around it. */
  margin: 1rem auto;
  padding: 0.75rem;
  /* Always white, in both themes — the SVG itself is now generated with an explicit white quiet
     zone (see EnableAuthenticatorModel), so a themed --surface here would otherwise show as a
     mismatched dark ring around the code in dark mode. Scanners also expect a plain light
     background regardless of site theme. */
  background: #fff;
  border: 1px solid var(--hairline);
  border-radius: 10px;
}

.qr-code svg {
  width: 100%;
  height: auto;
  display: block;
}

.manual-key {
  display: block;
  background: var(--canvas);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  padding: 0.6rem 0.8rem;
  margin-bottom: 1rem;
  word-break: break-all;
  font-size: 0.9rem;
}

.recovery-codes {
  list-style: none;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.5rem;
}

.recovery-codes code {
  display: block;
  background: var(--canvas);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  padding: 0.5rem 0.7rem;
}

/* --- Receipt detail --- */

.receipt-image {
  max-width: 100%;
  border-radius: 8px;
  margin: 1rem 0;
  border: 1px solid var(--hairline);
}

/* No margin of its own — always paired with .settings-section in markup (see ReceiptDetail.cshtml),
   which already sets margin-top consistently with every other panel on the page. A margin here
   too, on the same element, would win the tie on source order and make this one panel's spacing
   subtly different from the rest. */
.receipt-overview {
  display: flex;
  align-items: flex-start;
  gap: 1.5rem;
  flex-wrap: wrap;
}

/* 4:5, cropped (object-fit: cover) — a receipt is usually much taller than the thumbnail box, so
   a plain max-width/max-height letterboxed it into a tiny sliver. The full uncropped image is
   always one click away via the lightbox (DESIGNGRANSKNING.md #23). */
.receipt-image-thumb {
  width: 160px;
  height: 200px;
  object-fit: cover;
  /* Lines up the image's top edge with the first meta-table row's TEXT, not the row's own box
     edge — the row has 0.4rem of top padding before "Handlare" actually starts, so without this
     the image reads as sitting slightly higher than the text next to it. */
  margin-top: 0.4rem;
  border-radius: 8px;
  border: 1px solid var(--hairline);
  cursor: zoom-in;
  flex: 0 0 auto;
}

/* No image on file (e.g. a manually entered receipt) — an explicit "no image" placeholder rather
   than silently rendering nothing where the thumbnail would be (DESIGNGRANSKNING.md #23). */
.receipt-image-placeholder {
  width: 160px;
  height: 200px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 0.4rem;
  border-radius: 8px;
  border: 1px solid var(--hairline);
  background: var(--surface-alt);
  color: var(--hairline);
  flex: 0 0 auto;
}

.receipt-image-placeholder span {
  color: var(--text-label);
  font-size: 0.75rem;
}

.meta-table {
  flex: 1 1 260px;
  border-collapse: collapse;
  width: auto;
}

/* Three ancestor classes (.card .receipt-overview .meta-table), not just .meta-table, so this
   border-bottom: none reliably beats the generic ".page table th, .page table td" rule (also two
   classes) elsewhere in this file — a same-specificity tie would otherwise fall back to source
   order and silently lose, which is exactly what was drawing a hairline under every single key/
   value row here even though this rule already said "none". A short key/value list like this
   doesn't need a rule between every row; padding alone reads clearly enough. */
.card .receipt-overview .meta-table th, .card .receipt-overview .meta-table td {
  text-align: left;
  padding: 0.5rem 1.25rem 0.5rem 0;
  border-bottom: none;
  vertical-align: top;
}

/* Mono/uppercase labels, same family as .stat-label — smaller than the value next to it so the
   hierarchy (this is a label, that's the data) is legible at a glance, not just via position
   (DESIGNGRANSKNING.md #24). */
.meta-table th {
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-label);
  font-weight: 600;
  white-space: nowrap;
  width: 1%;
  padding-top: 0.65rem;
}

.card .receipt-overview .meta-table td {
  font-size: 0.9375rem;
  color: var(--ink);
}

/* Sits in the same card as .receipt-overview (see ReceiptDetail.cshtml) — a small diagnostic
   chip, not part of the formal record, so it reads as a footnote rather than another row of
   receipt data (DESIGNGRANSKNING.md #24). */
/* Same size/weight as the Summa card right next to it — used to default to whatever size the
   table-context .complaint-remaining rule gave it (much smaller, 400 weight), so two cards of
   equal visual size carried wildly different typographic weight even though the reklamationsrätt
   one is the app's whole reason to exist (DESIGNGRANSKNING-2.md #72). */
.receipt-highlight .complaint-remaining {
  display: block;
  font-size: 1.6875rem;
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -0.02em;
}

.extraction-note {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 1.25rem;
}

/* A real secondary button with a pencil icon, not a bare text link — "Rätta uppgifter" is an
   action, not a footnote. */
.extraction-fix-button {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.4rem 0.85rem;
  font-size: 0.8125rem;
  min-height: 0;
}

/* Small inline "Rätta" affordance right next to the field that actually failed verification —
   the fix button up top is easy to miss right after a red error, this puts it exactly where the
   eye already is ("visa knappen i anslutning till det fältet också"). */
.field-unverified-fix {
  margin-left: 0.3rem;
  padding: 0;
  min-height: 0;
  background: none;
  border: none;
  color: inherit;
  text-decoration: underline;
  font-size: inherit;
  font-weight: 600;
}

.field-unverified-fix:hover {
  background: none;
  filter: none;
  color: var(--danger);
}

/* Swaps in for .meta-table in the same .receipt-overview slot — the receipt image stays visible
   to its left either way. Two columns, not one — a single stacked column of 7
   fields ran to ~500px tall for no reason, most of the card's own width sitting empty next to it
   (Peter, 2026-08-16: "skulle bara vara mer kompakt"). Same field order as the read-only table
   above it, just paired up two-per-row instead of one-per-row. */
.correct-fields-form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem 1.25rem;
  max-width: 520px;
}

/* Without this, the rule above's own author-stylesheet `display: flex` beats the UA stylesheet's
   `[hidden] { display: none }` regardless of specificity (author rules always win over UA rules
   at equal or lower specificity) — same gotcha already documented on .search-clear[hidden]. */
.correct-fields-form[hidden] {
  display: none;
}

.correct-fields-form label {
  margin-bottom: 0;
}

.correct-fields-country {
  max-width: 5rem;
  text-transform: uppercase;
}

.correct-fields-actions {
  grid-column: 1 / -1;
  display: flex;
  gap: 0.6rem;
  margin-top: 0.25rem;
}

.correct-fields-actions .auth-submit {
  width: auto;
  flex: 0 0 auto;
}

@media (max-width: 480px) {
  /* Back to one column below ~480px — two columns of labelled inputs at phone width leaves each
     field too narrow to comfortably type into. */
  .correct-fields-form {
    grid-template-columns: 1fr;
  }
}

/* Zoom-indicator overlay, shown on hover/focus so the thumbnail reads as clickable
   (DESIGNGRANSKNING-2.md #74) — not visible at rest, same idea as a lightbox trigger anywhere
   else on the web. */
.receipt-image-wrap {
  position: relative;
  flex: 0 0 auto;
  border-radius: 8px;
}

.receipt-image-wrap .receipt-image-thumb {
  transition: outline-color 0.15s ease;
  outline: 2px solid transparent;
  outline-offset: 2px;
}

.receipt-image-wrap:hover .receipt-image-thumb,
.receipt-image-wrap:focus-within .receipt-image-thumb {
  outline-color: var(--accent);
}

/* A plain glyph with a drop-shadow for contrast against whatever the photo happens to show,
   rather than a solid dark circular badge — the badge read as too heavy/large sitting on a
   160×200px thumbnail (Peter, 2026-08-16). */
.receipt-image-zoom-icon {
  position: absolute;
  right: 0.5rem;
  bottom: 0.5rem;
  width: 1.125rem;
  height: 1.125rem;
  color: #fff;
  filter: drop-shadow(0 1px 3px rgba(18, 20, 58, 0.7));
  opacity: 0;
  transition: opacity 0.15s ease;
  pointer-events: none;
}

.receipt-image-wrap:hover .receipt-image-zoom-icon,
.receipt-image-wrap:focus-within .receipt-image-zoom-icon {
  opacity: 1;
}

.receipt-image-hint {
  display: none;
  margin-top: 0.4rem;
  font-size: 0.75rem;
  color: var(--text-label);
}

.field-unverified {
  display: inline-block;
  margin-left: 0.4rem;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  background: var(--danger-bg);
  color: var(--danger);
  font-size: 0.7rem;
  font-weight: 600;
}

.extraction-chip {
  display: inline-flex;
  align-items: center;
  padding: 0.25rem 0.65rem;
  border-radius: 999px;
  background: var(--surface-alt);
  color: var(--text-2);
  font-size: 0.75rem;
  font-weight: 500;
}

/* The Summa/Reklamationsrätt highlight band above the field list (DESIGNGRANSKNING.md #24) —
   same card family as the receipts list's own .stat-card, reusing its .stat-label/.stat-value
   typography rather than redefining it. */
.receipt-highlight-band {
  display: flex;
  flex-wrap: wrap;
  gap: 0.875rem;
  margin-bottom: 1.5rem;
}

.receipt-highlight {
  flex: 1 1 200px;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 13px;
  padding: 1.125rem 1.25rem;
}

/* --- Overflow menu ("⋯", currently just "Radera kvitto") — same details/summary dropdown shape
   as the header's own menus, scoped to this page's action row (DESIGNGRANSKNING.md #22). --- */
/* Fixed height, pinned to the summary's own 44px — without this, opening the dropdown grows the
   <details> element's own box by ~12px (confirmed by direct measurement: a Chromium <details>
   quirk around its native open/close transition, which doesn't fully respect the dropdown panel
   being position:absolute and out of flow). That growth pushed "Skriv ut" — a flex sibling in
   .page-header-actions with align-items:center — down by half the difference every time the menu
   opened, which read as the whole header row jumping (Peter, 2026-08-16). overflow stays visible
   (the default) so the dropdown panel itself still renders outside these bounds. */
.overflow-menu {
  position: relative;
  height: 44px;
}

.overflow-menu summary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  min-height: 44px;
  /* Same resting border as "Skriv ut" (.secondary-button) right next to it — a bare glyph with no
     visible edges didn't read as a tap target at all until you happened to hover it
     (DESIGNGRANSKNING-2.md #81). */
  border: 1px solid var(--hairline);
  border-radius: 8px;
  color: var(--ink-muted);
  list-style: none;
  font-weight: 400;
}

.overflow-menu summary::-webkit-details-marker {
  display: none;
}

.overflow-menu summary:hover, .overflow-menu[open] summary {
  background: var(--overlay-weak);
  color: var(--ink);
}

.overflow-menu-panel {
  position: absolute;
  right: 0;
  top: calc(100% + 0.4rem);
  min-width: 180px;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(var(--shadow-rgb), 0.18);
  padding: 0.4rem;
  z-index: 20;
}

.overflow-menu-panel .link-button {
  width: 100%;
  text-align: left;
  color: var(--danger);
}

.overflow-menu-panel .link-button:hover {
  background: var(--danger-bg);
}

/* --- Category editor — the selected category shows as a plain chip; the full picker (every
   quick-pick badge, custom-category form) is tucked behind "Ändra" instead of always rendering
   on the page (DESIGNGRANSKNING.md #21). --- */
.category-editor summary {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  list-style: none;
  font-weight: 400;
  color: inherit;
}

.category-editor summary::-webkit-details-marker {
  display: none;
}

.category-editor-trigger {
  color: var(--accent);
  font-weight: 600;
  font-size: 0.85rem;
}

.category-editor[open] summary {
  margin-bottom: 1rem;
}

/* Click-to-enlarge lightbox for the receipt thumbnail — a native <dialog>, same pattern as the
   delete-confirm dialog. Click or scroll on the image zooms (see receipt-lightbox.js); a dedicated
   close button handles dismissal since the image itself no longer closes it on click. */
.image-lightbox {
  position: relative;
  border: none;
  background: transparent;
  padding: 0;
  max-width: 92vw;
  max-height: 92vh;
  overflow: hidden;
}

.image-lightbox img {
  display: block;
  max-width: 92vw;
  max-height: 92vh;
  border-radius: 8px;
  cursor: zoom-in;
  transition: transform 0.15s ease;
  /* Pointer events (pointerdown/move/up) drive panning directly — without this, the browser's
     own touch scroll/pinch gestures compete with that drag on mobile. */
  touch-action: none;
  /* Images are natively draggable in browsers — without this, a left-click-drag starts the
     browser's own "drag this image out" gesture instead of reaching pointermove, which is what
     made panning look broken for a real mouse drag (the draggable="false" attribute alone isn't
     enough in every browser, e.g. Safari). */
  -webkit-user-drag: none;
  user-select: none;
}

/* Once zoomed, dragging (not just clicking) is the primary interaction, so the cursor should
   read as "grab" rather than "zoom-out" — clicking without dragging still zooms back out. */
.image-lightbox img.zoomed {
  cursor: grab;
}

.image-lightbox img.zoomed:active {
  cursor: grabbing;
}

.image-lightbox img.dragging {
  transition: none;
}

.image-lightbox::backdrop {
  background: rgba(18, 33, 59, 0.75);
}

.lightbox-close {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  z-index: 1;
  width: 2.25rem;
  height: 2.25rem;
  min-height: 0;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(18, 33, 59, 0.65);
  color: #fff;
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
}

.lightbox-close:hover {
  background: rgba(18, 33, 59, 0.9);
}

.page-header-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  /* Own bottom margin (rather than relying only on whatever follows to carry the spacing) so a
     page-header-row still reads correctly even when nothing with a top margin of its own comes
     right after it (e.g. Receipts.cshtml's empty-state text) — collapses harmlessly with a
     following sibling's own top margin (e.g. .receipts-toolbar) rather than stacking with it. */
  margin-bottom: 1.25rem;
}

.page-header-row h1 {
  margin: 0;
}

.page-header-actions {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
}

/* .delete-button is deliberately smaller/lighter everywhere else (a row-level action shouldn't
   look like the page's primary call to action — see its own rule) — but here it sits directly
   next to "Skriv ut" (.secondary-button) as an equally-weighted page-level action, so the size
   mismatch reads as a bug, not a hierarchy. Matches .secondary-button's own dimensions exactly. */
.page-header-actions .delete-button {
  padding: 0.6rem 1.2rem;
  font-size: 0.9rem;
  font-weight: 600;
  min-height: 44px;
}

/* A page-level "go back" needs to read as a distinct, deliberate control — not a random sentence
   with an arrow glued to it — while staying visually secondary to the h1 below it. */
.back-link {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  margin-bottom: 1rem;
  color: var(--ink-muted);
  font-weight: 600;
  font-size: 0.875rem;
}

.back-link:hover {
  color: var(--accent);
  text-decoration: none;
}

.back-link svg {
  flex-shrink: 0;
  transition: transform 0.15s ease;
}

.back-link:hover svg {
  transform: translateX(-2px);
}

/* Major sections on the receipt detail page (Kategori, Rader, Påminnelser, Dela kvitto) use
   .settings-section (see above, near .account-card) instead of a rule-per-section — a thin
   hairline above every heading read as the exact same weight as the hairlines between individual
   table rows just below it, so the page was "all lines, no hierarchy". A tinted, bordered box
   makes major sections unmistakable at a glance while leaving row-level hairlines as the only
   lines left, genuinely one tier lighter. */

.hint-text {
  color: var(--ink-muted);
  font-size: 0.9rem;
}

/* --- Collapsible sections (Rader, extra categories) — plain <details>/<summary>, no JS --- */

details summary {
  cursor: pointer;
  color: var(--ink-muted);
  font-weight: 700;
  font-size: 0.85rem;
}

details summary:hover {
  color: var(--ink);
}

details[open] summary {
  margin-bottom: 0.75rem;
}

.details-summary-heading {
  display: inline;
  margin: 0;
  color: inherit;
  font: inherit;
  letter-spacing: normal;
}

/* --- Category picker (whole-receipt category) --- */
/* Spacing/box now comes from .settings-section (this always sits combined with it in markup —
   see ReceiptDetail.cshtml) rather than its own margin rule. */

.category-badge-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

/* These forms exist only so the label/remove buttons (which live in the visible <span> above,
   not inside the form itself — see ReceiptDetail.cshtml) have something to submit via the HTML5
   form="id" attribute; a <button> can't be nested inside another <button>, so the pill can't be a
   single <button> wrapping both actions. */
.category-hidden-form {
  display: none;
}

/* Both are <button>s, so the global button shape rule's min-height: 44px otherwise wins (no other
   rule sets min-height, so the tag rule's value survives the cascade even though .category-badge's
   own class-level padding already beats it) — that's what made these look like oversized pills. */
.category-badge-label, .category-badge-remove {
  background: none;
  border: none;
  min-height: auto;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
}

/* Without this, the inner <button> falls back to the global `button:hover { background:
   var(--accent-hover) }` rule (a bright indigo fill) while its own text color stays the badge's
   muted gray — unreadable. Explicitly clearing it here (higher specificity than the global
   element-level rule) lets the pill's own, readable hover treatment below show through instead. */
.category-badge-label:hover {
  background: none;
}

.category-badge:not(.active):hover {
  background: var(--overlay-weak);
}

.category-badge-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1rem;
  height: 1rem;
  border-radius: 50%;
  font-size: 0.9rem;
  line-height: 1;
  opacity: 0.55;
}

.category-badge-remove:hover {
  opacity: 1;
  background: var(--overlay-medium);
}

.category-badge.active .category-badge-remove:hover {
  background: var(--active-overlay);
}

.category-badge-more {
  margin-top: 0.75rem;
}

.category-badge-more summary {
  font-size: 0.8rem;
}

.category-badge-more[open] .category-badge-list {
  margin-top: 0.6rem;
}

.category-custom {
  margin-top: 0.75rem;
}

.category-custom summary {
  font-size: 0.8rem;
}

.category-custom[open] .category-form {
  margin-top: 0.6rem;
}

/* --- Reminder info button + share-link row --- */

.info-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.3rem;
  height: 1.3rem;
  min-height: 0;
  padding: 0;
  margin-left: 0.35rem;
  border-radius: 999px;
  background: var(--canvas);
  border: 1px solid var(--hairline);
  color: var(--ink-muted);
  font-size: 0.7rem;
  font-weight: 700;
  vertical-align: middle;
}

.info-button:hover {
  background: var(--hairline);
}

/* --- Confirm dialog (replaces the native confirm()) --- */

/* background/color weren't set here before — <dialog>'s UA default (the `canvas`/`canvastext`
   system colors) renders as a plain white box in most browsers regardless of this app's own
   dark-mode toggle, not just the OS-level one. Affects every dialog sharing this class, not only
   the new advanced-search one below. */
.confirm-dialog {
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--hairline);
  border-radius: 12px;
  box-shadow: 0 16px 40px -12px rgba(var(--shadow-rgb), 0.25);
  padding: 1.5rem;
  max-width: 360px;
}

.confirm-dialog::backdrop {
  background: rgba(18, 33, 59, 0.45);
}

.confirm-dialog form {
  display: flex;
  gap: 0.75rem;
  justify-content: flex-end;
  margin-top: 1rem;
}

/* --- Wide dialog variant (design_handoff_spaces §4c/§4b) — the create-space and edit-identity
   dialogs, a full multi-field form rather than a one-line confirm. Overrides .confirm-dialog
   form's own flex-end/margin-top shape (built for a confirm button row) back to a normal stacked
   form. --- */
.wide-dialog {
  max-width: 560px;
  width: 100%;
  padding: 0;
}

.wide-dialog form {
  display: block;
  margin-top: 0;
  padding: 1.25rem 1.25rem 0;
}

.wide-dialog h2 {
  margin: 0;
}

/* Sits directly under a .dialog-field-label with no flex-row wrapper around it (unlike
   .create-space-name-row/.create-space-invite-row's fields below, which rely on that wrapper's
   flex:1 for width) — needs its own block/100% sizing. */
.dialog-text-input {
  display: block;
  width: 100%;
}

.dialog-field-label {
  display: block;
  margin: 1.1rem 0 0.5rem;
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.59375rem;
  font-weight: 500;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--text-label);
}

.create-space-name-row {
  display: flex;
  align-items: center;
  gap: 0.7rem;
}

.create-space-name-row input {
  flex: 1;
}

/* These field families are naked <input>/<select> elements sitting outside a <label> — the app's
   one visual style for text fields (border, radius, padding, focus ring) is scoped to
   "form label input"/"form label select" specifically because every other field in the app is
   laid out as a `<label>Text<input></label>` pair (see that rule far above); these compact rows
   instead put the field directly next to an avatar/button, so they never matched that selector and
   rendered with the bare browser-default look (Peter, 2026-08-25 — flagged as visually
   inconsistent on the space create/edit dialogs, the invite row, and the receipt "flytta till
   space" form). */
.create-space-name-row input,
.create-space-invite-row input[type="text"],
.create-space-invite-row select,
.space-invite-form-row input[type="email"],
.space-invite-form-row select,
.move-to-space-form select,
.dialog-text-input {
  min-height: 44px;
  padding: 0.6rem 0.8rem;
  font-family: inherit;
  font-size: 1rem;
  font-weight: 400;
  color: var(--ink);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  background: var(--surface);
  transition: border-color 0.1s ease, box-shadow 0.1s ease;
}

.create-space-name-row input:focus,
.create-space-invite-row input[type="text"]:focus,
.create-space-invite-row select:focus,
.space-invite-form-row input[type="email"]:focus,
.space-invite-form-row select:focus,
.move-to-space-form select:focus,
.dialog-text-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.move-to-space-form {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-top: 0.75rem;
}

.create-space-swatch-row {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.6rem;
}

.create-space-swatch {
  width: 22px;
  height: 22px;
  min-height: 0;
  padding: 0;
  border-radius: 7px;
  border: none;
  box-shadow: 0 0 0 2px var(--surface);
}

.create-space-swatch.selected {
  box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--ink);
}

.create-space-type-row {
  display: flex;
  gap: 0.5rem;
}

.create-space-type-option {
  /* The radio below it is position:absolute — without this, its containing block becomes the
     dialog itself (shown via showModal(), which the UA renders as position:fixed) instead of this
     label, so each hidden radio stretches to the dialog's full width from its own static-position
     offset and pushes it wider than the dialog, causing a horizontal scrollbar (Peter, 2026-08-25:
     "Redigera space" and "Nytt space" dialogs both had one). */
  position: relative;
  flex: 1;
  padding: 0.6rem 0.65rem;
  border-radius: 9px;
  border: 1px solid var(--hairline);
  background: var(--surface);
  cursor: pointer;
}

.create-space-type-option input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.create-space-type-option:has(input:checked) {
  background: var(--navy);
  border-color: var(--navy);
}

.create-space-type-option:has(input:checked) .create-space-type-name,
.create-space-type-option:has(input:checked) .create-space-type-sub {
  color: #fff;
}

.create-space-type-name {
  display: block;
  font-weight: 700;
  font-size: 0.78125rem;
  color: var(--ink);
}

.create-space-type-sub {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.625rem;
  color: var(--ink-muted);
}

.create-space-invite-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.create-space-invite-row input[type="text"] {
  flex: 1;
}

.create-space-invite-row select {
  width: auto;
}

.create-space-invite-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.6rem;
}

.create-space-invite-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.35rem 0.4rem 0.35rem 0.6rem;
  background: var(--surface-alt);
  border-radius: 999px;
  font-size: 0.75rem;
}

.create-space-invite-chip button {
  min-height: 0;
  padding: 0;
  width: 1.1rem;
  height: 1.1rem;
  border-radius: 50%;
  background: none;
  color: var(--ink-muted);
  font-size: 0.7rem;
}

.dialog-footer-row {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.75rem;
  margin: 1.25rem -1.25rem 0;
  padding: 0.9rem 1.25rem;
  background: var(--canvas);
  border-top: 1px solid var(--hairline);
}

.form-error {
  margin: 0.75rem 0 0;
  color: var(--danger);
  font-size: 0.8125rem;
}

/* --- ⌘K quick-jump (design_handoff_spaces §5.3) --- */
.command-palette {
  width: 100%;
  max-width: 700px;
  padding: 0;
  border: none;
  border-radius: 14px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.35);
  background: var(--surface);
  color: var(--ink);
}

.command-palette::backdrop {
  background: rgba(18, 33, 59, 0.55);
}

.command-palette-search-row {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 1rem 1.1rem;
  border-bottom: 1px solid var(--hairline);
  color: var(--text-label);
}

.command-palette-search-row input {
  flex: 1;
  border: none;
  background: none;
  min-height: 0;
  padding: 0;
  font-size: 0.9375rem;
  color: var(--ink);
}

.command-palette-esc {
  padding: 0.15rem 0.4rem;
  border: 1px solid var(--hairline);
  border-radius: 5px;
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.625rem;
  color: var(--text-label);
}

.command-palette-results {
  max-height: 50vh;
  overflow-y: auto;
  padding: 0.5rem 0 0.25rem;
}

.command-palette-group-label {
  padding: 0.55rem 1.1rem 0.3rem;
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.59375rem;
  font-weight: 500;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--text-label);
}

.command-palette-row {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.55rem 1.1rem;
  cursor: pointer;
}

.command-palette-row-active {
  background: var(--list-hover);
}

.command-palette-row-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.command-palette-row-name {
  font-weight: 600;
  font-size: 0.8125rem;
  color: var(--ink);
}

.command-palette-row-sub {
  font-size: 0.6875rem;
  color: var(--text-label);
}

.command-palette-row-meta {
  font-size: 0.75rem;
  color: var(--ink-muted);
  white-space: nowrap;
}

.command-palette-footer {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.65rem 1.1rem;
  border-top: 1px solid var(--hairline);
  background: var(--canvas);
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.625rem;
  color: var(--text-label);
}

/* --- Tables (stack into cards below the responsive breakpoint) --- */

table {
  width: 100%;
  border-collapse: collapse;
}

.page table th, .page table td {
  text-align: left;
  padding: 0.65rem 0.5rem;
  border-bottom: 1px solid var(--hairline);
}

/* Body cells had no size of their own, so they fell back to the browser's 1rem default — visibly
   larger than every other piece of UI text on the page (buttons, labels, badges all sit in the
   0.85-0.9rem range). 0.875rem matches that same established scale instead of introducing a new
   one-off value. */
.page table td {
  font-size: 0.875rem;
}

.page table th {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-muted);
  font-weight: 700;
}

/* The merchant name is a real link (to the receipt detail page), but reads as just another data
   cell rather than the usual accent-colored link — matching the rest of the row's neutral ink
   color looks calmer/more like a data table and less like a random blue link floating among plain
   text. Applies at every width; the mobile card layout below only adds its own tap-target trick,
   it used to also set this color redundantly for its own breakpoint. */
.receipt-merchant-link {
  color: var(--ink);
  font-weight: 600;
}

/* No underline on hover — the row's own background highlight (see .receipts-table tbody
   tr:hover below) is already the "this is clickable" signal, underline on top was redundant. */
.receipt-merchant-link:hover {
  color: var(--ink);
  text-decoration: none;
}

/* Own bordered/rounded card around just the table (see Receipts.cshtml) — matches the KPI stat
   cards' visual language (--surface/--hairline/13px radius) instead of one big shared card
   wrapping the whole page. overflow:hidden clips the first/last row to the rounded corners. */
.receipts-table-card {
  border: 1px solid var(--hairline);
  border-radius: 13px;
  background: var(--surface);
  overflow: hidden;
}

.receipts-table-card .receipts-table {
  margin: 0;
}

/* More generous than the shared .page table td default — this table now sits alone in its own
   card (no longer sharing the old big .card's own 1.75rem padding), so the row content needs its
   own breathing room from the card's edges. 12px/20px, ~76px rows — tight enough that a receipt
   archive feels like something you scan, not a form (DESIGNGRANSKNING.md #12,
   DESIGNGRANSKNING-2.md #78). */
.receipts-table-card .receipts-table td, .receipts-table-card .receipts-table th {
  padding: 0.75rem 1.25rem;
}

.receipts-table tbody .receipt-merchant-link {
  font-size: 0.9375rem;
}

/* Avoids a doubled-up line where the last row's own border-bottom would otherwise sit flush
   against the card's own bottom border. */
.receipts-table-card .receipts-table tbody tr:last-child td {
  border-bottom: none;
}

/* Whole-row click target, same trick the mobile card layout already used (see
   .receipt-merchant-link::after below): the pseudo-element is positioned against the row (needs
   position: relative here for that), not just the cell the link itself sits in, so clicking
   anywhere in a highlighted row — not just the merchant name text — opens the receipt. */
.receipts-table tbody tr {
  position: relative;
  cursor: pointer;
}

.receipt-merchant-link::after {
  content: "";
  position: absolute;
  inset: 0;
}

/* A second real link within the same row — filters to this category (see Receipts.cshtml.cs's
   Search matching) instead of opening the receipt. Needs position:relative + a z-index above the
   row's own whole-row overlay (.receipt-merchant-link::after, inset:0, no z-index of its own —
   default stacking would otherwise put it on top since it comes later in paint order) so a click
   actually lands on this link rather than falling through to "open the receipt". Text-decoration
   reset since .category-badge already supplies its own color (var(--ink-muted)); a plain <a>
   would otherwise underline on top of that. */
.category-badge-link {
  position: relative;
  z-index: 1;
  text-decoration: none;
}

.category-badge-link:hover {
  background: var(--hairline);
  color: var(--text-2);
}

/* Desktop table only (mobile's card layout already gets its own :active tint below) — a plain
   background tint on hover to make it clear which row you're pointing at before you click through
   to its detail page. */
.receipts-table tbody tr:hover {
  background: var(--canvas);
}

.country-flag {
  display: inline-flex;
  /* Not "middle" — that keyword aligns to the parent's baseline plus half the font's x-height,
     which measured ~2.5px lower than the visual center of a capital letter next to it on both
     usage sites (Receipts.cshtml's merchant-name flag and ReceiptDetail.cshtml's Land row,
     verified directly against getBoundingClientRect() on both). -1px lands within a fraction of a
     pixel of dead-center against cap-height on both, and isn't sensitive to which text sits next
     to the flag the way "middle" is (Peter, 2026-08-27 — flagged as visibly off-center). */
  vertical-align: -1px;
}

.country-flag-svg {
  display: block;
  flex-shrink: 0;
  border-radius: 2px;
  outline: 1px solid var(--overlay-medium);
  outline-offset: -0.5px;
}

/* Land is no longer its own column (DESIGNGRANSKNING.md #9) — shown as a small flag right next
   to the merchant name, and only for merchants outside --receipts-assumed-home-country (SE, see
   ReceiptsModel.AssumedHomeCountry), so it draws the eye only when it's actually informative. */
.country-flag-inline {
  margin-left: 0.4rem;
}

/* --- Sortable column headers (Datum/Summa/Reklamationsrätt till) --- */

.sortable-th {
  padding: 0 !important;
}

.sortable-header {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  width: 100%;
  height: 100%;
  padding: 0.875rem 1.25rem;
  color: inherit;
  text-decoration: none;
}

.sortable-header:hover {
  color: var(--ink);
  text-decoration: none;
}

.sortable-header.active {
  color: var(--ink);
}

.sort-arrow {
  font-size: 0.75rem;
}

.receipts-table th.receipt-col-total, .receipts-table td.receipt-col-total {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.receipts-table th.receipt-col-total .sortable-header {
  justify-content: flex-end;
}

/* --- Reklamationsrätt till: remaining time is the headline, the date is secondary detail
   (DESIGNGRANSKNING.md #10) — this column is the app's whole reason to exist, so it needs to be
   readable at a glance, not a raw ISO date next to a dash. --- */

.complaint-remaining {
  display: block;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.complaint-date {
  display: block;
  font-size: 0.75rem;
  color: var(--text-label);
  font-variant-numeric: tabular-nums;
}

.complaint-na {
  color: var(--muted-value);
}

.complaint-urgent .complaint-remaining {
  color: var(--accent);
}

.complaint-soon .complaint-remaining {
  color: var(--ink);
}

.complaint-muted .complaint-remaining {
  color: var(--text-2);
  font-weight: 400;
}

/* --- Status column badges: öppet köp/garanti (voluntary merchant policy) as small filled pills,
   reklamationsrätt (statutory) as a quieter outline pill last — kept visually distinct per
   CLAUDE.md's "never conflate open purchase with the statutory baseline" rule, and per Peter's
   own preference for several small badges over one bare date column (2026-08-16). --- */
.status-badges {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.25rem;
}

.status-badge {
  display: inline-block;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
  white-space: nowrap;
  border: 1px solid transparent;
}

.status-badge-openpurchase {
  color: var(--accent-hover);
  background: color-mix(in srgb, var(--accent) 14%, var(--surface));
  border-color: color-mix(in srgb, var(--accent) 35%, transparent);
}

.status-badge-warranty {
  color: var(--success);
  background: var(--success-bg);
  border-color: var(--success-border);
}

.status-badge-complaint {
  color: var(--ink-muted);
  background: transparent;
  border-color: var(--hairline);
  font-weight: 500;
}

/* Urgent (< 14 days) overrides the type colour — the deadline matters more than which kind of
   deadline it is once it's this close. Previously reused var(--accent) (brand violet), which read
   as just a bolder shade of .status-badge-openpurchase's own violet rather than a distinct urgency
   signal. Now shares --countdown-urgent (amber/orange) with the countdown view's urgent rows and
   the view-switcher's notification badge, so hero, countdown, and the real receipts list all speak
   the same colour for "act soon". */
.status-badge.complaint-urgent {
  color: var(--countdown-urgent);
  background: color-mix(in srgb, var(--countdown-urgent) 14%, var(--surface));
  border-color: color-mix(in srgb, var(--countdown-urgent) 35%, transparent);
}

/* --- Empty / no-results states (DESIGNGRANSKNING.md #14) --- */

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.5rem;
  padding: 3.5rem 1.5rem;
  color: var(--hairline);
}

.empty-state h2 {
  margin: 0.5rem 0 0;
  font-size: 1.125rem;
  color: var(--ink);
}

.empty-state p {
  margin: 0;
  max-width: 34ch;
  color: var(--ink-muted);
}

.empty-state .receipts-add-button, .empty-state .secondary-button {
  margin-top: 0.5rem;
}

/* --- Admin: deliberately plain, no theming --- */

body.admin {
  background: #fff;
}

.admin-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 2rem;
  padding: 1rem 1.5rem;
  border-bottom: 1px solid #ddd;
  background: #fafafa;
}

.admin-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
}

.admin-nav a {
  color: #333;
  text-decoration: none;
}

.admin-nav a:hover {
  text-decoration: underline;
}

.admin-main {
  max-width: 960px;
  margin: 1.5rem auto;
  padding: 0 1.5rem;
}

.admin-main table {
  width: 100%;
  border-collapse: collapse;
}

.admin-main th, .admin-main td {
  text-align: left;
  padding: 0.5rem 0.6rem;
  border-bottom: 1px solid #eee;
  font-size: 0.95rem;
}

.admin-main th {
  color: #666;
  font-weight: 600;
  border-bottom: 2px solid #ddd;
}

/* --- Calendar view (Mina kvitton alternate layout) --- */

/* Same card family as .stat-card/.receipts-table-card — title/nav/grid grouped into one tile
   instead of the old shared big .card wrapping the whole page. */
.calendar-card {
  border: 1px solid var(--hairline);
  border-radius: 13px;
  background: var(--surface);
  padding: 1.5rem;
}

/* One tight cluster — ‹ arrow, month title, › arrow, optional "Idag" — rather than the arrows
   pinned to opposite edges with the title floating alone above them (DESIGNGRANSKNING.md #19). */
.calendar-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-start;
  gap: 0.5rem;
  margin-bottom: 1.5rem;
}

.calendar-title {
  text-transform: capitalize;
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 0 0.25rem 0 0;
}

.calendar-nav-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 999px;
  color: var(--ink-muted);
  font-size: 1.25rem;
  line-height: 1;
  text-decoration: none;
}

.calendar-nav-arrow:hover {
  background: var(--overlay-weak);
  color: var(--ink);
  text-decoration: none;
}

.calendar-today-button {
  margin-left: 0.5rem;
}

/* Native <input type="month"> — a direct jump on top of the ‹ › click-through nav, for when the
   target month is many clicks away (DESIGNGRANSKNING-2.md feedback: a 5-year-old receipt was 60
   clicks). Same field styling as the date-range inputs elsewhere in the app. */
.calendar-jump input {
  height: 32px;
  padding: 0 0.5rem;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 0.8rem;
}

.calendar-jump input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

/* --- Agenda view — one heading per date with a receipt, not a grid of mostly-empty cells
   (DESIGNGRANSKNING.md #18). --- */

.calendar-agenda {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

.calendar-agenda-date {
  margin: 0 0 0.5rem;
  font-size: 0.6875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-label);
}

.calendar-agenda-day + .calendar-agenda-day {
  padding-top: 1.25rem;
  border-top: 1px solid var(--border-inner);
}

.calendar-agenda-receipt {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.6rem 0.25rem;
  color: var(--ink);
  text-decoration: none;
  border-radius: 8px;
}

.calendar-agenda-receipt:hover {
  background: var(--canvas);
  text-decoration: none;
}

.calendar-agenda-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
}

/* Keeps merchant names lined up in a column whether or not a given row has the urgency dot —
   an invisible same-size spacer, not a visible "nothing to see here" marker. */
.calendar-agenda-dot-placeholder {
  width: 6px;
  flex-shrink: 0;
}

.calendar-agenda-end {
  margin: 1.25rem 0 0;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border-inner);
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  color: var(--text-label);
  font-size: 0.8125rem;
}

.calendar-agenda-end a {
  font-weight: 600;
}

.calendar-agenda-merchant {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
}

.calendar-agenda-total {
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
  color: var(--ink-muted);
}

/* --- Responsive --- */

@media (max-width: 640px) {
  .hero {
    /* 16px top+bottom + 24px logo = 56px header (DESIGNGRANSKNING.md #3/#25). */
    padding: 1rem;
  }

  /* Tighter than the desktop 1.35rem column-gap — on some phone widths the row (brand + theme +
     language + the logged-out CTA) was close enough to the edge that a longer CTA label alone
     decided whether it wrapped onto its own line: "Skapa konto"/"Opret konto" (login page's CTA)
     is ~23px wider than "Logga in"/"Log ind" (every other page's), so the header wrapped on one
     but not the other at the exact same viewport width (Peter, 2026-09-11 — "top bar på publika
     och login sidan skiljer... radbryts", confirmed via measured link widths, not a layout bug).
     Buys back enough margin to cover that gap for the languages checked; not a hard guarantee for
     every one of the 10 languages' longest strings, but meaningfully narrows how often this shows
     up at all rather than only patching the one reported case. */
  .site-nav {
    gap: 0.35rem 0.6rem;
  }

  .site-nav a.login-link {
    padding-left: 0.45rem;
    padding-right: 0.45rem;
  }

  .brand-logo {
    height: 24px;
  }

  /* Same tallest-child fix as desktop (see .nav-menu/.lang-menu/.theme-menu/.user-menu's own
     comment) — 24px here, matching the mobile logo, so 16px padding + 24px = 56px header for
     real, not 16+40=72px like before. */
  .nav-menu, .lang-menu, .theme-menu, .user-menu {
    height: 1.5rem;
  }

  .nav-menu summary, .theme-menu summary {
    width: 1.5rem;
    height: 1.5rem;
  }

  .lang-menu summary {
    height: 1.5rem;
    padding: 0.2rem 0.4rem;
  }

  /* Otherwise a 30px avatar would still overflow .user-menu's new 24px box on mobile. */
  .avatar {
    width: 24px;
    height: 24px;
    font-size: 0.65rem;
  }

  /* Takes disproportionate space on a small screen for a dev-only signal — omitted entirely on
     mobile rather than shrunk further (DESIGNGRANSKNING.md #25 offers both options; the badge is
     never the reason someone opens the app, so dropping it costs nothing here). */
  .env-badge {
    display: none;
  }

  /* Hidden in the topbar on mobile, same reasoning as .env-badge above (no room in the fixed
     one-row header) — design_handoff_spaces §5.8 puts the mobile equivalent as its own full-width
     row directly under the page title instead, not squeezed into this header. Scoped to
     .hero-inner specifically — .space-switcher is the same class on BOTH the desktop pill and the
     mobile full-width row (_SpaceContextSwitcher.cshtml renders twice, see _Layout.cshtml), so an
     unscoped rule here would hide the mobile row too, not just this one. */
  .hero-inner .space-switcher, .space-switcher-divider {
    display: none;
  }

  /* --- Mobile context row + bottom sheet (design_handoff_spaces §5.8) --- */
  .space-switcher-mobile-row {
    display: block;
    padding: 0.75rem 1rem 0;
  }

  .space-switcher-mobile-row .space-switcher-trigger {
    width: 100%;
    justify-content: flex-start;
  }

  .space-switcher-mobile-row .space-switcher-name {
    max-width: none;
    flex: 1;
    text-align: left;
  }

  /* The panel becomes a bottom sheet at this width — fixed to the viewport, not anchored under
     the trigger the way the desktop dropdown is. */
  .space-switcher-mobile-row .space-switcher-panel {
    position: fixed;
    inset: auto 0 0 0;
    width: 100%;
    max-width: none;
    border-radius: 20px 20px 0 0;
    box-shadow: 0 -12px 30px rgba(var(--shadow-rgb), 0.18);
    max-height: 75vh;
    overflow-y: auto;
  }

  /* Draggable-looking handle, purely decorative (no actual drag-to-dismiss) — signals "this is a
     sheet" the way it does in every native mobile bottom sheet. */
  .space-switcher-mobile-row .space-switcher-panel::before {
    content: "";
    display: block;
    width: 38px;
    height: 4px;
    margin: 10px auto 2px;
    border-radius: 999px;
    background: var(--hairline);
  }

  /* Same dimming scrim as body.nav-menu-open — see header-menus.js's toggle listener on the
     mobile switcher instance specifically. */
  body.space-switcher-sheet-open::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: 19;
    background: rgba(15, 19, 48, 0.55);
  }

  /* --- Bottom tab bar (design_handoff_spaces §5.8) — Kvitton / + Nytt / Spaces. Context
     switching happens only via the row above, never here. --- */
  .bottom-tab-bar {
    display: flex;
    align-items: center;
    justify-content: space-around;
    position: fixed;
    inset: auto 0 0 0;
    z-index: 20;
    padding: 0.7rem 0.5rem calc(0.7rem + env(safe-area-inset-bottom, 0px));
    background: var(--canvas);
    border-top: 1px solid var(--hairline);
  }

  .bottom-tab-bar-item {
    font-weight: 700;
    font-size: 0.71875rem;
    color: var(--ink-muted);
  }

  .bottom-tab-bar-cta {
    padding: 0.55rem 1.1rem;
    border-radius: 999px;
    background: var(--navy);
    color: #fff;
    font-weight: 700;
    font-size: 0.75rem;
  }

  .bottom-tab-bar-cta:hover {
    text-decoration: none;
    background: var(--navy-hover);
    color: #fff;
  }

  .page, .admin-main {
    padding: 0 1rem;
  }

  /* Room for the fixed bottom tab bar so it never overlaps the page's own last row of content —
     after the shared .page rule above so its padding-bottom override actually sticks (same
     specificity, later source order wins). */
  .page-with-bottom-nav {
    padding-bottom: 4.5rem;
  }

  /* The default row layout (copyright left, nav links right, space-between) has nowhere to put
     the second item once it wraps to its own line below the first — space-between leaves a lone
     wrapped item flush left, so it reads as randomly stacked rather than a deliberate mobile
     footer. Centering both turns that same wrap into a normal, tidy stacked layout. */
  .site-footer-columns {
    grid-template-columns: 1fr 1fr;
  }

  .site-footer-inner {
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 0.6rem;
  }

  .footer-nav {
    justify-content: center;
  }

  /* Below this width "Lägg till kvitton" + "Mina kvitton" as plain inline links wrapped the header
     onto 2-3 rows, and the row count differed by language (English text is shorter, so it wrapped
     differently than Swedish) — an unstable header height that changed depending on UI language.
     Collapsing them into the hamburger keeps the header exactly one row on phones, always.
     The account avatar folds in too (its content is duplicated into .nav-menu-panel) so the row is
     just brand — theme — language — hamburger, in that fixed order, however wide any of their
     labels get to be. Theme and language stay directly visible rather than folding into the
     hamburger too — both are icon-only/compact enough to always fit, and switching either is
     common enough to not want an extra tap to reach.
     .login-link is deliberately NOT folded in here — it's only ever rendered for logged-out
     visitors, for whom the hamburger doesn't exist at all (see _Layout.cshtml), so there's nothing
     to hide it in favour of: a one-item menu is worse than the link itself (Peter, 2026-08-18). */
  .nav-menu {
    display: flex;
    order: 3;
  }

  .user-menu {
    display: none;
  }

  .theme-menu {
    order: 1;
  }

  .lang-menu {
    order: 2;
  }

  .receipts-toolbar {
    row-gap: 0.35rem;
    margin-bottom: 1rem;
  }

  .card {
    padding: 1.35rem 1.15rem;
  }

  /* Two explicit actions (Ta foto / Välj från galleriet) instead of one combined label
     (DESIGNGRANSKNING-2.md #70) — see Capture.cshtml. */
  .capture-actions-desktop {
    display: none;
  }

  .capture-actions-mobile {
    display: flex;
  }

  .recovery-codes {
    grid-template-columns: 1fr;
  }

  /* Dependency-free stacked-card table: each row becomes its own block, each cell shows its
     column header (via the data-label attribute set in the Razor markup) instead of relying on
     a <thead> row that would otherwise be squeezed unreadably narrow. */
  .stacking-table thead, .admin-main table thead {
    display: none;
  }

  .stacking-table, .stacking-table tbody, .stacking-table tr,
  .admin-main table, .admin-main table tbody, .admin-main table tr {
    display: block;
    width: 100%;
  }

  .stacking-table tr, .admin-main table tr {
    margin-bottom: 1rem;
    border: 1px solid var(--hairline);
    border-radius: 10px;
    padding: 0.5rem 0.75rem;
  }

  .stacking-table td, .admin-main table td {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--canvas);
    text-align: right;
  }

  .stacking-table td:last-child, .admin-main table td:last-child {
    border-bottom: none;
  }

  /* --ink-muted (not a hardcoded grey) — this table's headers are themed everywhere else (desktop
     shows them via a real <thead>, using the same token), so the mobile stacked-card labels need
     to follow light/dark mode too. A fixed #555 read fine in light mode by coincidence but was
     illegible-ish and visibly mismatched against dark mode's much lighter body text. */
  .stacking-table td::before {
    content: attr(data-label);
    font-weight: 600;
    text-align: left;
    color: var(--ink-muted);
  }

  /* .admin-main table deliberately stays plain/unthemed (see "Admin: deliberately plain, no
     theming" above) — kept as its own rule with the fixed grey, not merged with .stacking-table. */
  .admin-main table td[data-label]::before {
    content: attr(data-label);
    font-weight: 600;
    text-align: left;
    color: #555;
  }

  /* Receipts list — a purpose-built compact card, not the generic label/value stacking-table
     above: that pattern put all 6 columns on their own row each, so a single receipt took up to a
     full screen. Here every card is capped at two lines (merchant + total, then category + date);
     line count and complaint-deadline are dropped entirely — useful on the detail page, just noise
     when scanning a list. The whole card is one tap target (via .receipt-merchant-link::after)
     instead of only the small merchant-name text, with a trailing chevron as the visual cue. */
  .receipts-table thead {
    display: none;
  }

  /* Each row is already its own bordered/rounded card at this width (below) — .receipts-table-card
     would otherwise double-box them with a second outer border/background wrapping the whole
     gapped stack, which reads as a mistake, not a second layer of hierarchy. */
  .receipts-table-card {
    border: none;
    background: none;
    overflow: visible;
  }

  .receipts-table-card .receipts-table td, .receipts-table-card .receipts-table th {
    padding: 0;
  }

  .receipts-table, .receipts-table tbody {
    display: block;
    width: 100%;
  }

  .receipts-table tr {
    position: relative;
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    column-gap: 0.6rem;
    row-gap: 0.3rem;
    margin-bottom: 0.6rem;
    padding: 0.75rem 1.5rem 0.75rem 0.85rem;
    border: 1px solid var(--hairline);
    border-radius: 10px;
    background: var(--surface);
  }

  .receipts-table tr:active {
    background: var(--canvas);
  }

  /* CSS-only chevron (a rotated corner, no icon font/SVG needed) — the one visual hint, besides
     the whole card being tappable, that this row leads somewhere. */
  .receipts-table tr::after {
    content: "";
    position: absolute;
    right: 0.85rem;
    top: 50%;
    width: 7px;
    height: 7px;
    border-top: 1.5px solid var(--ink-muted);
    border-right: 1.5px solid var(--ink-muted);
    transform: translateY(-50%) rotate(45deg);
  }

  /* .page .receipts-table td (not just .receipts-table td) — needs to out-specificity the base
     ".page table td { border-bottom: ... }" rule, which otherwise still wins and leaves a stray
     divider line through the middle of every card. */
  .page .receipts-table td {
    padding: 0;
    border: none;
  }

  .receipts-table td::before {
    content: none;
  }

  .receipts-table .receipt-col-complaint {
    display: none;
  }

  .receipts-table .receipt-col-merchant {
    grid-column: 1 / 3;
    grid-row: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .receipts-table .receipt-col-total {
    grid-column: 3;
    grid-row: 1;
    text-align: right;
    font-weight: 700;
    white-space: nowrap;
  }

  .receipts-table .receipt-col-category {
    grid-column: 1;
    grid-row: 2;
  }

  .receipts-table .receipt-col-date {
    grid-column: 2 / 4;
    grid-row: 2;
    color: var(--ink-muted);
    font-size: 0.8rem;
  }
}

/* --- Receipts KPI row + header actions ("Filtrera"/"Lägg till kvitto") ---
   Sizing/radius/colors match design_handoff_login_transition's own
   "Rceep Login Transition.dc.html" app-shell reference as closely as the app's real (non-
   placeholder) header allows — see that file if these values ever need re-checking. */

/* Desktop only (min-width, not the usual max-width:640px) — this ID selector otherwise beats the
   shared .page-header-row's own mobile `flex-direction: column` rule on specificity alone, which
   turned "align-items: flex-end" into "everything pushed to the RIGHT edge" once the row became a
   column at mobile widths (align-items governs the cross axis, which flips from vertical to
   horizontal when flex-direction flips) — H1, kicker and the whole toolbar row all read as
   right-aligned (DESIGNGRANSKNING-2.md #67). Desktop keeps the original intent (buttons aligned to
   the heading's own baseline, not its top); mobile falls back to the shared class's left-aligned
   default. */
@media (min-width: 641px) {
  #receiptsHeaderRow {
    align-items: flex-end;
  }
}

.page-header-heading {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.receipts-kicker {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-family: "DM Mono", ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* Small identity chip prefixing the kicker when viewing a space's receipts (design_handoff_spaces
   §5.2 item 1 — "Kontextrubrik på varje listsida") — same .space-avatar shape used everywhere else
   a space is referenced, just smaller. */
.receipts-kicker .space-avatar {
  width: 18px;
  height: 18px;
  font-size: 8px;
  border-radius: 6px;
}

/* "Ingen indikation i UI om varför en knapp saknas" (design_handoff_spaces §5.2 item 2,
   _ViewerNotice.cshtml) — a Viewer sees this explaining why edit/delete/share buttons are simply
   absent, instead of the feature silently seeming not to exist. Amber, not --danger — nothing's
   wrong, it's informational. */
.viewer-notice {
  display: flex;
  margin: 0.75rem 0;
  padding: 0.75rem 0.875rem;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-left: 3px solid var(--pending-border);
  border-radius: 9px;
  font-size: 0.78125rem;
  line-height: 1.5;
  color: var(--ink-muted);
}

.viewer-notice strong {
  color: var(--ink);
}

/* Primary — deliberately navy (matches the reference's own accentColor), not var(--accent), even
   though every other primary action in the app uses the indigo accent. --navy has no dark-theme
   override (see :root above), so this stays a stable, always-dark button regardless of theme —
   intentional here, not an oversight. */
.receipts-add-button {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  border-radius: 9px;
  padding: 0.625rem 1rem;
  font-size: 0.8125rem;
  font-weight: 600;
  background: var(--navy);
  color: #ffffff;
  text-decoration: none;
  white-space: nowrap;
}

/* color AND text-decoration repeated here, not just inherited from the base rule above — the
   global "a:hover { color: var(--accent-hover); text-decoration: underline }" reset (line ~1216)
   ties this on specificity (both are class/tag + :hover) and wins, so an <a class="receipts-add-
   button"> (Receipts.cshtml/ReceiptsCountdown.cshtml/Spaces/Index.cshtml all use one, not just
   <button>) got blue text and a stray underline on hover instead of staying a plain white label
   (Peter, 2026-08-28: "vissa knappar har får en underline vid hover vissa inte"). */
.receipts-add-button:hover {
  background: var(--navy-hover);
  color: #ffffff;
  text-decoration: none;
}

/* --navy is deliberately stable across themes (see comment above), which meant this button went
   navy-on-navy and effectively disappeared once the page background itself turned dark
   (DESIGNGRANSKNING.md #4). Dark mode swaps it to the vivid, also-theme-stable indigo instead —
   still a solid, confident fill, just one that actually shows up against a dark canvas. */
:root[data-theme="dark"] .receipts-add-button {
  background: var(--accent-solid);
  color: var(--ink);
}

:root[data-theme="dark"] .receipts-add-button:hover {
  background: var(--accent-solid-hover);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .receipts-add-button {
    background: var(--accent-solid);
    color: var(--ink);
  }

  :root:not([data-theme]) .receipts-add-button:hover {
    background: var(--accent-solid-hover);
  }
}

/* Only the two time-sensitive counters live here now (öppet köp, garantier) — "Sparade kvitton"
   was a plain counter, not a KPI, and moved to .receipts-count-line above the table instead
   (DESIGNGRANSKNING.md #8). Two wider cards read as more deliberate than three cramped ones. */
/* flex, not a stretching 2-column grid — two small numbers don't need to fill the page's full
   width, and left-packing them (rather than spreading them edge to edge) reads as more deliberate
   (DESIGNGRANSKNING-2.md #79). */
.stat-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 0.875rem;
  margin-bottom: 1.5rem;
}

/* A real filter now, not decoration — an <a>, so it
   needs its own text-decoration/color reset since it's no longer a plain <div>. */
.stat-card {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  flex: 1 1 260px;
  max-width: 340px;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 13px;
  padding: 1.125rem 1.25rem;
  text-decoration: none;
  color: inherit;
  font-weight: 400;
  transition: border-color 0.1s ease, background-color 0.1s ease;
}

.stat-card:hover {
  border-color: var(--ink-muted);
  text-decoration: none;
}

.stat-card[aria-pressed="true"] {
  border-color: var(--accent);
  background: var(--surface-alt);
}

.stat-label {
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-label);
}

.stat-value {
  font-size: 1.6875rem;
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--ink);
}

.stat-value-accent {
  color: var(--accent);
}

/* A zero isn't the same kind of information as a real count — rendering it in the same weight
   and accent colour as an actual value made an empty state look like data worth double-checking
   (DESIGNGRANSKNING.md #8). Muted colour + a short reassurance line underneath instead. */
.stat-value-zero {
  color: var(--muted-value);
}

.stat-helper {
  font-size: 0.75rem;
  color: var(--text-label);
}

/* The relocated "Sparade kvitton" counter — a plain line above the table, not a KPI card. */
.receipts-count-line {
  margin: 0 0 0.75rem;
  color: var(--ink-muted);
  font-size: 0.875rem;
}

@media (max-width: 640px) {
  /* Two cards side by side (not stacked full-width) but still shrunk — full-size stat cards
     stacked vertically ate a big chunk of a phone screen for not much information. */
  .stat-grid {
    gap: 0.5rem;
  }

  .stat-card {
    /* The comment above says "side by side", but the base rule's flex: 1 1 260px never got
       overridden here — two cards at a 260px minimum basis can't fit a ~390px phone screen, so
       flex-wrap: wrap (from .stat-grid) forced them onto separate rows instead (Peter, 2026-08-18).
       flex-basis: 0 splits the row evenly between exactly two cards instead of each demanding its
       desktop minimum width. */
    flex: 1 1 0;
    max-width: none;
    padding: 0.625rem 0.7rem;
    gap: 0.2rem;
  }

  .stat-label {
    font-size: 0.5625rem;
    letter-spacing: 0.05em;
  }

  .stat-value {
    font-size: 1.15rem;
  }

  /* H1 gets its own full-width row instead of squeezing against Filtrera/Lägg till kvitto on the
     same line (DESIGNGRANSKNING.md #26). */
  .page-header-row {
    flex-direction: column;
    align-items: flex-start;
  }

  /* Full width, wraps as needed (view-switcher / secondary export link / add button) — the bottom
     tab bar's own "+ Nytt" (see .bottom-tab-bar-cta) is now the persistent mobile entry point for
     creating a receipt, so this row no longer needs to carve out special room for a second,
     competing "+" control (see .receipts-add-button below, no longer a FAB here — Peter,
     2026-08-25: having both a floating "+" FAB and the tab bar's own "+ Nytt" on screen at once
     read as two controls for one action). */
  .page-header-actions {
    width: 100%;
    justify-content: space-between;
  }

  /* Kontoadministration's five tabs never fit a phone-width row unwrapped (Peter, 2026-09-03:
     "Keys" sitting flush against the right edge on staging) — .internal-subnav needed ~471px at
     a 390px viewport's ~358px content width. Wraps onto two rows instead of a horizontal-scroll
     tab strip, since every tab stays visible without a swipe gesture on a settings page. */
  .internal-subnav {
    flex-wrap: wrap;
  }

  /* ReceiptDetail's action row (Skriv ut / ⋯) has no natural "left" item the way Receipts.cshtml's
     view-switcher does — right-aligned as a pair under H1 instead (DESIGNGRANSKNING-2.md #81). */
  #receiptDetailHeaderRow .page-header-actions {
    width: auto;
    align-self: flex-end;
  }

  /* Wider image, shown alongside its own caption instead of a tiny corner thumbnail
     (DESIGNGRANSKNING-2.md #74). */
  .receipt-image-hint {
    display: block;
  }

  /* Status badges as a third line on the mobile receipt card, but only when there's actually a
     badge to show — the "Gäller inte" fallback text carries no decision-relevant information on
     a cramped phone card, same reasoning as the old urgent-only rule this replaces
     (DESIGNGRANSKNING.md #28). */
  .receipts-table .receipt-col-status {
    display: none;
  }

  .receipts-table .receipt-col-status:has(.status-badges) {
    display: block;
    grid-column: 1 / 4;
    grid-row: 3;
    margin-top: 0.1rem;
  }
}

/* --- Login "melt" transition overlay (see Pages/Shared/_LoginTransition.cshtml,
   wwwroot/js/login-transition.js, design_handoff_login_transition/README.md) ---
   Logo mark only, no wordmark text — the handoff's own ordbild/kicker block was dropped, so
   there's no .rc-word here and no webfont dependency. Sizes/timing copied from the handoff's
   login-transition.html reference, which specifies ONE palette: --rc-navy sheet melting away,
   --rc-paper bars, --rc-lilac as the third bar's accent — see design_handoff_login_transition/
   login-transition.html. The sheet is the element the goo filter dissolves; it must always
   CONTRAST against the page being revealed underneath, or the melt reads as a blurry smear with
   nothing visible happening (this broke once already — see git history — when --rc-sheet got set
   to the same paper tone as the light-theme page background instead of navy). Dark theme's
   palette is this app's own extrapolation (the handoff has no dark variant): the same contrast
   rule just inverted, since the dark page background is itself navy-dark. */
/* Was style="position:absolute" — silently dropped by the response's style-src 'self' CSP (see
   Program.cs), though harmless here since the SVG is 0x0 either way and takes no layout space. */
.rc-goo-defs {
  position: absolute;
}

.rc-transition {
  /* Light-mode default: a light sheet with dark bars, matching the light page it's covering.
     These two blocks had the light/dark value sets swapped — light mode played a dark-navy
     overlay and dark mode played a light-cream one, backwards from the theme actually underneath
     (Peter, 2026-08-19). */
  --rc-t: 2600ms;
  --rc-sheet: #faf8f3;
  --rc-bar: #12143a;
  --rc-bar-accent: #5b5fe0;
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
}

:root[data-theme="dark"] .rc-transition {
  --rc-sheet: #12143a;
  --rc-bar: #faf8f3;
  --rc-bar-accent: #a9abf5;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .rc-transition {
    --rc-sheet: #12143a;
    --rc-bar: #faf8f3;
    --rc-bar-accent: #a9abf5;
  }
}

.rc-melt {
  position: absolute;
  inset: 0;
  filter: url(#rc-goo);
  animation: rc-melt var(--rc-t) cubic-bezier(0.72, 0, 0.22, 1) both;
}

.rc-sheet {
  position: absolute;
  inset: -2px 0 0;
  background: var(--rc-sheet);
}

.rc-drips {
  position: absolute;
  top: 100%;
  left: -4%;
  right: -4%;
  height: 0;
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
}

.rc-drip {
  border-radius: 50%;
  background: var(--rc-sheet);
  transform-origin: 50% 0;
  animation: rc-drip var(--rc-t) cubic-bezier(0.7, 0, 0.25, 1) both;
}

.rc-mark {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.rc-bars {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
}

.rc-bar {
  height: 22px;
  border-radius: 11px;
  background: var(--rc-bar);
}

.rc-bar-1 {
  width: 112px;
  animation: rc-b1 var(--rc-t) cubic-bezier(0.6, 0, 0.3, 1) both;
}

.rc-bar-2 {
  width: 76px;
  animation: rc-b2 var(--rc-t) cubic-bezier(0.6, 0, 0.3, 1) both;
}

.rc-bar-3 {
  width: 112px;
  background: var(--rc-bar-accent);
  animation: rc-b3 var(--rc-t) cubic-bezier(0.6, 0, 0.3, 1) both;
}

/* App content underneath — only ever animated when login-transition.js has decided to actually
   play (see that file); otherwise these classes are never added, and the content is just plainly
   visible, no dependency on JS running at all. */
.rc-reveal {
  animation: rc-reveal var(--rc-t, 2200ms) cubic-bezier(0.33, 1, 0.68, 1) both;
}

.rc-rise {
  animation: rc-rise 520ms cubic-bezier(0.33, 1, 0.68, 1) both;
}

@keyframes rc-melt {
  0%, 56% { transform: translateY(0); }
  100% { transform: translateY(118%); }
}

@keyframes rc-drip {
  0%, 50% { transform: translateY(0) scaleY(1); }
  68% { transform: translateY(26px) scaleY(1.75); }
  100% { transform: translateY(90px) scaleY(1); }
}

/* Bars used to hold in the intact logo formation for only ~16%-32% (roughly 350ms of the full
   run) before scattering and fading well before the sheet even started melting away at 56% — the
   mark was gone before the reveal even began. Now holds until ~55%, right up to where rc-melt
   starts sliding away, and doesn't fully fade until 92% — so the logo stays visible while the
   content underneath is actually being revealed, not just as a brief flash beforehand
   (Peter, 2026-08-19: "tanken är att man skall kunna se loggan också"). */
@keyframes rc-b1 {
  0% { transform: translateY(-72px); opacity: 0; }
  14% { transform: translateY(0); opacity: 1; }
  55% { transform: translateY(0); }
  72% { transform: translateY(42px); }
  80% { transform: translateY(42px) scale(1); opacity: 1; }
  92%, 100% { transform: translateY(42px) scale(1.9); opacity: 0; }
}

@keyframes rc-b2 {
  0% { transform: scaleX(0.45); opacity: 0; }
  18% { transform: scaleX(1); opacity: 1; }
  70% { transform: scale(1); opacity: 1; }
  92%, 100% { transform: scale(1.9); opacity: 0; }
}

@keyframes rc-b3 {
  0% { transform: translateY(72px); opacity: 0; }
  14% { transform: translateY(0); opacity: 1; }
  55% { transform: translateY(0); }
  72% { transform: translateY(-42px); }
  80% { transform: translateY(-42px) scale(1); opacity: 1; }
  92%, 100% { transform: translateY(-42px) scale(1.9); opacity: 0; }
}

@keyframes rc-reveal {
  0%, 54% { opacity: 0; filter: blur(22px); transform: scale(0.985); }
  82%, 100% { opacity: 1; filter: blur(0); transform: scale(1); }
}

@keyframes rc-rise {
  0% { opacity: 0; transform: translateY(14px); }
  100% { opacity: 1; transform: translateY(0); }
}

@keyframes rc-fade-out {
  to { opacity: 0; }
}

@keyframes rc-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Two ways into the same fallback: an OS-level motion preference (media query, required for
   accessibility regardless of what JS decides) or login-transition.js's own low-power-device
   check (.rc-reduced, added via JS — navigator.hardwareConcurrency isn't a CSS media feature).
   Same substitution either way: a quick 320ms crossfade, no goo filter, no bars/drips motion. */
@media (prefers-reduced-motion: reduce) {
  .rc-melt {
    animation: rc-fade-out 320ms linear both;
    filter: none;
  }

  .rc-bar, .rc-drip {
    animation: none;
  }

  .rc-reveal, .rc-rise {
    animation: rc-fade-in 320ms linear both;
  }
}

/* .rc-reduced is added to <body>, not the overlay itself — .rc-reveal/.rc-rise live on the page
   content, outside the overlay's own DOM subtree, so it needs an ancestor both share. */
body.rc-reduced .rc-melt {
  animation: rc-fade-out 320ms linear both;
  filter: none;
}

body.rc-reduced .rc-bar, body.rc-reduced .rc-drip {
  animation: none;
}

body.rc-reduced .rc-reveal, body.rc-reduced .rc-rise {
  animation: rc-fade-in 320ms linear both;
}

/* --- Nedräkning (countdown view) — replaces the old month-agenda
   calendar: the only time-dependent thing in this product is when a protection runs out, so the
   view is built around that instead of purchase date. Reuses this file's own tokens where they
   already fit (--ink/--surface/--hairline etc.); the summary band and per-type accent colours are
   new, purpose-specific values with their own light/dark pair, same pattern as the startsida's
   --landing-* tokens. --- */
:root {
  --countdown-band-bg: #12143a;
  --countdown-openpurchase: #5b5fe0;
  --countdown-openpurchase-bg: #efeffc;
  --countdown-warranty: #2e8b67;
  --countdown-warranty-bg: #eaf4ef;
  /* Deliberately NOT the same hex as --countdown-urgent below — the spec's own #C2521F for both
     meant every reklamationsrätt row looked identically "urgent" whether it had 6 days or 3 years
     left, since reklamationsrätt is the one protection nearly every receipt has (Peter, 2026-08-16:
     "alla nedräkningar fått samma röda färg"). A muted amber keeps its own identity distinct from
     true urgency, which is the entire point of the urgent override existing at all. */
  --countdown-statutory: #8a6d2e;
  --countdown-statutory-bg: #f5f0e2;
  --countdown-urgent: #c2521f;
  --countdown-card-border: #e9e4d6;
  --countdown-card-border-hover: #c9c4b2;
}

:root[data-theme="dark"] {
  --countdown-band-bg: #171a3c;
  --countdown-openpurchase: #8a8df0;
  --countdown-openpurchase-bg: rgba(138, 141, 240, 0.14);
  --countdown-warranty: #4fb98c;
  --countdown-warranty-bg: rgba(79, 185, 140, 0.14);
  --countdown-statutory: #cbaa5c;
  --countdown-statutory-bg: rgba(203, 170, 92, 0.14);
  --countdown-urgent: #e5794a;
  --countdown-card-border: #2e3268;
  --countdown-card-border-hover: #454a94;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --countdown-band-bg: #171a3c;
    --countdown-openpurchase: #8a8df0;
    --countdown-openpurchase-bg: rgba(138, 141, 240, 0.14);
    --countdown-warranty: #4fb98c;
    --countdown-warranty-bg: rgba(79, 185, 140, 0.14);
    --countdown-statutory: #cbaa5c;
    --countdown-statutory-bg: rgba(203, 170, 92, 0.14);
    --countdown-urgent: #e5794a;
    --countdown-card-border: #2e3268;
    --countdown-card-border-hover: #454a94;
  }
}

.countdown-summary-band {
  background: var(--countdown-band-bg);
  border-radius: 14px;
  padding: 20px 24px;
  margin-bottom: 1.5rem;
}

.countdown-summary-heading {
  margin: 0 0 0.3rem;
  font-size: 1.375rem;
  font-weight: 700;
  color: #faf8f3;
}

.countdown-summary-sub {
  margin: 0;
  font-size: 0.9375rem;
  color: #a5a8c4;
}

.countdown-filter-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-bottom: 1.5rem;
}

.countdown-filter-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.countdown-chip {
  display: inline-flex;
  align-items: center;
  padding: 0.4rem 0.85rem;
  border-radius: 999px;
  border: 1px solid var(--hairline);
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--ink-muted);
  text-decoration: none;
}

.countdown-chip:hover {
  color: var(--ink);
  border-color: var(--countdown-card-border-hover);
  text-decoration: none;
}

.countdown-chip.active {
  background: var(--navy);
  border-color: var(--navy);
  color: #faf8f3;
}

:root[data-theme="dark"] .countdown-chip.active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

.countdown-filter-note {
  font-size: 0.8125rem;
  color: var(--text-label);
}

.countdown-group {
  margin-bottom: 1.75rem;
}

.countdown-group-heading {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding-bottom: 0.5rem;
  margin-bottom: 0.75rem;
  border-bottom: 1px solid var(--hairline);
  font-size: 0.8125rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.countdown-group-urgent {
  color: var(--countdown-urgent);
}

.countdown-group-muted {
  color: var(--text-label);
}

.countdown-group-count {
  font-family: "DM Mono", ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.6875rem;
  color: var(--text-label);
  text-transform: none;
  letter-spacing: normal;
  font-weight: 500;
}

.countdown-rows {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.countdown-row {
  display: grid;
  grid-template-columns: 40px 1fr 200px 120px 136px;
  align-items: center;
  gap: 20px;
  padding: 16px 20px;
  border: 1px solid var(--countdown-card-border);
  border-radius: 14px;
  background: var(--surface);
  text-decoration: none;
  color: inherit;
  transition: border-color 0.15s ease;
}

.countdown-row:hover {
  border-color: var(--countdown-card-border-hover);
  text-decoration: none;
}

.countdown-row-badge {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 10px;
  font-family: "DM Mono", ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--countdown-openpurchase);
  background: var(--countdown-openpurchase-bg);
}

.protection-warranty .countdown-row-badge {
  color: var(--countdown-warranty);
  background: var(--countdown-warranty-bg);
}

.protection-statutory .countdown-row-badge {
  color: var(--countdown-statutory);
  background: var(--countdown-statutory-bg);
}

.countdown-row-main {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  min-width: 0;
}

.countdown-row-merchant {
  font-size: 1rem;
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.countdown-row-sub {
  font-size: 0.8125rem;
  color: var(--text-2);
}

.countdown-row-progress {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.countdown-row-bar {
  height: 6px;
  border-radius: 3px;
  background: var(--surface-alt);
  overflow: hidden;
}

.countdown-row-bar-fill {
  display: block;
  height: 100%;
  border-radius: 3px;
  background: var(--countdown-openpurchase);
}

.protection-warranty .countdown-row-bar-fill {
  background: var(--countdown-warranty);
}

/* Discrete 5%-step width classes, applied by ReceiptsCountdown.cshtml's FillPercent() — not an
   inline style="width: X%", since the response's style-src 'self' CSP (see Program.cs) has no
   unsafe-inline and silently drops inline style="" attributes with no console error. */
.countdown-fill-0 { width: 0%; }
.countdown-fill-5 { width: 5%; }
.countdown-fill-10 { width: 10%; }
.countdown-fill-15 { width: 15%; }
.countdown-fill-20 { width: 20%; }
.countdown-fill-25 { width: 25%; }
.countdown-fill-30 { width: 30%; }
.countdown-fill-35 { width: 35%; }
.countdown-fill-40 { width: 40%; }
.countdown-fill-45 { width: 45%; }
.countdown-fill-50 { width: 50%; }
.countdown-fill-55 { width: 55%; }
.countdown-fill-60 { width: 60%; }
.countdown-fill-65 { width: 65%; }
.countdown-fill-70 { width: 70%; }
.countdown-fill-75 { width: 75%; }
.countdown-fill-80 { width: 80%; }
.countdown-fill-85 { width: 85%; }
.countdown-fill-90 { width: 90%; }
.countdown-fill-95 { width: 95%; }
.countdown-fill-100 { width: 100%; }

.protection-statutory .countdown-row-bar-fill {
  background: var(--countdown-statutory);
}

.countdown-row-days {
  font-family: "DM Mono", ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.75rem;
  color: var(--text-2);
}

/* Urgency beats type — a protection with ≤7 days left reads as urgent regardless of which kind it
   is, so badge/bar/day-text all switch to the same urgent colour rather than staying colour-coded
   by type once the type distinction stops being the important thing. */
.countdown-row-urgent .countdown-row-badge {
  color: var(--countdown-urgent);
  background: color-mix(in srgb, var(--countdown-urgent) 14%, var(--surface));
}

.countdown-row-urgent .countdown-row-bar-fill {
  background: var(--countdown-urgent);
}

.countdown-row-urgent .countdown-row-days {
  color: var(--countdown-urgent);
  font-weight: 600;
}

.countdown-row-total {
  font-family: "DM Mono", ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.875rem;
  color: var(--ink);
  text-align: right;
  white-space: nowrap;
}

.countdown-row-action {
  display: flex;
  justify-content: flex-end;
}

/* Plain, honest link text — not a button. The row itself is the only click target (the whole
   <a> opens the receipt), so a bordered/filled button here previously implied a distinct action
   ("Lämna tillbaka") it didn't perform. Shares .countdown-row-expired-link's
   look below rather than its own rule. */
.countdown-row-action-link, .countdown-row-expired-link {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--accent);
  white-space: nowrap;
}

/* --- Nyss utgånget — a visually distinct, quieter block: the deadline has already passed, so it
   reads as archival context, not something still ticking. --- */
.countdown-group-expired-label {
  color: var(--muted-value);
}

.countdown-row-expired {
  grid-template-columns: 1fr 120px 136px;
  border-style: dashed;
  border-color: var(--countdown-card-border);
  background: var(--surface-alt);
}

/* --- Empty states — two different messages, not one: no receipts at all is an onboarding moment,
   receipts-but-nothing-expiring is a reassurance. --- */
.countdown-empty {
  text-align: center;
  padding: 3rem 1.5rem;
  border: 1px solid var(--hairline);
  border-radius: 14px;
  background: var(--surface);
}

.countdown-empty-dashed {
  border-style: dashed;
}

.countdown-empty h2 {
  margin: 0 0 0.5rem;
  font-size: 1.25rem;
}

.countdown-empty p {
  margin: 0 auto 1.25rem;
  max-width: 32em;
  color: var(--ink-muted);
}

.countdown-empty-graphic {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 6px;
  height: 32px;
  margin: 0 auto 1.25rem;
}

.countdown-empty-graphic span {
  display: block;
  width: 8px;
  border-radius: 3px;
  background: var(--hairline);
}

.countdown-empty-graphic span:nth-child(1) {
  height: 32px;
}

.countdown-empty-graphic span:nth-child(2) {
  height: 20px;
}

.countdown-empty-graphic span:nth-child(3) {
  height: 12px;
}

@media (max-width: 720px) {
  .countdown-filter-row {
    flex-direction: column;
    align-items: flex-start;
  }

  /* Row stacks: merchant+total on line one, type+date under the merchant, bar full-width, then
     days-left and the action as a text link on the same line under the bar. No badge box on
     mobile — the day text on its own is enough.
     .countdown-row-progress (bar+days, desktop's single 200px column) is display:contents here —
     its box disappears but .countdown-row-bar/.countdown-row-days become direct grid items of
     .countdown-row in its place, so each can get its own grid-area independently. */
  .countdown-row {
    grid-template-columns: 1fr auto;
    grid-template-areas:
      "main total"
      "bar bar"
      "days action";
    row-gap: 0.5rem;
    min-height: 44px;
  }

  .countdown-row-badge {
    display: none;
  }

  .countdown-row-main {
    grid-area: main;
  }

  .countdown-row-total {
    grid-area: total;
  }

  .countdown-row-progress {
    display: contents;
  }

  .countdown-row-bar {
    grid-area: bar;
  }

  .countdown-row-days {
    grid-area: days;
  }

  .countdown-row-action {
    grid-area: action;
  }

  /* Only .countdown-row-main/.countdown-row-total/.countdown-row-action exist on this variant
     (no badge/bar/days) — a smaller area map than the regular row above. */
  .countdown-row-expired {
    grid-template-columns: 1fr auto;
    grid-template-areas: "main total" "action action";
  }
}

/* --- Print --- */

@media print {
  /* Force plain black-on-white regardless of the viewer's on-screen theme — without this, printing
     while dark mode is active would send the dark palette's light-grey text (--ink, --ink-muted)
     to the printer: readable on a dark screen, close-to-invisible on white paper, and nothing like
     the plain business-document look a printed receipt should have. Every rule below this point
     still only ever reads var(--x), so overriding the tokens here is enough to flatten the whole
     page to grayscale without touching a single other selector.
     Specificity note: both real theme sources this needs to beat — ":root[data-theme='dark']" and
     ":root:not([data-theme])" inside the prefers-color-scheme query — are themselves two
     class/attribute/pseudo-class selectors deep (0,0,2,0). ":is()"'s specificity is that of its
     most specific branch, so "html:root:is([data-theme], :not([data-theme]))" also carries
     (0,0,2,·) from :root + :is(...), plus the extra "html" element type takes the tiebreaker
     (0,0,2,1) — a reliable win over both regardless of source order, and it matches unconditionally
     (an element either has the attribute or it doesn't) so it always applies. */
  html:root:is([data-theme], :not([data-theme])) {
    --ink: #000000;
    --ink-muted: #444444;
    --hairline: #b3b3b3;
    --surface: #ffffff;
    --canvas: #ffffff;
    --accent: #000000;
    --accent-hover: #000000;
    --accent-ink: #ffffff;
    --danger: #000000;
    --success: #000000;
    --shadow-rgb: 0, 0, 0;
    --overlay-weak: transparent;
    --overlay-medium: transparent;
    --focus-ring: transparent;
    color-scheme: light;
  }

  /* Site chrome never belongs on a printed page, on any page, not just the receipt — so this
     stays unscoped rather than needing a .no-print on the header/footer of every single template. */
  .hero, .site-footer {
    display: none;
  }

  .print-only {
    display: flex;
  }

  /* A small letterhead, not a competing visual element — logo on one side, a reference block
     (full URL + generated-at timestamp) on the other, the same spread two-column shape most
     printed business documents (invoices, statements) use for their header. Uses wordmark-navy.svg
     (not the bare icon-navy.svg): the icon mark is now purely abstract (three bars, no text baked
     in — see logo-assets/icon-navy.svg), so on its own it no longer identifies the brand by name
     the way the previous icon did. The wordmark carries the "Rceep" name itself, so no separate
     product-name span is needed alongside it. */
  .print-branding {
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
    padding-bottom: 0.9rem;
    margin-bottom: 1.25rem;
    border-bottom: 1px solid var(--hairline);
  }

  .print-branding-logo {
    height: 24px;
    width: auto;
    flex-shrink: 0;
  }

  .print-branding-meta {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.15rem;
    text-align: right;
  }

  .print-branding-url, .print-branding-timestamp {
    font-size: 0.8rem;
    color: var(--ink-muted);
  }

  /* Per-element opt-out for things that only make sense on-screen: edit controls, forms, dialogs,
     links back into the app. Used on the receipt detail page — see ReceiptDetail.cshtml. */
  .no-print {
    display: none !important;
  }

  body {
    background: #fff;
  }

  .page {
    margin: 0;
    padding: 0;
    max-width: none;
  }

  .card {
    border: none;
    box-shadow: none;
    border-radius: 0;
    padding: 0;
  }

  /* Same reasoning as .card above — a tinted, bordered panel is the right amount of structure on
     screen but just wastes ink and looks odd once the page itself is already plain white. */
  .settings-section {
    background: none;
    border: none;
    padding: 0;
    margin-top: 1.5rem;
  }

  /* Keeps the short receipt info panel from being cut across a page boundary — but deliberately
     excludes any panel containing a <details> (Kvittorader, always printed in full now — see
     below): that one can genuinely run to dozens of rows, and forcing a block that long to stay
     together would just strand a near-empty previous page rather than actually avoid a break. */
  .settings-section:not(:has(details)) {
    break-inside: avoid;
  }

  /* A formal paper record, not a photo printout: the receipt info block keeps its normal small
     side-by-side thumbnail (a reference image, not the main event — full-size detail is one click
     away on screen), and only two things print at all beyond it — Kvittorader (the itemised
     purchase) and nothing else. Kategori and Dela kvitto were already excluded via .no-print;
     Påminnelser is excluded the same way here, since day-to-day reminder status ("Väntar"/
     "Skickad") isn't part of what a formal record of the purchase needs to show. */
  .receipt-image-thumb {
    max-width: 220px;
    max-height: 220px;
    cursor: default;
  }

  /* Kvittorader always prints in full, regardless of whether it's expanded on screen — a formal
     record shouldn't depend on a UI toggle state the person printing may not even have noticed.
     A closed <details> hides its content via an internal browser mechanism that a CSS override on
     the child does NOT reliably defeat in current engines (confirmed by testing, not just spec-
     reading) — print.js flips the real "open" attribute for the duration of the print instead. */

  /* The disclosure triangle is a screen-only affordance — nothing to toggle once the content
     always shows on paper — and "no icons/graphical elements unless necessary" applies to it too. */
  .settings-section summary {
    list-style: none;
  }

  .settings-section summary::-webkit-details-marker {
    display: none;
  }
}

/* --- Spaces (docs/rceep-family-spec.md section 30) --- */
.space-invitation-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.75rem 0;
  border-bottom: 1px solid var(--hairline);
}

.space-invitation-row:last-child {
  border-bottom: none;
}

/* --- /Spaces home (design_handoff_spaces §5.4) — search, filter chips, pinned grid, all-spaces
   table. --- */

.spaces-search {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  /* min-height, not the button's own padding-driven sizing — a plain <button> gets 44px from the
     global button rule (see "button, input[type=submit]..." above), but this is a <div>, so
     without pinning it explicitly it only comes out as tall as its padding+text, visibly shorter
     than "Skapa space" right next to it (Peter, 2026-08-25). */
  min-height: 44px;
  padding: 0 0.9rem;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  min-width: 220px;
  color: var(--text-label);
}

.spaces-search input {
  flex: 1;
  border: none;
  background: none;
  min-height: 0;
  padding: 0;
  font-size: 0.8125rem;
  color: var(--ink);
}

.spaces-filter-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 1.25rem;
}

.spaces-filter-chip {
  padding: 0.4rem 0.8rem;
  min-height: 0;
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--hairline);
  color: var(--ink-muted);
  font-size: 0.71875rem;
  font-weight: 600;
}

.spaces-filter-chip.active {
  background: var(--navy);
  border-color: var(--navy);
  color: #fff;
}

.section-divider {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 1.5rem 0 0.75rem;
}

.section-divider span {
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.59375rem;
  font-weight: 500;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--text-label);
  white-space: nowrap;
}

.section-divider div {
  flex: 1;
  height: 1px;
  background: var(--hairline);
}

.space-card-sub {
  display: block;
  margin-top: 0.1rem;
  font-size: 0.71875rem;
  color: var(--text-label);
}

/* The pin button's own <form> is a plain block box (needed for the POST), which otherwise sits
   as the actual flex/grid item in both the card header and the table row — centering the FORM's
   box, not the star inside it, and leaving the star sitting at the form's own top/line-height
   offset instead of visually centered against the avatar/name/row it sits next to (Peter,
   2026-08-27). display: contents removes the form from the box model entirely so the button
   itself is the flex/grid item and gets centered directly. */
.space-card-pin-form {
  display: contents;
}

/* 28×28 hit target with its own background/border (Kontoadministration §5d, Claude Design
   2026-08-28) — was a bare 24px glyph with no surrounding shape at all. */
.space-card-pin {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  min-height: 0;
  padding: 0;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  background: var(--surface);
  border: 1px solid var(--hairline);
  color: var(--muted-value);
  font-size: 0.9rem;
}

/* Without this, the generic `button:hover` rule (higher specificity than a single class) paints
   an accent-colored fill behind the star on hover — same fix .toggle-password:hover already
   applies for the same reason (Peter, 2026-08-27). */
.space-card-pin:hover {
  background: var(--canvas);
  color: var(--ink-muted);
  filter: none;
}

.space-card-pin-active {
  background: var(--pending-bg);
  border-color: transparent;
  color: var(--pending-border);
}

.space-card-pin-active:hover {
  background: var(--pending-bg);
  color: var(--pending-border);
}

.role-chip {
  display: inline-flex;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  background: var(--surface-alt);
  font-size: 0.65625rem;
  font-weight: 600;
  color: var(--ink-muted);
  white-space: nowrap;
}

.spaces-table-card {
  padding: 0;
  overflow: hidden;
}

/* Whole-row click target, same trick .receipts-table tbody tr uses (see .receipt-merchant-link::
   after) — .spaces-table-row-link::after is stretched over the row via position:relative here, so
   clicking anywhere in a highlighted row opens the space, not just the name text (Peter,
   2026-08-28: "listan ... inte klickbar <tr> som 'Mina kvitton' har"). */
.spaces-table-row {
  position: relative;
  display: grid;
  grid-template-columns: auto 1.6fr 0.8fr 0.9fr auto auto;
  align-items: center;
  gap: 0.875rem;
  padding: 0.8rem 1rem;
  border-bottom: 1px solid var(--hairline);
  cursor: pointer;
}

.spaces-table-row:hover {
  background: var(--canvas);
}

.spaces-table-row-link::after {
  content: "";
  position: absolute;
  inset: 0;
}

.spaces-table-row:last-child {
  border-bottom: none;
}

.spaces-table-name {
  min-width: 0;
}

.spaces-table-name a {
  display: block;
  font-weight: 700;
  font-size: 0.84375rem;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.spaces-table-name a:hover {
  text-decoration: underline;
}

.spaces-table-activity {
  font-size: 0.71875rem;
  color: var(--text-label);
}

.spaces-table-count {
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.75rem;
  color: var(--text-label);
}

.spaces-table-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.9rem 1rem;
  background: var(--canvas);
  font-size: 0.75rem;
  color: var(--ink-muted);
}

.spaces-table-footer a {
  font-weight: 600;
}

.spaces-table-footer-usage {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-shrink: 0;
  font-weight: 600;
}

/* Continuous seat-usage meter in the footer (Kontoadministration §5d) — distinct from
   .seat-meter's discrete per-member segments (Space detail page), this one is a single
   proportional fill for "N of 5 owned spaces used". */
.spaces-table-footer-meter {
  flex: 1;
  max-width: 220px;
  height: 5px;
  border-radius: 999px;
  background: var(--hairline);
  overflow: hidden;
}

.spaces-table-footer-meter-fill {
  height: 100%;
  border-radius: 999px;
  background: var(--accent);
}

/* Column header row above the space rows (Kontoadministration §5d — the table previously had no
   headers at all, so "0" in the receipts column read as unexplained). Same grid as
   .spaces-table-row so columns line up; the two leading/trailing cells (avatar, favorite) stay
   unlabeled on purpose, nothing to name there. */
.spaces-table-header {
  display: grid;
  grid-template-columns: auto 1.6fr 0.8fr 0.9fr auto auto;
  align-items: center;
  gap: 0.875rem;
  padding: 0.6rem 1rem;
  background: var(--canvas);
  border-bottom: 1px solid var(--hairline);
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.59375rem;
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--text-label);
}

.spaces-table-header-count {
  text-align: right;
}

@media (max-width: 720px) {
  .spaces-table-header {
    display: none;
  }
}

/* A space with no receipts yet gets an action link here instead of a dead "no receipts" caption
   (Kontoadministration §5d) — z-index:1 for the same reason .space-card-pin already needs it: the
   row's own name-link overlay (.spaces-table-row-link::after, stretched via position:absolute over
   the whole row for whole-row click-through) would otherwise swallow the click. */
.spaces-table-activity-action {
  position: relative;
  z-index: 1;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--accent);
}

.spaces-table-count-zero {
  color: var(--muted-value);
}

/* Favorited row tinted at rest (not just on hover — same --canvas tone .spaces-table-row:hover
   already uses) PLUS a gold left-border accent matching the filled star itself — the tint alone
   read as too subtle to notice at all once favoriting stopped promoting a space into its own
   "PINNADE" card grid at the top (Peter, 2026-08-29: "favorit ... gör inget längre ... verkar
   blivit borttaget" — the toggle and the sort-to-top both still work, the payoff just wasn't
   visible enough to register as one). Left padding reduced by the border's own width so row
   content still lines up with .spaces-table-header's columns. */
.spaces-table-row[data-favorite="true"] {
  background: var(--canvas);
  border-left: 3px solid var(--pending-border);
  padding-left: calc(1rem - 3px);
}

/* The Free-tier upsell card (design_handoff_spaces §5.4's "Free-planruta") — accent left-border,
   distinct from the amber Viewer/pending-notice treatment since this is an upsell, not a warning. */
.free-plan-notice {
  margin-top: 1.25rem;
  border-left: 3px solid var(--accent);
}

.free-plan-notice h3 {
  margin: 0 0 0.3rem;
  font-size: 0.90625rem;
}

.free-plan-notice p {
  margin: 0 0 0.9rem;
  color: var(--ink-muted);
  font-size: 0.8125rem;
  line-height: 1.6;
  max-width: 640px;
}

/* Full responsive reflow of this table is part of the mobile pass (design_handoff_spaces §5.8) —
   for now just let it wrap rather than force a named-area layout with no matching markup. */
@media (max-width: 720px) {
  .spaces-table-row {
    grid-template-columns: auto 1fr;
    flex-wrap: wrap;
  }

  .spaces-table-activity, .spaces-table-count {
    grid-column: 2;
  }
}

/* --- Space detail page (design_handoff_spaces §5.6) — identity header, seat meter, single
   people-centric list (members + pending invites + embedded invite form), invitation history,
   de-emphasized subscription/leave footer. --- */

.space-detail-header {
  display: flex;
  align-items: flex-start;
  gap: 0.875rem;
  margin-bottom: 0.75rem;
}

.space-detail-heading {
  flex: 1;
  min-width: 0;
}

.space-detail-title-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.space-detail-title-row h1 {
  margin: 0;
}

.space-detail-meta {
  margin: 0.2rem 0 0;
  color: var(--ink-muted);
  font-size: 0.8125rem;
}

/* Redigera/⋯ were squeezed onto the same line as the H1, forcing a long space name to wrap
   against them instead of past them — full-width row below the identity block instead, same
   "actions drop under the title on mobile" pattern .page-header-row already uses. */
@media (max-width: 640px) {
  .space-detail-header {
    flex-wrap: wrap;
  }

  .space-detail-header .page-header-actions {
    flex-basis: 100%;
    justify-content: flex-end;
  }
}

.section-divider-meta {
  font-size: 0.71875rem;
  color: var(--ink-muted);
  white-space: nowrap;
}

.seat-meter {
  display: flex;
  gap: 4px;
  margin-bottom: 0.9rem;
}

.seat-meter-segment {
  flex: 1;
  height: 5px;
  border-radius: 999px;
  background: var(--hairline);
}

.seat-meter-segment.seat-pending {
  opacity: 0.35;
}

.space-people-card {
  padding: 0;
  overflow: hidden;
}

.space-person-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.7rem 0.9rem;
  border-bottom: 1px solid var(--border-inner);
}

.space-people-card > *:last-child {
  border-bottom: none;
}

.space-person-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.space-person-name {
  font-weight: 700;
  font-size: 0.84375rem;
  color: var(--ink);
}

.space-person-meta {
  font-size: 0.6875rem;
  color: var(--text-label);
}

.role-chip-mono {
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.59375rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-label);
  white-space: nowrap;
}

.role-chip-pending {
  display: inline-flex;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  background: var(--pending-bg);
  color: var(--pending-ink);
  font-size: 0.65625rem;
  font-weight: 600;
  white-space: nowrap;
}

.role-select {
  min-height: 0;
  padding: 0.35rem 0.5rem;
  width: auto;
  background: var(--canvas);
  border: 1px solid var(--hairline);
  color: var(--ink);
  font-weight: 600;
  font-size: 0.71875rem;
}

.space-person-remove {
  min-height: 0;
  padding: 0;
  background: none;
  color: var(--ink-muted);
  font-size: 0.71875rem;
  font-weight: 600;
  white-space: nowrap;
}

.space-avatar-pending {
  background: none;
  border: 1.5px dashed var(--hairline);
  color: var(--ink-muted);
}

.space-avatar-empty {
  background: none;
  border: 1.5px dashed var(--hairline);
  color: var(--ink-muted);
  font-size: 1rem;
}

.space-person-row-full {
  justify-content: space-between;
  background: var(--canvas);
  color: var(--ink-muted);
  font-size: 0.8125rem;
}

.space-invite-form-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.7rem 0.9rem;
  background: var(--canvas);
}

.space-invite-form-row input[type="email"] {
  flex: 1;
  min-width: 0;
}

.space-invite-form-row select {
  width: auto;
}

.space-invite-error {
  margin: 0;
  padding: 0.6rem 0.9rem;
}

.space-people-explainer {
  margin: 0.6rem 0 0;
  font-size: 0.71875rem;
  line-height: 1.6;
  color: var(--ink-muted);
}

.invitation-history-list {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.invitation-history-row {
  font-size: 0.75rem;
  color: var(--ink-muted);
}

.space-detail-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 1.5rem;
  padding: 0.9rem 1rem;
  background: rgba(var(--shadow-rgb), 0.015);
  border: 1px solid var(--hairline);
  border-radius: 10px;
}

.space-detail-footer p {
  margin: 0;
  font-weight: 600;
  font-size: 0.78125rem;
  color: var(--ink-muted);
}

.space-detail-footer-hint {
  font-weight: 400 !important;
  font-size: 0.71875rem !important;
  margin-top: 0.15rem !important;
}

.space-detail-footer-actions {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-shrink: 0;
}

.space-detail-footer-actions a, .space-detail-footer-actions button {
  font-weight: 600;
  font-size: 0.75rem;
  color: var(--ink-muted);
}

.space-leave-form {
  margin-top: 1rem;
}

/* Staff-only /Internal/* pages (Metrics/Staff/Account) — a plain sub-nav, same pill-tab shape as
   the theme/language dropdowns' trigger rather than a whole new component. */
/* Moved into .page-header-actions (right-aligned, same baseline as the H1) on all three admin pages
   — Kontoadministration §0's "Fliknavigering" (Claude Design, 2026-08-28), replacing the old
   left-aligned row under the heading. */
.internal-subnav {
  display: flex;
  gap: 0.5rem;
}

.internal-subnav a {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0.5rem 0.9375rem;
  border-radius: 999px;
  border: 1px solid var(--hairline);
  background: var(--surface);
  color: var(--ink-muted);
  text-decoration: none;
  font-size: 0.75rem;
  font-weight: 600;
}

/* Had no hover rule at all — the global a:hover reset (underline + a colour shift) was the only
   thing that fired, breaking the pill's shape with an underline and giving no real "this is
   clickable" feedback (Peter, 2026-08-29: "Kontoadministration 'Åtkomst', 'Abonnemang'"). --canvas
   (one tick off --surface) since inactive pills already sit on --surface. Placed before .active
   below so hovering the already-active tab still shows its own distinct state (tied specificity,
   .active wins on source order). */
.internal-subnav a:hover {
  background: var(--canvas);
  color: var(--ink);
  text-decoration: none;
}

/* Active tab is a filled pill, not just a thin accent border — Kontoadministration §0 (Claude
   Design, 2026-08-28). --primary-bg/-ink (navy-on-white in light, the still-vivid --accent-solid in
   dark — same pair every primary button already uses) rather than --ink/a literal white: --ink
   itself flips to the pale near-white text colour in dark mode, which would make both the fill and
   a fixed white label invisible against each other. */
.internal-subnav a.active {
  border-color: var(--primary-bg);
  background: var(--primary-bg);
  color: var(--primary-ink);
}

.form-success {
  color: var(--success);
  background: var(--success-bg);
  border: 1px solid var(--success-border);
  border-radius: 8px;
  padding: 0.75rem 1rem;
  margin: 0 0 1rem;
}

.internal-table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 1.25rem;
}

.internal-table td {
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--hairline);
}

.internal-table-action {
  text-align: right;
}

.internal-inline-form {
  display: flex;
  align-items: flex-end;
  gap: 0.6rem;
}

.internal-inline-form label {
  flex: 1;
  margin-bottom: 0;
}

/* .auth-submit defaults to width: 100% (a full-width CTA is the common case elsewhere) — inside
   this flex row that would claim the whole line and squeeze the label/input to min-content, same
   fix as .correct-fields-actions .auth-submit already applies for the same reason. */
.internal-inline-form .auth-submit {
  width: auto;
  flex: 0 0 auto;
}

/* Trend line under a Metrics stat-card value (see SparklineSvg) — stretches to the card's own
   width via preserveAspectRatio="none" on the SVG itself, not a fixed size here. */
.sparkline {
  display: block;
  width: 100%;
  height: 28px;
  margin-top: 0.4rem;
}

/* --- Kontoadministration §5a (Nyckeltal) --- */

.metrics-period-row {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 0.75rem;
  padding-bottom: 0.875rem;
  margin-bottom: 1.25rem;
  border-bottom: 1px solid var(--hairline);
}

.metrics-period-range {
  margin: 0;
  font-size: 0.84375rem;
  font-weight: 700;
}

.metrics-period-updated {
  margin: 0.2rem 0 0;
  font-size: 0.71875rem;
  color: var(--ink-muted);
}

.metrics-period-controls {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  flex-wrap: wrap;
}

.metrics-segmented {
  display: flex;
  gap: 3px;
  padding: 3px;
  border-radius: 9px;
  background: var(--surface-alt);
}

.metrics-segmented a {
  padding: 0.35rem 0.7rem;
  border-radius: 7px;
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--ink-muted);
  text-decoration: none;
}

.metrics-segmented a:hover {
  color: var(--ink);
  text-decoration: none;
}

.metrics-segmented a.active {
  background: var(--surface);
  color: var(--ink);
  box-shadow: 0 1px 2px rgba(var(--shadow-rgb), 0.08);
}

/* Native <details>/<summary> disclosure — the custom-range fields render only once opened, not
   always-visible like the old .period-custom-form (Kontoadministration §5a) — zero JS, CSP-safe.
   Floats as an absolutely-positioned panel (same shape as .overflow-menu/.overflow-menu-panel,
   ReceiptDetail's "⋯" menu) rather than expanding inline — an inline expansion pushed the whole
   .metrics-period-row taller and reflowed the segmented control beside it, which read as broken
   layout rather than an opened control (Peter, 2026-08-29). */
/* height:44px, not just height:auto — modern Chrome wraps a <details>'s non-summary content in an
   internal ::details-content box that still claims a few pixels of height when [open] even though
   .period-custom-form inside it is position:absolute (verified in devtools: 44px closed, 56px
   open, with .period-custom-form's own rect unaffected) — the very "jump" this whole floating-panel
   rewrite was supposed to eliminate (Peter, 2026-08-29: "det hoppar till när man öppnar upp den").
   Pinning the box's own height stops it from ever growing regardless of that internal wrapper. */
.metrics-custom-period {
  position: relative;
  height: 44px;
  overflow: visible;
}

.metrics-custom-period summary {
  cursor: pointer;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--accent);
  list-style: none;
  height: 44px;
  display: inline-flex;
  align-items: center;
}

.metrics-custom-period summary::-webkit-details-marker {
  display: none;
}

.metrics-custom-period .period-custom-form {
  position: absolute;
  right: 0;
  top: calc(100% + 0.4rem);
  z-index: 20;
  margin: 0;
  flex-wrap: wrap;
  padding: 0.875rem;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(var(--shadow-rgb), 0.18);
}

.metrics-hero-grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  gap: 1rem;
  margin-bottom: 1.25rem;
  align-items: stretch;
}

@media (max-width: 900px) {
  .metrics-hero-grid {
    grid-template-columns: 1fr;
  }
}

.metrics-hero-card-a {
  display: flex;
  flex-direction: column;
}

/* Kvitton totalt + Antal spaces, side by side in the same card (Kontoadministration §5a
   follow-up, Peter 2026-08-29: stacked read "lite tokigt", switched to a two-column row instead).
   Each .metrics-hero-stat takes half the card's width; a vertical divider replaces the stacked
   layout's horizontal one. */
.metrics-hero-stats-row {
  display: flex;
  gap: 1.5rem;
}

.metrics-hero-stat {
  flex: 1;
  min-width: 0;
}

.metrics-hero-stat + .metrics-hero-stat {
  padding-left: 1.5rem;
  border-left: 1px solid var(--hairline);
}

.metrics-hero-stat .area-chart {
  height: 90px;
  margin-top: 0.375rem;
}

@media (max-width: 560px) {
  .metrics-hero-stats-row {
    flex-direction: column;
    gap: 1rem;
  }

  .metrics-hero-stat + .metrics-hero-stat {
    padding-left: 0;
    border-left: none;
    padding-top: 1rem;
    border-top: 1px solid var(--hairline);
  }
}

.metrics-hero-card-a-top {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
}

.metrics-hero-label-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.metrics-hero-label {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--ink-muted);
}

.metrics-hero-value {
  display: block;
  margin-top: 0.2rem;
  font-size: 2.375rem;
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1;
}

.metrics-hero-secondary {
  flex-shrink: 0;
  text-align: right;
}

.metrics-hero-secondary-label {
  display: block;
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  color: var(--text-label);
}

.metrics-hero-secondary-value {
  display: block;
  margin-top: 0.2rem;
  font-size: 0.9375rem;
  font-weight: 700;
}

.metric-delta-chip {
  display: inline-flex;
  padding: 0.25rem 0.5625rem;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
}

.metric-delta-up {
  background: var(--success-bg);
  color: var(--success);
}

.metric-delta-down {
  background: var(--danger-bg);
  color: var(--danger);
}

.metric-delta-flat {
  background: var(--surface-alt);
  color: var(--ink-muted);
}

.area-chart {
  display: block;
  width: 100%;
  height: 120px;
  margin-top: 0.5rem;
}

.metrics-chart-axis {
  display: flex;
  justify-content: space-between;
  margin-top: 0.35rem;
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.625rem;
  color: var(--text-label);
}

.metrics-accounts-card-header {
  margin: 0 0 0.75rem;
  padding-bottom: 0.6rem;
  border-bottom: 1px solid var(--hairline);
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--text-label);
}

.metrics-account-row {
  display: grid;
  grid-template-columns: 1fr 96px auto;
  align-items: center;
  gap: 0.875rem;
  padding: 0.8125rem 0;
  border-bottom: 1px solid var(--border-inner);
}

.metrics-account-row:last-child {
  border-bottom: none;
}

.metrics-account-row-name {
  font-size: 0.8125rem;
  font-weight: 700;
}

.metrics-account-row-sub {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.6875rem;
  color: var(--text-label);
}

.metrics-mini-sparkline .sparkline {
  width: 96px;
  height: 26px;
  margin-top: 0;
}

/* display:block on both — these are <span> elements (kept as spans since they sit inside an
   inline-ish row of text-level siblings), and width/height are no-ops on a plain inline element,
   which silently collapsed both the track and its fill to zero size. */
.metrics-mini-gauge-track {
  display: block;
  width: 96px;
  height: 6px;
  border-radius: 999px;
  background: var(--surface-alt);
  overflow: hidden;
}

.metrics-mini-gauge-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--accent);
}

.metrics-mini-gauge-fill-30d {
  opacity: 0.45;
}

.metrics-account-row-value {
  min-width: 34px;
  text-align: right;
  font-size: 1.375rem;
  font-weight: 800;
}

.metrics-subscription-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  margin-bottom: 0.75rem;
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--text-label);
}

.metrics-subscription-share {
  font-family: inherit;
  font-size: 0.71875rem;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  color: var(--ink-muted);
}

.metrics-subscription-share strong {
  color: var(--ink);
  font-weight: 700;
}

.metrics-distribution-bar {
  display: flex;
  gap: 3px;
  height: 10px;
  border-radius: 999px;
  overflow: hidden;
  margin-bottom: 1.125rem;
}

.metrics-distribution-segment {
  flex-shrink: 0;
  height: 100%;
}

.metrics-distribution-segment:first-child {
  border-radius: 999px 0 0 999px;
}

.metrics-distribution-segment:last-child {
  border-radius: 0 999px 999px 0;
}

.metrics-distribution-free {
  background: var(--muted-value);
}

.metrics-distribution-plus {
  background: var(--tier-plus-bg);
}

.metrics-distribution-spaces {
  background: var(--accent-solid);
}

.metrics-legend {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.375rem;
}

@media (max-width: 720px) {
  .metrics-legend {
    grid-template-columns: 1fr;
  }
}

.metrics-legend-item {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
}

.metrics-legend-swatch {
  flex-shrink: 0;
  width: 9px;
  height: 9px;
  border-radius: 2px;
  margin-top: 0.3rem;
}

.metrics-legend-body {
  flex: 1;
  min-width: 0;
}

.metrics-legend-name {
  font-size: 0.78125rem;
  font-weight: 700;
}

.metrics-legend-desc {
  display: block;
  margin-top: 0.1rem;
  font-size: 0.6875rem;
  color: var(--ink-muted);
}

.metrics-legend-value {
  flex-shrink: 0;
  font-size: 1.125rem;
  font-weight: 800;
}

/* Metrics period selector (7/30/90 dagar) — same pill shape as .internal-subnav, just given its
   own top margin since it sits below the page's own hint text rather than right under the H1. */
.period-selector {
  margin-top: 0.75rem;
  margin-bottom: 1.25rem;
}

.stat-delta {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.8125rem;
  font-weight: 600;
}

.stat-delta-up {
  color: var(--success);
}

.stat-delta-down {
  color: var(--danger);
}

.stat-delta-flat {
  color: var(--ink-muted);
}

.stat-delta-compare {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.75rem;
  color: var(--text-label);
}

.period-selector-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.75rem 1.25rem;
}

.period-selector-row .period-selector {
  margin-top: 0;
  margin-bottom: 0;
}

.period-custom-form {
  display: flex;
  align-items: flex-end;
  gap: 0.5rem;
}

.period-custom-form label {
  margin-bottom: 0;
  font-size: 0.75rem;
}

.period-custom-form input[type="date"] {
  min-height: 36px;
  padding: 0.4rem 0.5rem;
}

/* --- Kontoadministration §5b (Åtkomst) / §5c (Abonnemang) — shared square identity avatar for a
   user account (Staff/Internal-Account rows have no per-account colour identity the way a Space
   does, so this is a single fixed --ink/--primary-ink pair, not a palette). --- */
/* --primary-bg/-ink (navy-on-white in light, the still-vivid --accent-solid in dark), not --ink —
   --ink itself flips to the pale near-white text colour in dark mode, which would make the square
   and a --primary-ink (also --ink in dark) letter the same colour. Same bug/fix as
   .internal-subnav a.active above. */
.account-avatar {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: 9px;
  background: var(--primary-bg);
  color: var(--primary-ink);
  font-weight: 700;
  font-size: 12px;
  line-height: 1;
}

/* Single-admin-left warning (§5b) — amber left-border, same --pending-* pair the Viewer-role/invite
   "waiting" banner already uses elsewhere, since this is the same "nothing's broken, but pay
   attention" register, not an error. */
.admin-warning-card {
  max-width: 640px;
  border-left: 3px solid var(--pending-border);
  padding: 0.8125rem 1rem;
}

.admin-warning-card h3 {
  margin: 0 0 0.3rem;
  font-size: 0.78125rem;
  font-weight: 700;
}

.admin-warning-card p {
  margin: 0;
  font-size: 0.75rem;
  line-height: 1.6;
  color: var(--ink-muted);
}

.staff-list {
  padding: 0;
  overflow: hidden;
}

.staff-row {
  display: grid;
  grid-template-columns: auto 1fr auto auto;
  align-items: center;
  gap: 0.875rem;
  padding: 0.875rem 1.125rem;
  border-bottom: 1px solid var(--border-inner);
}

.staff-row:last-of-type {
  border-bottom: none;
}

.staff-row-email {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}

.staff-row-email strong {
  font-size: 0.84375rem;
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.staff-row-you-chip {
  flex-shrink: 0;
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  background: var(--surface-alt);
  font-size: 0.625rem;
  font-weight: 600;
  color: var(--ink-muted);
}

.staff-row-meta {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.71875rem;
  color: var(--text-label);
}

.staff-row-scope {
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--text-label);
  white-space: nowrap;
}

.staff-row-protected {
  font-size: 0.71875rem;
  color: var(--muted-value);
  white-space: nowrap;
}

/* Trailing add-row inside the same .staff-list card, not a separate card — Kontoadministration §5b
   ("Inget separat kort för inbjudan"). */
.staff-add-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 0.875rem;
  padding: 0.875rem 1.125rem;
  background: var(--canvas);
}

.staff-add-icon {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 9px;
  border: 1.5px dashed var(--hairline);
  color: var(--text-label);
  font-weight: 600;
}

/* Self-contained for the same reason .filter-form .search-field > input is (see that rule's own
   comment) — the <label> here is a visually-hidden sibling, not a wrapper, so the global "form
   label input" descendant selector never matches it. */
.staff-add-row input[type="email"] {
  display: block;
  width: 100%;
  padding: 0.6rem 0.8rem;
  font-family: inherit;
  font-size: 1rem;
  font-weight: 400;
  color: var(--ink);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  min-height: 40px;
  background: var(--surface);
  transition: border-color 0.1s ease, box-shadow 0.1s ease;
}

.staff-add-row input[type="email"]:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.staff-add-row input[type="email"]::placeholder {
  color: var(--ink-muted);
  opacity: 1;
}

/* .auth-submit defaults to width: 100% — inside this grid row's auto-sized third column that would
   fight the column's own sizing, same fix .internal-inline-form .auth-submit already applies. */
.staff-add-row .auth-submit {
  width: auto;
}

/* Shared max-width cap for an explainer paragraph under a Kontoadministration page heading (§5b/§5c)
   — keeps a long line from stretching edge-to-edge on wide screens. */
.admin-explainer {
  max-width: 640px;
}

/* --- Kontoadministration §5c (Abonnemang) --- */

/* No max-width/centering here on purpose (a first pass narrowed this to a centered 700px column,
   Peter 2026-08-29: "den följer inte övriga sidor där det är full bredd, se Åtkomst") — this page
   now spans the full .page width exactly like Åtkomst's/Nyckeltal's own cards, just with the
   plan-grid/legend absorbing the extra width via auto-fit below instead of an artificial cap. */
.account-lookup-row {
  display: flex;
  align-items: flex-end;
  gap: 0.625rem;
  flex-wrap: wrap;
  margin-bottom: 1.25rem;
}

.account-lookup-row label {
  flex: 1;
  min-width: 200px;
  margin-bottom: 0;
}

.account-lookup-row .auth-submit {
  width: auto;
  flex: 0 0 auto;
  /* Matches the input's own min-height (global "form label input" rule) — the generic button rule
     has no min-height at all (just padding around its text), which left Sök ~8px shorter than the
     email field beside it and sitting visibly low against it under align-items:flex-end
     (Peter, 2026-08-29: "Sök-knappen är inte vertikalt centrerad"). */
  min-height: 44px;
}

.account-result-card {
  margin-bottom: 1.25rem;
}

.account-result-header {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  margin-bottom: 1.25rem;
}

.account-result-heading {
  flex: 1;
  min-width: 0;
}

.account-result-email {
  margin: 0;
  font-size: 0.9375rem;
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.account-result-meta {
  margin: 0.15rem 0 0;
  font-size: 0.71875rem;
  color: var(--ink-muted);
}

/* Four-tier plan chip — Free/Spaces/Custom map onto existing tokens, --tier-plus-* is the one new
   pair (see its own :root comment). */
.plan-chip {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
  font-size: 0.6875rem;
  font-weight: 700;
  white-space: nowrap;
}

.plan-chip-free {
  background: var(--surface-alt);
  color: var(--ink);
}

.plan-chip-plus {
  background: var(--tier-plus-bg);
  color: var(--tier-plus-ink);
}

/* --accent-solid + a fixed white, not --primary-bg/-ink — --accent-solid deliberately stays the
   same vivid indigo in both themes (see its own :root comment), so a fixed white reads correctly
   against it either way; --primary-bg would make this chip navy in light mode instead of indigo,
   indistinguishable from .plan-chip-custom below. */
.plan-chip-spaces {
  background: var(--accent-solid);
  color: #ffffff;
}

/* --navy + a fixed white (not --ink/--primary-ink — see .account-avatar's own comment on that
   trap) — --navy never flips between themes, same "always-dark" choice .receipts-add-button makes
   for the same reason, so this stays visually distinct from the indigo Spaces chip above in both
   themes rather than converging on it. */
.plan-chip-custom {
  background: var(--navy);
  color: #ffffff;
}

/* CaptureJob status chips (Internal/CaptureErrors) — same .plan-chip shape, reusing the existing
   form-error/form-success color tokens rather than inventing a third palette for what's really
   the same "bad/good/neutral" distinction those already make. */
.plan-chip-job-failed {
  background: var(--danger-bg);
  color: var(--danger);
}

.plan-chip-job-completed {
  background: var(--success-bg);
  color: var(--success);
}

.plan-chip-job-active {
  background: var(--surface-alt);
  color: var(--ink-muted);
}

/* ErrorMessage is raw ex.Message text (English, developer-facing — see CaptureJob's own doc
   comment) — monospace signals "this is a technical log line", not translated UI prose. */
.capture-error-detail {
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.8125rem;
  color: var(--ink-muted);
}

.account-section-label {
  margin: 0 0 0.6rem;
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--text-label);
}

/* auto-fit/minmax, not a fixed 1fr 1fr — now that the page spans full width (see §5c's own top
   comment), a fixed two-column grid left each card stretched oddly wide with mostly empty space;
   this settles at 4-across on a full-width desktop viewport and reflows down on its own as the
   viewport narrows, same responsive approach .plan-grid (Billing/Upgrade.cshtml) already uses. */
.account-plan-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 0.625rem;
  margin-bottom: 1.25rem;
}

.account-plan-option {
  position: relative;
  display: block;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 10px;
  padding: 0.75rem 0.875rem;
  cursor: pointer;
}

/* Two classes in the selector (.account-plan-option .account-plan-radio-input), not one — this
   radio sits inside a <label>, so the global "form label input:not([type=checkbox])" rule (0,1,3
   specificity: form+label+input types, :not([type=...]) counts as one attribute selector) would
   otherwise win over a single-class selector (0,1,0) and silently reapply that rule's
   display:block/width:100%/padding/border/min-height on top of this custom hidden-radio layout. */
.account-plan-option .account-plan-radio-input {
  position: absolute;
  top: 0.8rem;
  left: 0.875rem;
  width: 15px;
  height: 15px;
  margin: 0;
  opacity: 0;
}

.account-plan-radio-dot {
  position: absolute;
  top: 0.8rem;
  left: 0.875rem;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  border: 1.5px solid var(--ink-muted);
  background: var(--surface);
}

.account-plan-option .account-plan-radio-input:checked + .account-plan-radio-dot {
  border-color: var(--primary-bg);
  background: var(--primary-bg);
}

.account-plan-option .account-plan-radio-input:checked + .account-plan-radio-dot::after {
  content: "";
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  background: var(--primary-ink);
}

.account-plan-option .account-plan-radio-input:focus-visible + .account-plan-radio-dot {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.account-plan-option-top {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-left: 1.5rem;
}

.account-plan-option-name {
  font-size: 0.8125rem;
  font-weight: 700;
}

.account-plan-option-desc {
  display: block;
  margin-top: 0.25rem;
  margin-left: 1.5rem;
  font-size: 0.71875rem;
  line-height: 1.55;
  color: var(--ink-muted);
}

.account-plan-option-chip {
  flex-shrink: 0;
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  background: var(--surface-alt);
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.5625rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.account-plan-option-selected {
  border-color: var(--accent);
  border-width: 1.5px;
  padding: calc(0.75rem - 0.5px) calc(0.875rem - 0.5px);
  box-shadow: 0 1px 3px rgba(var(--shadow-rgb), 0.08);
}

/* pointer-events: none (not the disabled attribute) is deliberate — the radio still needs to POST
   its value when it's already checked (the account's current plan is Custom) and the form is
   submitted unchanged; a disabled input is excluded from form submission entirely, which would
   silently flip a Custom account to Free on an untouched save. See Account.cshtml's own comment
   and the OnPostSaveAsync guard for the server-side half of "downgradable out of, never selectable
   into". */
.account-plan-option-disabled {
  background: var(--canvas);
  border-style: dashed;
  cursor: not-allowed;
  pointer-events: none;
}

.account-plan-option-disabled .account-plan-radio-dot {
  border-style: dashed;
}

.account-plan-option-disabled .account-plan-option-name,
.account-plan-option-disabled .account-plan-option-desc {
  color: var(--text-label);
}

.account-status-row {
  display: grid;
  grid-template-columns: 220px 1fr;
  gap: 1rem;
  align-items: start;
  margin-bottom: 1.25rem;
}

@media (max-width: 560px) {
  .account-status-row {
    grid-template-columns: 1fr;
  }
}

/* Self-contained for the same reason .filter-form .search-field > input is (see that rule's own
   comment) — this <select> has a visually-hidden sibling <label>, not a wrapping one, so the
   global "form label select" rule never matches it. */
.account-status-row select {
  display: block;
  width: 100%;
  padding: 0.6rem 0.8rem;
  font-family: inherit;
  font-size: 1rem;
  font-weight: 400;
  color: var(--ink);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  min-height: 44px;
  background: var(--surface);
  transition: border-color 0.1s ease, box-shadow 0.1s ease;
}

.account-status-row select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.account-status-hint {
  margin: 0;
  padding-top: 0.65rem;
  font-size: 0.71875rem;
  line-height: 1.6;
  color: var(--ink-muted);
}

/* Card-foot row — same tonal role .spaces-table-footer already uses (--canvas), just a save/reset
   action row instead of a nav link. Negative margin cancels .card's own 1.75rem padding so the
   foot spans the card's full width, matching .spaces-table-footer's edge-to-edge look. */
.account-form-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin: 1.25rem -1.75rem -1.75rem;
  padding: 0.9rem 1.75rem;
  background: var(--canvas);
  border-radius: 0 0 11px 11px;
}

.account-form-footer-note {
  font-size: 0.71875rem;
  color: var(--ink-muted);
}

.account-form-footer-actions {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.account-form-footer-actions .auth-submit {
  width: auto;
}

/* Nothing in the base .auth-submit/button rules styles :disabled at all (no other primary button
   in the app is ever conditionally disabled) — without this the Save button looked fully "press
   me" even while internal-account.js had it disabled, which is the opposite of the point of
   disabling it in the first place (Kontoadministration §5c). */
.account-save-button:disabled {
  background: var(--overlay-medium);
  color: var(--ink-muted);
  cursor: not-allowed;
}

/* Plain text "Återställ" link — deliberately lighter than the primary Save action, same weight
   .space-person-remove already uses for a subtle row-level text action. */
.account-form-footer-reset {
  color: var(--ink-muted);
  text-decoration: none;
  font-size: 0.8125rem;
  font-weight: 600;
}

.account-form-footer-reset:hover {
  color: var(--ink);
  text-decoration: underline;
}

.account-downgrade-warning {
  margin: 0 0 1rem;
  padding: 0.8125rem 1rem;
  border-left: 3px solid var(--pending-border);
  background: var(--surface);
  border-radius: 0 8px 8px 0;
  font-size: 0.78125rem;
  line-height: 1.6;
  color: var(--ink);
}

/* CaptureConsent.cshtml — GDPR Art. 9 pre-screen override. Narrower than the default .page/.card
   width (1320px) since this is explanatory prose the user actually needs to read, not a data
   table or grid. */
.sensitive-consent-card {
  max-width: 640px;
  margin-top: 1.5rem;
}

.sensitive-consent-card h1 {
  margin-top: 0;
}

.sensitive-consent-card p {
  line-height: 1.6;
}

.sensitive-consent-form {
  margin-top: 1.5rem;
}

.sensitive-consent-form .checkbox-label {
  align-items: flex-start;
}

.sensitive-consent-form button,
.sensitive-consent-card form + form button {
  margin-top: 1rem;
}

.sensitive-consent-card form + form {
  margin-top: 0.75rem;
}

/* CaptureErrors.cshtml — consent detail, same size as .capture-error-detail but plain (not
   monospace/code-styled) since this is a normal sentence, not a raw diagnostic code. */
.capture-consent-detail {
  font-size: 0.8125rem;
  color: var(--ink-muted);
}
