/* --- 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. */

* {
  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 {
  --ink: #12213b;
  --ink-muted: #5b6579;
  --hairline: #e2e5eb;
  --surface: #ffffff;
  --canvas: #f7f8fa;
  --navy: #16294f;
  --navy-hover: #0f1d3a;
  /* 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: #585edd;
  --accent-hover: #4b50bc;
  --accent-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);
  /* Marketing-only tokens (Index/Faq/Privacy/Cookies — see body.marketing-bg). Deliberately
     *not* mirrored the same across themes the way every other token above is meant to be subtle
     UI chrome; a marketing "storefront" is allowed — expected — to look meaningfully different
     switching light/dark, not just swap a card's fill colour. Light reads bright/airy (colour on
     a near-white base); dark reads deep/moody (colour glowing out of near-black) — two distinct
     moods, not one gradient with an inverted card sitting on it. */
  --marketing-bg: linear-gradient(180deg, #f0f1fb 0%, #f7f8fa 60%);
  --landing-hero-bg:
    radial-gradient(700px 420px at 8% -15%, rgba(79, 70, 229, 0.18), transparent 60%),
    radial-gradient(560px 380px at 100% -10%, rgba(13, 148, 136, 0.14), transparent 55%),
    radial-gradient(620px 420px at 105% 115%, rgba(219, 39, 119, 0.12), transparent 55%),
    #ffffff;
  color-scheme: light;
}

:root[data-theme="dark"] {
  --ink: #e7e9f3;
  --ink-muted: #a3aac0;
  --hairline: #2a3350;
  --surface: #182238;
  --canvas: #0e1526;
  --navy-hover: #1f3563;
  /* 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: #7e8bff;
  --accent-hover: #a1b3ff;
  --accent-ink: #14123a;
  --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);
  --marketing-bg: linear-gradient(180deg, #060a15 0%, #0e1526 60%);
  --landing-hero-bg:
    radial-gradient(700px 420px at 8% -15%, rgba(129, 140, 248, 0.4), transparent 60%),
    radial-gradient(560px 380px at 100% -10%, rgba(45, 212, 191, 0.28), transparent 55%),
    radial-gradient(620px 420px at 105% 115%, rgba(236, 72, 153, 0.22), transparent 55%),
    #05070f;
  color-scheme: dark;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --ink: #e7e9f3;
    --ink-muted: #a3aac0;
    --hairline: #2a3350;
    --surface: #182238;
    --canvas: #0e1526;
    --navy-hover: #1f3563;
    --accent: #7e8bff;
    --accent-hover: #a1b3ff;
    --accent-ink: #14123a;
    --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);
    --marketing-bg: linear-gradient(180deg, #060a15 0%, #0e1526 60%);
    --landing-hero-bg:
      radial-gradient(700px 420px at 8% -15%, rgba(129, 140, 248, 0.4), transparent 60%),
      radial-gradient(560px 380px at 100% -10%, rgba(45, 212, 191, 0.28), transparent 55%),
      radial-gradient(620px 420px at 105% 115%, rgba(236, 72, 153, 0.22), transparent 55%),
      #05070f;
    color-scheme: dark;
  }
}

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;
}

/* Marketing pages only (Index landing + Faq/Privacy/Cookies — set via ViewData["Marketing"], see
   _Layout.cshtml) — a distinct "storefront" identity, separate from the plain functional --canvas
   every app page uses, using the --marketing-bg/--landing-hero-bg tokens above so the switch
   between light and dark actually changes the page's whole mood, not just a card's fill colour. */
body.marketing-bg {
  min-height: 100dvh;
  background: var(--marketing-bg);
}

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

/* --- Landing page hero — the one place that gets the louder --landing-hero-bg (the ambient
   --marketing-bg wash alone is calm enough for a page of body text like Privacy, but the landing
   page's whole point is to make a first impression). A contained rounded panel rather than a
   full-bleed band — simpler to build well than breaking out of .page's max-width, and it still
   reads as a deliberate "hero", just scoped like everything else on the page. --- */
.landing-hero {
  text-align: center;
  padding: 3.5rem 2rem;
  margin: 1.5rem 0 2rem;
  border-radius: 20px;
  border: 1px solid var(--hairline);
  background: var(--landing-hero-bg);
}

.landing-title {
  font-size: 2.5rem;
  margin: 0.5rem 0 1rem;
}

.landing-subtitle {
  font-size: 1.05rem;
  line-height: 1.5;
  color: var(--ink-muted);
  margin: 0 auto 2rem;
  max-width: 40rem;
}

.landing-cta {
  display: flex;
  justify-content: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}

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

.landing-features {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
  max-width: 960px;
  margin: 0 auto 2.5rem;
}

.landing-feature {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 12px;
  padding: 1.5rem;
}

.landing-feature-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 999px;
  background: var(--canvas);
  color: var(--accent);
  margin-bottom: 0.85rem;
}

.landing-feature h2 {
  color: var(--ink);
  font-size: 1.05rem;
  margin: 0 0 0.4rem;
}

.landing-feature p {
  color: var(--ink-muted);
  font-size: 0.9rem;
  margin: 0;
}

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

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

  .landing-features {
    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;
}

/* 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;
  background: var(--navy);
  color: #fff;
  padding: 1.1rem 2rem;
}

.hero-inner {
  position: relative;
  z-index: 1;
  max-width: 960px;
  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;
}

/* A real link (to "/" — the landing page, or a signed-in user's capture page once IndexModel
   redirects them there) wrapping the wordmark logo image. */
.brand {
  display: inline-flex;
  align-items: center;
}

/* width auto keeps wordmark-white.svg's own aspect ratio, never distorts. 32px cap height ~2x the
   nav text's 17px, not 3x — the previous 3.6rem (~58px) made the logo the page's heaviest
   element instead of a sender mark. 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: 32px;
  width: auto;
}

.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: rgba(255, 255, 255, 0.82);
  text-decoration: none;
  font-weight: 500;
  font-size: 0.875rem;
  padding: 0.4rem 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: #fff;
  background: rgba(255, 255, 255, 0.14);
  text-decoration: none;
}

/* The one actual call to action in the header for a signed-out visitor — everything else up here
   is a plain nav link, so this gets the same "press me" accent-filled treatment as the site's
   primary buttons elsewhere (Skapa konto etc.), instead of blending in as just another link. */
.site-nav a.login-link {
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
}

.site-nav a.login-link:hover {
  background: var(--accent-hover);
  color: var(--accent-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. */
  height: 2.5rem;
  /* <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;
  padding: 0.3rem;
  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: rgba(255, 255, 255, 0.7);
}

.avatar {
  width: 30px;
  height: 30px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.18);
  border: 1px solid rgba(255, 255, 255, 0.3);
  color: #fff;
  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: rgba(255, 255, 255, 0.14);
  /* 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;
}

/* 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. */
.nav-menu, .lang-menu, .theme-menu {
  position: relative;
  height: 2.5rem;
  /* 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;
}

/* Visual order is set with flexbox `order`, independent of source order, so the same markup can
   read "page links, language, account" on desktop and "language, hamburger" on mobile (the
   hamburger's own panel carries the page-link/account items that got hidden) without needing two
   different DOM arrangements. */
.nav-links-inline {
  order: 1;
}

.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: rgba(255, 255, 255, 0.82);
  margin: 0;
  border-radius: 999px;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.nav-menu summary, .theme-menu summary {
  width: 2.5rem;
  height: 2.5rem;
  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. */
.lang-menu summary {
  padding: 0.4rem 0.65rem;
  font-size: 0.875rem;
  font-weight: 500;
}

/* Full language name by default (there's room for it); swapped for the short code on narrow
   viewports (see @media max-width:640px) where the header needs to stay a single compact row. */
.lang-name-code {
  display: none;
}

.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: #fff;
  background: rgba(255, 255, 255, 0.14);
  /* 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;
}

.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);
}

/* .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;
}

/* display:contents makes this wrapper invisible to layout — the two links behave exactly as if
   they were direct children of .site-nav, so desktop looks identical to before .nav-menu existed.
   The 640px breakpoint below swaps this out for the hamburger instead. */
.nav-links-inline {
  display: contents;
}

.link-button {
  background: none;
  border: none;
  padding: 0.4rem 0.7rem;
  border-radius: 6px;
  color: rgba(255, 255, 255, 0.82);
  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: #fff;
  background: rgba(255, 255, 255, 0.14);
  filter: none;
}

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

.site-footer {
  border-top: 1px solid var(--hairline);
  padding: 1.25rem 1.5rem;
  margin-top: 2rem;
}

.site-footer-inner {
  max-width: 960px;
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.5rem 1.5rem;
  color: var(--ink-muted);
  font-size: 0.8rem;
}

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

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

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


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

/* 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. */
.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;
}

.auth-card {
  max-width: 400px;
  margin: 2.5rem auto;
}

.auth-eyebrow {
  margin: 0 0 0.4rem;
  color: var(--accent);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  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;
}

.toggle-password .icon-eye-off {
  display: none;
}

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

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

.auth-forgot {
  margin: -0.5rem 0 1.25rem;
  text-align: right;
  font-size: 0.82rem;
}

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

/* 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%;
}

.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. */
.settings-section > *: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). */
button, input[type="submit"], .secondary-button, .delete-button, a.auth-submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 8px;
  padding: 0.6rem 1.2rem;
  font-size: 0.9rem;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  min-height: 44px; /* comfortable touch target on phones */
  transition: background-color 0.1s ease;
}

/* Primary action colour — the one vivid, consistent accent (indigo), not the structural navy the
   header/selected-state chips use. Keeping these distinct is what makes "this is the button to
   press" read clearly against "this is where you already are" (.page-link.active etc, still navy
   in light mode). */
button, input[type="submit"], a.auth-submit {
  background: var(--accent);
  color: var(--accent-ink);
}

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

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

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

/* .settings-section's own background IS --canvas — without this, a .secondary-button's hover
   state (above) becomes invisible for any button living inside one of these panels (Kategori's
   "Spara kategori", Dela kvitto's "Skapa delningslänk"/"Kopiera"/"Återkalla" etc.), since the
   "highlighted" state and the resting background would be the exact same colour. --surface reads
   as the button visibly lifting off the tinted panel instead. */
.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. */
a.secondary-button:hover, a.page-link:hover, a.delete-button:hover {
  text-decoration: none;
}

/* 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: inherit;
  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;
}

.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;
}

.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. */
.capture-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.25rem;
}

.capture-card-header h1 {
  margin: 0;
}

/* Hidden on desktop — the inline nav ("Lägg till kvitton" / "Mina kvitton") is always visible
   there already, so a second link to the same place would be redundant. On mobile the nav
   collapses into the hamburger menu (see the @media max-width:640px block further down), which
   isn't obviously tappable/isn't obviously hiding more than one item, so this page — the one most
   people land on right after signing in — gets its own direct way to the receipt list instead of
   relying on the hamburger being discovered. */
.mobile-receipts-shortcut {
  display: none;
}

/* --- File picker (capture form) — the native <input type="file"> can't be restyled directly in
   any browser, so it's visually hidden (kept focusable/keyboard-usable, not display:none) and a
   <label for="..."> styled as a regular secondary-button acts as the visible trigger. --- */

.file-picker {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  flex-wrap: wrap;
  margin-bottom: 1.25rem;
}

/* Full label ("Välj filer") by default; swapped for a mobile-accurate one ("Ta kort/välj fil") at
   @media max-width:640px below — same underlying <input type="file"> either way (image/* +
   multiple already surfaces "Camera" as an option in the OS picker on phones), only the wording
   changes to describe what a phone's picker actually offers versus a desktop's plain file browser. */
.picker-label-mobile {
  display: none;
}

.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 {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 1.25rem;
}

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

.queue-item-name {
  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) --- */

/* Puts the search field and the result count on the same row on wide viewports (search left,
   count right via space-between — there are only ever these two children) — on narrow
   viewports flex-wrap naturally stacks them. The Lista/Kalender toggle lives in .page-header-row
   instead (next to the <h1>), not in this toolbar. */
/* Grid, not flex — .filter-form, .result-count and .search-options-panel are its three children
   (see Receipts.cshtml) and need to swap relative vertical position between desktop and mobile
   (see the named areas below). flex-wrap + order looked like the obvious tool for that, but proved
   unreliable here: with the panel's flex-basis clamped by its own max-width, Chrome sometimes
   placed it on the FIRST line ahead of items with a lower `order`, not after them — line-breaking
   order and paint order diverged in a way that isn't worth fighting. Named grid areas make each
   breakpoint's layout an explicit, unambiguous template instead. */
.receipts-toolbar {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "form  count"
    "panel panel";
  column-gap: 1rem;
  row-gap: 0.75rem;
  margin-top: 1.25rem;
  margin-bottom: 1.5rem;
}

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

.receipts-toolbar .result-count {
  grid-area: count;
  align-self: end;
}

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

.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/options buttons (right), same absolute-positioned-inside-a-relative-
   wrapper pattern as .password-field's show/hide toggle. */
.search-field {
  position: relative;
}

/* 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 3.9rem 0.6rem 2.5rem;
  font: inherit;
  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 search-options 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: 1.9rem;
  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);
}

.search-options-toggle {
  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);
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}

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

/* Small filled dot, same idea as an unread-count badge — the only hint that a date range is
   currently applied without having to open the panel to check. Positions relative to the toggle
   itself (already position:absolute, so it's already a valid containing block — no extra
   position rule needed here). */
.search-options-toggle.has-active-filter::after {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  width: 0.45rem;
  height: 0.45rem;
  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) — plain block left-alignment, no auto margin
     needed. 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: 380px;
  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-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: inherit;
  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);
}

.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. */

.category-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.25rem 0.5rem 0.25rem 0.65rem;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  white-space: nowrap;
  background: var(--surface);
  border: 1px solid var(--hairline);
  color: var(--ink-muted);
}

.category-badge::before {
  content: "";
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  flex-shrink: 0;
  background: var(--dot-color, var(--ink-muted));
}

/* 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);
  border-color: var(--active-bg);
  color: var(--active-ink);
}

.category-badge.active::before {
  background: var(--active-ink);
}

.category-badge-neutral::before {
  content: none;
}

/* 12 hues, the first 10 walking evenly around the color wheel (36° apart) so the 10 predefined
   categories (each fixed to one slot, see CategoryBadges.cs) are as visually distinct from each
   other as 10 colors can be — picking arbitrary "nice" colors instead left two pairs (red/pink,
   teal/cyan) too close together to tell apart at a glance. */
.category-badge-0 { --dot-color: #ef4444; }  /* red */
.category-badge-1 { --dot-color: #f97316; }  /* orange */
.category-badge-2 { --dot-color: #eab308; }  /* yellow */
.category-badge-3 { --dot-color: #22c55e; }  /* green */
.category-badge-4 { --dot-color: #14b8a6; }  /* teal */
.category-badge-5 { --dot-color: #06b6d4; }  /* cyan */
.category-badge-6 { --dot-color: #3b82f6; }  /* blue */
.category-badge-7 { --dot-color: #6366f1; }  /* indigo */
.category-badge-8 { --dot-color: #8b5cf6; }  /* violet */
.category-badge-9 { --dot-color: #ec4899; }  /* pink */
.category-badge-10 { --dot-color: #65a30d; } /* lime — hash-fallback only */
.category-badge-11 { --dot-color: #78716c; } /* stone — hash-fallback only */

.result-count {
  margin: 0;
  /* Right-aligns it even when .receipts-toolbar wraps onto multiple lines on narrow viewports (see
     @media max-width:640px below) — without this a lone wrapped flex item falls back to
     flex-start, which read as a stray line of text rather than a compact, deliberately-placed
     count. */
  margin-left: auto;
}

/* --- View toggle (Lista / Kalender) — a small segmented control of icon buttons, not plain
   links, so switching views reads as choosing a tab rather than navigating to a different page.
   Icon-only (not text labels) so it stays compact enough to always sit top-right next to the
   <h1> in .page-header-row, even on narrow viewports — see aria-label/title in the markup for
   the accessible name that replaces the removed visible text. --- */

.view-toggle {
  display: inline-flex;
  gap: 0.2rem;
  padding: 0.2rem;
  background: var(--canvas);
  border: 1px solid var(--hairline);
  border-radius: 999px;
  margin-bottom: 1.5rem;
}

.view-toggle-item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.5rem;
  border-radius: 999px;
  color: var(--ink-muted);
  text-decoration: none;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.view-toggle-item svg {
  display: block;
}

a.view-toggle-item:hover {
  color: var(--ink);
  background: var(--overlay-weak);
  text-decoration: none;
}

.view-toggle-item.active {
  background: var(--active-bg);
  color: var(--active-ink);
}

/* --- 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;
}

.page-link:hover {
  background: var(--canvas);
}

.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;
}

.receipt-image-thumb {
  max-width: 160px;
  max-height: 160px;
  /* 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;
}

.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.4rem 1.25rem 0.4rem 0;
  border-bottom: none;
  vertical-align: top;
  font-size: 0.92rem;
}

.meta-table th {
  color: var(--ink-muted);
  font-weight: 600;
  white-space: nowrap;
  width: 1%;
}

/* Sits in the same card as .receipt-overview (see ReceiptDetail.cshtml), bottom-right — a small
   diagnostic caption, not part of the formal record, so it reads as a footnote rather than
   another row of receipt data. */
.extraction-method-note {
  text-align: right;
  margin-top: 0.75rem;
}

/* 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;
}

/* Inside a .page-header-actions cluster the toggle sits next to page-level buttons/heading, not
   above a following section — its own standalone margin-bottom (meant for when it's used alone,
   e.g. above ReceiptsCalendar's month grid) would just add unwanted extra height to the row. */
.page-header-actions .view-toggle {
  margin-bottom: 0;
}

/* .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;
}

/* --- 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;
}

/* 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;
}

.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);
}

/* 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;
  vertical-align: middle;
}

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

.receipts-table .receipt-col-country {
  width: 1%;
  white-space: nowrap;
  text-align: center;
}

/* --- 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) --- */

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

.calendar-nav {
  display: flex;
  justify-content: space-between;
  margin-bottom: 1rem;
}

.calendar-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 1px;
  background: var(--hairline);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  overflow: hidden;
}

.calendar-weekday {
  background: var(--canvas);
  padding: 0.5rem;
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-muted);
  text-align: center;
}

.calendar-day {
  background: var(--surface);
  min-height: 5.5rem;
  padding: 0.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

.calendar-day-empty {
  background: var(--canvas);
}

.calendar-day-number {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--ink-muted);
}

.calendar-receipt {
  font-size: 0.72rem;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: block;
  padding: 0.1rem 0.3rem;
  background: var(--canvas);
  border-radius: 4px;
}

.calendar-receipt:hover {
  color: var(--ink);
  text-decoration: underline;
}

/* --- Responsive --- */

@media (max-width: 640px) {
  .hero {
    padding: 1.1rem 1rem;
  }

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

  /* 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-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/login link 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. */
  .nav-menu {
    display: flex;
    order: 3;
  }

  /* .login-link needs the qualified `.site-nav a.login-link` form here, not just `.login-link` —
     it's an <a> inside .site-nav, so the unconditional `.site-nav a { display: inline-flex }`
     base rule (tag+class, higher specificity) would otherwise silently beat a bare single-class
     `display: none` regardless of source order, leaving it visible alongside the hamburger. */
  .nav-links-inline, .user-menu, .site-nav a.login-link {
    display: none;
  }

  .theme-menu {
    order: 1;
  }

  .lang-menu {
    order: 2;
  }

  /* Short code instead of the full name here — keeps the header a single fixed-height row
     regardless of which language's name is longest (see .lang-name-code's base rule for why the
     full name is the default everywhere above this width). */
  .lang-name-full {
    display: none;
  }

  .lang-name-code {
    display: inline;
  }

  /* Single column, search field then panel then counter — the panel opens directly under the
     search bar/toggle button, and the count (still right-aligned via .result-count's own
     margin-left:auto) moves to the bottom instead of sitting between the search bar and the panel,
     which used to read as the panel "landing under the receipt counter" when opened. Desktop's
     "form + count share a row, panel spans full-width below" template is set in the base
     .receipts-toolbar rule — only the template changes here, none of the grid-area assignments
     on the children need to. */
  .receipts-toolbar {
    grid-template-columns: 1fr;
    grid-template-areas:
      "form"
      "panel"
      "count";
    row-gap: 0.35rem;
    margin-bottom: 1rem;
  }

  .calendar-grid {
    grid-template-columns: repeat(7, minmax(2.6rem, 1fr));
    font-size: 0.7rem;
    /* At widths under ~320px, 7 columns at their readable minimum (2.6rem) don't fit the card —
       without this the grid just got clipped by the base overflow:hidden, silently cutting off
       Saturday/Sunday. Scrolling keeps every day reachable instead of losing them off-screen. */
    overflow-x: auto;
  }

  .calendar-day {
    min-height: 3.5rem;
    padding: 0.25rem;
  }

  .calendar-receipt {
    font-size: 0.62rem;
  }

  .card {
    padding: 1.35rem 1.15rem;
  }

  /* See the base rules further up for why these exist — mobile-only versions of the capture
     page's "Mina kvitton" shortcut and the file-picker's label. */
  .mobile-receipts-shortcut {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.4rem 0.75rem;
    border: 1px solid var(--hairline);
    border-radius: 999px;
    color: var(--ink);
    font-size: 0.82rem;
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    flex-shrink: 0;
  }

  .mobile-receipts-shortcut:hover {
    background: var(--canvas);
    text-decoration: none;
  }

  .picker-label-full {
    display: none;
  }

  .picker-label-mobile {
    display: inline;
  }

  .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;
  }

  .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);
  }

  /* Dropped on the compact mobile card — the country's already the least useful field to scan
     in a list (merchant/total/date matter far more at a glance), and there wasn't a clean spot
     for it that didn't crowd the total or the date. Still shown on the receipt detail page. */
  .receipts-table .receipt-col-country {
    display: none;
  }

  .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-lines, .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;
  }
}

/* --- 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;
  }
}
