/* ==========================================================================
   Grocery POS — application shell and shared components.

   Design notes
   ------------
   The palette is neutral first. Indigo is the one accent and it marks the
   thing you are meant to press; green is no longer decoration, it now means
   one thing only — money in your favour (profit, paid, stock in hand). That
   is what pulls the eye to the right number on a busy screen.

   Every colour is a token, and every token has a dark counterpart in the
   [data-theme="dark"] block. Nothing below this token layer should contain a
   raw hex value, so a single edit here re-skins the whole application.

   Class names and markup are deliberately untouched: pos.js, purchase.js,
   expenses.js, khata.js and product-form.js all build DOM from these exact
   names, so this file restyles them rather than replacing them.
   ========================================================================== */

:root {
  color-scheme: light;

  /* --- Surfaces ---------------------------------------------------------- */
  --bg: #f4f5f8;
  --panel: #ffffff;
  --surface-2: #f7f8fa;          /* table heads, inset fills, quiet chips */
  --surface-3: #f0f2f6;          /* pressed / recessed */
  --overlay: rgba(13, 18, 28, .55);
  --topbar-bg: rgba(244, 245, 248, .82);
  --glow-a: rgba(79, 70, 229, .13);
  --glow-b: rgba(31, 111, 208, .10);

  /* --- Ink --------------------------------------------------------------- */
  --text: #101623;
  --text-2: #3b465a;
  --muted: #6a7688;
  --line: #e4e7ee;
  --line-soft: #eef0f5;
  --line-hover: #c3cad6;

  /* --- Accent ------------------------------------------------------------ */
  --primary: #4f46e5;            /* fills: buttons, active pills */
  --primary-strong: #4338ca;     /* fill hover */
  --primary-ink: #4338ca;        /* accent as text or icon */
  --primary-weak: #eef0ff;       /* tinted background */
  --primary-line: #cfd3fb;       /* border on a tinted background */
  --primary-contrast: #ffffff;   /* text on a filled accent */
  --focus-ring: rgba(79, 70, 229, .22);
  --accent-glow: 0 6px 18px rgba(79, 70, 229, .26);

  /* --- Status --------------------------------------------------------------
     Each status has an ink value (readable as text on the page background) and
     a fill value (readable with white text on top). In dark mode those have to
     diverge: the salmon that reads well as warning text would be unreadable
     behind white button text. */
  --success: #0f7b52;
  --success-fill: #0f7b52;
  --success-soft: #e7f6ef;
  --success-line: #bfe6d3;
  --danger: #cf3a30;
  --danger-fill: #cf3a30;
  --danger-soft: #fdeeec;
  --danger-line: #f6cfca;
  --warning: #a86a06;
  --warning-fill: #a86a06;
  --warning-soft: #fdf4e3;
  --warning-line: #f0dcb0;
  --info: #1f6fd0;
  --info-soft: #eaf2fd;
  --info-line: #c9dffa;

  /* --- Navigation chrome -------------------------------------------------- */
  --nav-bg: #ffffff;
  --nav-line: #e6e9ef;
  --nav-text: #4c5769;
  --nav-text-strong: #101623;
  --nav-hover: #f2f4f8;
  --nav-active-bg: #eef0ff;
  --nav-active-text: #4338ca;
  --nav-muted: #7c879a;
  --flyout-bg: #1b2231;
  --flyout-ink: #f2f5fa;

  /* The sidebar's two widths, and the timings the whole shell shares. One
     curve for layout that moves, a shorter one for colour that just changes -
     using the same duration for both makes hovers feel sluggish. */
  --nav-w: 245px;
  --nav-w-mini: 68px;
  --ease-nav: .22s cubic-bezier(.4, 0, .2, 1);
  --ease-ui: .13s ease;

  /* Deep ink. Named --sidebar for history: purchase.css, khata.css and
     receipt.css all use it as "the strongest dark in the palette" for step
     markers, active tabs, statement rules and the receipt stage. It is no
     longer the sidebar's background, but it is still that darkest ink. */
  --sidebar: #131a26;

  /* --- Elevation --------------------------------------------------------- */
  --shadow-xs: 0 1px 2px rgba(16, 22, 35, .05);
  --shadow-sm: 0 1px 2px rgba(16, 22, 35, .04), 0 2px 6px rgba(16, 22, 35, .04);
  --shadow: 0 1px 2px rgba(16, 22, 35, .04), 0 8px 22px rgba(16, 22, 35, .06);
  --shadow-lg: 0 18px 50px rgba(16, 22, 35, .14);

  /* --- Shape and type ---------------------------------------------------- */
  --radius-sm: 9px;
  --radius: 13px;
  --radius-lg: 18px;
  --font-sans: "Segoe UI Variable Text", "Segoe UI", Inter, system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  --font-display: "Segoe UI Variable Display", "Segoe UI Variable Text", "Segoe UI", Inter, system-ui, sans-serif;
  --font-mono: ui-monospace, "Cascadia Mono", Consolas, "SF Mono", Menlo, monospace;

  /* --- Legacy aliases -----------------------------------------------------
     Kept so khata.css, purchase.css, expenses.css and product-form.css keep
     resolving without being rewritten line by line. */
  --primary-dark: var(--primary-ink);
  --primary-soft: var(--primary-weak);
}

:root[data-theme="dark"] {
  color-scheme: dark;

  --bg: #0b0f17;
  --panel: #141a25;
  --surface-2: #1a2130;
  --surface-3: #212939;
  --overlay: rgba(3, 6, 12, .7);
  --topbar-bg: rgba(11, 15, 23, .82);
  --glow-a: rgba(99, 102, 241, .16);
  --glow-b: rgba(111, 168, 240, .10);

  --text: #e9eef6;
  --text-2: #c0cad9;
  --muted: #8c98ab;
  --line: #262f3f;
  --line-soft: #1d2533;
  --line-hover: #3b4759;

  --primary: #6366f1;
  --primary-strong: #7b7dff;
  --primary-ink: #a8aeff;
  --primary-weak: #1d2140;
  --primary-line: #333a66;
  --primary-contrast: #ffffff;
  --focus-ring: rgba(122, 125, 255, .30);
  --accent-glow: 0 6px 18px rgba(0, 0, 0, .45);

  --success: #3fbe89;
  --success-fill: #157a55;
  --success-soft: #10281f;
  --success-line: #1e4436;
  --danger: #f2867c;
  --danger-fill: #c23a2e;
  --danger-soft: #2b1614;
  --danger-line: #4d2724;
  --warning: #e0a94a;
  --warning-fill: #8a5a12;
  --warning-soft: #2a2011;
  --warning-line: #493720;
  --info: #6fa8f0;
  --info-soft: #131f31;
  --info-line: #22364f;

  --nav-bg: #101620;
  --nav-line: #212a38;
  --nav-text: #9fabbe;
  --nav-text-strong: #eef2f8;
  --nav-hover: #1a2130;
  --nav-active-bg: #1e2340;
  --nav-active-text: #b3b8ff;
  --nav-muted: #748093;
  --flyout-bg: #2b3446;
  --flyout-ink: #f2f5fa;

  --sidebar: #05080e;

  --shadow-xs: 0 1px 2px rgba(0, 0, 0, .35);
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4), 0 2px 6px rgba(0, 0, 0, .3);
  --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 10px 26px rgba(0, 0, 0, .34);
  --shadow-lg: 0 22px 60px rgba(0, 0, 0, .58);
}

/* ==========================================================================
   Base
   ========================================================================== */

* { box-sizing: border-box; }
html, body { margin: 0; min-height: 100%; }

body {
  font-family: var(--font-sans);
  background: var(--bg);
  color: var(--text);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

::selection { background: var(--primary-weak); color: var(--primary-ink); }

a { color: var(--primary-ink); text-decoration: none; }
a:hover { color: var(--primary-strong); }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
h1, h2, h3, p { margin-top: 0; }
h1 { font-family: var(--font-display); font-size: 26px; line-height: 1.2; margin-bottom: 5px; letter-spacing: -.5px; font-weight: 700; }
h2 { font-family: var(--font-display); font-size: 17px; margin-bottom: 4px; letter-spacing: -.2px; font-weight: 650; }
h3 { font-size: 14px; letter-spacing: -.1px; }
small { color: var(--muted); }
hr { border: 0; border-top: 1px solid var(--line); }

:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: 4px;
}

/* --- Icons -----------------------------------------------------------------
   One stroke weight and one grid for every icon in the application, drawn in
   currentColor so each one takes the colour of whatever it sits in. The sprite
   itself is in templates/_icons.html. */
.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.ui-icon, .nav-icon {
  width: 18px; height: 18px;
  /* Slightly heavier than a typical icon set: these are read at a glance
     across a counter, often on a cheap screen, and a 1.5 stroke disappears. */
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
  vertical-align: middle;
  pointer-events: none;
}

/* Visible to a screen reader, not to the eye. The icon buttons carry no text
   label, so this is the only thing that tells an assistive user what they do. */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Money and counts line up column-wise wherever they appear. */
.text-right, .money-input, .summary-card strong, .metric-card strong {
  font-variant-numeric: tabular-nums;
}

/* A quieter scrollbar. The counter looks at these lists all day. */
* { scrollbar-width: thin; scrollbar-color: var(--line) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--line); border: 3px solid transparent; background-clip: content-box; border-radius: 99px; }
::-webkit-scrollbar-thumb:hover { background: var(--muted); background-clip: content-box; }

/* ==========================================================================
   Shell
   ---------------------------------------------------------------------------
   The sidebar has two widths and the page has to follow both, so its width is
   a token rather than a number. --nav-w is read by .main-area here and by the
   sticky money bar in purchase.css; anything else that has to sit clear of the
   sidebar should read it too rather than hardcoding a figure.
   ========================================================================== */

.app-shell { min-height: 100vh; display: flex; }

.sidebar {
  width: var(--nav-w);
  background: var(--nav-bg);
  border-right: 1px solid var(--nav-line);
  color: var(--nav-text);
  display: flex;
  flex-direction: column;
  position: fixed;
  inset: 0 auto 0 0;
  z-index: 50;
  transition: width var(--ease-nav);
}

/* --- Brand and the collapse control -------------------------------------- */

.brand {
  height: 68px; flex: 0 0 auto;
  padding: 0 12px 0 16px;
  display: flex; align-items: center; gap: 10px;
  border-bottom: 1px solid var(--nav-line);
}
.brand-id { display: flex; align-items: center; gap: 11px; min-width: 0; flex: 1; color: inherit; }
.brand-mark, .login-logo {
  width: 38px; height: 38px; flex: 0 0 auto;
  display: grid; place-items: center;
  background: linear-gradient(150deg, #6f6af0, #4338ca);
  color: #fff;
  border-radius: 11px;
  font-family: var(--font-display);
  font-weight: 800; font-size: 14px; letter-spacing: .4px;
  box-shadow: 0 6px 16px rgba(79, 70, 229, .3);
}
.brand-text { min-width: 0; }
.brand strong { display: block; font-size: 14.5px; color: var(--nav-text-strong); letter-spacing: -.2px; white-space: nowrap; }
.brand small { display: block; color: var(--nav-muted); margin-top: 1px; font-size: 10.5px; white-space: nowrap; }

.nav-collapse {
  flex: 0 0 auto;
  width: 30px; height: 30px;
  display: grid; place-items: center;
  border: 0; border-radius: 8px;
  background: transparent; color: var(--nav-muted);
  transition: background var(--ease-ui), color var(--ease-ui);
}
.nav-collapse:hover { background: var(--nav-hover); color: var(--nav-text-strong); }
.nav-collapse .collapse-out { display: none; }

/* --- Menu ----------------------------------------------------------------- */

.nav-menu { padding: 12px 10px; display: flex; flex-direction: column; gap: 2px; flex: 1; overflow-y: auto; overflow-x: hidden; }
.nav-menu a {
  position: relative;
  color: var(--nav-text);
  height: 40px;
  padding: 0 12px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  gap: 11px;
  font-size: 13.5px;
  font-weight: 600;
  white-space: nowrap;
  transition: background var(--ease-ui), color var(--ease-ui);
}
.nav-icon { width: 20px; height: 20px; flex: 0 0 auto; opacity: .92; }
.nav-label { min-width: 0; overflow: hidden; }
.nav-menu a:hover { background: var(--nav-hover); color: var(--nav-text-strong); }
.nav-menu a.active { background: var(--nav-active-bg); color: var(--nav-active-text); font-weight: 700; }
.nav-menu a.active .nav-icon { opacity: 1; }

/* A hairline on the active item. Small, but it is what tells the eye at a
   glance which screen it is looking at without having to read the label. */
.nav-menu a.active::before {
  content: "";
  position: absolute; left: -10px; top: 50%;
  width: 3px; height: 20px; margin-top: -10px;
  border-radius: 0 3px 3px 0;
  background: var(--primary);
}

/* New Sale is the reason the till is switched on. It is the only filled item
   in the menu and it stays filled whether or not it is the current page. */
.nav-menu a.pos-link {
  background: var(--primary);
  color: var(--primary-contrast);
  font-weight: 700;
  margin-bottom: 6px;
  box-shadow: var(--accent-glow);
}
.nav-menu a.pos-link .nav-icon { opacity: 1; }
.nav-menu a.pos-link:hover, .nav-menu a.pos-link.active { background: var(--primary-strong); color: var(--primary-contrast); }
.nav-menu a.pos-link.active::before { display: none; }

/* --- Footer --------------------------------------------------------------- */

.sidebar-footer { flex: 0 0 auto; padding: 10px 12px 12px; border-top: 1px solid var(--nav-line); color: var(--nav-muted); font-size: 11px; }
.sidebar-user { display: flex; align-items: center; gap: 10px; }
.sidebar-avatar {
  flex: 0 0 auto; width: 34px; height: 34px;
  display: grid; place-items: center; border-radius: 50%;
  background: var(--primary-weak); color: var(--primary-ink);
  font-size: 14px; font-weight: 800;
}
.sidebar-user-text { min-width: 0; flex: 1; }
.sidebar-user-text strong { display: block; color: var(--nav-text-strong); font-size: 13px; line-height: 1.2; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sidebar-user-text small { display: block; margin-top: 1px; color: var(--nav-muted); font-size: 9.5px; letter-spacing: .06em; text-transform: uppercase; font-weight: 700; }

/* Two icon buttons instead of a labelled pair and a full-width logout bar.
   That was three stacked rows of chrome at the bottom of every screen; this is
   one, and it gives the height back to the menu. */
.sidebar-tools { display: flex; align-items: center; gap: 4px; flex: 0 0 auto; }
.sidebar-tools form { margin: 0; display: flex; }
.tool-button {
  width: 32px; height: 32px;
  display: grid; place-items: center;
  border: 0; border-radius: 9px;
  background: transparent; color: var(--nav-text);
  transition: background var(--ease-ui), color var(--ease-ui), transform var(--ease-ui);
}
.tool-button .ui-icon { width: 17px; height: 17px; }
.tool-button:hover { background: var(--nav-hover); color: var(--nav-text-strong); }
.tool-button.is-danger:hover { background: var(--danger-soft); color: var(--danger); }
.tool-button:active { transform: scale(.92); }

/* The single sun/moon button on the sign-in and expired cards, which have no
   sidebar to hold the pair. */
.theme-toggle {
  width: 34px; height: 34px;
  display: inline-grid; place-items: center;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--panel); color: var(--text-2);
  transition: background var(--ease-ui), color var(--ease-ui), border-color var(--ease-ui);
}
.theme-toggle:hover { border-color: var(--primary); color: var(--primary-ink); background: var(--primary-weak); }

/* Which glyph shows is decided here, off the attribute the pre-paint script
   sets, so the right one is on screen from the first frame rather than being
   swapped in afterwards. Shared by the sidebar tool button and the card one. */
.theme-out { display: none; }
:root[data-theme="dark"] .theme-in { display: none; }
:root[data-theme="dark"] .theme-out { display: block; }

.sidebar-status { margin: 9px 0 0; color: var(--nav-muted); font-size: 10.5px; line-height: 1.5; display: flex; align-items: center; gap: 6px; }
.status-dot { width: 7px; height: 7px; flex: 0 0 auto; display: inline-block; background: var(--success); border-radius: 50%; }
.status-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ==========================================================================
   Mini sidebar
   ---------------------------------------------------------------------------
   Icons only, labels on hover. The width is the only thing that animates —
   the labels are switched off outright rather than faded, because a label
   half-way through a fade while the panel is still moving is what makes a
   collapsing sidebar feel cheap.
   ========================================================================== */

:root[data-nav="mini"] { --nav-w: var(--nav-w-mini); }

:root[data-nav="mini"] .brand { padding: 0; justify-content: center; }
:root[data-nav="mini"] .brand-id { flex: 0 0 auto; justify-content: center; }
:root[data-nav="mini"] .brand-text,
:root[data-nav="mini"] .nav-label,
:root[data-nav="mini"] .sidebar-user-text,
:root[data-nav="mini"] .status-text { display: none; }

/* No room for the control beside the mark, so the header becomes a short
   vertical stack instead. Nothing is positioned by hand and nothing has to be
   padded around. */
:root[data-nav="mini"] .brand { flex-direction: column; height: auto; padding: 12px 0 10px; gap: 8px; }
:root[data-nav="mini"] .nav-collapse .collapse-in { display: none; }
:root[data-nav="mini"] .nav-collapse .collapse-out { display: block; }

:root[data-nav="mini"] .nav-menu { padding: 12px 8px; align-items: center; }
:root[data-nav="mini"] .nav-menu a { width: 46px; height: 44px; padding: 0; justify-content: center; gap: 0; }
:root[data-nav="mini"] .nav-icon { width: 21px; height: 21px; opacity: 1; }
/* The filled pill already says which page this is; the hairline beside it only
   showed as a stray sliver against the window edge. */
:root[data-nav="mini"] .nav-menu a.active::before { display: none; }

:root[data-nav="mini"] .sidebar-footer { padding: 10px 8px 12px; }
:root[data-nav="mini"] .sidebar-user { flex-direction: column; gap: 6px; }
:root[data-nav="mini"] .sidebar-tools { flex-direction: column; gap: 2px; }
:root[data-nav="mini"] .sidebar-avatar { width: 32px; height: 32px; font-size: 13px; }
:root[data-nav="mini"] .sidebar-status { justify-content: center; margin-top: 8px; }

/* --- Hover labels ---------------------------------------------------------
   A real flyout rather than the browser's title tooltip: it appears instantly
   instead of after a second, it is styled like the rest of the application,
   and it cannot be clipped by the sidebar because it is positioned against the
   viewport. Only ever built for the mini sidebar, where the label is the one
   piece of information missing from the screen. */
:root[data-nav="mini"] .nav-menu a::after,
:root[data-nav="mini"] .tool-button::after {
  content: attr(data-label);
  /* Fixed, with top left to auto: the box keeps the vertical static position it
     would have had inside the row, but escapes .nav-menu's scroll container,
     which would otherwise clip it. An absolutely positioned flyout cannot work
     here - overflow-y: auto forces the horizontal axis to clip too. */
  position: fixed;
  left: calc(var(--nav-w-mini) + 8px);
  margin-top: -2px;
  padding: 7px 11px;
  border-radius: 9px;
  background: var(--flyout-bg);
  color: var(--flyout-ink);
  font-size: 12.5px; font-weight: 650; white-space: nowrap;
  box-shadow: var(--shadow-lg);
  opacity: 0; visibility: hidden;
  transition: opacity .12s ease, visibility .12s ease;
  pointer-events: none;
  z-index: 90;
}
:root[data-nav="mini"] .nav-menu a:hover::after,
:root[data-nav="mini"] .nav-menu a:focus-visible::after,
:root[data-nav="mini"] .tool-button:hover::after,
:root[data-nav="mini"] .tool-button:focus-visible::after {
  opacity: 1; visibility: visible;
}
.main-area {
  margin-left: var(--nav-w);
  width: calc(100% - var(--nav-w));
  min-height: 100vh;
  transition: margin-left var(--ease-nav), width var(--ease-nav);
}

.topbar {
  height: 60px;
  background: var(--topbar-bg);
  backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid var(--line);
  padding: 0 24px;
  display: flex; align-items: center; gap: 14px;
  position: sticky; top: 0; z-index: 30;
}
.topbar-title { font-family: var(--font-display); font-weight: 650; font-size: 15.5px; letter-spacing: -.2px; }
.topbar-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.icon-button {
  width: 36px; height: 36px; display: grid; place-items: center;
  border: 0; background: transparent; color: var(--text-2);
  border-radius: 9px; transition: background var(--ease-ui), color var(--ease-ui);
}
.icon-button:hover { background: var(--surface-3); color: var(--text); }
.mobile-menu { display: none; }
.link-button { border: 0; background: transparent; color: var(--primary-ink); font-weight: 700; padding: 5px; }
.page-content { padding: 24px; max-width: 1600px; margin: 0 auto; }

/* ==========================================================================
   Page furniture
   ========================================================================== */

.page-heading { display: flex; justify-content: space-between; align-items: flex-end; gap: 20px; margin-bottom: 20px; }
.page-heading p, .panel-header p, .muted { color: var(--muted); margin-bottom: 0; }
.button-row { display: flex; gap: 8px; flex-wrap: wrap; }

.panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  padding: 18px;
}
.panel-header { display: flex; justify-content: space-between; align-items: center; gap: 15px; margin-bottom: 14px; }
.panel-header h2 { margin-bottom: 2px; }
.panel-header a { font-weight: 650; font-size: 13px; }
.panel-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.table-panel { padding: 0; overflow: hidden; }
.table-panel .pagination { padding: 14px 18px; border-top: 1px solid var(--line-soft); }

/* --- Metric cards ----------------------------------------------------------
   The old card wore a coloured dash above the label that carried no meaning.
   The accent now sits on the figure's own baseline as a hairline rule, so the
   card is quieter and the number is what you read first. */
/* auto-fit rather than a fixed five. The dashboard shows six cards to an owner
   and four to a cashier, and the reports page shows five or six depending on
   whether anything came back - a fixed count left one card stranded on a row
   of its own in most of those cases. */
.metric-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 13px; margin-bottom: 20px; }
.metric-card {
  position: relative; overflow: hidden;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px 17px;
  box-shadow: var(--shadow-xs);
  min-height: 112px;
  display: flex; flex-direction: column;
  color: var(--text);
  transition: border-color .14s ease, box-shadow .14s ease, transform .14s ease;
}
a.metric-card:hover { border-color: var(--primary); box-shadow: var(--shadow); transform: translateY(-2px); }
a.metric-card:active { transform: translateY(0); }
.metric-card::before {
  content: "";
  position: absolute; inset: 0 auto 0 0;
  width: 3px;
  background: var(--primary);
  opacity: .85;
}
.metric-card.warning-card::before { background: var(--danger); }
.metric-card.expense-card::before { background: var(--warning); }
.metric-card .metric-label { color: var(--muted); font-weight: 650; font-size: 12.5px; letter-spacing: .01em; }
.metric-card strong { font-family: var(--font-display); font-size: 24px; margin: 8px 0 4px; letter-spacing: -.7px; font-weight: 700; }
.metric-card small { margin-top: auto; font-size: 11.5px; line-height: 1.45; }

.dashboard-grid { display: grid; grid-template-columns: minmax(0, 1.7fr) minmax(320px, .8fr); gap: 16px; align-items: start; }
.compact-list { display: flex; flex-direction: column; }
.compact-row {
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
  padding: 11px 2px; border-bottom: 1px solid var(--line-soft); color: var(--text);
}
.compact-row:last-child { border-bottom: 0; }
.compact-row:hover strong { color: var(--primary-ink); }
.compact-row small, .table-subtext { display: block; margin-top: 2px; }

/* ==========================================================================
   Tables
   ========================================================================== */

.table-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; }
th {
  color: var(--muted);
  text-transform: uppercase;
  font-size: 10.5px;
  letter-spacing: .06em;
  font-weight: 800;
  background: var(--surface-2);
}
th, td { padding: 12px 14px; text-align: left; border-bottom: 1px solid var(--line-soft); white-space: nowrap; }
th { border-bottom-color: var(--line); }
tbody tr:last-child td { border-bottom: 0; }
tbody tr { transition: background .1s ease; }
tbody tr:hover { background: var(--surface-2); }
.text-right { text-align: right; }
.text-center { text-align: center; }
.empty-cell, .empty-state { text-align: center; color: var(--muted); padding: 34px 14px; white-space: normal; }
/* Every .actions-cell in the application is a <td>. `display: flex` took the
   cell out of the table's box model, so it stopped sharing its row's height
   and baseline - on the products list the Edit and Disable buttons sat below
   the row they belonged to and outside the table's own rule lines. Laying the
   buttons out inline keeps the cell a table cell. */
.actions-cell { white-space: nowrap; }
.actions-cell form { display: inline-block; margin: 0; vertical-align: middle; }
.actions-cell > .btn, .actions-cell > a { vertical-align: middle; }
.actions-cell > * + * { margin-left: 6px; }

/* ==========================================================================
   Buttons
   ========================================================================== */

.btn {
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  padding: 9px 14px;
  font-weight: 650;
  font-size: 13.5px;
  line-height: 1.2;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  transition: background var(--ease-ui), border-color var(--ease-ui), color var(--ease-ui), box-shadow var(--ease-ui), transform var(--ease-ui);
  white-space: nowrap;
}
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn:disabled { opacity: .5; cursor: not-allowed; box-shadow: none; transform: none; }

.btn-primary { background: var(--primary); color: var(--primary-contrast); box-shadow: var(--accent-glow); }
.btn-primary:hover:not(:disabled) { background: var(--primary-strong); color: var(--primary-contrast); }

.btn-secondary { background: var(--panel); border-color: var(--line); color: var(--text-2); box-shadow: var(--shadow-xs); }
.btn-secondary:hover:not(:disabled) { background: var(--surface-2); border-color: var(--muted); color: var(--text); }

.btn-large { padding: 11px 18px; font-size: 14.5px; border-radius: 11px; }
.btn-small { padding: 6px 10px; font-size: 12px; border-radius: 8px; }
.btn-block { width: 100%; }

.btn-danger-soft { background: var(--danger-soft); color: var(--danger); border-color: var(--danger-line); }
.btn-danger-soft:hover:not(:disabled) { background: var(--danger-fill); color: #fff; border-color: var(--danger-fill); }
.btn-success-soft { background: var(--success-soft); color: var(--success); border-color: var(--success-line); }
.btn-success-soft:hover:not(:disabled) { background: var(--success-fill); color: #fff; border-color: var(--success-fill); }
.btn-danger { background: var(--danger-fill); border-color: var(--danger-fill); color: #fff; }
.btn-danger:hover:not(:disabled) { background: var(--danger-fill); filter: brightness(.92); }

/* code_sheet.html prints with a plain <button class="button">. */
.button {
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 9px 14px; background: var(--panel); color: var(--text-2);
  font-size: 13.5px; font-weight: 650;
}
.button:hover { background: var(--surface-2); border-color: var(--muted); }

/* ==========================================================================
   Badges, pills and status ink
   ========================================================================== */

.badge {
  display: inline-flex; align-items: center;
  padding: 4px 9px; border-radius: 999px;
  background: var(--surface-3); color: var(--text-2);
  font-size: 11px; font-weight: 750; letter-spacing: .01em;
  white-space: nowrap;
}
.badge-success { background: var(--success-soft); color: var(--success); }
/* The warning and danger variants were being used by templates without ever
   having been defined, so a failed backup and a successful one wore the same
   neutral grey pill. The colour is the whole point of a status badge. */
.badge-warning { background: var(--warning-soft); color: var(--warning); }
.badge-danger { background: var(--danger-soft); color: var(--danger); }

.stock-danger, .danger-text { color: var(--danger) !important; font-weight: 750; }
.stock-good { color: var(--success); font-weight: 650; }
.good-text { color: var(--success); }
.nowrap { white-space: nowrap; }

/* ==========================================================================
   Forms
   ========================================================================== */

.form-input {
  width: 100%; min-height: 42px;
  padding: 9px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--panel);
  color: var(--text);
  outline: none;
  transition: border-color var(--ease-ui), box-shadow var(--ease-ui), background var(--ease-ui);
}
.form-input:hover:not(:focus) { border-color: var(--line-hover); }
.form-input:focus { border-color: var(--primary); box-shadow: 0 0 0 3px var(--focus-ring); }
.form-input::placeholder { color: var(--muted); opacity: .8; }
.form-input:disabled { background: var(--surface-3); color: var(--muted); cursor: not-allowed; }
textarea.form-input { min-height: 92px; line-height: 1.55; resize: vertical; }

/* The native "Choose File" button is drawn by the browser and ignores the
   page palette, so on the import screens it stayed a light grey slab in dark
   mode. Styling the pseudo-element brings it into the theme. */
input[type="file"] { color: var(--text-2); }
input[type="file"]::file-selector-button {
  margin-right: 10px; padding: 7px 12px;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--surface-2); color: var(--text-2);
  font: inherit; font-size: 13px; font-weight: 650; cursor: pointer;
}
input[type="file"]::file-selector-button:hover { background: var(--surface-3); border-color: var(--line-hover); }

.form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.form-group { display: flex; flex-direction: column; gap: 6px; font-weight: 650; font-size: 13px; }
.form-group small { font-weight: 400; font-size: 12px; line-height: 1.45; }
/* Stacked form groups have nothing between them: the 6px gap above is internal,
   and the separation inside a form comes from .form-grid's own gap. In a plain
   panel - a side panel, a settings card - that leaves each label sitting on the
   previous field's help text. Grid children opt back out, so they are not
   spaced twice. */
.form-group + .form-group { margin-top: 14px; }
/* Any container that lays its own groups out with a gap opts back out, or the
   margin is added on top of the gap and the row goes crooked. */
.form-grid .form-group + .form-group,
.filter-bar .form-group + .form-group { margin-top: 0; }
.form-group + button, .form-group + .btn { margin-top: 14px; }
/* Keeps a label and its "*" or "optional" marker on one line inside the
   column-flex .form-group, where they would otherwise be two flex items. */
.label-line { display: block; }
.label-line .optional, .label-line .required { margin-left: 2px; }
.full-width { grid-column: 1 / -1; }
.checkbox-group { justify-content: flex-end; }
.checkbox-input { width: 17px; height: 17px; accent-color: var(--primary); }
input[type="checkbox"], input[type="radio"] { accent-color: var(--primary); }
.model-form { max-width: 900px; }
.form-panel { max-width: 980px; }
.form-actions { display: flex; gap: 9px; margin-top: 20px; flex-wrap: wrap; }
.required { color: var(--danger); margin-left: 3px; }

.field-error, .inline-error {
  color: var(--danger); background: var(--danger-soft);
  border: 1px solid var(--danger-line);
  padding: 9px 11px; border-radius: 8px; font-weight: 600; font-size: 12.5px;
}

/* A wrapping flex row rather than a fixed four-column grid. The grid was
   written for the products screen; the kharcha book puts six controls in the
   same bar, so the last two dropped onto a second row and stretched to half
   the page each. purchase.css and khata.css still impose their own grids on
   their own screens. */
.filter-bar {
  display: flex; flex-wrap: wrap; gap: 10px; align-items: end;
  margin-bottom: 16px; padding: 12px;
  box-shadow: none; background: var(--panel);
}
.filter-bar > * { flex: 1 1 168px; min-width: 0; }
.filter-bar > input { flex: 2 1 240px; }
.filter-bar > button, .filter-bar > .btn { flex: 0 0 auto; }
.filter-bar label { display: flex; align-items: center; gap: 8px; color: var(--muted); font-weight: 650; }
/* A label that also carries .form-group stacks its caption above the control.
   Without resetting align-items it inherits `center` from the rule above,
   which in a column flex centres the caption over the field instead of
   starting it at the left edge. */
.filter-bar label.form-group { flex-direction: column; align-items: stretch; gap: 6px; }
.filter-bar label.form-group > span { font-size: 11px; font-weight: 750; letter-spacing: .05em; text-transform: uppercase; }

/* --- Filter chips ----------------------------------------------------------
   Lived only in khata.css, so the purchase list carried the markup and got
   none of the styling - the quick filters rendered as five plain links run
   together on one line. Page furniture, like .tab-row below. khata.css keeps
   its own copy and still wins on the khata screens. */
.chip-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.chip {
  display: inline-flex; align-items: center;
  border: 1px solid var(--line); background: var(--panel); cursor: pointer;
  padding: 6px 13px; border-radius: 999px;
  font-size: 12.5px; font-weight: 650; color: var(--muted);
  transition: background var(--ease-ui), color var(--ease-ui), border-color var(--ease-ui);
}
.chip:hover { border-color: var(--primary); color: var(--primary-ink); background: var(--primary-weak); }
.chip.is-on { background: var(--primary); border-color: var(--primary); color: var(--primary-contrast); }

/* --- Section tabs ----------------------------------------------------------
   These lived only in purchase.css, so the three Kharcha screens carried the
   markup and got none of the styling - "Kharcha book Recurring bills
   Categories & budgets" rendered as one run-on line of bare links. They are
   page furniture, like .summary-strip and .detail-grid above. purchase.css
   keeps its own copy and still wins on the purchase and supplier screens. */
.tab-row { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
.tab-row a {
  padding: 8px 15px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--panel);
  color: var(--muted); font-weight: 650; font-size: 13px;
}
.tab-row a.active { background: var(--primary); border-color: var(--primary); color: var(--primary-contrast); }
.tab-row a:hover:not(.active) { border-color: var(--primary); color: var(--primary-ink); background: var(--primary-weak); }

.two-column-layout { display: grid; grid-template-columns: minmax(300px, .7fr) minmax(0, 1.4fr); gap: 18px; align-items: start; }
.search-input-row { display: flex; gap: 8px; }

.pagination { display: flex; justify-content: center; align-items: center; gap: 14px; color: var(--muted); font-size: 13px; }
.pagination a {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 8px; padding: 7px 12px; font-weight: 650; color: var(--text-2);
}
.pagination a:hover { border-color: var(--primary); color: var(--primary-ink); background: var(--primary-weak); }

/* ==========================================================================
   Messages
   ========================================================================== */

.messages { margin-bottom: 15px; }
.alert {
  position: relative;
  overflow: hidden;
  padding: 12px 40px 12px 17px;
  border-radius: 11px;
  border: 1px solid;
  margin-bottom: 9px;
  font-size: 13.5px;
  line-height: 1.5;
}
.alert::before { content: ""; position: absolute; inset: 0 auto 0 0; width: 3px; background: currentColor; opacity: .55; }
.alert-success { background: var(--success-soft); border-color: var(--success-line); color: var(--success); }
.alert-danger { background: var(--danger-soft); border-color: var(--danger-line); color: var(--danger); }
.alert-info { background: var(--info-soft); border-color: var(--info-line); color: var(--info); }
/* Every messages.warning() in the application renders alert-warning, and this
   rule did not exist - so six call sites, including "your sign-in form
   expired" and the khata advance-payment prompt, have been showing as an
   unstyled box. The --warning tokens were already defined and simply unused. */
.alert-warning { background: var(--warning-soft); border-color: var(--warning-line); color: var(--warning); }
.alert strong { color: inherit; }
.alert-close { position: absolute; right: 8px; top: 6px; border: 0; background: transparent; font-size: 20px; color: inherit; opacity: .6; border-radius: 6px; padding: 0 6px; }
.alert-close:hover { opacity: 1; }

/* ==========================================================================
   Shared page layout
   ---------------------------------------------------------------------------
   These started life inside khata.css and purchase.css, which meant any screen
   that did not also load one of those stylesheets got the markup but none of
   the layout - a two-column page collapsing into an unstyled stack. They are
   page furniture, not khata furniture, so they belong in the stylesheet every
   page already loads. khata.css and purchase.css still carry their own copies
   and continue to win on their own pages.
   ========================================================================== */

.summary-strip {
  display: grid;
  /* auto-fit rather than a fixed four, because these strips carry four or five
     cards depending on the screen. */
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px;
  margin-bottom: 18px;
}
.summary-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 16px;
  box-shadow: var(--shadow-xs);
}
.summary-card span { display: block; color: var(--muted); font-weight: 650; font-size: 12px; }
.summary-card strong {
  display: block; margin-top: 5px;
  font-family: var(--font-display); font-size: 21px;
  letter-spacing: -.6px; font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.summary-card small { display: block; margin-top: 3px; font-size: 11.5px; color: var(--muted); }
.summary-card.is-due strong { color: var(--danger); }
.summary-card.is-good strong { color: var(--success); }
.summary-card.is-warn strong { color: var(--warning); }

.detail-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(300px, 380px);
  gap: 18px;
  align-items: start;
}
.detail-main { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.detail-side { display: flex; flex-direction: column; gap: 16px; min-width: 0; }

/* A till is often a small or low-resolution screen; below this the side column
   has to go under the main one or both become unusable. */
@media (max-width: 1100px) {
  .detail-grid { grid-template-columns: minmax(0, 1fr); }
}

.khata-table { width: 100%; border-collapse: collapse; }
.khata-table td, .khata-table th { vertical-align: top; }
.khata-table .text-right { font-variant-numeric: tabular-nums; }

/* A compact two-column table inside a narrow side panel.
   The global `th, td { white-space: nowrap }` above is right for a wide data
   table, because that sits inside a scrolling .table-wrap. A side panel has
   nowhere to scroll, so the same rule pushed the value column straight out
   past the panel edge and the reading simply vanished - the paper roll and
   every status badge on the settings and backup screens were invisible.
   Fixed layout keeps the two columns honest; wrapping keeps them inside. */
.detail-side .khata-table { table-layout: fixed; }
.detail-side .khata-table th,
.detail-side .khata-table td {
  white-space: normal;
  overflow-wrap: anywhere;
  padding: 9px 0;
}

.small-note { font-size: 12.5px; margin: 0 0 12px; color: var(--muted); }

/* Help text under a checkbox or radio label. Inline it runs straight into the
   label and, inside a table cell, into the next column - the global
   `th, td { white-space: nowrap }` gives it nowhere to break. */
.checkbox-row span small { display: block; margin-top: 2px; line-height: 1.45; }
/* Same for a hint under a control in a table cell. */
.field-hint { display: block; margin-top: 4px; font-size: 12px; color: var(--muted); }

/* ==========================================================================
   Reports
   ========================================================================== */

.report-metrics { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }

/* Receipt styling lives in receipt.css (thermal roll layout). */

/* ==========================================================================
   Sign in / first run / expired page
   ========================================================================== */

.login-body {
  min-height: 100vh;
  display: grid; place-items: center;
  background:
    radial-gradient(1100px 520px at 8% -10%, var(--glow-a), transparent 60%),
    radial-gradient(900px 520px at 100% 110%, var(--glow-b), transparent 60%),
    var(--bg);
  padding: 20px;
}
.login-card {
  position: relative;
  width: 100%; max-width: 408px;
  background: var(--panel);
  padding: 34px;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  border: 1px solid var(--line);
}
.login-logo { margin: 0 auto 17px; width: 52px; height: 52px; font-size: 18px; }
.login-card h1, .login-card > p, .login-card > small { text-align: center; display: block; }
.login-card > p { color: var(--muted); margin-bottom: 22px; }
.login-card > small { margin-top: 18px; font-size: 12px; }
.login-card .theme-toggle { position: absolute; top: 14px; right: 14px; }

.stack-form { display: flex; flex-direction: column; gap: 15px; }
.stack-form label { font-weight: 650; font-size: 13px; display: flex; flex-direction: column; gap: 7px; }
/* "Keep me signed in" reads as one tap target, so the box and its wording sit
   on a single row instead of inheriting the stacked label layout above. */
.stack-form label.checkbox-row { flex-direction: row; align-items: center; gap: 10px; font-weight: 600; color: var(--muted); cursor: pointer; }
.stack-form label.checkbox-row span { user-select: none; }
.login-card .btn-large { width: 100%; justify-content: center; }

/* ==========================================================================
   Quick code sheet
   ========================================================================== */

.cs-empty code, code {
  background: var(--surface-3);
  border-radius: 6px;
  padding: 2px 6px;
  font-family: var(--font-mono);
  font-size: .92em;
  color: var(--text-2);
}

/* ==========================================================================
   Responsive
   ========================================================================== */

@media (max-width: 950px) {
  /* Off-canvas drawer. Collapsing is pointless here - the sidebar is already
     out of the way - so the mini width is overridden back to full and the
     drawer always slides out with its labels showing, whatever the stored
     preference says. */
  :root, :root[data-nav="mini"] { --nav-w: 245px; }
  .sidebar { transform: translateX(-100%); transition: transform var(--ease-nav); box-shadow: var(--shadow-lg); }
  .sidebar.open { transform: translateX(0); }
  .main-area { margin-left: 0; width: 100%; }
  .money-bar { left: 0; }
  :root[data-nav="mini"] .brand { flex-direction: row; height: 68px; padding: 0 12px 0 16px; }
  :root[data-nav="mini"] .brand-text,
  :root[data-nav="mini"] .nav-label,
  :root[data-nav="mini"] .sidebar-user-text,
  :root[data-nav="mini"] .status-text { display: block; }
  :root[data-nav="mini"] .nav-menu { padding: 12px 10px; align-items: stretch; }
  :root[data-nav="mini"] .nav-menu a { width: auto; padding: 0 12px; justify-content: flex-start; gap: 11px; }
  :root[data-nav="mini"] .sidebar-user { flex-direction: row; }
  :root[data-nav="mini"] .sidebar-tools { flex-direction: row; }
  :root[data-nav="mini"] .nav-menu a::after,
  :root[data-nav="mini"] .tool-button::after { content: none; }
  .nav-collapse { display: none; }
  .mobile-menu { display: grid; }
  .dashboard-grid, .two-column-layout { grid-template-columns: 1fr; }
  .report-metrics { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 680px) {
  .page-content { padding: 14px; }
  .topbar { padding: 0 14px; }
  .page-heading { align-items: stretch; flex-direction: column; }
  .metric-grid, .report-metrics { grid-template-columns: 1fr 1fr; }
  .metric-card { min-height: 104px; }
  .form-grid { grid-template-columns: 1fr; }
  .filter-bar { grid-template-columns: 1fr; }
  .topbar-title { display: none; }
}
@media (max-width: 420px) {
  .metric-grid { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}

/* ==========================================================================
   Print
   ---------------------------------------------------------------------------
   Paper is always white, whatever the screen is set to. Forcing the light
   tokens back means a shop working in dark mode does not print a black page.
   ========================================================================== */

/* ==========================================================================
   Connection and the outbox
   The sidebar line has to be readable at a glance from across the counter,
   because it is the only place the cashier is told a bill has not gone out.
   ========================================================================== */
.status-dot.is-wait { background: var(--warning); animation: outbox-pulse 1.8s ease-in-out infinite; }
.status-dot.is-bad { background: var(--danger); }
.sidebar-status.is-clickable { cursor: pointer; color: var(--nav-text); }
.sidebar-status.is-clickable:hover { text-decoration: underline; }

@keyframes outbox-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
@media (prefers-reduced-motion: reduce) { .status-dot.is-wait { animation: none; } }

.outbox-panel {
  position: fixed; right: 18px; bottom: 18px; z-index: 1400; width: min(360px, calc(100vw - 36px));
  max-height: min(60vh, 520px); display: flex; flex-direction: column;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-lg); overflow: hidden;
}
.outbox-panel[hidden] { display: none; }
.outbox-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--line); }
.outbox-close { background: none; border: 0; font-size: 20px; line-height: 1; color: var(--muted); cursor: pointer; }
.outbox-body { overflow-y: auto; padding: 6px 0; }
.outbox-empty { margin: 0; padding: 18px 14px; color: var(--muted); font-size: 13px; text-align: center; }
.outbox-row { padding: 10px 14px; border-bottom: 1px solid var(--line-soft, var(--line)); font-size: 13px; }
.outbox-row:last-child { border-bottom: 0; }
.outbox-row-main { display: flex; justify-content: space-between; gap: 10px; }
.outbox-row small { color: var(--muted); font-size: 11.5px; }
.outbox-row.is-blocked { background: var(--danger-soft); }
.outbox-error { margin: 6px 0 8px; color: var(--danger); font-size: 12px; }
.outbox-actions { display: flex; gap: 8px; }
.outbox-foot { padding: 10px 14px; border-top: 1px solid var(--line); }
.outbox-foot .btn { width: 100%; }

/* A sync note is a whole sentence, and left on one line it drags the table
   wider than the panel and pushes the amount off the screen. */
.late-bills td:first-child { max-width: 26rem; }
.late-bills .table-subtext { white-space: normal; }

@media print {
  :root, :root[data-theme="dark"] {
    color-scheme: light;
    --bg: #fff; --panel: #fff;
    --surface-2: #fff; --surface-3: #fff;
    --text: #000; --text-2: #222; --muted: #444;
    --line: #ccc; --line-soft: #ddd;
    --primary: #000; --primary-ink: #000; --primary-weak: #fff; --primary-strong: #000;
    --success: #000; --danger: #000; --warning: #000; --info: #000;
    --success-soft: #fff; --danger-soft: #fff; --warning-soft: #fff; --info-soft: #fff;
    --sidebar: #000;
    --shadow-xs: none; --shadow-sm: none; --shadow: none; --shadow-lg: none;
  }
  body { background: #fff; }
  .sidebar, .topbar, .no-print, .messages, .theme-toggle { display: none !important; }
  .main-area { margin: 0; width: 100%; }
  .page-content { padding: 0; }
  .panel { border: 1px solid #ccc; box-shadow: none; }
}
