/* Smooth, native cross-page transitions for plain full-page navigations
   (this app is a classic server-rendered multi-page app, not a JS router) -
   supporting browsers fade the old page out and the new one in instead of
   the old hard white flash; unsupported browsers just ignore this line and
   navigate exactly as before, so it's a free upgrade with no fallback code
   needed. */
@view-transition {
  navigation: auto;
}

/* Respect the device's "reduce motion" accessibility setting: every
   animation/transition in this file collapses to effectively instant
   instead of being stripped out one-by-one, so this one rule keeps working
   even as new transitions are added later. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* =====================================================================
   COLOUR THEMES (2.2)
   ---------------------------------------------------------------------
   Five looks, each with its own mix of colours rather than one accent:

     light     "Mwanga"     clean white, emerald + teal, colourful icons
     dark      "Giza"       deep green-black, mint + cyan, colourful icons
     blue      "Zanzibar"   sea-mist page, deep-ocean sidebar, turquoise
                            and coral
     midnight  "Tanzanite"  the violet-blue of the stone, with magenta and
                            gold
     sand      "Serengeti"  savanna paper, a dusk sidebar, sunset orange
                            and acacia green

   The keys (light, dark, blue, midnight, sand) are the ones stored in the
   pos_theme cookie since 2.0, so nobody's choice is lost - only what they
   look like changed. views/layout.js keeps PAGE_BG / TITLEBAR_COLOR in
   step with --bg / the top of --sidebar-bg here.

   Every theme sets the same tokens:
     --a1..--a5        five accents: stat cards, icons, charts, the stripe
     --sa1..--sa5      the same five, tuned for the sidebar's background
     --grad-primary    the main button / active item / brand mark
     --pindo           the thin multi-colour stripe along the top of every
                       page - after the pindo, the patterned border of a
                       kanga
     --sidebar-*       the sidebar is its own surface (dark in Zanzibar,
                       Tanzanite and Serengeti)
   ===================================================================== */
@font-face {
  font-family: "Figtree";
  src: url("/css/fonts/figtree.woff2?v=1") format("woff2");
  font-weight: 300 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Bricolage Grotesque";
  src: url("/css/fonts/bricolage.woff2?v=1") format("woff2");
  font-weight: 500 800;
  font-style: normal;
  font-display: swap;
}

:root {
  color-scheme: light;
  --font-ui: "Figtree", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-display: "Bricolage Grotesque", "Figtree", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --radius-sm: 10px;
  --radius: 14px;
  --radius-lg: 18px;

  --bg: #f2f5f4;
  --surface: #ffffff;
  --surface-2: #f7f9f8;
  --border: #e2e8e5;
  --border-strong: #cfd8d4;
  --text: #13201b;
  --text-muted: #4b5852;
  --primary: #0a8560;
  --primary-dark: #076a4c;
  --primary-2: #0e7490;
  --primary-contrast: #ffffff;
  --grad-primary: #0a8560;
  --warn-bg: #fff6e6;
  --warn-border: #f5cf8e;
  --warn-text: #8a5300;
  --danger: #d03b2f;
  --danger-bg: #fdeceb;
  --ok-bg: #e3f5ee;
  --ok-text: #087552;
  --muted-bg: #eef2f0;
  --row-hover: #f5f9f7;
  --ring: rgba(10, 133, 96, 0.28);
  --shadow: 0 1px 2px rgba(15, 35, 28, 0.04), 0 2px 10px -3px rgba(15, 35, 28, 0.07);
  --shadow-lg: 0 18px 40px -12px rgba(15, 35, 28, 0.22);

  --a1: #059669;
  --a2: #0284c7;
  --a3: #7c3aed;
  --a4: #d97706;
  --a5: #e11d48;
  --sa1: var(--a1);
  --sa2: var(--a2);
  --sa3: var(--a3);
  --sa4: var(--a4);
  --sa5: var(--a5);
  --pindo: linear-gradient(90deg, #059669 0%, #0284c7 30%, #7c3aed 55%, #d97706 80%, #e11d48 100%);

  --sidebar-bg: #f2f5f4;
  --sidebar-glow: none;
  --sidebar-border: #e2e8e5;
  --sidebar-text: #13201b;
  --sidebar-text-muted: #46544e;
  --sidebar-hover-bg: #e7edea;
  --sidebar-active-bg: #d9efe5;
  --sidebar-active-text: #065f46;
  --nav-bar: #0a8560;
  --hero-bg: linear-gradient(150deg, #053d2f 0%, #0a8560 48%, #0891b2 100%);
}

/* "Giza" (Dark). Applied when the device prefers dark and no theme was
   picked (no data-theme), or when Dark was picked. The two blocks hold the
   same values - CSS cannot share one block between a media query and an
   attribute selector. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    color-scheme: dark;
    --bg: #0c1412;
    --surface: #131d1a;
    --surface-2: #182420;
    --border: #233430;
    --border-strong: #30443e;
    --text: #e6efeb;
    --text-muted: #a9b8b1;
    --primary: #34d399;
    --primary-dark: #6ee7b7;
    --primary-2: #22d3ee;
    --primary-contrast: #03140d;
    --grad-primary: #30b383;
    --warn-bg: #33280f;
    --warn-border: #6b5220;
    --warn-text: #f2c76b;
    --danger: #f87171;
    --danger-bg: #3a1a1a;
    --ok-bg: #12332a;
    --ok-text: #4fd8a8;
    --muted-bg: #1a2622;
    --row-hover: #17221f;
    --ring: rgba(52, 211, 153, 0.35);
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 4px 14px -4px rgba(0, 0, 0, 0.45);
    --shadow-lg: 0 20px 44px -12px rgba(0, 0, 0, 0.65);
    --a1: #6ee7b7;
    --a2: #7dd3fc;
    --a3: #c4b5fd;
    --a4: #fcd34d;
    --a5: #fda4af;
    --pindo: linear-gradient(90deg, #34d399 0%, #22d3ee 25%, #a78bfa 50%, #fbbf24 75%, #fb7185 100%);
    --sidebar-bg: #0c1412;
    --sidebar-border: #1f2e2a;
    --sidebar-text: #e6efeb;
    --sidebar-text-muted: #a3b2ab;
    --sidebar-hover-bg: #18241f;
    --sidebar-active-bg: rgba(52, 211, 153, 0.14);
    --sidebar-active-text: #a7f3d0;
    --nav-bar: #34d399;
    --hero-bg: linear-gradient(150deg, #04261e 0%, #0b5d4a 50%, #0e5f73 100%);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #0c1412;
  --surface: #131d1a;
  --surface-2: #182420;
  --border: #233430;
  --border-strong: #30443e;
  --text: #e6efeb;
  --text-muted: #a9b8b1;
  --primary: #34d399;
  --primary-dark: #6ee7b7;
  --primary-2: #22d3ee;
  --primary-contrast: #03140d;
  --grad-primary: #30b383;
  --warn-bg: #33280f;
  --warn-border: #6b5220;
  --warn-text: #f2c76b;
  --danger: #f87171;
  --danger-bg: #3a1a1a;
  --ok-bg: #12332a;
  --ok-text: #4fd8a8;
  --muted-bg: #1a2622;
  --row-hover: #17221f;
  --ring: rgba(52, 211, 153, 0.35);
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 4px 14px -4px rgba(0, 0, 0, 0.45);
  --shadow-lg: 0 20px 44px -12px rgba(0, 0, 0, 0.65);
  --a1: #6ee7b7;
  --a2: #7dd3fc;
  --a3: #c4b5fd;
  --a4: #fcd34d;
  --a5: #fda4af;
  --pindo: linear-gradient(90deg, #34d399 0%, #22d3ee 25%, #a78bfa 50%, #fbbf24 75%, #fb7185 100%);
  --sidebar-bg: #0c1412;
  --sidebar-border: #1f2e2a;
  --sidebar-text: #e6efeb;
  --sidebar-text-muted: #a3b2ab;
  --sidebar-hover-bg: #18241f;
  --sidebar-active-bg: rgba(52, 211, 153, 0.14);
  --sidebar-active-text: #a7f3d0;
  --nav-bar: #34d399;
  --hero-bg: linear-gradient(150deg, #04261e 0%, #0b5d4a 50%, #0e5f73 100%);
}

/* "Zanzibar" (key: blue). A light sea-mist page; the sidebar is the deep
   ocean going over to the lagoon, with turquoise, coral and sun-gold. */
:root[data-theme="blue"] {
  color-scheme: light;
  --bg: #edf5f7;
  --surface: #ffffff;
  --surface-2: #f5fafb;
  --border: #d5e5ea;
  --border-strong: #bcd3da;
  --text: #0c2230;
  --text-muted: #3f5866;
  --primary: #0a6fa8;
  --primary-dark: #075682;
  --primary-2: #0f766e;
  --primary-contrast: #ffffff;
  --grad-primary: #0a6fa8;
  --warn-bg: #fff5e3;
  --warn-border: #f3c97a;
  --warn-text: #8a5600;
  --danger: #c93b31;
  --danger-bg: #fdeceb;
  --ok-bg: #dff4f1;
  --ok-text: #0b7a70;
  --muted-bg: #e8f1f4;
  --row-hover: #f2f8fa;
  --ring: rgba(10, 111, 168, 0.28);
  --shadow: 0 1px 2px rgba(8, 42, 68, 0.05), 0 2px 10px -3px rgba(8, 42, 68, 0.09);
  --shadow-lg: 0 18px 40px -12px rgba(8, 42, 68, 0.25);
  --a1: #0d9488;
  --a2: #0369a1;
  --a3: #e2604f;
  --a4: #c47f10;
  --a5: #4f46e5;
  --sa1: #99f6e4;
  --sa2: #bae6fd;
  --sa3: #fecdd3;
  --sa4: #fde68a;
  --sa5: #c7d2fe;
  --pindo: linear-gradient(90deg, #0d9488 0%, #0284c7 30%, #f97362 65%, #f5a524 100%);
  --sidebar-bg: linear-gradient(180deg, #0b2d45 0%, #0c3a52 100%);
  --sidebar-glow: none;
  --sidebar-border: #0e3b52;
  --sidebar-text: #e8f6f8;
  --sidebar-text-muted: #c3dbe2;
  --sidebar-hover-bg: rgba(255, 255, 255, 0.08);
  --sidebar-active-bg: rgba(255, 255, 255, 0.14);
  --sidebar-active-text: #ffffff;
  --nav-bar: #5eead4;
  --hero-bg: linear-gradient(160deg, #082a44 0%, #0a5f7a 55%, #0d9488 100%);
}

/* "Tanzanite" (key: midnight). The stone's own violet-blue, deep and soft
   rather than near-black, lit with magenta, cyan and gold. */
:root[data-theme="midnight"] {
  color-scheme: dark;
  --bg: #110e1c;
  --surface: #191527;
  --surface-2: #1f1a31;
  --border: #2c2642;
  --border-strong: #3a3356;
  --text: #ece8f7;
  --text-muted: #b4adcc;
  --primary: #8b5cf6;
  --primary-dark: #c4b5fd;
  --primary-2: #2563eb;
  --primary-contrast: #ffffff;
  --grad-primary: #6d4fd8;
  --warn-bg: #2e2410;
  --warn-border: #6b5220;
  --warn-text: #f5cd72;
  --danger: #fb7185;
  --danger-bg: #3a1624;
  --ok-bg: #132c2a;
  --ok-text: #5eead4;
  --muted-bg: #221d36;
  --row-hover: #1e1930;
  --ring: rgba(139, 92, 246, 0.42);
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.45), 0 6px 18px -6px rgba(0, 0, 0, 0.55);
  --shadow-lg: 0 22px 48px -12px rgba(0, 0, 0, 0.7);
  --a1: #c4b5fd;
  --a2: #93c5fd;
  --a3: #f9a8d4;
  --a4: #fcd34d;
  --a5: #67e8f9;
  --pindo: linear-gradient(90deg, #7c3aed 0%, #3b82f6 30%, #22d3ee 55%, #f472b6 80%, #fbbf24 100%);
  --sidebar-bg: linear-gradient(180deg, #17122c 0%, #1a1534 100%);
  --sidebar-glow: none;
  --sidebar-border: #261f45;
  --sidebar-text: #ece8f7;
  --sidebar-text-muted: #bdb6d8;
  --sidebar-hover-bg: rgba(255, 255, 255, 0.06);
  --sidebar-active-bg: rgba(167, 139, 250, 0.16);
  --sidebar-active-text: #ffffff;
  --nav-bar: #a78bfa;
  --hero-bg: linear-gradient(160deg, #1b1145 0%, #4c1d95 55%, #1e3a8a 100%);
}

/* "Serengeti" (key: sand). Warm savanna paper; the sidebar is dusk over
   the plains with the sunset glowing at its foot - sunset orange, acacia
   green, gold and the violet of the evening sky. */
:root[data-theme="sand"] {
  color-scheme: light;
  --bg: #f4eee3;
  --surface: #fffcf6;
  --surface-2: #faf5ec;
  --border: #e6dac6;
  --border-strong: #d8c7ad;
  --text: #2a2017;
  --text-muted: #5f5145;
  --primary: #b8430f;
  --primary-dark: #933409;
  --primary-2: #9a6410;
  --primary-contrast: #ffffff;
  --grad-primary: #a8471c;
  --warn-bg: #fdf0d8;
  --warn-border: #e9c27a;
  --warn-text: #7c4f07;
  --danger: #b4232a;
  --danger-bg: #fbe9e6;
  --ok-bg: #e7efd9;
  --ok-text: #4b6b12;
  --muted-bg: #efe6d6;
  --row-hover: #faf4e9;
  --ring: rgba(184, 67, 15, 0.28);
  --shadow: 0 1px 2px rgba(60, 40, 15, 0.05), 0 2px 10px -3px rgba(60, 40, 15, 0.09);
  --shadow-lg: 0 18px 40px -12px rgba(60, 40, 15, 0.25);
  --a1: #4d7c0f;
  --a2: #c2410c;
  --a3: #a16207;
  --a4: #6d28d9;
  --a5: #0369a1;
  --sa1: #d9f99d;
  --sa2: #fed7aa;
  --sa3: #fef08a;
  --sa4: #ddd6fe;
  --sa5: #bae6fd;
  --pindo: linear-gradient(90deg, #4d7c0f 0%, #ca8a04 30%, #ea580c 60%, #9d174d 85%, #5b21b6 100%);
  --sidebar-bg: linear-gradient(180deg, #2a1d14 0%, #33241a 100%);
  --sidebar-glow: none;
  --sidebar-border: #3d2818;
  --sidebar-text: #fbf0e1;
  --sidebar-text-muted: #e3cdb2;
  --sidebar-hover-bg: rgba(255, 240, 220, 0.08);
  --sidebar-active-bg: rgba(255, 236, 214, 0.14);
  --sidebar-active-text: #ffffff;
  --nav-bar: #fdba74;
  --hero-bg: linear-gradient(160deg, #24180f 0%, #5a3214 55%, #9a3412 100%);
}

/* One accent per sidebar icon, the same meaning in every theme (stock is
   always the first accent, money the third...). "sell" sits on the solid
   gradient Sell button and stays the button's own text colour. */
.nav-icon[data-icon="dashboard"],
.nav-icon[data-icon="reports"],
.nav-icon[data-icon="summary"],
.nav-icon[data-icon="transfer"],
.nav-icon[data-icon="receipt"],
.nav-icon[data-icon="sync"] { color: var(--sa2); }
.nav-icon[data-icon="inventory"],
.nav-icon[data-icon="addstock"],
.nav-icon[data-icon="stockmgmt"],
.nav-icon[data-icon="till"] { color: var(--sa1); }
.nav-icon[data-icon="orders"],
.nav-icon[data-icon="accounting"],
.nav-icon[data-icon="users"],
.nav-icon[data-icon="assets"],
.nav-icon[data-icon="depreciation"] { color: var(--sa3); }
.nav-icon[data-icon="purchaseorder"],
.nav-icon[data-icon="approvals"],
.nav-icon[data-icon="branches"],
.nav-icon[data-icon="supplier"],
.nav-icon[data-icon="notes"] { color: var(--sa4); }
.nav-icon[data-icon="purchaserequest"],
.nav-icon[data-icon="stockincident"],
.nav-icon[data-icon="customer"] { color: var(--sa5); }
.nav-link.active:not(.nav-group-summary) .nav-icon,
.nav-link-primary .nav-icon { color: inherit; }

* { box-sizing: border-box; }
[hidden] { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: 15px;
  line-height: 1.45;
}

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

h1, h2, h3 { margin: 0 0 4px; line-height: 1.25; }
h1 { font-size: 1.5rem; }
h2 { font-size: 1.05rem; color: var(--text); }

.muted { color: var(--text-muted); }
.nowrap { white-space: nowrap; }

/* App shell: left sidebar + main column (modern POS layout). */
.app-shell { display: flex; min-height: 100vh; }
.app-shell.no-shell { display: block; min-height: auto; }
.app-shell.no-shell .app-main { display: block; }
.app-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.brand-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 8px;
  background: var(--primary);
  color: var(--primary-contrast);
  font-weight: 800;
  font-size: 0.85rem;
  flex-shrink: 0;
}
.brand-mark.large { width: 52px; height: 52px; border-radius: 14px; font-size: 1.3rem; margin: 0 auto 8px; }

/* Sidebar */
.sidebar {
  width: 232px;
  flex-shrink: 0;
  background: var(--sidebar-bg, var(--surface));
  border-right: 1px solid var(--sidebar-border, var(--border));
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  /* The aside itself never scrolls - only .sidebar-nav below does (see its
     own overflow-y). That keeps the brand row (and the collapse button
     pinned to it) and the bottom account block always in place, instead of
     scrolling away with the nav links whenever there are enough of them to
     overflow. */
  overflow: hidden;
  z-index: 40;
  transition: width 0.2s ease;
}
.sidebar-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 700;
  font-size: 1.02rem;
  /* Extra right padding reserves room for .sidebar-collapse-btn, which sits
     inside this same top-right corner of the sidebar - see its own comment. */
  padding: 16px 40px 12px 16px;
  color: var(--sidebar-text, var(--text));
  position: relative;
  flex-shrink: 0;
}
.sidebar-nav {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 6px 10px;
  /* min-height: 0 lets this flex child actually shrink below its content
     size so overflow-y: auto can kick in - without it a flex item defaults
     to never being smaller than its content and never scrolls. */
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  /* A tall sidebar (many nav items) can need to scroll, and the browser's
     own default scrollbar is a wide, high-contrast bar that reads like a
     second, heavier divider line sitting right next to the sidebar's own
     border - especially on Windows. A slim, low-contrast custom scrollbar
     keeps the sidebar/content boundary to just the 1px border above.

     The two standard properties that used to sit here have moved into the
     @supports block at the end of this file. In Chromium (so Edge, so the
     shop computer) a `scrollbar-color` on an element WINS over that
     element's ::-webkit-scrollbar rules and switches the browser back to
     its own drawing - which quietly disabled the hover state just below
     for everybody actually using the app. They now apply only where the
     ::-webkit- rules do not exist at all, which is Firefox. */
}
.sidebar-nav::-webkit-scrollbar { width: 6px; }
.sidebar-nav::-webkit-scrollbar-track { background: transparent; }
.sidebar-nav::-webkit-scrollbar-thumb {
  background: var(--sidebar-border, var(--border));
  border-radius: 999px;
}
.sidebar-nav::-webkit-scrollbar-thumb:hover { background: var(--sidebar-text-muted, var(--text-muted)); }
.nav-link {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 9px 12px;
  border-radius: 9px;
  color: var(--sidebar-text-muted, var(--text-muted));
  font-weight: 500;
  font-size: 0.9rem;
}
.nav-link:hover { background: var(--sidebar-hover-bg, var(--muted-bg)); text-decoration: none; color: var(--sidebar-text, var(--text)); }
.nav-link.active { background: var(--sidebar-active-bg, var(--ok-bg)); color: var(--sidebar-text, var(--primary-dark)); font-weight: 600; }
.nav-icon { display: inline-flex; width: 19px; height: 19px; flex-shrink: 0; }
.nav-icon svg { width: 100%; height: 100%; }
.nav-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* How many reports are waiting on the admin, right there in the sidebar
   (views/layout.js). Rendered only when the number is not zero, so an
   empty queue leaves no dot behind to be ignored - which is what makes a
   badge that IS there worth looking at. */
.nav-badge {
  margin-left: auto;
  min-width: 20px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--danger);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1.5;
  text-align: center;
  flex-shrink: 0;
}
.nav-link-primary {
  background: var(--primary);
  color: var(--primary-contrast);
  font-weight: 700;
  margin-bottom: 6px;
}
.nav-link-primary:hover { background: var(--primary-dark); color: var(--primary-contrast); }
.nav-link-primary.active { background: var(--primary-dark); color: var(--primary-contrast); }

.nav-group-summary { cursor: pointer; list-style: none; justify-content: space-between; }
.nav-group-summary::-webkit-details-marker { display: none; }
.nav-group-summary::after {
  content: '';
  width: 7px;
  height: 7px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  margin-left: auto;
  flex-shrink: 0;
  opacity: 0.6;
}
.nav-group[open] > .nav-group-summary::after { transform: rotate(45deg); }
.nav-group-items { display: flex; flex-direction: column; gap: 2px; padding: 2px 0 4px 16px; }
.nav-group-items .nav-link { font-size: 0.86rem; padding: 8px 12px; }

/* Collapse-to-icons toggle (desktop only - on a narrow/mobile screen the
   sidebar is already an off-canvas drawer via .sidebar-toggle-btn/#sidebar,
   see the max-width:900px block below, so a second "collapse" affordance
   there would just be confusing). A small round handle pinned near the
   sidebar's edge, not inside .sidebar-brand, so it never shifts around as
   the brand name/nav labels show or hide.
   Kept fully INSIDE the sidebar's own box (not hanging half outside it) -
   .sidebar has overflow-x: hidden (needed elsewhere, to stop nav-label
   text creating a horizontal scrollbar during the collapse animation), and
   an earlier version of this button used a negative "right" offset to hang
   half off the sidebar's edge, which that overflow silently clipped -
   the button was never fully clickable/hoverable as a result. */
.sidebar-collapse-btn {
  display: none;
  position: absolute;
  top: 14px;
  right: 8px;
  width: 24px;
  height: 24px;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  border: 1px solid var(--sidebar-border, var(--border));
  background: var(--surface);
  color: var(--text-muted);
  cursor: pointer;
  z-index: 41;
  box-shadow: var(--shadow);
  /* Subtle by default so it doesn't sit there as a permanent distraction -
     comes to full strength on hover/focus/touch so it's still easy to find
     and use, just not visually shouting the rest of the time. */
  opacity: 0.4;
  transition: opacity 0.15s ease, color 0.15s ease;
}
.sidebar-collapse-btn:hover,
.sidebar-collapse-btn:focus-visible { color: var(--text); opacity: 1; }
.sidebar-collapse-btn svg { width: 13px; height: 13px; transition: transform 0.2s ease; }
@media (min-width: 901px) { .sidebar-collapse-btn { display: inline-flex; } }
/* Always fully visible on a touch device - there's no hover state to
   reveal it there, so fading it out by default would just hide it. */
@media (hover: none) { .sidebar-collapse-btn { opacity: 1; } }

/* html.sb-collapsed is toggled by public/js/app.js and set as early as
   possible (an inline script right at the top of <body>, see
   views/layout.js) so the collapsed width is already correct before the
   sidebar ever paints - no visible flash from wide back to narrow. */
html.sb-collapsed .sidebar { width: 64px; }
html.sb-collapsed .sidebar-collapse-btn svg { transform: rotate(180deg); }
html.sb-collapsed .brand-name,
html.sb-collapsed .nav-label,
html.sb-collapsed .settings-toggle-info,
html.sb-collapsed .nav-group-summary::after { display: none; }
html.sb-collapsed .sidebar-brand { justify-content: center; padding-left: 6px; padding-right: 6px; }
html.sb-collapsed .nav-link,
html.sb-collapsed .settings-toggle { justify-content: center; padding-left: 9px; padding-right: 9px; gap: 0; }
html.sb-collapsed .nav-group-items { padding-left: 0; align-items: center; }
html.sb-collapsed .nav-group-summary { justify-content: center; }

.sidebar-bottom { padding: 10px; border-top: 1px solid var(--sidebar-border, var(--border)); position: relative; flex-shrink: 0; }
/* Small credit line pinned to the very bottom-left of the sidebar, under
   the settings/account block - hidden entirely when the sidebar is
   collapsed to icons-only since there's no room for text there. */
.sidebar-credit {
  padding: 6px 14px 10px;
  font-size: 0.7rem;
  color: var(--sidebar-text-muted, var(--text-muted));
  opacity: 0.6;
  white-space: nowrap;
}
html.sb-collapsed .sidebar-credit { display: none; }
.settings-toggle {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 10px;
  padding: 7px 8px;
  cursor: pointer;
  color: var(--text);
  font: inherit;
  text-align: left;
}
.settings-toggle:hover { background: var(--sidebar-hover-bg, var(--muted-bg)); }
.settings-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 999px;
  background: var(--ok-bg);
  color: var(--primary-dark);
  font-weight: 700;
  font-size: 0.8rem;
  flex-shrink: 0;
}
.settings-toggle-info { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.25; }
.settings-toggle-name { font-weight: 600; font-size: 0.86rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.settings-toggle-role { font-size: 0.74rem; color: var(--text-muted); }
.settings-gear { font-size: 1rem; color: var(--text-muted); flex-shrink: 0; }

.settings-dropdown {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 10px;
  right: 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 10px 30px rgba(0,0,0,0.18);
  padding: 8px;
  z-index: 45;
  display: flex;
  flex-direction: column;
  /* Same openModal()/closeModal()-driven pattern as .modal-backdrop above -
     scales/fades up from the button it hangs off of. */
  opacity: 0;
  transform: translateY(6px) scale(0.98);
  transform-origin: bottom left;
  transition: opacity 0.15s ease, transform 0.15s ease;
}
.settings-dropdown.is-open {
  opacity: 1;
  transform: translateY(0) scale(1);
}
.settings-group-label { padding: 6px 10px 2px; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-muted); font-weight: 600; }
.settings-item {
  display: block;
  padding: 8px 10px;
  border-radius: 8px;
  color: var(--text);
  font-size: 0.9rem;
  font-weight: 500;
}
.settings-item:hover { background: var(--muted-bg); text-decoration: none; }
.settings-item-active { background: var(--ok-bg); color: var(--primary-dark); }
.settings-item-danger { color: var(--danger); }
.settings-divider { height: 1px; background: var(--border); margin: 6px 2px; }

.sidebar-backdrop { display: none; }

/* Top bar: page title + mobile menu button */
.topbar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 20px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: env(safe-area-inset-top, 0px);
  z-index: 20;
}
.topbar-title { font-weight: 700; font-size: 1rem; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Back (views/layout.js, public/js/nav.js): on every page but the
   dashboard - an installed app window has no browser Back button. */
.topbar-back {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 34px;
  padding: 0 12px 0 7px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-size: 0.85rem;
  font-weight: 600;
  text-decoration: none;
  flex-shrink: 0;
}
.topbar-back:hover { background: var(--muted-bg); text-decoration: none; }
.topbar-back svg { width: 18px; height: 18px; }
@media (max-width: 600px) {
  .topbar-back { width: 34px; padding: 0; justify-content: center; }
  .topbar-back span { display: none; }
}
@media print { .topbar-back { display: none !important; } }
.topbar-user {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-muted);
  background: var(--muted-bg);
  padding: 5px 12px;
  border-radius: 999px;
  white-space: nowrap;
}
.topbar-bell {
  position: relative;
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 999px;
  color: var(--text-muted);
  flex-shrink: 0;
}
.topbar-bell:hover, .topbar-bell.active { background: var(--muted-bg); color: var(--text); text-decoration: none; }
.topbar-bell .nav-icon { width: 19px; height: 19px; }
.topbar-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 999px;
  color: var(--text-muted);
  flex-shrink: 0;
}
.topbar-icon-btn:hover { background: var(--muted-bg); color: var(--text); text-decoration: none; }
.topbar-icon-btn .nav-icon { width: 18px; height: 18px; }
.topbar-branch-label {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-muted);
  white-space: nowrap;
}
.topbar-branch-select {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text);
  background: var(--muted-bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 5px 10px;
  max-width: 140px;
}
.row-current { background: var(--muted-bg); }
.topbar-bell-badge {
  position: absolute;
  top: 2px;
  right: 2px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--danger);
  color: #fff;
  font-size: 0.65rem;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
}
.sidebar-toggle-btn {
  display: none;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  flex-shrink: 0;
}
.sidebar-toggle-btn svg { width: 18px; height: 18px; }

@media (max-width: 900px) {
  .sidebar-toggle-btn { display: inline-flex; }
  .sidebar {
    position: fixed;
    left: 0;
    top: 0;
    transform: translateX(-100%);
    transition: transform 0.2s ease;
    box-shadow: 0 0 0 rgba(0,0,0,0);
  }
  .sidebar.open { transform: translateX(0); box-shadow: 10px 0 30px rgba(0,0,0,0.15); }
  /* Stays in the layout (display:block) the whole time at this width and
     fades via opacity instead of popping in/out with display:none - so it
     can actually transition instead of jumping. pointer-events keeps it
     from blocking taps while invisible/closed. */
  .sidebar-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(10, 16, 13, 0.45);
    z-index: 39;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s ease;
  }
  .sidebar-backdrop.open {
    opacity: 1;
    pointer-events: auto;
  }
}

.page { padding: 24px; max-width: 1180px; margin: 0 auto; width: 100%; }
.page.no-nav { max-width: 420px; margin: 8vh auto 0; padding: 16px; }

.page-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}
.page-head.compact { margin-bottom: 12px; }
.page-head-actions { display: flex; gap: 8px; flex-wrap: wrap; }
/* A .panel's own title row, for the rare panel (Dashboard's Recent
   Updates) that needs a small action next to its <h2> - same
   space-between idea as .page-head, just scaled to sit inside a panel. */
.panel-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 8px; }
.panel-head h2 { margin: 0; }
.panel-head-action { margin: 0; }
/* Inventory's small "Inactive (N)" / "Back to active" link, sat at the far
   end of .page-head next to the title (see views/inventory/list.js) -
   replaces what used to be a 4-option status dropdown. */
.inv-status-toggle { font-size: 0.85rem; font-weight: 600; color: var(--primary-dark); white-space: nowrap; }
/* Inventory's filter row: the branch picker comes first (the bigger-picture
   choice), then a deliberately narrow search box - not the full-width
   input every other list page uses - since narrowing by name is a quick,
   secondary refinement here, not the main way this page is used. */
/* Add Stock (views/inventory/receive.js): a tick box on its own line,
   reading as a sentence rather than a stray control. */
.check-line { display: flex; align-items: center; gap: 8px; font-size: 0.9rem; cursor: pointer; }
.check-line input { width: 17px; height: 17px; flex-shrink: 0; }
/* The eleven catalogue fields, folded away - a delivery almost never
   changes what the product IS, and they used to have to be scrolled past
   every single time. */
.receive-details { border: 1px solid var(--border); border-radius: 10px; }
.receive-details > summary {
  padding: 11px 14px;
  cursor: pointer;
  font-weight: 600;
  font-size: 0.92rem;
  color: var(--text-muted);
}
.receive-details[open] > summary { border-bottom: 1px solid var(--border); color: var(--text); }
/* Scoped to [open] on purpose. A bare `.receive-details-body { display:
   flex }` is an AUTHOR rule, and it beat the browser's own `display: none`
   for the contents of a closed <details> - so the section rendered fully
   expanded while its arrow still said closed. */
.receive-details[open] > .receive-details-body { padding: 14px; display: flex; flex-direction: column; gap: 14px; }
/* What the typed quantity actually comes to in base units, shown live
   (public/js/app.js). Receiving is normally counted in whole boxes, so a
   "12" in that field is 288 tablets - the figure that lands in stock, and
   the one worth seeing before saving rather than after. */
.receive-qty-preview { margin: -6px 2px 0; font-size: 0.82rem; font-weight: 600; color: var(--primary-dark); }
/* Quantity received (2.3.2): a short number box with the unit beside it,
   and "1 Box = [ 10 ] Capsule" after that (views/inventory/receive.js). */
.receive-qty-field { display: flex; flex-direction: column; gap: 6px; }
.receive-qty-title { display: block; }
.receive-qty-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.receive-qty-row .input { min-height: 44px; font-weight: 400; }
/* Two classes deep: the generic .input (width: 100%) comes later in this file. */
.receive-qty-row .receive-qty-input { width: 7.5em; flex: 0 0 auto; }
.receive-qty-row .receive-unit-select { width: 10.5em; flex: 0 0 auto; }
.receive-qty-row .receive-unit-other { width: 11em; flex: 0 0 auto; }
.receive-qty-row .receive-unit-other[hidden] { display: none; }
.receive-unit-size { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; font-size: 0.92rem; color: var(--text-muted); }
.receive-unit-size[hidden] { display: none; }
.receive-unit-size-name, .receive-unit-size-base { font-weight: 600; color: var(--text); }
.receive-qty-row .receive-unit-size-input { width: 6em; }
.receive-unit-size-hint { margin: 0; }
.receive-unit-size-hint[hidden] { display: none; }
/* Purchase order: which product is this new line? (2.3.7,
   views/purchase-orders/confirm-product.js) */
.confirm-product-search { display: flex; gap: 8px; }
.confirm-product-search .input { flex: 1 1 auto; min-width: 0; }
.confirm-product-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.confirm-product-pick { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px; }
.confirm-product-name { display: flex; flex-direction: column; gap: 2px; font-weight: 600; }
.confirm-product-name .cell-sub { font-weight: 400; }
.confirm-product-name .cell-code { align-self: flex-start; }
/* Reduce stock added by mistake (2.3.6, views/inventory/receipts.js). */
.reduce-form { display: flex; flex-direction: column; gap: 6px; min-width: 220px; }
.reduce-form-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.reduce-form .reduce-qty { width: 6.5em; flex: 0 0 auto; }
.reduce-form .reduce-unit { width: auto; flex: 1 1 7em; min-width: 0; }
.reduce-form .reduce-reason { width: 100%; }
.reduce-form .flash { margin: 0 0 4px; }
/* Prices (2.3.5): two columns - buying | selling - with the price of one
   Packet on top and of one Tablet under it, then the reorder threshold. */
.receive-price-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.receive-price-grid [hidden] { display: none; }
.receive-price-warn { margin: 0; }
.receive-price-warn[hidden] { display: none; }
/* Prices (2.3.3): "the price on record is for: (o) 1 Packet ( ) 1 Tablet"
   when a size is given for the first time, and the delivery's total. */
.receive-price-basis { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 16px; padding: 10px 12px; border: 1px dashed var(--border-strong, var(--border)); border-radius: 10px; font-size: 0.9rem; }
.receive-price-basis[hidden] { display: none; }
.receive-price-basis > span { font-weight: 600; }
.receive-price-basis .check-line input { width: 16px; height: 16px; }
.receive-price-total { margin: -4px 2px 0; font-weight: 600; color: var(--text); }
.receive-price-total[hidden] { display: none; }
.receive-last-batch { margin-bottom: 4px; }

.inv-filters { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-bottom: 14px; }
.inv-search-input { max-width: 240px; }
/* "Low stock only" - the reorder question, asked in one click. */
.inv-low-toggle { display: inline-flex; align-items: center; gap: 6px; font-size: 0.9rem; cursor: pointer; white-space: nowrap; }
.inv-low-toggle input { width: 17px; height: 17px; }
/* Sortable column headings (2026-09). The whole heading is the link, so
   the target is the full cell rather than a few characters of text. */
.th-sort { padding: 0; }
.th-sort > a {
  display: block;
  padding: 10px 12px;
  color: inherit;
  text-decoration: none;
  white-space: nowrap;
}
.th-sort.num > a { text-align: right; }
.th-sort > a:hover { color: var(--primary-dark); }
.th-sort-active > a { color: var(--primary-dark); font-weight: 700; }

/* Performance panel (admin dashboard) */
.perf-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 14px; margin-bottom: 16px; }
.perf-item { display: flex; flex-direction: column; gap: 4px; padding: 12px; border-radius: var(--radius); background: var(--muted-bg); }
.perf-label { font-size: 0.78rem; color: var(--text-muted); font-weight: 600; text-transform: uppercase; letter-spacing: 0.02em; }
.perf-value { font-size: 1.25rem; font-weight: 700; }
.trend { display: inline-flex; align-items: center; gap: 4px; font-size: 0.82rem; font-weight: 600; width: fit-content; }
.trend-up { color: var(--ok-text); }
.trend-down { color: var(--danger); }
.trend-flat { color: var(--text-muted); }
.perf-top-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 14px; }
.perf-top-item h3 { margin: 0 0 4px; font-size: 0.85rem; color: var(--text-muted); }
.perf-top-item p { margin: 0; font-weight: 600; }

/* Buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 11px 18px;
  min-height: 42px;
  border-radius: 8px;
  font-weight: 600;
  font-size: 0.92rem;
  border: 1px solid transparent;
  cursor: pointer;
  text-decoration: none;
}
.btn:hover { text-decoration: none; }
.btn-primary { background: var(--primary); color: var(--primary-contrast); }
.btn-primary:hover { background: var(--primary-dark); }
.btn-primary:disabled { background: var(--muted-bg); color: var(--text-muted); cursor: not-allowed; }
.btn-secondary { background: var(--surface); color: var(--text); border-color: var(--border); }
.btn-secondary:hover { background: var(--muted-bg); }
.btn-danger { background: var(--danger); color: #fff; }
.btn-danger:hover { filter: brightness(0.92); }
.btn-block { width: 100%; }

.link-btn {
  background: none;
  border: none;
  padding: 6px 2px;
  margin: -6px -2px;
  color: var(--primary);
  cursor: pointer;
  font: inherit;
  text-decoration: none;
}
.link-btn:hover { text-decoration: underline; }
/* For a destructive link-style action (e.g. permanently delete a user) -
   same plain/underline-on-hover treatment as .link-btn, just colored to
   read as dangerous instead of a neutral/primary action. */
.link-btn-danger { color: var(--danger); }
.row-muted { opacity: 0.55; }
.inline-form { display: inline; }

/* Forms */
.input {
  width: 100%;
  padding: 10px 12px;
  min-height: 42px;
  border-radius: 8px;
  border: 1px solid var(--border);
  font-size: 0.95rem;
  background: var(--surface);
  color: var(--text);
}
.input:focus, input:focus, select:focus { outline: 2px solid var(--primary); outline-offset: 1px; }
.stack { display: flex; flex-direction: column; gap: 14px; }
label { display: flex; flex-direction: column; gap: 5px; font-size: 0.88rem; font-weight: 600; color: var(--text-muted); }
label input, label select, label textarea { font-weight: 400; color: var(--text); background: var(--surface); padding: 10px 12px; min-height: 42px; border-radius: 8px; border: 1px solid var(--border); font-size: 0.95rem; font-family: inherit; resize: vertical; }
label textarea { min-height: 72px; }
/* Bare inputs used outside a <label> wrapper (inline table-row forms like
   the purchase-order qty/batch/date fields) need the same fix. */
input, select, textarea { background: var(--surface); color: var(--text); }
.checkbox-label { flex-direction: row; align-items: center; gap: 6px; font-weight: 500; }
.form-row { display: flex; gap: 14px; flex-wrap: wrap; }
/* Section headings inside a combined form (e.g. the Add Stock page, which
   shows product-detail fields and batch/expiry fields together in one
   <form> - views/inventory/receive.js) so the two groups read as visually
   distinct sections rather than one long unbroken list of fields. */
.form-section-title { margin: 4px 0 -4px; padding-top: 14px; border-top: 1px solid var(--border); font-size: 0.95rem; font-weight: 700; color: var(--text); }
.form-section-title:first-child { padding-top: 0; border-top: none; }
.stack-tight { display: flex; flex-direction: column; gap: 8px; }
.duty-picker-label { font-size: 0.88rem; font-weight: 600; color: var(--text-muted); }
/* The Staff table's role cell (see views/users.js) is a <details> that
   expands into checkboxes/branch-select for editing duties - styled as a
   clickable, pill-shaped "chip" with a dropdown-style caret rather than
   plain colored text, so it visibly invites a tap and gives a large-enough
   touch target on a tablet, not just a mouse. */
.duty-editor summary {
  cursor: pointer;
  color: var(--primary-dark);
  font-weight: 600;
  font-size: 0.85rem;
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  min-height: 32px;
  max-width: 100%;
  border-radius: 8px;
  background: var(--ok-bg);
  border: 1px solid transparent;
}
.duty-editor summary::-webkit-details-marker { display: none; }
.duty-editor summary::after {
  content: '';
  flex-shrink: 0;
  width: 6px;
  height: 6px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  opacity: 0.7;
}
.duty-editor summary:hover { background: var(--muted-bg); }
.duty-editor[open] summary { border-color: var(--primary); }
.duty-form { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; padding: 10px; border-radius: 8px; background: var(--muted-bg); }
.duty-form .checkbox-label { font-weight: 400; font-size: 0.88rem; }
.add-stock-form { margin-top: 6px; }
.add-stock-form .checkbox-label { font-weight: 400; font-size: 0.86rem; color: var(--text); }
.add-stock-form input[type="checkbox"] { width: 16px; height: 16px; }
.po-receive-locked { font-size: 0.84rem; }
.form-row label { flex: 1; min-width: 160px; }
.form-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 6px; }
.hint { font-size: 0.85rem; color: var(--text-muted); margin: -4px 0 2px; }
.hint-danger { color: var(--danger); font-weight: 600; }
.reset-summary-list { margin: 8px 0 0; padding-left: 18px; }
.reset-summary-list li { margin: 2px 0; }
.si-qty-row { display: flex; gap: 14px; }
.si-qty-row label { flex: 1; min-width: 0; }
@media (max-width: 480px) { .si-qty-row { flex-direction: column; gap: 6px; } }

/* Panels & tables */
.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px;
  box-shadow: var(--shadow);
  margin-bottom: 20px;
  overflow-x: auto;
  /* NOTE: setting overflow-x alone makes the browser silently compute
     overflow-y as "auto" too, by spec - writing "overflow-y: visible"
     here would NOT undo that (the spec forces it back to "auto" as long
     as overflow-x isn't "visible" either), so this comment documents the
     gotcha instead of pretending to fix it here. The actual fix for the
     bug this caused - a thin scrollbar on the shorter side of a .grid-2
     row (e.g. Notes) once the taller side's content made the grid stretch
     both panels to match its height - is .grid-2's own align-items: start
     below, which stops that forced equal-height stretch in the first
     place, so there's nothing for this quirky auto-y to ever act on. */
}
/* The "add/create" form panel used to get its own accent top border +
   deeper shadow to stand out from the list panel next to it - removed
   per the owner's feedback (2026-09): sitting side by side, one plain and
   one decorated looked inconsistent/unfinished rather than intentional.
   Both panels now share the exact same plain .panel look; the form still
   reads as its own thing because of its heading and narrower width, not
   an extra decoration. */
.form-panel {
  max-width: 640px;
}
/* align-items: start (not the grid default "stretch") so neither side is
   forced to match the other's height - a short form next to a long list
   (e.g. Notes) just sizes to its own content instead of being stretched
   into a tall empty box, and the list side never gets force-stretched
   either. */
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: start; }
@media (max-width: 800px) { .grid-2 { grid-template-columns: 1fr; } }

/* Same list+form layout as .grid-2, but for pages where the list side is a
   table with several columns (Staff, Branches) - a plain 50/50 split left
   the table squeezed into half the page and forced to scroll sideways even
   on a normal laptop screen, while the form never actually needed that much
   room. The form column gets a fixed, comfortable width instead and the
   table gets everything else. */
.grid-table-form { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 20px; align-items: start; }
@media (max-width: 900px) { .grid-table-form { grid-template-columns: 1fr; } }

/* "Add ..." forms in a popup over a full-width list
   (views/partials/form-dialog.js). */
dialog.form-dialog {
  border: none;
  padding: 0;
  border-radius: 16px;
  width: min(560px, calc(100vw - 24px));
  max-height: calc(100vh - 24px);
  max-height: calc(100dvh - 24px);
  background: var(--surface);
  color: var(--text);
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.28);
  overflow: hidden;
}
dialog.form-dialog[open] { display: flex; flex-direction: column; animation: form-dialog-in 0.16s ease-out; }
dialog.form-dialog::backdrop { background: rgba(15, 25, 20, 0.5); }
@keyframes form-dialog-in { from { opacity: 0; transform: translateY(10px) scale(0.98); } to { opacity: 1; transform: none; } }
.form-dialog-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 20px; border-bottom: 1px solid var(--border); }
.form-dialog-head h2 { margin: 0; font-size: 1.1rem; }
.form-dialog-close {
  width: 34px; height: 34px; border-radius: 999px; border: 1px solid var(--border); background: var(--surface);
  color: var(--text-muted); font-size: 1.35rem; line-height: 1; cursor: pointer; flex-shrink: 0;
}
.form-dialog-close:hover { background: var(--muted-bg); color: var(--text); }
.form-dialog-body { padding: 16px 20px 20px; overflow-y: auto; overscroll-behavior: contain; }
.form-dialog-body .form-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 4px; }
dialog.form-dialog.confirm-dialog { width: min(460px, calc(100vw - 24px)); }
.confirm-dialog .form-dialog-body p { margin: 0 0 10px; line-height: 1.45; }
/* A line the "already added" popup points at (public/js/pages/purchase-order.js). */
@keyframes po-row-flash { from { background: var(--warn-bg); } to { background: transparent; } }
tr.po-row-flash > td { animation: po-row-flash 1.6s ease-out; }
@media (max-width: 600px) {
  dialog.form-dialog { width: 100vw; max-width: 100vw; max-height: 92dvh; margin: auto 0 0; border-radius: 16px 16px 0 0; }
  .form-dialog-body .form-actions .btn { flex: 1; }
}

.cell-subtext { font-size: 0.78rem; margin-top: 1px; }
.table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.table th, .table td { text-align: left; padding: 9px 10px; border-bottom: 1px solid var(--border); vertical-align: top; }
.table th { color: var(--text-muted); font-weight: 600; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.02em; }
.table td.num, .table th.num { text-align: right; font-variant-numeric: tabular-nums; }
.table tr:last-child td { border-bottom: none; }
.table tr.row-warn td { background: var(--warn-bg); }

/* Inventory list's own column widths (2026-09, owner's request) - left to
   its own devices the table auto-sized every column off its header text,
   which made "Buying price (per unit)"/"Selling price (per unit)" (long
   headers, short numeric content) balloon wide while "Actions" (short
   header, but three links - Edit/Receive stock/Deactivate - that need
   real room) got squeezed down to almost nothing and its links were
   cut off. table-layout: fixed plus explicit widths on just the three
   columns that needed to change - Name gets more room, Buying price
   gets less, Actions gets more - while Category/Selling price/Stock/
   Status are left exactly as they already were. */
.inv-table { table-layout: fixed; }
.inv-table th:nth-child(1), .inv-table td:nth-child(1) { width: 24%; }
.inv-table th:nth-child(3), .inv-table td:nth-child(3) { width: 11%; }
.inv-table th:nth-child(7), .inv-table td:nth-child(7) { width: 17%; }
/* Wrap at word boundaries only (overflow-wrap: normal is the browser
   default, stated here so nothing upstream can turn it into
   `break-word`/`anywhere` again) - a word never splits mid-letter
   ("BUYING" cut into "BUYIN"/"G"). This only reads cleanly because the
   header text itself is short (see the buy/sell column labels in
   views/inventory/list.js, which deliberately drop "(per unit)" for
   this table only - the longer label everywhere else, like the Edit
   Product form, is unaffected). A product name that's genuinely one
   long unbroken word is rare enough to just overflow its own cell
   rather than be butchered letter by letter. */
.inv-table td, .inv-table th { overflow-wrap: normal; word-break: normal; }
/* The Actions column mixes two different kinds of text: the short
   Edit/Receive stock/Deactivate LINKS (each meant to read as one label -
   "Receive stock" splitting into "Receive" / "stock" across two lines
   looks broken even though the column has room for it on one line), and,
   on a read-only branch view, one long plain-prose sentence ("Switch to
   this branch (topbar) to edit") that genuinely needs to wrap. `normal`
   on the container lets that sentence wrap at its word boundaries; `>a`
   puts each link's own text back to one line, which is what it almost
   always fits in anyway at this column's width - it only drops to its
   own row (a flex-wrap, not a text-wrap) when the column is too narrow
   for that pair to sit side by side. */
.inv-table td.actions { white-space: normal; }
.inv-table td.actions > a { white-space: nowrap; }
.inv-table .th-sort > a { white-space: normal; }

/* "Show more" row toggle (dashboard low-stock list etc.) - the extra
   rows are plain <tr> elements marked .extra-row, hidden by default and
   revealed with pure CSS when the details/summary right after the table
   is opened. No JavaScript needed. */
tr.extra-row { display: none; }
.panel:has(details.show-more[open]) tr.extra-row { display: table-row; }
details.show-more { margin-top: 10px; }
details.show-more summary { cursor: pointer; color: var(--primary); font-weight: 500; font-size: 0.85rem; }
details.show-more summary::-webkit-details-marker { color: var(--primary); }
.cell-title { font-weight: 600; }
.cell-sub { font-size: 0.8rem; color: var(--text-muted); }
.cell-code { font-weight: 500; font-size: 0.78rem; color: var(--text-muted); font-family: monospace; }
.actions { display: flex; gap: 10px; flex-wrap: wrap; white-space: nowrap; }

.badge { display: inline-block; padding: 2px 9px; border-radius: 999px; font-size: 0.78rem; font-weight: 600; }
.badge-ok { background: var(--ok-bg); color: var(--ok-text); }
.badge-muted { background: var(--muted-bg); color: var(--text-muted); }
.badge-warn { background: var(--warn-bg); color: var(--warn-text); }
.badge-danger { background: var(--danger); color: #fff; }
.badge-new { background: var(--primary); color: #fff; }

.btn-sm { padding: 6px 12px; font-size: 0.82rem; min-height: 34px; }

.si-evidence-link { display: inline-block; font-size: 0.82rem; margin-top: 4px; }
.si-reject-form { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.si-reject-form .po-inline-input { max-width: 180px; }

.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 16px; margin-bottom: 22px; }
.stat-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 16px; box-shadow: var(--shadow); min-width: 0; overflow: hidden; }
.stat-label { display: block; font-size: 0.78rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.03em; margin-bottom: 6px; }
/* clamp() shrinks the figure automatically as the card gets narrower or
   the number gets longer, instead of letting a big total (millions of
   TZS) overflow the card or force it wider than its grid column. */
.stat-value { display: block; font-size: clamp(1rem, 4.5vw, 1.5rem); font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.25; }
.stat-value .money { white-space: normal; overflow-wrap: anywhere; }
.stat-card .trend { margin-top: 6px; }
.stat-card-sub { font-size: 0.78rem; color: var(--text-muted); margin-top: 4px; }

/* Reports dashboard: hand-built inline-SVG charts (no charting library,
   see lib/charts.js) - colors come from the same theme variables as the
   rest of the app, so they follow light/dark mode automatically. */
.chart-panel { margin-bottom: 16px; }
.bar-chart { width: 100%; height: 160px; display: block; }
.chart-baseline { stroke: var(--border); stroke-width: 1; }
.chart-axis-label { font-size: 3px; fill: var(--text-muted); }
.chart-empty { padding: 24px 0; text-align: center; }

/* Dashboard "Sales trend" smooth line/area chart (views/dashboard.js's
   salesChartPanel + the hover script in public/js/app.js). Its own viewBox
   is pixel-scaled (not the 0-100 percent one barChart above uses), since a
   *stroked* line needs equal x/y scaling to stay an even thickness -
   percent-based x would stretch the stroke unevenly. */
.chart-subtitle { margin: -6px 0 10px; }
.chart-wrap { position: relative; }
.chart-svg { display: block; width: 100%; height: 170px; }
.chart-hover-dot, .chart-hover-line { pointer-events: none; }
.chart-tooltip {
  position: absolute;
  pointer-events: none;
  background: var(--text);
  color: var(--bg);
  font-size: 0.76rem;
  font-weight: 500;
  line-height: 1.35;
  padding: 6px 10px;
  border-radius: 8px;
  white-space: nowrap;
  transform: translate(-50%, -100%);
  box-shadow: var(--shadow);
  z-index: 5;
}
.chart-table-toggle { margin-top: 10px; }
.chart-table-toggle summary { cursor: pointer; color: var(--primary); font-weight: 500; font-size: 0.85rem; }

/* Offline banner (public/js/app.js toggles [hidden] on navigator online/
   offline events) - a calm, persistent strip rather than a scary browser
   error, explaining plainly that viewing still works but saving/selling
   needs the connection back. Uses the same warn-* tokens as every other
   warning banner in the app, so it doesn't invent a new color language. */
.offline-banner {
  background: var(--warn-bg);
  color: var(--warn-text);
  border-bottom: 1px solid var(--warn-border);
  padding: 9px 16px;
  font-size: 0.85rem;
  font-weight: 600;
  text-align: center;
  position: sticky;
  top: 0;
  z-index: 60;
}
/* The admin-only login/logout greeting (per the owner's request, 2026-09:
   "not too big, not too small - it pops up, then disappears") - a small,
   centered toast, not a full-width banner and not a modal that needs
   dismissing. public/js/app.js fades it in shortly after the page loads,
   holds it a few seconds, then fades it out and removes it - see its
   "Greeting popup" section for the exact timing. Server-rendered with
   opacity 0 so there's never a one-frame flash of it at full strength
   before the JS below takes over. */
.greeting-popup {
  position: fixed;
  top: 16px;
  left: 50%;
  transform: translateX(-50%) translateY(-8px);
  z-index: 80;
  max-width: min(90vw, 420px);
  padding: 12px 20px;
  border-radius: 10px;
  background: var(--primary-dark, #1f6f54);
  color: #fff;
  font-size: 0.92rem;
  font-weight: 600;
  text-align: center;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  opacity: 0;
  transition: opacity 0.35s ease, transform 0.35s ease;
  pointer-events: none;
}
.greeting-popup.is-visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}
/* While offline (html.is-offline, toggled by public/js/app.js's active
   /api/ping check - see its comments), every navigating control is dimmed
   AND made unclickable at the browser level, not just intercepted in JS -
   belt and braces against a click "sitting primed" and firing once the
   class is removed on reconnect. Deliberately does NOT cover .pos-layout
   (the Sell/New-order cart) - building up a sale/order is a purely local,
   client-side action with no network call until Checkout, so it keeps
   working offline exactly as before. */
html.is-offline .sidebar-nav,
html.is-offline .topbar-bell,
html.is-offline .topbar-icon-btn,
html.is-offline .settings-dropdown,
html.is-offline .topbar-branch-select {
  pointer-events: none;
  opacity: 0.5;
}
/* Demo mode indicator - deliberately a fixed color (not a theme token),
   the same in every theme and both light/dark, so it always reads as "not
   your normal screen" regardless of which appearance the person has
   chosen - the one thing this banner must never do is blend in. */
.demo-banner {
  background: #7c3aed;
  color: #ffffff;
  padding: 8px 16px;
  font-size: 0.85rem;
  font-weight: 700;
  text-align: center;
  letter-spacing: 0.02em;
  position: sticky;
  top: 0;
  z-index: 61;
}
.demo-badge {
  display: inline-block;
  background: #7c3aed;
  color: #ffffff;
  font-size: 0.62rem;
  font-weight: 700;
  padding: 2px 7px;
  border-radius: 999px;
  vertical-align: middle;
  letter-spacing: 0.03em;
}
.hbar-chart { display: flex; flex-direction: column; gap: 6px; }
.hbar-row { display: grid; grid-template-columns: minmax(90px, 34%) 1fr auto; align-items: center; gap: 8px; }
.hbar-label { font-size: 0.85rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hbar-track { background: var(--muted-bg); border-radius: 999px; height: 12px; overflow: hidden; }
.hbar-fill { display: block; height: 100%; border-radius: 999px; }
.hbar-value { font-size: 0.82rem; font-variant-numeric: tabular-nums; color: var(--text-muted); white-space: nowrap; }

/* Period-vs-period / branch-vs-branch comparison cards. */
.compare-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; }
.compare-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 14px; }
.compare-card h3 { margin: 0 0 8px; font-size: 0.82rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.03em; }
.compare-row { display: flex; justify-content: space-between; align-items: baseline; font-size: 0.9rem; padding: 3px 0; }
.compare-delta { font-weight: 700; font-variant-numeric: tabular-nums; }
.compare-delta.up { color: var(--primary-dark); }
.compare-delta.down { color: var(--danger); }

/* Currency display: a small muted currency tag in front of a bold amount,
   instead of repeating "TZS" as loud text before every number. */
.money { white-space: nowrap; }
.money-cur {
  font-size: 0.68em;
  font-weight: 700;
  color: var(--text-muted);
  letter-spacing: 0.03em;
  margin-right: 0.3em;
  vertical-align: 0.1em;
}
.money-amt { font-variant-numeric: tabular-nums; }
.money-lg .money-cur { font-size: 0.42em; vertical-align: 0.3em; }

.note-list { display: flex; flex-direction: column; gap: 10px; }
.note-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 14px; }

/* POS setup (2026-09): the grid of section cards that replaced the flat
   settings-dropdown links for Business info, Notifications, Discount
   rules, License, Backup, Audit trail and Demo access - see
   views/pos-setup.js. */
.pos-setup-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px; }
.pos-setup-card {
  display: flex;
  flex-direction: column;
  gap: 4px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
  box-shadow: var(--shadow);
  color: var(--text);
}
.pos-setup-card:hover { border-color: var(--primary); text-decoration: none; }
.pos-setup-card-title { font-weight: 600; font-size: 0.98rem; }
.pos-setup-card-hint { font-size: 0.85rem; color: var(--text-muted); }
/* POS setup > Security (2.3.2): each card's current state at the bottom. */
.pos-setup-card-status { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: auto; padding-top: 4px; }
.pos-setup-card-sub { font-size: 0.8rem; color: var(--text-muted); overflow-wrap: anywhere; }

/* Notes as a compact heading list (see views/notes.js) - one short line per
   thread instead of the full message text, which is what made this panel
   feel oversized before. Full text only shows once a thread is opened. */
.note-row-list { display: flex; flex-direction: column; gap: 2px; }
.note-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 10px 8px;
  border-radius: var(--radius);
  color: var(--text);
  border-bottom: 1px solid var(--border);
}
.note-row:last-child { border-bottom: none; }
.note-row:hover { background: var(--muted-bg); text-decoration: none; }
.note-row-main { display: flex; align-items: baseline; gap: 8px; min-width: 0; flex: 1; }
.note-row-heading { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-muted); font-size: 0.92rem; }
.note-row-meta { display: flex; align-items: center; gap: 10px; flex-shrink: 0; font-size: 0.8rem; }
.note-row-replies { color: var(--text-muted); }
.note-row-replies.has-replies { color: var(--primary); font-weight: 600; }

/* The opened thread - a short chat-style back-and-forth, own messages
   aligned right, the other side's aligned left, each with its own
   date/time - the "sent/received, like an internal messaging platform"
   framing the owner asked for. */
.note-thread { display: flex; flex-direction: column; gap: 10px; }
.note-bubble-row { display: flex; }
.note-bubble-row.note-mine { justify-content: flex-end; }
.note-bubble-row.note-theirs { justify-content: flex-start; }
.note-bubble {
  max-width: 80%;
  background: var(--muted-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 14px;
}
.note-mine .note-bubble { background: var(--ok-bg); }
.note-bubble-meta { font-size: 0.78rem; margin-bottom: 4px; }
.manual-section h2 { margin: 0 0 10px; font-size: 1.05rem; }
.manual-section p { margin: 0 0 12px; line-height: 1.55; }
.manual-section p:last-child { margin-bottom: 0; }
.note-meta { font-size: 0.8rem; margin-bottom: 4px; }
#pr-cart-list .note-meta { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.note-author { font-weight: 600; }
.note-message { margin: 0; white-space: pre-wrap; font-size: 0.92rem; }

.mini-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.mini-list li { display: flex; justify-content: space-between; gap: 10px; padding-bottom: 8px; border-bottom: 1px solid var(--border); font-size: 0.9rem; }
.mini-list li:last-child { border-bottom: none; padding-bottom: 0; }

.flash { padding: 10px 14px; border-radius: 8px; margin-bottom: 14px; font-size: 0.9rem; font-weight: 500; }
.flash-error { background: #fdecea; color: var(--danger); border: 1px solid #f3c1ba; }
.flash-info { background: var(--ok-bg); color: var(--ok-text); border: 1px solid #bfe4d6; }
.flash-warn { background: var(--warn-bg); color: var(--warn-text); border: 1px solid var(--warn-border); }

.chip { padding: 6px 14px; border-radius: 999px; border: 1px solid var(--border); background: var(--surface); font-size: 0.85rem; font-weight: 600; color: var(--text-muted); }
.chip-active { background: var(--primary); color: var(--primary-contrast); border-color: var(--primary); }
.chip-group { display: flex; gap: 8px; flex-wrap: wrap; }
.report-filters { display: flex; justify-content: space-between; align-items: center; gap: 16px; flex-wrap: wrap; margin-bottom: 16px; }
.custom-range-form { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; }
.custom-range-form label { flex-direction: row; align-items: center; gap: 6px; font-weight: 500; }
.custom-range-form input { padding: 7px 9px; border-radius: 6px; border: 1px solid var(--border); }

.pager { display: flex; justify-content: center; align-items: center; gap: 14px; margin-top: 14px; font-size: 0.9rem; flex-wrap: wrap; }
/* Page links for the Inventory list (2026-09, 50 rows a page). */
.pager-info { color: var(--text-muted); }
.pager-links { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.pager-page {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 34px;
  min-height: 34px;
  padding: 0 9px;
  border-radius: 8px;
  border: 1px solid var(--border);
  text-decoration: none;
  color: var(--text);
}
.pager-page:hover { border-color: var(--primary); }
.pager-current { background: var(--primary); border-color: var(--primary); color: #fff; font-weight: 600; }

.report-export-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.report-print-range { display: none; }

/* Reports picker menu (views/reports/index.js) - three big tappable cards
   instead of a dense page, easy to use on a phone screen. */
.report-picker-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; }
.report-picker-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: flex-start;
  text-decoration: none;
  color: inherit;
  transition: border-color 0.15s ease, transform 0.15s ease;
}
.report-picker-card:hover { border-color: var(--primary); transform: translateY(-1px); }
.report-picker-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 10px;
  background: var(--primary);
  color: var(--primary-contrast);
  font-weight: 700;
  font-size: 1.1rem;
}
.report-picker-title { font-weight: 700; font-size: 1.05rem; }
.report-picker-desc { font-size: 0.85rem; }

/* Sales-comparison report: two side-by-side date-range pickers. */
.compare-period-form { display: flex; gap: 20px; flex-wrap: wrap; margin-bottom: 16px; }
.compare-period-group { display: flex; flex-direction: column; gap: 8px; }
.compare-period-group h3 { margin: 0; font-size: 0.82rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.03em; }
.compare-period-inputs { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.compare-period-inputs input { padding: 7px 9px; border-radius: 6px; border: 1px solid var(--border); }

/* Stock management report: sub-report tab chips. */
.report-subtabs { margin-bottom: 16px; }
@media print {
  .report-print-range { display: block; margin: 0 0 16px; font-weight: 600; }
}

/* Login */
.login-wrap { display: flex; justify-content: center; }
.login-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 32px 28px;
  box-shadow: var(--shadow);
  width: 100%;
}
.login-brand { text-align: center; margin-bottom: 20px; }
.login-brand h1 { font-size: 1.3rem; }
.login-lang { text-align: center; margin-top: 16px; font-size: 0.88rem; }

/* POS / Sell screen */
.pos-layout { display: grid; grid-template-columns: 1.4fr 1fr; gap: 16px; align-items: start; }
@media (max-width: 900px) { .pos-layout { grid-template-columns: 1fr; } }
.pos-results { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; max-height: 65vh; overflow-y: auto; }
.pos-result-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px 14px;
  box-shadow: var(--shadow);
}
.pos-result-info { display: flex; flex-direction: column; gap: 2px; }
.pos-result-name { font-weight: 600; }
.pos-result-meta { font-size: 0.8rem; color: var(--text-muted); }
.pos-result-meta.out { color: var(--danger); font-weight: 600; }
/* Add Stock search dropdown (views/inventory/receive-search.js): the whole
   row is clickable, and keyboard Up/Down highlights one row at a time. */
#inv-receive-search-results .pos-result-item { cursor: pointer; }
#inv-receive-search-results .pos-result-item:hover,
#inv-receive-search-results .pos-result-item.pos-result-active {
  border-color: var(--primary);
  background: var(--muted-bg);
}
.pos-results-heading { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.03em; margin: 2px 0 0; }

/* The till's own keyboard highlight (2026-09). Up/Down move it, Enter adds
   the highlighted row - so the cashier can see, without looking away from
   the customer, exactly which product Enter is about to put in the cart.
   The left bar is what carries the meaning on a cheap/washed-out screen
   where a faint background tint is invisible. */
.pos-layout .pos-result-item.pos-result-active {
  border-color: var(--primary);
  background: var(--muted-bg);
  box-shadow: inset 3px 0 0 var(--primary);
}
/* Typed a name that isn't there: say so, rather than leaving the panel
   looking like it is still loading. */
.pos-no-results { margin-top: 14px; }
/* The one-line crib under the search box. Small and quiet - it is there
   for the first week, not forever. */
.pos-keyboard-hint { margin: 6px 2px 0; font-size: 0.78rem; }
/* Cart title + "clear cart" on one line; the button only appears once the
   cart actually has something in it (public/js/app.js toggles [hidden]). */
.cart-head { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.cart-head h2 { margin: 0; }
.cart-head .link-btn { font-size: 0.82rem; }

/* Cash received / change to give, inside the checkout confirmation popup
   (public/js/app.js showConfirmModal). Optional - leave it empty and the
   sale goes through exactly as before. */
.pos-tender {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}
.pos-tender-label { display: block; font-size: 0.85rem; color: var(--text-muted); margin-bottom: 6px; }
.pos-tender .input { font-size: 1.15rem; }
.pos-quick-tender { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }

/* How the customer paid (public/js/app.js paymentButtons). Deliberately
   big, finger-sized targets in one row: this is tapped on every single
   sale, often on a phone, often with a queue waiting, so it has to be
   hittable without looking. The chosen one is filled in the accent colour
   rather than merely outlined - at a glance across a counter, an outline
   is easy to miss and picking the wrong method silently corrupts the
   evening's drawer count. */
.pos-pay { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border); }
.pos-pay-methods { display: flex; flex-wrap: wrap; gap: 6px; }
.pos-pay-btn { min-height: 40px; padding-left: 14px; padding-right: 14px; }
.pos-pay-btn.is-selected {
  background: var(--primary);
  color: var(--primary-contrast);
  border-color: var(--primary);
  font-weight: 600;
}
.pos-pay-btn.is-selected:hover { background: var(--primary-dark); border-color: var(--primary-dark); }

/* Phone-only running total (public/js/app.js updateMobileBar). Hidden
   outright on a desktop screen, where the cart panel is already beside the
   search results; below 900px the cart stacks underneath and goes
   off-screen, which is the case this exists for. */
.pos-mobile-bar { display: none; }
@media (max-width: 900px) {
  .pos-mobile-bar {
    display: flex;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 14px;
    /* clears the home indicator on an iPhone and similar */
    padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
    background: var(--surface);
    border-top: 1px solid var(--border);
    box-shadow: 0 -4px 14px rgba(0, 0, 0, 0.12);
  }
  .pos-mobile-bar[hidden] { display: none; }
  .pos-mobile-summary { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
  .pos-mobile-count { font-size: 0.75rem; color: var(--text-muted); }
  .pos-mobile-bar .btn { flex-shrink: 0; }
  /* Room to scroll past the bar so it never covers the last cart line or
     the checkout button underneath it. */
  .pos-layout { padding-bottom: 76px; }
  /* Seller's order screen on a phone (2.3.10): the order on top, the
     search and its "Add" buttons right under it - so adding a product
     never pushes the order out of sight. The order's own list scrolls
     inside a short box, so the search box stays near the top however
     many lines there are. */
  .pos-layout-order .pos-cart-col { order: -1; }
  .pos-layout-order .cart-panel { position: static; }
  .pos-layout-order #cart-items { max-height: 34vh; }
  .pos-layout-order .pos-results { max-height: none; }
  .pos-layout-order .pos-keyboard-hint { display: none; }

  /* 2.3.11 (owner's request): the order and the search box stay where
     they are - only the list of products under the search scrolls. The
     page itself no longer scrolls: public/js/pages/sell.js
     (fitOrderLayout) adds body.pos-order-fit and sets --pos-order-h to
     exactly the room between the top of this block and the bottom bar
     (or the keyboard). Without that script the 2.3.10 layout above is
     what shows. The classes sit on <body>, which public/js/nav.js resets
     on every in-place page change, so they can never follow the seller
     to another screen. No pull-down-to-refresh here: it would throw the
     order away. */
  body.pos-order-fit { overscroll-behavior-y: contain; }
  html:has(> body.pos-order-fit) { overscroll-behavior-y: contain; }
  body.pos-order-fit main.page { padding-bottom: 0; }
  body.pos-order-fit .pos-layout-order {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
    height: var(--pos-order-h, auto);
    padding-bottom: 0;
  }
  body.pos-order-fit .pos-layout-order .pos-cart-col { flex: 0 1 auto; min-height: 0; display: flex; flex-direction: column; }
  body.pos-order-fit .pos-layout-order .cart-panel { flex: 0 1 auto; min-height: 0; display: flex; flex-direction: column; }
  body.pos-order-fit .pos-layout-order .cart-panel > * { flex: none; }
  body.pos-order-fit .pos-layout-order .cart-panel > #cart-items { flex: 0 1 auto; min-height: 0; overscroll-behavior: contain; }
  body.pos-order-fit .pos-layout-order #cart-items:has(.cart-line) { min-height: 64px; }
  /* The products list always keeps a good share of the screen (about
     2-3 rows); the order's own lines scroll inside it beyond that. */
  body.pos-order-fit .pos-layout-order .pos-search-col { flex: 1 1 0; min-height: max(190px, 38%); display: flex; flex-direction: column; }
  body.pos-order-fit .pos-layout-order #pos-search { flex: none; }
  body.pos-order-fit .pos-layout-order .pos-results {
    flex: 1 1 0;
    min-height: 0;
    margin-top: 8px;
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
  }
  /* While the keyboard is up there is only about half a screen: the
     title, the order's total and its button step aside (the bar at the
     bottom has the same total and button once the keyboard closes) and
     the order keeps at most ~2/5 of the room, so the products being
     searched for are never squeezed out. */
  body.pos-order-fit.pos-kb-open .pos-order-head,
  body.pos-order-fit.pos-kb-open .pos-layout-order .cart-total-row,
  body.pos-order-fit.pos-kb-open .pos-layout-order #checkout-btn { display: none; }
  body.pos-order-fit.pos-kb-open .pos-layout-order .pos-cart-col { max-height: 42%; }
  body.pos-order-fit.pos-kb-open .pos-layout-order .pos-search-col { min-height: 140px; }
  body.pos-order-fit.pos-kb-open .pos-layout-order #cart-items { margin-bottom: 0; }
}
.pos-order-head { margin-bottom: 4px; }

/* Skeleton placeholder rows: shown for the instant between a keystroke in
   a live search box (Sell screen, Add Stock, Purchase Order search - all
   three share this .pos-result-item shape) and the search reply coming
   back, so the box never sits empty/blank-white while waiting - see
   skeletonSearchRows() in public/js/app.js. */
.skeleton-row { pointer-events: none; }
.skeleton-line {
  display: block;
  height: 11px;
  border-radius: 4px;
  background: linear-gradient(90deg, var(--muted-bg) 25%, var(--border) 37%, var(--muted-bg) 63%);
  background-size: 400% 100%;
  animation: skeleton-shimmer 1.4s ease infinite;
}
.skeleton-line-name { width: 55%; margin-bottom: 8px; }
.skeleton-line-meta { width: 35%; height: 9px; margin-bottom: 0; }
@keyframes skeleton-shimmer {
  0% { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}
.pos-show-more { align-self: center; margin-top: 4px; padding: 8px; font-size: 0.85rem; font-weight: 600; }

.cart-panel { position: sticky; top: 82px; }
#cart-items { display: flex; flex-direction: column; gap: 10px; margin: 12px 0; max-height: 45vh; overflow-y: auto; }
.cart-line { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; border-bottom: 1px solid var(--border); padding-bottom: 10px; }
.cart-line-info { display: flex; flex-direction: column; gap: 2px; flex: 1 1 100%; }
.cart-line-name { font-weight: 600; font-size: 0.92rem; }
.cart-line-meta { font-size: 0.78rem; color: var(--text-muted); }
.cart-line-controls { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.qty-btn { width: 26px; height: 26px; border-radius: 6px; border: 1px solid var(--border); background: var(--surface); cursor: pointer; font-size: 1rem; line-height: 1; flex-shrink: 0; }
.qty-input { width: 46px; text-align: center; padding: 4px; border-radius: 6px; border: 1px solid var(--border); flex-shrink: 0; }
.unit-select { padding: 4px 6px; border-radius: 6px; border: 1px solid var(--border); background: var(--surface); color: var(--text); font-size: 0.78rem; max-width: 90px; flex-shrink: 0; }
.cart-line-unit-label { font-size: 0.78rem; color: var(--text-muted); padding: 4px 2px; }
.cart-line-total { font-weight: 700; min-width: 70px; text-align: right; font-variant-numeric: tabular-nums; margin-left: auto; }
.cart-remove { color: var(--danger); font-size: 0.78rem; cursor: pointer; background: none; border: none; padding: 0; align-self: flex-start; text-align: left; }

/* Seller-editable line total on the direct Sell screen (2026-09) - the
   grand total is normally read-only maths (qty * shelf price), but a
   seller can type a different TOTAL here (a rounded/negotiated price) and
   the system works out the effective unit price itself. See
   public/js/app.js's renderCart() and lib/sales.js's finalizeSale(); an
   overridden line is auto-flagged for admin review (routes/sell.js ->
   sale_issues, source='price_override') without ringing the notification
   bell. */
.cart-line-total-col { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; margin-left: auto; }
.cart-line-total-input { width: 84px; text-align: right; padding: 4px 6px; border-radius: 6px; border: 1px solid var(--border); font-weight: 700; font-variant-numeric: tabular-nums; }
.cart-line-total-input.is-overridden { border-color: var(--warn-border); background: var(--warn-bg); color: var(--warn-text); }
.cart-line-reset { font-size: 0.72rem; color: var(--text-muted); background: none; border: none; padding: 0; cursor: pointer; text-decoration: underline; }
.cart-total-row { display: flex; justify-content: space-between; font-weight: 700; font-size: 1.15rem; padding: 10px 0; border-top: 2px solid var(--border); }
.empty-cart-msg { color: var(--text-muted); font-size: 0.9rem; padding: 20px 0; text-align: center; }

/* Modal open/close: public/js/app.js's openModal()/closeModal() helpers add
   or remove the .is-open class (after un-hiding, one frame later, so the
   transition actually has a "before" state to animate from) instead of
   flipping [hidden] straight to visible. Only opacity/transform are
   animated - both are handled entirely by the GPU compositor, so this stays
   smooth even on an older budget phone and never causes scroll/layout jank
   the way animating something like top/height/margin would. */
.modal-backdrop {
  position: fixed; inset: 0; background: rgba(15, 25, 20, 0.45); display: flex; align-items: center; justify-content: center; z-index: 50; padding: 16px;
  opacity: 0;
  transition: opacity 0.18s ease;
}
.modal-backdrop.is-open { opacity: 1; }
.modal {
  background: var(--surface); border-radius: 14px; padding: 24px; max-width: 420px; width: 100%; box-shadow: 0 10px 40px rgba(0,0,0,0.2);
  transform: translateY(10px) scale(0.97);
  transition: transform 0.18s ease;
}
.modal-backdrop.is-open .modal { transform: translateY(0) scale(1); }

/* Receipt */
.receipt-wrap { max-width: 380px; margin: 0 auto; }
.receipt { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 20px; font-size: 0.88rem; }
.receipt-app-name { text-align: center; margin-bottom: 2px; }
.receipt-biz-info { text-align: center; color: var(--text-muted); font-size: 0.78rem; line-height: 1.4; margin-bottom: 10px; }
.receipt-meta { display: flex; flex-direction: column; gap: 3px; margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px dashed var(--border); font-size: 0.82rem; }
.receipt-table { width: 100%; border-collapse: collapse; margin-bottom: 10px; }
.receipt-table th, .receipt-table td { padding: 4px 2px; font-size: 0.8rem; text-align: left; }
.receipt-table th.num, .receipt-table td.num { text-align: right; }
.receipt-table thead th { border-bottom: 1px dashed var(--border); }
.receipt-grand-total { display: flex; justify-content: space-between; font-weight: 700; padding-top: 8px; border-top: 1px dashed var(--border); font-size: 1rem; }
.receipt-thanks { text-align: center; margin-top: 14px; color: var(--text-muted); }
.receipt-actions { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }

/* The customer's receipt as a thermal till slip (views/receipt.js): white
   paper whatever the theme, centred shop header, dashed rules, two-column
   details, Item / Rate / Qty / Amount, a big total between heavy rules. */
.receipt.thermal {
  --slip-ink: #1b1b1b;
  --slip-muted: #4a4a4a;
  background: #fff;
  color: var(--slip-ink);
  border: 1px solid #e3e3e3;
  border-radius: 6px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
  padding: 22px 16px 26px;
  font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;
  font-size: 0.8rem;
  line-height: 1.35;
  font-variant-numeric: tabular-nums;
}
.receipt.thermal a { color: inherit; }
.rc-head { text-align: center; }
.rc-biz { font-size: 1.3rem; font-weight: 800; letter-spacing: 0.03em; text-transform: uppercase; line-height: 1.15; margin-bottom: 2px; }
.rc-head > div + div { font-size: 0.8rem; text-transform: uppercase; }
.rc-sep { border-top: 1px dashed #777; margin: 7px 0; }
.rc-sep-strong { border-top: 2px dashed #222; }
.rc-label { text-align: center; font-family: 'Courier New', Courier, monospace; font-weight: 700; font-size: 0.95rem; letter-spacing: 0.04em; }
.rc-meta { display: grid; grid-template-columns: 1fr 1fr; column-gap: 10px; row-gap: 2px; }
.rc-kv { min-width: 0; overflow-wrap: anywhere; }
.rc-k { color: var(--slip-muted); }
.rc-v { font-weight: 600; }
.rc-items { width: 100%; border-collapse: collapse; }
.rc-items th { font-weight: 700; padding: 0 0 5px; border-bottom: 1px dashed #777; }
.rc-items td { padding: 3px 0; vertical-align: top; }
.rc-items th.num, .rc-items td.num { text-align: right; white-space: nowrap; padding-left: 6px; }
.rc-items .rc-q { text-align: center !important; }
.rc-items .rc-name { text-align: left; overflow-wrap: anywhere; }
.rc-items td.rc-name { text-transform: uppercase; }
.receipt-share-wrap > .btn { width: 100%; }
.rc-sums { display: flex; justify-content: space-between; gap: 10px; }
.rc-counts { display: flex; flex-direction: column; gap: 2px; }
.rc-sums-right { flex: 1; max-width: 62%; display: flex; flex-direction: column; gap: 2px; }
.rc-row { display: flex; justify-content: space-between; gap: 10px; }
.rc-row > span:last-child { text-align: right; font-weight: 600; }
.rc-total { display: flex; justify-content: flex-end; align-items: baseline; gap: 16px; font-size: 1.45rem; font-weight: 800; padding: 2px 0; }
.rc-pay { display: flex; flex-direction: column; gap: 2px; margin: 4px 0 2px; }
.rc-thanks { text-align: center; margin: 16px 0 10px; font-size: 0.9rem; }
.rc-customer-title { font-weight: 800; text-decoration: underline; font-size: 0.95rem; margin-bottom: 3px; }
.rc-customer { min-height: 2.2em; }

/* Receipt page: "Other share" popover (Bluetooth print / Email), grouped
   behind one button next to Print - same open/close fade+scale animation
   (openModal()/closeModal() in app.js) and item look as the sidebar's
   .settings-dropdown above, but anchored under its own toggle button and
   sized to match it, since here it sits inline among the receipt action
   buttons rather than spanning a fixed-width sidebar panel. */
.receipt-share-wrap { position: relative; }
.receipt-share-dropdown {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 10px 30px rgba(0,0,0,0.18);
  padding: 8px;
  z-index: 45;
  display: flex;
  flex-direction: column;
  opacity: 0;
  transform: translateY(-6px) scale(0.98);
  transform-origin: top center;
  transition: opacity 0.15s ease, transform 0.15s ease;
}
.receipt-share-dropdown.is-open {
  opacity: 1;
  transform: translateY(0) scale(1);
}
.receipt-share-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 8px 10px;
  border-radius: 8px;
  background: none;
  border: none;
  color: var(--text);
  font-size: 0.9rem;
  font-weight: 500;
  font-family: inherit;
  cursor: pointer;
}
.receipt-share-item:hover { background: var(--muted-bg); }

/* Printable purchase order: a clean business-header + order-info + plain
   item table, hidden on screen and shown only when printing (the
   interactive version with resolve buttons/inputs is what's normally
   visible, and gets hidden by .no-print in the same print rule below). */
.po-print-only { display: none; }
.po-print-title { text-align: center; margin: 0 0 10px; font-size: 0.95rem; }

.update-row { flex-wrap: wrap; }
.update-info { display: flex; align-items: center; gap: 8px; }
.update-name { font-weight: 600; }
.update-detail { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Purchase orders */
.po-resolve-actions { display: flex; gap: 8px; align-items: center; }
.po-select-all-row { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
.po-select-all-row .link-btn { font-size: 0.82rem; font-weight: 600; }
.po-check-label { display: flex; flex-direction: row; align-items: center; gap: 8px; font-weight: 500; color: var(--text); }
.po-check-label input[type="checkbox"] { min-height: 0; padding: 0; width: auto; flex: none; }
.po-qty-input { width: 80px; text-align: right; }
/* The optional per-row "estimate" box (see costCell() in
   views/purchase-orders/new.js) sits above its computed total, only shown
   for a line whose cost couldn't be pulled from the stock list. */
.po-cost-cell > .po-cost-wrap { display: flex; flex-direction: column; align-items: flex-end; gap: 3px; }
.po-est-cost-input { width: 100px; text-align: right; }
.po-inline-input { width: 130px; }
.po-request-meta { font-size: 0.78rem; color: var(--text-muted); margin-top: 2px; }
.po-resolve-form { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.po-resolve-row-1, .po-resolve-row-3 { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.po-resolve-row-2 { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; }
.po-price-label { max-width: 200px; }
.po-price-label input { width: 160px; }
.po-resolve-row-2 .hint { margin: 0; align-self: center; }
.po-budget-panel { display: flex; flex-direction: column; gap: 2px; background: var(--muted-bg); }
.po-budget-label { font-size: 0.82rem; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.02em; }
.po-budget-total { margin: 2px 0; }
.table tr.row-ok td { background: var(--ok-bg); }

/* Purchase order: collapsed "add more products" panel (search + unlisted) -
   kept closed by default so the suggested/staff-request sections above
   stay the primary, uncluttered view (see views/purchase-orders/new.js). */
/* Suggested products, folded (2.3.8): a bar with the count; click to open. */
.po-suggest-panel > summary { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; cursor: pointer; list-style: none; }
.po-suggest-panel > summary::-webkit-details-marker { display: none; }
.po-suggest-panel > summary::before { content: '+'; display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 50%; border: 1px solid var(--border); color: var(--primary); font-weight: 700; }
.po-suggest-panel[open] > summary::before { content: '\2212'; }
.po-suggest-panel[open] > summary { margin-bottom: 10px; }
.po-suggest-title { font-family: var(--font-display, inherit); font-size: 1.15rem; font-weight: 700; color: var(--text); }
.po-suggest-count { font-size: 0.9rem; }
.po-suggest-panel[open] .po-suggest-open { display: none; }
.po-more-panel summary { cursor: pointer; color: var(--primary); font-weight: 600; list-style: none; }
.po-more-panel summary::-webkit-details-marker { display: none; }
.po-more-panel summary::before { content: '+ '; }
.po-more-panel[open] summary::before { content: '\2212 '; }
.po-search-block, .po-unlisted-block { margin-top: 16px; }
.po-search-block h3, .po-unlisted-block h3 { margin: 0 0 8px; font-size: 0.92rem; }
.po-search-input { width: 100%; max-width: 420px; }
.po-search-results { margin-top: 10px; max-height: 40vh; }
.po-search-results:empty { margin-top: 0; }
.po-search-empty { color: var(--text-muted); font-size: 0.85rem; padding: 4px 0; }
.po-unlisted-add-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }

/* Notifications */
.notif-list { display: flex; flex-direction: column; padding: 0; overflow: hidden; }
.notif-row { display: flex; align-items: stretch; border-bottom: 1px solid var(--border); transition: opacity .15s ease; }
.notif-row:last-child { border-bottom: none; }
.notif-row.is-removing { opacity: 0; }
.notif-item { display: flex; align-items: flex-start; gap: 12px; padding: 14px 18px; position: relative; flex: 1; min-width: 0; }
.notif-item-new { background: var(--ok-bg); }
.notif-icon { flex-shrink: 0; width: 30px; height: 30px; border-radius: 999px; background: var(--muted-bg); display: inline-flex; align-items: center; justify-content: center; color: var(--text-muted); }
.notif-icon svg { width: 16px; height: 16px; }
.notif-item-new .notif-icon { background: var(--surface); color: var(--primary); }
.notif-body { flex: 1; min-width: 0; }
.notif-title { font-weight: 500; }
.notif-detail { margin-top: 3px; font-size: 0.88rem; }
.notif-time { margin-top: 4px; font-size: 0.76rem; color: var(--text-muted); }
.notif-dot { position: absolute; top: 16px; right: 16px; width: 8px; height: 8px; border-radius: 999px; background: var(--primary); }
/* A notification that jumps straight to where it's actioned (currently:
   purchase requests only - see views/notifications.js) is rendered as an
   <a> instead of a <div>, so it needs its link styling reset and a small
   affordance (the chevron) showing it's tappable. */
.notif-item-link { color: inherit; text-decoration: none; cursor: pointer; }
.notif-item-link:hover, .notif-item-link:focus-visible { background: var(--muted-bg); }
.notif-go { flex-shrink: 0; align-self: center; font-size: 1.3rem; line-height: 1; color: var(--text-muted); margin-right: 10px; }
.notif-item-link .notif-dot { top: 14px; right: 4px; }
/* The per-row "x" (dismiss) - kept faint until hovered/focused so it never
   competes with the notification's own content for attention, per the
   owner's request, 2026-09. Sits outside .notif-item (see
   views/notifications.js) so a clickable notification stays one valid <a>
   instead of nesting a <button> inside it. */
.notif-dismiss {
  flex-shrink: 0;
  align-self: center;
  width: 30px;
  height: 30px;
  margin: 0 10px;
  border: none;
  background: transparent;
  color: var(--text-muted);
  opacity: 0.35;
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
  border-radius: 999px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity .15s ease, background .15s ease, color .15s ease;
}
.notif-dismiss:hover, .notif-dismiss:focus-visible { opacity: 1; background: var(--muted-bg); color: var(--text); }
.notif-dismiss:disabled { opacity: 0.15; cursor: default; }

/* Product updates */
.upd-hint { font-size: 0.85rem; color: var(--text-muted); background: var(--muted-bg); border-radius: var(--radius); padding: 10px 12px; margin-bottom: 14px; }
.upd-filters { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
.upd-filter { padding: 6px 14px; border-radius: 999px; border: 1px solid var(--border); background: var(--surface); color: var(--text); font-size: 0.85rem; font-weight: 600; text-decoration: none; }
.upd-filter:hover { background: var(--muted-bg); text-decoration: none; }
.upd-filter.active { background: var(--ok-bg); color: var(--primary-dark); border-color: transparent; }
.upd-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; align-content: start; }
.upd-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 16px; display: flex; flex-direction: column; gap: 8px; }
.upd-card-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; }
.upd-meta { font-size: 0.8rem; color: var(--text-muted); }
.upd-source { font-size: 0.78rem; color: var(--text-muted); font-style: italic; }

@media print {
  .sidebar, .sidebar-backdrop, .topbar, .receipt-actions, .no-print { display: none !important; }
  .app-shell, .app-main { display: block; }
  .page { padding: 0; }
  .receipt { border: none; box-shadow: none; }
  .receipt-wrap { max-width: 78mm; }
  .receipt.thermal { padding: 0; font-size: 9pt; }
  .grid-2 { display: block; }
  .grid-2 .panel { margin-bottom: 14px; }
  .panel { box-shadow: none; border: 1px solid #ccc; }
  .po-print-only { display: block; max-width: 480px; margin: 0 auto; }
}

/* ---------- Approvals page (views/approvals.js) ---------- */
/* The three review queues gathered into one page. Each section is its own
   <form>, so "approve selected" can never reach across into another
   queue's rows. */
.apr-section { margin-bottom: 18px; }
.apr-section-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.apr-section-head h2 { margin: 0; font-size: 1.05rem; display: flex; align-items: center; gap: 8px; }
.apr-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--muted-bg);
  color: var(--text-muted);
  font-size: 0.78rem;
  font-weight: 700;
}
.apr-count-lg { background: var(--danger); color: #fff; font-size: 0.85rem; }
.apr-pick { width: 34px; }
.apr-pick input { width: 17px; height: 17px; }
/* A staff member's own words about what happened - the part the decision
   actually turns on, so it is set apart rather than run together with the
   name and date above it. */
.apr-note { font-style: italic; }
.apr-bulk-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.apr-empty { text-align: center; padding: 36px 20px; color: var(--text-muted); }
.apr-empty-mark { font-size: 2.4rem; color: var(--ok-text); margin: 0 0 6px; line-height: 1; }

/* Batches of purchase requests (2.3.19, views/request-batch.js): a saved
   batch is one line in the requests section, with the plain requests
   underneath and the save/add controls in their bulk bar. */
.prb-subhead { margin: 14px 0 8px; font-size: 0.92rem; display: flex; align-items: center; gap: 8px; color: var(--text-muted); text-transform: none; }
.prb-lines { display: flex; flex-direction: column; gap: 8px; }
.prb-line {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--warn-border);
  border-left: 4px solid var(--a4);
  border-radius: var(--radius-sm);
  background: var(--warn-bg);
}
.prb-line-main { min-width: 0; }
.prb-line-name { font-weight: 700; color: var(--text); text-decoration: none; margin-right: 6px; }
.prb-line-name:hover { text-decoration: underline; }
.prb-icon { margin-right: 6px; }
.prb-preview { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 60ch; }
.prb-line-actions { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; flex-shrink: 0; }
.prb-bulk { align-items: center; }
.prb-bulk-group { display: inline-flex; gap: 6px; align-items: center; flex-wrap: wrap; padding-right: 8px; border-right: 1px solid var(--border); }
.prb-bulk .prb-bulk-group input[type="text"],
.prb-bulk .prb-bulk-group select { width: auto; min-width: 0; max-width: 220px; padding: 6px 10px; font-size: 0.88rem; min-height: 0; height: auto; }
.prb-bulk .prb-bulk-group input[type="text"] { width: 190px; }
.prb-info-line { margin: 0 0 8px; }
.prb-rename { display: flex; gap: 8px; align-items: flex-end; flex-wrap: wrap; margin-top: 6px; }
.prb-rename label { display: flex; flex-direction: column; gap: 4px; font-size: 0.85rem; color: var(--text-muted); }
.prb-rename input { max-width: 320px; }
.prb-dissolve { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.prb-dissolve .hint { margin: 0; }
.prb-tag { text-decoration: none; font-weight: 600; }
.prb-po-row { background: var(--warn-bg); }
.prb-po-banner { margin: 8px 0; }
@media (max-width: 640px) {
  .prb-line { flex-direction: column; align-items: stretch; }
  .prb-line-actions { justify-content: flex-start; }
  .prb-preview { white-space: normal; }
  .prb-bulk-group { border-right: 0; padding-right: 0; }
  .prb-bulk .prb-bulk-group input[type="text"] { width: 100%; max-width: none; }
}

/* ---------- Inventory: prices edited down the list ---------- */
/* Narrow, right-aligned boxes that read as part of the table rather than a
   form bolted on top of it - the row should still scan as a row. */
.inv-price-cell { padding: 6px 8px; }
.inv-price-input {
  width: 100%;
  max-width: 110px;
  min-height: 34px;
  padding: 5px 8px;
  text-align: right;
  font-size: 0.88rem;
  background: transparent;
  border-color: transparent;
}
.inv-price-input:hover { border-color: var(--border); }
.inv-price-input:focus { background: var(--surface); border-color: var(--primary); }
/* A price that has been changed but not yet saved. Both the box and the
   bar below say so - a page full of editable boxes must never look like
   unsaved work when there isn't any. */
.inv-price-input.inv-price-changed {
  background: var(--warn-bg);
  border-color: var(--warn-border);
  font-weight: 700;
}
.inv-price-bar {
  position: sticky;
  bottom: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 12px;
  padding: 11px 14px;
  border: 1px solid var(--warn-border);
  border-radius: 10px;
  background: var(--warn-bg);
  color: var(--warn-text);
  box-shadow: var(--shadow);
}
.inv-price-bar[hidden] { display: none; }
.inv-price-bar-text { font-weight: 600; font-size: 0.88rem; }
.inv-price-bar-actions { display: flex; gap: 8px; }

/* ---------- Expiring stock (views/inventory/expiring.js) ---------- */
.exp-tabs { display: flex; gap: 6px; flex-wrap: wrap; margin: 12px 0 14px; }
.exp-tab {
  padding: 7px 13px;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 0.86rem;
  font-weight: 500;
  color: var(--text-muted);
  text-decoration: none;
}
.exp-tab:hover { border-color: var(--primary); text-decoration: none; }
.exp-tab-active { background: var(--primary); border-color: var(--primary); color: #fff; font-weight: 600; }
.exp-totals { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.exp-total {
  flex: 1 1 200px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
}
/* What has already gone is money lost, not money at risk - a different
   number with a different meaning, so it never shares a box with the one
   that can still be acted on. */
.exp-total-danger { border-color: #f3c1ba; background: #fdecea; }
.exp-total-danger .money { color: var(--danger); }
.exp-total-label { display: block; font-size: 0.78rem; color: var(--text-muted); margin-bottom: 4px; }
.exp-soon { color: var(--warn-text); font-weight: 600; }
/* A batch that has already gone. .badge-danger is defined once, further
   up, and shared with every rejected-status badge in the app - redefining
   it here would have quietly restyled all of those too. */
.row-danger { background: #fdecea; }
.row-danger td { color: var(--text); }

/* ---------- My sales today (views/my-sales.js) ---------- */
.mysales-totals { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.mysales-total {
  flex: 1 1 180px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
}
.mysales-total-label { display: block; font-size: 0.78rem; color: var(--text-muted); margin-bottom: 4px; }
.mysales-total-value { font-size: 1.5rem; font-weight: 700; }

/* Admin's notification-clearing page (views/notification-settings.js). */
.notif-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 26px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--warn-bg);
  color: var(--warn-text);
  font-weight: 700;
  font-size: 0.85rem;
}
.notifset-all { margin-top: 14px; }

/* Payment details printed under a receipt's total (views/receipt.js).
   Same two-column shape as the total line above it, one step smaller, so
   it reads as a footnote to the total rather than competing with it. */
.receipt-pay-line {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-size: 0.9rem;
  color: var(--text-muted);
  margin-top: 4px;
}
.receipt-pay-line:first-of-type { margin-top: 10px; }

/* A label + control sitting inline inside a row of action buttons
   (views/orders/detail.js's payment picker), so the picker lines up with
   the buttons beside it instead of stacking above them. */
.inline-select { display: inline-flex; align-items: center; gap: 8px; margin-right: 10px; }
.inline-select > span { font-size: 0.85rem; color: var(--text-muted); white-space: nowrap; }
.inline-select select { min-height: 38px; }

/* Till shift (views/shifts.js). A row of "label on the left, money on the
   right" lines - the same shape as the receipt's totals, because it is the
   same kind of reading: one figure per line, scanned down the right edge. */
.shift-line {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
}
.shift-line:last-child { border-bottom: none; }
/* The one figure the whole page exists for - what should be in the drawer
   right now - gets the weight to match. */
.shift-line-total {
  border-top: 2px solid var(--border);
  border-bottom: none;
  margin-top: 4px;
  padding-top: 12px;
  font-weight: 700;
}
.shift-noncash-head { margin: 14px 0 0; }

/* A shift's difference. Exactly right is a tick and nothing else; short is
   coloured like every other blocking problem in the app; over is flagged
   too, since it is nearly always a sale rung up wrong rather than luck. */
.shift-diff-ok { color: var(--ok-text); font-weight: 700; }
.shift-diff-short .money, .shift-diff-short { color: var(--danger); font-weight: 600; }
.shift-diff-over .money, .shift-diff-over { color: var(--warn-text); font-weight: 600; }
.shift-open-tag {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--ok-bg);
  color: var(--ok-text);
  font-size: 0.78rem;
  font-weight: 600;
}

/* Counting stock (views/stock-takes.js). The count box and the search box
   are the two things a thumb has to hit while holding a box of medicine in
   the other hand, so both are laid out to wrap rather than shrink, and
   never get narrower than a comfortable target. */
/* .hint carries a -4px top margin so it tucks under a form label; after a
   search row that pulls it up INTO the input, so the row keeps enough
   space below it for the hint to clear. */
.stk-search-form { display: flex; gap: 8px; flex-wrap: wrap; margin: 10px 0 12px; }
.stk-search-form .input { flex: 1 1 220px; min-width: 0; }
.stk-search-table { margin-top: 14px; }
.stk-count-form { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.stk-count-input { width: 90px; flex: 0 0 auto; }
.stk-reason-input { width: 150px; flex: 0 0 auto; }
.stk-tag {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
  vertical-align: middle;
}
.stk-tag-ok { background: var(--ok-bg); color: var(--ok-text); }
.stk-tag-muted { background: var(--muted-bg); color: var(--text-muted); }

@media (max-width: 560px) {
  /* On a phone the reason box would squeeze the count box down to nothing,
     and the count is the field that actually matters - so the reason drops
     to its own full-width line underneath. */
  .stk-count-form { flex-direction: column; align-items: stretch; }
  .stk-count-input, .stk-reason-input { width: 100%; }
}

/* Product barcodes (views/inventory/form.js). A stack of boxes rather than
   one, because a product carries one number per manufacturer it is bought
   from. Laid out like any other labelled field on the form, so it does not
   read as a special case. */
.barcode-field { display: block; margin-bottom: 14px; }
.barcode-label { display: block; font-size: 0.85rem; color: var(--text-muted); margin-bottom: 6px; }
#barcode-rows { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
.barcode-input { width: 100%; }

/* The thin bar that shows a page is on its way (public/js/nav.js). Pages
   are now fetched in the background while the current one stays on screen,
   which removes the white flash but also removes the browser's own "I am
   loading" signal - without something here, a slow reply reads as a dead
   button and gets tapped again. Deliberately a 2px line at the very top
   rather than a spinner over the content: it never covers a figure
   somebody is mid-way through reading. */
html.nav-busy::before {
  content: '';
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 2px;
  z-index: 2000;
  background: var(--primary);
  transform-origin: 0 50%;
  animation: nav-progress 1.1s ease-out infinite;
  pointer-events: none;
}
@keyframes nav-progress {
  0%   { transform: scaleX(0);    opacity: 1; }
  70%  { transform: scaleX(0.85); opacity: 1; }
  100% { transform: scaleX(1);    opacity: 0; }
}

/* A button that opens to reveal the questions behind it (views/shifts.js's
   "Close shift"). Built on <details>/<summary> so it needs no JavaScript at
   all and can never submit anything by being opened - the marker is removed
   so the summary reads purely as the button it is styled to be. */
.disclose > summary { list-style: none; cursor: pointer; display: inline-flex; }
.disclose > summary::-webkit-details-marker { display: none; }
.disclose > summary::marker { content: ''; }
.disclose-btn { justify-content: center; }
.disclose-body { margin-top: 16px; }
/* A bigger target for the one or two buttons that end something - closing a
   till, starting the next sale. These get hit at speed, often with a queue
   waiting, and are worth being unmissable. */
.btn-lg {
  min-height: 52px;
  padding-left: 26px;
  padding-right: 26px;
  font-size: 1.05rem;
  font-weight: 600;
}

/* The quiet "Clear this list" / "Show them again" line under somebody's own
   requests and reports (views/partials/own-list-clear.js). Deliberately a
   link, not a button, and set apart from the list above it: tidying up is
   housekeeping, not one of the actions the page exists for. */
.own-clear {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.own-clear form { margin: 0; }

/* ---------------------------------------------------------------------
   SCROLLBARS - SEEN ONLY WHEN LOOKED FOR (2026-09)

   Windows draws a wide, near-white scrollbar by default. On a dark theme
   that is the brightest thing on the screen - a glowing white stripe down
   the right-hand edge, sitting beside a page of muted greys, pulling the
   eye all day for a control that is touched once an hour.

   So the thumb is drawn at barely a tenth of full strength: present enough
   to show there is more page below, faint enough not to be read as part of
   the design. Point at it and it brightens and thickens; hold it and it
   brightens again. The track is invisible always.

   Deliberately a neutral grey at low opacity rather than a colour per
   theme: at these opacities it settles correctly against a light page and
   a dark one alike, which is one rule instead of five kept in step by hand.

   The bar stays 12px wide the whole time - that is the part the mouse has
   to hit. Only the painted strip inside it grows, which is why the border
   is transparent and the background is clipped to the content box.
   --------------------------------------------------------------------- */
::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-corner { background: transparent; }
::-webkit-scrollbar-thumb {
  background-color: rgba(145, 155, 165, 0.13);
  border-radius: 999px;
  border: 4px solid transparent;
  background-clip: content-box;
  transition: background-color 0.15s ease, border-width 0.15s ease;
}
::-webkit-scrollbar-thumb:hover {
  background-color: rgba(145, 155, 165, 0.36);
  border-width: 3px;
}
::-webkit-scrollbar-thumb:active {
  background-color: rgba(145, 155, 165, 0.52);
  border-width: 3px;
}

/* Firefox has no ::-webkit- pseudo-elements, so it gets the standard
   properties instead - and ONLY here, because in Chromium these same two
   properties override everything above and hand the scrollbar back to the
   browser. It has no hover state to offer, so it gets one quiet middle
   value rather than the two steps. */
@supports not selector(::-webkit-scrollbar) {
  html,
  .sidebar-nav {
    scrollbar-width: thin;
    scrollbar-color: rgba(145, 155, 165, 0.32) transparent;
  }
}

/* One line of an accounting transaction (views/accounting/new.js). Wide
   enough to read the account name, because picking the wrong one is the
   mistake this whole page exists to avoid; the amount stays a fixed,
   comfortable width so a column of figures lines up down the page. */
.acc-line {
  display: flex;
  gap: 10px;
  align-items: flex-end;
  flex-wrap: wrap;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}
.acc-line:last-of-type { border-bottom: none; }
.acc-line label { margin: 0; }
.acc-line-what { flex: 2 1 260px; min-width: 0; }
.acc-line-amount { flex: 0 0 150px; }
.acc-line-note { flex: 1 1 180px; min-width: 0; }
.acc-line-remove { flex: 0 0 auto; padding-bottom: 10px; }
@media (max-width: 640px) {
  /* On a phone each field takes its own line - a squeezed account name is
     exactly how the wrong one gets picked. */
  .acc-line-what, .acc-line-amount, .acc-line-note { flex: 1 1 100%; }
}

/* Cash / On credit / Some of each (views/accounting/new.js). Three real
   choices rather than two boxes that have to add up - the selected one is
   filled rather than outlined, because picking the wrong one here quietly
   changes whether the books say you owe somebody money. */
.acc-pay-modes { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.acc-pay-mode { min-height: 44px; padding-left: 18px; padding-right: 18px; }
.acc-pay-mode.is-selected {
  background: var(--primary);
  color: var(--primary-contrast);
  border-color: var(--primary);
  font-weight: 600;
}
.acc-pay-mode.is-selected:hover { background: var(--primary-dark); border-color: var(--primary-dark); }

/* The verdict on the review page (views/accounting/review.js) - the one
   sentence the whole step exists to show. Given its own weight because it
   is what the person is being asked to judge; the debits and credits
   below it are the working, not the question. */
.acc-verdict { border-left: 4px solid var(--primary); }
.acc-verdict-line { margin: 0 0 6px; font-size: 1.05rem; font-weight: 600; }
.acc-verdict-class { margin: 0 0 10px; font-size: 1.15rem; line-height: 1.4; color: var(--primary-dark); }
:root[data-theme="dark"] .acc-verdict-class,
:root[data-theme="midnight"] .acc-verdict-class { color: var(--primary); }

/* Add or Ignore. Side by side and both large, because this is a decision,
   not a form with a save button - and Ignore has to be as easy to hit as
   Add, or the page is pushing rather than asking. */
.acc-decide { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 18px; }
.acc-decide form { margin: 0; }

/* ---------------------------------------------------------------------
   CHART COLOURS (2026-09)

   Six categorical hues, assigned in fixed order to branches and never
   cycled by rank - so filtering the report leaves each branch the colour
   it already was. Both columns were run through the data-viz validator:
   lightness band, chroma floor, colour-blind separation of adjacent
   pairs, and contrast against the surface they sit on. The dark column is
   the same six hues re-stepped for a dark surface, not a different set.

   Deliberately NOT the theme's own --primary: a line chart needs hues
   that stay apart from each other, which is a different problem from a
   brand colour, and reusing the brand for series one would make that one
   branch look like "the system's" branch.
   --------------------------------------------------------------------- */
:root {
  --series-1: #2a78d6;
  --series-2: #eb6834;
  --series-3: #1baf7a;
  --series-4: #eda100;
  --series-5: #e87ba4;
  --series-6: #008300;
}
:root[data-theme="dark"],
:root[data-theme="midnight"] {
  --series-1: #3987e5;
  --series-2: #d95926;
  --series-3: #199e70;
  --series-4: #c98500;
  --series-5: #d55181;
  --series-6: #008300;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]):not([data-theme="blue"]):not([data-theme="sand"]) {
    --series-1: #3987e5;
    --series-2: #d95926;
    --series-3: #199e70;
    --series-4: #c98500;
    --series-5: #d55181;
    --series-6: #008300;
  }
}

.chart-wrap { width: 100%; }
.line-chart { width: 100%; height: auto; display: block; overflow: visible; }
/* Recessive on purpose: the grid is there to be measured against, not
   read. Anything heavier competes with the lines themselves. */
.chart-grid { stroke: var(--border); stroke-width: 1; opacity: 0.55; }
.chart-axis { fill: var(--text-muted); font-size: 11px; }
.chart-dot { opacity: 0; transition: opacity 0.12s ease; }
.line-chart:hover .chart-dot { opacity: 1; }
.chart-endlabel { fill: var(--text-muted); font-size: 11px; font-weight: 600; }
.chart-legend { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 10px; }
.chart-legend-item { display: inline-flex; align-items: center; gap: 6px; font-size: 0.85rem; color: var(--text-muted); }
.chart-legend-item i { width: 12px; height: 3px; border-radius: 999px; display: inline-block; }

/* The stat cards along the top of the sales report. The sparkline sits
   under the figure rather than beside it, so the number is never squeezed
   - on a phone the number is the part that has to survive. */
.stat-card .spark { width: 100%; height: 40px; margin-top: 10px; display: block; }
.stat-delta { font-size: 0.82rem; font-weight: 600; margin-left: 8px; white-space: nowrap; }
.stat-delta-up { color: var(--ok-text); }
.stat-delta-down { color: var(--danger); }
.stat-delta-flat { color: var(--text-muted); }
.stat-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 2px; }

/* Which way the money went, on the review page. A coloured left edge
   rather than a coloured background: the sentence under it is the thing
   to read, and a filled block would out-shout it. Uses the existing
   status colours so it matches the rest of the system in every theme. */
.acc-verdict-dir {
  margin: 0 0 .35rem;
  font-size: .8rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
}
.acc-dir-out { color: var(--danger, #c0392b); }
.acc-dir-in { color: var(--success, #1f8f5f); }
.acc-flip {
  margin: -.4rem 0 1rem;
  padding: .75rem 1rem;
  border: 1px dashed var(--border);
  border-radius: 10px;
  background: var(--panel-bg, transparent);
}
.acc-flip .hint { margin: 0 0 .5rem; }

/* ---------- Financial statements (pos-fix-13, views/accounting/statements.js) ---------- */
.table-scroll { overflow-x: auto; }
.fs-heading { margin-bottom: 10px; }
.fs-heading h2 { margin: 2px 0; }
.fs-business { font-weight: 700; font-size: 0.95rem; }
.fs-table td.fs-indent { padding-left: 22px; }
.fs-table tr.fs-section td { font-weight: 700; color: var(--text); background: var(--muted-bg); text-transform: uppercase; font-size: 0.78rem; letter-spacing: 0.03em; }
.fs-table tr.fs-total td { font-weight: 600; border-top: 1px solid var(--border); }
.fs-table tr.fs-grand td { font-weight: 800; border-top: 2px solid var(--text); border-bottom: 2px solid var(--border); }
.fs-table tr.fs-pct td { font-size: 0.8rem; padding-top: 2px; }
.fs-table td.num { white-space: nowrap; }
.opening-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 10px 16px; }
@media print {
  .fs-panel { border: none !important; padding: 0; }
  .table-scroll { overflow: visible; }
  .fs-table { font-size: 0.8rem; }
}

/* ---------- Annual Report & Financial Statements book (pos-fix-14) ----------
   On screen: white A4-shaped sheets on the grey page. In print: exactly one
   section per sheet, nothing else from the app. Print -> "Save as PDF". */
.ar-book { display: flex; flex-direction: column; align-items: center; gap: 18px; margin: 12px 0 20px; }
.ar-page {
  background: #fff; color: #111; width: 100%; max-width: 210mm; min-height: 297mm;
  padding: 18mm 16mm; box-shadow: 0 1px 6px rgba(0,0,0,.18); border-radius: 2px;
  font-family: Georgia, "Times New Roman", serif; font-size: 10.5pt; line-height: 1.45;
}
.ar-page p { margin: 0 0 8px; text-align: justify; }
.ar-page h3 { font-size: 10.5pt; margin: 12px 0 4px; text-transform: none; }
.ar-h { font-size: 12pt; text-align: center; margin: 0 0 12px; letter-spacing: .02em; }
.ar-running { font-size: 8pt; color: #666; text-align: right; border-bottom: 1px solid #ccc; padding-bottom: 4px; margin-bottom: 14px; text-transform: uppercase; }
.ar-sub { text-align: center; font-weight: 600; margin-bottom: 10px !important; }
.ar-cover { display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; }
.ar-cover-name { font-size: 20pt; font-weight: 700; margin-bottom: 30px; }
.ar-cover-title { font-size: 15pt; font-weight: 700; }
.ar-cover-year { font-size: 13pt; margin-top: 8px; }
.ar-cover-meta { margin-top: 60px; font-size: 10pt; }
.ar-contents li { margin: 6px 0; }
.ar-info { width: 100%; border-collapse: collapse; }
.ar-info th, .ar-info td { text-align: left; vertical-align: top; padding: 5px 6px; border-bottom: 1px solid #e3e3e3; }
.ar-info th { width: 36%; font-weight: 600; }
.ar-table { width: 100%; border-collapse: collapse; margin: 6px 0 12px; }
.ar-table th, .ar-table td { padding: 4px 6px; vertical-align: top; }
.ar-table th { font-weight: 700; border-bottom: 1px solid #111; font-size: 9.5pt; }
.ar-table td.num, .ar-table th.num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; width: 22%; }
.ar-table .ar-note { width: 6%; text-align: center; color: #444; }
.ar-table tr.ar-total td { font-weight: 700; border-top: 1px solid #111; }
.ar-table tr.ar-grand td { font-weight: 700; border-top: 1px solid #111; border-bottom: 3px double #111; }
.ar-table tr.ar-section td { font-weight: 700; padding-top: 10px; }
.ar-table tr.ar-subsection td { font-style: italic; padding-top: 6px; }
.ar-narrow { max-width: 150mm; }
.ar-approve { margin-top: 18px !important; }
.ar-sigs { display: flex; flex-wrap: wrap; gap: 24px 40px; margin-top: 26px; }
.ar-sig { flex: 1 1 200px; font-size: 10pt; line-height: 1.8; }
.ar-sig-line { border-bottom: 1px solid #111; height: 40px; margin-bottom: 4px; }
.ar-placeholder { margin-top: 80px !important; text-align: center !important; color: #666; font-style: italic; }
.ar-warn-list { margin: 6px 0 0 18px; padding: 0; }
.ar-page h3 { break-after: avoid; page-break-after: avoid; }
.ar-soce td:first-child { width: 32%; }
.ar-ppe { font-size: 9pt; }
.ar-ppe td.num, .ar-ppe th.num { width: auto; }
.ar-page p.ar-small { font-size: 8.5pt; color: #333; text-align: left; }
.ar-page h3.ar-subh { font-size: 10pt; margin-top: 10px; }
.ar-soce td.num, .ar-soce th.num { width: 17%; }
@media print {
  /* The margin lives on the sheet, not the section, so a long section
     (the notes) that runs onto a second sheet keeps its margins too. */
  @page { size: A4; margin: 16mm 14mm; }
  .ar-book { display: block; margin: 0; }
  .ar-page { box-shadow: none; border-radius: 0; max-width: none; width: auto; min-height: 0; padding: 0; margin: 0; page-break-after: always; break-after: page; }
  .ar-cover { min-height: 250mm; }
  .ar-page:last-child { page-break-after: auto; break-after: auto; }
  .ar-table tr, .ar-sig, .ar-sigs { page-break-inside: avoid; break-inside: avoid; }
  body:has(.ar-book), body:has(.ar-book) .app-shell, body:has(.ar-book) .app-main, html:has(.ar-book) { background: #fff !important; }
}

/* ---------- Company documents checklist (pos-fix-17) ---------- */
.doc-checklist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.doc-check { display: flex; gap: 10px; align-items: flex-start; padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px; }
.doc-check .cell-sub { display: block; margin-top: 2px; }
.doc-mark { flex: 0 0 22px; height: 22px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-weight: 700; font-size: 0.8rem; }
.doc-have .doc-mark { background: var(--ok-bg); color: var(--primary-dark, var(--primary)); }
.doc-missing { border-color: var(--warn-border, #e0b252); }
.doc-missing .doc-mark { background: var(--warn-bg); color: #8a5a00; }
.doc-optional .doc-mark { background: var(--muted-bg); color: var(--text-muted); }

/* ---- Added in 2.0 ------------------------------------------------------ */
.status-list { list-style: none; margin: 0 0 12px; padding: 0; display: grid; gap: 6px; }
.status-list li { display: flex; gap: 8px; align-items: baseline; }
.code-block { background: var(--muted-bg); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 12px; overflow-x: auto; font-size: 13px; white-space: pre-wrap; word-break: break-all; }
.input-code { text-transform: uppercase; max-width: 8em; }
.check-row { display: flex; flex-direction: row; gap: 10px; align-items: flex-start; }
.check-row input[type="checkbox"] { margin-top: 2px; width: auto; min-height: 0; padding: 0; flex: none; }
td > .check-row { align-items: center; font-weight: 500; white-space: nowrap; }
td > .check-row input[type="checkbox"] { margin-top: 0; }
.input-narrow { max-width: 7em; }
.input-with-suffix { display: inline-flex; align-items: center; gap: 6px; }
.unit-picker { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.unit-factor { display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
.unit-factor[hidden] { display: none; }
.unit-factor-input { width: 6em; }
.extra-units { border: 1px dashed var(--border); border-radius: var(--radius); padding: 10px 12px; margin: 8px 0; }
.extra-units legend { padding: 0 6px; font-weight: 600; }
.extra-unit-row { display: flex; align-items: center; gap: 8px; margin: 6px 0; }
.extra-unit-row input[type="text"] { max-width: 12em; }
.med-fields { border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 12px; margin: 8px 0; }
.med-fields legend { padding: 0 6px; font-weight: 600; }
.stat-card-accent { border-color: var(--primary); }
.stat-inline { display: flex; align-items: baseline; gap: 10px; margin-bottom: 10px; }
.table-compact td { padding-top: 4px; padding-bottom: 4px; }
.signature-line { margin-top: 36px; }
@media print { .print-doc { border: 0; box-shadow: none; padding: 0; } }

/* Search results list (stock transfers and other page searches) */
.search-results { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; }
.search-result { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 6px 8px; border: 1px solid var(--border, #ddd); border-radius: 6px; }
.search-result .muted { margin-left: 6px; }

/* Checkout popup: customer picker and prescription */
.pos-customer { margin-top: 10px; }
.pos-cust-results { display: flex; flex-direction: column; gap: 4px; margin-top: 4px; }
.pos-cust-option { text-align: left; padding: 6px 8px; border: 1px solid var(--border, #ddd); border-radius: 6px; background: transparent; color: inherit; cursor: pointer; }
.pos-cust-option:hover, .pos-cust-option:focus { border-color: var(--primary, #2563eb); }
.pos-cust-picked { padding: 6px 0; }
.pos-cust-new { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; }
.pos-cust-new .input { flex: 1 1 140px; }
#pos-cust-ref { margin-top: 6px; }
.pos-rx { margin-top: 12px; border: 1px solid var(--border, #ddd); border-radius: 8px; padding: 8px 10px; }
.pos-rx legend { font-weight: 600; padding: 0 4px; }
.pos-rx-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 8px; }
.pos-rx-field { display: flex; flex-direction: column; gap: 2px; font-size: 0.9rem; }
#pos-confirm-error { margin-top: 10px; }
.text-danger { color: var(--danger, #dc2626); font-weight: 600; }

/* Sales kept in the browser while offline, waiting to be sent */
.offline-queue { position: fixed; left: 12px; bottom: 12px; z-index: 60; border: 0; border-radius: 999px; padding: 8px 14px; font-weight: 600; cursor: pointer; background: #b45309; color: #fff; box-shadow: 0 2px 8px rgba(0,0,0,.25); }
.offline-queue.has-failed { background: #b91c1c; }
@media print { .offline-queue { display: none !important; } }

/* A popup taller than the screen (checkout with a customer and a
   prescription on a small laptop) scrolls inside itself, so its buttons
   can always be reached. */
.modal { max-height: calc(100vh - 32px); max-height: calc(100dvh - 32px); overflow-y: auto; overscroll-behavior: contain; }


/* =====================================================================
   THE 2.2 LOOK
   ---------------------------------------------------------------------
   Laid over everything above, on screen only (@media screen) - printing a
   report, a purchase order or a receipt comes out exactly as before.
   Typefaces: Figtree for everything you read and type, Bricolage
   Grotesque for page titles and the big figures.
   ===================================================================== */
@media screen {
  html, body {
    font-family: var(--font-ui);
    font-size: 15px;
    line-height: 1.5;
    /* A touch heavier than regular: Figtree's thin strokes otherwise read
       as faded on an ordinary shop screen (2.2.2). */
    font-weight: 450;
    text-rendering: optimizeLegibility;
  }
  strong, b { font-weight: 700; }
  input, select, textarea, button { font-family: inherit; }
  h1 { font-family: var(--font-display); font-weight: 700; font-size: 1.8rem; letter-spacing: -0.02em; line-height: 1.12; }
  h2 { font-size: 1.07rem; font-weight: 700; letter-spacing: -0.005em; }
  h3 { font-weight: 700; }
  a { color: var(--primary-dark); }
  ::selection { background: color-mix(in srgb, var(--primary) 26%, transparent); }
  input[type="checkbox"], input[type="radio"] { accent-color: var(--primary); }

  /* ---- shell ------------------------------------------------------- */
  .page { padding: 26px 30px 48px; max-width: 1280px; }

  /* No line between the menu and the page (2.2.3, owner's request): the
     menu either shares the page's own colour (Mwanga, Giza) or is a block
     of its own colour (Zanzibar, Tanzanite, Serengeti) - never a ruled
     edge. */
  .sidebar {
    width: 252px;
    background: var(--sidebar-bg);
    border-right: 0;
  }
  .sidebar::before {
    content: '';
    position: absolute;
    inset: 0;
    background: var(--sidebar-glow);
    pointer-events: none;
  }
  .sidebar-brand, .sidebar-nav, .sidebar-credit { position: relative; }
  .sidebar-brand { padding: 18px 40px 14px 18px; gap: 11px; }
  .brand-mark {
    width: 34px;
    height: 34px;
    border-radius: 11px;
    background: var(--grad-primary);
    color: var(--primary-contrast);
    font-family: var(--font-display);
    font-weight: 800;
    font-size: 0.95rem;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18);
  }
  .brand-mark.large { width: 60px; height: 60px; border-radius: 18px; font-size: 1.45rem; }
  .brand-name { font-family: var(--font-display); font-weight: 700; font-size: 1.1rem; letter-spacing: -0.01em; color: var(--sidebar-text); }

  .sidebar-nav { padding: 6px 12px 10px; gap: 3px; }
  .nav-link {
    gap: 12px;
    padding: 9px 12px;
    border-radius: 12px;
    font-weight: 580;
    font-size: 0.92rem;
    color: var(--sidebar-text-muted);
    transition: background-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
  }
  .nav-link:hover { background: var(--sidebar-hover-bg); color: var(--sidebar-text); }
  /* The page you are on: a soft tint with a coloured edge - calm, and
     never mistaken for the solid Sell button. */
  .nav-link.active {
    background: var(--sidebar-active-bg);
    color: var(--sidebar-active-text);
    font-weight: 700;
    box-shadow: inset 3px 0 0 var(--nav-bar);
  }
  /* The group holding the page you are on: its name reads as "you are in
     here", the page itself carries the highlight. */
  .nav-group-summary.active,
  .nav-group-summary.active:hover {
    background: transparent;
    color: var(--sidebar-text);
    box-shadow: none;
  }
  .nav-group-items {
    margin: 2px 0 6px 21px;
    padding: 0 0 0 10px;
    border-left: 1px solid color-mix(in srgb, var(--sidebar-text) 14%, transparent);
  }
  .nav-group-items .nav-link { font-size: 0.88rem; padding: 8px 11px; }
  .nav-link-primary,
  .nav-link-primary.active {
    background: var(--grad-primary);
    color: var(--primary-contrast);
    font-weight: 700;
    margin: 2px 0 10px;
    box-shadow: 0 2px 6px -2px rgba(0, 0, 0, 0.25);
  }
  .nav-link-primary:hover { background: var(--grad-primary); color: var(--primary-contrast); filter: brightness(0.94); }
  .nav-badge { box-shadow: 0 0 0 2px color-mix(in srgb, var(--danger) 25%, transparent); }

  .sidebar-bottom { border-top: 0; padding: 10px 12px 6px; }
  .settings-toggle { color: var(--sidebar-text); border-radius: 12px; }
  .settings-toggle:hover { background: var(--sidebar-hover-bg); }
  .settings-toggle-role, .settings-gear { color: var(--sidebar-text-muted); }
  .settings-avatar {
    width: 36px;
    height: 36px;
    background: var(--grad-primary);
    color: var(--primary-contrast);
    font-weight: 750;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--sidebar-text) 14%, transparent);
  }
  .sidebar-credit { color: var(--sidebar-text-muted); }
  .sidebar-collapse-btn { background: var(--surface); color: var(--text-muted); border-color: var(--border); }

  .settings-dropdown { border-radius: 16px; padding: 8px; box-shadow: var(--shadow-lg); }
  .settings-group-label { text-transform: none; letter-spacing: 0; font-size: 0.78rem; }
  .settings-item { border-radius: 10px; }
  .theme-picker { display: flex; gap: 8px; padding: 6px 10px 8px; flex-wrap: wrap; }
  .theme-swatch {
    width: 30px;
    height: 30px;
    border-radius: 999px;
    border: 2px solid var(--surface);
    box-shadow: 0 0 0 1px var(--border-strong);
    flex-shrink: 0;
  }
  .theme-swatch:hover { transform: scale(1.08); text-decoration: none; }
  .theme-swatch.is-current { box-shadow: 0 0 0 2px var(--primary); }
  .theme-swatch[data-swatch="light"] { background: linear-gradient(135deg, #f2f5f4 0 46%, #0a8560 46%, #0284c7 72%, #d97706 100%); }
  .theme-swatch[data-swatch="dark"] { background: linear-gradient(135deg, #0c1412 0 46%, #30b383 46%, #7dd3fc 72%, #c4b5fd 100%); }
  .theme-swatch[data-swatch="blue"] { background: linear-gradient(135deg, #0c3a52 0 46%, #0d9488 46%, #e2604f 72%, #c47f10 100%); }
  .theme-swatch[data-swatch="midnight"] { background: linear-gradient(135deg, #1a1534 0 46%, #6d4fd8 46%, #93c5fd 72%, #f9a8d4 100%); }
  .theme-swatch[data-swatch="sand"] { background: linear-gradient(135deg, #33241a 0 46%, #a8471c 46%, #a16207 72%, #4d7c0f 100%); }
  .theme-current { font-weight: 700; color: var(--text); }

  /* ---- top bar ----------------------------------------------------- */
  /* The top bar is the page itself, carried on as it scrolls - same
     colour, no rule under it. */
  .topbar {
    min-height: 62px;
    padding: 10px 26px;
    gap: 10px;
    background: color-mix(in srgb, var(--bg) 88%, transparent);
    -webkit-backdrop-filter: saturate(1.2) blur(12px);
    backdrop-filter: saturate(1.2) blur(12px);
    border-bottom: 0;
  }
  /* The pindo: a kanga's patterned border, in this theme's colours. */
  /* The pindo runs across the whole window, menu included, so nothing
     breaks where the menu meets the page. */
  .app-shell:not(.no-shell)::before {
    content: '';
    position: fixed;
    left: 0;
    right: 0;
    top: 0;
    height: 2px;
    background: var(--pindo);
    opacity: 0.85;
    z-index: 45;
    pointer-events: none;
  }
  .topbar-title { font-size: 0.95rem; font-weight: 650; color: var(--text-muted); }
  /* The page's own heading says the same thing, larger, just below. */
  .app-main:has(.page-head h1) .topbar-title { display: none; }
  .topbar-back {
    height: 38px;
    padding: 0 14px 0 9px;
    border-color: var(--border-strong);
    background: var(--surface);
    font-weight: 650;
    transition: background-color 0.15s ease, border-color 0.15s ease;
  }
  .topbar-back:hover { background: var(--surface-2); border-color: color-mix(in srgb, var(--primary) 45%, var(--border-strong)); }
  .topbar-bell, .topbar-icon-btn { width: 38px; height: 38px; transition: background-color 0.15s ease, color 0.15s ease; }
  .topbar-bell:hover, .topbar-bell.active, .topbar-icon-btn:hover { background: var(--muted-bg); }
  .topbar-bell-badge { top: 3px; right: 3px; box-shadow: 0 0 0 2px var(--surface); }
  .topbar-branch-label {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 0.82rem;
    font-weight: 650;
    color: var(--text);
    padding: 6px 12px;
    border-radius: 999px;
    background: var(--surface);
    border: 1px solid var(--border);
  }
  .topbar-branch-label::before {
    content: '';
    width: 8px;
    height: 8px;
    border-radius: 999px;
    background: var(--a1);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--a1) 22%, transparent);
  }
  .topbar-branch-select { font-size: 0.82rem; padding: 7px 12px; background: var(--surface); border-color: var(--border); max-width: 170px; }
  .topbar-user {
    font-size: 0.82rem;
    font-weight: 700;
    color: var(--text);
    background: var(--muted-bg);
    padding: 6px 13px;
  }
  .sidebar-toggle-btn { width: 38px; height: 38px; border-radius: 12px; border-color: var(--border-strong); }

  /* ---- page heading ------------------------------------------------ */
  .page-head { margin-bottom: 22px; gap: 14px; align-items: center; }
  .page-head.compact { margin-bottom: 16px; }
  .page-head-actions { gap: 10px; }
  .page-head + .hint, .page-head.compact + .hint { margin-top: -10px; margin-bottom: 16px; }

  /* ---- surfaces ---------------------------------------------------- */
  .panel {
    border-radius: var(--radius-lg);
    padding: 22px 22px;
    border: 1px solid var(--border);
    box-shadow: var(--shadow);
    margin-bottom: 22px;
  }
  .panel > h2:first-child, .panel-head h2 { margin-bottom: 12px; }
  .grid-2, .grid-table-form { gap: 22px; }

  /* ---- buttons ----------------------------------------------------- */
  .btn {
    min-height: 42px;
    padding: 10px 18px;
    border-radius: 12px;
    font-weight: 650;
    font-size: 0.92rem;
    transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease, filter 0.15s ease, transform 0.1s ease;
  }
  .btn:active:not(:disabled) { transform: translateY(1px); }
  .btn:focus-visible { outline: none; box-shadow: 0 0 0 4px var(--ring); }
  .btn-primary {
    background: var(--grad-primary);
    color: var(--primary-contrast);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
  }
  .btn-primary:hover { background: var(--grad-primary); filter: brightness(0.93); }
  .btn-primary:disabled { background: var(--muted-bg); color: var(--text-muted); box-shadow: none; filter: none; }
  .btn-secondary { background: var(--surface); color: var(--text); border-color: var(--border-strong); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04); }
  .btn-secondary:hover { background: var(--surface-2); border-color: color-mix(in srgb, var(--primary) 45%, var(--border-strong)); }
  .btn-danger { background: var(--danger); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12); }
  .btn-sm { min-height: 34px; padding: 6px 13px; border-radius: 10px; font-size: 0.84rem; }
  .btn-lg { border-radius: 14px; }

  /* ---- forms ------------------------------------------------------- */
  label { font-size: 0.86rem; gap: 6px; }
  .input,
  label input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
  label select,
  label textarea {
    min-height: 44px;
    padding: 10px 13px;
    border-radius: 12px;
    border: 1px solid var(--border-strong);
    background: var(--surface);
    color: var(--text);
    font-size: 0.95rem;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
  }
  .input:hover,
  label input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):hover,
  label select:hover,
  label textarea:hover { border-color: color-mix(in srgb, var(--primary) 35%, var(--border-strong)); }
  input:not([type="checkbox"]):not([type="radio"]):focus,
  select:focus,
  textarea:focus,
  .input:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 4px var(--ring);
  }
  input[type="checkbox"]:focus-visible, input[type="radio"]:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
  .form-section-title { font-size: 1rem; }
  .hint { font-size: 0.86rem; }

  /* ---- tables ------------------------------------------------------ */
  .table { border-collapse: separate; border-spacing: 0; font-size: 0.91rem; }
  .table th {
    text-transform: none;
    letter-spacing: 0;
    font-size: 0.82rem;
    font-weight: 700;
    color: var(--text-muted);
    background: var(--surface-2);
    border-bottom: 1px solid var(--border);
    padding: 11px 12px;
  }
  .table thead tr:first-child th:first-child { border-top-left-radius: 12px; }
  .table thead tr:first-child th:last-child { border-top-right-radius: 12px; }
  .table td { padding: 12px 12px; border-bottom: 1px solid var(--border); }
  .table tbody tr { transition: background-color 0.12s ease; }
  .table tbody tr:hover > td { background: var(--row-hover); }
  .table tr.row-warn > td, .table tbody tr.row-warn:hover > td { background: var(--warn-bg); }
  .th-sort > a { padding: 11px 12px; }
  .th-sort-active > a { color: var(--primary-dark); }
  td.cell-code { font-family: var(--font-ui); font-variant-numeric: tabular-nums; font-weight: 650; color: var(--text-muted); }
  .cell-code:not(td) {
    font-family: var(--font-ui);
    font-variant-numeric: tabular-nums;
    font-size: 0.74rem;
    font-weight: 650;
    color: var(--text-muted);
    background: var(--muted-bg);
    padding: 1px 6px;
    border-radius: 6px;
    margin-right: 4px;
  }
  /* A row's actions as a neat row of small buttons rather than a stack of
     bare links. */
  td.actions { gap: 6px; }
  .table td.actions > a,
  .table td.actions > form > .link-btn,
  .table td.actions > .inline-form > .link-btn {
    display: inline-flex;
    align-items: center;
    min-height: 30px;
    padding: 4px 10px;
    margin: 0;
    border-radius: 9px;
    border: 1px solid var(--border);
    background: var(--surface);
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--primary-dark);
    text-decoration: none;
    white-space: nowrap;
  }
  .table td.actions > a:hover,
  .table td.actions > form > .link-btn:hover,
  .table td.actions > .inline-form > .link-btn:hover { border-color: color-mix(in srgb, var(--primary) 45%, var(--border)); background: var(--surface-2); }
  .table td.actions > .link-btn-danger, .table td.actions .link-btn-danger { color: var(--danger); }

  /* ---- badges, chips, flashes ------------------------------------- */
  .badge { padding: 3px 10px; font-size: 0.76rem; font-weight: 650; border: 1px solid transparent; }
  .badge-ok { border-color: color-mix(in srgb, var(--ok-text) 22%, transparent); }
  .badge-warn { border-color: var(--warn-border); }
  .badge-muted { border-color: var(--border); }
  .badge-new { background: var(--grad-primary); color: var(--primary-contrast); }
  .chip { padding: 7px 15px; font-weight: 650; transition: background-color 0.15s ease, border-color 0.15s ease; }
  .chip:hover { border-color: color-mix(in srgb, var(--primary) 45%, var(--border)); text-decoration: none; }
  .chip-active, .chip-active:hover { background: var(--grad-primary); color: var(--primary-contrast); border-color: transparent; }
  .flash { border-radius: 12px; padding: 12px 16px; border-left-width: 4px; font-weight: 550; }
  .flash-error { background: var(--danger-bg); color: var(--danger); border-color: color-mix(in srgb, var(--danger) 30%, transparent); border-left-color: var(--danger); }
  .flash-info { border-color: color-mix(in srgb, var(--ok-text) 25%, transparent); border-left-color: var(--ok-text); }
  .flash-warn { border-left-color: var(--warn-text); }
  .pager-page { border-radius: 10px; }
  .pager-current { background: var(--grad-primary); border-color: transparent; color: var(--primary-contrast); }

  /* ---- figures: stat cards, setup and report cards ----------------- */
  .stat-grid { gap: 16px; margin-bottom: 24px; }
  .stat-card,
  .pos-setup-card,
  .report-picker-card,
  .compare-card { --c: var(--a1); }
  :is(.stat-grid, .pos-setup-grid, .report-picker-grid, .compare-grid) > :nth-child(5n + 2) { --c: var(--a2); }
  :is(.stat-grid, .pos-setup-grid, .report-picker-grid, .compare-grid) > :nth-child(5n + 3) { --c: var(--a3); }
  :is(.stat-grid, .pos-setup-grid, .report-picker-grid, .compare-grid) > :nth-child(5n + 4) { --c: var(--a4); }
  :is(.stat-grid, .pos-setup-grid, .report-picker-grid, .compare-grid) > :nth-child(5n + 5) { --c: var(--a5); }
  .stat-card {
    position: relative;
    border-radius: 16px;
    padding: 16px 18px 17px;
    background: var(--surface);
  }
  .stat-label {
    display: flex;
    align-items: center;
    gap: 8px;
    text-transform: none;
    letter-spacing: 0;
    font-size: 0.86rem;
    font-weight: 650;
    margin-bottom: 8px;
  }
  .stat-label::before {
    content: '';
    width: 9px;
    height: 9px;
    border-radius: 3px;
    background: var(--c);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--c) 20%, transparent);
    flex-shrink: 0;
  }
  .stat-value { font-family: var(--font-display); font-weight: 700; font-size: clamp(1.25rem, 2.3vw, 1.75rem); letter-spacing: -0.015em; }
  .stat-value .money-cur { font-family: var(--font-ui); font-size: 0.5em; vertical-align: 0.35em; }
  .stat-card-accent { border-color: color-mix(in srgb, var(--c) 45%, var(--border)); }
  .perf-item { border-radius: 14px; background: var(--surface-2); border: 1px solid var(--border); }
  .perf-label, .compare-card h3, .compare-period-group h3, .pos-results-heading { text-transform: none; letter-spacing: 0; }
  .perf-value { font-family: var(--font-display); font-size: 1.35rem; letter-spacing: -0.01em; }
  .money-lg { font-family: var(--font-display); }
  .compare-card { border-radius: 14px; border-top: 3px solid var(--c); }

  .pos-setup-grid, .report-picker-grid { gap: 16px; }
  .pos-setup-card {
    gap: 6px;
    padding: 18px;
    border-radius: 16px;
    transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
  }
  .pos-setup-card:hover,
  .report-picker-card:hover {
    border-color: color-mix(in srgb, var(--c) 55%, var(--border));
    box-shadow: var(--shadow);
    transform: translateY(-1px);
  }
  .card-icon,
  .report-picker-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 42px;
    height: 42px;
    border-radius: 13px;
    margin-bottom: 6px;
    color: var(--c);
    background: color-mix(in srgb, var(--c) 14%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c) 24%, transparent);
  }
  .card-icon svg { width: 21px; height: 21px; }
  .pos-setup-card-title, .report-picker-title { font-weight: 700; font-size: 1rem; }

  /* ---- selling ----------------------------------------------------- */
  .pos-result-item { border-radius: 14px; padding: 12px 15px; transition: border-color 0.12s ease, background-color 0.12s ease; }
  .pos-result-item:hover { border-color: color-mix(in srgb, var(--primary) 40%, var(--border)); }
  .pos-pay-btn.is-selected, .pos-pay-btn.is-selected:hover { background: var(--grad-primary); border-color: transparent; color: var(--primary-contrast); }
  .cart-total-row { font-family: var(--font-display); font-size: 1.3rem; letter-spacing: -0.01em; }
  .qty-btn, .qty-input, .unit-select, .cart-line-total-input { border-radius: 8px; }

  /* ---- popups ------------------------------------------------------ */
  .modal { border-radius: 20px; box-shadow: var(--shadow-lg); border: 1px solid var(--border); }
  .modal-backdrop, dialog.form-dialog::backdrop { -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); }
  dialog.form-dialog { border-radius: 20px; box-shadow: var(--shadow-lg); border: 1px solid var(--border); }
  .form-dialog-head { padding: 18px 22px; }
  .form-dialog-head h2 { font-family: var(--font-display); font-size: 1.2rem; }
  .form-dialog-body { padding: 18px 22px 22px; }
  .greeting-popup { background: var(--grad-primary); color: var(--primary-contrast); border-radius: 14px; box-shadow: var(--shadow-lg); }

  /* ---- moving between pages --------------------------------------- */
  /* Each page's content rises into place; the sidebar and the top bar
     stay where they are. While the next page is on its way the current
     one dims a little, after a short pause so a quick page never flickers. */
  main.page { animation: page-in 0.32s cubic-bezier(0.2, 0.75, 0.25, 1) backwards; }
  html.nav-busy main.page { opacity: 0.55; transition: opacity 0.2s ease 0.12s; }
  html.nav-busy::before { height: 3px; background: var(--pindo); }
  .sidebar { view-transition-name: pos-sidebar; }
  .topbar { view-transition-name: pos-topbar; }
}
@keyframes page-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}
::view-transition-old(root) { animation: 0.14s ease-out both vt-out; }
::view-transition-new(root) { animation: 0.3s cubic-bezier(0.2, 0.75, 0.25, 1) both vt-in; }
::view-transition-group(pos-sidebar), ::view-transition-group(pos-topbar) { animation: none; }
@keyframes vt-out { to { opacity: 0; } }
@keyframes vt-in { from { opacity: 0; transform: translateY(6px); } }

/* ---- phones and small tablets ---------------------------------------- */
@media screen and (max-width: 900px) {
  .page { padding: 18px 16px 40px; }
  .topbar { padding: 8px 14px; gap: 8px; min-height: 58px; }
  .sidebar { width: 272px; }
}
@media screen and (max-width: 600px) {
  h1 { font-size: 1.5rem; }
  .topbar-user { display: none; }
  .topbar-branch-label { padding: 5px 10px; max-width: 42vw; overflow: hidden; text-overflow: ellipsis; }
  .panel { padding: 16px; border-radius: 16px; }
  .stat-grid { gap: 12px; }
  .stat-card { padding: 14px 14px 15px; }
  .page-head-actions { width: 100%; }
  .page-head-actions .btn { flex: 1 1 auto; }
}

/* ---- sign-in, lock screen and the plain message pages (2.2) ---------- */
@media screen {
  .page.no-nav:has(.login-split) { max-width: 960px; margin-top: 7vh; padding: 20px; }
  .login-split {
    display: grid;
    grid-template-columns: 1.05fr 1fr;
    border-radius: 26px;
    overflow: hidden;
    background: var(--surface);
    border: 1px solid var(--border);
    box-shadow: var(--shadow-lg);
  }
  .login-hero {
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: 28px;
    min-height: 460px;
    padding: 36px 36px 40px;
    color: #ffffff;
    background:
      radial-gradient(300px 240px at 90% 8%, color-mix(in srgb, var(--a2) 22%, transparent), transparent 70%),
      var(--hero-bg);
    isolation: isolate;
  }
  .login-hero::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 6px;
    background: var(--pindo);
  }
  .login-hero .brand-mark.large {
    margin: 0;
    background: rgba(255, 255, 255, 0.16);
    color: #ffffff;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.28), 0 12px 30px -12px rgba(0, 0, 0, 0.5);
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
  }
  .login-hero-title {
    margin: 0 0 10px;
    font-family: var(--font-display);
    font-size: clamp(2rem, 4vw, 2.7rem);
    font-weight: 800;
    letter-spacing: -0.03em;
    line-height: 1.02;
    color: #ffffff;
  }
  .login-hero-sub { margin: 0; max-width: 30ch; font-size: 1.02rem; line-height: 1.5; color: rgba(255, 255, 255, 0.86); }
  .login-split .login-card {
    border: 0;
    border-radius: 0;
    box-shadow: none;
    padding: 46px 42px 34px;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
  .login-split .login-brand { text-align: left; margin-bottom: 22px; }
  .login-split .login-brand h1 { font-size: 1.75rem; margin-bottom: 6px; }
  .login-split .login-brand p { margin: 0; }
  .login-lang { margin-top: 18px; }
  /* The lock screen keeps its single card. */
  .login-wrap .login-card { border-radius: 22px; padding: 34px 30px; box-shadow: var(--shadow-lg); }
  .login-wrap .login-brand h1 { font-size: 1.45rem; }

  .msg-page { text-align: center; padding: 40px 30px 34px; border-radius: 22px; box-shadow: var(--shadow-lg); }
  .msg-page .msg-code {
    display: block;
    font-family: var(--font-display);
    font-size: 3.4rem;
    font-weight: 800;
    letter-spacing: -0.04em;
    line-height: 1;
    margin-bottom: 10px;
    background: var(--grad-primary);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
  .msg-page h1 { font-size: 1.4rem; margin-bottom: 8px; }
  .msg-page p { color: var(--text-muted); margin: 0 auto 22px; max-width: 34ch; }
}
@media screen and (max-width: 760px) {
  .page.no-nav:has(.login-split) { margin-top: 0; padding: 0; max-width: none; }
  .login-split { grid-template-columns: 1fr; border-radius: 0; border: 0; min-height: 100dvh; box-shadow: none; align-content: start; }
  .login-hero { min-height: 0; padding: 28px 22px 30px; flex-direction: row; align-items: center; gap: 16px; }
  .login-hero .brand-mark.large { width: 52px; height: 52px; border-radius: 16px; font-size: 1.2rem; flex-shrink: 0; }
  .login-hero-title { font-size: 1.6rem; margin-bottom: 4px; }
  .login-hero-sub { font-size: 0.9rem; }
  .login-split .login-card { padding: 28px 22px 30px; justify-content: flex-start; }
}

@media screen {
  /* "Low stock only" and similar one-tick filters, as a toggle chip. */
  .inv-low-toggle {
    flex-direction: row;
    align-items: center;
    gap: 8px;
    min-height: 44px;
    padding: 0 16px;
    border-radius: 999px;
    border: 1px solid var(--border-strong);
    background: var(--surface);
    color: var(--text);
    font-weight: 600;
    transition: background-color 0.15s ease, border-color 0.15s ease;
  }
  .inv-low-toggle:hover { border-color: color-mix(in srgb, var(--primary) 45%, var(--border-strong)); }
  .inv-low-toggle:has(input:checked) { background: var(--ok-bg); border-color: color-mix(in srgb, var(--ok-text) 40%, transparent); color: var(--ok-text); }
  .check-line, .checkbox-label { flex-direction: row; }
  .inv-filters { gap: 12px; margin-bottom: 18px; }
  .inv-search-input { max-width: 300px; }
}

@media screen {
  /* A table cell stays a table cell (a flex <td> lost the row's own
     background, e.g. the yellow of a low-stock row); its buttons flow and
     wrap inside it. */
  .table td.actions { display: table-cell; white-space: normal; }
  .table td.actions > * { margin: 0 6px 6px 0; vertical-align: middle; }
  .table td.actions > .inline-form { display: inline-block; }
  .table td.actions form[action$="/deactivate"] .link-btn { color: var(--danger); }
  .inv-table th:nth-child(1), .inv-table td:nth-child(1) { width: 23%; }
  .inv-table th:nth-child(2), .inv-table td:nth-child(2) { width: 9%; }
  .inv-table th:nth-child(3), .inv-table td:nth-child(3) { width: 11%; }
  .inv-table th:nth-child(4), .inv-table td:nth-child(4) { width: 11%; }
  .inv-table th:nth-child(6), .inv-table td:nth-child(6) { width: 10%; }
  .inv-table th:nth-child(7), .inv-table td:nth-child(7) { width: 24%; }
  /* Prices edited down the list keep reading as part of the row until
     touched (see the Inventory rules above); the 2.2 box look must not
     outline every one of them. */
  .inv-price-input { min-height: 36px; border-radius: 10px; background: transparent; border-color: transparent; }
  .inv-price-input.inv-price-changed { background: var(--warn-bg); border-color: var(--warn-border); }
  .app-shell.no-shell .app-main { background: none; }
}

@media screen {
  /* The till's "Add" buttons: the one action on each result, tinted so the
     eye finds them down the list. */
  .pos-results .btn-secondary {
    background: color-mix(in srgb, var(--primary) 10%, var(--surface));
    border-color: color-mix(in srgb, var(--primary) 28%, transparent);
    color: var(--primary-dark);
  }
  .pos-results .btn-secondary:hover { background: color-mix(in srgb, var(--primary) 16%, var(--surface)); }
}

@media screen {
  /* Row headings inside a table body (a number column) read as part of
     the row, not as a second header. */
  .table tbody th { background: transparent; border-radius: 0; font-weight: 600; }
  .po-budget-label, .acc-verdict-dir { text-transform: none; letter-spacing: 0; }
  .po-budget-panel { background: var(--surface-2); }
  .po-budget-total { font-family: var(--font-display); font-size: 1.35rem; }

  /* Any plain box typed into - small ones inside table rows included -
     shares the same rounded look. Zero specificity (:where), so every
     box with its own size or style keeps it. */
  :where(input:not([type="checkbox"], [type="radio"], [type="file"], [type="hidden"], [type="range"], [type="color"]), select, textarea) {
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    padding: 7px 10px;
    font-size: 0.93rem;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
  }
  :where(select) { padding-right: 28px; }
  fieldset { border: 1px solid var(--border); border-radius: 14px; }
  legend { font-weight: 700; padding: 0 6px; }
}

@media screen {
  .topbar-branch-label::before { flex-shrink: 0; }
}
/* Long lists on a phone (2.2): each row becomes a small card with its own
   labels, instead of a table squeezed to a third of its width. A table
   opts in with .table-stack and a data-label on each cell. */
@media screen and (max-width: 640px) {
  .panel:has(> .table-stack) { background: none; border: 0; box-shadow: none; padding: 0; overflow: visible; }
  .table-stack, .table-stack tbody { display: block; width: 100%; }
  .table-stack thead { display: none; }
  .table-stack tbody tr {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px 14px;
    padding: 14px 15px;
    margin-bottom: 12px;
    border: 1px solid var(--border);
    border-radius: 16px;
    background: var(--surface);
    box-shadow: var(--shadow);
  }
  .table-stack tbody tr.row-warn { background: var(--warn-bg); border-color: var(--warn-border); }
  .table.table-stack td,
  .table.table-stack tr.row-warn > td,
  .table.table-stack tbody tr:hover > td { display: block; width: auto; padding: 0; border: 0; background: transparent; text-align: left; }
  .table-stack td[data-label]::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 4px;
    font-size: 0.76rem;
    font-weight: 600;
    color: var(--text-muted);
  }
  .table-stack td.stack-title { grid-column: 1 / -1; font-size: 1rem; }
  .table.table-stack td.actions { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 8px; padding-top: 4px; }
  .table.table-stack td.actions > * { margin: 0; }
  /* Category on one short line, so buying and selling price sit side by
     side below it. */
  .table.inv-table.table-stack td:nth-child(2) { grid-column: 1 / -1; display: flex; gap: 6px; align-items: baseline; margin-top: -4px; }
  .table.inv-table.table-stack td:nth-child(2)::before { display: inline; margin: 0; }
  .table-stack .inv-price-input { width: 100%; max-width: none; text-align: left; background: var(--surface); border-color: var(--border-strong); }
  .table-stack .inv-price-input.inv-price-changed { background: var(--warn-bg); border-color: var(--warn-border); }
}

.pos-confirm-total { font-size: 1.4rem; margin: 0 0 6px; }
@media screen {
  /* The figure the cashier reads out to the customer. */
  .pos-confirm-total { font-family: var(--font-display); font-size: 2.3rem; font-weight: 800; letter-spacing: -0.02em; line-height: 1.1; }
  .modal h2 { font-family: var(--font-display); font-size: 1.3rem; }
  .table td.muted[colspan] { text-align: center; padding: 26px 12px; }
}

@media screen {
  /* Filter rows: boxes side by side, wrapping when there is no room,
     rather than each taking the full width. */
  .inv-filters > .input { width: auto; flex: 1 1 220px; }
  .inv-filters > input[type="date"].input { flex: 0 1 180px; }
  .form-row > input[type="search"], .form-row > input[type="text"] { flex: 1 1 220px; min-height: 42px; }
  .stat-inline { display: flex; align-items: baseline; gap: 10px; margin: 6px 0 14px; }
}

/* The AS Enterprises leaf (views/partials/logo.js, 2.2.6) draws its own
   shape in the colours of the theme it sits in - the same solid colour as
   the main buttons - so it belongs to every theme instead of sitting on
   top of it. Beside it: "AS Enterprises" with "Pharmacy POS" beneath. */
:root {
  --brand-tile: var(--grad-primary);
  --brand-ink: var(--primary-contrast);
  --brand-sprout: color-mix(in srgb, var(--primary-contrast) 72%, transparent);
  --hero-ink: #0a8560;
}
:root[data-theme="dark"] { --hero-ink: #0b5d4a; }
:root[data-theme="blue"] { --hero-ink: #0a5f9e; }
:root[data-theme="midnight"] { --hero-ink: #6d4fd8; }
:root[data-theme="sand"] { --hero-ink: #a8471c; }
@media (prefers-color-scheme: dark) { :root:not([data-theme]) { --hero-ink: #0b5d4a; } }
.brand-mark .brand-svg { display: block; width: 100%; height: 100%; }
@media screen {
  .brand-mark,
  .brand-mark.large,
  .login-hero .brand-mark.large { background: none; box-shadow: none; border-radius: 0; -webkit-backdrop-filter: none; backdrop-filter: none; }
  .brand-mark { width: 38px; height: 38px; }
  .brand-mark.large { width: 68px; height: 68px; }
  .sidebar-brand { gap: 12px; padding: 18px 40px 16px 18px; }
  .brand-name { display: flex; flex-direction: column; min-width: 0; line-height: 1.15; }
  .brand-title {
    font-family: var(--font-display);
    font-weight: 800;
    font-size: 1.06rem;
    letter-spacing: -0.015em;
    color: var(--sidebar-text);
    white-space: nowrap;
  }
  .brand-sub { font-size: 0.76rem; font-weight: 600; color: var(--sidebar-text-muted); white-space: nowrap; }
  /* On the coloured sign-in panel: a white leaf, letters in the theme's
     own deep colour. */
  .login-hero .brand-mark { --brand-tile: #ffffff; --brand-ink: var(--hero-ink); --brand-sprout: color-mix(in srgb, var(--hero-ink) 55%, transparent); }
  .login-hero-product { margin: 0 0 14px; font-size: 1.05rem; font-weight: 700; color: rgba(255, 255, 255, 0.92); }
}

/* ---- Two-step sign-in (2.3.0, views/two-factor.js) ---------------------- */
.tf-code-input {
  font-family: var(--font-display), var(--font-ui), sans-serif;
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: 0.32em;
  text-align: center;
}
.tf-intro p, .tf-on p { margin: 12px 0; }
.tf-points { margin: 0 0 18px; padding-left: 20px; display: grid; gap: 6px; color: var(--text-muted); }
.tf-steps { margin: 0; padding-left: 22px; display: grid; gap: 22px; }
.tf-steps > li::marker { font-weight: 800; color: var(--primary); }
.tf-steps p { margin: 4px 0 10px; color: var(--text-muted); }
/* Always dark on white, whatever the theme: that is what cameras read. */
.tf-qr {
  display: inline-block;
  padding: 10px;
  background: #ffffff;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  line-height: 0;
}
.tf-qr svg { width: min(220px, 62vw); height: auto; }
.tf-secret code, .tf-codes code {
  font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
  font-size: 1.02rem;
  letter-spacing: 0.06em;
  overflow-wrap: anywhere;
}
.tf-secret code { display: inline-block; padding: 8px 12px; border-radius: var(--radius-sm); background: var(--muted-bg); }
.tf-cancel { margin-top: 18px; }
.tf-action .hint { margin: 4px 0 14px; }
.tf-login-backup { margin-top: 14px; text-align: center; }
.tf-codes {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 8px 18px;
  margin: 14px 0;
  padding-left: 26px;
}
.tf-codes li { padding: 4px 0; }
@media print {
  .tf-codes-panel .form-actions, .tf-codes-panel .hint { display: none !important; }
}

/* ---- Forgotten password by email (2.3.0, views/password-reset.js) ------- */
.login-forgot { margin: 14px 0 0; text-align: center; font-size: 0.92rem; font-weight: 600; }
.pr-staff-hint { margin-top: 14px; text-align: center; }
.pr-status { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 12px 0; }
.pr-remove { margin-top: 14px; }
.pr-detail { margin-top: 6px; font-size: 0.82rem; opacity: 0.85; overflow-wrap: anywhere; font-family: ui-monospace, Consolas, monospace; }
.pr-remove summary { display: inline-block; cursor: pointer; list-style: none; }
.pr-remove summary::-webkit-details-marker { display: none; }
.pr-remove form { margin-top: 12px; }

/* ---- Inventory: stock added on a date (2.3.1) --------------------------- */
.inv-date-filter {
  display: flex;
  flex: 1 1 100%;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 10px;
  padding: 8px 10px 8px 16px;
  min-height: 44px;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-strong);
  background: var(--surface);
}
.inv-date-filter.is-on { background: var(--ok-bg); border-color: color-mix(in srgb, var(--ok-text) 40%, transparent); }
.inv-date-label { font-weight: 600; font-size: 0.9rem; white-space: nowrap; }
.inv-date-filter input[type="date"].input { width: auto; min-height: 34px; padding: 4px 10px; flex: 0 1 160px; }
.inv-date-dash { color: var(--text-muted); }
.inv-date-quick { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.inv-date-chip {
  padding: 5px 11px;
  border-radius: 999px;
  border: 1px solid var(--border);
  font-size: 0.84rem;
  font-weight: 600;
  text-decoration: none;
  color: var(--text);
  background: var(--surface);
  white-space: nowrap;
}
.inv-date-chip:hover { border-color: var(--primary); }
.inv-date-chip.is-current { background: var(--primary); border-color: var(--primary); color: var(--primary-contrast); }
.inv-date-clear {
  display: inline-grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 999px;
  font-size: 1.1rem;
  line-height: 1;
  text-decoration: none;
  color: var(--text-muted);
}
.inv-date-clear:hover { background: var(--muted-bg); color: var(--text); }
.inv-added strong { color: var(--ok-text); white-space: nowrap; }
/* One more column while the date filter is on: the widths, re-shared. */
@media screen {
  .inv-table.inv-table-added th:nth-child(1), .inv-table.inv-table-added td:nth-child(1) { width: 20%; }
  .inv-table.inv-table-added th:nth-child(2), .inv-table.inv-table-added td:nth-child(2) { width: 8%; }
  .inv-table.inv-table-added th:nth-child(3), .inv-table.inv-table-added td:nth-child(3) { width: 10%; }
  .inv-table.inv-table-added th:nth-child(4), .inv-table.inv-table-added td:nth-child(4) { width: 10%; }
  .inv-table.inv-table-added th:nth-child(5), .inv-table.inv-table-added td:nth-child(5) { width: 9%; }
  .inv-table.inv-table-added th:nth-child(6), .inv-table.inv-table-added td:nth-child(6) { width: 12%; }
  .inv-table.inv-table-added th:nth-child(7), .inv-table.inv-table-added td:nth-child(7) { width: 9%; }
  .inv-table.inv-table-added th:nth-child(8), .inv-table.inv-table-added td:nth-child(8) { width: 22%; }
}
@media (max-width: 640px) {
  .inv-date-filter { border-radius: var(--radius); padding: 10px 12px; }
  .inv-date-label { flex-basis: 100%; }
  .inv-date-filter input[type="date"].input { flex: 1 1 130px; }
}

/* ---- Stock summary: 3 big figures on top, 4 small ones under (2.3.12) ----
   views/inventory/summary.js. Owner's request: the seven equal boxes broke
   the money figures over two lines. The top three share the row, and each
   amount stays on ONE line - its size follows the box's own width
   (container units), so a long figure gets a little smaller instead of
   wrapping. The counts underneath are a quieter, smaller row of four.
   Colours stay what they were for each figure. */
.stat-grid.stat-grid-main { grid-template-columns: repeat(3, minmax(0, 1fr)); margin-bottom: 14px; }
.stat-grid.stat-grid-minor { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
.stat-grid-main .stat-card { container-type: inline-size; padding: 18px 20px 20px; }
.stat-grid-main .stat-value { white-space: nowrap; font-size: 1.6rem; font-size: clamp(1.15rem, 9cqi, 2.1rem); line-height: 1.2; }
.stat-grid-main .stat-value .money { white-space: nowrap; overflow-wrap: normal; }
.stat-grid-minor .stat-card { padding: 11px 14px 12px; }
.stat-grid-minor .stat-label { font-size: 0.8rem; margin-bottom: 4px; }
.stat-grid-minor .stat-value { font-size: 1.25rem; }
.stat-grid.stat-grid-minor > :nth-child(1) { --c: var(--a4); }
.stat-grid.stat-grid-minor > :nth-child(2) { --c: var(--a5); }
.stat-grid.stat-grid-minor > :nth-child(3) { --c: var(--a1); }
.stat-grid.stat-grid-minor > :nth-child(4) { --c: var(--a2); }
@media (max-width: 600px) {
  .stat-grid.stat-grid-main { grid-template-columns: 1fr; gap: 10px; }
  .stat-grid-main .stat-value { font-size: clamp(1.15rem, 7.5cqi, 1.9rem); }
  .stat-grid.stat-grid-minor { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
}
/* A label that wraps onto two lines must not push its figure out of line
   with the others in the row. */
.stat-grid-main .stat-card,
.stat-grid-minor .stat-card { display: flex; flex-direction: column; }
.stat-grid-main .stat-value,
.stat-grid-minor .stat-value { margin-top: auto; }

/* ---- Long boxes: five lines, then "Show more" (2.3.13) ------------------
   public/js/app.js gives every [data-show-more] list/table body a button;
   lines 6+ are hidden from the first paint so a long box never flashes
   open and then jumps shut. */
[data-show-more]:not(.sm-open) > :nth-child(n + 6) { display: none; }
[data-show-more].sm-open > :nth-child(n + 6) { animation: sm-in 0.22s ease both; }
@keyframes sm-in { from { opacity: 0; transform: translateY(-3px); } }
.show-more-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  margin-top: 10px;
  padding: 8px 12px;
  border: 1px dashed color-mix(in srgb, var(--primary) 40%, var(--border));
  border-radius: 10px;
  background: color-mix(in srgb, var(--primary) 6%, transparent);
  color: var(--primary);
  font: inherit;
  font-size: 0.85rem;
  font-weight: 650;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}
.show-more-btn:hover { background: color-mix(in srgb, var(--primary) 13%, transparent); border-style: solid; }
.show-more-btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.show-more-btn svg { width: 16px; height: 16px; transition: transform 0.2s ease; }
.show-more-btn[aria-expanded="true"] svg { transform: rotate(180deg); }
@media print {
  [data-show-more]:not(.sm-open) > :nth-child(n + 6) { display: revert; }
  .show-more-btn { display: none; }
}

/* ---- Boxes light up under the cursor (2.3.13) ---------------------------
   Owner's request: the box the mouse is on should glow a little, so it
   stands apart from the rest. A soft ring and halo in the box's own
   accent colour (--c on the figure cards, the theme's primary on the
   others), a faint light that follows the pointer inside the box
   (--mx/--my, set by public/js/app.js), and the small cards lift a touch.
   Only with a real mouse - a phone has no "hover", so nothing changes
   there - and never in print. */
@media (hover: hover) and (pointer: fine) {
  :where(.panel, .stat-card, .perf-item, .pos-setup-card, .report-picker-card, .compare-card, .upd-card) { position: relative; }
  .panel, .stat-card, .perf-item, .pos-setup-card, .report-picker-card, .compare-card, .upd-card {
    transition: border-color 0.2s ease, box-shadow 0.25s ease, transform 0.2s ease;
  }
  .panel::after, .stat-card::after, .perf-item::after, .pos-setup-card::after, .report-picker-card::after, .compare-card::after, .upd-card::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.25s ease;
    background: radial-gradient(420px circle at var(--mx, 50%) var(--my, 0%), color-mix(in srgb, var(--c, var(--primary)) 13%, transparent), transparent 60%);
  }
  .panel:hover, .stat-card:hover, .perf-item:hover, .pos-setup-card:hover, .report-picker-card:hover, .compare-card:hover, .upd-card:hover {
    border-color: color-mix(in srgb, var(--c, var(--primary)) 55%, var(--border));
    box-shadow:
      0 0 0 1px color-mix(in srgb, var(--c, var(--primary)) 22%, transparent),
      0 12px 32px -12px color-mix(in srgb, var(--c, var(--primary)) 55%, transparent),
      var(--shadow);
  }
  .panel:hover::after, .stat-card:hover::after, .perf-item:hover::after, .pos-setup-card:hover::after, .report-picker-card:hover::after, .compare-card:hover::after, .upd-card:hover::after { opacity: 1; }
  /* Big boxes (a whole list, a form) only glow; the small ones also lift. */
  .stat-card:hover, .perf-item:hover, .pos-setup-card:hover, .report-picker-card:hover, .compare-card:hover, .upd-card:hover { transform: translateY(-2px); }
  /* A box inside a box: only the one under the pointer lights up. */
  .panel:has(.stat-card:hover, .perf-item:hover, .upd-card:hover, .panel:hover) { border-color: var(--border); box-shadow: var(--shadow); }
  .panel:has(.stat-card:hover, .perf-item:hover, .upd-card:hover, .panel:hover)::after { opacity: 0; }
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: reduce) {
  .stat-card:hover, .perf-item:hover, .pos-setup-card:hover, .report-picker-card:hover, .compare-card:hover, .upd-card:hover { transform: none; }
}

/* Live search (2.3.14, public/js/nav.js): the page swapped in while typing
   appears in place - no slide-in for every letter. */
html.nav-live main.page { animation: none !important; }
/* Sign-in sent by itself once the password is right (public/js/app.js). */
.is-signing-in button[type="submit"] { opacity: 0.7; pointer-events: none; }

/* Warnings that can be closed for the rest of the session (2.3.14,
   public/js/app.js): an x at the right-hand end. */
.is-dismissible { position: relative; padding-right: 44px !important; transition: opacity 0.18s ease, transform 0.18s ease; }
.is-dismissible.is-closing { opacity: 0; transform: translateY(-4px); }
.notice-close {
  position: absolute;
  top: 50%;
  right: 8px;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: inherit;
  cursor: pointer;
  opacity: 0.65;
  transition: opacity 0.15s ease, background-color 0.15s ease;
}
.notice-close:hover { opacity: 1; background: color-mix(in srgb, currentColor 14%, transparent); }
.notice-close:focus-visible { outline: 2px solid currentColor; outline-offset: 1px; opacity: 1; }
.notice-close svg { width: 16px; height: 16px; }
@media print { .notice-close { display: none; } }
/* "Expired stock" beside "Low stock only" (2.3.14): ticked, it wears the
   warning colours, like the expired-stock notice it answers. */
@media screen {
  .inv-expired-toggle:has(input:checked) { background: var(--warn-bg); border-color: var(--warn-border); color: var(--warn-text); }
}

/* ---- Fingerprint sign-in (2.3.15) ---------------------------------------
   Sign-in page: one big fingerprint button first (views/login.js #fp-login,
   public/js/app.js), "Use password instead" under it. Setup page
   (/account/fingerprint), the Security list and the dashboard suggestion. */
.fp-login { display: flex; flex-direction: column; align-items: stretch; gap: 14px; }
.fp-scan {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  width: 100%;
  padding: 22px 16px 18px;
  border: 1px solid color-mix(in srgb, var(--primary) 35%, var(--border));
  border-radius: 18px;
  background: color-mix(in srgb, var(--primary) 7%, var(--surface));
  color: var(--text);
  font: inherit;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
}
.fp-scan:hover { background: color-mix(in srgb, var(--primary) 12%, var(--surface)); }
.fp-scan:active { transform: scale(0.99); }
.fp-scan-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 84px;
  height: 84px;
  border-radius: 50%;
  color: var(--primary);
  background: color-mix(in srgb, var(--primary) 12%, transparent);
  box-shadow: 0 0 0 8px color-mix(in srgb, var(--primary) 6%, transparent);
}
.fp-scan-icon svg { width: 48px; height: 48px; }
.fp-login.is-scanning .fp-scan-icon { animation: fp-pulse 1.4s ease-in-out infinite; }
@keyframes fp-pulse {
  0%, 100% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--primary) 8%, transparent); }
  50% { box-shadow: 0 0 0 16px color-mix(in srgb, var(--primary) 3%, transparent); }
}
.fp-scan-title { font-weight: 700; font-size: 1.05rem; margin-top: 6px; }
.fp-scan-who { font-weight: 600; color: var(--primary); }
.fp-scan-hint { font-size: 0.85rem; color: var(--text-muted); }
.fp-use-password { align-self: center; font-weight: 600; }
.fp-login-label { margin: 0 0 6px; font-size: 0.85rem; color: var(--text-muted); text-align: center; }
.fp-login-people { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; }
.fp-person {
  padding: 7px 14px;
  border-radius: 999px;
  border: 1px solid var(--border-strong, var(--border));
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 0.88rem;
  font-weight: 600;
  cursor: pointer;
}
.fp-person.is-current { border-color: var(--primary); background: color-mix(in srgb, var(--primary) 12%, var(--surface)); color: var(--primary); }
.fp-login-failed { margin-bottom: 4px; }

.fp-setup { display: flex; align-items: center; gap: 18px; }
.fp-icon {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 72px;
  height: 72px;
  border-radius: 50%;
  color: var(--primary);
  background: color-mix(in srgb, var(--primary) 11%, transparent);
}
.fp-icon svg { width: 42px; height: 42px; }
.fp-setup.is-ready .fp-icon { color: var(--ok-text); background: var(--ok-bg); }
.fp-setup-body { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; min-width: 0; }
.fp-setup-state { margin: 0; font-weight: 650; color: var(--ok-text); }
.fp-setup-status { margin: 0; font-size: 0.9rem; color: var(--text-muted); }
.fp-setup-status:empty { display: none; }
.fp-setup-status.is-ok { color: var(--ok-text); font-weight: 600; }
.fp-setup-status.is-error { color: var(--danger); font-weight: 600; }
.fp-device { font-weight: 600; }
@media (max-width: 600px) {
  .fp-setup { flex-direction: column; text-align: center; }
  .fp-setup-body { align-items: center; }
}

/* "Did you mean...?" for a medicine not tied to the catalogue (2.3.21,
   public/js/pages/po-suggest.js): a dotted name, and a small list of names
   - not a table - next to it. */
.po-sugg-name { text-decoration: underline dotted; text-decoration-color: var(--warn-text, #b7791f); text-underline-offset: 3px; cursor: help; }
button.po-new-badge { border: 0; cursor: pointer; font: inherit; }
.po-new-badge { margin-left: 6px; }
.po-sugg-pop {
  position: fixed;
  z-index: 3000;
  min-width: 200px;
  max-width: min(320px, calc(100vw - 16px));
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 12px 30px -12px rgba(0, 0, 0, 0.45);
  padding: 6px;
  font-size: 0.92rem;
}
.po-sugg-title { padding: 4px 8px 6px; font-size: 0.78rem; font-weight: 700; color: var(--text-muted); text-transform: none; }
.po-sugg-item { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; width: 100%; padding: 7px 8px; border: 0; border-radius: 8px; background: transparent; color: inherit; text-align: left; font: inherit; cursor: pointer; }
.po-sugg-item:hover, .po-sugg-item:focus { background: color-mix(in srgb, var(--primary) 12%, transparent); outline: none; }
.po-sugg-pname { font-weight: 600; }
.po-sugg-code { font-size: 0.78rem; color: var(--text-muted); flex-shrink: 0; }
.po-sugg-hint { padding: 6px 8px 2px; font-size: 0.75rem; color: var(--text-muted); border-top: 1px solid var(--border); margin-top: 4px; }
.po-sugg-none, .po-sugg-loading { padding: 8px; color: var(--text-muted); }
.po-row-missing td { background: color-mix(in srgb, var(--danger) 10%, transparent); }
.po-row-missing [data-po-qty] { border-color: var(--danger); }

/* POS setup > Android application, and the download page (2.3.20). */
.aa-current { display: flex; align-items: flex-start; gap: 18px; }
.aa-icon {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 72px;
  height: 72px;
  border-radius: 20px;
  color: var(--primary);
  background: color-mix(in srgb, var(--primary) 11%, transparent);
}
.aa-icon svg { width: 40px; height: 40px; }
.aa-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 10px; }
.aa-body h2 { margin: 0; }
.aa-body p { margin: 0; }
.aa-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px 18px; }
.aa-facts > div { display: flex; flex-direction: column; gap: 2px; }
.aa-k { font-size: 0.82rem; color: var(--text-muted); }
.aa-v { font-weight: 650; }
.aa-version { font-size: 1.5rem; font-weight: 800; letter-spacing: -0.01em; }
.aa-steps { margin: 0 0 8px; padding-left: 1.3em; display: flex; flex-direction: column; gap: 6px; }
.aa-upload input[type="file"] { max-width: 100%; }
@media (max-width: 600px) {
  .aa-current { flex-direction: column; }
  .aa-download { width: 100%; justify-content: center; }
}

.sec-fp-panel .panel-head { flex-wrap: wrap; gap: 10px; }
.sec-fp-panel h2 { display: inline-flex; align-items: center; gap: 8px; }
.sec-fp-icon { display: inline-flex; width: 22px; height: 22px; color: var(--primary); }
.sec-fp-icon svg { width: 22px; height: 22px; }
.sec-fp-switch { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 0.88rem; }

.fp-nudge {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 14px;
  padding: 12px 14px;
  border-radius: 14px;
  border: 1px solid color-mix(in srgb, var(--primary) 30%, var(--border));
  background: color-mix(in srgb, var(--primary) 8%, var(--surface));
  font-weight: 600;
}
.fp-nudge-actions { display: inline-flex; gap: 8px; }
.sec-fp-panel { margin-top: 18px; }
.table-stack td.actions .link-btn-danger, .fp-setup ~ .panel .link-btn-danger { color: var(--danger); }

/* ---------- 2.3.16: a Dose (or Box) with a selling price of its own ---------- */
.extra-unit-row { flex-wrap: wrap; }
.extra-unit-row input.input-price,
.pack-price-field input.input-price { max-width: 9em; }
.pack-price-field { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.pack-price-field > span { color: var(--text-muted); font-size: .92em; }
.ps-accept { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; margin-bottom: 6px; }
.ps-accept-fields { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.ps-accept-fields .input-price { width: 7.5em; }
.ps-accept-fields select { max-width: 14em; }
.ps-accept-fields .input-narrow { width: 5em; }
@media (max-width: 640px) {
  .ps-accept-fields { width: 100%; }
  .ps-accept-fields .input-price { flex: 1 1 6em; }
  .ps-accept-fields select { flex: 1 1 10em; max-width: none; }
}
.imp-file-status:empty { display: none; }
.imp-file-status.imp-file-error { color: var(--danger); font-weight: 600; }
@media screen and (max-width: 640px) {
  .ps-table td.actions .ps-accept { width: 100%; }
  .ps-table td.actions .ps-accept .btn { width: 100%; justify-content: center; }
}
.extra-units .pack-price-field { flex-direction: row; justify-content: flex-start; text-align: left; }
.extra-unit-price-label { color: var(--text-muted); font-size: .9em; margin-left: 6px; }

/* ---- Branches: move stock / copy selling prices (2.3.17) --------------- */
.branch-moves { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 16px; margin: 4px 0 18px; }
.branch-move { display: flex; flex-direction: column; gap: 14px; margin: 0; }
.branch-move-head { display: flex; gap: 12px; align-items: flex-start; }
.branch-move-head h2 { margin: 0 0 2px; font-size: 1.06rem; }
.branch-move-head .hint { margin: 0; }
.branch-move-icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 12px;
  background: color-mix(in srgb, var(--primary) 14%, transparent);
  color: var(--primary);
}
.branch-move-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px; }
.branch-move-form label { flex: 1 1 140px; display: flex; flex-direction: column; gap: 4px; font-size: 0.86rem; font-weight: 600; }
.branch-move-arrow { align-self: center; padding-top: 18px; color: var(--text-muted); font-size: 1.2rem; }
.branch-move-link { font-size: 0.9rem; font-weight: 600; }
.branch-move-cta { align-self: flex-start; margin-top: auto; }

.bp-form { display: flex; flex-direction: column; gap: 16px; }
.bp-step { margin: 0; }
.bp-step h2 { display: flex; align-items: center; gap: 10px; margin: 0 0 14px; font-size: 1.05rem; }
.bp-num {
  display: inline-grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 999px;
  background: var(--primary);
  color: var(--primary-contrast);
  font-size: 0.85rem;
  font-weight: 800;
}
.bp-route { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 14px; }
.bp-from { flex: 0 1 260px; display: flex; flex-direction: column; gap: 4px; font-weight: 600; font-size: 0.9rem; }
.bp-arrow { align-self: center; color: var(--text-muted); font-size: 1.3rem; }
.bp-targets { flex: 1 1 320px; border: 0; margin: 0; padding: 0; min-width: 0; }
.bp-targets legend { display: flex; gap: 12px; align-items: center; padding: 0; margin-bottom: 6px; font-weight: 600; font-size: 0.9rem; }
.bp-target-list { display: flex; flex-wrap: wrap; gap: 8px; }
.bp-target {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  cursor: pointer;
  font-weight: 600;
  background: var(--surface);
}
.bp-target small { display: block; font-weight: 500; color: var(--text-muted); font-size: 0.76rem; }
.bp-target:has(input:checked) { background: var(--ok-bg); border-color: color-mix(in srgb, var(--ok-text) 40%, transparent); color: var(--ok-text); }
.bp-target[hidden] { display: none; }
.bp-choice-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 10px; }
.bp-choice {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 12px 14px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  cursor: pointer;
}
.bp-choice input { margin-top: 3px; }
.bp-choice span { display: flex; flex-direction: column; gap: 2px; }
.bp-choice small { color: var(--text-muted); }
.bp-choice:has(input:checked) { border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary) inset; }
.bp-picker { margin-top: 14px; display: flex; flex-direction: column; gap: 8px; }
.bp-picker[hidden] { display: none; }
.bp-chips { list-style: none; margin: 4px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.bp-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 6px 6px 12px;
  border-radius: 999px;
  background: var(--muted-bg);
  font-weight: 600;
  font-size: 0.9rem;
}
.bp-chip small { font-weight: 500; color: var(--text-muted); }
.bp-chip-x { border: 0; background: transparent; color: var(--text-muted); font-size: 1.1rem; line-height: 1; width: 26px; height: 26px; border-radius: 999px; cursor: pointer; }
.bp-chip-x:hover { background: var(--surface); color: var(--text); }
.bp-units { margin-top: 14px; }
.bp-actions { justify-content: flex-end; }
.bp-summary { display: flex; flex-direction: column; gap: 6px; }
.bp-summary-route { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.bp-summary-count { margin: 0; font-size: 1.1rem; font-weight: 700; }
.bp-new { color: var(--ok-text); }
.bp-unit { white-space: nowrap; }
/* Labels are columns with boxed inputs by default; these two are a tick
   or a dot beside their text. */
.bp-form label.bp-target, .bp-form label.bp-choice { flex-direction: row; color: var(--text); font-size: 0.92rem; }
.bp-form label.bp-target input, .bp-form label.bp-choice input {
  flex: none;
  width: 18px;
  height: 18px;
  min-height: 0;
  padding: 0;
  margin: 0;
  accent-color: var(--primary);
}
.bp-form label.bp-target input { margin: 0; }
.bp-form label.bp-choice input { margin-top: 2px; }
.bp-form label.bp-choice strong { color: var(--text); }
.bp-form label.bp-choice small { font-weight: 500; }
@media (max-width: 640px) {
  .bp-route > .bp-arrow, .branch-move-arrow { display: none; }
  .bp-from { flex-basis: 100%; }
}

/* ---- Sale corrections: one card each (2.3.17, views/sale-issues/list.js).
   2.3.18, owner's request: small, tidy cards - name, receipt line and the
   two prices on a few short lines; the decision box beside them. */
.sli-list { display: flex; flex-direction: column; gap: 10px; }
.sli-card { display: grid; grid-template-columns: minmax(0, 1fr) minmax(260px, 310px); gap: 14px; margin: 0; padding: 12px 14px; align-items: start; overflow: visible; }
.sli-card.sli-approved, .sli-card.sli-rejected { grid-template-columns: minmax(0, 1fr); }
.sli-main { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.sli-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.sli-product { margin: 0; font-size: 1rem; line-height: 1.3; }
.sli-head .badge, .sli-tags .badge { font-size: 0.72rem; padding: 1px 8px; }
.sli-tags { display: flex; flex-wrap: wrap; gap: 4px; }
.sli-tags:empty { display: none; }
.sli-meta { display: flex; flex-wrap: wrap; gap: 2px 14px; margin: 0; font-size: 0.82rem; }
.sli-meta div { display: flex; align-items: baseline; gap: 5px; }
.sli-meta dt { font-size: 0.72rem; font-weight: 600; color: var(--text-muted); }
.sli-meta dd { margin: 0; font-weight: 600; }
.sli-compare { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.sli-price { display: flex; flex-direction: column; gap: 0; padding: 5px 10px; border-radius: 8px; background: var(--muted-bg); line-height: 1.3; }
.sli-price strong { font-size: 0.9rem; }
.sli-price.is-now { background: color-mix(in srgb, var(--primary) 10%, var(--surface)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--primary) 30%, transparent); }
.sli-price-label { font-size: 0.7rem; font-weight: 600; color: var(--text-muted); }
.sli-price-total { font-size: 0.76rem; color: var(--text-muted); }
.sli-arrow { color: var(--text-muted); font-size: 1rem; }
.sli-diff { padding: 2px 9px; border-radius: 999px; font-weight: 700; font-size: 0.78rem; white-space: nowrap; }
.sli-diff.is-less { background: var(--danger-bg); color: var(--danger); }
.sli-diff.is-more { background: var(--ok-bg); color: var(--ok-text); }
.sli-desc { margin: 0; color: var(--text-muted); font-size: 0.85rem; }
.sli-outcome { font-size: 0.82rem; color: var(--text-muted); display: flex; flex-direction: column; gap: 1px; }
.sli-scope { font-weight: 600; color: var(--text); }
.sli-act {
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding: 10px;
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--muted-bg) 70%, transparent);
  border: 1px solid var(--border);
}
.sli-act .hint { margin: 0; font-size: 0.78rem; }
.sli-fields { display: grid; grid-template-columns: 0.8fr 1.2fr; gap: 8px; }
.sli-fields label, .sli-unit-pick { display: flex; flex-direction: column; gap: 3px; font-size: 0.76rem; }
.sli-fields label small { font-weight: 500; }
.sli-card .sli-act label input:not([type="checkbox"]):not([type="radio"]),
.sli-card .sli-act label select,
.sli-card .sli-act input.sli-note { min-height: 34px; padding: 5px 9px; font-size: 0.9rem; border-radius: 8px; }
.sli-fields .input, .sli-act .sli-note { width: 100%; min-width: 0; }
.sli-buttons { position: relative; display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
.sli-buttons .btn { min-height: 36px; padding: 6px 12px; font-size: 0.88rem; }
.sli-approve { position: static; }
.sli-approve > summary { list-style: none; width: 100%; justify-content: center; gap: 8px; cursor: pointer; }
.sli-approve > summary::-webkit-details-marker { display: none; }
.sli-caret { width: 0; height: 0; border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 6px solid currentColor; transition: transform 0.15s ease; }
.sli-approve[open] .sli-caret { transform: rotate(180deg); }
/* The choices pop up right at the Approve button. */
.sli-pop {
  position: absolute;
  z-index: 30;
  bottom: calc(100% + 8px);
  left: 0;
  right: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px;
  border-radius: var(--radius);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-lg);
  animation: sli-pop-in 0.14s ease-out;
}
.sli-pop::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 28px;
  border: 8px solid transparent;
  border-top-color: var(--surface);
}
@keyframes sli-pop-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.sli-choice {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  width: 100%;
  padding: 7px 10px;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.sli-choice strong { font-size: 0.86rem; }
.sli-choice small { color: var(--text-muted); font-size: 0.74rem; line-height: 1.3; }
.sli-choice:hover, .sli-choice:focus-visible { border-color: var(--primary); background: color-mix(in srgb, var(--primary) 8%, var(--surface)); }
.sli-choice.is-stock strong { color: var(--primary); }
.sli-reject { width: 100%; }
.sli-tab-n { display: inline-block; min-width: 20px; padding: 0 6px; margin-left: 4px; border-radius: 999px; background: color-mix(in srgb, currentColor 16%, transparent); font-size: 0.8rem; text-align: center; }
@media (max-width: 860px) {
  .sli-card { grid-template-columns: minmax(0, 1fr); gap: 10px; }
}
@media (max-width: 480px) {
  .sli-card { padding: 10px 12px; }
  .sli-price { flex: 1 1 120px; min-width: 0; }
}

/* Staff: which approvals an admin sees (2.3.17) - tick rows close together. */
.approvals-editor .duty-form { gap: 6px; }
.approvals-editor .checkbox-label { flex-direction: row; align-items: center; gap: 8px; min-height: 0; color: var(--text); font-weight: 500; }
.approvals-editor .checkbox-label input[type="checkbox"] { width: 16px; height: 16px; min-height: 0; padding: 0; margin: 0; accent-color: var(--primary); }

/* Sale corrections, 2.3.18: the Approve choices are tick boxes that can be
   combined (this order / the whole stock / stop price cuts), then one
   "Approve" to confirm. */
.sli-pop-title { margin: 0 2px; font-size: 0.74rem; font-weight: 600; color: var(--text-muted); }
label.sli-choice { flex-direction: row; align-items: flex-start; gap: 9px; font-size: inherit; font-weight: 400; color: var(--text); }
label.sli-choice > span { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
label.sli-choice input[type="checkbox"] { flex: none; width: 16px; height: 16px; min-height: 0; padding: 0; margin: 2px 0 0; accent-color: var(--primary); }
label.sli-choice:has(input:checked) { border-color: var(--primary); background: color-mix(in srgb, var(--primary) 8%, var(--surface)); }
.sli-choice.is-lock strong { color: var(--danger); }
label.sli-choice.is-lock:has(input:checked) { border-color: var(--danger); background: color-mix(in srgb, var(--danger) 7%, var(--surface)); }
label.sli-choice.is-lock input[type="checkbox"] { accent-color: var(--danger); }
.sli-choice.is-done { cursor: default; }
.sli-choice-box { display: flex; flex-direction: column; gap: 6px; }
.sli-choice-box .sli-unit-pick { padding: 0 4px 2px 36px; }
.sli-choice-box:not(:has(input[name="apply_stock"]:checked)) .sli-unit-pick { display: none; }
.sli-confirm { width: 100%; justify-content: center; margin-top: 2px; }
.sli-confirm:disabled { opacity: 0.5; cursor: not-allowed; }
/* The same cards on the Approvals page. */
.apr-cards { margin-bottom: 18px; }
.apr-cards .apr-section-head { padding: 0 2px; }
.apr-cards > .hint { margin: 0 2px 10px; }
/* Inventory > Edit: stop sellers lowering this price (2.3.18). */
label.inv-lock { align-items: flex-start; gap: 10px; padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius); color: var(--text); font-weight: 400; }
label.inv-lock input[type="checkbox"] { flex: none; width: 18px; height: 18px; min-height: 0; padding: 0; margin: 2px 0 0; accent-color: var(--danger); }
label.inv-lock > span { display: flex; flex-direction: column; gap: 3px; }
label.inv-lock strong { font-weight: 600; }
label.inv-lock small { color: var(--text-muted); font-size: 0.82rem; }
label.inv-lock:has(input:checked) { border-color: var(--danger); background: color-mix(in srgb, var(--danger) 6%, var(--surface)); }

/* Detailed sales report (2.3.18, views/reports/sales-detail.js). */
.rdet-filters { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px 12px; padding: 14px 16px; margin-bottom: 12px; }
.rdet-filters label { flex: 1 1 140px; min-width: 0; }
.rdet-filters .rdet-q { flex: 3 1 220px; }
@media (max-width: 640px) { .rdet-filters { padding: 12px; } .rdet-filters > .btn { flex: 1 1 100%; } }
.rdet-filters select, .rdet-filters input { width: 100%; }
.rdet-quick { display: flex; flex-wrap: wrap; gap: 6px; flex-basis: 100%; }
.rdet-info { display: flex; flex-wrap: wrap; gap: 4px 18px; margin: 0 0 10px; font-size: 0.86rem; color: var(--text-muted); }
.rdet-info strong { color: var(--text); }
.rdet-stats { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; margin-bottom: 14px; }
.rdet-stat { display: flex; flex-direction: column; gap: 2px; padding: 8px 12px; border-radius: var(--radius); background: var(--surface); border: 1px solid var(--border); min-width: 0; }
.rdet-stat span { font-size: 0.74rem; color: var(--text-muted); font-weight: 600; }
.rdet-stat strong { font-size: 1rem; overflow-wrap: anywhere; }
.rdet-stat strong .money-amt { font-size: 1rem; font-weight: 700; color: var(--text); }
.rdet-panel { padding: 0; overflow: hidden; }
.rdet-table { font-size: 0.8rem; width: max-content; min-width: 100%; }
.rdet-table th, .rdet-table td { padding: 5px 8px; vertical-align: top; white-space: nowrap; }
.rdet-table td.rdet-product, .rdet-table td.rdet-note { white-space: normal; }
.rdet-table thead th { position: sticky; top: 0; background: var(--surface-2, var(--surface)); white-space: nowrap; }
.rdet-table tr.rdet-first td { border-top: 1px solid var(--border-strong); }
.rdet-table tr.rdet-cont td { border-top: 1px dashed var(--border); }
.rdet-table .nowrap { white-space: nowrap; }
.rdet-table .rdet-product { min-width: 180px; max-width: 260px; font-weight: 600; }
.rdet-table .rdet-note { min-width: 200px; max-width: 320px; color: var(--text-muted); font-size: 0.76rem; }
.rdet-table .rdet-loss { color: var(--danger); }
.rdet-table tfoot td { border-top: 2px solid var(--border-strong); background: color-mix(in srgb, var(--primary) 7%, var(--surface)); }
.rdet-export.is-xlsx { border-color: #1d6f42; }
.rdet-export.is-docx { border-color: #2b579a; }
.rdet-export.is-pdf { border-color: #b30b00; }
@media print {
  .rdet-table { width: 100%; min-width: 0; font-size: 7pt; }
  .rdet-table th, .rdet-table td { white-space: normal; }
  .rdet-panel { border: 0; box-shadow: none; }
}
