/* ═══════════════════════════════════════════════════════════════════════════
   Flutra Business Suite — Design System
   "Aurora" · light-first · Orchid #914C9D + Apricot #FBBA7E on paper white
   Sora + DM Sans + JetBrains Mono · Emil Kowalski animation philosophy
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Fonts ──────────────────────────────────────────────────────────────── */
/* 500 added to Sora (small caps kickers read better than 600 at 10px) and
   700 to DM Sans (the reference's numerics carry real weight). The heading
   face stays Sora — chosen from a rendered 8-way specimen on 2026-08-19. */
@import url('https://fonts.googleapis.com/css2?family=Sora:wght@500;600;700;800&family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;1,9..40,400&family=JetBrains+Mono:wght@400;500;700&display=swap');

/* ── Design Tokens ──────────────────────────────────────────────────────────
   The palette is the app's own logo: Orchid #914C9D and Apricot #FBBA7E, on
   paper. Two rules govern it:

   1. LIGHT IS THE DEFAULT. The product makes documents; documents are white.
      `:root` therefore carries the light values, dark mode is opted into by
      `[data-theme="dark"]` or by the OS preference when the user has made no
      explicit choice. (Theme.toggle() in app.js writes an explicit value on
      the first click either way, so nothing about the switch changes.)
   2. ORCHID IS THE ONLY ACCENT. Apricot is a *highlight*, never a
      foreground colour: at 0.571 relative luminance nothing legible sits on
      white with it. It carries tints, sparkline fills, focus bloom, and it
      is the readable accent in DARK mode, where it has room to be one.

   Measured against the surface each colour actually sits on:
     white on --brand-500 (#914C9D) ......... 5.64:1   AA ✔ (was 3.51 teal)
     --brand-600 (#7A3E85) text on white .... 7.60:1   AAA ✔
     --brand-300 (#D9A9E0) text on #17111C .. 9.42:1   AAA ✔ (dark mode)
     --peach-700 (#96560C) text on white .... 5.06:1   AA ✔
   ──────────────────────────────────────────────────────────────────────── */
:root {
  /* Brand — Orchid */
  --brand-300: #D9A9E0;   /* dark-mode text / hover                        */
  --brand-400: #A968B4;   /* dark-mode accent, light-mode decorative fills  */
  --brand-500: #914C9D;   /* THE brand colour — fills, active states        */
  --brand-600: #7A3E85;   /* light-mode accent TEXT, hover fill             */
  --brand-700: #5F2F69;   /* pressed / deepest                              */

  /* Accent — Apricot. Decorative in light, legible in dark. */
  --peach-200: #FEEBD6;
  --peach-300: #FDD6AE;
  --peach-400: #FBBA7E;   /* the logo's own apricot                         */
  --peach-500: #F0A15A;
  --peach-700: #96560C;   /* the only apricot that may carry text on white  */

  /* Surfaces — warm paper. Cards are pure white; the page is a shade
     off it, so a card reads as a sheet lying on a desk rather than a
     rectangle drawn on the same plane. */
  --bg:        #F7F4F1;
  --surface-1: #FFFFFF;
  --surface-2: #F5F1EE;
  --surface-3: #EDE7E3;
  --surface-4: #E0D8D3;

  /* Borders — warm hairlines, never pure black at low alpha */
  --border:        #EAE3DE;
  --border-strong: #D9D0CA;
  --border-focus:  rgba(145,76,157,0.45);

  /* Text — warm near-black with a trace of the brand hue in it */
  --text-1: #241B28;   /* 15.6:1 on white                                   */
  --text-2: #6A6270;   /* 5.9:1  — the supporting-text step                  */
  /* DECORATIVE ONLY — dots, dividers, placeholders, disabled glyphs. It is
     the one token in this file that does not clear 4.5:1, and it must not
     carry text: the 2026-08-19 pass already moved the dashboard's supporting
     copy off it for exactly this reason, and this pass finished the job in
     the two editors and Settings, which were still setting it inline on real
     hint text ("(optional)", "(Kostenlos)", "max 5 MB") at 3.01:1.
     Anything a user has to READ takes --text-2. */
  --text-3: #9A929F;
  --text-brand: var(--brand-600);

  /* Semantic — desaturated to sit beside orchid without shouting */
  --success:    #2F8F5B; --success-bg:  rgba(47,143,91,0.10);
  --warning:    #C98A10; --warning-bg:  rgba(217,161,24,0.13);
  --danger:     #C7452F; --danger-bg:   rgba(199,69,47,0.10);
  --info:       #2E7C97; --info-bg:     rgba(46,124,151,0.10);
  --violet:     #6455C7; --violet-bg:   rgba(100,85,199,0.10);

  /* ON-TINT TEXT. A semantic colour that reads correctly as a 4px dot or a
     6px bar is NOT dark enough to carry 11px type on its own 10% tint — a
     status pill is the most common place in any dashboard where that gets
     forgotten. Measured on the exact tint each one sits on:
       --on-success  6.03:1      --on-danger  6.68:1
       --on-warning  6.04:1      --on-info    6.74:1      --on-violet 7.8:1
     Dark mode reuses the base colours: they are already 6–9:1 there, and
     darkening them would be the wrong direction entirely. */
  --on-success: #1E6837;
  --on-warning: #7A5006;
  --on-danger:  #96301C;
  --on-info:    #1F5A6E;
  --on-violet:  #443A9B;

  /* Translucent chrome (topbar, sticky form footer) and modal scrim.
     Tokenised so the two themes stop needing duplicate rules. */
  --glass: rgba(247,244,241,0.78);
  --scrim: rgba(36,27,40,0.42);

  /* Layout */
  --sidebar-w:    248px;
  --sidebar-cw:   72px;
  --topbar-h:     60px;
  --content-max:  1400px;

  /* Radius — softer, and varied: tight inside, generous outside */
  --r-sm: 8px;
  --r-md: 11px;
  --r-lg: 16px;
  --r-xl: 22px;

  /* Shadows — layered like real light, tinted with the ink colour.
     Two offsets per step: a tight contact shadow plus a wide ambient one.
     A single blurred black rectangle is the thing that makes a UI look
     cheap; this is the thing that stops it. */
  --shadow-xs:   0 1px 2px rgba(36,27,40,0.05);
  --shadow-sm:   0 1px 3px rgba(36,27,40,0.05), 0 1px 2px rgba(36,27,40,0.03);
  --shadow-md:   0 4px 8px -2px rgba(36,27,40,0.07), 0 2px 4px -2px rgba(36,27,40,0.04);
  --shadow-lg:   0 12px 20px -6px rgba(36,27,40,0.10), 0 4px 8px -4px rgba(36,27,40,0.05);
  --shadow-xl:   0 24px 40px -12px rgba(36,27,40,0.16), 0 8px 16px -8px rgba(36,27,40,0.07);
  --shadow-brand:0 4px 14px -4px rgba(145,76,157,0.42);
  --shadow-glow: 0 0 0 3px rgba(145,76,157,0.14);

  /* Animation */
  --ease-out:    cubic-bezier(0.23, 1, 0.32, 1);
  --ease-spring: cubic-bezier(0.32, 0.72, 0, 1);
  --ease:        cubic-bezier(0.22, 0.61, 0.36, 1);
  --dur-fast:    150ms;
  --dur-normal:  220ms;
  --dur-slow:    320ms;
}

/* ── Dark mode ──────────────────────────────────────────────────────────────
   A plum-black, not a blue-black: the same hue family as the brand, three
   stops further down, so the two themes read as one product lit differently.
   Apricot becomes the readable accent here — it has the luminance for it. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:        #100B14;
    --surface-1: #17111C;
    --surface-2: #1F1826;
    --surface-3: #2A2133;
    --surface-4: #372C42;

    --border:        rgba(255,255,255,0.08);
    --border-strong: rgba(255,255,255,0.15);
    --border-focus:  rgba(217,169,224,0.5);

    --text-1: #F2ECF4;
    --text-2: #B3A8BB;
    --text-3: #7A6F83;   /* decorative only — see the light-mode note */
    --text-brand: var(--brand-300);

    --success: #4FBF83; --success-bg: rgba(79,191,131,0.13);
    --warning: #E8B33D; --warning-bg: rgba(232,179,61,0.14);
    --danger:  #E86A54; --danger-bg:  rgba(232,106,84,0.14);
    --info:    #5AA8C4; --info-bg:    rgba(90,168,196,0.14);
    --violet:  #948AE3; --violet-bg:  rgba(148,138,227,0.14);

    --on-success: var(--success); --on-warning: var(--warning);
    --on-danger:  var(--danger);  --on-info:    var(--info);
    --on-violet:  var(--violet);

    --glass: rgba(16,11,20,0.78);
    --scrim: rgba(8,5,10,0.62);

    --shadow-xs:   0 1px 2px rgba(0,0,0,0.4);
    --shadow-sm:   0 1px 3px rgba(0,0,0,0.45), 0 1px 2px rgba(0,0,0,0.3);
    --shadow-md:   0 4px 10px -2px rgba(0,0,0,0.5), 0 2px 5px -2px rgba(0,0,0,0.35);
    --shadow-lg:   0 14px 26px -8px rgba(0,0,0,0.6), 0 5px 10px -5px rgba(0,0,0,0.4);
    --shadow-xl:   0 28px 48px -14px rgba(0,0,0,0.7), 0 10px 20px -10px rgba(0,0,0,0.5);
    --shadow-brand:0 4px 16px -4px rgba(169,104,180,0.5);
    --shadow-glow: 0 0 0 3px rgba(169,104,180,0.26);
  }
}
[data-theme="dark"] {
  --bg:        #100B14;
  --surface-1: #17111C;
  --surface-2: #1F1826;
  --surface-3: #2A2133;
  --surface-4: #372C42;

  --border:        rgba(255,255,255,0.08);
  --border-strong: rgba(255,255,255,0.15);
  --border-focus:  rgba(217,169,224,0.5);

  --text-1: #F2ECF4;
  --text-2: #B3A8BB;
  --text-3: #7A6F83;
  --text-brand: var(--brand-300);

  --success: #4FBF83; --success-bg: rgba(79,191,131,0.13);
  --warning: #E8B33D; --warning-bg: rgba(232,179,61,0.14);
  --danger:  #E86A54; --danger-bg:  rgba(232,106,84,0.14);
  --info:    #5AA8C4; --info-bg:    rgba(90,168,196,0.14);
  --violet:  #948AE3; --violet-bg:  rgba(148,138,227,0.14);

  --on-success: var(--success); --on-warning: var(--warning);
  --on-danger:  var(--danger);  --on-info:    var(--info);
  --on-violet:  var(--violet);

  --glass: rgba(16,11,20,0.78);
  --scrim: rgba(8,5,10,0.62);

  --shadow-xs:   0 1px 2px rgba(0,0,0,0.4);
  --shadow-sm:   0 1px 3px rgba(0,0,0,0.45), 0 1px 2px rgba(0,0,0,0.3);
  --shadow-md:   0 4px 10px -2px rgba(0,0,0,0.5), 0 2px 5px -2px rgba(0,0,0,0.35);
  --shadow-lg:   0 14px 26px -8px rgba(0,0,0,0.6), 0 5px 10px -5px rgba(0,0,0,0.4);
  --shadow-xl:   0 28px 48px -14px rgba(0,0,0,0.7), 0 10px 20px -10px rgba(0,0,0,0.5);
  --shadow-brand:0 4px 16px -4px rgba(169,104,180,0.5);
  --shadow-glow: 0 0 0 3px rgba(169,104,180,0.26);
}

/* An explicit light choice must also win over an OS dark preference. */
[data-theme="light"] {
  --bg:        #F7F4F1;
  --surface-1: #FFFFFF;
  --surface-2: #F5F1EE;
  --surface-3: #EDE7E3;
  --surface-4: #E0D8D3;
  --border:        #EAE3DE;
  --border-strong: #D9D0CA;
  --border-focus:  rgba(145,76,157,0.45);
  --text-1: #241B28;
  --text-2: #6A6270;
  --text-3: #9A929F;
  --text-brand: var(--brand-600);
  --success: #2F8F5B; --success-bg: rgba(47,143,91,0.10);
  --warning: #C98A10; --warning-bg: rgba(217,161,24,0.13);
  --danger:  #C7452F; --danger-bg:  rgba(199,69,47,0.10);
  --info:    #2E7C97; --info-bg:    rgba(46,124,151,0.10);
  --violet:  #6455C7; --violet-bg:  rgba(100,85,199,0.10);
  --on-success: #1E6837; --on-warning: #7A5006; --on-danger: #96301C;
  --on-info:    #1F5A6E; --on-violet:  #443A9B;
  --glass: rgba(247,244,241,0.78);
  --scrim: rgba(36,27,40,0.42);
  --shadow-xs:   0 1px 2px rgba(36,27,40,0.05);
  --shadow-sm:   0 1px 3px rgba(36,27,40,0.05), 0 1px 2px rgba(36,27,40,0.03);
  --shadow-md:   0 4px 8px -2px rgba(36,27,40,0.07), 0 2px 4px -2px rgba(36,27,40,0.04);
  --shadow-lg:   0 12px 20px -6px rgba(36,27,40,0.10), 0 4px 8px -4px rgba(36,27,40,0.05);
  --shadow-xl:   0 24px 40px -12px rgba(36,27,40,0.16), 0 8px 16px -8px rgba(36,27,40,0.07);
  --shadow-brand:0 4px 14px -4px rgba(145,76,157,0.42);
  --shadow-glow: 0 0 0 3px rgba(145,76,157,0.14);
}

/* ── Reset ──────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { font-size: 16px; scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
body {
  font-family: 'DM Sans', system-ui, -apple-system, sans-serif;
  background: var(--bg);
  color: var(--text-1);
  line-height: 1.6;
  min-height: 100dvh;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
img, video { max-width: 100%; display: block; }
button { font-family: inherit; cursor: pointer; }
a { color: var(--text-brand); text-decoration: none; }
input, textarea, select { font-family: inherit; font-size: inherit; }
:focus-visible { outline: 2px solid var(--brand-500); outline-offset: 2px; border-radius: var(--r-sm); }

/* ── Scrollbar ──────────────────────────────────────────────────────────── */
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--text-3); }

/* ══════════════════════════════════════════════════════════════════════════
   LAYOUT
   ══════════════════════════════════════════════════════════════════════════ */

.app-shell {
  display: flex;
  min-height: 100dvh;
}

/* ── Sidebar ────────────────────────────────────────────────────────────── */
.sidebar {
  position: fixed;
  top: 0; left: 0; bottom: 0;
  width: var(--sidebar-w);
  background: var(--surface-1);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  z-index: 200;
  transition: width var(--dur-normal) var(--ease-out),
              transform var(--dur-normal) var(--ease-spring);
  overflow: hidden;
}

.sidebar.collapsed { width: var(--sidebar-cw); }

.sidebar-logo {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 18px 16px 14px;
  border-bottom: 1px solid var(--border);
  min-height: 64px;
  overflow: hidden;
  white-space: nowrap;
}
/* The logo plate is deliberately WHITE in both themes, not a themed surface:
   the symbol is two-tone (purple + peach) artwork that was drawn for a light
   ground, and a dark plate mutes the peach. The 1px border is what keeps the
   plate visible in light mode, where the sidebar is white too. */
.sidebar-logo-mark {
  width: 32px; height: 32px; flex-shrink: 0;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  display: flex; align-items: center; justify-content: center;
  padding: 3px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.16);
}
/* object-fit: contain — the symbol is portrait (184×268), so it must letterbox
   inside the square plate rather than stretch. */
.sidebar-logo-mark img,
.login-logo-mark img,
.pwa-banner-icon img {
  width: 100%; height: 100%;
  object-fit: contain;
  display: block;
}
.sidebar-logo-text {
  overflow: hidden;
  transition: opacity var(--dur-normal), width var(--dur-normal);
}
.sidebar-logo-text strong {
  font-family: 'Sora', sans-serif;
  font-weight: 700; font-size: 14px;
  color: var(--text-1); display: block; line-height: 1.1;
}
.sidebar-logo-text span {
  font-size: 11px; color: var(--text-3); font-weight: 500;
}
.sidebar.collapsed .sidebar-logo-text { opacity: 0; width: 0; overflow: hidden; }

.sidebar-nav {
  flex: 1;
  padding: 10px 8px;
  overflow-y: auto;
  overflow-x: hidden;
}

.nav-section-label {
  font-size: 10px; font-weight: 600; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-3);
  padding: 12px 10px 4px;
  white-space: nowrap;
  overflow: hidden;
  transition: opacity var(--dur-normal);
}
.sidebar.collapsed .nav-section-label { opacity: 0; }

.nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 10px;
  border-radius: var(--r-md);
  color: var(--text-2);
  font-size: 14px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  transition: background var(--dur-fast), color var(--dur-fast), transform var(--dur-fast);
  position: relative;
  text-decoration: none;
}
.nav-item:hover {
  background: var(--surface-3);
  color: var(--text-1);
}
.nav-item.active {
  background: rgba(145,76,157,0.12);
  color: var(--text-brand);
  font-weight: 600;
}
.nav-item.active::before {
  content: '';
  position: absolute; left: 0; top: 50%;
  transform: translateY(-50%);
  width: 3px; height: 60%;
  background: var(--brand-500);
  border-radius: 0 2px 2px 0;
}
.nav-item:active { transform: scale(0.97); }
.nav-icon {
  width: 20px; height: 20px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
}
.nav-icon svg { width: 18px; height: 18px; stroke-width: 1.75; }
.nav-label {
  transition: opacity var(--dur-normal), width var(--dur-normal);
  overflow: hidden;
}
.sidebar.collapsed .nav-label { opacity: 0; width: 0; }
.nav-badge {
  margin-left: auto;
  background: var(--surface-3); color: var(--text-2);
  font-size: 10px; font-weight: 600;
  padding: 2px 6px; border-radius: 10px;
  transition: opacity var(--dur-normal);
}
.sidebar.collapsed .nav-badge { opacity: 0; }

.sidebar-footer {
  padding: 10px 8px 12px;
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 10px;
  overflow: hidden;
  min-height: 56px;
}
.user-avatar {
  width: 32px; height: 32px; flex-shrink: 0;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--brand-500), var(--violet));
  display: flex; align-items: center; justify-content: center;
  color: #fff; font-weight: 700; font-size: 13px;
  text-transform: uppercase;
}
.user-info { overflow: hidden; flex: 1; }
.user-name { font-size: 13px; font-weight: 600; color: var(--text-1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.user-role { font-size: 11px; color: var(--text-3); }
.sidebar.collapsed .user-info { opacity: 0; width: 0; }

/* Sidebar toggle button */
.sidebar-toggle {
  position: fixed;
  top: 16px;
  left: calc(var(--sidebar-w) - 12px);
  width: 24px; height: 24px;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  z-index: 210;
  transition: left var(--dur-normal) var(--ease-out), background var(--dur-fast);
  box-shadow: var(--shadow-sm);
}
.sidebar-toggle:hover { background: var(--surface-3); }
.sidebar-toggle svg { width: 12px; height: 12px; color: var(--text-2); transition: transform var(--dur-normal); }
.sidebar.collapsed ~ .sidebar-toggle { left: calc(var(--sidebar-cw) - 12px); }
.sidebar.collapsed ~ .sidebar-toggle svg { transform: rotate(180deg); }

/* ── Top Bar ────────────────────────────────────────────────────────────── */
.topbar {
  position: fixed;
  top: 0;
  left: var(--sidebar-w);
  right: 0;
  height: var(--topbar-h);
  background: var(--glass);
  backdrop-filter: blur(16px) saturate(1.4);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  padding: 0 24px;
  gap: 16px;
  z-index: 100;
  transition: left var(--dur-normal) var(--ease-out);
}
.sidebar.collapsed ~ .topbar { left: var(--sidebar-cw); }

.topbar-breadcrumb {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--text-2);
  flex: 1;
}
.topbar-breadcrumb .page-title {
  font-family: 'Sora', sans-serif;
  font-weight: 700; font-size: 16px;
  color: var(--text-1);
}
.topbar-breadcrumb .sep { color: var(--text-3); }

.topbar-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

.icon-btn {
  width: 36px; height: 36px;
  border-radius: var(--r-md);
  border: 1px solid var(--border);
  background: var(--surface-2);
  display: flex; align-items: center; justify-content: center;
  color: var(--text-2);
  transition: background var(--dur-fast), color var(--dur-fast), transform var(--dur-fast), border-color var(--dur-fast);
  cursor: pointer;
}
.icon-btn:hover { background: var(--surface-3); color: var(--text-1); border-color: var(--border-strong); }
.icon-btn:active { transform: scale(0.93); }
.icon-btn svg { width: 16px; height: 16px; }

/* Mobile menu toggle */
.mobile-menu-btn { display: none; }

/* ── Main Content ────────────────────────────────────────────────────────── */
.main-content {
  flex: 1;
  margin-left: var(--sidebar-w);
  padding-top: var(--topbar-h);
  min-height: 100dvh;
  transition: margin-left var(--dur-normal) var(--ease-out);
}
.sidebar.collapsed ~ .main-content { margin-left: var(--sidebar-cw); }

.page-container {
  padding: 28px 28px 60px;
  max-width: var(--content-max);
  /* Opacity-only entrance. MUST NOT animate transform: an animated/filled
     transform makes this container the containing block for position:fixed,
     so every modal rendered inside the page gets positioned against the tall
     page body instead of the viewport (clipped top/bottom, sunk below center). */
  animation: pageFadeIn var(--dur-slow) var(--ease-out) both;
}

@keyframes pageFadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes fadeInUp {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Page header */
.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 28px;
  flex-wrap: wrap;
}
.page-header-left h1 {
  font-family: 'Sora', sans-serif;
  font-weight: 700; font-size: 26px;
  color: var(--text-1); line-height: 1.15;
}
.page-header-left p {
  font-size: 14px; color: var(--text-2); margin-top: 4px;
}
.page-header-right {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}

/* ══════════════════════════════════════════════════════════════════════════
   TYPOGRAPHY
   ══════════════════════════════════════════════════════════════════════════ */

h1,h2,h3,h4 { font-family: 'Sora', sans-serif; line-height: 1.2; color: var(--text-1); }
h1 { font-size: 28px; font-weight: 700; }
h2 { font-size: 20px; font-weight: 700; }
h3 { font-size: 16px; font-weight: 700; }
h4 { font-size: 14px; font-weight: 700; }

.mono { font-family: 'JetBrains Mono', monospace; }
.num  { font-family: 'JetBrains Mono', monospace; font-variant-numeric: tabular-nums; }

/* ══════════════════════════════════════════════════════════════════════════
   BUTTONS
   ══════════════════════════════════════════════════════════════════════════ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 9px 18px;
  border-radius: var(--r-md);
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
  border: 1px solid transparent;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--dur-fast), color var(--dur-fast),
              border-color var(--dur-fast), box-shadow var(--dur-fast),
              transform var(--dur-fast);
  -webkit-user-select: none; user-select: none;
  text-decoration: none;
}
.btn:active { transform: scale(0.97); }
.btn svg { width: 16px; height: 16px; flex-shrink: 0; }

.btn-primary {
  /* White on --brand-500 measured 3.51:1 — below AA on the app's most-used
     control, in BOTH themes. --brand-600 is already this button's own border
     colour, so this is the palette's next step down, not a new brand colour:
     white on it is 4.97:1. Hover goes DARKER (brand-700, 7.12:1) because
     lightening would drop it back under the threshold. */
  background: var(--brand-600);
  color: #fff;
  border-color: var(--brand-700);
  box-shadow: 0 1px 4px rgba(145,76,157,0.3);
}
.btn-primary:hover {
  background: var(--brand-700);
  box-shadow: 0 2px 12px rgba(145,76,157,0.4);
}

.btn-secondary {
  background: var(--surface-3);
  color: var(--text-1);
  border-color: var(--border-strong);
}
.btn-secondary:hover { background: var(--surface-4); }

.btn-outline {
  background: transparent;
  color: var(--text-brand);
  border-color: var(--brand-600);
}
.btn-outline:hover { background: rgba(145,76,157,0.08); }

.btn-ghost {
  background: transparent;
  color: var(--text-2);
  border-color: transparent;
}
.btn-ghost:hover { background: var(--surface-2); color: var(--text-1); }

.btn-danger {
  background: var(--danger-bg);
  color: var(--danger);
  border-color: rgba(199,69,47,0.3);
}
.btn-danger:hover { background: rgba(199,69,47,0.18); }

.btn-sm { padding: 6px 13px; font-size: 13px; }
.btn-lg { padding: 12px 24px; font-size: 15px; }

.btn:disabled, .btn[data-loading] {
  opacity: 0.55; cursor: not-allowed; pointer-events: none;
}
.btn[data-loading] {
  pointer-events: none;
  position: relative;
}
.btn[data-loading]::after {
  content: '';
  width: 14px; height: 14px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: spin 0.6s linear infinite;
  margin-left: 4px;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ══════════════════════════════════════════════════════════════════════════
   CARDS
   ══════════════════════════════════════════════════════════════════════════ */

.card {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
}

.card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
}
.card-header h3 { font-size: 14px; font-weight: 700; }
.card-header .card-header-actions { display: flex; align-items: center; gap: 8px; }

.card-body { padding: 20px; }

/* Stat cards */
.stat-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 16px;
  margin-bottom: 28px;
}
.stat-card {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 20px;
  position: relative;
  overflow: hidden;
  transition: border-color var(--dur-fast), box-shadow var(--dur-fast);
}
.stat-card:hover {
  border-color: var(--border-strong);
  box-shadow: var(--shadow-md);
}
.stat-card::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
}
.stat-card.brand::before  { background: var(--brand-500); }
.stat-card.success::before{ background: var(--success); }
.stat-card.warning::before{ background: var(--warning); }
.stat-card.danger::before { background: var(--danger); }
.stat-card.violet::before { background: var(--violet); }

.stat-icon {
  width: 36px; height: 36px;
  border-radius: var(--r-md);
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 14px;
  font-size: 16px;
}
.stat-card.brand  .stat-icon { background: rgba(145,76,157,0.12);  color: var(--text-brand); }
.stat-card.success .stat-icon{ background: var(--success-bg); color: var(--success); }
.stat-card.warning .stat-icon{ background: var(--warning-bg); color: var(--warning); }
.stat-card.danger  .stat-icon{ background: var(--danger-bg);  color: var(--danger); }
.stat-card.violet  .stat-icon{ background: var(--violet-bg);  color: var(--violet); }
.stat-icon svg { width: 18px; height: 18px; }

.stat-value {
  font-family: 'JetBrains Mono', monospace;
  font-size: 24px; font-weight: 700;
  color: var(--text-1); line-height: 1;
  margin-bottom: 5px;
  font-variant-numeric: tabular-nums;
}
.stat-label { font-size: 12px; color: var(--text-3); font-weight: 500; text-transform: uppercase; letter-spacing: 0.04em; }

/* ══════════════════════════════════════════════════════════════════════════
   DATA TABLE
   ══════════════════════════════════════════════════════════════════════════ */

.table-wrap {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
}

.table-toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}
.table-toolbar-left  { display: flex; align-items: center; gap: 8px; flex: 1; flex-wrap: wrap; }
.table-toolbar-right { display: flex; align-items: center; gap: 8px; }

/* Tabs */
.tab-group {
  display: flex;
  align-items: center;
  gap: 2px;
  background: var(--surface-2);
  border-radius: var(--r-md);
  padding: 3px;
  flex-wrap: wrap;
}
.tab {
  padding: 5px 13px;
  border-radius: calc(var(--r-md) - 2px);
  font-size: 13px; font-weight: 500;
  color: var(--text-2);
  cursor: pointer;
  transition: background var(--dur-fast), color var(--dur-fast);
  border: none; background: transparent;
  white-space: nowrap;
  text-decoration: none;
  display: inline-flex; align-items: center; gap: 5px;
}
.tab.active {
  background: var(--surface-1);
  color: var(--text-1);
  font-weight: 600;
  box-shadow: var(--shadow-sm);
}
.tab-count {
  background: var(--surface-3);
  /* --text-3 here was 1.97:1 and --text-2 only reached 4.17:1 against
     --surface-3; at 10px this needs the full 4.5, so it takes --text-1. */
  color: var(--text-1);
  font-size: 10px; font-weight: 700;
  padding: 1px 5px;
  border-radius: 8px;
}
.tab.active .tab-count {
  /* The tint alone carries "active" — the tab itself is already highlighted.
     Teal-on-teal only reached 4.17:1 in light, so the digits stay ink. */
  background: rgba(145,76,157,0.15);
  color: var(--text-1);
}

/* Search */
.search-box {
  position: relative;
  flex: 1; min-width: 180px; max-width: 320px;
}
.search-box svg {
  position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
  width: 14px; height: 14px; color: var(--text-3); pointer-events: none;
}
.search-box input {
  width: 100%;
  padding: 8px 12px 8px 32px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  color: var(--text-1);
  font-size: 13px;
  transition: border-color var(--dur-fast), box-shadow var(--dur-fast);
}
.search-box input:focus {
  border-color: var(--brand-600);
  box-shadow: 0 0 0 3px rgba(145,76,157,0.15);
  outline: none;
}
.search-box input::placeholder { color: var(--text-3); }

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}
.data-table thead th {
  padding: 11px 14px;
  background: var(--surface-2);
  color: var(--text-2);
  font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em;
  text-align: left;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.data-table tbody tr {
  border-bottom: 1px solid var(--border);
  transition: background var(--dur-fast);
}
.data-table tbody tr:last-child { border-bottom: none; }
.data-table tbody tr:hover { background: var(--surface-2); }
.data-table td {
  padding: 12px 14px;
  color: var(--text-1);
  vertical-align: middle;
}
.data-table td.muted { color: var(--text-2); font-size: 13px; }

.td-num  { font-family: 'JetBrains Mono', monospace; font-size: 13px; font-weight: 500; color: var(--text-brand); }
.td-money{ font-family: 'JetBrains Mono', monospace; font-weight: 600; font-variant-numeric: tabular-nums; }
.td-actions { text-align: right; white-space: nowrap; }
.td-actions .action-group { display: flex; align-items: center; justify-content: flex-end; gap: 4px; }

.action-btn {
  width: 30px; height: 30px;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  background: transparent;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--text-2);
  cursor: pointer;
  transition: background var(--dur-fast), color var(--dur-fast), border-color var(--dur-fast), transform var(--dur-fast);
  text-decoration: none;
}
.action-btn:hover {
  background: var(--surface-3);
  color: var(--text-1);
  border-color: var(--border);
}
.action-btn:active { transform: scale(0.9); }
.action-btn.danger:hover { background: var(--danger-bg); color: var(--danger); border-color: rgba(199,69,47,0.2); }
.action-btn.success { color: var(--success); }
.action-btn.success:hover { background: var(--success-bg); color: var(--success); border-color: rgba(47,143,91,0.25); }
.action-btn svg { width: 14px; height: 14px; }

.empty-state {
  padding: 60px 20px;
  text-align: center;
}
.empty-state svg {
  width: 48px; height: 48px;
  color: var(--text-3);
  margin: 0 auto 16px;
}
.empty-state h3 { font-size: 16px; font-weight: 700; margin-bottom: 6px; }
.empty-state p { color: var(--text-2); font-size: 14px; margin-bottom: 20px; }

/* Table footer / pagination */
.table-footer {
  padding: 12px 16px;
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: var(--surface-2);
  flex-wrap: wrap;
}
.table-count { font-size: 13px; color: var(--text-2); }
.pagination { display: flex; align-items: center; gap: 4px; }
.page-btn {
  min-width: 32px; height: 32px;
  border-radius: var(--r-sm);
  border: 1px solid var(--border);
  background: var(--surface-1);
  color: var(--text-2);
  font-size: 13px; font-weight: 500;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; text-decoration: none;
  transition: all var(--dur-fast);
  padding: 0 8px;
}
.page-btn:hover { background: var(--surface-3); color: var(--text-1); }
.page-btn.active { background: var(--brand-600); color: #fff; border-color: var(--brand-700); }  /* same 3.51 -> 4.97 fix */

/* ══════════════════════════════════════════════════════════════════════════
   FORMS
   ══════════════════════════════════════════════════════════════════════════ */

.form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
  align-items: start;
}
.form-col { display: flex; flex-direction: column; gap: 16px; }

.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}
.field label {
  font-size: 12px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--text-2);
}
.field label .req { color: var(--danger); margin-left: 2px; }

.input {
  width: 100%;
  padding: 10px 13px;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  color: var(--text-1);
  font-size: 14px;
  transition: border-color var(--dur-fast), box-shadow var(--dur-fast), background var(--dur-fast);
  outline: none;
}
.input:focus {
  border-color: var(--brand-600);
  box-shadow: 0 0 0 3px rgba(145,76,157,0.15);
  background: var(--surface-1);
}
.input::placeholder { color: var(--text-3); }
.input:read-only { opacity: 0.6; cursor: default; }

textarea.input { resize: vertical; min-height: 80px; line-height: 1.6; }

select.input {
  appearance: none;
  /* Force the native option popup to render in light mode. Without this, Chrome
     on Windows paints the dropdown list using the dark control colours and the
     popup comes out solid black/unreadable (the closed control keeps its dark
     .input background, set explicitly above, so only the popup is affected). */
  color-scheme: light;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238C8494' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 36px;
  cursor: pointer;
}
/* Solid, high-contrast colours so the native popup is always readable. */
select.input option { color: #1e293b; background-color: #ffffff; }

.input-group {
  display: flex;
  align-items: stretch;
}
.input-group .input { border-radius: var(--r-md) 0 0 var(--r-md); border-right: none; flex: 1; }
.input-group .input-addon {
  padding: 10px 13px;
  background: var(--surface-3);
  border: 1px solid var(--border-strong);
  border-radius: 0 var(--r-md) var(--r-md) 0;
  color: var(--text-2); font-size: 13px;
  display: flex; align-items: center;
  white-space: nowrap;
}

.field-error { font-size: 12px; color: var(--danger); }
.input.has-error { border-color: var(--danger); }

/* Company radio cards */
.company-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 10px;
}
.company-card {
  position: relative;
  border: 1.5px solid var(--border);
  border-radius: var(--r-md);
  padding: 14px;
  cursor: pointer;
  transition: border-color var(--dur-fast), background var(--dur-fast), transform var(--dur-fast);
}
.company-card:hover { background: var(--surface-2); }
.company-card input[type="radio"] {
  position: absolute; opacity: 0; width: 0; height: 0;
}
.company-card.active {
  border-color: var(--brand-500);
  background: rgba(145,76,157,0.06);
}
.company-card:active { transform: scale(0.98); }
.company-card-dot {
  width: 10px; height: 10px;
  border-radius: 50%;
  border: 2px solid var(--border-strong);
  position: absolute; top: 12px; right: 12px;
  transition: all var(--dur-fast);
}
.company-card.active .company-card-dot {
  background: var(--brand-500);
  border-color: var(--brand-500);
}
.company-card-color {
  width: 28px; height: 4px;
  border-radius: 2px;
  margin-bottom: 10px;
}
.company-card strong { font-size: 13px; font-weight: 700; display: block; margin-bottom: 2px; line-height: 1.2; }
.company-card span   { font-size: 11px; color: var(--text-2); display: block; line-height: 1.4; }

/* Language picker */
.lang-picker {
  display: flex;
  gap: 8px;
}
.lang-option {
  flex: 1;
  display: flex; align-items: center; justify-content: center; gap: 6px;
  padding: 9px 12px;
  border: 1.5px solid var(--border);
  border-radius: var(--r-md);
  cursor: pointer; font-size: 13px; font-weight: 500; color: var(--text-2);
  transition: all var(--dur-fast);
}
.lang-option:hover { border-color: var(--border-strong); color: var(--text-1); }
.lang-option input { position: absolute; opacity: 0; width: 0; height: 0; }
.lang-option.active { border-color: var(--brand-500); color: var(--text-brand); background: rgba(145,76,157,0.06); font-weight: 600; }

/* Item rows (invoice / offer) */
.items-section { display: flex; flex-direction: column; gap: 0; }
.items-header-mobile { display: none; }
.items-header {
  display: grid;
  grid-template-columns: 24px 1fr 110px 110px 32px;
  gap: 8px;
  padding: 8px 16px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-2);
}
.items-header.bonus-header {
  grid-template-columns: 24px 1fr auto 32px;
  background: rgba(100,85,199,0.06);
  color: var(--violet);
  border-bottom-color: rgba(100,85,199,0.15);
}
.item-row {
  display: grid;
  grid-template-columns: 24px 1fr 110px 110px 32px;
  gap: 8px;
  padding: 8px 16px;
  border-bottom: 1px solid var(--border);
  align-items: center;
  transition: background var(--dur-fast);
  animation: rowSlideIn 200ms var(--ease-out) both;
}
.item-row:hover { background: var(--surface-2); }
.item-row.bonus-row {
  grid-template-columns: 24px 1fr auto 32px;
}
/* Offer service rows: Pos / Service / Unit Price / Total / remove (no Qty). */
.offer-items .items-header,
.offer-items .item-row:not(.bonus-row) {
  grid-template-columns: 22px minmax(0,1fr) 100px 100px 28px;
}
.item-col-qty .item-input,
.item-col-price .item-input,
.item-col-total .item-input { min-width: 0; }

/* Editable money fields: clearly visible, not ghost */
.item-col-qty .item-input,
.item-col-price .item-input {
  background: var(--surface-2);
  border: 1.5px solid var(--border-strong);
  border-radius: var(--r-sm);
  font-size: 13px !important;
  font-weight: 600 !important;
  font-family: 'JetBrains Mono', monospace !important;
  font-variant-numeric: tabular-nums;
  padding: 7px 10px;
  color: var(--text-1);
  text-align: right;
  transition: border-color var(--dur-fast), background var(--dur-fast), box-shadow var(--dur-fast);
}
.item-col-qty .item-input:hover,
.item-col-price .item-input:hover {
  border-color: rgba(145,76,157,0.4);
  background: var(--surface-3);
}
.item-col-qty .item-input:focus,
.item-col-price .item-input:focus {
  border-color: var(--brand-500);
  background: var(--surface-3);
  box-shadow: 0 0 0 3px rgba(145,76,157,0.12);
  outline: none;
}
/* Suppress number spinners — they misfire on small fields */
.item-col-qty .item-input::-webkit-outer-spin-button,
.item-col-qty .item-input::-webkit-inner-spin-button,
.item-col-price .item-input::-webkit-outer-spin-button,
.item-col-price .item-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.item-col-qty .item-input[type=number],
.item-col-price .item-input[type=number] { -moz-appearance: textfield; appearance: textfield; }

/* Read-only total: tinted, clearly not editable */
.item-col-total .item-input,
.item-col-total .item-total-display {
  background: rgba(145,76,157,0.07);
  border: 1.5px solid rgba(145,76,157,0.18);
  border-radius: var(--r-sm);
  font-size: 13px !important;
  font-weight: 700 !important;
  font-family: 'JetBrains Mono', monospace !important;
  font-variant-numeric: tabular-nums;
  padding: 7px 10px;
  color: var(--text-brand) !important;
  text-align: right !important;
  pointer-events: none;
  display: block;
  width: 100%;
  box-sizing: border-box;
}
.item-row-num {
  font-size: 11px; color: var(--text-3); font-weight: 600;
  font-variant-numeric: tabular-nums; text-align: center;
}
.item-input {
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  padding: 6px 8px;
  color: var(--text-1);
  font-size: 13px;
  width: 100%;
  transition: border-color var(--dur-fast), background var(--dur-fast);
  outline: none;
}
.item-input:focus {
  border-color: var(--brand-600);
  background: var(--surface-2);
}
.item-input.right { text-align: right; font-family: 'JetBrains Mono', monospace; font-size: 12px; }
.item-total-display {
  text-align: right; padding: 6px 8px;
  font-family: 'JetBrains Mono', monospace; font-size: 12px;
  font-weight: 600; color: var(--text-1);
}
.bonus-label {
  font-size: 10px; font-weight: 700; letter-spacing: 0.05em;
  color: var(--violet); text-transform: uppercase;
  background: var(--violet-bg); padding: 3px 8px; border-radius: 4px;
}
.remove-row-btn {
  width: 24px; height: 24px;
  border-radius: var(--r-sm);
  border: none; background: transparent;
  color: var(--text-3);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: color var(--dur-fast), background var(--dur-fast), transform var(--dur-fast);
}
.remove-row-btn:hover { color: var(--danger); background: var(--danger-bg); }
.remove-row-btn:active { transform: scale(0.9); }
.remove-row-btn svg { width: 13px; height: 13px; }

@keyframes rowSlideIn {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Totals box */
.totals-box {
  margin-top: 16px;
  display: flex; justify-content: flex-end;
}
.totals-table {
  min-width: 280px;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.total-row {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
}
.total-row:last-child { border-bottom: none; }
.total-row.grand {
  background: var(--brand-500);
  color: #fff; padding: 14px 16px;
}
.total-row .lbl { color: var(--text-2); }
.total-row.grand .lbl { color: rgba(255,255,255,0.75); font-size: 12px; }
.total-row .amt { font-weight: 700; font-family: 'JetBrains Mono', monospace; font-variant-numeric: tabular-nums; }
.total-row.grand .amt { font-size: 20px; font-weight: 800; }

/* Form footer */
.form-footer {
  position: sticky;
  bottom: 0;
  background: var(--glass);
  backdrop-filter: blur(16px) saturate(1.4);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
  border-top: 1px solid var(--border);
  padding: 14px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  z-index: 10;
  margin: 0 -28px -60px;
}
.form-footer-right { display: flex; align-items: center; gap: 10px; }

/* ══════════════════════════════════════════════════════════════════════════
   BADGES / STATUS
   ══════════════════════════════════════════════════════════════════════════ */

.badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px;
  border-radius: 100px;
  font-size: 11px; font-weight: 700;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.badge::before {
  content: '';
  width: 5px; height: 5px;
  border-radius: 50%;
  flex-shrink: 0;
}
.badge-draft   { background: rgba(150,140,158,0.12); color: #8C8494; }
.badge-draft::before { background: #8C8494; }
.badge-sent    { background: var(--info-bg);    color: var(--info); }
.badge-sent::before { background: var(--info); }
.badge-paid    { background: var(--success-bg); color: var(--success); }
.badge-paid::before { background: var(--success); }
.badge-overdue { background: var(--danger-bg);  color: var(--danger); }
.badge-overdue::before { background: var(--danger); }
.badge-final   { background: var(--success-bg); color: var(--success); }
.badge-final::before { background: var(--success); }

/* Inline status dropdown on the invoice list — looks like a .badge pill but is a
   real <select> so the status can be changed without opening the editor. The
   badge-<status> colour class supplies the background/text colour. */
.status-select {
  -webkit-appearance: none; appearance: none;
  /* Force the native dropdown popup to render in light mode. Without this,
     Chrome on Windows paints the option list using the select's (translucent
     badge) background composited over black → an unreadable "black window". */
  color-scheme: light;
  border: none; cursor: pointer;
  font-size: 11px; font-weight: 700; letter-spacing: 0.02em;
  padding: 4px 26px 4px 11px;
  border-radius: 100px;
  line-height: 1.2;
  background-repeat: no-repeat;
  background-position: right 9px center;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='3' stroke-linecap='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
}
.status-select:focus-visible { outline: 2px solid var(--info); outline-offset: 1px; }
/* Solid, high-contrast colours on the option list so the native popup is always
   readable regardless of the app theme (the pill itself keeps its badge colour). */
.status-select option { color: #1e293b; background-color: #ffffff; font-weight: 600; }

/* ── Bulk select / download (invoice + offer lists) ── */
.col-select { width: 34px; text-align: center; padding-left: 12px; padding-right: 0; }
.col-select input[type="checkbox"] { width: 16px; height: 16px; cursor: pointer; accent-color: var(--info); margin: 0; vertical-align: middle; }
.bulk-bar {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 16px; margin: 0;
  background: var(--info-bg); border-bottom: 1px solid var(--border);
}
.bulk-bar .bulk-count { font-size: 13px; font-weight: 700; color: var(--text-1); margin-right: auto; }
@media (max-width: 767px) {
  .bulk-bar { flex-wrap: wrap; }
}

/* ── Year / Month filter dropdowns (list toolbars) ── */
.filter-form { display: inline-flex; align-items: center; gap: 6px; }
.filter-select {
  appearance: none; -webkit-appearance: none;
  color-scheme: light; /* keep the native option popup readable (not black) on Windows Chrome */
  background: var(--surface-2); color: var(--text-1);
  border: 1px solid var(--border); border-radius: 8px;
  font-size: 13px; font-weight: 500; padding: 7px 26px 7px 11px; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%238C8494' stroke-width='3' stroke-linecap='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 9px center;
}
.filter-select:hover { border-color: var(--border-strong); }
.filter-select:focus-visible { outline: 2px solid var(--info); outline-offset: 1px; }
.filter-select option { color: #1e293b; background-color: #ffffff; }
.filter-clear {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 8px;
  color: var(--text-2); text-decoration: none; font-size: 14px;
  border: 1px solid var(--border);
}
.filter-clear:hover { color: var(--danger); border-color: var(--danger); }

/* ── Dashboard statistics period toggle ── */
.stat-period-bar {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin-bottom: 14px;
}
.stat-period-title {
  font-family: 'Sora', sans-serif; font-size: 18px; font-weight: 700;
  color: var(--text-1);
}
#stat-period-toggle .tab { cursor: pointer; font-family: inherit; }

/* ══════════════════════════════════════════════════════════════════════════
   MODAL
   ══════════════════════════════════════════════════════════════════════════ */

.modal-overlay {
  /* Above the mobile bottom-nav (9999) and PWA banner (900) — otherwise on a
     phone those fixed bars render over the bottom-sheet footer and hide the
     Save/Cancel buttons. Toasts sit just above this so errors stay visible. */
  position: fixed; inset: 0; z-index: 10000;
  background: var(--scrim);
  backdrop-filter: blur(6px) saturate(1.2);
  -webkit-backdrop-filter: blur(6px) saturate(1.2);
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-normal);
  overflow-y: auto;
}
.modal-overlay.open {
  opacity: 1;
  pointer-events: auto;
}
.modal {
  background: var(--surface-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-xl);
  width: 100%; max-width: 480px;
  box-shadow: var(--shadow-lg);
  transform: scale(0.95) translateY(8px);
  transition: transform var(--dur-normal) var(--ease-out), opacity var(--dur-normal);
  opacity: 0;
  /* Never exceed the viewport — long forms scroll inside the body instead of
     pushing the header/footer off-screen. */
  max-height: calc(100vh - 40px);
  max-height: calc(100dvh - 40px);
  display: flex;
  flex-direction: column;
}
.modal-header { flex-shrink: 0; }
.modal-footer { flex-shrink: 0; }
.modal-body   { overflow-y: auto; min-height: 0; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; }
.modal-overlay.open .modal {
  transform: scale(1) translateY(0);
  opacity: 1;
}
.modal-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 20px 22px 16px;
  border-bottom: 1px solid var(--border);
}
.modal-header h3 { font-size: 16px; font-weight: 700; }
.modal-close {
  width: 28px; height: 28px;
  border-radius: var(--r-sm); border: none;
  background: var(--surface-3); color: var(--text-2);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: all var(--dur-fast);
}
.modal-close:hover { background: var(--surface-4); color: var(--text-1); }
.modal-close svg { width: 14px; height: 14px; }
.modal-body { padding: 20px 22px; }
.modal-footer {
  padding: 14px 22px;
  border-top: 1px solid var(--border);
  display: flex; justify-content: flex-end; gap: 10px;
}

/* Bottom sheet on mobile */
@media (max-width: 767px) {
  .modal-overlay { align-items: flex-end; padding: 0; }
  .modal {
    max-width: 100%; width: 100%;
    border-radius: var(--r-xl) var(--r-xl) 0 0;
    transform: translateY(100%);
    opacity: 1;
    /* Prevent modal from exceeding screen height */
    max-height: 92dvh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }
  .modal-overlay.open .modal { transform: translateY(0); }
  /* Body scrolls when content is taller than the sheet */
  .modal-body {
    overflow-y: auto;
    flex: 1;
    -webkit-overflow-scrolling: touch;
  }
  /* Header and footer stay pinned */
  .modal-header { flex-shrink: 0; }
  .modal-footer {
    flex-shrink: 0;
    /* Keep the action buttons clear of the home indicator / browser bottom bar */
    padding-bottom: calc(14px + env(safe-area-inset-bottom));
  }

  /* Confirm dialogs stay centered (not a bottom sheet) so the Cancel/Confirm
     buttons are always visible. On iOS, position:fixed/inset:0 extends behind the
     browser toolbar, so we pin the overlay to the *dynamic* viewport height and
     pad by the safe-area insets — that way centering happens within the visible
     area and the footer can never tuck behind the toolbar / home indicator. */
  #confirm-modal.modal-overlay {
    align-items: center;
    height: 100vh;            /* fallback */
    height: 100dvh;           /* excludes dynamic browser chrome */
    padding: 20px;
    padding-top: calc(20px + env(safe-area-inset-top));
    padding-bottom: calc(20px + env(safe-area-inset-bottom));
  }
  #confirm-modal .modal {
    max-width: 100%; width: 100%;
    border-radius: var(--r-xl);
    transform: scale(0.95) translateY(8px);
    opacity: 0;
    max-height: 100%;
  }
  #confirm-modal.modal-overlay.open .modal {
    transform: scale(1) translateY(0);
    opacity: 1;
  }
  #confirm-modal .modal-footer { padding-bottom: 14px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   TOASTS
   ══════════════════════════════════════════════════════════════════════════ */

.toast-container {
  position: fixed;
  top: 70px; right: 20px;
  z-index: 10001; /* above .modal-overlay (10000) so toasts show over open modals */
  display: flex;
  flex-direction: column;
  gap: 10px;
  pointer-events: none;
}
.toast {
  display: flex; align-items: flex-start; gap: 11px;
  padding: 14px 16px;
  border-radius: var(--r-lg);
  background: var(--surface-3);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-lg);
  min-width: 280px; max-width: 380px;
  pointer-events: auto;
  opacity: 0;
  transform: translateX(20px);
  transition: opacity var(--dur-normal) var(--ease-out),
              transform var(--dur-normal) var(--ease-out);
}
.toast.show { opacity: 1; transform: translateX(0); }
.toast.hide {
  opacity: 0;
  transform: translateX(20px);
  transition-duration: 150ms;
}
.toast-icon {
  width: 20px; height: 20px; flex-shrink: 0; margin-top: 1px;
  border-radius: 50%; display: flex; align-items: center; justify-content: center;
}
.toast-icon svg { width: 12px; height: 12px; color: #fff; }
.toast.success .toast-icon { background: var(--success); }
.toast.error   .toast-icon { background: var(--danger); }
.toast.info    .toast-icon { background: var(--info); }
.toast.warning .toast-icon { background: var(--warning); }
.toast-body { flex: 1; }
.toast-title { font-size: 13px; font-weight: 700; color: var(--text-1); line-height: 1.3; }
.toast-msg   { font-size: 12px; color: var(--text-2); margin-top: 2px; line-height: 1.4; }
.toast-close { color: var(--text-3); cursor: pointer; background: none; border: none; line-height: 1; padding: 0; }
.toast-close:hover { color: var(--text-1); }
.toast-close svg { width: 13px; height: 13px; }
.toast-action {
  flex-shrink: 0; align-self: center;
  background: var(--surface-1); color: var(--text-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  font-size: 12px; font-weight: 700;
  padding: 5px 11px; cursor: pointer;
  transition: background var(--dur-fast);
}
.toast-action:hover { background: var(--surface-4); }

/* ══════════════════════════════════════════════════════════════════════════
   SETTINGS PAGE
   ══════════════════════════════════════════════════════════════════════════ */

.settings-grid {
  display: grid;
  grid-template-columns: 220px 1fr;
  gap: 24px;
  align-items: start;
}
.settings-nav {
  position: sticky; top: calc(var(--topbar-h) + 20px);
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.settings-nav-item {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 16px;
  font-size: 13px; font-weight: 500;
  color: var(--text-2);
  border-bottom: 1px solid var(--border);
  cursor: pointer; text-decoration: none;
  transition: background var(--dur-fast), color var(--dur-fast);
}
.settings-nav-item:last-child { border-bottom: none; }
.settings-nav-item:hover { background: var(--surface-2); color: var(--text-1); }
.settings-nav-item.active { background: rgba(145,76,157,0.08); color: var(--text-brand); font-weight: 600; }
.settings-nav-item svg { width: 16px; height: 16px; }

.settings-section { display: none; }
.settings-section.active { display: block; }
.settings-section > .card { margin-bottom: 16px; }

.company-slot-tabs {
  display: flex; gap: 4px; flex-wrap: wrap;
  margin-bottom: 4px;
}
.slot-tab {
  flex: 0 1 auto; min-width: 120px; padding: 9px 12px;
  background: var(--surface-2);
  border: 1.5px solid var(--border);
  border-radius: var(--r-md);
  text-align: center; cursor: pointer;
  font-size: 12px; font-weight: 600; color: var(--text-2);
  transition: all var(--dur-fast);
  position: relative;
}
.slot-tab.active {
  border-color: var(--brand-500);
  color: var(--text-brand);
  background: rgba(145,76,157,0.06);
}
.slot-tab .dot {
  display: inline-block; width: 6px; height: 6px;
  border-radius: 50%; margin-right: 4px; vertical-align: middle;
}

/* Color input */
.color-field {
  display: flex; align-items: center; gap: 10px;
}
.color-swatch {
  width: 36px; height: 36px;
  border-radius: var(--r-sm);
  border: 2px solid var(--border-strong);
  flex-shrink: 0; cursor: pointer; overflow: hidden;
}
.color-swatch input[type="color"] {
  width: 100%; height: 100%;
  border: none; padding: 0; cursor: pointer; opacity: 0; position: absolute;
}
.color-swatch { position: relative; }

/* ══════════════════════════════════════════════════════════════════════════
   DASHBOARD
   ══════════════════════════════════════════════════════════════════════════ */

.dash-sections {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
}
.dash-list-item {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  transition: background var(--dur-fast);
  text-decoration: none; color: inherit;
}
.dash-list-item:last-child { border-bottom: none; }
.dash-list-item:hover { background: var(--surface-2); }
.dash-list-item-left {}
.dash-list-item-num { font-family: 'JetBrains Mono', monospace; font-size: 13px; font-weight: 600; color: var(--dsh-link); }
.dash-list-item-client { font-size: 13px; font-weight: 500; margin-top: 1px; }
.dash-list-item-date { font-size: 11px; color: var(--text-2); margin-top: 2px; }
.dash-list-item-right { text-align: right; }
.dash-list-item-amount { font-family: 'JetBrains Mono', monospace; font-size: 14px; font-weight: 700; }
.dash-view-all {
  display: block; text-align: center;
  padding: 12px; font-size: 13px; font-weight: 600;
  color: var(--dsh-link); text-decoration: none;
  border-top: 1px solid var(--border);
  transition: background var(--dur-fast);
}
.dash-view-all:hover { background: var(--surface-2); }

/* ══════════════════════════════════════════════════════════════════════════
   LOGIN PAGE
   ══════════════════════════════════════════════════════════════════════════ */

.login-page {
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
  padding: 20px;
  position: relative;
  overflow: hidden;
}
.login-page::before {
  content: '';
  position: absolute;
  top: -200px; left: 50%; transform: translateX(-50%);
  width: 600px; height: 600px;
  background: radial-gradient(circle, rgba(145,76,157,0.1) 0%, transparent 70%);
  pointer-events: none;
}
.login-card {
  background: var(--surface-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-xl);
  padding: 44px 40px;
  width: 100%; max-width: 420px;
  box-shadow: var(--shadow-lg), 0 0 0 1px rgba(145,76,157,0.05);
  animation: fadeInUp var(--dur-slow) var(--ease-out) both;
}
.login-logo {
  display: flex; flex-direction: column; align-items: center;
  margin-bottom: 32px; text-align: center;
}
.login-logo-mark {
  width: 52px; height: 52px;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  display: flex; align-items: center; justify-content: center;
  padding: 6px;
  margin-bottom: 14px;
  box-shadow: 0 4px 20px rgba(0,0,0,0.22);
}
.login-logo h1 {
  font-size: 22px; font-weight: 800;
  margin-bottom: 4px;
}
.login-logo p { font-size: 13px; color: var(--text-2); }

.login-error {
  background: var(--danger-bg);
  border: 1px solid rgba(199,69,47,0.25);
  border-radius: var(--r-md);
  padding: 11px 14px;
  font-size: 13px; color: var(--danger);
  margin-bottom: 20px;
}

/* ══════════════════════════════════════════════════════════════════════════
   PWA INSTALL BANNER
   ══════════════════════════════════════════════════════════════════════════ */

.pwa-banner {
  position: fixed;
  bottom: 20px; left: 50%; transform: translateX(-50%) translateY(120px);
  background: var(--surface-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-xl);
  padding: 16px 20px;
  display: flex; align-items: center; gap: 14px;
  box-shadow: var(--shadow-lg);
  z-index: 900;
  max-width: 380px; width: calc(100% - 32px);
  transition: transform var(--dur-slow) var(--ease-spring),
              opacity var(--dur-slow);
  opacity: 0;
}
.pwa-banner.show { transform: translateX(-50%) translateY(0); opacity: 1; }
.pwa-banner-icon {
  width: 40px; height: 40px; flex-shrink: 0;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  display: flex; align-items: center; justify-content: center;
  padding: 4px;
}
.pwa-banner-text { flex: 1; }
.pwa-banner-text strong { font-size: 13px; font-weight: 700; display: block; }
.pwa-banner-text span   { font-size: 11px; color: var(--text-2); }
.pwa-banner-actions { display: flex; gap: 8px; }

/* ══════════════════════════════════════════════════════════════════════════
   SKELETON LOADING
   ══════════════════════════════════════════════════════════════════════════ */

.skeleton {
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 50%, var(--surface-2) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
  border-radius: var(--r-sm);
}
@keyframes shimmer {
  from { background-position: 200% 0; }
  to   { background-position: -200% 0; }
}

/* ══════════════════════════════════════════════════════════════════════════
   RESPONSIVE
   ══════════════════════════════════════════════════════════════════════════ */

@media (max-width: 1024px) {
  .form-grid { grid-template-columns: 1fr; }
  .settings-grid { grid-template-columns: 1fr; }
  .settings-nav {
    position: static;
    display: flex; flex-wrap: wrap; border-radius: var(--r-md);
  }
  .settings-nav-item { border-bottom: none; border-right: 1px solid var(--border); flex: 1; }
  .dash-sections { grid-template-columns: 1fr; }
}

@media (max-width: 767px) {
  :root {
    --sidebar-w: 100%;
    --topbar-h: 52px;
  }

  .sidebar {
    transform: translateX(-100%);
    width: 280px !important;
    z-index: 300;
  }
  .sidebar.mobile-open { transform: translateX(0); }
  .sidebar.collapsed { width: 280px !important; }
  .sidebar.collapsed .sidebar-logo-text,
  .sidebar.collapsed .nav-label,
  .sidebar.collapsed .nav-badge,
  .sidebar.collapsed .user-info { opacity: 1; width: auto; }

  .sidebar-overlay {
    position: fixed; inset: 0; z-index: 290;
    background: rgba(0,0,0,0.5);
    opacity: 0; pointer-events: none;
    transition: opacity var(--dur-normal);
  }
  .sidebar-overlay.show { opacity: 1; pointer-events: auto; }

  .sidebar-toggle { display: none; }
  .mobile-menu-btn { display: flex; }

  .topbar { left: 0 !important; }
  .main-content { margin-left: 0 !important; }

  .page-container { padding: 18px 16px 100px; }

  /* Bottom nav */
  .bottom-nav {
    position: fixed; bottom: 0; left: 0; right: 0;
    height: 64px;
    background: var(--surface-1);
    border-top: 1px solid var(--border);
    display: flex; align-items: center; justify-content: space-around;
    z-index: 9999;
    pointer-events: all;
    padding: 0 8px;
    padding-bottom: env(safe-area-inset-bottom, 0);
  }
  .bottom-nav-item {
    flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 3px; padding: 8px 4px;
    color: var(--text-3); font-size: 10px; font-weight: 600;
    text-decoration: none;
    border-radius: var(--r-md);
    transition: color var(--dur-fast), background var(--dur-fast);
    min-height: 44px;
  }
  .bottom-nav-item svg { width: 20px; height: 20px; }
  .bottom-nav-item.active { color: var(--text-brand); }
  .bottom-nav-item.active svg { stroke-width: 2.5; }

  /* Card layout for data tables */
  .data-table-scroll { overflow-x: visible; }
  .data-table, .data-table tbody { display: block; width: 100%; }
  .data-table thead { display: none; }
  .data-table tbody tr {
    display: block;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    padding: 12px 14px;
    margin-bottom: 10px;
    animation: none;
  }
  .data-table tbody tr.is-empty-row { background: transparent; border: none; padding: 0; margin: 0; }
  .data-table tbody td {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 6px 0;
    border-bottom: 1px solid var(--border);
    border-radius: 0;
    font-size: 13px;
    min-height: 0;
  }
  .data-table tbody td:last-child { border-bottom: none; }
  .data-table td.hide-mobile { display: none !important; }
  .data-table td::before {
    content: attr(data-label);
    font-size: 10px; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--text-3);
    flex-shrink: 0;
    margin-right: 10px;
    min-width: 60px;
  }
  .data-table td.td-actions { border-bottom: none; padding-top: 8px; }
  .data-table td.td-actions::before { display: none; }
  .data-table td.td-actions .action-group { width: 100%; justify-content: flex-end; gap: 8px; }
  .data-table td.td-actions .action-btn { width: 38px; height: 38px; }
  .data-table td[colspan] { display: block; border-bottom: none; }
  .data-table td[colspan]::before { display: none; }
  /* Toolbar */
  .search-box { min-width: 0; max-width: 100%; flex: 1; }
  .table-toolbar { flex-direction: column; align-items: stretch; }
  .table-toolbar-left, .table-toolbar-right { width: 100%; }
  .table-toolbar-right { flex-wrap: wrap; }
  .pagination { flex-wrap: wrap; justify-content: center; }
  .page-btn { min-width: 44px; height: 44px; }
  .table-footer { flex-direction: column; align-items: flex-start; gap: 8px; }

  /* ─ Mobile Services: card-per-row layout (name · qty · price · total) ─ */

  /* Hide both the desktop grid header and the old 2-label mobile header —
     each field is now labelled inline inside its own card. */
  .items-header,
  .items-header-mobile { display: none !important; }

  /* Give the row list a little breathing room from the card edges */
  #main-items, #bonus-items,
  #offer-main-items, #offer-bonus-items { padding: 12px 0 4px !important; }

  /* Each service row becomes a self-contained card */
  .item-row {
    display: flex !important;
    flex-direction: row !important;
    flex-wrap: wrap !important;
    align-items: flex-end !important;
    gap: 10px !important;
    padding: 14px !important;
    margin: 0 12px 10px !important;
    background: var(--surface-2) !important;
    border: 1px solid var(--border) !important;
    border-radius: 12px !important;
    position: relative !important;
    overflow: visible !important;
    min-width: 0;
  }

  /* Row number hidden on mobile */
  .item-row-num { display: none !important; }

  /* Service name + description: full width, top of card */
  .item-row > div:not(.item-col-qty):not(.item-col-price):not(.item-col-total) {
    order: 1 !important;
    flex: 1 1 100% !important;
    min-width: 0 !important;
    display: flex !important;
    flex-direction: column !important;
    gap: 6px !important;
    padding: 0 !important;
    border: none !important;
    margin: 0 !important;
    background: none !important;
  }
  .item-row > div:not(.item-col-qty):not(.item-col-price):not(.item-col-total) .item-input {
    width: 100% !important;
    padding: 11px 12px !important;
    padding-right: 46px !important; /* clear the delete button in the corner */
    font-size: 15px !important;
    background: var(--surface-1) !important;
    border: 1.5px solid var(--border-strong) !important;
    border-radius: 9px !important;
    color: var(--text-1) !important;
    text-align: left !important;
    min-height: 46px !important;
    display: block !important;
    margin: 0 !important;
    box-sizing: border-box !important;
  }
  /* Optional description line (offers): smaller, no delete-button gap */
  .item-row > div:not(.item-col-qty):not(.item-col-price):not(.item-col-total) .item-input + .item-input {
    padding-right: 12px !important;
    min-height: 42px !important;
    font-size: 13px !important;
  }
  .item-row > div:not(.item-col-qty):not(.item-col-price):not(.item-col-total) .item-input:focus {
    border-color: var(--brand-500) !important;
    outline: none !important;
    box-shadow: 0 0 0 3px rgba(145,76,157,0.15) !important;
  }

  /* Numeric columns: labelled, side by side under the name */
  .item-col-qty,
  .item-col-price,
  .item-col-total {
    display: flex !important;
    flex-direction: column !important;
    gap: 5px !important;
    padding: 0 !important;
    border: none !important;
    margin: 0 !important;
    min-width: 0 !important;
  }
  .item-col-qty   { order: 2 !important; flex: 0 0 68px !important; }
  .item-col-price { order: 3 !important; flex: 1 1 90px !important; }
  .item-col-total { order: 4 !important; flex: 1 1 90px !important; }

  /* Inline mini-labels above each numeric field */
  .item-col-qty::before,
  .item-col-price::before,
  .item-col-total::before {
    font-size: 10px !important;
    font-weight: 700 !important;
    text-transform: uppercase !important;
    letter-spacing: 0.05em !important;
    color: var(--text-3) !important;
    display: block !important;
    line-height: 1 !important;
  }
  .item-col-qty::before   { content: 'Menge'; }
  .item-col-price::before { content: 'Einzelpreis €'; }
  .item-col-total::before { content: 'Gesamt €'; }

  /* Fields fill their column at a comfortable touch size */
  .item-col-qty .item-input,
  .item-col-price .item-input,
  .item-col-total .item-input,
  .item-col-total .item-total-display {
    width: 100% !important;
    min-height: 46px !important;
    padding: 13px 10px !important;
    font-size: 15px !important;
    font-weight: 600 !important;
    text-align: right !important;
    font-family: 'JetBrains Mono', monospace !important;
    border-radius: 9px !important;
    box-sizing: border-box !important;
    display: block !important;
  }
  /* Total display is a span — center its text vertically in the field box */
  .item-col-total .item-total-display {
    display: flex !important;
    align-items: center !important;
    justify-content: flex-end !important;
    padding: 11px 10px !important;
  }
  .item-col-qty .item-input,
  .item-col-price .item-input {
    background: var(--surface-1) !important;
    border: 1.5px solid var(--border-strong) !important;
    color: var(--text-1) !important;
  }
  .item-col-qty .item-input:focus,
  .item-col-price .item-input:focus {
    border-color: var(--brand-500) !important;
    outline: none !important;
    box-shadow: 0 0 0 3px rgba(145,76,157,0.15) !important;
  }
  /* Read-only total: tinted, clearly not editable */
  .item-col-total .item-input,
  .item-col-total .item-total-display {
    background: rgba(145,76,157,0.08) !important;
    border: 1.5px solid rgba(145,76,157,0.2) !important;
    color: var(--text-brand) !important;
    font-weight: 700 !important;
  }

  /* Delete button: top-right corner of the card */
  .item-row > .remove-row-btn {
    position: absolute !important;
    top: 8px !important;
    right: 8px !important;
    flex: none !important;
    width: 36px !important;
    height: 36px !important;
    min-width: 36px !important;
    background: transparent !important;
    border: none !important;
    border-radius: 8px !important;
    color: var(--text-3) !important;
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    padding: 0 !important;
    z-index: 2 !important;
    cursor: pointer;
  }
  .item-row > .remove-row-btn:active {
    color: var(--danger) !important;
    background: var(--danger-bg) !important;
  }
  .item-row > .remove-row-btn svg {
    width: 16px !important;
    height: 16px !important;
  }

  /* Bonus rows: compact single line */
  .item-row.bonus-row {
    flex-wrap: nowrap !important;
    align-items: center !important;
    padding: 12px 14px !important;
    gap: 10px !important;
  }
  .item-row.bonus-row .item-row-num { display: none !important; }
  .item-row.bonus-row .item-input {
    flex: 1 1 auto !important;
    min-width: 0 !important;
    padding: 11px 12px !important;
    font-size: 14px !important;
    background: var(--surface-1) !important;
    border: 1.5px solid var(--border-strong) !important;
    border-radius: 9px !important;
    color: var(--text-1) !important;
    min-height: 44px !important;
  }
  .item-row.bonus-row .bonus-label { flex: none !important; }
  .item-row.bonus-row > .remove-row-btn {
    position: static !important;
    flex: 0 0 36px !important;
    width: 36px !important;
    height: 36px !important;
  }

  /* Bonus header: keep visible, drop the # column */
  .items-header.bonus-header {
    display: grid !important;
    grid-template-columns: 1fr 32px;
    padding: 8px 14px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--violet);
    background: rgba(100,85,199,0.06);
    border-bottom: 1px solid rgba(100,85,199,0.15);
  }
  .items-header.bonus-header span:first-child { display: none; }

  /* Add buttons: full-width dashed, easy tap target */
  [data-add-main],
  [data-add-bonus],
  [data-add-offer-main],
  [data-add-offer-bonus] {
    width: 100% !important;
    justify-content: center !important;
    border: 1.5px dashed var(--brand-500) !important;
    border-radius: 10px !important;
    color: var(--text-brand) !important;
    background: transparent !important;
    padding: 13px 16px !important;
    font-size: 14px !important;
    font-weight: 600 !important;
    letter-spacing: 0.01em;
  }

  /* Card header with inline actions (e.g. invoice "Package" field) wraps */
  .card-header { flex-wrap: wrap; }
  .card-header-actions { width: 100%; }
  .card-header-actions .field { width: 100% !important; flex-direction: column !important; align-items: stretch !important; gap: 6px !important; }
  .card-header-actions .field label { white-space: normal !important; }
  .card-header-actions .input { width: 100% !important; }

  .company-cards { grid-template-columns: 1fr 1fr; }
  .lang-picker { flex-wrap: wrap; }

  .page-header { flex-direction: column; align-items: flex-start; }
  .page-header-right { width: 100%; justify-content: flex-start; }

  .stat-cards { grid-template-columns: 1fr 1fr; }
  .stat-value { font-size: 20px; }

  .topbar-breadcrumb .page-title { font-size: 14px; }

  .form-footer {
    bottom: 64px;
    z-index: 100;
    margin: 0 -16px -100px;
    padding: 12px 16px;
    width: calc(100% + 32px);
  }
  .form-footer .btn { width: 100%; justify-content: center; }

  .field-row { grid-template-columns: 1fr; }
  .totals-box { justify-content: stretch; }
  .totals-table { min-width: auto; width: 100%; }

  .toast-container { top: 60px; left: 12px; right: 12px; }
  .toast { min-width: auto; max-width: 100%; }

  /* ── Mobile accordion — ONLY on non-essential sections (marked .acc) ─────
     Essential sections (Details, Client, Services, Total) are never collapsed.
     .acc sections (Sender Company, Notes, Bonus, Payment) collapse to their
     header; tap to expand. Desktop is unaffected (mobile media query only). */
  #invoice-form .card.acc > .card-header,
  #offer-form .card.acc > .card-header {
    position: relative;
    cursor: pointer;
    padding-right: 42px;
    -webkit-user-select: none; user-select: none;
  }
  #invoice-form .card.acc > .card-header::after,
  #offer-form .card.acc > .card-header::after {
    content: '';
    position: absolute;
    right: 18px; top: 50%;
    width: 8px; height: 8px;
    margin-top: -6px;
    border-right: 2px solid var(--text-2);
    border-bottom: 2px solid var(--text-2);
    transform: rotate(45deg);
    transition: transform var(--dur-normal) var(--ease-out);
  }
  #invoice-form .card.acc.collapsed > .card-header::after,
  #offer-form .card.acc.collapsed > .card-header::after {
    transform: rotate(-45deg);
    margin-top: -2px;
  }
  #invoice-form .card.acc.collapsed .card-body,
  #invoice-form .card.acc.collapsed .items-section,
  #offer-form .card.acc.collapsed .card-body,
  #offer-form .card.acc.collapsed .items-section {
    display: none !important;
  }
  /* Smooth reveal when expanding */
  #invoice-form .card.acc:not(.collapsed) .card-body,
  #invoice-form .card.acc:not(.collapsed) .items-section,
  #offer-form .card.acc:not(.collapsed) .card-body,
  #offer-form .card.acc:not(.collapsed) .items-section {
    animation: fadeInUp 200ms var(--ease-out) both;
  }
}

@media (max-width: 767px) {
  .bottom-nav { display: flex; }
}
@media (min-width: 768px) {
  .bottom-nav { display: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   UTILITIES
   ══════════════════════════════════════════════════════════════════════════ */

.d-flex { display: flex; }
.d-none { display: none; }
.gap-8  { gap: 8px; }
.gap-12 { gap: 12px; }
.gap-16 { gap: 16px; }
.mt-8  { margin-top: 8px; }
.mt-16 { margin-top: 16px; }
.mt-24 { margin-top: 24px; }
.mb-16 { margin-bottom: 16px; }
.text-sm   { font-size: 13px; }
.text-xs   { font-size: 11px; }
.text-muted{ color: var(--text-2); }
.text-right{ text-align: right; }
.text-center{ text-align: center; }
.fw-700 { font-weight: 700; }
.color-success { color: var(--success); }
.color-danger  { color: var(--danger); }
.color-warning { color: var(--warning); }
.color-brand   { color: var(--text-brand); }

/* Divider */
.divider { height: 1px; background: var(--border); margin: 16px 0; }

/* Section subtitle */
.section-sub {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--text-3); margin-bottom: 10px; padding-top: 4px;
}

/* ══════════════════════════════════════════════════════════════════════════
   CLIENT DROPDOWN (saved clients autofill)
   ══════════════════════════════════════════════════════════════════════════ */

/* Let the saved-clients dropdown escape the card instead of being clipped
   by the card's `overflow:hidden`. */
.client-card { overflow: visible; }

.client-dropdown {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
  max-height: 240px; overflow-y: auto;
  z-index: 400;
  animation: dropFadeIn 160ms var(--ease-out);
}
@keyframes dropFadeIn {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.client-option {
  padding: 10px 14px; cursor: pointer;
  border-bottom: 1px solid var(--border);
  transition: background var(--dur-fast);
}
.client-option:last-child { border-bottom: none; }
.client-option:hover,
.client-option.focused { background: var(--surface-3); }
.client-option-name { font-size: 14px; font-weight: 600; color: var(--text-1); }
.client-option-sub  { font-size: 12px; color: var(--text-2); margin-top: 2px; }

/* "Save this client" opt-in below the recipient fields */
.save-client-check {
  display: flex; align-items: center; gap: 9px;
  margin-top: 14px; padding: 11px 13px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  cursor: pointer;
  font-size: 13px; color: var(--text-2);
  transition: border-color var(--dur-fast), background var(--dur-fast), color var(--dur-fast);
  -webkit-user-select: none; user-select: none;
}
.save-client-check:hover { border-color: var(--border-strong); color: var(--text-1); }
.save-client-check input[type="checkbox"] {
  width: 17px; height: 17px; flex-shrink: 0;
  accent-color: var(--brand-500); cursor: pointer; margin: 0;
}
.save-client-check:has(input:checked) {
  border-color: var(--brand-500);
  background: rgba(145,76,157,0.06);
  color: var(--text-1);
}

/* ══════════════════════════════════════════════════════════════════════════
   CLIENT REGISTRY — domain-expiry traffic lights + bulk import
   expired = overdue (solid red) · red = this month · orange = next month · green = later
   ══════════════════════════════════════════════════════════════════════════ */

/* Expired = the most severe state: a SOLID red badge so it reads stronger than
   the tinted "expires this month" red. Domains stay expired until manually renewed. */
.badge.exp-expired { background: var(--danger); color: #fff; font-weight: 700; }
.badge.exp-expired::before { background: #fff; }
.badge.exp-red    { background: var(--danger-bg);  color: var(--danger); }
.badge.exp-red::before    { background: var(--danger); }
.badge.exp-orange { background: var(--warning-bg); color: var(--warning); }
.badge.exp-orange::before { background: var(--warning); }
.badge.exp-green  { background: var(--success-bg); color: var(--success); }
.badge.exp-green::before  { background: var(--success); }
.badge.exp-none   { background: rgba(150,140,158,0.12); color: #8C8494; }
.badge.exp-none::before   { background: #8C8494; }

.exp-sub { font-size: 11.5px; color: var(--text-2); margin-top: 3px; }

.exp-text-expired { color: var(--danger);  font-weight: 700; }
.exp-text-red    { color: var(--danger);  font-weight: 600; }
.exp-text-orange { color: var(--warning); font-weight: 600; }
.exp-text-green  { color: var(--success); }

/* Colored dot inside the status filter tabs */
.exp-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 5px; vertical-align: 1px; }
.exp-dot-expired { background: var(--danger); box-shadow: 0 0 0 2px var(--danger-bg); }
.exp-dot-red    { background: var(--danger); }
.exp-dot-orange { background: var(--warning); }
.exp-dot-green  { background: var(--success); }
.exp-dot-none   { background: #8C8494; }

/* Urgency tint on the row's left edge */
tr.row-exp-expired td:first-child { box-shadow: inset 3px 0 0 var(--danger); }
tr.row-exp-red    td:first-child { box-shadow: inset 3px 0 0 var(--danger); }
tr.row-exp-orange td:first-child { box-shadow: inset 3px 0 0 var(--warning); }
tr.row-exp-green  td:first-child { box-shadow: inset 3px 0 0 var(--success); }

/* ── Admin activity log (Clients page) ── */
.activity-log { padding: 2px 0; }
.activity-item {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 11px 16px; border-bottom: 1px solid var(--border);
}
.activity-item:last-child { border-bottom: 0; }
.activity-dot {
  width: 8px; height: 8px; border-radius: 50%;
  margin-top: 6px; flex-shrink: 0; background: #8C8494;
}
.activity-dot.activity-domain_expired { background: var(--danger); }
.activity-dot.activity-domain_renewed { background: var(--success); }
.activity-msg { flex: 1; min-width: 0; font-size: 13.5px; color: var(--text-1); line-height: 1.45; }
.activity-time { font-size: 12px; color: var(--text-2); white-space: nowrap; flex-shrink: 0; padding-top: 1px; }

.td-domain { color: var(--text-brand); text-decoration: none; font-weight: 500; }
.td-domain:hover { text-decoration: underline; }

/* Owner subline inside the Business cell is redundant on desktop (own column) */
@media (min-width: 768px) { .show-mobile-sub { display: none; } }

/* ── Import modal ── */
.import-drop {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 30px 16px; cursor: pointer; text-align: center;
  border: 1.5px dashed var(--border-strong); border-radius: var(--r-md);
  color: var(--text-2);
  transition: border-color var(--dur-fast), background var(--dur-fast);
}
.import-drop:hover { border-color: var(--brand-500); background: rgba(145,76,157,0.05); }
.import-drop svg { width: 28px; height: 28px; }
.import-drop strong { color: var(--text-1); font-size: 14px; }
.import-drop span { font-size: 12.5px; }

.import-help { font-size: 12.5px; color: var(--text-2); line-height: 1.6; margin-top: 12px; }
.import-help em { font-style: normal; color: var(--text-1); }

.import-preview-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  flex-wrap: wrap; margin: 16px 0 10px; font-size: 13px;
}
.import-header-toggle {
  display: flex; align-items: center; gap: 7px;
  font-size: 12.5px; color: var(--text-2); cursor: pointer;
  -webkit-user-select: none; user-select: none;
}
.import-header-toggle input { accent-color: var(--brand-500); margin: 0; }

.import-preview-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--r-md); }
.import-preview-table { min-width: 100%; }
.import-preview-table th { padding: 8px; }
.import-preview-table td { padding: 8px 10px; font-size: 12.5px; white-space: nowrap; max-width: 220px; overflow: hidden; text-overflow: ellipsis; }
.import-preview-table .map-select { width: 100%; min-width: 130px; font-size: 12.5px; padding: 6px 8px; }

/* ── Projects ── */
/* Type chip under the project title */
.proj-type-chip {
  display: inline-block; margin-top: 4px;
  font-size: 11px; font-weight: 600; letter-spacing: 0.02em;
  padding: 2px 8px; border-radius: 100px;
  background: rgba(150,140,158,0.14); color: var(--text-2);
}
.proj-type-website     { background: rgba(145,76,157,0.14);  color: var(--text-brand); }
.proj-type-logo        { background: var(--violet-bg);      color: var(--violet); }
.proj-type-branding    { background: var(--warning-bg);     color: var(--warning); }
.proj-type-seo         { background: var(--success-bg);     color: var(--success); }
.proj-type-maintenance { background: var(--info-bg);        color: var(--info); }

/* Inline status pill colours (mirror the badge palette) */
.proj-status-none   { background: rgba(150,140,158,0.16); color: #8C8494; }
.proj-status-info   { background: var(--info-bg);    color: var(--info); }
.proj-status-violet { background: var(--violet-bg);  color: var(--violet); }
.proj-status-orange { background: var(--warning-bg); color: var(--warning); }
.proj-status-green  { background: var(--success-bg); color: var(--success); }
.proj-status-red    { background: var(--danger-bg);  color: var(--danger); }
/* Colour the .badge dot to match the pill (only badges have ::before) */
.badge.proj-status-none::before,
.badge.proj-status-info::before,
.badge.proj-status-violet::before,
.badge.proj-status-orange::before,
.badge.proj-status-green::before,
.badge.proj-status-red::before { background: currentColor; }

/* Progress bar */
.proj-progress { display: flex; align-items: center; gap: 8px; min-width: 120px; }
.proj-progress-bar {
  flex: 1; height: 7px; border-radius: 100px;
  background: rgba(150,140,158,0.18); overflow: hidden;
}
.proj-progress-bar span {
  display: block; height: 100%; border-radius: 100px;
  background: var(--brand-500); transition: width var(--dur-fast);
}
.proj-progress-num { font-size: 12px; font-weight: 600; color: var(--text-2); min-width: 32px; text-align: right; }

/* ══════════════════════════════════════════════════════════════════════════
   VISUAL REFRESH — 2026-06-30  (micro-interaction polish · Kowalski-style)
   Purely additive. Rules here only ENHANCE existing components — no layout,
   structure or behaviour changes.  Principles applied:
     · Hover *movement* is gated to fine-pointer devices so a touch tap never
       gets stuck in a hover state (phones/tablets are unaffected by the lifts).
     · Custom --ease-out curve, all durations < 300ms (already in the tokens).
     · Press (:active) states are re-affirmed AFTER the hover rules so a press
       always wins the cascade and the element settles under the finger.
     · Nothing animates `transform` on a modal ancestor (see .page-container
       note ~line 360) — every target below is a leaf element.
     · The reduced-motion block (kept last) neutralises all of this via
       !important, so users who ask for less motion still get a calm UI.
   ══════════════════════════════════════════════════════════════════════════ */

@media (hover: hover) and (pointer: fine) {
  /* Buttons gently lift, with a slightly warmer shadow, then settle on press. */
  .btn {
    transition: background var(--dur-fast), color var(--dur-fast),
                border-color var(--dur-fast), box-shadow var(--dur-fast),
                transform var(--dur-fast) var(--ease-out);
  }
  .btn:hover            { transform: translateY(-1px); }
  .btn-primary:hover    { box-shadow: 0 4px 16px rgba(145,76,157,0.45); }

  .icon-btn:hover       { transform: translateY(-1px); }

  /* Cards lean up toward the cursor and deepen their shadow. */
  .stat-card:hover      { transform: translateY(-3px); }
  .card:hover           { border-color: var(--border-strong); }

  /* Nav rows slide a hair toward their label; the active accent stays put. */
  .nav-item:hover       { transform: translateX(2px); }
  .bottom-nav-item:hover{ transform: translateY(-1px); }
  .dash-list-item:hover { transform: translateX(2px); }

  /* Selection cards (company / language / saved-client) */
  .company-card:hover   { transform: translateY(-1px); }
  .lang-option:hover    { transform: translateY(-1px); }

  /* Settings + table action affordances */
  .settings-nav-item:hover { transform: translateX(2px); }
  .action-btn:hover     { transform: translateY(-1px); }
  .page-btn:hover       { transform: translateY(-1px); }

  /* Theme toggle: a friendly little sun/moon swivel on hover. */
  [data-theme-toggle]:hover svg { transform: rotate(-12deg); }
  [data-theme-toggle] svg { transition: transform var(--dur-normal) var(--ease-spring); }
}

/* Press states ALWAYS win over the hover lifts above (settle under the finger). */
.btn:active            { transform: scale(0.97); }
.icon-btn:active       { transform: scale(0.93); }
.action-btn:active     { transform: scale(0.9); }
.stat-card:active      { transform: translateY(-1px) scale(0.995); }
.nav-item:active       { transform: scale(0.97); }
.bottom-nav-item:active{ transform: scale(0.95); }
.company-card:active   { transform: scale(0.98); }
.lang-option:active    { transform: scale(0.98); }
.page-btn:active       { transform: scale(0.94); }

/* The active-nav accent bar springs into place on navigation. */
.nav-item.active::before { transition: height var(--dur-normal) var(--ease-spring); }

/* Smoother fill on the project / progress bars (was --dur-fast / no easing). */
.proj-progress-bar span { transition: width var(--dur-slow) var(--ease-out); }

/* ── Staggered entrance for the stat-card row (decorative, fast, < 240ms) ──
   Leaf elements, no fixed-position descendants → safe to animate transform. */
.stat-cards .stat-card {
  /* `backwards` (not `both`): the start frame applies during the stagger delay,
     but once the entrance ends the element returns to the normal cascade — so a
     filled animation transform can't shadow the :hover lift defined above. The
     end frame equals the element's natural state, so there's no snap. */
  animation: statCardIn var(--dur-slow) var(--ease-out) backwards;
}
.stat-cards .stat-card:nth-child(1) { animation-delay: 0ms; }
.stat-cards .stat-card:nth-child(2) { animation-delay: 45ms; }
.stat-cards .stat-card:nth-child(3) { animation-delay: 90ms; }
.stat-cards .stat-card:nth-child(4) { animation-delay: 135ms; }
.stat-cards .stat-card:nth-child(5) { animation-delay: 180ms; }
.stat-cards .stat-card:nth-child(6) { animation-delay: 225ms; }
.stat-cards .stat-card:nth-child(7) { animation-delay: 270ms; }
.stat-cards .stat-card:nth-child(8) { animation-delay: 315ms; }
@keyframes statCardIn {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ── Login page: a touch more life ──────────────────────────────────────── */
.login-logo-mark {
  animation: loginMarkIn 520ms var(--ease-spring) both;
}
@keyframes loginMarkIn {
  from { opacity: 0; transform: scale(0.8) translateY(6px); }
  to   { opacity: 1; transform: scale(1)   translateY(0); }
}
@media (hover: hover) and (pointer: fine) {
  #toggle-pw:hover { color: var(--text-1) !important; }
}

/* ══════════════════════════════════════════════════════════════════════════
   POLISH PASS — 2026-07-01  (additive only; no layout/markup/logic changes)
   ══════════════════════════════════════════════════════════════════════════ */

/* The confirm dialog can now stack ON TOP of an open form modal (the new
   unsaved-changes guard). Same-z-index siblings stack by DOM order, which
   Modal.open() already guarantees — this makes it explicit and future-proof. */
#confirm-modal.modal-overlay { z-index: 10002; }

/* Themed thin scrollbars — the OS-default chrome scrollbar looks foreign
   against the dark surfaces (modal bodies, client dropdown, tables). */
* { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 8px; }
*::-webkit-scrollbar-thumb:hover { background: var(--text-3); }

/* Login error: a short, small-amplitude shake draws the eye to the message
   without feeling punitive. Runs once per render of the error box. */
.login-error { animation: loginErrShake 340ms var(--ease-out); }
@keyframes loginErrShake {
  0%, 100% { transform: translateX(0); }
  20%      { transform: translateX(-5px); }
  40%      { transform: translateX(5px); }
  60%      { transform: translateX(-3px); }
  80%      { transform: translateX(3px); }
}

/* Checkboxes/radios give a tiny tactile squeeze while pressed (pairs with the
   existing toggle tick sound), then settle back on release. */
input[type="checkbox"], input[type="radio"] { transition: transform 120ms var(--ease-out); }
input[type="checkbox"]:active, input[type="radio"]:active { transform: scale(0.88); }

/* ── PROFIT SPLIT — 2026-08-06 ─────────────────────────────────────────────
   Two extra dashboard stat cards: "My Net Profit" (revenue minus every
   invoice's partner share) and "Partner Share" (the accumulated payout).
   Same anatomy as the existing .stat-card variants — accent bar + tinted icon. */
.stat-card.profit::before  { background: var(--success); }
.stat-card.partner::before { background: var(--info); }
.stat-card.profit  .stat-icon { background: var(--success-bg); color: var(--success); }
.stat-card.partner .stat-icon { background: var(--info-bg);    color: var(--info); }
/* Second line under the label: the already-paid part of the same KPI. Quiet on
   purpose — the card's headline number is the one that matters. */
.stat-sub { font-size: 11px; color: var(--text-3); opacity: 0.75; margin-top: 3px; }
.stat-sub .num { font-family: 'JetBrains Mono', monospace; }

/* The UA rule for the `hidden` attribute is `[hidden]{display:none}`, which any
   class that sets `display` silently beats — `.btn` is `inline-flex`, so
   `pinRemoveBtn.hidden = true` left the button on screen. Fixed here rather
   than with a blanket `[hidden]{display:none!important}`: `.modal-overlay` is
   `display:flex` while carrying `hidden` between opens and fades in via
   opacity, so forcing display:none on it globally would kill that transition. */
.btn[hidden] { display: none !important; }

/* ── LOGIN: PASSWORD / PIN SWITCHER — 2026-08-07 ───────────────────────────
   The login card now offers two credentials. There is no "keep me signed in"
   any more, so the PIN is what makes signing in again cheap on a phone. */
.login-modes { width: 100%; margin-bottom: 22px; }
.login-modes .tab { flex: 1; justify-content: center; padding: 7px 10px; }

/* Six separate boxes rather than one field: it reads as a passcode, and each
   box is a real <input name="pin_d[]">, so the form still submits without JS. */
.pin-boxes { display: flex; gap: 8px; justify-content: space-between; }
.pin-box {
  flex: 1 1 0; min-width: 0; width: 100%;
  height: 54px;
  text-align: center;
  font-size: 22px; font-weight: 600;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  color: var(--text-1);
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  outline: none;
  transition: border-color var(--dur-fast), box-shadow var(--dur-fast), transform var(--dur-fast);
  -moz-appearance: textfield;
}
.pin-box::-webkit-outer-spin-button, .pin-box::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.pin-box:focus {
  border-color: var(--brand-500);
  box-shadow: 0 0 0 3px rgba(145,76,157,0.18);
}
.pin-box:not(:placeholder-shown) { background: var(--surface-1); }
@media (max-width: 380px) { .pin-box { height: 48px; font-size: 19px; } }

/* Settings → Account: the PIN card's own six boxes reuse the same look, just
   smaller and left-aligned instead of stretched across the card. */
.pin-boxes.compact { justify-content: flex-start; max-width: 300px; }
.pin-boxes.compact .pin-box { height: 44px; font-size: 18px; }

/* ── DASHBOARD: REVENUE TABLE VIEW — 2026-08-07 ────────────────────────────
   The same six months as the bar chart, as a matrix: one row per month, one
   column per income stream, plus month totals down the right and stream totals
   along the bottom. Scrolls inside its own box — the page never scrolls
   sideways, however many companies earn something. */
.rev-table-wrap { overflow-x: auto; padding: 4px 0 8px; -webkit-overflow-scrolling: touch; }
.rev-table { width: 100%; border-collapse: collapse; font-size: 13px; min-width: 460px; }
.rev-table th, .rev-table td { padding: 10px 16px; text-align: right; white-space: nowrap; }
.rev-table thead th { font-size: 11px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-2); border-bottom: 1px solid var(--border); }
.rev-table thead th:first-child,
.rev-table tbody th, .rev-table tfoot th { text-align: left; }
.rev-table tbody th { font-weight: 600; color: var(--text-1); }
.rev-table tbody tr + tr th, .rev-table tbody tr + tr td { border-top: 1px solid var(--border); }
.rev-table tbody tr:hover td, .rev-table tbody tr:hover th { background: var(--surface-2); }
.rev-table .num { font-family: 'JetBrains Mono', monospace; color: var(--text-1); }
/* A stream that earned nothing that month is present but stays quiet. */
.rev-table .rev-zero { color: var(--text-2); opacity: 0.75; }
.rev-table .rev-total-col { font-weight: 700; color: var(--text-1); }
.rev-table tfoot th, .rev-table tfoot td { border-top: 2px solid var(--border-strong); font-weight: 700; color: var(--text-1); }
/* The legend swatch, so a column maps back to its bar colour at a glance. */
.rev-dot { display: inline-block; width: 8px; height: 8px; border-radius: 2px; margin-right: 6px; vertical-align: baseline; }
@media (max-width: 640px) {
  .rev-table th, .rev-table td { padding: 9px 11px; }
  .rev-table { font-size: 12px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   DASHBOARD REDESIGN — 2026-08-19
   A self-contained `dsh-` namespace. Nothing here overrides an existing
   component: the dashboard's old classes (.stat-card, .dash-list-item,
   .rev-table, .badge, .proj-progress-bar) are all still in use and still
   styled by their original rules above — this block only adds the new shell,
   the KPI anatomy, the donut and the two new lists.
   Every colour comes from the design tokens, so light and dark both work; the
   only literals are the per-accent tints, which need an alpha channel.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Per-theme text steps (measured, not guessed) ─────────────────────────
   A hue that passes on one surface rarely passes on the other, so each theme
   gets its own step of the same hue rather than one value flipped over. Now
   declared LIGHT-first, matching the token block at the top of this file.
   Ratios are measured against the surface each colour actually sits on. */
:root {
  --dsh-grid:       rgba(145,76,157,0.10);  /* blueprint graph-paper hairline */
  --dsh-link:       var(--brand-600);   /* #7A3E85 → 7.60 on white           */
  --dsh-link-hover: var(--brand-700);   /* #5F2F69 → 10.9                    */
  --dsh-good:       #22713F;            /* 5.72 on the light success chip    */
  --dsh-bad:        #A3341F;            /* 6.34 on the light danger chip     */
  --dsh-warn:       #8A5B06;            /* 5.61 on the light warning chip    */
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --dsh-grid:       rgba(217,169,224,0.10);
    --dsh-link:       var(--brand-300); /* #D9A9E0 → 9.42 on #17111C         */
    --dsh-link-hover: var(--peach-400); /* apricot is the dark-mode highlight */
    --dsh-good:       var(--success);   /* 8.11 on the dark success chip     */
    --dsh-bad:        var(--danger);    /* 6.02                              */
    --dsh-warn:       var(--warning);   /* 9.34                              */
  }
}
[data-theme="dark"] {
  --dsh-grid:       rgba(217,169,224,0.10);
  --dsh-link:       var(--brand-300);
  --dsh-link-hover: var(--peach-400);
  --dsh-good:       var(--success);
  --dsh-bad:        var(--danger);
  --dsh-warn:       var(--warning);
}

/* Accent map. A component opts in with data-accent="…" and then reads --acc /
   --acc-soft, so one rule set serves every colour variant. */
.dsh-kpi, .dsh-mini { --acc: var(--text-brand); --acc-soft: rgba(145,76,157,0.10); }
[data-accent="brand"]   { --acc: var(--text-brand); --acc-soft: rgba(145,76,157,0.10); }
[data-theme="dark"] [data-accent="brand"],
[data-theme="dark"] .dsh-kpi,
[data-theme="dark"] .dsh-mini { --acc-soft: rgba(169,104,180,0.16); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) [data-accent="brand"],
  :root:not([data-theme="light"]) .dsh-kpi,
  :root:not([data-theme="light"]) .dsh-mini { --acc-soft: rgba(169,104,180,0.16); }
}
[data-accent="success"],
[data-accent="paid"]    { --acc: var(--success);   --acc-soft: rgba(47,143,91,0.13); }
[data-accent="danger"]  { --acc: var(--danger);    --acc-soft: rgba(199,69,47,0.13); }
[data-accent="warning"] { --acc: var(--warning);   --acc-soft: rgba(217,161,24,0.14); }
[data-accent="info"]    { --acc: var(--info);      --acc-soft: rgba(46,124,151,0.13); }
[data-accent="violet"]  { --acc: var(--violet);    --acc-soft: rgba(100,85,199,0.14); }

.dsh-i   { width: 19px; height: 19px; flex-shrink: 0; }
.dsh-i-s { width: 15px; height: 15px; flex-shrink: 0; }

/* ── Hero ────────────────────────────────────────────────────────────────── */
.dsh-hero {
  position: relative;
  overflow: hidden;
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 22px; flex-wrap: wrap;
  padding: 26px 28px;
  margin-bottom: 22px;
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  background:
    linear-gradient(135deg, rgba(145,76,157,0.09) 0%, rgba(251,186,126,0.10) 26%, rgba(255,255,255,0) 56%),
    var(--surface-1);
  box-shadow: var(--shadow-md);
  animation: dshIn 420ms var(--ease-out) backwards;
}
/* A soft brand bloom in the top-right. Pointer-events off so it can never eat
   a click on the action buttons underneath it. */
/* Blueprint field + brand bloom. The graph paper is the one detail that makes
   this dashboard unmistakably THIS product: the invoice template it generates
   is called Blueprint and is drafted on exactly this grid. Two hairline
   gradients, masked so the grid resolves out of nothing toward the corner —
   no image, no request, and it costs one composited layer. */
.dsh-hero-glow {
  position: absolute; inset: 0;
  pointer-events: none;
  background:
    radial-gradient(circle at 88% -20%, rgba(251,186,126,0.34) 0%, rgba(145,76,157,0.12) 34%, rgba(145,76,157,0) 62%),
    repeating-linear-gradient(0deg,  var(--dsh-grid) 0 1px, transparent 1px 22px),
    repeating-linear-gradient(90deg, var(--dsh-grid) 0 1px, transparent 1px 22px);
  -webkit-mask-image: radial-gradient(ellipse 75% 130% at 100% 0%, #000 10%, transparent 72%);
          mask-image: radial-gradient(ellipse 75% 130% at 100% 0%, #000 10%, transparent 72%);
}
.dsh-hero-copy   { position: relative; min-width: 0; }
.dsh-hero-eyebrow {
  font-size: 10px; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--text-2); margin-bottom: 9px;
}

/* Hero meta band — the Blueprint invoice's key/value device. Tiny wide-tracked
   key, large tabular value, hairline separators between the cells. */
.dsh-meta {
  position: relative;
  display: flex; flex-wrap: wrap;
  margin-top: 18px;
}
.dsh-meta > div {
  padding: 0 22px;
  border-left: 1px solid var(--border);
}
.dsh-meta > div:first-child { padding-left: 0; border-left: 0; }
.dsh-meta dt {
  font-size: 9.5px; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--text-2); margin-bottom: 5px; white-space: nowrap;
}
.dsh-meta dd {
  font-family: 'JetBrains Mono', monospace;
  font-size: 17px; font-weight: 700; letter-spacing: -0.01em;
  color: var(--text-1); font-variant-numeric: tabular-nums;
}
.dsh-hero-title {
  font-family: 'Sora', sans-serif; font-weight: 700;
  font-size: clamp(22px, 1.4vw + 16px, 30px); line-height: 1.12;
  color: var(--text-1); letter-spacing: -0.01em;
}
/* Orchid into apricot — the logo's own two colours, in the order they appear
   in the mark. Both stops are dark enough in light mode to carry the name at
   hero size; in dark mode the ramp is inverted so it stays legible there too. */
.dsh-hero-title span {
  background: linear-gradient(100deg, var(--brand-500) 10%, var(--peach-700) 96%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
[data-theme="dark"] .dsh-hero-title span {
  background: linear-gradient(100deg, var(--brand-300) 10%, var(--peach-400) 96%);
  -webkit-background-clip: text; background-clip: text;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .dsh-hero-title span {
    background: linear-gradient(100deg, var(--brand-300) 10%, var(--peach-400) 96%);
    -webkit-background-clip: text; background-clip: text;
  }
}
.dsh-hero-sub { margin-top: 8px; font-size: 13.5px; color: var(--text-2); }
.dsh-hero-sub strong { color: var(--text-1); font-weight: 700; }
.dsh-hero-actions { position: relative; display: flex; gap: 10px; flex-wrap: wrap; }

/* ── Section bar (Statistics + period toggle) ────────────────────────────── */
.dsh-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; flex-wrap: wrap; margin-bottom: 14px;
}
.dsh-bar-title {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: 'Sora', sans-serif; font-weight: 700; font-size: 13px;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-1);
}
.dsh-bar-title em {
  font-style: normal; font-family: 'DM Sans', sans-serif;
  font-size: 12px; font-weight: 600; color: var(--text-2);
  padding: 2px 9px; border-radius: 100px;
  background: var(--surface-2); border: 1px solid var(--border);
}
.dsh-bar-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--brand-500); box-shadow: 0 0 0 4px rgba(145,76,157,0.16);
}

/* ── KPI cards ───────────────────────────────────────────────────────────── */
.dsh-kpis {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px; margin-bottom: 14px;
}
.dsh-kpi {
  position: relative; overflow: hidden;
  padding: 18px;
  min-height: 172px;
  display: flex; flex-direction: column;
  background: var(--surface-1);
  /* Drafted, not floating: the hairline does the work a drop shadow was
     doing, which is what makes the set read as a measured instrument panel
     rather than four cards hovering over a page. */
  border: 1px solid var(--border-strong);
  border-radius: var(--r-lg);
  transition: border-color var(--dur-fast), box-shadow var(--dur-fast), transform var(--dur-fast) var(--ease-out);
  animation: dshIn 420ms var(--ease-out) backwards;
}
.dsh-kpi::before {                       /* the accent hairline along the top */
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px;
  background: linear-gradient(90deg, var(--acc), transparent 92%);
}
.dsh-kpis .dsh-kpi:nth-child(2) { animation-delay: 60ms; }
.dsh-kpis .dsh-kpi:nth-child(3) { animation-delay: 120ms; }
.dsh-kpis .dsh-kpi:nth-child(4) { animation-delay: 180ms; }

.dsh-kpi-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 14px; }
.dsh-kpi-ico {
  width: 38px; height: 38px; border-radius: var(--r-md);
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--acc-soft); color: var(--acc);
}
.dsh-kpi-val {
  font-size: clamp(20px, 1.2vw + 13px, 28px); font-weight: 700;
  color: var(--text-1); line-height: 1.02; letter-spacing: -0.025em;
  font-variant-numeric: tabular-nums;
}
/* The kicker: 10px, uppercase, .16em tracking — lifted from the Blueprint
   invoice's own field labels. Small and quiet so the figure above it is
   unambiguously the thing being read. */
.dsh-kpi-lab {
  margin-top: 7px;
  font-size: 10px; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--text-2);
}
.dsh-kpi-sub { margin-top: 3px; font-size: 11.5px; color: var(--text-2); }
.dsh-kpi-sub .num { font-family: 'JetBrains Mono', monospace; }

/* Delta chip — "▲ 12.4% vs. last month". `is-flat` stays neutral so a quiet
   period doesn't shout in either colour. */
.dsh-delta {
  font-size: 11px; font-weight: 700; letter-spacing: 0.01em;
  padding: 3px 9px; border-radius: 100px; white-space: nowrap;
  border: 1px solid transparent;
}
.dsh-delta.is-good { background: var(--success-bg); color: var(--dsh-good); border-color: rgba(47,143,91,0.22); }
.dsh-delta.is-bad  { background: var(--danger-bg);  color: var(--dsh-bad);  border-color: rgba(199,69,47,0.22); }
.dsh-delta.is-flat { background: var(--surface-2);  color: var(--text-2);   border-color: var(--border); }
.dsh-delta[hidden] { display: none !important; }

/* Collection meter (Paid card) */
.dsh-meter {
  margin-top: 12px; height: 6px; border-radius: 100px;
  background: rgba(150,140,158,0.18); overflow: hidden;
}
.dsh-meter span {
  display: block; height: 100%; border-radius: 100px;
  background: linear-gradient(90deg, var(--acc), color-mix(in srgb, var(--acc) 55%, #ffffff));
  transition: width var(--dur-slow) var(--ease-out);
  transform-origin: left center;
  animation: dshBarReveal 800ms var(--ease-out) 260ms backwards;
}
/* color-mix is recent; browsers without it just get the flat accent. */
@supports not (background: color-mix(in srgb, red 50%, blue)) {
  .dsh-meter span { background: var(--acc); }
}

/* Sparkline — 8 months of context, pinned to the bottom of the card and bled
   to its edges. Purely decorative: aria-hidden in the markup. */
.dsh-spark {
  display: block; width: calc(100% + 36px); height: 40px;
  margin: auto -18px -18px; overflow: visible;   /* bleeds to the card edges */
}
.dsh-spark-line {
  fill: none; stroke: var(--acc); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke;
  /* pathLength="100" on the polyline normalises its length, so the line can be
     drawn with a flat dasharray of 100 — no JS measuring, no per-card maths.
     The trace reads left-to-right, the same direction as the months it plots. */
  stroke-dasharray: 100; stroke-dashoffset: 100;
  animation: dshSparkDraw 1100ms var(--ease-out) 220ms forwards;
}
.dsh-spark-fill {
  fill: var(--acc); stroke: none; opacity: 0;
  animation: dshSparkFill 700ms var(--ease-out) 900ms forwards;   /* lands as the line finishes */
}
.dsh-kpis .dsh-kpi:nth-child(2) .dsh-spark-line { animation-delay: 320ms; }
.dsh-kpis .dsh-kpi:nth-child(3) .dsh-spark-line { animation-delay: 420ms; }
.dsh-kpis .dsh-kpi:nth-child(4) .dsh-spark-line { animation-delay: 520ms; }
@keyframes dshSparkDraw { to { stroke-dashoffset: 0; } }
@keyframes dshSparkFill { to { opacity: 0.10; } }

/* ── Secondary KPI strip ─────────────────────────────────────────────────── */
.dsh-minis {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px; margin-bottom: 26px;
}
.dsh-mini {
  display: flex; align-items: center; gap: 12px;
  padding: 13px 15px; min-width: 0;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  color: inherit; text-decoration: none;
  transition: border-color var(--dur-fast), background var(--dur-fast), transform var(--dur-fast) var(--ease-out);
  animation: dshIn 420ms var(--ease-out) backwards;
}
a.dsh-mini:hover { border-color: var(--border-strong); background: var(--surface-2); }
.dsh-minis .dsh-mini:nth-child(2) { animation-delay: 50ms; }
.dsh-minis .dsh-mini:nth-child(3) { animation-delay: 100ms; }
.dsh-minis .dsh-mini:nth-child(4) { animation-delay: 150ms; }
.dsh-mini-ico {
  width: 32px; height: 32px; flex-shrink: 0; border-radius: 9px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--acc-soft); color: var(--acc);
}
.dsh-mini-body { min-width: 0; }
.dsh-mini-val {
  font-size: 16px; font-weight: 700; color: var(--text-1); line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.dsh-mini-lab {
  margin-top: 3px;
  font-size: 9.5px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--text-2);
}
/* The kicker is uppercase; its trailing detail is NOT — caps-casing a money
   value cramps the tabular digits and turns the row into noise. */
.dsh-mini-sub {
  text-transform: none; letter-spacing: normal;
  font-size: 11px; font-weight: 500; color: var(--text-2);
}
.dsh-mini-sub .num { font-family: 'JetBrains Mono', monospace; }

/* ── Section rows ────────────────────────────────────────────────────────── */
.dsh-row { display: grid; gap: 20px; margin-bottom: 24px; align-items: start; }
.dsh-row-wide {
  grid-template-columns: minmax(0, 1.65fr) minmax(0, 1fr);
  /* The chart card grows to the donut card's height instead of leaving a step
     between the two — the plot takes up the slack, not whitespace. */
  align-items: stretch;
}
.dsh-row-even { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* A row whose partner card is conditionally absent must not leave a hole. */
.dsh-row > :only-child { grid-column: 1 / -1; }

.dsh-card { border-radius: var(--r-lg); animation: dshIn 420ms var(--ease-out) backwards; }
.dsh-row-wide > .dsh-card { display: flex; flex-direction: column; }
.dsh-row .dsh-card:nth-child(2) { animation-delay: 80ms; }

.dsh-card-h {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 14px; flex-wrap: wrap;
  padding: 17px 20px 14px;
  border-bottom: 1px solid var(--border);
}
.dsh-card-title { min-width: 0; }
.dsh-card-title h3 {
  display: flex; align-items: center; gap: 9px;
  font-family: 'Sora', sans-serif; font-size: 13px; font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-1);
}
.dsh-card-title p { margin-top: 3px; font-size: 12px; color: var(--text-2); }
.dsh-card-title p .num { font-family: 'JetBrains Mono', monospace; color: var(--text-2); font-weight: 600; }
.dsh-card-tools { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.dsh-h-ico {
  width: 26px; height: 26px; border-radius: 8px; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
}
.dsh-h-ico.tone-brand  { background: rgba(169,104,180,0.13);  color: var(--text-brand); }
.dsh-h-ico.tone-violet { background: var(--violet-bg);      color: var(--violet); }
.dsh-h-ico.tone-warn   { background: var(--warning-bg);     color: var(--warning); }
.dsh-h-ico.tone-info   { background: var(--info-bg);        color: var(--info); }
.dsh-link {
  font-size: 12.5px; font-weight: 600; color: var(--dsh-link);
  white-space: nowrap; text-decoration: none;
  transition: color var(--dur-fast);
}
.dsh-link:hover { color: var(--dsh-link-hover); text-decoration: underline; }

/* The legend is never hidden: with two or more income streams it is the only
   thing that stops the bars being identified by colour alone. It wraps. */
.dsh-legend { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; font-size: 11.5px; color: var(--text-2); font-weight: 500; }
.dsh-legend i { display: inline-block; width: 9px; height: 9px; border-radius: 3px; margin-right: 6px; vertical-align: middle; }

/* min-height, not height: inside the stretched card the box grows with the
   card, and it always reserves room for the x-axis band under the plot. */
.dsh-chart-box { flex: 1; min-height: 300px; padding: 14px 14px 10px; }

/* The plot and the month-breakdown panel share this box, so it is a column:
   the canvas takes what is left after the panel, and the panel is only as tall
   as its content. */
#rev-view-chart { display: flex; flex-direction: column; }
/* Chart.js sizes a responsive canvas from its PARENT. Left in normal flow next
   to the panel, the canvas would be measuring a height it is itself part of —
   an infinite growth loop (13.485px and climbing, before this box existed).
   Taking it out of flow breaks the cycle at the source: `.rev-canvas` has a
   height, the canvas fills it, and the canvas contributes nothing back. */
/* The floor belongs on the PLOT, not on the plot-plus-panel: with
   `.dsh-chart-box`'s 300px covering the pair, opening the breakdown crushed the
   chart from 316px to 230px to pay for it. Here the card grows by exactly the
   panel's height and the bars keep the size they had. */
.rev-canvas { position: relative; flex: 1 1 auto; min-height: 300px; }
.rev-canvas > canvas { position: absolute; inset: 0; width: 100% !important; height: 100% !important; }

/* ── Invoice-status donut ─────────────────────────────────────────────────
   A thin ring for the at-a-glance split, exact figures in the rows below it,
   and a per-row bar for the comparison an arc can't give (two arcs of similar
   size are hard to rank by eye; two bars never are). Hovering or focusing a
   row isolates its arc and reads that slice out in the middle. */
.dsh-donut-wrap { display: flex; justify-content: center; padding: 20px 0 6px; }
.dsh-donut { width: 178px; height: 178px; animation: dshDonut 520ms var(--ease-spring) both; }
.dsh-donut-track { fill: none; stroke: var(--surface-2); stroke-width: 13; }
.dsh-donut-seg {
  fill: none; stroke-width: 13; stroke-linecap: butt;
  animation: dshFade 420ms var(--ease-out) backwards; animation-delay: var(--d, 0ms);
  /* The Count/Value switch re-weights the ring — the arcs travel there. */
  transition: stroke-dasharray var(--dur-slow) var(--ease-out),
              stroke-dashoffset var(--dur-slow) var(--ease-out),
              opacity var(--dur-fast), stroke-width var(--dur-fast);
}
/* Isolate the hovered slice: everything else recedes, the live one thickens. */
[data-hi] .dsh-donut-seg { opacity: 0.24; }
[data-hi="paid"]    .dsh-donut-seg[data-st="paid"],
[data-hi="sent"]    .dsh-donut-seg[data-st="sent"],
[data-hi="draft"]   .dsh-donut-seg[data-st="draft"],
[data-hi="overdue"] .dsh-donut-seg[data-st="overdue"] { opacity: 1; stroke-width: 16; }

/* A hero figure wears the body sans with proportional digits — a display face
   or tabular figures both read as decoration at this size. */
.dsh-donut-num {
  font-family: 'DM Sans', system-ui, sans-serif;
  font-size: 30px; font-weight: 700; letter-spacing: -0.02em;
  font-variant-numeric: proportional-nums; fill: var(--text-1);
}
.dsh-donut-cap { font-size: 9px; font-weight: 700; letter-spacing: 0.18em; fill: var(--text-2); }

.dsh-donut-rows { padding: 6px 12px 14px; }
.dsh-donut-row {
  display: grid;
  grid-template-columns: 10px minmax(0, 1fr) auto auto;
  align-items: center; gap: 4px 10px;
  padding: 9px 10px; border-radius: var(--r-md);
  font-size: 13px; color: inherit; text-decoration: none;
  transition: background var(--dur-fast);
}
.dsh-donut-row:hover,
.dsh-donut-row:focus-visible { background: var(--surface-2); }
.dsh-donut-dot { width: 10px; height: 10px; border-radius: 3px; }
.dsh-donut-name { font-weight: 600; color: var(--text-1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dsh-donut-amt  { font-family: 'JetBrains Mono', monospace; font-size: 12.5px; font-weight: 700; color: var(--text-1); }
.dsh-donut-cnt  { font-size: 11.5px; color: var(--text-2); min-width: 58px; text-align: right; }
.dsh-donut-cnt b { color: var(--text-2); font-weight: 700; }
.dsh-donut-bar {
  grid-column: 2 / -1; height: 4px; border-radius: 100px;
  background: rgba(150,140,158,0.18); overflow: hidden;
}
.dsh-donut-bar i {
  display: block; height: 100%; border-radius: 100px;
  transition: width var(--dur-slow) var(--ease-out);
  transform-origin: left center;
  animation: dshBarReveal 700ms var(--ease-out) both;
}
.dsh-donut-row:nth-child(1) .dsh-donut-bar i { animation-delay: 260ms; }
.dsh-donut-row:nth-child(2) .dsh-donut-bar i { animation-delay: 350ms; }
.dsh-donut-row:nth-child(3) .dsh-donut-bar i { animation-delay: 440ms; }
.dsh-donut-row:nth-child(4) .dsh-donut-bar i { animation-delay: 530ms; }
@keyframes dshBarReveal { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* ══ REVENUE CARD — legend, month breakdown, stream ring ═══════════════════
   The revenue card carries three views of one set of numbers: Chart (by
   month), Donut (by income stream) and Table (the record). What follows is
   everything the Chart/Donut pair needs that Chart.js cannot draw on a canvas.

   `hidden` is honoured explicitly on each of these, never with a blanket
   `[hidden]{display:none!important}` — the rule at the top of this file
   explains why, and every one of these boxes carries a `display` of its own
   that would otherwise win. */
#rev-view-chart[hidden],
#rev-view-donut[hidden],
#rev-legend[hidden],
.rev-detail[hidden],
.rev-hint[hidden],
.rev-donut-empty[hidden] { display: none !important; }

/* ── The legend is a control, not a caption ───────────────────────────────
   Clicking one mutes that income stream in the bars AND in the ring, which is
   how a six-deep stack becomes readable: mute the two that dominate and the
   rest stop being slivers. */
.dsh-leg {
  display: inline-flex; align-items: center;
  padding: 5px 10px; border-radius: 8px;
  font-family: inherit; font-size: 11.5px; font-weight: 500;
  color: var(--text-2); background: transparent;
  border: 1px solid var(--border);
  cursor: pointer; text-align: left;
  transition: background var(--dur-fast), border-color var(--dur-fast), color var(--dur-fast);
}
.dsh-leg:hover { background: var(--surface-2); color: var(--text-1); }
/* A muted stream is struck through rather than faded out: dimming the LABEL is
   the obvious move and it takes 11.5px type straight through the contrast
   floor. The dot is decorative — the label names the stream — so the dot is
   what dims, and the text keeps its full step. */
.dsh-leg.off { border-style: dashed; border-color: var(--border-strong); text-decoration: line-through; }
.dsh-leg.off i { opacity: 0.32; }
/* Chips are their own boundary, so they need less air between them than the
   bare captions this list used to hold. */
#rev-legend { gap: 7px; }
.dsh-leg i { transition: transform var(--dur-fast) var(--ease-out), opacity var(--dur-fast); }
.dsh-leg:hover i { transform: scale(1.22); }

/* ── The month breakdown, under the plot ──────────────────────────────────
   A tooltip is a glance; this is a reading. It outlives the pointer, so the
   figures stay put while the eye goes back to compare the columns. */
.rev-picked { padding: 4px 2px 0; }
.rev-hint {
  font-size: 11px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--text-2); text-align: center; padding: 8px 0 2px;
}
.rev-detail {
  margin-top: 10px; padding: 13px 14px 14px;
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface-2);
  animation: revDetailIn var(--dur-normal) var(--ease-out) both;
}
@keyframes revDetailIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.rev-detail-h {
  display: flex; align-items: baseline; gap: 10px;
  padding-bottom: 11px; margin-bottom: 12px;
  border-bottom: 1px solid var(--border);
}
.rev-detail-title {
  font-family: 'Sora', sans-serif; font-size: 11px; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-1);
}
.rev-detail-total {
  margin-left: auto;
  font-family: 'JetBrains Mono', monospace; font-size: 14px; font-weight: 700;
  color: var(--text-1); font-variant-numeric: tabular-nums;
}
.rev-detail-x {
  align-self: center; flex-shrink: 0;
  width: 22px; height: 22px; padding: 0; border: 0; border-radius: 7px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; color: var(--text-2); cursor: pointer;
  transition: background var(--dur-fast), color var(--dur-fast);
}
.rev-detail-x svg { width: 13px; height: 13px; }
.rev-detail-x:hover { background: var(--surface-3); color: var(--text-1); }
.rev-detail-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(158px, 1fr)); gap: 10px; }
.rev-dcard {
  padding: 11px 12px 12px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--surface-1);
}
.rev-dcard-lab {
  display: flex; align-items: center; gap: 7px;
  font-size: 11px; color: var(--text-2); font-weight: 600;
  margin-bottom: 7px; min-width: 0;
}
.rev-dcard-lab i { width: 8px; height: 8px; border-radius: 2px; flex-shrink: 0; }
.rev-dcard-val {
  font-family: 'JetBrains Mono', monospace; font-size: 15px; font-weight: 700;
  color: var(--text-1); font-variant-numeric: tabular-nums; letter-spacing: -0.01em;
}
.rev-dcard-bar {
  height: 3px; margin: 9px 0 6px; border-radius: 100px;
  background: rgba(150,140,158,0.18); overflow: hidden;
}
.rev-dcard-bar i {
  display: block; height: 100%; border-radius: 100px;
  transform-origin: left center;
  animation: dshBarReveal 620ms var(--ease-out) both;
}
.rev-dcard-sub { font-size: 11px; color: var(--text-2); }
.rev-detail-none { font-size: 12.5px; color: var(--text-2); }

/* ── DONUT VIEW: the same money cut by stream instead of by time ───────── */
.rev-donut-view { display: block; padding: 14px 16px 18px; }
.rev-months { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 18px; }
.rev-pill {
  padding: 5px 13px; border-radius: 100px;
  font-family: inherit; font-size: 12px; font-weight: 600;
  color: var(--text-2); background: transparent;
  border: 1px solid var(--border);
  cursor: pointer; white-space: nowrap;
  transition: background var(--dur-fast), color var(--dur-fast),
              border-color var(--dur-fast);
}
.rev-pill:hover { background: var(--surface-2); color: var(--text-1); }
.rev-pill.active {
  background: var(--brand-500); border-color: var(--brand-500);
  color: #fff; font-weight: 700;
}
/* A month that earned nothing is still selectable — "was July really empty?"
   is a question worth being able to answer — but it says so before you click. */
.rev-pill.is-empty:not(.active) { border-style: dashed; }

.rev-donut-wrap { display: flex; align-items: center; gap: 30px; flex-wrap: wrap; }
.rev-donut-fig { flex: 0 0 auto; display: flex; justify-content: center; margin: 0 auto; }
.rev-donut { width: 210px; height: 210px; animation: dshDonut 520ms var(--ease-spring) both; }
.rev-donut-track { fill: none; stroke: var(--surface-2); stroke-width: 18; }
.rev-donut-seg {
  fill: none; stroke-width: 18; stroke-linecap: butt;
  animation: revArcIn 760ms cubic-bezier(0.23,1,0.32,1) backwards;
  animation-delay: var(--d, 0ms);
  transition: opacity var(--dur-fast), stroke-width var(--dur-fast);
}
/* Only the LENGTH of the dash grows. Each arc's dashoffset is its position on
   the ring, so unfurling from 0 grows it from where it actually starts rather
   than sliding it around the circle. */
@keyframes revArcIn { from { stroke-dasharray: 0 100000; } }
.rev-donut-seg.is-dim { opacity: 0.22; }
.rev-donut-seg.is-hi  { stroke-width: 22; }
.rev-donut-num {
  font-family: 'DM Sans', system-ui, sans-serif;
  font-size: 26px; font-weight: 700; letter-spacing: -0.02em;
  font-variant-numeric: proportional-nums; fill: var(--text-1);
}
.rev-donut-cap { font-size: 9px; font-weight: 700; letter-spacing: 0.16em; fill: var(--text-2); }

.rev-donut-rows { flex: 1 1 260px; min-width: 0; }
.rev-donut-row {
  display: grid;
  grid-template-columns: 10px minmax(0, 1fr) auto auto;
  align-items: center; gap: 5px 10px;
  padding: 9px 10px; border-radius: var(--r-md);
  font-size: 13px;
  transition: background var(--dur-fast);
}
.rev-donut-row.is-hi { background: var(--surface-2); }
.rev-donut-dot  { width: 10px; height: 10px; border-radius: 3px; }
.rev-donut-name { font-weight: 600; color: var(--text-1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rev-donut-amt  { font-family: 'JetBrains Mono', monospace; font-size: 12.5px; font-weight: 700; color: var(--text-1); }
.rev-donut-pct  { font-size: 11.5px; font-weight: 600; color: var(--text-2); min-width: 34px; text-align: right; }
/* Second row of the grid: the proportion bar takes the width the name and the
   amount occupy above it, and the change chip sits under the percentage it
   qualifies. Only present when ONE month is selected — "all 6 months" has no
   previous six months to be up or down against. */
.rev-donut-bar {
  grid-column: 2 / 4; height: 4px; align-self: center; border-radius: 100px;
  background: rgba(150,140,158,0.18); overflow: hidden;
}
.rev-donut-bar i {
  display: block; height: 100%; border-radius: 100px;
  transform-origin: left center;
  animation: dshBarReveal 700ms var(--ease-out) both;
}
.rev-donut-delta {
  grid-column: 4 / -1; justify-self: end;
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.01em;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.rev-donut-delta:empty { display: none; }
.rev-donut-delta.is-up   { color: var(--on-success); }
.rev-donut-delta.is-down { color: var(--on-danger); }
/* A stream that earned nothing last month has no percentage to have grown BY,
   and one that changed by nothing has no direction to point in. "new" and
   "flat" are the honest words; "+∞%", "▲ 0%" and a silently missing chip
   are not. */
.rev-donut-delta.is-new,
.rev-donut-delta.is-flat {
  color: var(--text-2); font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.1em; font-size: 9.5px;
}
.rev-donut-empty {
  margin-top: 14px; padding: 12px 14px;
  border: 1px dashed var(--border-strong); border-radius: var(--r-md);
  font-size: 12.5px; color: var(--text-2); text-align: center;
}

@media (max-width: 560px) {
  .rev-donut-view { padding: 12px 12px 16px; }
  .rev-donut-wrap { gap: 14px; }
  .rev-donut { width: 178px; height: 178px; }
  .rev-detail-h { flex-wrap: wrap; }
  .rev-detail-total { margin-left: 0; }
}

/* ── Due & outstanding ───────────────────────────────────────────────────── */
.dsh-due-list { padding: 6px 10px 10px; }
.dsh-due {
  display: flex; align-items: center; gap: 13px;
  padding: 11px 10px; border-radius: var(--r-md);
  color: inherit; text-decoration: none; cursor: pointer;
  transition: background var(--dur-fast), transform var(--dur-fast) var(--ease-out);
}
.dsh-due + .dsh-due { border-top: 1px solid var(--border); border-radius: 0; }
.dsh-due:hover { background: var(--surface-2); }
.dsh-due-date {
  width: 46px; height: 46px; flex-shrink: 0; border-radius: var(--r-md);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  background: var(--surface-2); border: 1px solid var(--border);
}
.dsh-due-date b { font-family: 'JetBrains Mono', monospace; font-size: 15px; font-weight: 700; line-height: 1; color: var(--text-1); }
.dsh-due-date i { font-style: normal; font-size: 9.5px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-2); margin-top: 3px; }
.dsh-due-date.tone-red    { background: var(--danger-bg);  border-color: rgba(199,69,47,0.3); }
.dsh-due-date.tone-red b,
.dsh-due-date.tone-red i  { color: var(--dsh-bad); }
.dsh-due-date.tone-orange { background: var(--warning-bg); border-color: rgba(217,161,24,0.3); }
.dsh-due-date.tone-orange b,
.dsh-due-date.tone-orange i { color: var(--dsh-warn); }
.dsh-due-body   { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.dsh-due-client { font-size: 13.5px; font-weight: 600; color: var(--text-1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dsh-due-meta   { font-size: 11.5px; color: var(--text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dsh-due-right  { text-align: right; flex-shrink: 0; display: flex; flex-direction: column; align-items: flex-end; gap: 5px; }
.dsh-due-amt    { font-family: 'JetBrains Mono', monospace; font-size: 13.5px; font-weight: 700; color: var(--text-1); }

/* ── Activity feed ───────────────────────────────────────────────────────── */
.dsh-act-list { padding: 6px 10px 10px; }
.dsh-act { display: flex; align-items: flex-start; gap: 12px; padding: 11px 10px; }
.dsh-act + .dsh-act { border-top: 1px solid var(--border); }
.dsh-act-ico {
  width: 32px; height: 32px; flex-shrink: 0; border-radius: 9px;
  display: inline-flex; align-items: center; justify-content: center;
}
.dsh-act-ico.tone-ok      { background: var(--success-bg); color: var(--success); }
.dsh-act-ico.tone-warn    { background: var(--danger-bg);  color: var(--danger); }
.dsh-act-ico.tone-neutral { background: var(--surface-3);  color: var(--text-2); }
.dsh-act-body { flex: 1; min-width: 0; }
.dsh-act-body b { display: block; font-size: 13px; font-weight: 700; color: var(--text-1); }
.dsh-act-body span { display: block; font-size: 12px; color: var(--text-2); line-height: 1.45; }
.dsh-act time { font-size: 11px; color: var(--text-2); white-space: nowrap; margin-top: 2px; }

/* ── Generic two-line list rows (domains · projects) ─────────────────────── */
.dsh-mini-list { padding: 6px 10px 10px; }
.dsh-row-item {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 11px 10px; border-radius: var(--r-md);
  color: inherit; text-decoration: none;
  transition: background var(--dur-fast);
}
.dsh-row-item + .dsh-row-item { border-top: 1px solid var(--border); border-radius: 0; }
.dsh-row-item:hover { background: var(--surface-2); }
.dsh-row-main  { min-width: 0; flex: 1; display: flex; flex-direction: column; }
.dsh-row-title { font-size: 13.5px; font-weight: 600; color: var(--text-1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dsh-row-sub   { font-size: 11.5px; color: var(--text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dsh-row-end   { text-align: right; flex-shrink: 0; display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
/* .proj-progress-bar is `flex:1` for its usual flex parent — inside a column it
   needs an explicit box instead. */
.dsh-row-bar { display: block; flex: none; width: 100%; max-width: 230px; margin-top: 7px; }

/* ── Entrances ───────────────────────────────────────────────────────────── */
@keyframes dshIn    { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes dshFade  { from { opacity: 0; } to { opacity: 1; } }
@keyframes dshDonut { from { opacity: 0; transform: scale(0.9); } to { opacity: 1; transform: none; } }

@media (hover: hover) and (pointer: fine) {
  .dsh-kpi:hover  { transform: translateY(-3px); border-color: var(--border-strong); box-shadow: var(--shadow-md); }
  .dsh-mini:hover { transform: translateY(-2px); }
  .dsh-due:hover  { transform: translateX(2px); }
}
.dsh-kpi:active { transform: translateY(-1px) scale(0.995); }

/* ── Touch targets ────────────────────────────────────────────────────────
   Gated on `pointer: coarse`, not on width: a 1024px tablet is touch-driven
   and needs the same 44px minimum that a phone does, while a 400px-wide
   desktop window does not — it would just get bloated rows. Only elements
   this redesign introduced are listed; the inherited ones are untouched.
   `.dsh-link` becomes a flex box so the 44px lands as a real hit area rather
   than a taller line-box that pushes the card header around. */
@media (pointer: coarse) {
  .dsh-donut-row { min-height: 44px; }
  .dsh-row-item  { min-height: 44px; }
  .dsh-link {
    display: inline-flex; align-items: center;
    min-height: 44px; padding: 0 4px; margin: -8px -4px;
  }
  /* The segmented switches (period · Chart/Table · Count/Value) are the most
     tapped things on the page and were ~26px tall. */
  #stat-period-toggle .tab,
  #rev-view-toggle .tab,
  #donut-mode-toggle .tab { min-height: 44px; padding-left: 16px; padding-right: 16px; }
  #stat-period-toggle, #rev-view-toggle, #donut-mode-toggle { min-height: 50px; }
  /* The shared `.btn` is 36px tall. Raising it globally would re-space every
     dense toolbar in the app, which is not this session's scope — so only the
     hero's two primary actions, which are new here, get the touch minimum. */
  .dsh-hero-actions .btn { min-height: 44px; }
}

/* ── Responsive ──────────────────────────────────────────────────────────── */
@media (max-width: 1280px) {
  .dsh-kpis  { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dsh-minis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dsh-row-wide { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 1100px) {
  /* Dividers only exist above this width, where the four cells demonstrably
     sit on one line beside the action buttons. Below it the band can wrap,
     and a wrapped row would open with a stray divider — so it becomes a grid
     separated by gaps instead. Structural, not a guess at the wrap point. */
  .dsh-meta {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(110px, max-content));
    gap: 14px 26px;
  }
  .dsh-meta > div { padding: 0; border-left: 0; }
}
@media (max-width: 900px) {
  .dsh-row-even { grid-template-columns: minmax(0, 1fr); }
  /* Below this the header can't hold the legend and the toggle on one line —
     the legend drops to its own row rather than disappearing. */
  .dsh-card-tools { width: 100%; justify-content: space-between; gap: 10px; }
  .dsh-legend { order: 2; width: 100%; gap: 12px; }
}
@media (max-width: 767px) {
  /* `.main-content` is a flex child of `.app-shell`, so its automatic minimum
     size is its content's min-content width — one horizontally-scrolling strip
     (the period tabs below) was enough to stretch it to 383px inside a 360px
     body. `body` is `overflow-x: hidden`, so the surplus wasn't scrollable,
     it was CLIPPED: the right edge of every card fell off a 360px handset.
     Letting the column shrink to the viewport is the fix; anything genuinely
     wider than the screen already scrolls inside its own container
     (.data-table-scroll, .rev-table-wrap, the tab strip). */
  .main-content { min-width: 0; }

  .dsh-hero { padding: 20px 18px; gap: 16px; margin-bottom: 18px; }
  .dsh-hero-actions { width: 100%; }
  /* The flex band wraps below ~1100px, and a wrapped row would start with a
     stray left divider. A 2×2 grid separated by gaps has no such seam. */
  .dsh-meta { display: grid; grid-template-columns: 1fr 1fr; gap: 14px 18px; width: 100%; margin-top: 16px; }
  .dsh-meta > div { padding: 0; border-left: 0; }
  .dsh-meta dd { font-size: 15px; }
  .dsh-hero-actions .btn { flex: 1; }
  /* The glow used to be an offset circle; it is now an `inset: 0` layer whose
     mask does the positioning, so nudging it out of the box only slid the
     blueprint grid off the corner it is supposed to resolve into. */

  .dsh-bar { gap: 10px; }
  .dsh-bar-title { font-size: 14px; }
  /* The four period tabs must stay reachable with a thumb without wrapping
     into two rows that push the KPIs down. `min-width: 0` is the load-bearing
     part: without it the nowrap track keeps its min-content width, the strip
     can't shrink below four tabs, and the whole page scrolls sideways on a
     360px handset instead of just this strip. */
  #stat-period-toggle {
    width: 100%; min-width: 0; max-width: 100%;
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
  }
  .dsh-bar-title { min-width: 0; }
  #stat-period-toggle::-webkit-scrollbar { display: none; }
  #stat-period-toggle .tab { flex: 1 0 auto; justify-content: center; }

  .dsh-kpis  { gap: 12px; }
  .dsh-minis { gap: 10px; margin-bottom: 20px; }
  .dsh-kpi   { min-height: 152px; padding: 15px 14px; }
  .dsh-kpi-top { margin-bottom: 10px; }
  .dsh-kpi-ico { width: 32px; height: 32px; }
  .dsh-kpi-ico .dsh-i { width: 16px; height: 16px; }
  .dsh-delta { font-size: 10px; padding: 2px 7px; }
  .dsh-spark { width: calc(100% + 28px); margin: auto -14px -15px; height: 34px; }

  .dsh-row { gap: 14px; margin-bottom: 18px; }
  .dsh-card-h { padding: 15px 16px 12px; }
  .dsh-chart-box { min-height: 268px; padding: 10px 4px 4px; }
  .rev-canvas { min-height: 252px; }
  .dsh-donut { width: 158px; height: 158px; }
  .dsh-donut-wrap { padding: 16px 0 2px; }
  /* The four columns still fit at 360px — only the trailing "n · %" needs to
     stop reserving desktop width, so the status name keeps its ellipsis room. */
  .dsh-donut-row { padding: 8px 8px; gap: 4px 8px; }
  .dsh-donut-cnt { min-width: 0; font-size: 11px; }
  .dsh-donut-amt { font-size: 12px; }
  .dsh-due-list, .dsh-act-list, .dsh-mini-list, .dsh-donut-rows { padding: 4px 6px 8px; }
}
@media (max-width: 420px) {
  /* Below this the two-up KPI grid starts truncating five-figure amounts. */
  .dsh-kpis, .dsh-minis { grid-template-columns: minmax(0, 1fr); }
  .dsh-kpi { min-height: 0; }
}

/* ══════════════════════════════════════════════════════════════════════════
   LIST PAGES INHERIT THE DASHBOARD'S LANGUAGE — 2026-08-19
   Invoices / Offers / Clients / Projects still render `.page-header`,
   `.stat-card` and `.data-table` — the components the dashboard used BEFORE
   this redesign, which is exactly why those pages started to look like a
   different product. Restyling the shared components here, rather than
   rewriting four more views, carries the Ledger Blueprint across every list
   page with NO markup change: their filters, tabs, bulk-select, inline status
   dropdowns, pagination and modals are not touched at all.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Page header: title, then a quiet meta line, then a hairline ─────────── */
.page-header {
  padding-bottom: 16px;
  border-bottom: 1px solid var(--border);
}
.page-header-left h1 { letter-spacing: -0.02em; }
.page-header-left p {
  font-size: 12.5px; color: var(--text-2); letter-spacing: 0.01em;
}
/* The counts and money in that line are data — set them like data. */
.page-header-left p { font-variant-numeric: tabular-nums; }

/* ── Stat cards: same anatomy as the dashboard's KPI cards ──────────────── */
.stat-card {
  padding: 18px;
  /* Drafted, not floating — the hairline replaces the drop shadow, matching
     .dsh-kpi so the two page types read as one system. */
  border-color: var(--border-strong);
  box-shadow: none;
}
.stat-card:hover { box-shadow: var(--shadow-sm); }
.stat-value {
  font-size: clamp(20px, 0.9vw + 14px, 26px);
  letter-spacing: -0.025em; line-height: 1.02;
  margin-bottom: 7px;
}
/* The kicker. Also lifts this label off `--text-3`, which failed contrast in
   BOTH themes (2.51 dark / 2.56 light) on every page that renders it. */
.stat-label {
  font-size: 10px; font-weight: 700;
  letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--text-2);
}
.stat-sub { color: var(--text-2); opacity: 1; font-size: 11px; }

/* ── Table shell + head ─────────────────────────────────────────────────── */
.table-wrap { border-color: var(--border-strong); }
.data-table thead th {
  font-size: 10px;
  letter-spacing: 0.16em;
  padding: 12px 14px;
  background: var(--surface-2);
  color: var(--text-2);
}
/* Row numbers were brand-teal, which fails contrast on a white surface; the
   per-theme link step passes in both. */
.td-num { color: var(--dsh-link); }

/* ── Toolbar ────────────────────────────────────────────────────────────── */
.table-toolbar { padding: 13px 16px; }

/* ══════════════════════════════════════════════════════════════════════════
   AURORA — 2026-08-21
   A finishing layer, not a rewrite. Every rule below re-dresses a component
   that already exists and keeps its class name, its markup and its JS hooks;
   nothing here changes a grid, a control's behaviour, or a document template.
   `pdf/*` was not opened.

   Three ideas carry the whole thing:

   · ONE ACCENT, TWO ROLES. Orchid is structure — fills, active states, the
     things you click. Apricot is light — blooms, sheens, the top edge of a
     card. They never compete for the same job, which is why two colours read
     as richer than one instead of noisier than one.
   · SURFACES ARE LIT, NOT DRAWN. Cards get a contact shadow *and* an ambient
     one, a hairline that is warm rather than black-at-8%, and a 1px inner
     highlight along the top edge. That inner line is the whole trick: it is
     what a real sheet of paper does under a light above it.
   · MOTION IS FEEDBACK. Nothing moves to be seen moving. Lifts are 1–3px,
     everything lands under 320ms, presses always settle under the finger, and
     the reduced-motion block at the very bottom of this file switches all of
     it off.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Ambient page field ───────────────────────────────────────────────────
   Two viewport-anchored radial washes and a fine grain. A perfectly flat
   background is the loudest "generated" tell there is; this removes it for
   the cost of a paint.

   Deliberately declared as extra `background` LAYERS on <body> rather than as
   a fixed pseudo-element. A `position: fixed` overlay would have had to be
   given a z-index, and every sibling of `.app-shell` — the mobile bottom nav
   (9999), the toast container (10001), the PWA banner (900) — would then have
   needed one too, flattening a stacking order that three separate sessions
   tuned so that modals clear the bottom nav and toasts clear modals. Layers
   can't reach the stacking context at all, so that whole class of bug is
   simply not available here. The grain's alpha is baked into the SVG for the
   same reason: a layer has no `opacity` of its own. */
body {
  background-color: var(--bg);
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.035'/%3E%3C/svg%3E"),
    radial-gradient(ellipse 70% 46% at 78% -6%,  rgba(251,186,126,0.26), transparent 62%),
    radial-gradient(ellipse 58% 42% at 4%  102%, rgba(145,76,157,0.10), transparent 66%);
  background-attachment: fixed, fixed, fixed;
  background-repeat: repeat, no-repeat, no-repeat;
}
[data-theme="dark"] body {
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.06'/%3E%3C/svg%3E"),
    radial-gradient(ellipse 70% 46% at 78% -6%,  rgba(169,104,180,0.20), transparent 62%),
    radial-gradient(ellipse 58% 42% at 4%  102%, rgba(251,186,126,0.07), transparent 66%);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) body {
    background-image:
      url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.06'/%3E%3C/svg%3E"),
      radial-gradient(ellipse 70% 46% at 78% -6%,  rgba(169,104,180,0.20), transparent 62%),
      radial-gradient(ellipse 58% 42% at 4%  102%, rgba(251,186,126,0.07), transparent 66%);
  }
}

/* ══ SIDEBAR — the one deliberately dark surface ══════════════════════════
   In BOTH themes. A rail of ink beside a page of paper is what separates
   "chrome" from "document" without a single divider, and it is the detail
   that makes the reference read as a product rather than a page. Because it
   is dark in light mode too, every token inside it is re-declared locally —
   the panel carries its own miniature theme rather than fighting the global
   one. */
.sidebar {
  --border:        rgba(255,255,255,0.07);
  --border-strong: rgba(255,255,255,0.13);
  --surface-2:     rgba(255,255,255,0.05);
  --surface-3:     rgba(255,255,255,0.08);
  --surface-4:     rgba(255,255,255,0.12);
  --text-1:        #F7F2F8;
  --text-2:        #B0A5B8;
  --text-3:        #837A8B;
  --text-brand:    var(--peach-400);

  background: linear-gradient(180deg, #1B1220 0%, #2A1B33 62%, #241729 100%);
  border-right: 1px solid rgba(255,255,255,0.06);
  color: var(--text-2);
}
/* Orchid bloom out of the top-left corner, apricot answering at the foot. */
.sidebar::before {
  content: '';
  position: absolute; inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 82% 34% at 18% 0%,   rgba(145,76,157,0.55), transparent 70%),
    radial-gradient(ellipse 70% 26% at 82% 100%, rgba(251,186,126,0.13), transparent 72%);
}
.sidebar > * { position: relative; z-index: 1; }

.sidebar-logo { border-bottom-color: rgba(255,255,255,0.07); min-height: 70px; }
.sidebar-logo-mark {
  border-radius: 11px;
  border-color: rgba(255,255,255,0.18);
  box-shadow: 0 4px 14px rgba(145,76,157,0.42), inset 0 1px 0 rgba(255,255,255,0.6);
}
.sidebar-logo-text strong { color: #FBF8FC; letter-spacing: -0.005em; font-size: 15px; }
/* 9.5px caps on the sidebar's mid-gradient: #8C8194 measured 4.36:1, just
   under. Two steps lighter clears it without brightening the label into
   competing with the wordmark above it. */
.sidebar-logo-text span {
  color: #A093A9; font-size: 9.5px; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase;
}

.sidebar-nav { padding: 12px 10px 16px; }
.nav-section-label {
  font-size: 9.5px; letter-spacing: 0.16em; font-weight: 700;
  /* Group headings are structure, not decoration — they were 2.88:1. */
  color: #A093A9; padding: 16px 10px 6px;
}
.sidebar-nav > .nav-section-label:first-child { padding-top: 4px; }

.nav-item {
  padding: 10px 11px;
  border-radius: 10px;
  border: 1px solid transparent;
  font-size: 13.5px;
  letter-spacing: -0.005em;
  transition: background var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease),
              transform var(--dur-fast) var(--ease);
}
.nav-item .nav-icon svg { opacity: 0.68; transition: opacity var(--dur-fast) var(--ease); }
.nav-item:hover { background: rgba(255,255,255,0.05); color: #EFE8F2; }
.nav-item:hover .nav-icon svg { opacity: 1; }

/* Active: an orchid card with an apricot icon and an inner top highlight.
   The old 3px left bar is gone — the fill already says "you are here", and
   the bar was competing with the border for the same edge. */
.nav-item.active {
  background: linear-gradient(180deg, rgba(145,76,157,0.42), rgba(145,76,157,0.26));
  color: #FBEEDF;
  border-color: rgba(251,186,126,0.28);
  box-shadow: 0 2px 10px rgba(145,76,157,0.28), inset 0 1px 0 rgba(255,255,255,0.09);
}
.nav-item.active::before { display: none; }
.nav-item.active .nav-icon svg { opacity: 1; color: var(--peach-400); stroke-width: 2; }

.nav-badge {
  background: rgba(255,255,255,0.11);
  color: #D5CCDB;
  font-size: 10px; font-weight: 700;
  min-width: 22px; text-align: center;
  padding: 2px 7px; border-radius: 100px;
  font-variant-numeric: tabular-nums;
}
.nav-item.active .nav-badge { background: rgba(251,186,126,0.24); color: #FCE3C6; }

.sidebar-footer {
  border-top-color: rgba(255,255,255,0.07);
  padding: 12px 10px 14px; min-height: 62px;
}
/* Squircle, not a circle — the reference's own avatar shape, and it stops the
   footer reading like a chat app. */
.user-avatar {
  border-radius: 11px;
  background: linear-gradient(140deg, var(--brand-500), #C06AA0 55%, var(--peach-400));
  box-shadow: 0 3px 10px rgba(145,76,157,0.45), inset 0 1px 0 rgba(255,255,255,0.25);
  font-family: 'Sora', sans-serif;
}
.user-name { color: #F3EDF5; }
.user-role { color: #A093A9; font-size: 10px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; }
.sidebar-footer .icon-btn {
  background: rgba(255,255,255,0.05);
  border-color: rgba(255,255,255,0.09);
  color: #A99FB1;
}
.sidebar-footer .icon-btn:hover {
  background: rgba(199,69,47,0.22); border-color: rgba(232,106,84,0.4); color: #FFC9BE;
}

/* The collapse handle straddles the seam, so it belongs to neither surface.
   It gets the page's own colours and a real shadow to sit above both. */
.sidebar-toggle {
  width: 26px; height: 26px;
  background: var(--surface-1);
  border-color: var(--border-strong);
  box-shadow: var(--shadow-md);
  top: 22px;
}
.sidebar-toggle:hover { background: var(--surface-1); border-color: var(--brand-500); }
.sidebar-toggle:hover svg { color: var(--text-brand); }

/* ══ TOP BAR ══════════════════════════════════════════════════════════════ */
.topbar { padding: 0 28px; }
.topbar::after {
  /* A hairline that fades out toward the right instead of stopping dead. */
  content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: linear-gradient(90deg, rgba(145,76,157,0.28), transparent 62%);
  pointer-events: none;
}
.topbar-breadcrumb .page-title {
  font-size: 17px; letter-spacing: -0.02em;
}
.icon-btn {
  border-radius: 10px;
  background: var(--surface-1);
  border-color: var(--border);
  box-shadow: var(--shadow-xs);
}
.icon-btn:hover {
  background: var(--surface-1);
  border-color: var(--border-strong);
  color: var(--text-1);
  box-shadow: var(--shadow-md);
}

/* ══ TYPOGRAPHY ═══════════════════════════════════════════════════════════ */
h1, .page-header-left h1 { letter-spacing: -0.03em; }
h2 { letter-spacing: -0.02em; }
/* Every figure in the app is a figure: same width per digit, always. */
.stat-value, .dsh-kpi-val, .dsh-mini-val, .td-money, .num, .mono,
.total-row .amt, .dsh-meta dd, .nav-badge, .tab-count {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}
/* Prose never runs past a comfortable measure. */
.page-header-left p, .card-header p, .empty-state p, .import-help { text-wrap: pretty; }
.empty-state p { max-width: 46ch; margin-left: auto; margin-right: auto; }

/* ══ BUTTONS ══════════════════════════════════════════════════════════════
   The primary button gets a vertical sheen and a coloured shadow — a flat
   fill plus a grey shadow is the default that makes every AI-built UI look
   the same. On hover it goes DARKER, not lighter, so white-on-orchid stays
   above 4.5:1 (5.64 → 7.60). */
.btn {
  border-radius: 10px;
  padding: 10px 18px;
  letter-spacing: -0.005em;
}
.btn-primary {
  background: linear-gradient(180deg, #9E5AAA, var(--brand-500) 55%, #8A4795);
  border-color: var(--brand-700);
  box-shadow: var(--shadow-brand), inset 0 1px 0 rgba(255,255,255,0.18);
}
.btn-primary:hover {
  background: linear-gradient(180deg, var(--brand-600), var(--brand-700));
  border-color: var(--brand-700);
  box-shadow: 0 8px 22px -6px rgba(145,76,157,0.55), inset 0 1px 0 rgba(255,255,255,0.16);
}
.btn-secondary {
  background: var(--surface-1);
  border-color: var(--border-strong);
  box-shadow: var(--shadow-xs);
}
.btn-secondary:hover { background: var(--surface-2); box-shadow: var(--shadow-md); }
.btn-outline { border-color: var(--brand-500); background: transparent; }
.btn-outline:hover { background: rgba(145,76,157,0.08); border-color: var(--brand-600); }
.btn-ghost:hover { background: var(--surface-3); }
.btn-sm { padding: 7px 13px; border-radius: 9px; }
.btn-lg { padding: 13px 24px; border-radius: 12px; }

/* ══ CARDS ════════════════════════════════════════════════════════════════ */
.card, .table-wrap, .dsh-card, .settings-nav, .totals-table {
  border-radius: var(--r-lg);
  border-color: var(--border);
  background: var(--surface-1);
  box-shadow: var(--shadow-sm);
}
/* The inner top highlight — a sheet catching the light from above. */
.card, .dsh-card, .table-wrap, .stat-card, .dsh-kpi, .modal, .login-card {
  position: relative;
}
/* `.stat-card` and `.dsh-kpi` are excluded on purpose: their top edge already
   belongs to the accent stripe, and a white inner line drawn over it flattens
   the colour to nothing. Those two cards are lit by the stripe instead. */
.card::after, .dsh-card::after, .table-wrap::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  pointer-events: none;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.55);
}
[data-theme="dark"] .card::after, [data-theme="dark"] .dsh-card::after,
[data-theme="dark"] .table-wrap::after { box-shadow: inset 0 1px 0 rgba(255,255,255,0.05); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .card::after,
  :root:not([data-theme="light"]) .dsh-card::after,
  :root:not([data-theme="light"]) .table-wrap::after { box-shadow: inset 0 1px 0 rgba(255,255,255,0.05); }
}
.card-header { padding: 17px 20px 15px; }
.card-header h3 {
  font-family: 'Sora', sans-serif; font-size: 12px; font-weight: 700;
  letter-spacing: 0.13em; text-transform: uppercase; color: var(--text-1);
}

/* ══ STAT / KPI CARDS ═════════════════════════════════════════════════════
   The accent stripe becomes a gradient that fades out to the right, so the
   four cards read as one instrument panel rather than four flags. */
.stat-card, .dsh-kpi {
  border-radius: var(--r-lg);
  border-color: var(--border);
  background: var(--surface-1);
  box-shadow: var(--shadow-sm);
}
.stat-card::before { height: 3px; border-radius: var(--r-lg) var(--r-lg) 0 0; }
.stat-card.brand::before   { background: linear-gradient(90deg, var(--brand-500), var(--peach-400) 92%); }
.stat-card.success::before { background: linear-gradient(90deg, var(--success), transparent 96%); }
.stat-card.warning::before { background: linear-gradient(90deg, var(--warning), transparent 96%); }
.stat-card.danger::before  { background: linear-gradient(90deg, var(--danger),  transparent 96%); }
.stat-card.violet::before  { background: linear-gradient(90deg, var(--violet),  transparent 96%); }
.stat-card.profit::before  { background: linear-gradient(90deg, var(--success), transparent 96%); }
.stat-card.partner::before { background: linear-gradient(90deg, var(--info),    transparent 96%); }
.dsh-kpi::before { height: 3px; }
.stat-icon, .dsh-kpi-ico, .dsh-mini-ico, .dsh-act-ico, .dsh-h-ico {
  border-radius: 11px;
  box-shadow: inset 0 0 0 1px rgba(145,76,157,0.06);
}
.stat-card.brand .stat-icon { box-shadow: inset 0 0 0 1px rgba(145,76,157,0.16); }
.stat-value, .dsh-kpi-val { font-family: 'Sora', sans-serif; letter-spacing: -0.035em; }

/* ══ DATA TABLES ══════════════════════════════════════════════════════════ */
.data-table thead th {
  background: var(--surface-2);
  color: var(--text-2);
  font-weight: 700;
  border-bottom: 1px solid var(--border-strong);
}
.data-table tbody tr { transition: background var(--dur-fast) var(--ease); }
.data-table tbody tr:hover { background: var(--surface-2); }
/* The row's key column warms up when you point at the row — a quiet promise
   that the whole row is the click target, not just the link inside it. */
@media (hover: hover) and (pointer: fine) {
  .data-table tbody tr:hover .td-num { color: var(--dsh-link-hover); }
}
.td-num { font-weight: 700; letter-spacing: -0.01em; }
.action-btn { border-radius: 9px; }
.action-btn:hover { background: var(--surface-2); border-color: var(--border-strong); box-shadow: var(--shadow-xs); }
.table-footer { background: var(--surface-2); }
.page-btn { border-radius: 9px; background: var(--surface-1); }
.page-btn.active {
  background: var(--brand-500);
  border-color: var(--brand-700);
  box-shadow: 0 2px 8px rgba(145,76,157,0.32);
}

/* ══ BADGES ═══════════════════════════════════════════════════════════════
   A ring, not just a tint. It is what stops a pale pill from dissolving into
   a white row, and it is the difference between the reference's badges and
   the generic ones. */
.badge {
  padding: 3px 10px 3px 8px;
  border: 1px solid transparent;
  letter-spacing: 0.01em;
}
.badge-draft   { background: var(--surface-3);   color: var(--text-2);  border-color: var(--border-strong); }
.badge-draft::before { background: var(--text-3); }
.badge-sent    { border-color: rgba(46,124,151,0.28); }
.badge-paid,
.badge-final   { border-color: rgba(47,143,91,0.28); }
.badge-overdue { border-color: rgba(199,69,47,0.30); }
.badge.exp-red     { border-color: rgba(199,69,47,0.30); }
.badge.exp-orange  { border-color: rgba(217,161,24,0.34); }
.badge.exp-green   { border-color: rgba(47,143,91,0.28); }
.badge.exp-none    { background: var(--surface-3); color: var(--text-2); border-color: var(--border-strong); }
.badge.exp-none::before { background: var(--text-3); }
.badge.exp-expired { box-shadow: 0 2px 8px rgba(199,69,47,0.35); border-color: transparent; }

/* ── The text inside every tinted chip ────────────────────────────────────
   Measured, not guessed: at 11px on a 10% tint the base semantic colours
   landed at 2.66–4.22:1 in light mode — `On Hold` was the worst pill in the
   app at 2.66. They all take the --on-* step now, which passes at 6:1+ and is
   the same hue, so nothing about the colour language changes. The DOT keeps
   the vivid base colour: a 5px disc is not text and reads better bright.
   In dark mode --on-* resolves back to the base, so this is a no-op there. */
.badge-paid, .badge-final,
.badge.exp-green,
.proj-status-green,
.proj-type-seo,
.exp-text-green,
.color-success           { color: var(--on-success); }
.badge-overdue,
.badge.exp-red,
.proj-status-red,
.exp-text-red, .exp-text-expired,
.color-danger            { color: var(--on-danger); }
.badge.exp-orange,
.proj-status-orange,
.proj-type-branding,
.exp-text-orange,
.color-warning           { color: var(--on-warning); }
.badge-sent,
.proj-status-info,
.proj-type-maintenance   { color: var(--on-info); }
.proj-status-violet,
.proj-type-logo,
.bonus-label,
.items-header.bonus-header { color: var(--on-violet); }
.proj-status-none,
.proj-type-website ~ .proj-status-none,
.proj-type-chip          { color: var(--text-2); }
.proj-type-website       { color: var(--text-brand); }
/* The dot inside a .badge is decorative — keep it vivid. */
.badge-paid::before, .badge-final::before, .badge.exp-green::before { background: var(--success); }
.badge-overdue::before, .badge.exp-red::before   { background: var(--danger); }
.badge.exp-orange::before                        { background: var(--warning); }
.badge-sent::before                              { background: var(--info); }
/* `exp-expired` is the one SOLID pill — white on the fill, so it needs the
   opposite treatment: a fixed deep red in BOTH themes. Dark mode's --danger is
   a bright coral (right for a 4px dot, wrong under white text: 3.17:1); this
   fill measures 5.52:1 and stays the most severe thing on the page. */
.badge.exp-expired {
  background: #B8402A;
  color: #fff;
  box-shadow: 0 2px 8px rgba(184,64,42,0.38);
}
.badge.exp-expired::before { background: #fff; }

/* The two ghost buttons that borrow a semantic colour as their text. */
.btn-danger { color: var(--on-danger); border-color: rgba(199,69,47,0.32); }

/* The last rules still setting real TEXT on the decorative step. Icons,
   placeholders and dots keep --text-3 — these six are read. */
.item-row-num,
.section-sub,
.topbar-breadcrumb .sep { color: var(--text-2); }
@media (max-width: 767px) {
  .bottom-nav-item,                       /* 10px tab labels                 */
  .data-table td::before,                 /* the stacked-card field labels   */
  .item-col-qty::before,
  .item-col-price::before,
  .item-col-total::before { color: var(--text-2) !important; }
}
/* Delta chips on the dashboard read from the same measured steps. */
.dsh-delta.is-good { color: var(--on-success); }
.dsh-delta.is-bad  { color: var(--on-danger); }

/* ══ TABS / SEGMENTED CONTROLS ════════════════════════════════════════════ */
.tab-group {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 3px;
}
.tab { border-radius: 9px; transition: all var(--dur-fast) var(--ease); }
.tab:hover:not(.active) { color: var(--text-1); }
.tab.active {
  background: var(--surface-1);
  box-shadow: var(--shadow-sm), inset 0 1px 0 rgba(255,255,255,0.6);
  color: var(--text-1);
}
[data-theme="dark"] .tab.active { box-shadow: var(--shadow-sm), inset 0 1px 0 rgba(255,255,255,0.06); }
.tab-count { background: var(--surface-3); border-radius: 100px; padding: 1px 6px; }
.tab.active .tab-count { background: rgba(145,76,157,0.14); }

/* Filter chips + slot tabs pick up the orchid when selected. */
.slot-tab.active, .lang-option.active, .company-card.active {
  border-color: var(--brand-500);
  background: rgba(145,76,157,0.07);
  box-shadow: 0 2px 10px -4px rgba(145,76,157,0.45);
}
.company-card.active .company-card-dot { background: var(--brand-500); border-color: var(--brand-500); }
.filter-select { border-radius: 9px; background: var(--surface-1); border-color: var(--border); }
.filter-select:hover { border-color: var(--brand-500); }
.filter-clear { border-radius: 9px; }

/* ══ FORMS ════════════════════════════════════════════════════════════════
   Fields sit on a tint at rest and go white on focus: the control lifts out
   of the card instead of just gaining an outline. */
.field label {
  font-size: 10px; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--text-2);
}
.input, .search-box input, .pin-box {
  border-radius: 10px;
  border-color: var(--border-strong);
  background: var(--surface-2);
  transition: border-color var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease),
              background var(--dur-fast) var(--ease);
}
.input:hover:not(:focus), .search-box input:hover:not(:focus) { border-color: var(--text-3); }
.input:focus, .search-box input:focus, .pin-box:focus {
  border-color: var(--brand-500);
  background: var(--surface-1);
  box-shadow: var(--shadow-glow);
}
.search-box input { border-radius: 100px; padding-left: 34px; }
.input-group .input-addon { background: var(--surface-3); border-color: var(--border-strong); }
.total-row.grand {
  background: linear-gradient(120deg, var(--brand-500), #A85BAF 52%, var(--brand-600));
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.16);
}
.total-row.grand .amt { font-family: 'Sora', sans-serif; letter-spacing: -0.03em; }
[data-add-main], [data-add-bonus], [data-add-offer-main], [data-add-offer-bonus] {
  border-color: var(--brand-500) !important;
}

/* ══ MODAL · TOAST · DROPDOWN ═════════════════════════════════════════════ */
.modal {
  border-radius: var(--r-xl);
  border-color: var(--border);
  box-shadow: var(--shadow-xl);
}
.modal::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  pointer-events: none; box-shadow: inset 0 1px 0 rgba(255,255,255,0.6);
}
[data-theme="dark"] .modal::after { box-shadow: inset 0 1px 0 rgba(255,255,255,0.07); }
.modal-overlay.open .modal { transition-timing-function: var(--ease-spring); }
.modal-header { padding: 22px 24px 16px; }
.modal-header h3 { font-family: 'Sora', sans-serif; letter-spacing: -0.015em; font-size: 17px; }
.modal-body   { padding: 20px 24px; }
.modal-footer { padding: 15px 24px; background: var(--surface-2); }
.modal-close   { border-radius: 9px; background: var(--surface-2); }
.modal-close:hover { background: var(--danger-bg); color: var(--danger); }
.toast { border-radius: var(--r-lg); background: var(--surface-1); box-shadow: var(--shadow-xl); }
.client-dropdown { border-radius: 12px; background: var(--surface-1); box-shadow: var(--shadow-lg); }
.client-option.focused, .client-option:hover { background: rgba(145,76,157,0.07); }

/* ══ SETTINGS NAV ═════════════════════════════════════════════════════════ */
.settings-nav-item { transition: all var(--dur-fast) var(--ease); }
.settings-nav-item.active {
  background: rgba(145,76,157,0.08);
  box-shadow: inset 3px 0 0 var(--brand-500);
}

/* ══ DASHBOARD ════════════════════════════════════════════════════════════ */
.dsh-hero { border-radius: var(--r-xl); }
.dsh-hero-eyebrow { color: var(--text-brand); letter-spacing: 0.2em; }
.dsh-card-h { padding: 18px 20px 15px; }
.dsh-kpi-lab, .dsh-mini-lab, .stat-label { color: var(--text-2); }
/* The donut's own proportion bars are deliberately NOT in this list: each one
   carries its status colour as an inline style, which is the whole point of
   the row — a gradient there would say "brand" where the ring says "overdue". */
.dsh-meter span, .proj-progress-bar span {
  background: linear-gradient(90deg, var(--brand-500), var(--peach-400));
}
.dsh-meter, .dsh-donut-bar, .proj-progress-bar { background: var(--surface-3); }
.dsh-donut-track { stroke: var(--surface-3); }
.dsh-donut-row:hover, .dsh-due:hover, .dsh-row-item:hover { background: rgba(145,76,157,0.06); }
.dsh-due-date { border-radius: 11px; box-shadow: var(--shadow-xs); }
.dsh-link { color: var(--dsh-link); }

/* ══ MOBILE BOTTOM NAV ════════════════════════════════════════════════════ */
@media (max-width: 767px) {
  .bottom-nav {
    background: var(--glass);
    backdrop-filter: blur(20px) saturate(1.6);
    -webkit-backdrop-filter: blur(20px) saturate(1.6);
    box-shadow: 0 -10px 30px -20px rgba(36,27,40,0.4);
  }
  .bottom-nav-item { font-size: 9.5px; letter-spacing: -0.01em; }
  .bottom-nav-item.active { color: var(--text-brand); }
  /* An apricot tick above the active tab — findable at a glance without
     needing to read the label at 9.5px. */
  .bottom-nav-item.active::before {
    content: ''; position: absolute; top: 2px;
    width: 18px; height: 3px; border-radius: 0 0 3px 3px;
    background: var(--peach-400);
  }
  .bottom-nav-item { position: relative; }
  .sidebar-toggle { display: none; }
}

/* ══ LOGIN ════════════════════════════════════════════════════════════════ */
/* Transparent, not `var(--bg)` — otherwise this full-height panel paints over
   the ambient field on <body> and the sign-in screen is the one flat page. */
.login-page { background: transparent; }
.login-page::before {
  width: 760px; height: 760px; top: -260px;
  background: radial-gradient(circle, rgba(145,76,157,0.20) 0%, rgba(251,186,126,0.10) 42%, transparent 72%);
}
.login-card {
  border-radius: var(--r-xl);
  border-color: var(--border);
  box-shadow: var(--shadow-xl);
}
.login-logo-mark {
  border-radius: var(--r-lg);
  box-shadow: 0 8px 26px rgba(145,76,157,0.32), inset 0 1px 0 rgba(255,255,255,0.7);
}
.login-logo h1 { letter-spacing: -0.03em; }

/* ══ PAGE ENTRANCE ════════════════════════════════════════════════════════
   The blocks of a page arrive in reading order, 40ms apart. `.modal-overlay`
   is excluded on purpose: a filled transform on an ancestor would make it the
   containing block for its own `position: fixed` panel — the exact bug
   documented at `.page-container` above. `backwards` (not `both`) so the
   transform is gone once the entrance ends and can never shadow a :hover. */
/* The dashboard's grids are skipped: their children already stagger
   themselves (`dshIn`), and animating the container as well would play the
   same movement twice, nested. */
.page-container > *:not(.modal-overlay):not(.dsh-kpis):not(.dsh-minis):not(.dsh-row) {
  animation: auroraRise 400ms var(--ease) backwards;
}
.page-container > *:nth-child(1) { animation-delay: 0ms; }
.page-container > *:nth-child(2) { animation-delay: 40ms; }
.page-container > *:nth-child(3) { animation-delay: 80ms; }
.page-container > *:nth-child(4) { animation-delay: 120ms; }
.page-container > *:nth-child(5) { animation-delay: 160ms; }
.page-container > *:nth-child(n+6) { animation-delay: 200ms; }
@keyframes auroraRise {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

/* ══ HOVER / PRESS ════════════════════════════════════════════════════════ */
@media (hover: hover) and (pointer: fine) {
  .card:hover      { box-shadow: var(--shadow-md); }
  .stat-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); border-color: var(--border-strong); }
  .dsh-kpi:hover   { transform: translateY(-3px); box-shadow: var(--shadow-lg); }
  .dsh-mini:hover  { transform: translateY(-2px); box-shadow: var(--shadow-md); border-color: var(--border-strong); }
  .btn-primary:hover { transform: translateY(-1px); }
  .nav-item:hover  { transform: none; }   /* the fill is the feedback here */
  .action-btn:hover, .page-btn:hover { transform: translateY(-1px); }
  .settings-nav-item:hover { transform: none; background: var(--surface-2); }
}
/* Presses re-affirmed last so they always win the cascade. */
.btn:active         { transform: scale(0.97); }
.icon-btn:active    { transform: scale(0.93); }
.action-btn:active  { transform: scale(0.9); }
.nav-item:active    { transform: scale(0.98); }
.stat-card:active,
.dsh-kpi:active     { transform: translateY(-1px) scale(0.995); }

/* ══ FOCUS ════════════════════════════════════════════════════════════════
   One ring, brand-coloured, offset so it never crowds the glyphs, and with a
   white halo underneath so it stays visible on a tinted surface too. */
:focus-visible {
  outline: 2px solid var(--brand-500);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}
.nav-item:focus-visible, .sidebar-footer .icon-btn:focus-visible {
  outline-color: var(--peach-400);
}
/* Keyboard users get a way past the nav on every page. */
.skip-link {
  position: absolute; left: 12px; top: -60px; z-index: 999;
  padding: 10px 16px; border-radius: 0 0 10px 10px;
  background: var(--brand-500); color: #fff;
  font-size: 13px; font-weight: 700; text-decoration: none;
  box-shadow: var(--shadow-lg);
  transition: top var(--dur-normal) var(--ease);
}
.skip-link:focus { top: 0; }

/* ══ THE LIST PAGES STOP PUSHING THE PAGE SIDEWAYS ════════════════════════
   Pre-existing, and not a small one: on a 768–1200px window every list page
   scrolled horizontally. `/invoices` measured 1200px of document inside a
   768px viewport, `/clients` 1332px. Verified against the pre-Aurora
   stylesheet — it behaves identically there, so this is an old bug being
   fixed, not a new one being papered over.

   Cause: `.main-content` is a flex child of `.app-shell`, so its automatic
   minimum size is its content's MIN-CONTENT width — and `.data-table` is a
   nine-column table that refuses to go below ~950px. The column could not
   shrink to the viewport, so it grew past it and dragged the page header, the
   toolbar and the sticky topbar's layout with it.

   The ≤767px block already carries `min-width: 0` for exactly this reason,
   with a comment explaining it. It was simply scoped too narrowly: a 900px
   tablet has the same problem as a 360px phone. Lifting it out of the media
   query is the whole fix, and `.data-table-scroll` then does the job its name
   promises — the table scrolls inside its own card while the page does not. */
.main-content { min-width: 0; }
.data-table-scroll { overflow-x: auto; }
@media (min-width: 768px) {
  /* Without a floor the table now shrinks INSTEAD of scrolling, and at ~900px
     every client name wrapped onto three lines — technically no overflow, but
     unreadable. A comfortable minimum makes it scroll like a table again. It
     is well under the container on any normal desktop, so nothing changes
     there. */
  .data-table { min-width: 940px; }
}
@media (max-width: 767px) {
  /* On phones the table is re-laid-out as stacked cards, so there is nothing
     left to scroll — the original `visible` stays correct there. */
  .data-table-scroll { overflow-x: visible; }
}

/* ══ ONE PRIMARY ACTION PER PAGE ══════════════════════════════════════════
   Invoices / Offers / Clients / Projects each render "New …" twice: once in
   the topbar and once in their own page header, ~60px apart and identical.
   Two identical primary buttons in one eyeful is not a choice, it is noise —
   so on a wide screen only the page header's survives.

   Which copy survives flips with the viewport, because which one is useful
   flips too:
     · Desktop — the page header's, sitting with the title and the meta line
       where the eye already is.
     · Phone — the TOPBAR's, because that bar is sticky: it is still on screen
       after the page header has scrolled away, which is exactly when you want
       to start a new document.
   Both are `display: none` on the `.btn` only. No markup, handler or href
   changes; the theme toggle beside the topbar copy is untouched; and on
   /clients the page header keeps Import and Export CSV in both cases —
   only its duplicated primary is dropped. */
@media (min-width: 768px) {
  .topbar-actions .btn { display: none; }
}
@media (max-width: 767px) {
  .page-header-right > .btn-primary { display: none; }

  /* A phone turns every table cell into a "LABEL … value" flex row, which
     works right up until a cell holds two lines — the client's business AND
     its owner, the amount AND its partner split. Both then land side by side
     on one line and collide. Letting the cell wrap and giving the second line
     a full row puts them back in the order they were written. */
  .data-table tbody td { flex-wrap: wrap; }
  .data-table tbody td > div:nth-child(2) { flex-basis: 100%; text-align: right; }
}

/* ══ TOUCH TARGETS, APP-WIDE ══════════════════════════════════════════════
   The 2026-08-19 pass added a `pointer: coarse` block but scoped it to the
   six controls that redesign introduced, and explicitly deferred the shared
   ones ("not this session's scope"). Measured at 390px, this is what was left:

     .status-select  85×21   ← the worst control in the app, and it is the one
                               that changes an invoice's STATUS from the list
     .tab            75×31     (the Draft/Sent/Paid/Overdue filters)
     .filter-select  91×32     (year / month)
     .search-box     102×34
     .icon-btn       36×36  ·  .action-btn 38×38  ·  .btn-sm 38 tall
     .modal-close    28×28  ·  .remove-row-btn 24×24

   All below the 44pt/48dp minimum in both HIG and Material.

   Gated on `pointer: coarse`, NOT on width — that is the distinction the
   previous session drew and it is the right one: a 1024px tablet is finger-
   driven and needs the minimum, a 400px-wide desktop window is not and would
   just get bloated rows. Desktop density is untouched. */
@media (pointer: coarse) {
  .icon-btn,
  .action-btn,
  .modal-close { min-width: 44px; min-height: 44px; }

  /* `!important` only because the ≤767px item-card rules pin this button to
     36×36 with `!important` of their own — specificity and order can't beat
     that, and on a phone it is the control that deletes a line item. */
  .remove-row-btn,
  .item-row > .remove-row-btn { min-width: 44px !important; min-height: 44px !important; width: 44px !important; height: 44px !important; }

  .btn, .btn-sm { min-height: 44px; }

  /* Segmented tabs and the two filter dropdowns. These are the primary filter
     controls on every list page, so they take the full 44 even though it makes
     the strip taller — density loses to hit rate on a finger. */
  /* min-WIDTH too: the "All" chip is only 42px wide on its own. */
  .tab { min-height: 44px; min-width: 44px; }
  .tab-group { padding: 4px; }
  .slot-tab { min-height: 44px; }          /* Settings → company slots */
  .filter-select,
  .search-box input,
  .input,
  input[type="file"] { min-height: 44px; }

  /* The invoice/offer line-item fields. The ≤767px block already rebuilds
     these as 46px cards, which left the TABLET case behind: at 768–1024 the
     desktop grid still applies and you would be tapping 30px-tall price and
     quantity inputs. */
  .item-input,
  .item-total-display,
  .item-col-qty .item-input,
  .item-col-price .item-input,
  .item-col-total .item-input,
  .item-col-total .item-total-display { min-height: 44px; }
  /* The two colour pickers in Settings sit inside a 36px swatch. */
  .color-swatch { width: 44px; height: 44px; }

  /* A pill that is also a real <select>. It cannot carry a pseudo-element hit
     area, so the pill itself grows — on a phone the status sits alone on its
     own row in the stacked-card layout, so there is room for it. */
  .status-select { min-height: 44px; padding-top: 0; padding-bottom: 0; }

  /* Bulk-select checkboxes were 16×16. 24×24 is WCAG 2.5.8 (AA) — and it is
     as far as CSS alone can take a native checkbox: reaching 44 means wrapping
     each one in a <label> that carries the hit area, which is a markup change
     to two list views and is deliberately not in this pass. Flagged, not
     silently rounded up. */
  .col-select input[type="checkbox"],
  .save-client-check input[type="checkbox"],
  .import-header-toggle input,
  input[type="checkbox"], input[type="radio"] { width: 24px; height: 24px; }
  .col-select { width: 44px; }
  /* Where a checkbox already sits inside a label, the LABEL is the hit area —
     so those genuinely reach 44 and only the bare ones are capped at 24. */
  .save-client-check, .import-header-toggle { min-height: 44px; }

  .nav-item { min-height: 44px; }
  .settings-nav-item { min-height: 44px; }
  .dash-list-item, .dsh-due, .client-option { min-height: 44px; }

  /* Both of these were already fixed for phones inside the ≤767px block, which
     leaves a touch TABLET — where the sidebar is expanded and the table is
     still a table — with a 26px collapse handle and 32px pager buttons.
     Scoped through `.pagination` so this rule out-specifies the base
     `.page-btn` no matter where either ends up in the file. */
  .sidebar-toggle { width: 44px; height: 44px; }
  .pagination .page-btn { min-width: 44px; height: 44px; }

  /* The revenue card's three new controls. The legend chip and the month pill
     are the only way to re-cut that chart on a phone, and the ✕ dismisses the
     breakdown panel — all three were ~22–31px tall at their design size. */
  .dsh-leg   { min-height: 44px; }
  .rev-pill  { min-height: 44px; min-width: 44px; }
  .rev-detail-x { width: 44px; height: 44px; }
}

/* ══ RESPONSIVE TRIMS ═════════════════════════════════════════════════════ */
@media (max-width: 767px) {
  .topbar { padding: 0 16px; }
  .topbar::after { background: linear-gradient(90deg, rgba(145,76,157,0.24), transparent 70%); }
  .sidebar { border-right: 0; box-shadow: var(--shadow-xl); }
  .btn { padding: 10px 16px; }
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}
