/* ==========================================================================
   POS UI - design system
   --------------------------------------------------------------------------
   Single source of truth for the appearance of the whole product: POS
   terminal, hotel (HMS) screens, back-office pages, forms, tables, modals.

   It loads AFTER style.min.css (the Bootstrap-based template) and after
   custom.css, so it refines them without either being edited out. The
   template keeps owning layout primitives - grid, utilities, components -
   and this file owns colour, type, spacing rhythm, states and the POS
   workspace shell.

   Sections
     01  Tokens + theme presets
     02  Reset / base / typography
     03  Scrollbars
     04  App shell + sidebar
     05  Header
     06  Cards + panels
     07  Buttons
     08  Forms + controls
     09  Tables
     10  Badges, pills, status
     11  Modals, dropdowns, tooltips, toasts
     12  POS workspace
     13  Running orders panel
     14  Products panel
     15  Cart panel
     16  Hotel / folio / room components
     17  Empty, loading + feedback states
     18  Theme panel
     19  Density
     20  Responsive
   ========================================================================== */


/* ==========================================================================
   01  TOKENS + THEME PRESETS
   ========================================================================== */

:root {
  /* -- brand ------------------------------------------------------------ */
  --pos-primary: #2563EB;
  --pos-primary-hover: #1D4ED8;
  --pos-primary-soft: #EFF6FF;
  --pos-primary-rgb: 37, 99, 235;

  /* -- semantic --------------------------------------------------------- */
  --pos-success: #059669;
  --pos-success-soft: #ECFDF5;
  --pos-warning: #D97706;
  --pos-warning-soft: #FFFBEB;
  --pos-danger: #DC2626;
  --pos-danger-hover: #B91C1C;
  --pos-danger-soft: #FEF2F2;
  --pos-info: #0891B2;
  --pos-info-soft: #ECFEFF;
  --pos-violet: #7C3AED;
  --pos-violet-soft: #F5F3FF;

  /* -- surfaces --------------------------------------------------------- */
  --pos-page-bg: #F5F7FA;
  --pos-surface: #FFFFFF;
  --pos-surface-alt: #F8FAFC;
  --pos-surface-hover: #F1F5F9;

  /* -- text ------------------------------------------------------------- */
  --pos-text-primary: #0F172A;
  --pos-text-secondary: #475569;
  --pos-text-muted: #94A3B8;

  /* Money. Its own token because a price is neither body text nor an action:
     black made the product card monotone, and the brand blue made the figure
     look like a link - which is exactly how it read before. Emerald-700 is
     unmistakably an amount, is reserved for nothing else in this system, and
     clears 5.48:1 on white, so it is legible at the 14px a price is set in.
     (Emerald-600 sits at 3.77:1 and would only pass for large text.) */
  --pos-price: #047857;

  /* -- lines ------------------------------------------------------------ */
  --pos-border: #E2E8F0;
  --pos-border-strong: #CBD5E1;

  /* -- elevation -------------------------------------------------------- */
  --pos-shadow-sm: 0 1px 2px rgba(15, 23, 42, .05);
  --pos-shadow-md: 0 4px 12px rgba(15, 23, 42, .08);
  --pos-shadow-lg: 0 12px 32px rgba(15, 23, 42, .12);

  /* -- radii ------------------------------------------------------------ */
  --pos-radius-sm: 6px;
  --pos-radius-md: 10px;
  --pos-radius-lg: 12px;
  --pos-radius-pill: 999px;

  /* -- shell metrics ---------------------------------------------------- */
  --pos-sidebar-w: 240px;
  --pos-sidebar-w-collapsed: 70px;
  --pos-header-h: 62px;

  /* -- rhythm (density-aware) ------------------------------------------- */
  --pos-control-h: 42px;
  --pos-gap: 12px;
  --pos-pad: 14px;
  --pos-font: 13.5px;

  /* -- sidebar skin (theme panel overrides these) ----------------------- */
  --pos-sidebar-bg: #FFFFFF;
  --pos-sidebar-border: var(--pos-border);
  --pos-sidebar-link: var(--pos-text-secondary);

  /* -- motion ----------------------------------------------------------- */
  --pos-t: 150ms cubic-bezier(.4, 0, .2, 1);

  --pos-font-family: Inter, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* -- Bootstrap bridge --------------------------------------------------
     The template's components read --bs-* tokens. Re-pointing them here
     means every stock Bootstrap element the pages use - alerts, links,
     progress bars, list groups - picks up the new palette without each
     one needing a bespoke override. */
  --bs-primary: var(--pos-primary);
  --bs-primary-rgb: var(--pos-primary-rgb);
  --bs-secondary: #64748B;
  --bs-secondary-rgb: 100, 116, 139;
  --bs-success: var(--pos-success);
  --bs-success-rgb: 5, 150, 105;
  --bs-info: var(--pos-info);
  --bs-info-rgb: 8, 145, 178;
  --bs-warning: var(--pos-warning);
  --bs-warning-rgb: 217, 119, 6;
  --bs-danger: var(--pos-danger);
  --bs-danger-rgb: 220, 38, 38;
  --bs-light: var(--pos-surface-alt);
  --bs-dark: var(--pos-text-primary);
  --bs-body-color: var(--pos-text-primary);
  --bs-body-color-rgb: 15, 23, 42;
  --bs-body-bg: var(--pos-page-bg);
  --bs-border-color: var(--pos-border);
  --bs-border-radius: var(--pos-radius-sm);
  --bs-border-radius-sm: var(--pos-radius-sm);
  --bs-border-radius-lg: var(--pos-radius-md);
  --bs-link-color: var(--pos-primary);
  --bs-link-hover-color: var(--pos-primary-hover);
  --bs-body-font-family: var(--pos-font-family);
  --bs-body-font-size: var(--pos-font);
  --bs-emphasis-color: var(--pos-text-primary);
}

/* Presets. Each only re-points the brand ramp - every preset stays light. */
:root[data-pos-theme="emerald"] {
  --pos-primary: #059669;
  --pos-primary-hover: #047857;
  --pos-primary-soft: #ECFDF5;
  --pos-primary-rgb: 5, 150, 105;
}
:root[data-pos-theme="indigo"] {
  --pos-primary: #4F46E5;
  --pos-primary-hover: #4338CA;
  --pos-primary-soft: #EEF2FF;
  --pos-primary-rgb: 79, 70, 229;
}
:root[data-pos-theme="slate"] {
  --pos-primary: #475569;
  --pos-primary-hover: #334155;
  --pos-primary-soft: #F1F5F9;
  --pos-primary-rgb: 71, 85, 105;
}

/* Sidebar skins. */
:root[data-pos-sidebar="soft"] {
  --pos-sidebar-bg: #F8FAFC;
  --pos-sidebar-border: #E2E8F0;
}
:root[data-pos-sidebar="tint"] {
  --pos-sidebar-bg: var(--pos-primary-soft);
  --pos-sidebar-border: rgba(var(--pos-primary-rgb), .18);
  --pos-sidebar-link: #334155;
}


/* ==========================================================================
   02  RESET / BASE / TYPOGRAPHY
   ========================================================================== */

*,
*::before,
*::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  background: var(--pos-page-bg) !important;
  color: var(--pos-text-primary) !important;
  font-family: var(--pos-font-family) !important;
  font-size: var(--pos-font) !important;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  letter-spacing: 0;
}

/* The template shipped a decorative serif for headings and labels. It is
   the single biggest reason the UI read as dated, and at 1.5rem inside a
   narrow column it forced labels to wrap one character per line. */
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6,
label, .form-label, th, .card-title, .main-title, .header-text {
  font-family: var(--pos-font-family) !important;
  letter-spacing: 0 !important;
  text-transform: none !important;
}

h1, .h1 { font-size: 22px !important; font-weight: 700 !important; color: var(--pos-text-primary) !important; }
h2, .h2 { font-size: 20px !important; font-weight: 700 !important; }
h3, .h3 { font-size: 18px !important; font-weight: 700 !important; }
h4, .h4 { font-size: 16px !important; font-weight: 600 !important; }
h5, .h5 { font-size: 15px !important; font-weight: 600 !important; }
h6, .h6 { font-size: 14px !important; font-weight: 600 !important; }

h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
  color: var(--pos-text-primary);
  margin-bottom: .4rem;
}

/* Labels: readable, compact, never decorative. */
label,
.form-label {
  font-size: 12.5px !important;
  font-weight: 600 !important;
  color: var(--pos-text-secondary) !important;
  margin-bottom: 5px !important;
  line-height: 1.35 !important;
  display: inline-block;
  /* Long field names must wrap on word boundaries, never per character. */
  overflow-wrap: break-word;
  word-break: normal;
  hyphens: none;
}

small, .small, .fs-sm { font-size: 12px !important; }
.fs-12 { font-size: 12px !important; }
.fs-15 { font-size: 14px !important; }
.main-title { font-size: 15px !important; font-weight: 600 !important; }

a {
  color: var(--pos-primary);
  text-decoration: none;
  transition: color var(--pos-t);
}
a:hover { color: var(--pos-primary-hover); }

.text-secondary { color: var(--pos-text-secondary) !important; }
.text-muted, .text-grey { color: var(--pos-text-muted) !important; }
.text-dark { color: var(--pos-text-primary) !important; }
.text-primary { color: var(--pos-primary) !important; }
.text-success { color: var(--pos-success) !important; }
.text-warning { color: var(--pos-warning) !important; }

/* The template forced pure red + bold on every .text-danger, which made
   ordinary helper text shout. Keep it a signal, not an alarm. */
.text-danger {
  color: var(--pos-danger) !important;
  font-weight: 600 !important;
}

/* Money and quantities line up column-wise. */
.pos-num,
.price, .pprice, .tprice, .del-price, .delivery-price,
#sub-total, #sub-total2, #grand-total, #grand-total2,
#total_discount, #total_discount2, #Service-charge, #Service-charge2,
td.text-end, .text-end input.form-control {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

hr { border-color: var(--pos-border); opacity: 1; }

::selection { background: rgba(var(--pos-primary-rgb), .16); }


/* ==========================================================================
   03  SCROLLBARS
   ========================================================================== */

* { scrollbar-width: thin; scrollbar-color: var(--pos-border-strong) transparent; }

::-webkit-scrollbar { width: 9px; height: 9px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--pos-border-strong);
  border-radius: var(--pos-radius-pill);
  border: 2px solid transparent;
  background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background: #94A3B8; background-clip: content-box; }
::-webkit-scrollbar-corner { background: transparent; }


/* ==========================================================================
   04  APP SHELL + SIDEBAR
   ========================================================================== */

.sidebar {
  background: var(--pos-sidebar-bg) !important;
  border-right: 1px solid var(--pos-sidebar-border) !important;
  box-shadow: none !important;
  width: var(--pos-sidebar-w);
  transition: width var(--pos-t);
}

.sidebar-header {
  height: var(--pos-header-h);
  min-height: var(--pos-header-h);
  display: flex !important;
  align-items: center;
  padding: 0 14px !important;
  border-bottom: 1px solid var(--pos-sidebar-border);
  background: var(--pos-sidebar-bg);
}

/* The brand mark was rendered as a gradient-clipped text block and the
   logo image was stretched to 100% of the rail. Both are replaced by a
   contained, correctly-proportioned mark. */
.sidebar-logo {
  display: flex !important;
  align-items: center;
  gap: 10px;
  margin: 0 !important;
  font-size: 15px !important;
  font-weight: 700 !important;
  color: var(--pos-text-primary) !important;
  background: none !important;
  -webkit-background-clip: border-box !important;
  -webkit-text-fill-color: currentColor !important;
  letter-spacing: -.01em;
  overflow: hidden;
  white-space: nowrap;
}
/* Sizing for the brand images lives in section 34 (BRAND BAND), which is the
   single authority. A `.sidebar-logo img` rule here out-specifies the class
   selectors there, so it must not restate height/width. */
.sidebar-logo img { flex: 0 1 auto; }

.sidebar-body,
#sidebarMenu {
  max-height: calc(100vh - var(--pos-header-h) - 58px);
  overflow-y: auto;
  overflow-x: hidden;
  padding: 10px 10px 16px !important;
}

.nav-label {
  font-size: 10.5px !important;
  font-weight: 700 !important;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--pos-text-muted) !important;
  padding: 14px 10px 6px !important;
  margin: 0 !important;
  border: 0 !important;
  white-space: nowrap;
}

.nav-sidebar { flex-direction: column; gap: 1px; }
.nav-sidebar .nav-item { width: 100%; }

.nav-sidebar .nav-link {
  display: flex !important;
  align-items: center;
  gap: 11px;
  min-height: 44px;
  padding: 9px 11px !important;
  margin: 0;
  border-radius: var(--pos-radius-sm);
  font-size: 13.5px !important;
  font-weight: 500;
  line-height: 1.3;
  color: var(--pos-sidebar-link) !important;
  background: transparent;
  position: relative;
  transition: background var(--pos-t), color var(--pos-t);
}

.nav-sidebar .nav-link span {
  font-size: 13.5px !important;
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Icons are Remix Icon glyphs, so they take a real colour. They sit in a
   soft tinted tile that fills in on hover and active - that is what makes
   the rail read as designed rather than dead. The old build used
   flat-colour PNGs, which could never respond to state at all. */
.nav-sidebar .nav-link i.icon,
.nav-sidebar .nav-link .icon {
  display: inline-flex !important;
  align-items: center;
  justify-content: center;
  width: 28px !important;
  min-width: 28px;
  height: 28px;
  font-size: 17px;
  line-height: 1;
  text-align: center;
  flex: 0 0 28px;
  border-radius: 7px;
  background: var(--pos-primary-soft);
  color: var(--pos-primary) !important;
  opacity: 1;
  transition: background var(--pos-t), color var(--pos-t);
}

/* Colour is forced on every state. The template and the old custom.css
   both set a maroon on .nav-link (and bumped it to 1.5rem on hover),
   which is why some menu entries still rendered brown. */
.sidebar .nav-sidebar .nav-link,
.sidebar .nav-sidebar .nav-link:link,
.sidebar .nav-sidebar .nav-link:visited {
  color: var(--pos-sidebar-link) !important;
  font-size: 13.5px !important;
}

.sidebar .nav-sidebar .nav-link:hover,
.sidebar .nav-sidebar .nav-link:focus {
  background: var(--pos-surface-hover) !important;
  color: var(--pos-text-primary) !important;
  font-size: 13.5px !important;   /* template bumped this to 1.5rem on hover */
}
.nav-sidebar .nav-link:hover i.icon {
  background: rgba(var(--pos-primary-rgb), .16);
  color: var(--pos-primary) !important;
}

.sidebar .nav-sidebar .nav-link.active {
  background: var(--pos-primary-soft) !important;
  color: var(--pos-primary) !important;
  font-weight: 600;
  font-size: 13.5px !important;
}
.nav-sidebar .nav-link.active i.icon {
  background: var(--pos-primary);
  color: #fff !important;
}

/* Small active rail marker. */
.nav-sidebar .nav-link.active::before {
  content: "";
  position: absolute;
  left: -10px;
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 22px;
  border-radius: 0 3px 3px 0;
  background: var(--pos-primary);
}

/* Parent of an open group. */
.nav-sidebar .nav-link.has-sub::after {
  content: "\ea4e";                     /* remixicon arrow-down-s-line */
  font-family: remixicon !important;
  font-size: 15px;
  margin-left: auto;
  color: var(--pos-text-muted);
  transition: transform var(--pos-t);
  flex: 0 0 auto;
}
.nav-group.show > .nav-link.has-sub::after { transform: rotate(180deg); }

.nav-group.show { height: auto; overflow: visible; }

.nav-sub {
  flex-direction: column;
  gap: 1px;
  margin: 2px 0 4px;
  padding-left: 30px;
  position: relative;
}
.nav-sub::before {
  content: "";
  position: absolute;
  left: 20px;
  top: 2px;
  bottom: 2px;
  width: 1px;
  background: var(--pos-border);
}
.nav-sub-link,
.nav-sub .nav-sub-link.dropdown-item {
  display: block;
  padding: 7px 10px !important;
  border-radius: var(--pos-radius-sm);
  font-size: 12.75px !important;
  font-weight: 500;
  color: var(--pos-text-secondary) !important;
  background: transparent !important;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: background var(--pos-t), color var(--pos-t);
}
.nav-sub-link:hover,
.nav-sub .nav-sub-link.dropdown-item:hover {
  background: var(--pos-surface-hover) !important;
  color: var(--pos-text-primary) !important;
}
.nav-sub-link.active,
.nav-sub .nav-sub-link.dropdown-item.active {
  background: var(--pos-primary-soft) !important;
  color: var(--pos-primary) !important;
  font-weight: 600;
}

.sidebar-footer {
  background: var(--pos-sidebar-bg) !important;
  border-top: 1px solid var(--pos-sidebar-border);
  box-shadow: none !important;
  padding: 10px 12px;
}
.sidebar-footer-body h6 a,
.sidebar-footer-body .h6 a {
  color: var(--pos-text-primary) !important;
  font-size: 13px;
  font-weight: 600;
}

/* -- collapsed rail ------------------------------------------------------ */
body.sidebar-collapsed .sidebar,
.sidebar.pos-collapsed {
  width: var(--pos-sidebar-w-collapsed) !important;
}
body.sidebar-collapsed .nav-sidebar .nav-link span,
body.sidebar-collapsed .nav-label,
body.sidebar-collapsed .nav-sub,
body.sidebar-collapsed .sidebar-logo span,
body.sidebar-collapsed .nav-link.has-sub::after,
body.sidebar-collapsed .sidebar-footer-body { display: none !important; }
body.sidebar-collapsed .nav-sidebar .nav-link {
  justify-content: center;
  padding: 9px !important;
}
body.sidebar-collapsed .main { margin-left: var(--pos-sidebar-w-collapsed); }

.main {
  background: var(--pos-page-bg);
  background-repeat: no-repeat;
  background-position: center;
  transition: margin-left var(--pos-t);
}


/* ==========================================================================
   05  HEADER
   ========================================================================== */

.header-main {
  background: var(--pos-surface) !important;
  border-bottom: 1px solid var(--pos-border) !important;
  height: var(--pos-header-h);
  min-height: var(--pos-header-h);
  display: flex;
  align-items: center;
  gap: 10px;
  box-shadow: none !important;
  z-index: 900;
  /* position is deliberately not set: the POS pins its header, the
     back-office leaves it in flow. Forcing either here breaks the other. */
}

/* Oversized decorative branding out; a clean tenant name in. */
.header-text {
  font-family: var(--pos-font-family) !important;
  font-size: 15px !important;
  font-weight: 700 !important;
  color: var(--pos-text-primary) !important;
  letter-spacing: -.01em;
  margin: 0 !important;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.menu-link {
  width: 38px;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--pos-radius-sm);
  color: var(--pos-text-secondary);
  background: transparent;
  font-size: 20px;
  flex: 0 0 auto;
  transition: background var(--pos-t), color var(--pos-t);
}
.menu-link:hover {
  background: var(--pos-surface-hover);
  color: var(--pos-primary);
}

/* Compact icon buttons in the header strip. */
.header-main .btn > i,
.header-main .nav-link > i { font-size: 18px; line-height: 1; }

.header-main .form-search,
.header-main .form-control {
  background: var(--pos-surface) !important;
  border: 1px solid var(--pos-border) !important;
  border-radius: var(--pos-radius-sm);
  height: 38px;
  font-size: 13px;
}
.header-main .form-search:focus-within {
  border-color: var(--pos-primary) !important;
  box-shadow: 0 0 0 3px rgba(var(--pos-primary-rgb), .12) !important;
}

.full-scrn { display: flex; align-items: center; gap: 8px; }

/* Tenant logo in the header. The template shipped no rule for the image
   inside .avatar, so a wide logo rendered at its natural size and smeared
   across the header. Contain it in a fixed circle. */
.avatar {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  flex: 0 0 auto;
  border-radius: 50%;
  overflow: hidden;
  background: var(--pos-surface-hover);
  border: 1px solid var(--pos-border);
}
.avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.avatar.avatar-xl { width: 66px; height: 66px; }

.avatar.online::after,
.avatar-xl.online::after {
  content: "";
  position: absolute;
  right: 0;
  bottom: 0;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: 2px solid var(--pos-surface);
  background: var(--pos-success) !important;
}

/* Bill-number field: wide enough that its placeholder is not clipped. */
#invoiceNo { min-width: 190px; }

.dropdown-profile .dropdown-link {
  display: inline-flex;
  align-items: center;
  border-radius: var(--pos-radius-pill);
}

/* Online / offline pill. */
.pos-conn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 28px;
  padding: 0 10px;
  border-radius: var(--pos-radius-pill);
  font-size: 11.5px;
  font-weight: 600;
  background: var(--pos-success-soft);
  color: var(--pos-success);
  border: 1px solid rgba(5, 150, 105, .18);
  white-space: nowrap;
}
.pos-conn::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
}
.pos-conn.is-offline {
  background: var(--pos-danger-soft);
  color: var(--pos-danger);
  border-color: rgba(220, 38, 38, .18);
}


/* ==========================================================================
   06  CARDS + PANELS
   ========================================================================== */

.card {
  background: var(--pos-surface) !important;
  border: 1px solid var(--pos-border) !important;
  border-radius: var(--pos-radius-lg) !important;
  box-shadow: var(--pos-shadow-sm) !important;
  overflow: hidden;
}

.card-header {
  background: var(--pos-surface) !important;
  border-bottom: 1px solid var(--pos-border) !important;
  padding: 11px var(--pos-pad) !important;
  min-height: 0;
  display: block;
}
.card-header h6,
.card-header .h6,
.card-header h5 {
  font-size: 14.5px !important;
  font-weight: 600 !important;
  color: var(--pos-text-primary) !important;
  margin: 0 !important;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.card-body { padding: var(--pos-pad) !important; }

.card-footer {
  background: var(--pos-surface-alt) !important;
  border-top: 1px solid var(--pos-border) !important;
  padding: 11px var(--pos-pad) !important;
}

/* Page heading block used across back-office screens. */
.pos-page-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--pos-gap);
  margin-bottom: 16px;
  flex-wrap: wrap;
}
.pos-page-head h1,
.pos-page-head h2,
.pos-page-head h3 {
  font-size: 19px !important;
  font-weight: 700 !important;
  margin: 0 !important;
}

/* Stat tile. */
.pos-stat {
  background: var(--pos-surface);
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-lg);
  padding: 15px 16px;
  box-shadow: var(--pos-shadow-sm);
}
.pos-stat__label {
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--pos-text-muted);
  margin-bottom: 6px;
}
.pos-stat__value {
  font-size: 23px;
  font-weight: 700;
  line-height: 1.15;
  color: var(--pos-text-primary);
  font-variant-numeric: tabular-nums;
}


/* ==========================================================================
   07  BUTTONS
   ========================================================================== */

.btn {
  --bs-btn-padding-y: 0;
  --bs-btn-padding-x: 14px;
  min-height: var(--pos-control-h);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 0 14px;
  border-radius: var(--pos-radius-sm) !important;
  font-family: var(--pos-font-family);
  font-size: 13.5px !important;
  font-weight: 600 !important;
  line-height: 1.2;
  letter-spacing: 0;
  white-space: nowrap;
  text-transform: none;
  box-shadow: none !important;
  transition: background var(--pos-t), border-color var(--pos-t),
              color var(--pos-t), transform var(--pos-t);
}
.btn:active { transform: translateY(1px); }
.btn i { font-size: 16px; line-height: 1; }

.btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(var(--pos-primary-rgb), .35) !important;
}

.btn-sm {
  min-height: 34px;
  padding: 0 10px;
  font-size: 12.5px !important;
}
.btn-lg { min-height: 48px; font-size: 15px !important; padding: 0 20px; }

/* -- primary ------------------------------------------------------------- */
.btn-primary {
  --bs-btn-color: #fff;
  --bs-btn-bg: var(--pos-primary);
  --bs-btn-border-color: var(--pos-primary);
  --bs-btn-hover-color: #fff;
  --bs-btn-hover-bg: var(--pos-primary-hover);
  --bs-btn-hover-border-color: var(--pos-primary-hover);
  --bs-btn-active-color: #fff;
  --bs-btn-active-bg: var(--pos-primary-hover);
  --bs-btn-active-border-color: var(--pos-primary-hover);
  --bs-btn-disabled-color: #fff;
  --bs-btn-disabled-bg: #93B4F5;
  --bs-btn-disabled-border-color: #93B4F5;
  background: var(--pos-primary);
  border: 1px solid var(--pos-primary);
  color: #fff;
}
.btn-primary:hover,
.btn-primary:focus {
  background: var(--pos-primary-hover);
  border-color: var(--pos-primary-hover);
  color: #fff;
}

/* -- success / warning / danger / info ----------------------------------- */
.btn-success {
  background: var(--pos-success); border: 1px solid var(--pos-success); color: #fff;
  --bs-btn-bg: var(--pos-success); --bs-btn-border-color: var(--pos-success);
  --bs-btn-hover-bg: #047857; --bs-btn-hover-border-color: #047857; --bs-btn-hover-color: #fff;
}
.btn-success:hover { background: #047857; border-color: #047857; color: #fff; }

.btn-warning {
  background: var(--pos-warning); border: 1px solid var(--pos-warning); color: #fff;
  --bs-btn-bg: var(--pos-warning); --bs-btn-color: #fff;
  --bs-btn-hover-bg: #B45309; --bs-btn-hover-color: #fff;
}
.btn-warning:hover { background: #B45309; border-color: #B45309; color: #fff; }

.btn-danger {
  background: var(--pos-danger); border: 1px solid var(--pos-danger); color: #fff;
  --bs-btn-bg: var(--pos-danger); --bs-btn-border-color: var(--pos-danger);
  --bs-btn-hover-bg: var(--pos-danger-hover); --bs-btn-hover-border-color: var(--pos-danger-hover);
  --bs-btn-hover-color: #fff;
}
.btn-danger:hover { background: var(--pos-danger-hover); border-color: var(--pos-danger-hover); color: #fff; }

.btn-info {
  background: var(--pos-info); border: 1px solid var(--pos-info); color: #fff;
  --bs-btn-bg: var(--pos-info); --bs-btn-color: #fff;
}
.btn-info:hover { background: #0E7490; border-color: #0E7490; color: #fff; }

.btn-secondary {
  background: var(--pos-surface); border: 1px solid var(--pos-border-strong);
  color: var(--pos-text-secondary);
  --bs-btn-bg: var(--pos-surface); --bs-btn-color: var(--pos-text-secondary);
}
.btn-secondary:hover {
  background: var(--pos-surface-hover); border-color: var(--pos-border-strong);
  color: var(--pos-text-primary);
}

/* -- neutral / light ------------------------------------------------------ */
.btn-light {
  background: var(--pos-surface);
  border: 1px solid var(--pos-border);
  color: var(--pos-text-secondary);
  --bs-btn-bg: var(--pos-surface);
  --bs-btn-color: var(--pos-text-secondary);
  --bs-btn-border-color: var(--pos-border);
}
.btn-light:hover {
  background: var(--pos-surface-hover);
  border-color: var(--pos-border-strong);
  color: var(--pos-text-primary);
}

/* -- outlines ------------------------------------------------------------- */
.btn-outline-primary {
  background: var(--pos-surface); border: 1px solid var(--pos-primary); color: var(--pos-primary);
}
.btn-outline-primary:hover { background: var(--pos-primary-soft); color: var(--pos-primary-hover); }

.btn-outline-secondary {
  background: var(--pos-surface); border: 1px solid var(--pos-border-strong);
  color: var(--pos-text-secondary);
}
.btn-outline-secondary:hover { background: var(--pos-surface-hover); color: var(--pos-text-primary); }

.btn-outline-danger {
  background: var(--pos-surface); border: 1px solid var(--pos-danger); color: var(--pos-danger);
}
.btn-outline-danger:hover { background: var(--pos-danger-soft); color: var(--pos-danger-hover); }

.btn-outline-success {
  background: var(--pos-surface); border: 1px solid var(--pos-success); color: var(--pos-success);
}
.btn-outline-success:hover { background: var(--pos-success-soft); }

/* Disabled reads as a distinct neutral state rather than a faded version
   of the colour. Dimming a green or blue fill with opacity produced the
   muddy grey-brown that the primary action showed while inactive. */
.btn.disabled,
.btn:disabled,
fieldset:disabled .btn {
  background: var(--pos-surface-alt) !important;
  border-color: var(--pos-border) !important;
  color: var(--pos-text-muted) !important;
  opacity: 1;
  cursor: not-allowed;
  transform: none;
  box-shadow: none !important;
}
.btn.disabled:hover,
.btn:disabled:hover {
  background: var(--pos-surface-alt) !important;
  border-color: var(--pos-border) !important;
  color: var(--pos-text-muted) !important;
}

/* Icon-only. */
.pos-icon-btn {
  width: var(--pos-control-h);
  min-width: var(--pos-control-h);
  padding: 0;
  border-radius: var(--pos-radius-sm);
}


/* ==========================================================================
   08  FORMS + CONTROLS
   ========================================================================== */

.form-control,
.form-select,
input[type="text"],
input[type="number"],
input[type="search"],
input[type="email"],
input[type="tel"],
input[type="password"],
input[type="date"],
input[type="time"],
input[type="datetime-local"],
textarea,
select {
  min-height: var(--pos-control-h);
  padding: 8px 12px;
  border: 1px solid var(--pos-border) !important;
  border-radius: var(--pos-radius-sm) !important;
  background-color: var(--pos-surface) !important;
  color: var(--pos-text-primary) !important;
  font-family: var(--pos-font-family);
  font-size: 13.5px !important;
  font-weight: 500;
  line-height: 1.4;
  box-shadow: none !important;
  transition: border-color var(--pos-t), box-shadow var(--pos-t);
}

textarea, textarea.form-control { min-height: 78px; padding: 10px 12px; }

.form-control::placeholder,
input::placeholder,
textarea::placeholder { color: var(--pos-text-muted); font-weight: 400; }

/* One clear focus treatment everywhere. The template used a 15px black
   drop shadow on focus, which looked like a rendering fault. */
.form-control:focus,
.form-select:focus,
input:focus,
select:focus,
textarea:focus {
  border-color: var(--pos-primary) !important;
  box-shadow: 0 0 0 3px rgba(var(--pos-primary-rgb), .14) !important;
  outline: none !important;
}
input[type="checkbox"]:focus,
input[type="radio"]:focus,
input.select2-search__field:focus {
  box-shadow: 0 0 0 3px rgba(var(--pos-primary-rgb), .2) !important;
}

.form-control:disabled,
.form-select:disabled,
.form-control[readonly] {
  background-color: var(--pos-surface-alt) !important;
  color: var(--pos-text-secondary) !important;
  cursor: not-allowed;
}

.form-select {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%2394A3B8' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
  background-repeat: no-repeat;
  background-position: right 11px center;
  background-size: 15px;
  padding-right: 34px;
  appearance: none;
}

.form-control.is-invalid,
.form-select.is-invalid,
.parsley-error {
  border-color: var(--pos-danger) !important;
  background-color: var(--pos-danger-soft) !important;
}
.form-control.is-invalid:focus,
.parsley-error:focus {
  box-shadow: 0 0 0 3px rgba(220, 38, 38, .14) !important;
}

.parsley-errors-list {
  width: 100%;
  list-style: none;
  padding: 0;
  margin: 5px 0 0;
  font-size: 11.5px;
  font-weight: 500;
  color: var(--pos-danger);
}

.required-must { color: var(--pos-danger); font-weight: 700; }

/* Checkboxes + radios: bigger touch target, brand fill. */
.form-check-input,
input[type="checkbox"],
input[type="radio"] {
  width: 17px;
  height: 17px;
  min-height: 0;
  border: 1.5px solid var(--pos-border-strong) !important;
  border-radius: 4px !important;
  cursor: pointer;
  accent-color: var(--pos-primary);
  transition: background var(--pos-t), border-color var(--pos-t);
}
input[type="radio"] { border-radius: 50% !important; }
.form-check-input:checked,
input[type="checkbox"]:checked {
  background-color: var(--pos-primary) !important;
  border-color: var(--pos-primary) !important;
}

/* --------------------------------------------------------------------
   Switch toggles (Active / Inactive).

   Bootstrap's .form-check reserves 1.5em (~24px) of left padding and pulls
   the input back into it with margin-left:-1.5em. Widening the switch to
   34px made it overrun that reserved space and sit on top of its own
   label, which is the misalignment on every edit screen.

   Laying the row out with flex removes the em arithmetic entirely: the
   switch and its label are siblings with a real gap, so the control can be
   any width and still line up.
   -------------------------------------------------------------------- */
.form-check.form-switch {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-left: 0;          /* the reserved gutter is no longer needed */
  margin-bottom: 0;
  min-height: 34px;
}
.form-switch .form-check-input {
  flex: 0 0 auto;
  width: 40px !important;
  height: 22px !important;
  margin: 0 !important;     /* kills Bootstrap's -1.5em pull */
  border-radius: var(--pos-radius-pill) !important;
  background-color: var(--pos-border-strong) !important;
  border: 1px solid var(--pos-border-strong) !important;
  background-position: left center;
  background-size: 18px 18px;
  background-repeat: no-repeat;
  /* Knob drawn here rather than inherited, so the global checkbox rules
     above cannot leave the track blank. */
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e") !important;
  transition: background-color var(--pos-t), background-position var(--pos-t), border-color var(--pos-t);
  cursor: pointer;
}
.form-switch .form-check-input:checked {
  background-color: var(--pos-success) !important;
  border-color: var(--pos-success) !important;
  background-position: right center;
}
.form-switch .form-check-input:focus-visible {
  box-shadow: 0 0 0 3px rgba(var(--pos-primary-rgb), .25) !important;
}
.form-switch .form-check-label {
  margin: 0;
  font-size: 13px !important;
  font-weight: 600 !important;
  color: var(--pos-text-secondary) !important;
  cursor: pointer;
  user-select: none;
  line-height: 1.3;
}

.input-group-text {
  background: var(--pos-surface-alt);
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-sm);
  color: var(--pos-text-secondary);
  font-size: 13px;
  font-weight: 600;
}

/* -- select2 -------------------------------------------------------------- */
.select2-container--default { padding: 0 !important; border: 0 !important; }
span.select2.select2-container.select2-container--default { width: 100% !important; }

.select2-container--default .select2-selection--single {
  height: var(--pos-control-h) !important;
  border: 1px solid var(--pos-border) !important;
  border-radius: var(--pos-radius-sm) !important;
  background: var(--pos-surface) !important;
  display: flex;
  align-items: center;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
  color: var(--pos-text-primary) !important;
  font-size: 13.5px;
  font-weight: 500;
  line-height: 1.4 !important;
  padding-left: 12px;
  padding-right: 28px;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
  height: 100% !important;
  right: 6px;
}
.select2-container--default.select2-container--open .select2-selection--single {
  border-color: var(--pos-primary) !important;
  box-shadow: 0 0 0 3px rgba(var(--pos-primary-rgb), .14);
}
span.select2-dropdown.select2-dropdown--below { margin-top: 0 !important; }
.select2-dropdown {
  border: 1px solid var(--pos-border) !important;
  border-radius: var(--pos-radius-md) !important;
  box-shadow: var(--pos-shadow-md);
  overflow: hidden;
}
.select2-search--dropdown .select2-search__field {
  border: 1px solid var(--pos-border) !important;
  border-radius: var(--pos-radius-sm) !important;
  padding: 7px 10px;
}
.select2-container--default .select2-results__option { font-size: 13.5px; padding: 8px 12px; }
.select2-container--default .select2-results__option--highlighted[aria-selected] {
  background-color: var(--pos-primary) !important;
  color: #fff !important;
}
.select2-container--default .select2-results__option[aria-selected="true"] {
  background: var(--pos-primary-soft);
  color: var(--pos-primary);
  font-weight: 600;
}


/* ==========================================================================
   09  TABLES
   ========================================================================== */

.table {
  --bs-table-bg: transparent;
  width: 100%;
  margin-bottom: 0;
  color: var(--pos-text-primary);
  font-size: 13px;
  border-color: var(--pos-border);
}

.table > thead > tr,
thead tr {
  background: var(--pos-surface-alt) !important;
  color: var(--pos-text-secondary) !important;
}
.table > thead > tr > th,
th {
  padding: 10px 12px !important;
  font-family: var(--pos-font-family) !important;
  font-size: 11.5px !important;
  font-weight: 700 !important;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--pos-text-secondary) !important;
  background: var(--pos-surface-alt) !important;
  border-bottom: 1px solid var(--pos-border) !important;
  border-top: 0 !important;
  white-space: nowrap;
  vertical-align: middle;
}

.table > tbody > tr > td,
td {
  padding: 10px 12px !important;
  border-bottom: 1px solid var(--pos-border) !important;
  border-top: 0 !important;
  vertical-align: middle;
  color: var(--pos-text-primary);
}

.table > tbody > tr { transition: background var(--pos-t); }
.table > tbody > tr:hover { background: var(--pos-surface-alt); }
.table > tbody > tr:last-child > td { border-bottom: 0 !important; }

.table-striped > tbody > tr:nth-of-type(odd) > * {
  background: var(--pos-surface-alt);
  box-shadow: none;
}

.table-responsive {
  overflow-x: auto;
  border-radius: var(--pos-radius-md);
}

/* Sticky header for any scrolling table body. */
.pos-table-sticky thead th {
  position: sticky;
  top: 0;
  z-index: 3;
}

/* DataTables chrome. */
.dataTables_wrapper .dataTables_filter input,
.dataTables_wrapper .dataTables_length select {
  min-height: 36px;
  border: 1px solid var(--pos-border) !important;
  border-radius: var(--pos-radius-sm) !important;
  padding: 5px 10px;
  font-size: 13px;
}
.dataTables_wrapper .dataTables_info,
.dataTables_wrapper .dataTables_length,
.dataTables_wrapper .dataTables_filter { font-size: 12.5px; color: var(--pos-text-secondary); }

.page-link {
  border: 1px solid var(--pos-border);
  color: var(--pos-text-secondary);
  font-size: 13px;
  font-weight: 600;
  padding: 7px 12px;
  transition: background var(--pos-t), color var(--pos-t);
}
.page-link:hover { background: var(--pos-surface-hover); color: var(--pos-text-primary); }
.page-item.active .page-link {
  background: var(--pos-primary);
  border-color: var(--pos-primary);
  color: #fff;
}
.paginate_button.current {
  background: var(--pos-primary) !important;
  border-color: var(--pos-primary) !important;
  color: #fff !important;
  border-radius: var(--pos-radius-sm);
}


/* ==========================================================================
   10  BADGES, PILLS, STATUS
   ========================================================================== */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 9px;
  border-radius: var(--pos-radius-pill);
  font-family: var(--pos-font-family);
  font-size: 11px;
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: .01em;
  white-space: nowrap;
}

.badge-success, .bg-success {
  background: var(--pos-success-soft) !important;
  color: var(--pos-success) !important;
}
.badge-danger, .bg-danger {
  background: var(--pos-danger-soft) !important;
  color: var(--pos-danger) !important;
}
span.badge.badge-warning, .badge-warning, .bg-warning {
  background: var(--pos-warning-soft) !important;
  color: var(--pos-warning) !important;
}
.badge-primary, .bg-primary {
  background: var(--pos-primary-soft) !important;
  color: var(--pos-primary) !important;
}
.badge-info, .bg-info {
  background: var(--pos-info-soft) !important;
  color: var(--pos-info) !important;
}
.badge-secondary, .bg-secondary {
  background: var(--pos-surface-hover) !important;
  color: var(--pos-text-secondary) !important;
}

/* Count chip beside a panel title. */
.pos-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 20px;
  padding: 0 7px;
  border-radius: var(--pos-radius-pill);
  background: var(--pos-primary-soft);
  color: var(--pos-primary);
  font-size: 11px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}


/* ==========================================================================
   11  MODALS, DROPDOWNS, TOOLTIPS, TOASTS
   ========================================================================== */

.modal-backdrop { background: #0F172A; }
.modal-backdrop.show { opacity: .45; }

.modal-dialog { max-width: 70% !important; }
.modal-sm  { max-width: 420px !important; }
.modal-lg  { max-width: 860px !important; }

.modal-content {
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-lg);
  box-shadow: var(--pos-shadow-lg);
  overflow: hidden;
  background: var(--pos-surface);
}

.modal-header {
  background: var(--pos-surface);
  border-bottom: 1px solid var(--pos-border);
  padding: 14px 18px;
  align-items: center;
}
.modal-title {
  font-family: var(--pos-font-family) !important;
  font-size: 15.5px !important;
  font-weight: 700 !important;
  color: var(--pos-text-primary) !important;
  margin: 0;
}
.modal-body { padding: 18px; }
.modal-footer {
  background: var(--pos-surface-alt);
  border-top: 1px solid var(--pos-border);
  padding: 12px 18px;
  gap: 8px;
}

.btn-close {
  width: 32px;
  height: 32px;
  border-radius: var(--pos-radius-sm);
  opacity: .55;
  transition: opacity var(--pos-t), background var(--pos-t);
}
.btn-close:hover { opacity: 1; background: var(--pos-surface-hover); }

/* Legacy manually-positioned overlays used by the POS variation pickers. */
#variation-model,
#variation-model2 { background: rgba(15, 23, 42, .45); }

.dropdown-menu {
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-md);
  box-shadow: var(--pos-shadow-md);
  padding: 6px;
  font-size: 13.5px;
  background: var(--pos-surface);
}
.dropdown-item {
  border-radius: var(--pos-radius-sm);
  padding: 8px 11px;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--pos-text-secondary);
  transition: background var(--pos-t), color var(--pos-t);
}
.dropdown-item:hover,
.dropdown-item:focus {
  background: var(--pos-surface-hover);
  color: var(--pos-text-primary);
}
.dropdown-divider { border-color: var(--pos-border); margin: 5px 0; }
.dropdown-menu-body { padding: 6px 10px; }

.tooltip-inner {
  background: #0F172A;
  border-radius: var(--pos-radius-sm);
  font-size: 12px;
  font-weight: 500;
  padding: 6px 10px;
  max-width: 240px;
}
.tooltip .tooltip-arrow::before { border-top-color: #0F172A; }

/* Toasts. */
.toast, .pos-toast {
  background: var(--pos-surface);
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-md);
  box-shadow: var(--pos-shadow-md);
  font-size: 13px;
}
.toast-header {
  background: var(--pos-surface-alt);
  border-bottom: 1px solid var(--pos-border);
  font-weight: 600;
}

.alert {
  border: 1px solid transparent;
  border-radius: var(--pos-radius-md);
  padding: 11px 14px;
  font-size: 13px;
  font-weight: 500;
}
.alert-success { background: var(--pos-success-soft); border-color: rgba(5,150,105,.2);  color: #065F46; }
.alert-danger  { background: var(--pos-danger-soft);  border-color: rgba(220,38,38,.2);  color: #991B1B; }
.alert-warning { background: var(--pos-warning-soft); border-color: rgba(217,119,6,.2);  color: #92400E; }
.alert-info    { background: var(--pos-info-soft);    border-color: rgba(8,145,178,.2);  color: #155E75; }
.alert-primary { background: var(--pos-primary-soft); border-color: rgba(var(--pos-primary-rgb),.2); color: var(--pos-primary-hover); }


/* ==========================================================================
   12  POS WORKSPACE
   ========================================================================== */

/* The POS terminal is a fixed-height workspace: the page itself never
   scrolls, each panel scrolls on its own. Scoped to body.pos-app so no
   back-office page inherits the locked height. */
body.pos-app {
  height: 100dvh;
  overflow: hidden;
}
/* Full viewport height including the fixed header's padding-top, so the
   workspace bottom lands exactly on the viewport edge and every panel's
   sticky footer stays on screen. */
body.pos-app .main.main-app {
  height: 100dvh;
  max-height: 100dvh;
  overflow: hidden;
  padding: calc(var(--pos-header-h) + 10px) 12px 12px !important;
}
body.pos-app #order-form {
  height: 100%;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
body.pos-app #order-form > .row {
  flex: 1 1 auto;
  min-height: 0;
  margin: 0;
  --bs-gutter-x: 10px;
}
body.pos-app #order-form > .row > [class*="col-md-"] {
  min-width: 0;                 /* stops grid children from overflowing */
  height: 100%;
  margin-bottom: 0 !important;
  display: flex;
  flex-direction: column;
}

/* The three workspace panels fill their column instead of a fixed 85vh. */
body.pos-app #order-form > .row > [class*="col-md-"] > .card {
  height: 100% !important;
  display: flex;
  flex-direction: column;
  min-height: 0;
  margin-bottom: 0;
}
body.pos-app .card > .card-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
/* The Running Orders action tray is nested INSIDE .card-body, so a direct
   child selector never reached it and it was laid out as a growing block
   that fell off the bottom of the panel. Match it wherever it sits. */
body.pos-app .card > .card-header,
body.pos-app .card > .card-footer,
body.pos-app .card .card-body > .card-footer,
body.pos-app .pos-order-actions { flex: 0 0 auto; }

/* The scrolling regions must be allowed to shrink below their content. */
body.pos-app #running-orders,
body.pos-app #product,
body.pos-app #cart {
  flex: 1 1 0 !important;
  min-height: 0 !important;
  overflow-y: auto !important;
}

.pos-panel-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px !important;
  font-weight: 600 !important;
  color: var(--pos-text-primary) !important;
  margin: 0 !important;
}


/* ==========================================================================
   13  RUNNING ORDERS PANEL
   ========================================================================== */

#running-orders {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 8px !important;
  display: flex;
  flex-direction: column;
  gap: 7px;
}

/* Each running order is a light card, never a solid dark slab.
   The markup is an <a class="order-card"> written by load-orders.php. */
.order-card {
  position: relative;
  display: block;
  width: auto;
  margin: 0 0 7px !important;
  padding: 9px 10px 9px 11px;
  background: var(--pos-surface) !important;
  border: 1px solid var(--pos-border);
  border-left: 3px solid var(--pos-border-strong);
  border-radius: var(--pos-radius-md);
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--pos-text-primary) !important;
  cursor: pointer;
  transition: background var(--pos-t), border-color var(--pos-t),
              box-shadow var(--pos-t);
}
.order-card:hover {
  background: var(--pos-surface-alt) !important;
  border-color: var(--pos-border-strong);
  box-shadow: var(--pos-shadow-sm);
}

/* Selected order: light blue fill + blue border, never a dark slab. */
.order-card.active,
.order-card.active:hover {
  background: var(--pos-primary-soft) !important;
  border-color: rgba(var(--pos-primary-rgb), .5);
  color: var(--pos-text-primary) !important;
}
.order-card.active { box-shadow: 0 0 0 1px rgba(var(--pos-primary-rgb), .25); }

/* Order-type accent on the left rail - the only saturated element. */
.order-card.otype-dinein   { border-left-color: var(--pos-primary); }
.order-card.otype-takeaway { border-left-color: var(--pos-violet); }
.order-card.otype-delivery { border-left-color: var(--pos-warning); }
.order-card.otype-room     { border-left-color: var(--pos-info); }
.order-card.status-pending { border-left-color: #EA580C; }
.order-card.status-done    { border-left-color: var(--pos-success); }
.order-card.status-cancel  { border-left-color: var(--pos-danger); }

.order-card .oc-top {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-bottom: 5px;
  min-width: 0;
}
.order-card .order-checkbox { flex: 0 0 auto; margin: 0; }
.order-card .oc-name {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--pos-text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.order-card .oc-time {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 10.5px;
  font-weight: 600;
  color: var(--pos-text-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* The old .details rule pulled the block up 20px and in 22px, which is
   why the text used to sit half outside its card. */
.order-card .details {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  margin: 0 !important;
}
.order-card .oc-no {
  font-size: 11.5px;
  font-weight: 700;
  color: var(--pos-text-secondary);
  font-variant-numeric: tabular-nums;
}
.order-card .oc-tag {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  max-width: 100%;
  padding: 1px 7px;
  border-radius: var(--pos-radius-pill);
  background: var(--pos-surface-hover);
  color: var(--pos-text-secondary);
  font-size: 10.5px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.order-card .oc-tag i { font-size: 11px; }
.order-card.otype-dinein   .oc-type { background: var(--pos-primary-soft); color: var(--pos-primary); }
.order-card.otype-takeaway .oc-type { background: var(--pos-violet-soft);  color: var(--pos-violet); }
.order-card.otype-delivery .oc-type { background: var(--pos-warning-soft); color: var(--pos-warning); }
.order-card .oc-room { background: var(--pos-info-soft); color: var(--pos-info); }
.order-card .oc-note {
  display: block;
  width: 100%;
  margin-top: 2px;
  font-size: 10.5px;
  color: var(--pos-text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.order-card .oc-note i { margin-right: 3px; }

/* Legacy shape, kept for any cached markup still using <small>. */
.order-card small { font-size: 11px; display: block; width: 100%; }

#order-search {
  height: 36px;
  min-height: 36px;
  font-size: 12.5px;
}

#clear-check,
#refresh-orders {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: var(--pos-radius-sm);
  color: var(--pos-text-muted);
  font-size: 16px;
  transition: background var(--pos-t), color var(--pos-t);
}
#clear-check:hover,
#refresh-orders:hover { background: var(--pos-surface-hover); color: var(--pos-primary); }

/* Sticky action tray under the order list.
   It is a flex-none footer inside the flex card body, so the order list
   above scrolls and every action stays reachable at 1366x768. */
.pos-order-actions,
body.pos-app #running-orders + .card-footer {
  flex: 0 0 auto;
  background: var(--pos-surface) !important;
  border-top: 1px solid var(--pos-border) !important;
  padding: 8px !important;
  margin: 0 -1px -1px;
  overflow: visible;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 5px;
}
/* Full-width primary actions span both columns. */
.pos-order-actions > #modify-order,
.pos-order-actions > #print-kot,
.pos-order-actions > #View-order-f,
.pos-order-actions > #cancel-order-f { grid-column: 1 / -1; }
/* The paired rows already carry their own bootstrap grid. */
.pos-order-actions > .row {
  grid-column: 1 / -1;
  margin: 0;
  --bs-gutter-x: 4px;
}
.pos-order-actions .btn {
  min-height: 34px;
  width: 100%;
  padding: 0 6px !important;
  margin-top: 0 !important;
  font-size: 12px !important;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Action buttons carry meaning through colour. */
#modify-order   { background: var(--pos-primary); border-color: var(--pos-primary); color: #fff; }
#modify-order:hover { background: var(--pos-primary-hover); border-color: var(--pos-primary-hover); }

/* Print actions are neutral outlines - they never change order state. */
#print-bill,
#print-kot,
#print {
  background: var(--pos-surface) !important;
  border: 1px solid var(--pos-border-strong) !important;
  color: var(--pos-text-secondary) !important;
}
#print-bill:hover,
#print-kot:hover,
#print:hover {
  background: var(--pos-surface-hover) !important;
  border-color: var(--pos-primary) !important;
  color: var(--pos-primary) !important;
}

/* Complete Bill is the money action, so it is the only green control in
   the tray and reads as clearly different from the print buttons. */
#print-invoice {
  background: var(--pos-success) !important;
  border: 1px solid var(--pos-success) !important;
  color: #fff !important;
  font-weight: 700 !important;
}
#print-invoice:hover { background: #047857 !important; border-color: #047857 !important; }

#cancel-order,
#cancel-order-f {
  background: var(--pos-surface) !important;
  border: 1px solid var(--pos-danger) !important;
  color: var(--pos-danger) !important;
}
#cancel-order:hover,
#cancel-order-f:hover {
  background: var(--pos-danger) !important;
  color: #fff !important;
}

#split-bill  { background: var(--pos-info); border-color: var(--pos-info); color: #fff; }
#merge-bill  { background: var(--pos-surface); border: 1px solid var(--pos-border-strong); color: var(--pos-text-secondary); }
#View-order-f {
  background: var(--pos-surface) !important;
  border: 1px solid var(--pos-border-strong) !important;
  color: var(--pos-text-secondary) !important;
}
#View-order-f:hover { background: var(--pos-surface-hover) !important; color: var(--pos-primary) !important; }


/* ==========================================================================
   14  PRODUCTS PANEL
   ========================================================================== */

/* Categories: a horizontal chip strip, not a narrow vertical stack. The
   old column was ~90px wide, so any multi-word category wrapped to one
   character per line. */
.categories {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 7px;
  overflow-x: auto;
  overflow-y: hidden;
  padding: 2px 2px 8px;
  margin-bottom: 4px;
  scrollbar-width: thin;
  scroll-behavior: smooth;
  flex: 0 0 auto;
  width: 100%;
}
.categories::-webkit-scrollbar { height: 5px; }

.categories .btn.cat,
a.cat {
  flex: 0 0 auto;
  width: auto !important;
  min-height: 38px;
  margin: 0 !important;
  padding: 0 15px;
  border-radius: var(--pos-radius-pill) !important;
  background: var(--pos-surface) !important;
  border: 1px solid var(--pos-border) !important;
  color: var(--pos-text-secondary) !important;
  font-size: 13px !important;
  font-weight: 600 !important;
  /* The chip must never break a label into characters. */
  white-space: nowrap !important;
  word-break: normal !important;
  overflow-wrap: normal !important;
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: background var(--pos-t), border-color var(--pos-t), color var(--pos-t);
}
.categories .btn.cat:hover,
a.cat:hover {
  background: var(--pos-primary-soft) !important;
  border-color: rgba(var(--pos-primary-rgb), .4) !important;
  color: var(--pos-primary) !important;
}
.categories .btn.cat.active,
a.cat.active {
  background: var(--pos-primary) !important;
  border-color: var(--pos-primary) !important;
  color: #fff !important;
}

/* Product grid. */
#product {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  display: grid !important;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 10px;
  align-content: start;
  padding: 2px 4px 4px 2px;
  margin: 0;
}

/* Bootstrap column classes on the cards are neutralised - the grid owns
   the track sizing now. The classes stay so existing JS selectors work. */
#product > .add-to-cart,
#product > .product2 {
  width: 100% !important;
  max-width: 100% !important;
  flex: none !important;
  padding: 0 !important;
  margin: 0 !important;
  min-width: 0;
}

#product .card,
.add-to-cart .card {
  height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--pos-surface) !important;
  border: 1px solid var(--pos-border) !important;
  border-radius: var(--pos-radius-md) !important;
  box-shadow: none !important;
  padding: 0 !important;
  cursor: pointer;
  overflow: hidden;
  transition: border-color var(--pos-t), box-shadow var(--pos-t),
              transform var(--pos-t);
}
#product .card:hover,
.add-to-cart .card:hover {
  border-color: var(--pos-primary);
  box-shadow: var(--pos-shadow-md);
  transform: translateY(-1px);
}
#product .card:active { transform: translateY(0); }

.add-to-cart .card.selected,
#product .card.selected {
  border-color: var(--pos-primary);
  background: var(--pos-primary-soft) !important;
  box-shadow: 0 0 0 2px rgba(var(--pos-primary-rgb), .18);
}

.card-img-top,
#product .card img {
  width: 100%;
  aspect-ratio: 4 / 3;
  height: auto;
  object-fit: cover;
  background: var(--pos-surface-hover);
  border-radius: 0;
  display: block;
  flex: 0 0 auto;
}

#product .card-body,
.add-to-cart .card-body {
  padding: 8px 9px 9px !important;
  display: flex;
  flex-direction: column;
  gap: 3px;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}

.prd-lable,
.card-title.prd-lable {
  font-family: var(--pos-font-family) !important;
  font-size: 12.5px !important;
  font-weight: 600 !important;
  line-height: 1.32 !important;
  color: var(--pos-text-primary) !important;
  margin: 0 !important;
  /* Exactly two lines, then ellipsis - names never blow up the card. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: normal;
  overflow-wrap: break-word;
  min-height: 2.64em;
}

/* The whole card is an <a>, so anything without an explicit colour inherited
   the link blue. That is why "KOT Type:- KOT" was the same blue as the price
   and the card read as a wall of one colour with no hierarchy. */
#product .add-to-cart,
.add-to-cart {
  color: var(--pos-text-primary);
  text-decoration: none !important;
}

/* Metadata: quiet, but still readable. --pos-text-muted (#94A3B8) measures
   2.56:1 on white - well under the 4.5:1 small text needs - so at 10.5px it
   was washed out rather than merely secondary. Slate-500 is the quietest
   value that still passes, at 4.76:1. */
.prd-meta {
  font-size: 10.5px !important;
  font-weight: 500 !important;
  line-height: 1.3 !important;
  color: #64748B !important;
  margin: 0 !important;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
:root[data-theme="dark"] .prd-meta { color: var(--pos-text-muted) !important; }

.pprice, .tprice, .del-price, .delivery-price,
.card-text.font-weight-bold,
.dine-p, .take-p {
  font-size: 14px !important;
  font-weight: 700 !important;
  /* Its own money colour. Blue made the figure read as a link and matched
     the KOT line beside it; near-black fixed that but left the whole card in
     one tone with nothing to catch the eye. --pos-price is used for nothing
     else, so a price is identifiable at a glance across a grid of 47 cards. */
  color: var(--pos-price) !important;
  margin: 0 !important;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  /* Pinned to the bottom of the card body, so prices sit on one line across
     the row whatever the names above them did. */
  margin-top: auto !important;
}

/* No sibling override is needed: exactly one price is visible at a time and
   the others are display:none, which removes them from the flex layout
   entirely - so whichever one is showing takes the auto margin and lands on
   the same line as its neighbours. */

.kot_type,
.pcategory,
.p_id, .pp_id, .pname {
  font-size: 11px;
  color: var(--pos-text-muted);
}
.kot_type {
  display: inline-block;
  padding: 1px 6px;
  border-radius: var(--pos-radius-pill);
  background: var(--pos-surface-hover);
  font-weight: 600;
}

/* Unavailable product. */
#product .card.out-of-stock,
.add-to-cart.disabled .card {
  opacity: .5;
  cursor: not-allowed;
  filter: saturate(.4);
}
#product .card.out-of-stock:hover { transform: none; border-color: var(--pos-border); box-shadow: none; }

#search {
  min-height: var(--pos-control-h);
  font-size: 13.5px;
}

.pos-search-wrap { position: relative; flex: 1 1 auto; min-width: 0; }
.pos-search-wrap > i {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--pos-text-muted);
  font-size: 16px;
  pointer-events: none;
}
.pos-search-wrap > input { padding-left: 34px !important; }


/* ==========================================================================
   15  CART PANEL
   ========================================================================== */

#cart {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
}

#cart-table { width: 100%; margin: 0; }
#cart-table thead th {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--pos-surface-alt) !important;
  font-size: 11px !important;
  padding: 8px 9px !important;
}
#cart-table tbody td {
  padding: 7px 9px !important;
  font-size: 12.5px;
  vertical-align: middle;
}
#cart-table tbody tr:hover { background: var(--pos-surface-alt); }

/* Columns are laid out from the header's declared widths rather than from
   content, so every row lines up on the same five edges no matter how long an
   item name is or how big a figure gets. */
#cart-table { table-layout: fixed; }
#cart-table colgroup { display: table-column-group; }

/* Alignment is driven by CLASS, not by child position.
   The row markup in pos.js opens each <tr> with ten hidden <input> elements
   before the first <td>, so the name cell is not :first-child - it was being
   picked up by the numeric `nth-child(n+2)` rule and right-aligned, which is
   why item names sat hard against the Price column. */
#cart-table td { text-align: right; }
#cart-table td.cart-product-name {
  text-align: left;
  white-space: normal;
  overflow-wrap: anywhere;
  line-height: 1.3;
  font-weight: 600;
}
#cart-table th:nth-child(1) { text-align: left; }
#cart-table th:nth-child(2),
#cart-table th:nth-child(3),
#cart-table th:nth-child(4) { text-align: right; }
#cart-table th:nth-child(5),
#cart-table td:last-child { text-align: center; }

/* The price cell is `<span>Rs</span><input class="cart-item-price">`.
   Appearance and width for .cart-item-price are owned by the in-table editor
   block in View/pos-head.php - that block loads after this file and uses
   !important, so restating border/background/padding here would just be dead
   declarations. Only the cell layout belongs here. */
#cart-table td.cart-price-cell {
  white-space: nowrap;
  text-align: right;
}
#cart-table td.cart-price-cell > span {
  margin-right: 3px;
  font-size: 11.5px;
  color: var(--pos-text-muted);
}
#cart-table .cart-item-price:disabled { opacity: 1; }   /* stay readable */

/* Money columns use tabular figures so decimal points stack vertically. */
#cart-table td { font-variant-numeric: tabular-nums; }

#cart-table input.form-control {
  min-height: 30px;
  padding: 4px 7px;
  font-size: 12.5px;
  text-align: right;
}
/* Quantity stepper: one compact, centred group. */
#cart-table td.cart-qty-cell { text-align: center; white-space: nowrap; }
#cart-table .btn { min-height: 26px; padding: 0 7px; font-size: 12px !important; }
#cart-table .sub-quantity,
#cart-table .add-quantity {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  min-width: 26px;
  padding: 0;
  font-weight: 700;
}
#cart-table .cart-qty-cell span { display: inline-block; min-width: 24px; text-align: center; }
/* Remove control: a square icon button, not a wide pill. */
#cart-table .cart-remove-item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  min-width: 28px;
  height: 28px;
  min-height: 28px;
  padding: 0;
}

/* Order type selector - four equal segments. */
#order-type,
#o-type {
  display: flex;
  gap: 6px;
  width: 100%;
}
#order-type > *,
#o-type > * { flex: 1 1 0; min-width: 0; }

.dine-in,
#otype1, #otype2 {
  min-height: var(--pos-control-h);
  border-radius: var(--pos-radius-sm) !important;
  font-size: 13px !important;
  font-weight: 600 !important;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Order-type segmented control.
   Only "Dine In" carries the .dine-in class and every button also carries
   Bootstrap's .text-white, so styling has to come from the row wrapper and
   must out-specify that utility - otherwise a neutral button renders white
   text on a white fill. */
/* The row owns its vertical spacing, not the buttons.
   Five order types wrap to a second line, and the gap between the lines
   came from Bootstrap .mb-2 on each column - which
   @media (max-height: 820px) and (min-width: 992px) zeroes to save
   height. On a short desktop that left Room sitting flush against Dine
   In with no gap at all. row-gap survives that rule, and zeroing the
   column margins here means the two can never be added together. */
.pos-otype-row { --bs-gutter-x: 8px; row-gap: 8px; }
.pos-otype-row > [class*="col-"] { min-width: 0; margin-bottom: 0 !important; }

.pos-otype-row .btn,
.pos-otype-row .btn.text-white {
  background: var(--pos-surface) !important;
  border: 1px solid var(--pos-border) !important;
  color: var(--pos-text-secondary) !important;
  min-height: var(--pos-control-h);
  font-size: 13px !important;
  font-weight: 600 !important;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pos-otype-row .btn:hover,
.pos-otype-row .btn.text-white:hover {
  background: var(--pos-primary-soft) !important;
  border-color: rgba(var(--pos-primary-rgb), .4) !important;
  color: var(--pos-primary) !important;
}
.pos-otype-row .btn.active,
.pos-otype-row .btn.active.text-white,
.pos-otype-row .btn.active:hover {
  background: var(--pos-primary) !important;
  border-color: var(--pos-primary) !important;
  color: #fff !important;
}
.pos-otype-row .btn:disabled,
.pos-otype-row .btn.text-white:disabled {
  background: var(--pos-surface-alt) !important;
  color: var(--pos-text-muted) !important;
  border-color: var(--pos-border) !important;
}

/* Same treatment wherever the .dine-in control appears on its own. */
.dine-in:not(.active) {
  background: var(--pos-surface) !important;
  border: 1px solid var(--pos-border) !important;
  color: var(--pos-text-secondary) !important;
}
.dine-in.active {
  background: var(--pos-primary) !important;
  border: 1px solid var(--pos-primary) !important;
  color: #fff !important;
}

/* Customer / waiter / table row: long values ellipsis, never reflow. */
#customer-select,
#waiter-select,
#table-selected,
#room-selected,
#delivery-selected,
#table-no,
#delivery-partner {
  min-height: var(--pos-control-h);
  max-width: 100%;
  text-overflow: ellipsis;
}

/* Summary block. */
#sub-total, #sub-total2,
#total_discount, #total_discount2,
#Service-charge, #Service-charge2,
#serviceCharge, #sale-discount {
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.pos-summary {
  border-top: 1px solid var(--pos-border);
  padding-top: 10px;
  margin-top: 6px;
  font-size: 13px;
}
.pos-summary__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 3px 0;
  color: var(--pos-text-secondary);
}
.pos-summary__row > span:last-child {
  font-weight: 600;
  color: var(--pos-text-primary);
  font-variant-numeric: tabular-nums;
}

/* Grand total is the loudest number on the screen. */
#grand-total,
#grand-total2,
.pos-total-value {
  font-size: 21px !important;
  font-weight: 700 !important;
  color: var(--pos-text-primary) !important;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.01em;
  line-height: 1.2;
}
.pos-total-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 12px;
  margin-top: 8px;
  background: var(--pos-primary-soft);
  border: 1px solid rgba(var(--pos-primary-rgb), .2);
  border-radius: var(--pos-radius-md);
}
.pos-total-row .pos-total-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--pos-text-secondary);
}

/* The checkout tray stays reachable at 1366x768. */
body.pos-app .pos-order .card-footer,
.pos-checkout-bar {
  position: sticky;
  bottom: 0;
  background: var(--pos-surface) !important;
  border-top: 1px solid var(--pos-border);
  padding: 10px var(--pos-pad) !important;
  z-index: 5;
}

/* These are ID selectors, so they outrank every class-based sizing rule in this
   file - including the order-actions block in section 40. The height therefore
   has to be correct HERE or nothing else can change it: at 46px the three order
   actions dominated the panel, which is the "buttons are too big" report. */
#place-order,
#update-order {
  min-height: 38px;
  height: 38px;
  padding-top: 0;
  padding-bottom: 0;
  line-height: 1;
  font-size: 13px !important;
  font-weight: 700 !important;
  background: var(--pos-success);
  border-color: var(--pos-success);
  color: #fff;
}
#place-order:hover,
#update-order:hover { background: #047857; border-color: #047857; }

#draft {
  background: var(--pos-surface) !important;
  border: 1px solid var(--pos-warning) !important;
  color: var(--pos-warning) !important;
}
#draft:hover { background: var(--pos-warning-soft) !important; }

#orderNote { min-height: 60px; font-size: 13px; }

#currency { color: var(--pos-text-secondary); font-weight: 600; }


/* ==========================================================================
   16  HOTEL / FOLIO / ROOM COMPONENTS
   ========================================================================== */

.hms-room-card,
.room-card {
  background: var(--pos-surface);
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-md);
  padding: 12px;
  box-shadow: var(--pos-shadow-sm);
  transition: border-color var(--pos-t), box-shadow var(--pos-t);
}
.hms-room-card:hover { border-color: var(--pos-primary); box-shadow: var(--pos-shadow-md); }

.hms-status,
.room-status {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  border-radius: var(--pos-radius-pill);
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
}
.hms-status--vacant,   .status-vacant   { background: var(--pos-success-soft); color: var(--pos-success); }
.hms-status--occupied, .status-occupied { background: var(--pos-primary-soft); color: var(--pos-primary); }
.hms-status--dirty,    .status-dirty    { background: var(--pos-warning-soft); color: var(--pos-warning); }
.hms-status--ooo,      .status-ooo      { background: var(--pos-danger-soft);  color: var(--pos-danger); }
.hms-status--reserved, .status-reserved { background: var(--pos-violet-soft);  color: var(--pos-violet); }

.hms-folio-total {
  font-size: 20px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--pos-text-primary);
}


/* ==========================================================================
   17  EMPTY, LOADING + FEEDBACK STATES
   ========================================================================== */

/* The old full-screen loader could latch on when its dismissal script
   threw, leaving the POS permanently blocked. It is gone; these rules
   guarantee no stale markup can ever cover the app again. */
.loader,
.containers,
#powered {
  display: none !important;
  visibility: hidden !important;
  opacity: 0 !important;
  pointer-events: none !important;
}

.pos-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 32px 16px;
  text-align: center;
  color: var(--pos-text-muted);
  min-height: 150px;
}
.pos-empty i { font-size: 34px; opacity: .5; line-height: 1; }
.pos-empty__title {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--pos-text-secondary);
}
.pos-empty__hint { font-size: 12px; color: var(--pos-text-muted); }

/* Inline skeleton for in-panel loading. */
.pos-skeleton {
  background: linear-gradient(90deg,
    var(--pos-surface-hover) 25%,
    #EDF1F6 37%,
    var(--pos-surface-hover) 63%);
  background-size: 400% 100%;
  animation: pos-shimmer 1.2s ease-in-out infinite;
  border-radius: var(--pos-radius-sm);
  height: 14px;
}
@keyframes pos-shimmer {
  0%   { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}

.pos-spinner {
  width: 17px;
  height: 17px;
  border: 2px solid rgba(var(--pos-primary-rgb), .25);
  border-top-color: var(--pos-primary);
  border-radius: 50%;
  animation: pos-spin .7s linear infinite;
  display: inline-block;
}
@keyframes pos-spin { to { transform: rotate(360deg); } }

/* Working state.
 *
 * This used to set `color: transparent` and draw a WHITE spinner. On a filled
 * primary button that reads correctly, but on a white or outline button it is
 * white-on-white: the label vanishes and nothing replaces it, so the control
 * looks like a blank rectangle while the save it is running completes
 * perfectly well. That is what "the button goes white" was.
 *
 * The label now stays put and the spinner is drawn in currentColor, so it is
 * legible on every variant - filled, outline, light or dark - and a busy
 * button always still says what it is doing. */
.btn.is-busy {
  position: relative;
  pointer-events: none;
  opacity: .78;
  /* Room for the spinner without the text jumping as it appears. */
  padding-left: 30px !important;
}
.btn.is-busy::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 10px;
  margin-top: -7px;
  width: 14px;
  height: 14px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  opacity: .85;
  animation: pos-spin .7s linear infinite;
}

/* Brief confirmation flash after add/remove/qty change. */
@keyframes pos-flash {
  0%   { background: var(--pos-success-soft); }
  100% { background: transparent; }
}
.pos-flash { animation: pos-flash 600ms ease-out; }

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


/* ==========================================================================
   18  THEME PANEL
   ========================================================================== */

.pos-theme-btn {
  width: 38px;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-sm);
  background: var(--pos-surface);
  color: var(--pos-text-secondary);
  font-size: 17px;
  cursor: pointer;
  transition: background var(--pos-t), color var(--pos-t), border-color var(--pos-t);
}
.pos-theme-btn:hover {
  background: var(--pos-primary-soft);
  border-color: var(--pos-primary);
  color: var(--pos-primary);
}

.pos-theme-panel {
  position: fixed;
  top: 0;
  right: 0;
  width: 296px;
  max-width: 92vw;
  height: 100dvh;
  background: var(--pos-surface);
  border-left: 1px solid var(--pos-border);
  box-shadow: var(--pos-shadow-lg);
  z-index: 20000;
  transform: translateX(100%);
  transition: transform 180ms cubic-bezier(.4, 0, .2, 1);
  display: flex;
  flex-direction: column;
  font-size: 13px;
}
.pos-theme-panel.open { transform: translateX(0); }

.pos-theme-panel__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  border-bottom: 1px solid var(--pos-border);
  font-size: 14.5px;
  font-weight: 700;
  flex: 0 0 auto;
}
.pos-theme-panel__body {
  padding: 16px;
  overflow-y: auto;
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.pos-theme-group__title {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--pos-text-muted);
  margin-bottom: 8px;
}

.pos-theme-swatches { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
.pos-swatch {
  height: 46px;
  border: 2px solid var(--pos-border);
  border-radius: var(--pos-radius-sm);
  cursor: pointer;
  position: relative;
  background: var(--pos-surface);
  overflow: hidden;
  transition: border-color var(--pos-t), transform var(--pos-t);
  padding: 0;
}
.pos-swatch:hover { transform: translateY(-1px); }
.pos-swatch::before {
  content: "";
  position: absolute;
  inset: 5px 5px 16px;
  border-radius: 3px;
  /* --sw is set inline per swatch by pos-ui.js. The fallback keeps the
     control legible if the script has not run yet. */
  background: var(--sw, var(--pos-primary));
}
.pos-swatch::after {
  content: attr(data-label);
  position: absolute;
  left: 0; right: 0; bottom: 1px;
  font-size: 8.5px;
  font-weight: 700;
  text-align: center;
  color: var(--pos-text-muted);
  text-transform: uppercase;
  letter-spacing: .03em;
}
.pos-swatch.active { border-color: var(--sw, var(--pos-primary)); }

.pos-theme-seg { display: flex; gap: 6px; }
.pos-theme-seg button {
  flex: 1 1 0;
  min-height: 36px;
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-sm);
  background: var(--pos-surface);
  color: var(--pos-text-secondary);
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--pos-t), color var(--pos-t), border-color var(--pos-t);
}
.pos-theme-seg button:hover { background: var(--pos-surface-hover); }
.pos-theme-seg button.active {
  background: var(--pos-primary-soft);
  border-color: var(--pos-primary);
  color: var(--pos-primary);
}

.pos-theme-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.pos-theme-row input[type="color"] {
  width: 44px;
  height: 34px;
  min-height: 34px;
  padding: 2px;
  border: 1px solid var(--pos-border) !important;
  border-radius: var(--pos-radius-sm) !important;
  background: var(--pos-surface) !important;
  cursor: pointer;
}

.pos-theme-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, .35);
  z-index: 19999;
  opacity: 0;
  pointer-events: none;
  transition: opacity 180ms;
}
.pos-theme-backdrop.open { opacity: 1; pointer-events: auto; }


/* ==========================================================================
   19  DENSITY
   ========================================================================== */

:root[data-pos-density="compact"] {
  --pos-control-h: 36px;
  --pos-gap: 8px;
  --pos-pad: 10px;
  --pos-font: 12.5px;
  --pos-header-h: 54px;
}
:root[data-pos-density="compact"] .btn { min-height: 36px; font-size: 12.5px !important; }
:root[data-pos-density="compact"] .nav-sidebar .nav-link { min-height: 38px; padding: 7px 10px !important; }
:root[data-pos-density="compact"] .card-body { padding: 10px !important; }
:root[data-pos-density="compact"] .table > tbody > tr > td,
:root[data-pos-density="compact"] td { padding: 7px 10px !important; }
:root[data-pos-density="compact"] #product { grid-template-columns: repeat(auto-fill, minmax(134px, 1fr)); gap: 8px; }


/* ==========================================================================
   20  RESPONSIVE
   ========================================================================== */

/* 1920 and up - roomier product tiles. */
@media (min-width: 1601px) {
  #product { grid-template-columns: repeat(auto-fill, minmax(168px, 1fr)); }
}

/* 1280-1600 - the usual POS terminal. Tighten so nothing clips. */
@media (max-width: 1600px) {
  /* No --pos-sidebar-w here: an unconditional `:root { --pos-sidebar-w: 264px }`
     in section 26 comes later in the file, and a media query adds no
     specificity, so this declaration never applied. Leaving it in place read
     as a 224px rail that does not exist. The rail width is 264px from
     section 26 down to 1281px, then 200px in section 35. */
  #product { grid-template-columns: repeat(auto-fill, minmax(144px, 1fr)); gap: 9px; }
}

/* 1366x768 - the tightest supported terminal. */
@media (max-width: 1440px) {
  :root {
    --pos-control-h: 38px;
    --pos-pad: 11px;
    --pos-font: 13px;
  }
  /* Must keep the fixed-header offset in the top padding. A bare
     `padding: 9px` here wiped the calc() from the base rule, so the panels
     slid 45px under the fixed header and the Current Order title and
     order-type buttons were clipped at 1366x768. */
  body.pos-app .main.main-app { padding: calc(var(--pos-header-h) + 8px) 9px 9px !important; }
  #product { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 8px; }
  .prd-lable { font-size: 12px !important; }
  #grand-total, #grand-total2, .pos-total-value { font-size: 19px !important; }
  body.pos-app .card-footer .btn { min-height: 34px; font-size: 12px !important; }
}

@media (max-height: 800px) {
  #cart { max-height: none; }        /* panel flex owns the height now */
  .card-img-top, #product .card img { aspect-ratio: 3 / 2; }
}

/* Tablet / narrow: the workspace stops being a locked 3-pane grid so
   every panel remains reachable by scrolling. */
@media (max-width: 991.98px) {
  body.pos-app { height: auto; overflow: auto; }
  body.pos-app .main.main-app { height: auto; overflow: visible; }
  body.pos-app #order-form > .row > [class*="col-md-"] { height: auto; margin-bottom: 10px !important; }
  body.pos-app #order-form > .row > [class*="col-md-"] > .card { height: auto !important; }
  /* !important so these still win over the desktop flex-chain rules in
     section 32, which have to be !important themselves to beat the
     template's inline vh heights. */
  body.pos-app #running-orders { max-height: 260px !important; flex: 0 0 auto !important; }
  body.pos-app #product { max-height: 460px !important; flex: 0 0 auto !important; }
  body.pos-app #cart { max-height: 320px !important; }
  .modal-dialog { max-width: 94% !important; }
}

@media (max-width: 767.98px) {
  #product { grid-template-columns: repeat(auto-fill, minmax(126px, 1fr)); }
  .header-text { font-size: 13.5px !important; }
  .pos-theme-panel { width: 100%; }
}

@media (max-width: 584px) {
  .full-scrn { display: none; }
  .fixed-bottom { position: fixed !important; bottom: 20px !important; left: 35% !important; z-index: 1030 !important; }
  .fixed-bottom .w-10 { width: 35% !important; }
  .card-body .d-flex { display: block !important; }
  .card-body .d-flex .w-50 { width: 100% !important; }
  .modal-dialog { max-width: 96% !important; }
}

/* Print: strip app chrome so receipts and A4 invoices stay clean. */
@media print {
  .sidebar, .header-main, .pos-theme-panel, .pos-theme-backdrop,
  .card-footer .btn, .no-print { display: none !important; }
  body, body.pos-app { height: auto !important; overflow: visible !important; background: #fff !important; }
  .main, .main-app { margin: 0 !important; padding: 0 !important; height: auto !important; overflow: visible !important; }
  .card { border: 0 !important; box-shadow: none !important; }
}


/* ==========================================================================
   21  COMPLETE BILL MODAL
   ========================================================================== */

.fs-order-context {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 10px;
  padding: 11px 13px;
  margin-bottom: 14px;
  background: var(--pos-surface-alt);
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-md);
}
.fs-ctx-lbl {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--pos-text-muted);
}
.fs-ctx-val {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--pos-text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.fs-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: 16px;
}
.fs-col { min-width: 0; }

.fs-section-title {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin: 14px 0 8px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--pos-text-muted);
}
.fs-section-title.mt-0 { margin-top: 0; }
.fs-hint { font-size: 10.5px; font-weight: 600; color: var(--pos-primary); text-transform: none; letter-spacing: 0; }

.fs-pm-label {
  display: flex !important;
  align-items: center;
  gap: 5px;
  font-size: 12px !important;
  margin-bottom: 4px !important;
}
.fs-auto-badge {
  padding: 0 5px;
  border-radius: var(--pos-radius-pill);
  background: var(--pos-primary-soft);
  color: var(--pos-primary);
  font-size: 9.5px;
  font-weight: 700;
  text-transform: uppercase;
}
.split-payment-input { text-align: right; font-variant-numeric: tabular-nums; }
/* The balancing tender is visibly system-managed. */
.split-payment-input.is-cash-tender {
  background: var(--pos-primary-soft) !important;
  border-color: rgba(var(--pos-primary-rgb), .35) !important;
  font-weight: 700;
}

.fs-cash-box {
  margin-top: 14px;
  padding: 12px;
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-md);
  background: var(--pos-surface-alt);
}
#cashReceived { text-align: right; font-variant-numeric: tabular-nums; }
.fs-change {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  min-height: var(--pos-control-h);
  padding: 0 12px;
  border: 1px dashed var(--pos-border-strong);
  border-radius: var(--pos-radius-sm);
  background: var(--pos-surface);
  font-size: 15px;
  font-weight: 700;
  color: var(--pos-success);
  font-variant-numeric: tabular-nums;
}

.fs-summary {
  padding: 14px;
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-md);
  background: var(--pos-surface-alt);
}
/* One grid shape for every summary line, so labels and values share an
   exact left and right edge no matter how wide the number is. */
.fs-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 16px;
  min-height: 30px;
  font-size: 13px;
  color: var(--pos-text-secondary);
}
.fs-row > span:last-child {
  font-weight: 600;
  color: var(--pos-text-primary);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.fs-row--total {
  margin-top: 6px;
  padding-top: 10px;
  border-top: 1px solid var(--pos-border);
  font-weight: 700;
  color: var(--pos-text-primary);
}
.fs-row--total > span:last-child { font-size: 20px; color: var(--pos-primary); }
.fs-row--balance > span:last-child { font-size: 15px; }

.fs-validation {
  margin-top: 12px;
  padding: 10px 12px;
  border: 1px solid rgba(220, 38, 38, .25);
  border-radius: var(--pos-radius-md);
  background: var(--pos-danger-soft);
  color: #991B1B;
  font-size: 12.5px;
  font-weight: 600;
}

@media (max-width: 767.98px) {
  .fs-grid { grid-template-columns: minmax(0, 1fr); }
}


/* ==========================================================================
   22  CLOSE BUTTONS - always visible
   ========================================================================== */

/* Close controls used to vanish on hover: a white icon on a white hover
   fill, or a zeroed opacity. They now keep a visible icon in every state,
   sit above their siblings and are never clipped by a parent. */
.btn-close,
.pos-close,
.modal .btn-close,
.pos-theme-panel .btn-close {
  position: relative;
  z-index: 5;
  width: 40px;
  height: 40px;
  min-width: 40px;
  display: inline-flex !important;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--pos-radius-sm);
  background: transparent;
  color: var(--pos-text-secondary);
  opacity: 1 !important;
  cursor: pointer;
  transition: background var(--pos-t), color var(--pos-t);
}

/* Bootstrap draws .btn-close from a background image that cannot follow
   currentColor, so it is replaced with a mask that can. */
.btn-close {
  background-image: none !important;
  -webkit-mask: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e") center / 14px 14px no-repeat;
  mask: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e") center / 14px 14px no-repeat;
  background-color: currentColor !important;
}

.btn-close:hover,
.pos-close:hover {
  background-color: var(--pos-danger-soft) !important;
  color: var(--pos-danger) !important;
  opacity: 1 !important;
}
.btn-close:hover { background-color: var(--pos-danger) !important; }  /* mask fill */

.btn-close:active,
.pos-close:active { background-color: rgba(220, 38, 38, .18) !important; }

.btn-close:focus-visible,
.pos-close:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(var(--pos-primary-rgb), .35);
}

/* A close control must never be clipped by its own header. */
.modal-header, .sidebar-header, .pos-theme-panel__head { overflow: visible; }


/* ==========================================================================
   23  DARK MODE
   --------------------------------------------------------------------------
   Only the tokens are redefined - every component above already draws from
   them, so the whole product follows: sidebar, header, panels, cards, order
   cards, tables, forms, modals, dropdowns, tooltips, toasts, scrollbars and
   empty states. No pure black is used anywhere.
   ========================================================================== */

:root[data-theme="dark"] {
  --pos-page-bg: #0B1220;
  --pos-surface: #111827;
  --pos-surface-alt: #172033;
  --pos-surface-hover: #1E293B;

  --pos-text-primary: #F8FAFC;
  --pos-text-secondary: #CBD5E1;
  --pos-text-muted: #94A3B8;

  /* Emerald-400 on the dark card surface: 7.61:1. Emerald-700 would be all
     but invisible there. */
  --pos-price: #34D399;

  --pos-border: #263244;
  --pos-border-strong: #334155;

  --pos-primary: #3B82F6;
  --pos-primary-hover: #60A5FA;
  --pos-primary-soft: rgba(59, 130, 246, .16);
  --pos-primary-rgb: 59, 130, 246;

  --pos-success: #34D399;
  --pos-success-soft: rgba(52, 211, 153, .14);
  --pos-warning: #FBBF24;
  --pos-warning-soft: rgba(251, 191, 36, .14);
  --pos-danger: #F87171;
  --pos-danger-hover: #FCA5A5;
  --pos-danger-soft: rgba(248, 113, 113, .14);
  --pos-info: #22D3EE;
  --pos-info-soft: rgba(34, 211, 238, .14);
  --pos-violet: #A78BFA;
  --pos-violet-soft: rgba(167, 139, 250, .14);

  --pos-shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
  --pos-shadow-md: 0 4px 12px rgba(0, 0, 0, .45);
  --pos-shadow-lg: 0 12px 32px rgba(0, 0, 0, .55);

  --pos-sidebar-bg: #111827;
  --pos-sidebar-border: #263244;
  --pos-sidebar-link: #CBD5E1;

  color-scheme: dark;
}

/* Sidebar skins have to be re-pointed too, or they would keep their light
   values and leave a bright rail against a dark app. */
:root[data-theme="dark"][data-pos-sidebar="soft"] { --pos-sidebar-bg: #0F1726; }
:root[data-theme="dark"][data-pos-sidebar="tint"] {
  --pos-sidebar-bg: rgba(59, 130, 246, .10);
  --pos-sidebar-link: #E2E8F0;
}

/* A few places carry a literal light value that tokens cannot reach. */
:root[data-theme="dark"] body { background: var(--pos-page-bg) !important; }
:root[data-theme="dark"] .modal-backdrop.show { opacity: .65; }
:root[data-theme="dark"] .tooltip-inner { background: #1E293B; color: #F8FAFC; }
:root[data-theme="dark"] .tooltip .tooltip-arrow::before { border-top-color: #1E293B; }
:root[data-theme="dark"] .card,
:root[data-theme="dark"] .hms-card,
:root[data-theme="dark"] .dropdown-menu,
:root[data-theme="dark"] .modal-content { background: var(--pos-surface) !important; }
:root[data-theme="dark"] .form-select {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%2394A3B8' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
}
/* Product photos and logos keep their own colour; only chrome inverts. */
:root[data-theme="dark"] .card-img-top,
:root[data-theme="dark"] #product .card img { background: var(--pos-surface-hover); }

:root[data-theme="dark"] .btn-light {
  background: var(--pos-surface-alt);
  border-color: var(--pos-border);
  color: var(--pos-text-secondary);
}
:root[data-theme="dark"] .btn-light:hover { background: var(--pos-surface-hover); color: var(--pos-text-primary); }

/* Theme toggle segmented control. */
.pos-theme-mode { display: flex; gap: 6px; }
.pos-theme-mode button {
  flex: 1 1 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  min-height: 36px;
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-sm);
  background: var(--pos-surface);
  color: var(--pos-text-secondary);
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--pos-t), color var(--pos-t), border-color var(--pos-t);
}
.pos-theme-mode button:hover { background: var(--pos-surface-hover); }
.pos-theme-mode button.active {
  background: var(--pos-primary-soft);
  border-color: var(--pos-primary);
  color: var(--pos-primary);
}


/* ==========================================================================
   24  PRINT - always white
   --------------------------------------------------------------------------
   Receipts, KOTs, pre-bills and A4 invoices must print on white with black
   text even while the app is in dark mode, or they come out unreadable.
   ========================================================================== */

@media print {
  :root,
  :root[data-theme="dark"] {
    --pos-page-bg: #FFFFFF;
    --pos-surface: #FFFFFF;
    --pos-surface-alt: #FFFFFF;
    --pos-surface-hover: #FFFFFF;
    --pos-text-primary: #000000;
    --pos-text-secondary: #000000;
    --pos-text-muted: #333333;
    --pos-border: #CCCCCC;
    --pos-border-strong: #999999;
    color-scheme: light;
  }
  :root[data-theme="dark"] body,
  body {
    background: #FFFFFF !important;
    color: #000000 !important;
  }
  #print, #print *, .ticket, .ticket * {
    background: #FFFFFF !important;
    color: #000000 !important;
    box-shadow: none !important;
  }
}


/* ==========================================================================
   25  SIDEBAR - one scroller, full labels
   --------------------------------------------------------------------------
   The rail previously produced two scrollbars: .sidebar scrolled AND
   .sidebar-body scrolled inside it. Only the nav may scroll now; the header
   and footer are flex-none, and the nav owns the remaining height.
   ========================================================================== */

.sidebar {
  height: 100dvh;
  display: flex;
  flex-direction: column;
  overflow: hidden;                 /* the rail itself never scrolls */
  width: var(--pos-sidebar-w);
}
.sidebar-header { flex: 0 0 auto; }
.sidebar-footer { flex: 0 0 auto; }

.sidebar-body,
#sidebarMenu {
  flex: 1 1 auto;
  min-height: 0;                    /* required or flex refuses to shrink */
  max-height: none;                 /* drop the old calc() cap */
  overflow-y: auto;
  overflow-x: hidden;
  /* No scrollbar-gutter here. It reserves a permanent empty track on the
     inline-end edge, which sat directly beside the real scrollbar and read as
     a SECOND scrollbar in the rail. The right padding below keeps labels off
     the thumb, which is all the gutter was for.

     scrollbar-width is also pinned rather than inherited from the `*` rule at
     the top of this file: `thin` there and `::-webkit-scrollbar { width: 9px }`
     size the same bar differently, and Chromium honours the standard property,
     leaving the 9px track partly empty - the other half of the doubled look. */
  scrollbar-width: thin;
  padding-right: 6px !important;
}
.sidebar-body::-webkit-scrollbar,
#sidebarMenu::-webkit-scrollbar { width: 6px; }
.sidebar-body::-webkit-scrollbar-track,
#sidebarMenu::-webkit-scrollbar-track { background: transparent; }
.sidebar-body::-webkit-scrollbar-thumb,
#sidebarMenu::-webkit-scrollbar-thumb {
  background: var(--pos-border-strong);
  border-radius: var(--pos-radius-pill);
  border: 0;
  background-clip: padding-box;
}

/* No nested scroller may reintroduce a second bar.

   `overflow` is deliberately NOT set here, and must not be. These three
   elements are animated by jQuery slideToggle() (script.js), which clips the
   box by writing `overflow: hidden` as an INLINE style for the duration of
   the slide. An `!important` declaration outranks an inline style, so
   `overflow: visible !important` left the submenu unclipped while its height
   animated: the links painted outside the collapsing box and over the items
   below it, which is the "transparent"/ghost submenu seen while closing.

   max-height is enough on its own to stop a nested scroller: with no cap the
   content never overflows, so no second bar can appear. Nothing in
   style.css, custom.css, hms.css or pos.css sets overflow or max-height on
   these elements, so there is nothing else to defend against. */
.sidebar .nav-group,
.sidebar .nav-sidebar,
.sidebar .nav-sub { max-height: none !important; }

/* 264px expanded: enough for "Ingredient Consumption" and
   "Adjusted Stock Report" to read in full. */
:root { --pos-sidebar-w: 264px; }

/* Long submenu labels wrap to two lines instead of being cut. */
.nav-sub-link,
.nav-sub .nav-sub-link.dropdown-item {
  white-space: normal;
  overflow-wrap: break-word;
  word-break: normal;
  line-height: 1.35;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Top-level labels stay on one line but get the room to do so. */
.nav-sidebar .nav-link span {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Consistent breathing room between groups. */
.nav-group + .nav-group { margin-top: 2px; }

/* Collapsed rail: tooltips carry the label. */
body.sidebar-collapsed .sidebar { width: var(--pos-sidebar-w-collapsed) !important; }


/* ==========================================================================
   26  POS WORKSPACE GRID
   --------------------------------------------------------------------------
   Running Orders / Products / Current Order as a real grid with explicit
   widths, so the order panel is usable and the checkout panel is wide
   enough to bill in. Bootstrap's col-md-* on these three children is
   neutralised; the classes stay for any script that reads them.
   ========================================================================== */

body.pos-app #order-form > .row {
  display: grid;
  grid-template-columns:
      minmax(320px, 360px)      /* running orders */
      minmax(420px, 1fr)        /* products - takes the slack */
      minmax(480px, 560px);     /* current order / checkout */
  /* THE row must be exactly the container's height and never more.
     Without this the implicit row is `auto`, which sizes to the TALLEST
     item's content - so adding items to the cart grew the row, grew the
     Current Order card past the viewport, and pushed Total Amount and the
     Clear / Q-Invoice / Add-Order row off the bottom of the screen. The
     panel's internal flex chain can only cap #cart if the row it lives in
     is itself bounded. minmax(0, 1fr) is required over plain 1fr: 1fr has
     an `auto` minimum, which grows for exactly the same reason. */
  grid-template-rows: minmax(0, 1fr);
  gap: 10px;
  align-items: stretch;
}
body.pos-app #order-form > .row > [class*="col-md-"] {
  width: auto !important;
  max-width: none !important;
  flex: none !important;
  padding: 0 !important;
  /* Grid items default to min-height:auto, which also refuses to go below
     content height. Both the column and its card must be allowed to. */
  min-height: 0;
  height: 100%;
  overflow: hidden;
}
body.pos-app #order-form > .row > [class*="col-md-"] > .card {
  min-height: 0;
  max-height: 100%;
}

/* 1440 and below: trim the side panels before the product area. */
@media (max-width: 1500px) {
  body.pos-app #order-form > .row {
    grid-template-columns: minmax(290px, 320px) minmax(360px, 1fr) minmax(430px, 500px);
    gap: 8px;
  }
}

/* 1366x768 - the tightest supported terminal. Collapse the sidebar by
   default so all three panels keep workable widths. */
@media (max-width: 1400px) {
  body.pos-app #order-form > .row {
    grid-template-columns: minmax(258px, 288px) minmax(300px, 1fr) minmax(390px, 440px);
    gap: 8px;
  }
}

/* Stacking must begin at exactly the width where scrolling is switched on
   (991.98px, below). Between 992px and 1199px the grid used to stack while
   .main was still height:100dvh with overflow:hidden, so the stacked panels
   ran past the bottom of a clipped container: on a 1024x768 terminal the
   order summary and the Clear / Q-Invoice / Add-Order row were simply
   unreachable, with no scrollbar to get to them. 1024x768 keeps three panes
   instead - see section 35 for the column widths that make it fit. */
@media (max-width: 991.98px) {
  body.pos-app #order-form > .row { grid-template-columns: minmax(0, 1fr); }
}

/* Never let the workspace produce a horizontal page scrollbar. */
body.pos-app { overflow-x: hidden; }
body.pos-app .main.main-app { overflow-x: hidden; }


/* ==========================================================================
   27  PRODUCTS PANEL - sticky chrome, one scroller
   ========================================================================== */

/* Search and categories stay put; only the grid scrolls. */
/* Stable hooks, not Bootstrap column numbers: the products panel widens to
   col-md-7 on a package with no running-orders rail, and a selector tied to
   col-md-5 silently stopped matching - the panel lost its flex column and the
   product grid its height. */
body.pos-app .pos-products .card-body > .row,
body.pos-app #product-panel-body {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1 1 auto;
}
#search { position: sticky; top: 0; z-index: 4; }

.categories {
  position: relative;
  scrollbar-width: none;            /* no permanent ugly bar */
}
.categories::-webkit-scrollbar { height: 0; display: none; }

/* Product grid is the only vertical scroller in this panel. */
#product {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;     /* wheel never escapes to the page */
  scrollbar-gutter: stable;
  padding-bottom: 6px;
}
#product::-webkit-scrollbar { width: 10px !important; }

/* Cards are equal height with the price pinned to the bottom, so every
   price in the grid shares one baseline. */
#product .card,
.add-to-cart .card { height: 100%; }
#product .card-body,
.add-to-cart .card-body { display: flex; flex-direction: column; }
#product .card-body > .pprice,
#product .card-body > .tprice,
#product .card-body > .del-price,
#product .card-body > p:last-child,
.add-to-cart .card-body > p:last-child { margin-top: auto !important; }

/* Placeholder art is contained, real food photos are cropped. */
#product .card img[src*="NO-IMAGE"] { object-fit: contain; padding: 12px; }

#product .card:focus-visible,
.add-to-cart:focus-visible .card {
  outline: none;
  border-color: var(--pos-primary);
  box-shadow: 0 0 0 3px rgba(var(--pos-primary-rgb), .3);
}


/* ==========================================================================
   28  CURRENT ORDER PANEL
   ========================================================================== */

/* Item list scrolls on its own; the summary and the action row stay put. */
#cart {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
}

/* One grid shape for every summary line - identical left and right edges
   whatever the number's width. */
.order-summary-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 16px;
  min-height: 30px;
  font-size: 13px;
  color: var(--pos-text-secondary);
}
.order-summary-row > :last-child {
  justify-self: end;
  font-weight: 600;
  color: var(--pos-text-primary);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Empty order state. */
.pos-empty-cart {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 28px 16px;
  text-align: center;
  color: var(--pos-text-muted);
}
.pos-empty-cart i { font-size: 32px; opacity: .45; }


/* ==========================================================================
   29  BRAND MARK
   --------------------------------------------------------------------------
   The Synco logo is a 2:1 landscape lockup: mark on the left, wordmark to
   its right. Expanded it is shown in full; collapsed it is cropped to the
   mark with object-position, so the rail keeps a real brand rather than a
   squashed wordmark.
   ========================================================================== */

/* Superseded by section 34 (BRAND BAND), which sizes both brand images and
   swaps them on collapse. Kept as a marker so the section numbering below
   still lines up with the comments that reference it. */


/* ==========================================================================
   30  RUNNING ORDERS - header, filters, action tray
   ========================================================================== */

.ro-header { padding: 10px 11px !important; }

.ro-titlebar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 8px;
}
.ro-titlebar .pos-panel-title { font-size: 14.5px !important; }
.ro-tools { display: flex; gap: 2px; flex: 0 0 auto; }

.ro-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--pos-radius-sm);
  color: var(--pos-text-muted);
  font-size: 16px;
  transition: background var(--pos-t), color var(--pos-t);
}
.ro-icon-btn:hover { background: var(--pos-surface-hover); color: var(--pos-primary); }

.ro-search { margin-bottom: 8px; }
.ro-search #order-search { height: 38px; min-height: 38px; padding-right: 34px !important; }
.ro-clear {
  position: absolute;
  right: 4px;
  top: 50%;
  transform: translateY(-50%);
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  padding: 0;
  border-radius: var(--pos-radius-sm);
  background: transparent;
  color: var(--pos-text-muted);
  cursor: pointer;
}
.ro-clear:hover { background: var(--pos-surface-hover); color: var(--pos-danger); }

/* Type filters. Horizontally scrollable so they never steal list height. */
/* WRAP, do not scroll.
 *
 * These five chips sat in a horizontal scroller with the scrollbar
 * deliberately hidden - scrollbar-width:none plus a zero-height webkit
 * bar - inside a rail only a few hundred pixels wide. So "Delivery" and
 * "Room" were simply cut off at the edge with nothing to say they were
 * there: no arrow, no fade, not even a scrollbar. A filter the operator
 * cannot see is a filter that does not exist.
 *
 * Five short chips wrap into two rows at worst, which costs about thirty
 * pixels and makes every option visible without a gesture - the right
 * trade on a touch till, where a horizontal drag inside a vertical list
 * is awkward anyway. */
.ro-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  padding-bottom: 1px;
}
.ro-chip {
  flex: 0 0 auto;
  min-height: 28px;
  padding: 0 10px;
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-pill);
  background: var(--pos-surface);
  color: var(--pos-text-secondary);
  font-size: 11.5px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--pos-t), color var(--pos-t), border-color var(--pos-t);
}
.ro-chip:hover { background: var(--pos-primary-soft); color: var(--pos-primary); }
.ro-chip.active {
  background: var(--pos-primary);
  border-color: var(--pos-primary);
  color: #fff;
}

/* Slightly roomier cards - the brief asked for readability over density. */
.order-card { padding: 11px 12px 11px 13px; font-size: 13px; }
.order-card .oc-name { font-size: 13.5px; }
.order-card .oc-tag { font-size: 11px; padding: 2px 8px; }

/* Action tray: 40-44px controls, meaning carried by colour. */
.pos-order-actions .btn {
  min-height: 40px;
  font-size: 12.5px !important;
  gap: 5px;
}
.pos-order-actions .btn i { font-size: 15px; }

#modify-order {
  background: var(--pos-primary) !important;
  border: 1px solid var(--pos-primary) !important;
  color: #fff !important;
}
#modify-order:hover { background: var(--pos-primary-hover) !important; border-color: var(--pos-primary-hover) !important; }

/* Room actions are informational navigation - teal, distinct from money. */
.pos-order-actions a[href="hms-room-service"],
.pos-order-actions a[href="hms-inhouse"] {
  background: var(--pos-surface) !important;
  border: 1px solid var(--pos-info) !important;
  color: var(--pos-info) !important;
}
.pos-order-actions a[href="hms-room-service"]:hover,
.pos-order-actions a[href="hms-inhouse"]:hover { background: var(--pos-info-soft) !important; }

/* Nothing is actionable until an order is picked. */
.pos-order-actions .btn.is-disabled,
.pos-order-actions .btn[disabled] {
  background: var(--pos-surface-alt) !important;
  border-color: var(--pos-border) !important;
  color: var(--pos-text-muted) !important;
  pointer-events: none;
  opacity: 1;
}


/* Sidebar section headers. Plain <li> so no permission block was touched. */
.nav-sidebar .nav-section {
  list-style: none;
  padding: 15px 11px 5px;
  margin: 0;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--pos-text-muted);
  white-space: nowrap;
}
.nav-sidebar .nav-section:first-child { padding-top: 4px; }
body.sidebar-collapsed .nav-sidebar .nav-section { display: none; }


/* Order-summary block: one shared label edge, one shared value edge. */
.pos-summary-block { padding-top: 4px; }
.pos-summary-block .order-summary-row + .order-summary-row { margin-top: 2px; }

/* Editable controls in the summary stay visibly editable but compact. */
.order-summary-row #serviceCharge,
.order-summary-row #sale-discount {
  width: 116px;
  min-height: 34px;
  padding: 4px 10px;
  font-size: 13px;
  text-align: right;
}
.order-summary-row #sale-discount { font-variant-numeric: tabular-nums; }

/* Divider above the total, and the total is the loudest value here. */
.order-summary-row--total {
  margin-top: 8px !important;
  padding-top: 10px;
  border-top: 1px solid var(--pos-border);
  min-height: 40px;
}
.order-summary-row--total > span:first-child {
  font-size: 14px;
  font-weight: 700;
  color: var(--pos-text-primary);
}
.order-summary-row--total #grand-total2 {
  font-size: 22px;
  font-weight: 700;
  line-height: 1.2;
  color: var(--pos-text-primary);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.01em;
}
@media (max-width: 1440px) {
  .order-summary-row--total #grand-total2 { font-size: 20px; }
}


/* ==========================================================================
   31  POS HEADER - deterministic single row
   --------------------------------------------------------------------------
   Every child declares how it flexes, so no element can grow over another.
   The tenant title is the only thing allowed to absorb slack, and it
   truncates rather than pushing its siblings.
   ========================================================================== */

body.pos-app .header-main,
.header-main {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: 8px;
  padding: 0 14px !important;
}

/* Leading icon buttons never shrink. */
.header-main > a.menu-link,
.header-main > button,
.header-main > .dropdown { flex: 0 0 auto; }

/* Tenant name: takes the slack, truncates, never pushes siblings. */
.pos-header-title {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  padding-left: 6px;
}
.pos-header-title .header-text {
  margin: 0;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Control clusters keep their intrinsic size. */
.header-main > .full-scrn { flex: 0 0 auto; }

/* The bill lookup is the one cluster that may shrink, down to a floor that
   still shows the field and its button. */
.header-main > .full-scrn.m-hide {
  flex: 0 1 320px !important;
  min-width: 208px !important;
  max-width: 320px !important;
  margin-right: 8px !important;
}
#invoiceNo { min-width: 0 !important; width: 100%; }

.pos-conn { flex: 0 0 auto; }

/* Below 1500px the bill search collapses to its icon button so the header
   never wraps onto a second line. */
@media (max-width: 1500px) {
  .header-main > .full-scrn.m-hide { flex: 0 1 220px !important; min-width: 160px !important; }
}
@media (max-width: 1200px) {
  .header-main > .full-scrn.m-hide .pos-search-wrap { display: none; }
  .header-main > .full-scrn.m-hide { flex: 0 0 auto !important; min-width: 0 !important; }
  .pos-header-title { justify-content: flex-start; }
}


.sidebar-header { padding: 10px 12px !important; height: auto; min-height: var(--pos-header-h); }


/* ==========================================================================
   32  PANEL FLEX CHAIN  (the actual scrolling fix)
   --------------------------------------------------------------------------
   Verified against the rendered DOM. Both the Products and the Current Order
   panels put a plain Bootstrap .row between .card-body and the region that
   needs to scroll:

       .card-body            flex column, overflow:hidden
         └── .row            <-- plain block: BREAKS the chain
              ├── .col-md-*  search / customer / waiter
              └── .col-md-12
                   └── #product

   Because that .row was never a flex container, `flex:1 1 0` on #product
   resolved against nothing, the grid grew to its full content height and
   overflowed the hidden card-body. That is why Products would not scroll,
   why the last products were unreachable, and why the cart summary and
   buttons were pushed out of view.

   Making the chain flex end to end gives each panel exactly one scroller.
   ========================================================================== */

body.pos-app .card > .card-body > .row {
  display: flex !important;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  margin: 0;
  --bs-gutter-x: 0;
}

/* Fixed-height chrome inside those rows: search, categories, customer,
   waiter, the summary block and the button row. */
body.pos-app .card > .card-body > .row > [class*="col-md-"] {
  flex: 0 0 auto;
  width: 100%;
  max-width: 100%;
  padding-left: 0;
  padding-right: 0;
  min-width: 0;
}

/* Customer / waiter / edit sit on one line, not stacked. */
body.pos-app .card > .card-body > .row > .col-md-6.mb-2,
body.pos-app .card > .card-body > .row > .col-md-5.mb-2,
body.pos-app .card > .card-body > .row > .col-md-1.mb-2 {
  width: auto;
  flex: 1 1 0;
}
body.pos-app .card > .card-body > .row > .col-md-1.mb-2 { flex: 0 0 auto; }

/* The column that actually holds the product grid is the one that grows. */
body.pos-app .card > .card-body > .row > .col-md-12:last-child {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* Product grid + cart: the single scroller in their panel. */
body.pos-app #product {
  flex: 1 1 0 !important;
  min-height: 0 !important;
  height: auto !important;
  max-height: none !important;
  overflow-y: auto !important;
  overflow-x: hidden;
}
body.pos-app #cart {
  flex: 1 1 auto !important;
  min-height: 60px !important;
  height: auto !important;
  max-height: none !important;
  overflow-y: auto !important;
}

/* Summary + action buttons never scroll away. */
body.pos-app .card > .card-body > .row > .pos-summary-block { flex: 0 0 auto; }

/* Categories strip keeps its height and never becomes a second scroller. */
body.pos-app .categories { flex: 0 0 auto; overflow-y: hidden; }

/* The running-orders tray is a direct child of its card-body and must not
   be allowed to grow; the list above it takes all remaining space. */
body.pos-app #running-orders {
  flex: 1 1 0 !important;
  min-height: 0 !important;
  height: auto !important;
  max-height: none !important;
  overflow-y: auto !important;
}
body.pos-app .pos-order-actions { flex: 0 0 auto !important; }

/* Old inline vh heights from the template must not come back. */
body.pos-app #cart,
body.pos-app #running-orders,
body.pos-app div#product,
body.pos-app div.categories { height: auto !important; }


/* ==========================================================================
   33  CART GROWTH FIX  (adding items must not push the total off-screen)
   --------------------------------------------------------------------------
   In the Current Order panel the customer row, #cart and the summary row are
   all SIBLINGS of .card-body. Section 32 made every .card-body > .row
   flexible, so all three competed for height: as items were added the cart
   grew and shoved the Total Amount and the Clear / Q-Invoice / Add-Order
   buttons below the fold.

   Rows are now flex-none by default and only the row explicitly tagged
   .pos-flex-row (the one holding the product grid) is allowed to grow.
   #cart is the single flexible element in the order panel.
   ========================================================================== */

/* Default: a row takes exactly its content height. */
body.pos-app .card > .card-body > .row {
  flex: 0 0 auto !important;
}

/* Only the products row grows, because it contains the scrolling grid. */
body.pos-app .card > .card-body > .row.pos-flex-row {
  flex: 1 1 auto !important;
  min-height: 0 !important;
}

/* Explicitly pinned rows: customer/waiter and the order summary. */
body.pos-app .card > .card-body > .row.pos-fixed-row {
  flex: 0 0 auto !important;
  min-height: 0;
}

/* #cart is the only elastic region in the Current Order panel. It shrinks
   before the summary does, and scrolls once it hits its floor. */
body.pos-app .card > .card-body > #cart {
  flex: 1 1 auto !important;
  min-height: 72px !important;
  max-height: none !important;
  height: auto !important;
  overflow-y: auto !important;
}

/* Customer / waiter / edit sit on one line inside their pinned row. */
body.pos-app .row.pos-fixed-row > .col-md-6.mb-2 { flex: 1 1 auto; width: auto; }
body.pos-app .row.pos-fixed-row > .col-md-5.mb-2 { flex: 1 1 auto; width: auto; }
body.pos-app .row.pos-fixed-row > .col-md-1.mb-2 { flex: 0 0 auto; width: auto; }
body.pos-app .row.pos-fixed-row {
  display: flex !important;
  flex-direction: row !important;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
/* The summary row stacks its single full-width column. */
body.pos-app .row.pos-fixed-row > .col-md-12 { flex: 1 1 100%; width: 100%; }

/* The action buttons are the whole point of the panel: they must never
   shrink, never scroll away and never be pushed below the fold. They are a
   direct child of .card-body now, so flex-none actually reaches them.
   (A default flex item is `0 1 auto` - it can still be compressed.) */
body.pos-app .card > .card-body > .buttons {
  flex: 0 0 auto !important;
  margin-top: auto;              /* hugs the bottom when the cart is short */
  padding-top: 4px;
}
body.pos-app .card > .card-body > .buttons > .row { margin: 0; --bs-gutter-x: 8px; }
body.pos-app .card > .card-body > .buttons .btn { width: 100%; }

/* EVERY child of the button row, not one hand-picked column class.
   This was keyed to .col-md-4 alone, so any column that was not col-md-4 -
   Charge to Room is col-12 - kept its .mb-3 and was spaced differently from
   the buttons beside it. Stating the padding here too means a column's own
   gutter can never disagree with its neighbours', whatever class it carries.
   The old rule also existed to kill dead space below the fold; row-gap keeps
   the spacing BETWEEN wrapped rows without putting a margin under the last. */
body.pos-app .card > .card-body > .buttons > .row { row-gap: 8px; }
body.pos-app .card > .card-body > .buttons > .row > * {
  padding-right: calc(var(--bs-gutter-x) * .5);
  padding-left:  calc(var(--bs-gutter-x) * .5);
  margin-bottom: 0 !important;
}

/* Empty-order guidance sits inside the item list. */
#cart:empty::after,
#cart-table tbody:empty::after {
  content: "No items yet - tap a product to start the order";
  display: block;
  padding: 26px 12px;
  text-align: center;
  font-size: 12.5px;
  color: var(--pos-text-muted);
}


/* ==========================================================================
   34  BRAND BAND
   --------------------------------------------------------------------------
   The logo sits on a full-width white band across the top of the sidebar,
   centred, with the rail's border beneath it. The band IS the plate, so the
   mark keeps full contrast on every sidebar skin and in dark mode.

   The source artwork carried ~2.3x its own height in white margin, which is
   why the mark used to render at a fraction of its box and read as missing.
   It is now trimmed to the artwork (727x200), so max-height is the real
   glyph height and the band can be sized honestly.

   Two <img> elements, exactly one visible: the full lockup when the rail is
   open, the square "S" when collapsed. object-fit: cover on a single wide
   image used to slice through the middle of the wordmark.
   ========================================================================== */

.sidebar-header {
  background: #FFFFFF !important;
  border-bottom: 1px solid var(--pos-border);
  padding: 12px 16px !important;
  min-height: var(--pos-header-h);
  display: flex !important;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.sidebar-logo {
  display: flex !important;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  gap: 0 !important;
  margin: 0 !important;
  overflow: hidden;
}
.sidebar-brand-img {
  display: block;
  width: auto !important;
  max-width: 100%;
  height: auto;
  object-fit: contain;
  object-position: center;
  filter: none !important;
  flex: 0 1 auto;
}
.sidebar-brand-img--full { max-height: 38px; }
.sidebar-brand-img--mark { max-height: 34px; display: none; }

/* Collapsed rail: swap the lockup for the square mark. */
body.sidebar-collapsed .sidebar-header { padding: 12px 6px !important; }
body.sidebar-collapsed .sidebar-brand-img--full { display: none; }
body.sidebar-collapsed .sidebar-brand-img--mark { display: block; max-height: 30px; }

/* Keep the white band white in dark mode - the logo is drawn for light. */
:root[data-theme="dark"] .sidebar-header {
  background: #FFFFFF !important;
  border-bottom-color: rgba(255, 255, 255, .14);
}
/* The trimmed art is already full-contrast; no dark-mode lift needed. */
:root[data-theme="dark"] .sidebar-brand-img,
:root[data-theme="dark"] .sign-form__logo img { filter: none; }


/* ==========================================================================
   35  POS TERMINAL DISPLAY SIZES
   --------------------------------------------------------------------------
   Real POS hardware is short before it is narrow. The panels are 100dvh with
   overflow:hidden, so on a low screen the fixed chrome (order-type buttons,
   customer row, six summary lines, order note, action buttons) can add up to
   more than the viewport and the Add-Order button ends up clipped below the
   card. The width breakpoints in section 20 do not help with that.

   These tiers compress the fixed chrome by HEIGHT so #cart keeps a usable
   window and the action buttons stay on screen and finger-sized. Panels
   covered: 1920x1080, 1600x900, 1366x768, 1280x1024, 1280x800, 1024x768,
   1024x600, 800x600, and portrait 768x1024 / 800x1280.

   Touch floor: no interactive target drops below 34px, and the three order
   actions never go below 38px, at any tier.
   ========================================================================== */

/* ---- 1280x1024 and 1024x768: narrow but not short --------------------- */
@media (max-width: 1280px) and (min-width: 992px) {
  :root { --pos-sidebar-w: 200px; --pos-font: 13px; }
  body.pos-app .main.main-app { padding: calc(var(--pos-header-h) + 8px) 8px 8px !important; }
  /* Three panes still fit a 1024-wide terminal, and they must: stacking is
     only safe below 992px where the container is allowed to scroll.
     Budget at 1024: 998 viewport - 16 padding - 14 gaps = 968 usable;
     the minimums below total 852, so there is real slack. */
  body.pos-app #order-form > .row {
    grid-template-columns:
        minmax(216px, 258px)     /* running orders */
        minmax(260px, 1fr)       /* products */
        minmax(376px, 430px);    /* current order - widest, it is billed from */
    gap: 7px;
    --bs-gutter-x: 7px;
  }
  #product { grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); gap: 7px; }
  /* Four order-type buttons on a 1024 screen: let them wrap 2x2 rather than
     squeezing the labels into two illegible characters. */
  body.pos-app .pos-otype-row > [class*="col-md-"] { flex: 1 1 46%; max-width: 50%; }
  body.pos-app .pos-otype-row .btn { font-size: 12px !important; padding-left: 4px; padding-right: 4px; }
  #cart-table th, #cart-table td { padding: 6px 5px !important; font-size: 12px !important; }
  .order-summary-row { font-size: 12.5px; }
}

/* ---- short panels: 1366x768, 1280x800, 1024x768 ---------------------- */
@media (max-height: 820px) and (min-width: 992px) {
  :root { --pos-control-h: 38px; --pos-pad: 10px; }
  body.pos-app .card > .card-header { padding: 8px 10px !important; }
  body.pos-app .card > .card-body { padding: 9px 10px !important; }
  body.pos-app .pos-otype-row > [class*="col-md-"] { margin-bottom: 0 !important; }
  /* min-height is the real floor on this block - six rows x 30px locks in
     ~200px of unshrinkable chrome. Cutting padding alone does nothing. */
  .order-summary-row { min-height: 27px; padding-top: 3px !important; padding-bottom: 3px !important; }
  .order-summary-row .form-select,
  .order-summary-row .form-control { min-height: 32px; height: 32px; padding-top: 2px; padding-bottom: 2px; }
  #orderNote { min-height: 34px; height: 34px; }
  body.pos-app .card > .card-body > .buttons .btn { min-height: 40px; }
}

/* ---- 1024x600 and 800x600 -------------------------------------------- */
@media (max-height: 700px) and (min-width: 992px) {
  :root { --pos-control-h: 34px; --pos-pad: 8px; --pos-header-h: 54px; }
  body.pos-app .card > .card-header { padding: 6px 9px !important; }
  body.pos-app .card > .card-body { padding: 7px 9px !important; }
  .pos-panel-title { font-size: 12.5px !important; }
  #product { grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); gap: 6px; }
  .card-img-top, #product .card img { aspect-ratio: 16 / 9; }
  /* Six summary lines are the biggest fixed block in the panel. Tighten the
     line box, keep every figure - none of them may be hidden. */
  .order-summary-row { min-height: 24px; padding-top: 1px !important; padding-bottom: 1px !important; font-size: 12px; }
  .order-summary-row .form-select,
  .order-summary-row .form-control { min-height: 29px; height: 29px; font-size: 12px; }
  #grand-total, #grand-total2, .pos-total-value { font-size: 16px !important; }
  /* The order note label is the one line that can go; the input keeps its
     placeholder, so nothing becomes ambiguous. */
  body.pos-app .pos-note-label { display: none; }
  #orderNote { min-height: 32px; height: 32px; }
  #orderNote::placeholder { color: var(--pos-text-muted); }
  body.pos-app .card > .card-body > #cart { min-height: 56px !important; }
  body.pos-app .card > .card-body > .buttons .btn { min-height: 38px; font-size: 12.5px !important; }
}

/* ---- 800x600: the floor. Nothing may be unreachable here. ------------- */
@media (max-height: 620px) and (min-width: 992px) {
  :root { --pos-sidebar-w: 180px; }
  #cart-table th, #cart-table td { padding: 4px 4px !important; font-size: 11.5px !important; }
  .order-summary-row { min-height: 22px; font-size: 11.5px; }
  .order-summary-row .form-select,
  .order-summary-row .form-control { min-height: 26px; height: 26px; }
  body.pos-app .card > .card-body > #cart { min-height: 44px !important; }
  body.pos-app .card > .card-body > .buttons .btn { min-height: 36px; }
}

/* ---- stacked layouts (below 992px: 800x600 landscape, tablets) --------
   Section 20 sets height:auto here but NOT max-height, and the global
   `overflow-x: hidden` on .main turns its overflow-y from visible into auto.
   The net effect was that .main silently became the scroller while still
   clamped to 100dvh - the content was reachable, but only by accident of the
   cascade. Clearing max-height makes the DOCUMENT the scroller, which is what
   the portrait tier below already relies on, so both behave the same way. */
@media (max-width: 991.98px) {
  body.pos-app, body.pos-app .main.main-app { max-height: none !important; }
  /* position:sticky is measured against the nearest scrollport. .main, .card
     and .card-body are all overflow hidden/auto, which makes each of them a
     scrollport that never scrolls - so the pin below would silently do
     nothing. `overflow-x: clip` still blocks horizontal drift but, unlike
     hidden, does NOT create a scroll container, so the document stays the
     scrollport and sticky works. Heights are auto in this tier and #cart /
     #product keep their own max-height caps, so nothing needs clipping on Y.
     Where clip is unsupported the buttons simply scroll normally. */
  body.pos-app .main.main-app { overflow-x: clip; overflow-y: visible; }
  body.pos-app .card,
  body.pos-app .card > .card-body { overflow-x: clip; overflow-y: visible; }

  /* The card and its body are flex items with the default `flex-shrink: 1`,
     so in a height:auto column they computed SHORTER than their own content
     (438px around ~700px of children). Visually it still drew, but the
     card-body no longer contained the button row, which left sticky with no
     valid range to pin inside. Let both size to their content. */
  body.pos-app #order-form > .row > [class*="col-md-"] > .card,
  body.pos-app .card > .card-body {
    flex: 0 0 auto !important;
    height: auto !important;
  }

  /* Section 20 tries to cap these panels for the stacked layout, but section
     32's `flex: 1 1 0 !important` / `max-height: none !important` come later
     in the file at equal specificity and win - so #product was resolving to
     8px tall here (a flex basis of 0 inside a height:auto column) and the
     product grid was an unusable sliver. Re-assert real heights from the end
     of the file, where they actually stick. */
  body.pos-app #running-orders {
    flex: 0 0 auto !important;
    height: auto !important;
    min-height: 140px !important;
    max-height: 260px !important;
  }
  body.pos-app #product {
    flex: 0 0 auto !important;
    height: auto !important;
    min-height: 220px !important;
    max-height: 46vh !important;
  }
  body.pos-app .card > .card-body > #cart {
    flex: 0 0 auto !important;
    height: auto !important;
    min-height: 90px !important;
    max-height: 38vh !important;
  }
  /* The order panel is a long block in a scrolling page here, so pin its
     actions - otherwise billing means scrolling to the very bottom. */
  body.pos-app .card > .card-body > .buttons {
    position: sticky;
    bottom: 0;
    z-index: 5;
    background: var(--pos-surface);
    border-top: 1px solid var(--pos-border);
    margin-top: 0;
    padding-top: 8px;
  }
}

/* ---- portrait terminals (768x1024, 800x1280) -------------------------- */
/* Tall and narrow: the 3-pane grid cannot work, so panels stack and the page
   scrolls. Each region keeps its own scroller so the buttons stay reachable
   without scrolling the whole document. */
@media (orientation: portrait) and (max-width: 1024px) {
  body.pos-app, body.pos-app .main.main-app { height: auto; max-height: none; overflow: visible; }
  body.pos-app #order-form, body.pos-app #order-form > .row { height: auto; }
  body.pos-app #order-form > .row > [class*="col-md-"] { height: auto; margin-bottom: 9px !important; }
  body.pos-app #order-form > .row > [class*="col-md-"] > .card { height: auto !important; }
  body.pos-app #running-orders { max-height: 240px !important; flex: 0 0 auto !important; }
  body.pos-app #product { max-height: 44vh !important; flex: 0 0 auto !important; }
  body.pos-app .card > .card-body > #cart { max-height: 40vh !important; flex: 0 0 auto !important; }
  /* Stacked, the buttons are at the end of a scrolling document - pin them so
     they are always one tap away. */
  body.pos-app .card > .card-body > .buttons {
    position: sticky;
    bottom: 0;
    z-index: 5;
    background: var(--pos-surface);
    border-top: 1px solid var(--pos-border);
    margin-top: 0;
    padding-top: 8px;
  }
}

/* Coarse pointers (resistive / capacitive touch panels): keep the targets that
   get tapped constantly comfortably large.

   Deliberately NOT a blanket rule on .form-control / .form-select. A blanket
   min-height out-specifies the compact summary heights in the tiers above and
   silently undid them on exactly the hardware those tiers exist for. The
   frequently-tapped controls are named instead; the summary fields (service
   charge, discount) are set once per order and stay compact. */
@media (pointer: coarse) {
  body.pos-app .card > .card-body > .buttons .btn { min-height: 44px; }
  body.pos-app .pos-otype-row .btn { min-height: 40px; }
  body.pos-app #customer-select,
  body.pos-app #waiter-select { min-height: 38px; }
  body.pos-app #cart-table .btn,
  body.pos-app #cart-table button,
  body.pos-app #cart-table input[type="number"] { min-width: 32px; min-height: 32px; }
  /* Product tiles are the primary target: give them a real tap area. */
  body.pos-app #product .add-to-cart { min-height: 40px; }
}


/* ==========================================================================
   36  APP SHELL ALIGNMENT  (sidebar / header / main share one width)
   --------------------------------------------------------------------------
   The template hardcodes the shell offsets:

       @media (min-width: 992px)  .header-main { left: 240px }
                                  .main { margin-left: 240px }
       @media (min-width: 1400px) .header-main { left: 250px }
                                  .main { margin-left: 250px }
       .sidebar-hide .sidebar     { left: -250px }

   while the rail itself is sized from --pos-sidebar-w (264px). The two never
   agreed, so on every page with the sidebar open the header and main content
   started 14-24px UNDERNEATH the rail - which is what clipped the header's
   hamburger by 9px at 1366x768 - and at 1024, where the rail narrows to
   200px, the same offsets left a 40px dead gap instead.

   `.sidebar-hide .sidebar { left: -250px }` was wrong for the same reason: a
   264px rail slid to -250px leaves a 14px sliver of itself on screen, on top
   of the page content.

   Everything below derives from --pos-sidebar-w so the three parts of the
   shell cannot drift apart again. Scoped to >=992px because below that the
   template runs the rail off-canvas with .sidebar-show and main takes no
   offset at all.
   ========================================================================== */

@media (min-width: 992px) {
  .header-main { left: var(--pos-sidebar-w); }
  .main        { margin-left: var(--pos-sidebar-w); }

  /* Collapsed rail: the header had no collapsed offset at all, so it stayed
     at the template's 240px while the rail shrank to 70px. */
  body.sidebar-collapsed .header-main { left: var(--pos-sidebar-w-collapsed); }
  body.sidebar-collapsed .main        { margin-left: var(--pos-sidebar-w-collapsed); }

  /* Hidden rail: slide it fully off, whatever its current width. */
  body.sidebar-hide .sidebar     { left: calc(-1 * var(--pos-sidebar-w) - 8px); }
  body.sidebar-hide .header-main { left: 0; }
  body.sidebar-hide .main        { margin-left: 0; }

  /* If both state classes are ever set at once, hidden must win outright.
     Otherwise the rail resolves to the 70px collapsed width while also being
     translated off-canvas, which is what left a strip of icons on screen
     instead of collapsing away. These selectors carry an extra class so they
     beat the single-class collapsed rules above. */
  body.sidebar-hide.sidebar-collapsed .sidebar {
    left: calc(-1 * var(--pos-sidebar-w) - 8px);
  }
  body.sidebar-hide.sidebar-collapsed .header-main { left: 0; }
  body.sidebar-hide.sidebar-collapsed .main        { margin-left: 0; }
}

/* A fully hidden rail must be inert at every width, not merely moved: an
   off-canvas element still takes focus from the keyboard and can still be
   clicked through by assistive tech. */
body.sidebar-hide .sidebar {
  visibility: hidden;
  pointer-events: none;
}
/* ...but never when it is being shown deliberately on a small screen. */
body.sidebar-hide.sidebar-show .sidebar {
  visibility: visible;
  pointer-events: auto;
  left: 0;
}


/* ==========================================================================
   37  RUNNING ORDERS - filter visibility
   --------------------------------------------------------------------------
   The filter toggles this class instead of writing inline display styles per
   card. With 1000+ orders open, .show()/.hide() per card on every keystroke
   was a per-card inline style write plus a getComputedStyle read to count the
   survivors, i.e. a full style recalc per keypress.
   ========================================================================== */

#running-orders .order-card.ro-hidden { display: none !important; }


/* ==========================================================================
   38  FINALIZE SALE - progressive payment split
   --------------------------------------------------------------------------
   Cash is the only tender on screen when the modal opens, already holding the
   full payable, so a single-tender sale is one tap. "Add payment" reveals a
   method dropdown plus an amount; confirming reveals that method's row and
   cash re-balances. Every control here is sized for a finger.
   ========================================================================== */

.fs-pm-row { position: relative; }
.fs-pm-row[hidden] { display: none !important; }

/* Small remove affordance on an added tender's label. */
.fs-pm-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  margin-left: 6px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--pos-danger-soft);
  color: #991B1B;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  vertical-align: middle;
}
.fs-pm-remove:hover { background: rgba(220, 38, 38, .2); }

.fs-add-pay { margin-top: 12px; }
.fs-add-pay__form {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) auto auto;
  gap: 8px;
  align-items: center;
  margin-top: 10px;
  padding: 10px;
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-md);
  background: var(--pos-surface-alt);
}
.fs-add-pay__form[hidden] { display: none !important; }
.fs-add-pay__form .form-select,
.fs-add-pay__form .form-control { min-height: 42px; }
.fs-add-pay__form .btn { min-height: 42px; white-space: nowrap; }
.fs-add-pay__err {
  margin-top: 8px;
  font-size: 12.5px;
  font-weight: 600;
  color: #991B1B;
}
.fs-add-pay__err[hidden] { display: none !important; }

/* Narrow panels and short terminals: stack the adder rather than crushing it. */
@media (max-width: 900px) {
  .fs-add-pay__form { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .fs-add-pay__form .btn { grid-column: span 1; }
}
@media (pointer: coarse) {
  .fs-add-pay__form .form-select,
  .fs-add-pay__form .form-control,
  .fs-add-pay__form .btn { min-height: 46px; }
  .fs-pm-remove { width: 28px; height: 28px; font-size: 15px; }
}


/* ==========================================================================
   39  SIDEBAR COLLAPSE CONTROL + CONTROL SIZING
   --------------------------------------------------------------------------
   The collapse button lives in the brand band but, unlike the X it replaces,
   it does not sit on top of the logo: the band is a flex row and the button
   is a sibling that reserves its own space.
   ========================================================================== */

.sidebar-header { gap: 8px; justify-content: space-between !important; }
.sidebar-header .sidebar-logo { width: auto; flex: 1 1 auto; justify-content: flex-start; }

.sidebar-collapse-btn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-sm);
  background: var(--pos-surface);
  color: var(--pos-text-secondary);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  transition: background var(--pos-t), color var(--pos-t);
}
.sidebar-collapse-btn:hover {
  background: var(--pos-surface-hover);
  color: var(--pos-primary);
  border-color: var(--pos-primary);
}
.sidebar-collapse-btn i { pointer-events: none; }
/* Icon-rail mode (the theme panel's own "collapsed" preference) still needs the
   button centred and the arrow flipped. */
body.sidebar-collapsed .sidebar-collapse-btn i { transform: rotate(180deg); }
body.sidebar-collapsed .sidebar-header { justify-content: center !important; flex-wrap: wrap; }
body.sidebar-collapsed .sidebar-header .sidebar-logo { flex: 0 0 auto; }


/* --------------------------------------------------------------------------
   Control sizing. The earlier touch pass pushed every tray button to 44-46px,
   which on a desktop-sized terminal reads as oversized and cost the panels
   real vertical room. These are sized for a finger without dominating the
   panel: 38px standard, 40px for the three order actions, and the coarse
   pointer bump is now a modest +2px rather than +6px.
   -------------------------------------------------------------------------- */

body.pos-app .pos-order-actions .btn { min-height: 36px; font-size: 12.5px !important; }
body.pos-app .card > .card-body > .buttons .btn { min-height: 40px; }
body.pos-app .pos-otype-row .btn { min-height: 36px; }

@media (pointer: coarse) {
  body.pos-app .card > .card-body > .buttons .btn { min-height: 42px; }
  body.pos-app .pos-otype-row .btn { min-height: 38px; }
  body.pos-app .pos-order-actions .btn { min-height: 38px; }
  body.pos-app #customer-select,
  body.pos-app #waiter-select { min-height: 36px; }
}


/* ==========================================================================
   40  CONTROL SIZING - final pass
   --------------------------------------------------------------------------
   Reported as "buttons are too big". The touch pass had pushed the tray and
   order actions to 44-46px, which on a desktop-class terminal dominates the
   panel and steals vertical room from the cart. Sizes below are comfortable
   for a finger (>=36px, the practical floor for a resistive POS panel) without
   the panel looking like a phone app. Cart-row controls are deliberately
   smaller: they sit inside a dense list, not on the main action path.
   ========================================================================== */

body.pos-app .btn { font-size: 12.5px !important; }
body.pos-app .card > .card-body > .buttons .btn { min-height: 38px; font-size: 13px !important; }
body.pos-app .pos-order-actions .btn      { min-height: 34px; }
body.pos-app .pos-otype-row .btn          { min-height: 34px; }
body.pos-app #customer-select,
body.pos-app #waiter-select               { min-height: 34px; }
body.pos-app .order-summary-row .form-select,
body.pos-app .order-summary-row .form-control { min-height: 30px; height: 30px; }
body.pos-app #orderNote                   { min-height: 34px; }

/* Touch panels get a small, honest bump - not a different design. */
@media (pointer: coarse) {
  body.pos-app .card > .card-body > .buttons .btn { min-height: 42px; }
  body.pos-app .pos-order-actions .btn { min-height: 38px; }
  body.pos-app .pos-otype-row .btn     { min-height: 38px; }
  body.pos-app #customer-select,
  body.pos-app #waiter-select          { min-height: 38px; }
  /* Cart rows stay compact so more of the order is visible at once. */
  body.pos-app #cart-table .sub-quantity,
  body.pos-app #cart-table .add-quantity { width: 30px; min-width: 30px; min-height: 30px; }
  body.pos-app #cart-table .cart-remove-item { width: 32px; min-width: 32px; height: 32px; min-height: 32px; }
}


/* --------------------------------------------------------------------------
   41  CART PRICE COLUMN + ACTION BUTTON HEIGHT
   --------------------------------------------------------------------------
   Two measured problems.

   1. The per-row "Rs" label ate ~18px of the Price column while the Total
      column next to it shows a bare number - so the currency was both
      redundant and inconsistent, and it squeezed the input until a large
      figure clipped. The label is hidden and the input fills the column, so
      Price and Total now read the same way and six-figure prices fit.

   2. min-height does not cap a button: vertical padding pushed the order
      actions to 46px even with min-height:38px. Height is set explicitly and
      the padding zeroed so the number is the number.
   -------------------------------------------------------------------------- */

#cart-table td.cart-price-cell > span { display: none; }
#cart-table .cart-item-price,
#cart-table input.cart-item-price {
  width: 100% !important;
  max-width: none !important;
}

body.pos-app .card > .card-body > .buttons .btn {
  height: 38px;
  min-height: 38px;
  padding-top: 0;
  padding-bottom: 0;
  line-height: 1;
}
@media (pointer: coarse) {
  body.pos-app .card > .card-body > .buttons .btn { height: 42px; min-height: 42px; }
}

/* Touch bump for the two ID-selector order actions (see the note at
   #place-order): a class-based rule cannot reach them. */
@media (pointer: coarse) {
  #place-order,
  #update-order { min-height: 42px; height: 42px; }
}

/* Headroom for large figures. At 12.5px the Price box fitted "1400.00" exactly,
   which left nothing spare for a five-figure item price ("15000.00"). One step
   down buys ~2 characters and is still comfortably legible on a POS panel. */
#cart-table .cart-item-price,
#cart-table td.cart-total-cell { font-size: 12px !important; }

/* Type filtering is applied by the container's data-ro-type attribute rather
   than by scripting a class onto every card. At 1276 cards the scripted
   version froze the UI for ~840ms per chip click; this hands the same work to
   the style engine, which does it internally. .ro-hidden remains for the text
   search, which CSS cannot express. */
#running-orders[data-ro-type="otype-dinein"]   .order-card:not(.otype-dinein),
#running-orders[data-ro-type="otype-takeaway"] .order-card:not(.otype-takeaway),
#running-orders[data-ro-type="otype-delivery"] .order-card:not(.otype-delivery),
#running-orders[data-ro-type="otype-room"]     .order-card:not(.otype-room) {
  display: none !important;
}


/* ==========================================================================
   42  DARK MODE - TABLES, REPORTS AND THE BRAND-BAND CONTROLS
   --------------------------------------------------------------------------
   Measured on the daily sales report in dark mode: table cells rendered
   fg 248,250,252 on bg 255,255,255 - a contrast ratio of 1.05, i.e. white text
   on white. The card behind them was correctly dark; the cells were not.

   Cause: Bootstrap paints every cell with `background-color: var(--bs-table-bg)`
   and hardcodes that variable to #fff. Setting `color` for dark mode therefore
   produced invisible text on a white cell. Re-pointing the Bootstrap table
   variables fixes every table in the app at once rather than page by page.
   ========================================================================== */

:root[data-theme="dark"] .table {
  --bs-table-bg: transparent;              /* inherit the card, do not paint white */
  --bs-table-color: var(--pos-text-primary);
  --bs-table-border-color: var(--pos-border);
  --bs-table-striped-bg: rgba(255, 255, 255, .035);
  --bs-table-striped-color: var(--pos-text-primary);
  --bs-table-hover-bg: rgba(255, 255, 255, .06);
  --bs-table-hover-color: var(--pos-text-primary);
  --bs-table-accent-bg: transparent;
  color: var(--pos-text-primary);
}
:root[data-theme="dark"] .table > :not(caption) > * > * {
  background-color: transparent;
  color: var(--pos-text-primary);
  border-bottom-color: var(--pos-border);
}
:root[data-theme="dark"] .table > thead > tr > th {
  background: var(--pos-surface-alt) !important;
  color: var(--pos-text-secondary);
}
/* DataTables chrome (search box, pager, "showing N of M") sits outside .table. */
:root[data-theme="dark"] .dataTables_wrapper,
:root[data-theme="dark"] .dataTables_info,
:root[data-theme="dark"] .dataTables_length,
:root[data-theme="dark"] .dataTables_filter { color: var(--pos-text-secondary); }
:root[data-theme="dark"] .dataTables_wrapper .paginate_button { color: var(--pos-text-primary) !important; }

/* Breadcrumbs measured at 4.22 - under AA for body text. */
:root[data-theme="dark"] .breadcrumb-item,
:root[data-theme="dark"] .breadcrumb-item.active,
:root[data-theme="dark"] .breadcrumb-item + .breadcrumb-item::before {
  color: var(--pos-text-secondary);
}

/* The brand band is deliberately forced white in dark mode (the logo is drawn
   for light backgrounds). Anything sitting ON that band must therefore keep
   LIGHT-mode colours, or it renders as a dark blob on white - which is why the
   collapse control looked missing. */
:root[data-theme="dark"] .sidebar-collapse-btn {
  background: #FFFFFF;
  border-color: #E2E8F0;
  color: #475569;
}
:root[data-theme="dark"] .sidebar-collapse-btn:hover {
  background: #F1F5F9;
  border-color: var(--pos-primary);
  color: var(--pos-primary);
}


/* ==========================================================================
   43  REPORT PAGES
   --------------------------------------------------------------------------
   Reports are read, exported and printed. They get a calmer table than the
   POS: tabular figures so money columns align, a sticky header so the columns
   stay identified while scrolling a long day, and right-aligned numerics.
   ========================================================================== */

.rpt-card { padding-bottom: 6px; }
.rpt-card .table { font-size: 13px; }
.rpt-card .table th { white-space: nowrap; }

/* Money and count columns line up on the decimal point. */
.rpt-card .table td.num,
.rpt-card .table th.num,
.rpt-card .table td.rpt-tender,
.rpt-card .table th.rpt-tender {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* A tender column that took nothing shows a dash, not a shouting 0.00. */
.rpt-zero { color: var(--pos-text-muted); }

/* Header stays put while scanning a long report. */
.rpt-card .table thead th {
  position: sticky;
  top: 0;
  z-index: 2;
}

/* The reconciliation rows are the point of the footer - make them read as a
   conclusion rather than more table. */
.rpt-card .table tfoot tr.rpt-tender-total td {
  border-top: 2px solid var(--pos-border-strong);
  background: var(--pos-surface-alt);
}
.rpt-card .table tfoot tr.rpt-mismatch td {
  background: var(--pos-danger-soft);
  color: #991B1B;
}
:root[data-theme="dark"] .rpt-card .table tfoot tr.rpt-mismatch td { color: #FCA5A5; }

/* DataTables paints its own row striping - table.dataTable tbody tr.odd and
   td.sorting_1 carry a hardcoded white. That is what survived the Bootstrap
   variable fix above and left cells at contrast 1.05 (white on white): the
   stripe is on the <tr>, not on the cell, so a cell-level rule never reached
   it. Neutralised here and restored as a faint light-on-dark stripe. */
:root[data-theme="dark"] table.dataTable tbody tr,
:root[data-theme="dark"] table.dataTable tbody tr.odd,
:root[data-theme="dark"] table.dataTable tbody tr.even {
  background-color: transparent !important;
  color: var(--pos-text-primary);
}
:root[data-theme="dark"] table.dataTable.stripe tbody tr.odd,
:root[data-theme="dark"] table.dataTable.display tbody tr.odd {
  background-color: rgba(255, 255, 255, .035) !important;
}
:root[data-theme="dark"] table.dataTable tbody tr:hover,
:root[data-theme="dark"] table.dataTable.hover tbody tr:hover,
:root[data-theme="dark"] table.dataTable.display tbody tr:hover {
  background-color: rgba(255, 255, 255, .06) !important;
}
/* The sorted column gets its own tint from DataTables - same treatment. */
:root[data-theme="dark"] table.dataTable tbody td.sorting_1,
:root[data-theme="dark"] table.dataTable tbody tr.odd > .sorting_1,
:root[data-theme="dark"] table.dataTable tbody tr.even > .sorting_1 {
  background-color: transparent !important;
  color: var(--pos-text-primary);
}
:root[data-theme="dark"] table.dataTable thead th,
:root[data-theme="dark"] table.dataTable thead td {
  background-color: var(--pos-surface-alt) !important;
  color: var(--pos-text-secondary);
  border-bottom-color: var(--pos-border);
}
/* Search / length inputs in the DataTables chrome. */
:root[data-theme="dark"] .dataTables_wrapper input,
:root[data-theme="dark"] .dataTables_wrapper select {
  background: var(--pos-surface-alt);
  color: var(--pos-text-primary);
  border: 1px solid var(--pos-border);
}


/* ==========================================================================
   44  REPORT TABLES - SORT INDICATORS AND ALIGNMENT
   --------------------------------------------------------------------------
   DataTables 1.10 draws TWO absolutely positioned arrows per sortable header
   (:before = up at right:1em, :after = down at right:0.5em). On a narrow
   column such as "SN" there is no room for them, so both land on top of the
   label - the stacked triangles over the SN text.

   The pair is replaced with a single chevron that reflects the actual state,
   sitting in padding reserved for it, so it can never overlap a label however
   narrow the column. Loaded after the CDN stylesheet, hence the !important on
   the properties DataTables sets inline in its own sheet.
   ========================================================================== */

/* Reserve room for the indicator and give it something to anchor to. */
table.dataTable thead th,
table.dataTable thead td {
  position: relative;
  padding-right: 26px !important;
  vertical-align: middle;
  white-space: nowrap;
}

/* Kill the stock double-arrow entirely. */
table.dataTable thead .sorting:before,
table.dataTable thead .sorting:after,
table.dataTable thead .sorting_asc:before,
table.dataTable thead .sorting_asc:after,
table.dataTable thead .sorting_desc:before,
table.dataTable thead .sorting_desc:after,
table.dataTable thead .sorting_asc_disabled:before,
table.dataTable thead .sorting_asc_disabled:after,
table.dataTable thead .sorting_desc_disabled:before,
table.dataTable thead .sorting_desc_disabled:after {
  content: "" !important;
  display: none !important;
}

/* One indicator, vertically centred, in the reserved gutter. */
table.dataTable thead th.sorting,
table.dataTable thead th.sorting_asc,
table.dataTable thead th.sorting_desc { cursor: pointer; }

table.dataTable thead th.sorting::after,
table.dataTable thead th.sorting_asc::after,
table.dataTable thead th.sorting_desc::after {
  content: "" !important;
  display: block !important;
  position: absolute;
  top: 50%;
  right: 9px;
  width: 8px;
  height: 8px;
  margin-top: -5px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
  opacity: .25;
  transition: opacity var(--pos-t), transform var(--pos-t);
  pointer-events: none;
}
/* Active column: full-strength chevron, flipped for ascending. */
table.dataTable thead th.sorting_asc::after {
  opacity: .9;
  margin-top: -1px;
  transform: rotate(-135deg);
}
table.dataTable thead th.sorting_desc::after { opacity: .9; }
table.dataTable thead th.sorting:hover::after { opacity: .55; }

/* Right-aligned money headers put the label left of the indicator, not under it. */
.rpt-card table.dataTable thead th.num,
.rpt-card table.dataTable thead th.rpt-tender { padding-right: 26px !important; }

/* The wrapper chrome: search box and pager should match the app, not the CDN. */
.dataTables_wrapper .dataTables_filter input {
  min-height: 34px;
  padding: 4px 10px;
  margin-left: 8px;
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-sm);
  background: var(--pos-surface);
  color: var(--pos-text-primary);
}
.dataTables_wrapper .dataTables_filter input:focus {
  outline: none;
  border-color: var(--pos-primary);
  box-shadow: 0 0 0 3px rgba(var(--pos-primary-rgb), .18);
}
.dataTables_wrapper .dataTables_paginate .paginate_button {
  padding: 5px 11px !important;
  margin-left: 3px;
  border-radius: var(--pos-radius-sm) !important;
  border: 1px solid var(--pos-border) !important;
  background: var(--pos-surface) !important;
  color: var(--pos-text-primary) !important;
}
.dataTables_wrapper .dataTables_paginate .paginate_button.current {
  background: var(--pos-primary) !important;
  border-color: var(--pos-primary) !important;
  color: #fff !important;
}
.dataTables_wrapper .dataTables_paginate .paginate_button.disabled {
  opacity: .45;
  cursor: default;
}

/* DataTables 1.10 ALSO paints arrows as background PNG sprites on the header
   (images/sort_asc.png etc.), independently of the :before/:after pair killed
   above. That sprite is what remained stacked over the "SN" label - the column
   is too narrow for a centre-right background image, so it sat on the text.
   The chevron above is the single indicator; these sprites must go.

   The CDN declares these with !important AND loads after this file, so an
   equally-specific !important here would still lose. The class is repeated
   (.dataTable.dataTable) purely to raise specificity above theirs - the
   cheapest way to win without reordering the stylesheets or editing a
   third-party file. */
table.dataTable.dataTable thead th,
table.dataTable.dataTable thead td,
table.dataTable.dataTable thead .sorting,
table.dataTable.dataTable thead .sorting_asc,
table.dataTable.dataTable thead .sorting_desc,
table.dataTable.dataTable thead .sorting_asc_disabled,
table.dataTable.dataTable thead .sorting_desc_disabled {
  background-image: none !important;
}


/* ==========================================================================
   45  SIDEBAR SUBMENU - LONG LABELS
   --------------------------------------------------------------------------
   "Payment type Sale Report" wrapped with its second line starting at the
   rail's left edge instead of under the first line, so the item read as two
   unrelated entries. Two causes: `display: -webkit-box` (needed for
   line-clamp) does not honour the indent for wrapped lines, and the leading
   dash marker is inline, so the wrap point sits to its left.

   The marker is absolutely positioned and the label indented past it, which
   gives a proper hanging indent - every wrapped line aligns under the text.
   ========================================================================== */

.nav-sub-link,
.nav-sidebar .nav-sub-link,
.nav-sub .nav-sub-link.dropdown-item {
  display: block !important;          /* not -webkit-box: it breaks the indent */
  position: relative;

  /* THE fix for wrapped labels.
     The template sets `text-indent: 30.5px` on this link. text-indent moves
     the FIRST LINE ONLY, so "Purchased Stock Report" put "Purchased Stock"
     at 50px and dropped "Report" back to the 20px padding edge - the wrapped
     line landed under the dash instead of under the text, and the item read
     as two separate entries. Padding indents every line equally, so the
     indent has to come from padding and text-indent must be cleared. */
  text-indent: 0 !important;
  padding-left: 30px !important;

  white-space: normal !important;
  overflow: visible !important;       /* a two-line label must not be clipped */
  text-overflow: clip !important;
  overflow-wrap: break-word;
  line-height: 1.35;
}

/* Replace whatever marker the template supplies with one that cannot be
   reflowed by the text beside it. Sits on the FIRST line - `top: 50%` would
   centre it between the two lines of a wrapped label, leaving it floating
   opposite the gap rather than against the label it belongs to. */
.nav-sub .nav-sub-link::before,
.nav-sidebar .nav-sub-link::before,
.nav-sub .nav-sub-link.dropdown-item::before {
  content: "" !important;
  position: absolute !important;
  left: 14px !important;
  top: 0.95em !important;
  transform: none !important;
  width: 7px !important;
  height: 1px !important;
  background: currentColor !important;
  opacity: .45;
}

/* The template widens and slides the marker on hover, which made the dash
   visibly jump left and grow every time the pointer crossed an item. */
.nav-sidebar .nav-sub-link:hover::before,
.nav-sidebar .nav-sub-link:focus::before,
.nav-sub .nav-sub-link:hover::before,
.nav-sub .nav-sub-link.dropdown-item:hover::before {
  left: 14px !important;
  width: 7px !important;
  opacity: .75;
}


/* ==========================================================================
   36  STATUS PILLS
   --------------------------------------------------------------------------
   Active/Inactive used to be rendered with Bootstrap's .alert-danger - an
   ALERT component - plus an inline colour override. That gave each cell
   alert padding and a near-block box, so the column never lined up and the
   "Inactive" label carried a danger background regardless of meaning.
   ========================================================================== */

.status-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-width: 74px;
  justify-content: center;
  padding: 3px 10px;
  border-radius: var(--pos-radius-pill);
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
  border: 1px solid transparent;
}
.status-pill::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex: 0 0 auto;
}
.status-pill--on {
  background: var(--pos-success-soft);
  color: var(--pos-success);
  border-color: rgba(5, 150, 105, .2);
}
.status-pill--off {
  background: var(--pos-surface-hover);
  color: var(--pos-text-secondary);
  border-color: var(--pos-border);
}

/* Keep the whole status column on one axis. */
td:has(> .status-pill) { white-space: nowrap; }


/* ==========================================================================
   37  TABLES MANAGEMENT
   --------------------------------------------------------------------------
   Compact status cards. The previous screen filled the entire card with
   bright red for reserved/inactive tables, floated the status text -35px so
   it overlapped a half-width plate graphic, and exposed no edit, delete or
   reserve control. Colour now lives in a badge and a top rail; the card
   itself stays light and readable.
   ========================================================================== */

.tbl-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.tbl-search { min-width: 210px; max-width: 280px; }

.tbl-filters {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}
.tbl-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 34px;
  padding: 0 12px;
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-pill);
  background: var(--pos-surface);
  color: var(--pos-text-secondary);
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--pos-t), color var(--pos-t), border-color var(--pos-t);
}
.tbl-chip:hover { background: var(--pos-primary-soft); color: var(--pos-primary); }
.tbl-chip.active {
  background: var(--pos-primary);
  border-color: var(--pos-primary);
  color: #fff;
}
.tbl-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 18px;
  padding: 0 5px;
  border-radius: var(--pos-radius-pill);
  background: var(--pos-surface-hover);
  color: var(--pos-text-secondary);
  font-size: 11px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.tbl-chip.active .tbl-count { background: rgba(255, 255, 255, .22); color: #fff; }

.tbl-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 12px;
}

.tbl-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px;
  background: var(--pos-surface);
  border: 1px solid var(--pos-border);
  border-top: 3px solid var(--pos-border-strong);
  border-radius: var(--pos-radius-lg);
  box-shadow: var(--pos-shadow-sm);
  min-width: 0;
  transition: border-color var(--pos-t), box-shadow var(--pos-t), transform var(--pos-t);
}
.tbl-card:hover { box-shadow: var(--pos-shadow-md); transform: translateY(-1px); }

.tbl-card__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  min-width: 0;
}
.tbl-no {
  font-size: 15px;
  font-weight: 700;
  color: var(--pos-text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tbl-badge {
  flex: 0 0 auto;
  padding: 2px 9px;
  border-radius: var(--pos-radius-pill);
  font-size: 10.5px;
  font-weight: 700;
  white-space: nowrap;
}

/* Status carried by the rail + badge only - never a saturated card fill. */
.tbl-card.is-available { border-top-color: var(--pos-success); }
.tbl-card.is-available .tbl-badge { background: var(--pos-success-soft); color: var(--pos-success); }
.tbl-card.is-reserved  { border-top-color: var(--pos-warning); }
.tbl-card.is-reserved .tbl-badge  { background: var(--pos-warning-soft); color: var(--pos-warning); }
.tbl-card.is-occupied  { border-top-color: var(--pos-primary); }
.tbl-card.is-occupied .tbl-badge  { background: var(--pos-primary-soft); color: var(--pos-primary); }
.tbl-card.is-inactive  { border-top-color: var(--pos-border-strong); }
.tbl-card.is-inactive .tbl-badge  { background: var(--pos-surface-hover); color: var(--pos-text-muted); }
.tbl-card.is-inactive .tbl-no     { color: var(--pos-text-secondary); }

.tbl-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  font-size: 12px;
  color: var(--pos-text-secondary);
}
.tbl-meta i { margin-right: 3px; }

.tbl-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: auto;
  padding-top: 8px;
  border-top: 1px solid var(--pos-border);
  flex-wrap: wrap;
}
.tbl-act { min-height: 34px; font-size: 12px !important; padding: 0 10px !important; }
.tbl-actions .tbl-act:first-child { flex: 1 1 auto; }

/* Edit carried Bootstrap's .btn-light (white on grey text) and Delete only
   found its red on hover, so next to the blue Reserve both read as dead
   controls. Each action now states what it is at rest: blue = change it,
   red = remove it. Delete stays the smaller, icon-only control so it is
   clearly coloured without becoming the loudest thing on the card. */
.tbl-actions .tbl-act.btn-light,
.tbl-edit {
  background: var(--pos-primary-soft) !important;
  border: 1px solid rgba(var(--pos-primary-rgb), .35) !important;
  color: var(--pos-primary) !important;
  font-weight: 600 !important;
}
.tbl-actions .tbl-act.btn-light:hover,
.tbl-edit:hover {
  background: var(--pos-primary) !important;
  border-color: var(--pos-primary) !important;
  color: #fff !important;
}

.tbl-del {
  flex: 0 0 auto;
  min-width: 34px;
  padding: 0 9px !important;
  background: var(--pos-danger-soft) !important;
  border: 1px solid rgba(220, 38, 38, .35) !important;
  color: var(--pos-danger) !important;
}
.tbl-del:hover,
.tbl-del:focus-visible {
  background: var(--pos-danger) !important;
  border-color: var(--pos-danger) !important;
  color: #fff !important;
}
/* The icon must inherit the button colour, never stay a stale grey. */
.tbl-act i, .tbl-del i { color: inherit !important; }

@media (max-width: 575.98px) {
  .tbl-grid { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); }
  .tbl-search { max-width: 100%; }
}


/* ==========================================================================
   38  FOOTER WATERMARK
   --------------------------------------------------------------------------
   The footer had no rule of its own anywhere in the bundle, so it inherited
   the body's near-black text and a full-strength link blue - far too loud
   for a watermark, and unreadable once dark mode inverted the page.
   It now sits in the muted tone, with the two lines on one baseline.
   ========================================================================== */

.footer {
  padding: 0 !important;
  background: transparent;
}

.main-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px 24px;
  flex-wrap: wrap;
  margin-top: 32px !important;
  padding: 14px 2px 4px;
  border-top: 1px solid var(--pos-border);
  font-size: 11.5px;
  line-height: 1.6;
  color: var(--pos-text-muted);
}
.main-footer span { color: var(--pos-text-muted); }

/* The vendor link stays readable without shouting: muted at rest, brand
   colour on hover, and it keeps a focus ring for keyboard users. */
.main-footer a {
  color: var(--pos-text-secondary);
  font-weight: 600;
  text-decoration: none;
  transition: color var(--pos-t);
}
.main-footer a:hover { color: var(--pos-primary); text-decoration: underline; }
.main-footer a:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(var(--pos-primary-rgb), .3);
  border-radius: 3px;
}

/* Narrow screens: stack and centre rather than squeeze onto one row. */
@media (max-width: 575.98px) {
  .main-footer {
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 6px;
  }
}

/* The POS workspace is a locked-height grid - a footer there would steal
   height from the panels and push the checkout tray off screen. */
body.pos-app .footer,
body.pos-app .main-footer { display: none !important; }

/* Printed documents keep the watermark, always in black on white. */
@media print {
  .main-footer {
    border-top-color: #999 !important;
    color: #000 !important;
  }
  .main-footer span,
  .main-footer a { color: #000 !important; }
}


/* ==========================================================================
   39  REPORT PAGES  (sales + stock, matched to the hotel reports layout)
   --------------------------------------------------------------------------
   The hotel reports screen reads well because its table sits in a full-width
   card with a bordered, rounded scroll region and a sticky header. The sales
   and stock reports used `container card-one card w-90 mt-5`, which is a
   Bootstrap .container INSIDE the page container, capped at 90% and pushed
   down 3rem - so every report was inset, off-centre against the breadcrumb
   above it, and its table ran edge to edge with no frame.

   This normalises that shape for all 23 report pages without touching their
   markup, so each one gains the hotel treatment.
   ========================================================================== */

.main-app .container.card-one.card,
.main-app .card-one.card.w-90 {
  width: 100% !important;
  max-width: 100% !important;
  margin: 0 0 16px !important;
  padding: 0 !important;
  background: var(--pos-surface) !important;
  border: 1px solid var(--pos-border) !important;
  border-radius: var(--pos-radius-lg) !important;
  box-shadow: var(--pos-shadow-sm) !important;
  /* .card-one carries height:100% from the theme. That is harmless on a page
     with a single card, but a page that STACKS cards had its first card
     inflate to fill the whole column - a tall empty white panel with a line
     of text at the top, pushing everything after it below the fold. A card
     should be as tall as what is in it. */
  height: auto !important;
}

/* The nested .row .g-3 wrappers these pages use add no columns of their
   own; stop them adding gutters that offset the card from the breadcrumb. */
.main-app .h-100 > .row.g-3,
.main-app .h-100 > .row.g-3 > [class*="col-"] > .row.g-3 {
  --bs-gutter-x: 0;
  margin-left: 0;
  margin-right: 0;
}

/* Scroll region gets the same frame as .hms-table-wrap. */
.main-app .card-body > .table-responsive,
.main-app .table-responsive {
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-md);
  overflow: auto;
  background: var(--pos-surface);
  max-height: 68vh;                 /* long reports scroll, page stays put */
}
.main-app .table-responsive > .table { margin-bottom: 0; }

/* Header stays put while a long report scrolls - the single biggest
   readability win the hotel screen already had. */
.main-app .table-responsive > .table > thead > tr > th {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--pos-surface-alt) !important;
}

/* Totals row reads as a total, not another data row. */
.main-app .table > tfoot > tr > td,
.main-app .table > tfoot > tr > th {
  background: var(--pos-surface-alt);
  font-weight: 700;
  color: var(--pos-text-primary);
  border-top: 1px solid var(--pos-border) !important;
}

/* Numeric columns line up on the decimal wherever a page marks them. */
.num, td.num, th.num,
.main-app .table td.num,
.main-app .table th.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Filter row above the table, aligned like the hotel reports form.
 *
 * SCOPED TO ROWS THAT ACTUALLY CARRY A SUBMIT CONTROL.
 *
 * This was written for one shape - a single row of inputs with a Run button
 * that has to sit level with them - but it was applied to EVERY .row.g-3
 * inside every form in the application, which is 274 rows across 135 pages.
 *
 * flex-end aligns a row's columns along their BOTTOMS. On a filter bar that is
 * right, because every column is the same height and the button is the odd one
 * out. On an ordinary form row it is silently wrong: any column shorter than
 * its neighbour is pushed DOWN by the difference. A field sitting beside one
 * that has a checkbox under it dropped by exactly the height of that checkbox,
 * and a two-column layout where one side had more cards than the other had the
 * whole shorter column shunted to the bottom.
 *
 * Nothing in the markup said "this row is a filter bar", so the rule could not
 * tell them apart - it just took all of them. A submit control inside the row
 * is what distinguishes the two: a filter bar carries its own button, while a
 * form's Save and Cancel belong in a footer beneath the grid, not in it.
 *
 * If :has() is unavailable the rule simply does not apply, and every row
 * top-aligns - which is correct for forms and merely unpolished for the
 * handful of filter bars. That is the right way round to fail. */
.main-app form .row.g-3:has(> [class*="col"] input[type="submit"]),
.main-app form .row.g-3:has(> [class*="col"] button[type="submit"]) {
  align-items: flex-end;
}
.main-app .card-body > .row > .me-auto:empty { display: none; }

/* Report page heading block. */
.rpt-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}
.rpt-head h4 {
  font-size: 19px !important;
  font-weight: 700 !important;
  margin: 0 !important;
  color: var(--pos-text-primary) !important;
  letter-spacing: -.01em;
}

@media print {
  .main-app .table-responsive {
    max-height: none;
    overflow: visible;
    border: 0;
  }
  .main-app .table-responsive > .table > thead > tr > th { position: static; }
}


/* ==========================================================================
   40  TABLE PAGINATION
   --------------------------------------------------------------------------
   Chrome for the dependency-free paginator in pos-ui.js. One bar above the
   table (page size + search + nav) and a compact repeat below it, so on a
   long list the controls are reachable without scrolling back up.
   ========================================================================== */

.pg-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 10px 2px;
  font-size: 12.5px;
  color: var(--pos-text-secondary);
}
.pg-bar--foot { border-top: 1px solid var(--pos-border); }

.pg-left, .pg-right { display: flex; align-items: center; gap: 10px; }
.pg-right { margin-left: auto; }

.pg-size {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  font-size: 12.5px !important;
  font-weight: 500 !important;
  color: var(--pos-text-secondary) !important;
  white-space: nowrap;
}
.pg-size select {
  width: auto;
  min-height: 32px;
  padding: 2px 26px 2px 9px;
  font-size: 12.5px !important;
}

.pg-search { position: relative; min-width: 190px; }
.pg-search > i {
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--pos-text-muted);
  font-size: 15px;
  pointer-events: none;
}
.pg-search input {
  min-height: 32px;
  padding-left: 30px !important;
  font-size: 12.5px !important;
}

.pg-info { font-variant-numeric: tabular-nums; white-space: nowrap; }

.pg-nav { display: inline-flex; align-items: center; gap: 3px; }
.pg-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-sm);
  background: var(--pos-surface);
  color: var(--pos-text-secondary);
  font-size: 16px;
  cursor: pointer;
  transition: background var(--pos-t), color var(--pos-t), border-color var(--pos-t);
}
.pg-btn:hover:not(:disabled) {
  background: var(--pos-primary-soft);
  border-color: var(--pos-primary);
  color: var(--pos-primary);
}
.pg-btn:disabled { opacity: .45; cursor: not-allowed; }
.pg-btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(var(--pos-primary-rgb), .3);
}

.pg-pages {
  min-width: 54px;
  padding: 0 8px;
  text-align: center;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--pos-text-primary);
  white-space: nowrap;
}

@media (max-width: 640px) {
  .pg-bar { justify-content: center; }
  .pg-right { margin-left: 0; width: 100%; justify-content: space-between; }
  .pg-search { min-width: 100%; order: -1; }
}

/* Controls belong on screen, never on a printed report. */
@media print { .pg-bar { display: none !important; } }

/* Seat-accurate table diagram. Generated inline so it inherits the card's
   status colour; small and secondary, never the decorative plate the old
   screen used. */
.tbl-body { display: flex; align-items: center; gap: 12px; }
.tbl-art {
  width: 56px;
  height: 56px;
  flex: 0 0 56px;
  color: var(--pos-text-muted);
  transition: color var(--pos-t);
}
.tbl-card.is-available .tbl-art { color: var(--pos-success); }
.tbl-card.is-reserved  .tbl-art { color: var(--pos-warning); }
.tbl-card.is-occupied  .tbl-art { color: var(--pos-primary); }
.tbl-card.is-inactive  .tbl-art { color: var(--pos-border-strong); }
.tbl-body .tbl-meta { flex-direction: column; align-items: flex-start; gap: 2px; }
@media (max-width: 575.98px) { .tbl-art { width: 44px; height: 44px; flex-basis: 44px; } }


/* ==========================================================================
   41  SELECT2 - alignment + stacking
   --------------------------------------------------------------------------
   The purchase screen pairs select2 with Bootstrap input-groups and puts a
   data table directly beneath it. Two things went wrong: the container was
   not sized like a .form-control, so it sat at a different height to the
   inputs beside it; and the dropdown had no stacking context of its own, so
   it opened UNDER the table headings instead of over them.
   ========================================================================== */

/* Container behaves exactly like a form-control in the flow. */
span.select2.select2-container,
span.select2.select2-container.select2-container--default {
  width: 100% !important;
  min-width: 0;
  display: block;
}
.select2-container--default .select2-selection--single {
  display: flex !important;
  align-items: center;
  height: var(--pos-control-h) !important;
  padding: 0 !important;
  border: 1px solid var(--pos-border) !important;
  border-radius: var(--pos-radius-sm) !important;
  background: var(--pos-surface) !important;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0 30px 0 12px !important;
  line-height: 1.4 !important;
  color: var(--pos-text-primary) !important;
  font-size: 13.5px;
}
.select2-container--default .select2-selection--single .select2-selection__placeholder {
  color: var(--pos-text-muted) !important;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
  position: absolute;
  right: 8px;
  top: 0;
  height: 100% !important;
  display: flex;
  align-items: center;
}

/* Inside an input-group the control must share the row, and only the outer
   corners stay rounded. */
.input-group > .select2-container {
  flex: 1 1 auto;
  width: 1% !important;
}
.input-group > .select2-container .select2-selection--single {
  border-top-left-radius: 0 !important;
  border-bottom-left-radius: 0 !important;
}
.input-group > .input-group-text {
  display: flex;
  align-items: center;
  min-height: var(--pos-control-h);
}

/* The dropdown must clear tables, sticky headers and cards beneath it. */
.select2-container--open { z-index: 20000 !important; }
.select2-dropdown {
  z-index: 20001 !important;
  margin-top: 2px;                 /* no negative pull-up hack */
  border: 1px solid var(--pos-border) !important;
  border-radius: var(--pos-radius-md) !important;
  box-shadow: var(--pos-shadow-md);
  background: var(--pos-surface);
  overflow: hidden;
}
.select2-search--dropdown { padding: 8px !important; }
.select2-search--dropdown .select2-search__field {
  min-height: 36px;
  padding: 6px 10px !important;
  border: 1px solid var(--pos-border) !important;
  border-radius: var(--pos-radius-sm) !important;
  font-size: 13.5px;
}
.select2-results__options { max-height: 260px !important; }
.select2-results__message {
  padding: 10px 12px;
  color: var(--pos-text-muted);
  font-size: 12.5px;
}

/* Dark mode: the dropdown is appended to <body>, outside any themed card. */
:root[data-theme="dark"] .select2-dropdown,
:root[data-theme="dark"] .select2-container--default .select2-selection--single {
  background: var(--pos-surface) !important;
  border-color: var(--pos-border) !important;
}


/* ==========================================================================
   42  SELECT2 INSIDE AN INPUT-GROUP  (correction)
   --------------------------------------------------------------------------
   Section 41 forced every select2 container to width:100% !important so it
   would behave like a .form-control. That is right on its own, but inside a
   Bootstrap .input-group the container is a FLEX ITEM - a 100% basis makes
   it claim the whole row, so the leading icon wrapped onto the line above
   it. That is the Supplier field sitting under its own button.

   The blanket rule is now scoped to containers that are NOT in an
   input-group, and the input-group case gets its own explicit sizing.
   ========================================================================== */

.input-group > span.select2.select2-container,
.input-group > span.select2.select2-container.select2-container--default {
  flex: 1 1 auto !important;
  width: 1% !important;          /* flex-basis floor, Bootstrap's own idiom */
  min-width: 0 !important;
  display: block;
}
/* Only the outer corners stay round, so it reads as one joined control. */
.input-group > .select2-container .select2-selection--single {
  border-top-left-radius: 0 !important;
  border-bottom-left-radius: 0 !important;
  border-left: 0 !important;
}
.input-group > .input-group-text {
  border-right: 0;
  background: var(--pos-surface-alt);
  color: var(--pos-text-secondary);
}

/* --------------------------------------------------------------------------
   Purchase line: the unit label (Ps, Nos, Kg ...) beside the quantity box.
   It is a bare <p class="p-2"> whose colour came from whatever it inherited,
   which left it near-invisible against the row. Make it a legible unit chip.
   -------------------------------------------------------------------------- */
#table td .p-2,
.delivery-table td .p-2,
td > p.p-2 {
  display: inline-flex;
  align-items: center;
  height: 34px;
  margin: 0 0 0 6px !important;
  padding: 0 10px !important;
  border-radius: var(--pos-radius-sm);
  background: var(--pos-surface-hover);
  color: var(--pos-text-primary) !important;
  font-size: 12.5px;
  font-weight: 600;
  white-space: nowrap;
}

/* The quantity input it sits next to. */
td input.in-quantity {
  width: auto !important;
  max-width: 110px;
  float: none !important;
  display: inline-block;
  text-align: right;
  font-variant-numeric: tabular-nums;
}


/* ==========================================================================
   43  SELECT2 PLACEHOLDER / VALUE - vertical centring
   --------------------------------------------------------------------------
   select2 nests its text two levels below the box it draws:

       span.select2-container
         span.selection                     <-- inline by default, no height
           span.select2-selection--single   <-- the visible bordered box
             span.select2-selection__rendered
               span.select2-selection__placeholder

   Only the bordered box was being flex-centred, and the wrapper above it is
   an inline span with no height, so the text hung near the top of the
   control instead of sitting on its middle line. Every level in that chain
   now carries its height through, and the text is centred on the last one
   rather than relying on a line-height that has to be guessed.
   ========================================================================== */

.select2-container,
.select2-container .selection {
  display: block !important;
  width: 100%;
}
.select2-container .selection { height: 100%; }

.select2-container--default .select2-selection--single {
  position: relative;            /* the arrow is absolutely positioned */
  box-sizing: border-box;
  overflow: hidden;
}

/* The text layer fills the box and centres on its own axis. */
.select2-container--default .select2-selection--single .select2-selection__rendered {
  display: flex !important;
  align-items: center;
  height: 100% !important;
  line-height: normal !important;   /* stop select2's fixed 28px */
  margin: 0 !important;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.select2-container--default .select2-selection--single .select2-selection__placeholder,
.select2-container--default .select2-selection--single .select2-selection__rendered > * {
  line-height: normal !important;
  display: inline-block;
  vertical-align: middle;
}
.select2-container--default .select2-selection--single .select2-selection__placeholder {
  color: var(--pos-text-muted) !important;
  font-weight: 400;
}

/* Clear ("x") sits on the same middle line instead of pushing the text. */
.select2-container--default .select2-selection--single .select2-selection__clear {
  margin: 0 6px 0 0;
  line-height: normal;
  color: var(--pos-text-muted);
}

/* Multi-select follows the same rule so the two never drift apart. */
.select2-container--default .select2-selection--multiple {
  min-height: var(--pos-control-h);
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px;
  padding: 4px 8px !important;
  border: 1px solid var(--pos-border) !important;
  border-radius: var(--pos-radius-sm) !important;
}

/* Room service already posted to the guest folio: visible for the kitchen
   and front desk, but never collectable at the till. */
.order-card .oc-folio {
  background: var(--pos-info-soft);
  color: var(--pos-info);
  font-weight: 700;
}
.order-card.is-on-folio { border-left-color: var(--pos-info); }
.order-card.is-on-folio.active { box-shadow: 0 0 0 1px rgba(var(--pos-primary-rgb), .25); }


/* ==========================================================================
   44  POS ACTION TRAY - paired buttons
   --------------------------------------------------------------------------
   Room Service and R/Checkout sit two-up inside a ~320px panel, so each gets
   roughly 150px. With an icon plus a 12.5px label the text hit the ellipsis
   rule and rendered as "Room Serv..." - which is the buttons "not showing
   properly". They now get their own sizing: a smaller icon, a tighter gap,
   and permission to wrap to a second line rather than truncate.
   ========================================================================== */

.pos-order-actions > .row > [class*="col-"] { padding-left: 2px; padding-right: 2px; }

.pos-order-actions > .row .btn {
  min-height: 40px;
  padding: 4px 6px !important;
  font-size: 11.5px !important;
  line-height: 1.2;
  gap: 4px;
  /* Two short lines beat one truncated one on a control this narrow. */
  white-space: normal !important;
  overflow: visible !important;
  text-overflow: clip !important;
  text-align: center;
  hyphens: none;
}
.pos-order-actions > .row .btn i {
  font-size: 14px;
  flex: 0 0 auto;
}

/* At the tightest terminal width drop the icons rather than the words -
   the label is what the operator reads. */
@media (max-width: 1400px) {
  .pos-order-actions > .row .btn { font-size: 11px !important; }
  .pos-order-actions > .row .btn i { display: none; }
}


/* ==========================================================================
   45  DATA-LIST TABLES - scroll frame, stock state, responsive columns
   --------------------------------------------------------------------------
   Three problems this section solves.

   1. `.overflow-scrolls` is the wrapper 34 admin list pages put around their
      table, and it was never defined in any stylesheet. With no rules on it
      the element did nothing, so a wide table simply overflowed its card on
      any screen narrower than the table - the reason these lists were not
      usable on a tablet or phone. It is aliased onto the same frame the
      hotel/report tables already use rather than given a second look.

   2. Data lists need to say what the stock LEVEL means, not just print a
      number. The pill component already exists for Active/Inactive, so the
      stock states extend it instead of introducing a parallel widget.

   3. A 14-column table cannot show every column on a 390px phone. Columns
      are tagged with a priority and drop out from the least important up,
      so the small screen keeps identity + quantity + state + action and the
      rest stay reachable by scrolling sideways.
   ========================================================================== */

/* ---- 1. the missing scroll frame ------------------------------------- */
.main-app .overflow-scrolls {
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-md);
  overflow: auto;
  background: var(--pos-surface);
  max-height: 68vh;
  -webkit-overflow-scrolling: touch;      /* momentum scroll on iOS */
}
.main-app .overflow-scrolls > .table { margin-bottom: 0; }
.main-app .overflow-scrolls > .table > thead > tr > th {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--pos-surface-alt) !important;
}

/* ---- 2. stock state pills -------------------------------------------- */
/* Negative stock is not "low", it is a data fault: more has been consumed
   than was ever received. It gets its own state so it cannot be read as a
   merely small number. */
.status-pill--warn {
  background: var(--pos-warning-soft);
  color: var(--pos-warning);
  border-color: rgba(217, 119, 6, .22);
}
.status-pill--danger {
  background: var(--pos-danger-soft);
  color: var(--pos-danger);
  border-color: rgba(220, 38, 38, .22);
}

/* Quantity cell: number and unit on one line, number aligned on the
   decimal so a column of stock levels can be compared by eye. */
.qty-cell { white-space: nowrap; }
.qty-cell .qty-val {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--pos-text-primary);
}
.qty-cell .qty-unit {
  color: var(--pos-text-secondary);
  font-size: 11.5px;
  margin-left: 4px;
}
.qty-cell.is-low   .qty-val { color: var(--pos-warning); }
.qty-cell.is-out   .qty-val { color: var(--pos-text-secondary); }
.qty-cell.is-neg   .qty-val { color: var(--pos-danger); }

/* ---- 3. responsive column priorities --------------------------------- */
/* p1 always shows. p2 drops first, then p3, then p4 - so the narrowest
   screen keeps only p1. Applied to BOTH th and td by column class. */
@media (max-width: 1399.98px) { .main-app .col-p4 { display: none; } }
@media (max-width: 1199.98px) { .main-app .col-p3 { display: none; } }
@media (max-width:  767.98px) { .main-app .col-p2 { display: none; } }

/* On a phone the frame should not also eat vertical space. */
@media (max-width: 767.98px) {
  .main-app .overflow-scrolls,
  .main-app .table-responsive { max-height: none; }
  .main-app .overflow-scrolls > .table > thead > tr > th,
  .main-app .table-responsive > .table > thead > tr > th { position: static; }
}

/* Page header + action button stack instead of colliding on small screens. */
@media (max-width: 575.98px) {
  .list-toolbar { flex-direction: column; align-items: stretch; gap: 8px; }
  .list-toolbar .btn { width: 100%; }
}


/* ==========================================================================
   46  SUMMARY TILES (non-hotel pages)
   --------------------------------------------------------------------------
   The hotel screens get their .hms-stat tiles from Assets/css/hms.css, which
   is loaded ONLY by View/hms/hms-top.php. Any page built on the standard
   layout that reused those class names rendered them completely unstyled -
   three lines of naked text where a row of tiles was intended.

   This is the same component expressed in the stylesheet every page already
   loads, so a summary strip works outside the hotel module too.
   ========================================================================== */

.sum-stats {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(215px, 1fr));
  gap: 12px;
  margin-bottom: 16px;
  align-items: stretch;
}

.sum-stat {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 13px 16px;
  background: var(--pos-surface);
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-lg);
  box-shadow: var(--pos-shadow-sm);
  min-width: 0;                       /* let long values wrap, not overflow */
}
.sum-stat .lbl {
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--pos-text-secondary);
}
.sum-stat .val {
  font-size: 21px;
  font-weight: 700;
  line-height: 1.2;
  color: var(--pos-text-primary);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
.sum-stat .sub {
  font-size: 11.5px;
  color: var(--pos-text-secondary);
}

/* Money still owed reads as something to act on, not a neutral figure. */
.sum-stat.is-warn { border-color: rgba(217, 119, 6, .35); }
.sum-stat.is-warn .val { color: var(--pos-warning); }
.sum-stat.is-good .val { color: var(--pos-success); }

@media (max-width: 575.98px) {
  .sum-stats { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; }
  .sum-stat .val { font-size: 18px; }
}


/* ==========================================================================
   47  SIDEBAR - PHONE AND TABLET
   --------------------------------------------------------------------------
   Every sidebar rule in this stylesheet was scoped to >=992px, on the
   assumption that the base template already handled smaller screens. It does
   not, because its off-canvas maths is hard-coded to its own 290px rail while
   this design system sets the rail to 264px:

     .sidebar            { left: -290px }
     .sidebar-show .main { transform: translateX(290px) }

   So on a tablet the content was pushed 290px while the drawer was only
   264px wide, leaving a 26px dead strip down the side. Worse, PUSHING is the
   wrong model on a phone at all: on a 360px viewport a 290px shove leaves
   70px of page, so opening the menu effectively threw the screen away.

   Below 992px the rail becomes a proper overlay drawer: it sits above the
   page, the backdrop dims what is behind it, and nothing is displaced.
   ========================================================================== */

@media (max-width: 991.98px) {

  /* Never wider than the screen. 86vw keeps a strip of the dimmed page in
     view so it reads as a dismissable overlay, and the cap stops the drawer
     becoming a slab on a large tablet. Everything below derives from this
     one value, so the drawer and its parked position cannot drift apart. */
  :root { --pos-sidebar-w: min(86vw, 300px); }

  /* Height and the single-scroller flex column are already set in section 25
     and are correct here; only the width and the parked position change. */
  .sidebar {
    width: var(--pos-sidebar-w) !important;
    left: calc(-1 * var(--pos-sidebar-w) - 8px);
    box-shadow: none;
  }

  body.sidebar-show .sidebar {
    left: 0;
    opacity: 1;
    visibility: visible;
    box-shadow: var(--pos-shadow-lg);
  }

  /* Overlay, never push. */
  body.sidebar-show .main,
  body.sidebar-show .header-main,
  body.sidebar-show .main-mobile-header {
    transform: none !important;
  }

  /* The shell reserves no space for a rail that is off-canvas by default. */
  .main        { margin-left: 0 !important; }
  .header-main { left: 0 !important; }

  /* --- desktop-only states must not leak in -----------------------------
     sidebar-hide and sidebar-collapsed are persisted in localStorage from a
     desktop session. Carried onto a phone they would either pin the drawer
     shut or shrink it to the 70px icon rail with its labels stripped out. */
  body.sidebar-hide .sidebar,
  body.sidebar-collapsed .sidebar {
    width: var(--pos-sidebar-w) !important;
  }
  body.sidebar-hide.sidebar-show .sidebar,
  body.sidebar-collapsed.sidebar-show .sidebar { left: 0; }
  body.sidebar-hide .main,
  body.sidebar-collapsed .main        { margin-left: 0 !important; }
  body.sidebar-hide .header-main,
  body.sidebar-collapsed .header-main { left: 0 !important; }

  /* Labels always show on mobile - there is no icon-rail mode here. */
  body.sidebar-collapsed .nav-sidebar .nav-link span,
  body.sidebar-collapsed .nav-label,
  body.sidebar-collapsed .nav-section,
  body.sidebar-collapsed .sidebar-logo span,
  body.sidebar-collapsed .nav-link.has-sub::after,
  body.sidebar-collapsed .sidebar-footer-body { display: revert !important; }
  body.sidebar-collapsed .nav-sub { display: none !important; }   /* until opened */
  body.sidebar-collapsed .sidebar-brand-img--full { display: block; }
  body.sidebar-collapsed .sidebar-brand-img--mark { display: none; }
  body.sidebar-collapsed .sidebar-header { padding: 0 14px !important; }

  /* Collapsing the rail is a desktop affordance; on a drawer the control to
     offer is "close", which the backdrop and the hamburger already provide. */
  #sidebarCollapseBtn { display: none !important; }

  /* --- touch sizing ------------------------------------------------------
     Pointer targets need real height. 12px of padding on a 13px label gives
     roughly 44px, the smallest reliably tappable target. */
  .nav-sidebar .nav-link {
    padding-top: 11px !important;
    padding-bottom: 11px !important;
  }
  .nav-sub-link,
  .nav-sub .nav-sub-link.dropdown-item {
    padding-top: 10px !important;
    padding-bottom: 10px !important;
  }

  /* One scroller, with momentum. */
  .sidebar-body {
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;      /* stop the page scrolling underneath */
  }

  /* The backdrop must sit under the drawer but over the fixed header. */
  .main-backdrop { z-index: 900; }
}

/* Very small phones: give the drawer the width it needs, and stop the
   header crowding it. */
@media (max-width: 575.98px) {
  :root { --pos-sidebar-w: min(88vw, 290px); }
}


/* ==========================================================================
   48  POS TABLE PICKER
   --------------------------------------------------------------------------
   The picker inside the POS reused none of the Tables screen's design: it
   drew Bootstrap .card tiles with inline `background: var(--pos-primary)
   !important` for a taken table, white text punched over a half-width PNG
   with `margin-top: -25px`, and no indication of WHY a table could not be
   chosen. It now renders the same .tbl-card as the Tables screen, so a table
   looks like a table wherever it appears.
   ========================================================================== */

.tbl-picker {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(178px, 1fr));
  gap: 12px;
  padding: 2px;
  max-height: 62vh;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* The card is a real <button> now, so the browser gives it keyboard focus
   and Enter/Space for free. These reset the button chrome it brings. */
.tbl-pick {
  appearance: none;
  -webkit-appearance: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
  width: 100%;
}

.tbl-pick__meta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--pos-text-secondary);
}
.tbl-pick__meta i { font-size: 14px; opacity: .75; }

/* Chosen state: a ring plus a tick, never a saturated fill - the status rail
   still has to be readable while the table is selected. */
.tbl-pick__tick {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  display: none;
  align-items: center;
  justify-content: center;
  background: var(--pos-primary);
  color: #fff;
  font-size: 12px;
  line-height: 1;
}
.tbl-pick.is-picked {
  border-color: var(--pos-primary);
  box-shadow: 0 0 0 2px var(--pos-primary-soft), var(--pos-shadow-sm);
}
.tbl-pick.is-picked .tbl-pick__tick { display: inline-flex; }

.tbl-pick:focus-visible {
  outline: 2px solid var(--pos-primary);
  outline-offset: 2px;
}

/* Not free: say so before the click, rather than only in an alert after it.
   Still focusable and still clickable, so the reason can be announced. */
.tbl-pick.is-locked {
  cursor: not-allowed;
  opacity: .62;
}
.tbl-pick.is-locked:hover {
  transform: none;
  box-shadow: var(--pos-shadow-sm);
}

@media (max-width: 575.98px) {
  .tbl-picker {
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: 9px;
    max-height: 58vh;
  }
}

/* Shared scroll body for the POS pickers (rooms, delivery partner). The
   markup previously used a Bootstrap .row with an inline height:370px, which
   put a second scroller around a list that already scrolled and applied
   negative gutters the cards inside were not built for. */
.pos-picker-body {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  max-height: 62vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 2px;
}
.pos-picker-body > a,
.pos-picker-body > .card { flex: 1 1 200px; min-width: 0; }

@media (max-width: 575.98px) {
  .pos-picker-body { max-height: 58vh; gap: 9px; }
  .pos-picker-body > a,
  .pos-picker-body > .card { flex: 1 1 100%; }
}


/* ==========================================================================
   49  ORDER CONTEXT STRIP
   --------------------------------------------------------------------------
   States what the order in progress is actually set to. Choosing a table
   previously changed only hidden inputs, so the operator had no way to see
   which table - or any - had been chosen, and no way to undo it.
   ========================================================================== */

.pos-context {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
  min-height: 30px;                   /* reserve the row so nothing jumps */
}

.pos-context__item {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  border-radius: var(--pos-radius-pill);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.4;
  white-space: nowrap;
  background: var(--pos-surface-alt);
  border: 1px solid var(--pos-border);
  color: var(--pos-text-secondary);
}
.pos-context__item i { font-size: 13px; opacity: .8; }

/* The table is the one an operator most needs to spot, so it carries the
   accent while the order type stays quiet beside it. */
.pos-context__item.is-table {
  background: var(--pos-primary-soft);
  border-color: rgba(var(--pos-primary-rgb), .35);
  color: var(--pos-primary);
}
.pos-context__item.is-room     { background: var(--pos-info-soft);    color: var(--pos-info);    border-color: rgba(8, 145, 178, .28); }
.pos-context__item.is-delivery { background: var(--pos-warning-soft); color: var(--pos-warning); border-color: rgba(217, 119, 6, .28); }

.pos-context__clear {
  appearance: none;
  -webkit-appearance: none;
  border: 0;
  background: transparent;
  padding: 0;
  margin-left: 2px;
  line-height: 1;
  color: inherit;
  opacity: .65;
  cursor: pointer;
  font-size: 14px;
}
.pos-context__clear:hover { opacity: 1; }

/* Dine-in with no table is valid (counter service), so this reads as an
   invitation rather than a warning. */
.pos-context__hint {
  appearance: none;
  -webkit-appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  border-radius: var(--pos-radius-pill);
  border: 1px dashed var(--pos-border-strong);
  background: transparent;
  color: var(--pos-text-secondary);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.pos-context__hint:hover {
  border-color: var(--pos-primary);
  color: var(--pos-primary);
}

/* A table being attached already turns this button on via .active, so the
   marker only needs to say "and something is held here" - an extra ring on
   top of the filled state was just noise. */
#order-table.has-selection {
  position: relative;
}
#order-table.has-selection::after {
  content: "";
  position: absolute;
  top: 5px;
  right: 6px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .12);
}


/* ==========================================================================
   50  CATEGORY STRIP - scroll affordance
   --------------------------------------------------------------------------
   The strip scrolls horizontally, but section 25 hides its scrollbar
   (`scrollbar-width: none`) to avoid a permanent grey bar under the chips.
   That left NO indication that more categories existed: the last chip was
   simply cut off mid-word at the panel edge, and with a mouse there was no
   obvious way to reach the rest.

   Hiding the bar is right; hiding the fact that it scrolls is not. The strip
   is wrapped so the overflow can be shown as a fading edge plus a stepper
   button on whichever side still has chips to reveal.
   ========================================================================== */

/* A flex row, so the steppers sit BESIDE the chips rather than on top of
   them. Overlaying covered the very label the button was meant to reveal,
   and centring against the wrapper put them a few pixels high because the
   strip carries bottom padding that pulls its optical centre up. */
.cat-wrap {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: 0 0 auto;
  width: 100%;
  min-width: 0;
}

/* The scroll window. Fades belong here, over the chips only - on the wrapper
   they would also wash over the buttons. */
.cat-track {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;                       /* or the strip refuses to shrink */
}

.cat-track::before,
.cat-track::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 8px;                        /* clear of the strip's own bottom pad */
  width: 28px;
  pointer-events: none;               /* never eat a click meant for a chip */
  opacity: 0;
  transition: opacity var(--pos-t);
  z-index: 2;
}
.cat-track::before {
  left: 0;
  background: linear-gradient(to right, var(--pos-surface) 15%, transparent);
}
.cat-track::after {
  right: 0;
  background: linear-gradient(to left, var(--pos-surface) 15%, transparent);
}
.cat-wrap.can-prev .cat-track::before,
.cat-wrap.can-next .cat-track::after { opacity: 1; }

/* The strip already reserves 8px at the bottom for a scrollbar that is
   deliberately hidden; inside the track that gap is dead space which throws
   the buttons out of line with the chips. */
.cat-wrap .categories { padding-bottom: 2px !important; margin-bottom: 0 !important; }

.cat-step {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  display: none;                      /* only when there is more to reveal */
  align-items: center;
  justify-content: center;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--pos-border);
  background: var(--pos-surface);
  color: var(--pos-text-secondary);
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
  transition: border-color var(--pos-t), color var(--pos-t), background var(--pos-t);
}
.cat-step:hover {
  border-color: var(--pos-primary);
  color: var(--pos-primary);
  background: var(--pos-primary-soft);
}
.cat-step:focus-visible { outline: 2px solid var(--pos-primary); outline-offset: 2px; }
.cat-wrap.can-prev .cat-step--prev,
.cat-wrap.can-next .cat-step--next { display: inline-flex; }

/* A touch screen scrolls by swiping and does not need the buttons; the
   fades still say there is more. */
@media (hover: none) and (pointer: coarse) {
  .cat-wrap .cat-step { display: none !important; }
}

/* Order-number / table-number line in the Current Order panel header. These
   were three fixed col-md-4 thirds carrying a heading and two slots; with the
   heading removed a flex row keeps the two slots together and lets the header
   collapse to nothing when neither is set (a new order has no number yet). */
.pos-order-meta {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  min-width: 0;
}
.pos-order-meta h6 {
  margin: 0;
  font-size: 13px;
  font-weight: 700;
  color: var(--pos-text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* :empty so an unset slot contributes no gap at all. */
.pos-order-meta h6:empty { display: none; }

/* Product description. Had no rule of its own, so it inherited the card
   anchor's link colour and read louder than the item name above it. */
.prd-desc {
  font-size: 10.5px !important;
  font-weight: 400 !important;
  line-height: 1.3 !important;
  color: var(--pos-text-secondary) !important;
  margin: 0 !important;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Room Service / R/Checkout. These navigate out of the POS to the hotel
   screens, so they are deliberately quieter than the order actions beside
   them - an operator must never mistake one for Complete Bill. The hotel
   accent lives on the icon only. */
.pos-order-actions .btn.pos-tray-link,
.btn.pos-tray-link {
  background: var(--pos-surface) !important;
  border: 1px solid var(--pos-border-strong) !important;
  color: var(--pos-text-secondary) !important;
  font-weight: 600 !important;
  text-decoration: none !important;
}
.pos-order-actions .btn.pos-tray-link i,
.btn.pos-tray-link i { color: var(--pos-info); }

.pos-order-actions .btn.pos-tray-link:hover,
.btn.pos-tray-link:hover {
  background: var(--pos-info-soft) !important;
  border-color: var(--pos-info) !important;
  color: var(--pos-info) !important;
}
.pos-order-actions .btn.pos-tray-link:hover i,
.btn.pos-tray-link:hover i { color: var(--pos-info); }

.btn.pos-tray-link:focus-visible {
  outline: 2px solid var(--pos-info);
  outline-offset: 2px;
}

/* Product with no photo. Drawn, not downloaded: the previous fallback was a
   2000x2000 JPEG fetched and decoded once per card - 47 times on this grid.
   Shares .card-img-top so it occupies exactly the same box as a real image
   and the rows stay aligned. */
.prd-noimg {
  display: flex !important;
  align-items: center;
  justify-content: center;
  width: 100%;
  aspect-ratio: 4 / 3;
  flex: 0 0 auto;
  background: var(--pos-surface-hover);
  color: var(--pos-text-muted);
  border-radius: 0;
}
.prd-noimg i { font-size: 30px; opacity: .55; }

/* ==========================================================================
   51  PERMISSION MATRIX
   --------------------------------------------------------------------------
   Forty permissions as one flat list is unreadable, so they are grouped by
   module in columns that reflow with the viewport. Each group heads with a
   tri-state checkbox: ticked, clear, or indeterminate when only part of the
   module is granted - so a partly-granted module is never misread as none.
   ========================================================================== */
.perm-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 14px;
}
.perm-group {
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-md);
  padding: 10px 12px 12px;
  background: var(--pos-surface);
  min-width: 0;
}
.perm-group__head {
  padding-bottom: 8px;
  margin-bottom: 6px;
  border-bottom: 1px solid var(--pos-border);
}
.perm-mod {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--pos-text-primary);
  cursor: pointer;
}
.perm-item {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0;
  padding: 5px 0;
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--pos-text-secondary);
  cursor: pointer;
}
.perm-item input { margin-top: 2px; flex: 0 0 auto; }
.perm-item:hover { color: var(--pos-text-primary); }
/* The key is what a developer greps for; the sentence is what an operator
   reads. Both, with the key subordinate. */
.perm-item code {
  display: block;
  font-size: 10.5px;
  color: var(--pos-text-muted);
  background: none;
  padding: 0;
}
.perm-item input:disabled + span { opacity: .6; }

@media (max-width: 575.98px) {
  .perm-grid { grid-template-columns: 1fr; gap: 10px; }
}

/* ==========================================================================
   52  LICENCE USAGE METERS
   --------------------------------------------------------------------------
   How much of each package limit is spent. A bar plus the raw numbers: the
   bar is scanned, the numbers are what an operator quotes when they call to
   upgrade, so both are shown rather than one or the other.
   ========================================================================== */
.lic-usage {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 14px;
}
.lic-meter { min-width: 0; }
.lic-meter__top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 5px;
}
.lic-meter__name {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--pos-text-primary);
}
.lic-meter__num {
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--pos-text-secondary);
  white-space: nowrap;
}
.lic-meter__bar {
  height: 7px;
  border-radius: var(--pos-radius-pill);
  background: var(--pos-surface-alt);
  overflow: hidden;
}
.lic-meter__bar > span {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--pos-success);
  transition: width var(--pos-t);
}
/* Approaching the ceiling is worth noticing before it is hit. */
.lic-meter.is-near .lic-meter__bar > span { background: var(--pos-warning); }
.lic-meter.is-full .lic-meter__bar > span { background: var(--pos-danger); }
.lic-meter.is-full .lic-meter__num { color: var(--pos-danger); font-weight: 700; }
.lic-meter__note {
  margin-top: 4px;
  font-size: 11px;
  color: var(--pos-text-secondary);
}
.lic-meter.is-full .lic-meter__note { color: var(--pos-danger); }

.lic-current { background: var(--pos-primary-soft); }

@media (max-width: 575.98px) {
  .lic-usage { grid-template-columns: 1fr; gap: 10px; }
}

/* Licence banner. Full width under the fixed header, so it is impossible to
   miss without stealing space from the workspace on a normal day - it is only
   rendered at all when there is something to say. */
.lic-banner {
  position: fixed;
  top: var(--pos-header-h);
  left: 0;
  right: 0;
  z-index: 799;                       /* under the header (800), over content */
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 16px;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.4;
}
.lic-banner i { font-size: 15px; flex: 0 0 auto; }
.lic-banner a {
  margin-left: auto;
  flex: 0 0 auto;
  color: inherit;
  text-decoration: underline;
  font-weight: 700;
  white-space: nowrap;
}
.lic-banner.is-expired  { background: var(--pos-danger);  color: #fff; }
.lic-banner.is-expiring { background: var(--pos-warning); color: #fff; }

/* Push the workspace down by exactly the banner's height so it never covers
   the first row of content. */
body:has(.lic-banner) .main-app { padding-top: 38px !important; }
body:has(.lic-banner) .sidebar  { padding-top: 0; }

@media (min-width: 992px) {
  .lic-banner { left: var(--pos-sidebar-w); }
  body.sidebar-hide .lic-banner { left: 0; }
  body.sidebar-collapsed .lic-banner { left: var(--pos-sidebar-w-collapsed); }
}
@media print { .lic-banner { display: none; } }

/* Permissions reserved for the built-in Super Admin. Shown rather than
   hidden - an administrator should be able to see the whole surface and
   understand why part of it is not theirs to hand out. */
.perm-item.is-system { opacity: .72; cursor: not-allowed; }
.perm-item.is-system input { cursor: not-allowed; }
.perm-sys {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: var(--pos-radius-pill);
  background: var(--pos-warning-soft);
  color: var(--pos-warning);
  font-size: 10px;
  font-weight: 700;
  font-style: normal;
  white-space: nowrap;
}
/* Core modules in the package editor read the same way. */
.perm-item.mod-pick.is-core { opacity: .78; }
.mod-core {
  display: inline-block;
  margin-left: 6px;
  font-size: 10px;
  font-style: normal;
  font-weight: 700;
  color: var(--pos-text-muted);
}

/* ==========================================================================
   53  TILL LAYOUT WHEN A PANEL IS ABSENT
   ==========================================================================

   #order-form > .row is a CSS grid with explicitly sized tracks, one per
   panel. The Bootstrap col-md-* classes on the children are inert inside it.

   That is fine while all three panels exist, but the running-orders rail is a
   module: a package without it renders two children against three tracks. CSS
   grid fills tracks in order, so the products panel took the RAIL's narrow
   track (288px at 1400px wide) and the third track - sized for checkout -
   stood empty. On screen: a cramped product list beside a wide order panel
   with a dead strip down the right. Nothing about the panels was wrong; the
   grid was still drawing a column for a panel that was not there.

   The row now declares how many panels it has, and each breakpoint gets a
   two-track version. Products keeps the slack (1fr) in both, so the product
   grid gains the width the rail gave up - which is what makes the cards fill
   the row the way a catalogue should.
   ========================================================================== */

body.pos-app #order-form > .row.pos-cols-2 {
  grid-template-columns:
      minmax(420px, 1fr)        /* products - takes all the slack */
      minmax(480px, 560px);     /* current order / checkout */
}

@media (max-width: 1500px) {
  body.pos-app #order-form > .row.pos-cols-2 {
    grid-template-columns: minmax(360px, 1fr) minmax(430px, 500px);
  }
}

@media (max-width: 1400px) {
  body.pos-app #order-form > .row.pos-cols-2 {
    grid-template-columns: minmax(300px, 1fr) minmax(390px, 440px);
  }
}

@media (max-width: 1280px) and (min-width: 992px) {
  body.pos-app #order-form > .row.pos-cols-2 {
    grid-template-columns: minmax(260px, 1fr) minmax(376px, 430px);
  }
}

/* Below 992px every layout stacks to one column - already handled, but state
   it for this variant too so a future edit to the shared rule cannot orphan
   it. */
@media (max-width: 991.98px) {
  body.pos-app #order-form > .row.pos-cols-2 { grid-template-columns: minmax(0, 1fr); }
}

/* With the extra width the product grid should behave like a catalogue rather
   than a list: more, smaller cards per row. The tracks below are minimums -
   auto-fill decides the count from whatever width the panel actually gets. */
body.pos-app #order-form > .row.pos-cols-2 #product {
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
}

/* ==========================================================================
   54  PRODUCT CARDS AS A CATALOGUE
   ==========================================================================

   The grid was drawn for a narrow rail-flanked column: few large cards, a
   4:3 image block, and a metadata line under every name. Given the width the
   missing rail gives back, that reads as a sparse list rather than something
   to pick from quickly - most of each card was an empty picture area.

   Denser tracks, a shorter image block, and the price given the visual weight
   it earns. Nothing here changes behaviour; the whole card is still one hit
   target and the class names JS binds to are untouched.
   ========================================================================== */

/* More, smaller cards. auto-fill still decides the count from real width, so
   this narrows the card rather than fixing how many appear. */
#product {
  grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
  gap: 8px;
}

/* A 4:3 block on a 132px card is 99px of mostly-empty space. 5:4 keeps the
   photo readable while giving the name and price the room they need. */
.card-img-top,
#product .card img,
.prd-noimg {
  aspect-ratio: 5 / 4;
}

/* The placeholder should recede, not announce itself: a soft field with a
   small mark, so a card without a photo reads as "no picture" rather than as
   a broken image. */
.prd-noimg {
  background:
    linear-gradient(135deg,
      var(--pos-surface-hover) 0%,
      var(--pos-surface-alt) 100%);
}
.prd-noimg i { font-size: 22px; opacity: .38; }

/* Name, then price. The metadata line only appears when an item actually
   routes to a station, so the card is usually two lines of text. */
#product .card-body,
.add-to-cart .card-body { padding: 7px 8px 8px !important; gap: 2px; }

.prd-lable,
.card-title.prd-lable {
  font-size: 12px !important;
  line-height: 1.3 !important;
  /* Two lines, then ellipsis - a long name must not push the price out of
     the card and break the grid's rhythm. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.prd-meta {
  font-size: 10.5px !important;
  line-height: 1.2 !important;
  margin: 0 !important;
  opacity: .7;
}

/* The price is what the eye looks for; give it the bottom of the card. */
.dine-p, .take-p {
  font-size: 13.5px !important;
  margin: auto 0 0 !important;
  padding-top: 2px;
}

@media (max-width: 1280px) {
  #product { grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); gap: 7px; }
  .prd-lable, .card-title.prd-lable { font-size: 11.5px !important; }
}


/* ==========================================================================
   55  COMPLETE BILL - PAYMENT LINES
   ==========================================================================
   One line per tender: method, amount, remove. This replaces a fixed box per
   configured method (most of them hidden) plus a separate cash-handling panel
   - four controls to describe a single payment, and hidden boxes that could
   go missing without anyone noticing.
   ========================================================================== */

.fs-pay-rows { display: flex; flex-direction: column; gap: 8px; }

.fs-pay-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* The method leads, because it is what the operator is choosing. */
.fs-pay-row .fs-pay-method {
  flex: 1 1 auto;
  min-width: 0;
  font-weight: 600;
}

/* The amount is the figure being checked: right-aligned, tabular, and wide
   enough for a real bill rather than a token field. */
.fs-pay-row .split-payment-input {
  flex: 0 0 130px;
  text-align: right;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.fs-pay-row .fs-row-remove {
  flex: 0 0 32px;
  height: 32px;
  border: 1px solid var(--pos-border);
  background: var(--pos-surface);
  color: var(--pos-text-muted);
  border-radius: 8px;
  line-height: 1;
  cursor: pointer;
  transition: color var(--pos-t), border-color var(--pos-t), background var(--pos-t);
}
.fs-pay-row .fs-row-remove:hover {
  color: #fff;
  background: var(--pos-danger);
  border-color: var(--pos-danger);
}

/* Keeps the first line's amount aligned with the ones below it, which have a
   remove button. Without it the column jumps as soon as a payment is added. */
.fs-pay-row .fs-row-spacer { flex: 0 0 32px; }

#add-payment:disabled { opacity: .5; cursor: not-allowed; }

.fs-add-pay__err {
  font-size: 12px;
  font-weight: 600;
  color: var(--pos-danger);
}

@media (max-width: 640px) {
  .fs-pay-row .split-payment-input { flex: 0 0 104px; }
}


/* ==========================================================================
   43  THE HEADERLESS TILL - EDGE CONTROLS AND THE RIGHT DRAWER
   --------------------------------------------------------------------------
   The till used to spend ~62px of every screen on a header bar carrying
   things a cashier touches a few times a shift, while Running Orders - a
   dozen buttons, used constantly - fought for what was left. On a 1024x768
   till that trade is simply wrong, so the bar is gone.

   What replaces it costs no layout height at all: two floating tabs on the
   screen edges, and a drawer that slides OVER the workspace rather than
   displacing it.

   --pos-header-h is deliberately NOT zeroed here. The sidebar's own header
   band and its scroll height are measured from the same variable, so
   blanking it would collapse the rail's logo as a side effect. Only the
   workspace padding the bar used to occupy is given back.
   ========================================================================== */

:root { --pos-drawer-w: 320px; }

body.pos-app .main.main-app {
  padding: 10px 12px 12px !important;
}

/* Edge controls ---------------------------------------------------------- */

.pos-edge-btn {
  position: fixed;
  top: 10px;
  left: 10px;
  z-index: 1035;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  border: 1px solid var(--pos-border);
  background: var(--pos-surface);
  color: var(--pos-text-primary);
  font-size: 20px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .12);
  transition: background var(--pos-t), color var(--pos-t);
}
.pos-edge-btn:hover,
.pos-edge-btn:focus-visible {
  background: var(--pos-primary);
  border-color: var(--pos-primary);
  color: #fff;
}

/* The drawer handle. A tall thin tab rather than a round icon: it reads as
   a panel living behind the edge, which a floating circle does not. */
.pos-drawer-tab {
  position: fixed;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  z-index: 1035;
  width: 30px;
  min-height: 108px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 12px 0;
  border: 1px solid var(--pos-border);
  border-right: 0;
  border-radius: 10px 0 0 10px;
  background: var(--pos-surface);
  color: var(--pos-text-primary);
  box-shadow: -2px 0 10px rgba(0, 0, 0, .12);
  cursor: pointer;
  transition: background var(--pos-t), color var(--pos-t), right var(--pos-t);
}
.pos-drawer-tab:hover,
.pos-drawer-tab:focus-visible {
  background: var(--pos-primary);
  border-color: var(--pos-primary);
  color: #fff;
}
.pos-drawer-tab i {
  font-size: 18px;
  line-height: 1;
  transition: transform var(--pos-t);
}
.pos-drawer-tab__label {
  writing-mode: vertical-rl;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
}
/* The arrow points at what the click will do: in to open, out to close. */
body.pos-drawer-open .pos-drawer-tab { right: var(--pos-drawer-w); }
body.pos-drawer-open .pos-drawer-tab i { transform: rotate(180deg); }

/* The drawer -------------------------------------------------------------- */

.pos-drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: var(--pos-drawer-w);
  max-width: 92vw;
  z-index: 1040;
  display: flex;
  flex-direction: column;
  background: var(--pos-surface);
  border-left: 1px solid var(--pos-border);
  box-shadow: -8px 0 28px rgba(0, 0, 0, .18);
  transform: translateX(100%);
  transition: transform var(--pos-t), visibility var(--pos-t);
  /* A hidden panel must not be reachable by keyboard, or Tab walks into a
     drawer nobody can see. */
  visibility: hidden;
}
body.pos-drawer-open .pos-drawer {
  transform: translateX(0);
  visibility: visible;
}

.pos-drawer-backdrop {
  position: fixed;
  inset: 0;
  z-index: 1039;
  background: rgba(0, 0, 0, .35);
  opacity: 0;
  transition: opacity var(--pos-t);
  pointer-events: none;
}
body.pos-drawer-open .pos-drawer-backdrop {
  opacity: 1;
  pointer-events: auto;
}

.pos-drawer__head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px;
  border-bottom: 1px solid var(--pos-border);
}
.pos-drawer__id {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  flex: 1 1 auto;
}
.pos-drawer__id .avatar { flex: 0 0 auto; }
.pos-drawer__idtext { min-width: 0; }
.pos-drawer__idtext h6,
.pos-drawer__idtext small {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pos-drawer__close {
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  border: 1px solid var(--pos-border);
  border-radius: 8px;
  background: transparent;
  color: var(--pos-text-primary);
  font-size: 18px;
  line-height: 1;
}
.pos-drawer__close:hover {
  background: var(--pos-danger);
  border-color: var(--pos-danger);
  color: #fff;
}

.pos-drawer__body { flex: 1 1 auto; overflow-y: auto; padding: 14px; }
.pos-drawer__foot { flex: 0 0 auto; padding: 14px; border-top: 1px solid var(--pos-border); }

.pos-drawer__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 14px;
}
.pos-drawer__section { margin-bottom: 18px; }
.pos-drawer__label {
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--pos-text-secondary);
  margin-bottom: 7px;
}
.pos-drawer__search { display: flex; align-items: center; gap: 8px; }
.pos-drawer__search .pos-search-wrap { flex: 1 1 auto; min-width: 0; }

.pos-drawer__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}
.pos-drawer__btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 68px;
  padding: 10px 6px;
  border: 1px solid var(--pos-border);
  border-radius: 10px;
  background: var(--pos-surface);
  color: var(--pos-text-primary);
  font-size: 12px;
  font-weight: 600;
  text-align: center;
  line-height: 1.2;
}
.pos-drawer__btn i { font-size: 20px; line-height: 1; }
.pos-drawer__btn:hover,
.pos-drawer__btn:focus-visible {
  background: var(--pos-primary);
  border-color: var(--pos-primary);
  color: #fff;
}

/* Add Customer, beside the field it belongs to ---------------------------- */

.pos-cust-row { display: flex; align-items: stretch; gap: 6px; }
.pos-cust-row .form-select { flex: 1 1 auto; min-width: 0; }
.pos-cust-add {
  flex: 0 0 auto;
  width: var(--pos-control-h);
  min-height: var(--pos-control-h);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font-size: 17px;
}

/* Small screens and short tills ------------------------------------------ */

@media (max-width: 575.98px) {
  :root { --pos-drawer-w: 300px; }
  .pos-drawer-tab { min-height: 92px; width: 26px; }
  .pos-drawer-tab__label { display: none; }
  /* Full width on a phone: a 300px panel beside a 320px viewport is a slit. */
  .pos-drawer { width: 100%; max-width: 100%; }
  body.pos-drawer-open .pos-drawer-tab { right: 0; }
}

/* A 1024x768 till in landscape has very little vertical room, and the tab is
   centred on a viewport barely taller than the tab itself. */
@media (max-height: 560px) {
  .pos-drawer-tab { min-height: 84px; padding: 8px 0; }
  .pos-drawer__btn { min-height: 58px; }
  .pos-drawer__head,
  .pos-drawer__body,
  .pos-drawer__foot { padding: 10px; }
}

.pos-drawer,
.pos-drawer-tab,
.pos-edge-btn { -webkit-tap-highlight-color: transparent; }

/* None of this chrome belongs on a receipt. */
@media print {
  .pos-edge-btn,
  .pos-drawer-tab,
  .pos-drawer,
  .pos-drawer-backdrop { display: none !important; }
}


/* ==========================================================================
   44  SHELL - TOASTS, IN-PAGE DIALOGS, FULL-SCREEN RESUME
   --------------------------------------------------------------------------
   Native alert()/confirm() are not used on the till. A browser refuses to
   draw a system modal over full-screen content, so it leaves full screen to
   show one - which is why removing a cart item kept ending full-screen mode.
   These are the in-page replacements.
   ========================================================================== */

.pos-toast-host {
  position: fixed;
  top: 12px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 2000050;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: center;
  pointer-events: none;
  width: max-content;
  max-width: min(560px, 94vw);
}

.pos-toast {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid var(--pos-border);
  background: var(--pos-surface);
  color: var(--pos-text-primary);
  font-size: 13px;
  font-weight: 600;
  box-shadow: 0 8px 26px rgba(0, 0, 0, .18);
  opacity: 0;
  transform: translateY(-10px);
  transition: opacity .18s ease, transform .18s ease;
  max-width: 100%;
}
.pos-toast.is-in  { opacity: 1; transform: translateY(0); }
.pos-toast.is-out { opacity: 0; transform: translateY(-10px); }
.pos-toast > i { font-size: 18px; line-height: 1; flex: 0 0 auto; }
.pos-toast > span { min-width: 0; overflow-wrap: anywhere; }

.pos-toast--success { border-left: 4px solid var(--pos-success); }
.pos-toast--success > i { color: var(--pos-success); }
.pos-toast--error   { border-left: 4px solid var(--pos-danger); }
.pos-toast--error   > i { color: var(--pos-danger); }
.pos-toast--warning { border-left: 4px solid var(--pos-warning, #d98200); }
.pos-toast--warning > i { color: var(--pos-warning, #d98200); }
.pos-toast--info    { border-left: 4px solid var(--pos-primary); }
.pos-toast--info    > i { color: var(--pos-primary); }

.pos-toast__x {
  flex: 0 0 auto;
  border: 0;
  background: transparent;
  color: var(--pos-text-secondary);
  font-size: 16px;
  line-height: 1;
  padding: 0 2px;
}
.pos-toast__x:hover { color: var(--pos-text-primary); }

/* In-page confirm ---------------------------------------------------------- */

.pos-dialog-back {
  position: fixed;
  inset: 0;
  z-index: 2000060;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(0, 0, 0, .45);
  opacity: 0;
  transition: opacity .16s ease;
}
.pos-dialog-back.is-in { opacity: 1; }

.pos-dialog {
  width: min(430px, 94vw);
  border-radius: 12px;
  background: var(--pos-surface);
  border: 1px solid var(--pos-border);
  box-shadow: 0 18px 50px rgba(0, 0, 0, .35);
  transform: translateY(-8px) scale(.99);
  transition: transform .16s ease;
}
.pos-dialog-back.is-in .pos-dialog { transform: none; }

.pos-dialog__body {
  padding: 22px 22px 18px;
  font-size: 15px;
  line-height: 1.5;
  color: var(--pos-text-primary);
}
.pos-dialog__foot {
  display: flex;
  gap: 10px;
  justify-content: flex-end;
  padding: 0 18px 18px;
}
.pos-dialog__foot .btn { min-width: 104px; }

/* Full-screen resume ------------------------------------------------------- */

/* Full screen cannot be restored without a user gesture, so after a page
   navigation the next real tap takes it back. This says so, rather than
   leaving the operator wondering why the till came out of full screen. */
.pos-fs-resume {
  position: fixed;
  left: 50%;
  bottom: 18px;
  transform: translateX(-50%);
  z-index: 2000040;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 16px;
  border-radius: 999px;
  border: 1px solid var(--pos-border);
  background: var(--pos-surface);
  color: var(--pos-text-primary);
  font-size: 12.5px;
  font-weight: 700;
  box-shadow: 0 8px 26px rgba(0, 0, 0, .2);
}
.pos-fs-resume:hover {
  background: var(--pos-primary);
  border-color: var(--pos-primary);
  color: #fff;
}

@media print {
  .pos-toast-host,
  .pos-dialog-back,
  .pos-fs-resume { display: none !important; }
}


/* ==========================================================================
   45  RUNNING-ORDERS TRAY - ONE GRID, NO BOOTSTRAP SCAFFOLDING
   --------------------------------------------------------------------------
   .pos-order-actions is a two-column CSS grid. Some of its children still
   carried Bootstrap .row/.col-md-6, which sets width:50% - INSIDE a grid
   cell that is already 50%, so Split Bill and Merge Bill rendered at a
   quarter width while the rest of the row sat empty. Add to Room took a
   single column because full-width was declared as a hand-maintained list
   of ids that nobody updated when a button was added.

   The scaffolding is gone from the markup and the span is now declared by
   class, so a new button lands correctly without editing this file.
   ========================================================================== */

.pos-order-actions > .pos-act-full { grid-column: 1 / -1; }

/* Every child is a real control: identical height, no stray margins from
   the .mt-1 utilities the old rows relied on for spacing. */
.pos-order-actions > .btn,
.pos-order-actions > .pos-act-full {
  margin: 0 !important;
  width: 100%;
  min-height: var(--pos-control-h);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A disabled action must read as unavailable rather than merely pale -
   Add to Room is disabled until an order with a room is selected. */
.pos-order-actions > .btn:disabled,
.pos-order-actions > .btn.disabled { opacity: .5; cursor: not-allowed; }


/* ==========================================================================
   46  THE MENU BUTTON MUST NOT COVER THE PANEL IT SITS ON
   --------------------------------------------------------------------------
   #menuSidebar floats at the top-left of the workspace, which is exactly
   where the Running Orders card puts its title - so the button sat on top of
   the word "Running". Floating is still right (it costs no layout height);
   what was missing is that the panel has to leave room for it.
   ========================================================================== */

body.pos-app .ro-titlebar { padding-left: 46px; min-height: 40px; }

/* When the rail is not rendered, the products panel is first instead. */
/* !important is required, not decoration: .card-header sets the PADDING
   SHORTHAND with !important, which beats an unflagged padding-left however
   specific the selector is. Without it this rule was inert and the floating
   menu button sat on top of the Products title on any package without the
   running-orders rail. */
body.pos-app .row.pos-cols-2 > div:first-child .card-header { padding-left: 46px !important; }

@media (max-width: 575.98px) {
  .pos-edge-btn { width: 36px; height: 36px; font-size: 18px; top: 8px; left: 8px; }
  body.pos-app .ro-titlebar { padding-left: 40px; }
  body.pos-app .row.pos-cols-2 > div:first-child .card-header { padding-left: 40px !important; }
}


/* ==========================================================================
   47  ONE MENU CONTROL, NOT TWO
   --------------------------------------------------------------------------
   The rail carries its own collapse button (#sidebarCollapseBtn) in its
   header, and the floating #menuSidebar sits at the top-left of the
   workspace - which, while the rail is open, is directly beside it. Two
   controls, side by side, for the same menu.

   The floating one exists only to bring a hidden rail BACK, so it shows only
   when there is something to bring back. While the rail is open its own
   button is the one on screen.
   ========================================================================== */

@media (min-width: 992px) {
  /* Desktop: the rail is open unless .sidebar-hide is set. */
  body.pos-app:not(.sidebar-hide) .pos-edge-btn--menu { display: none; }
  /* With no floating button over it, the panel title needs no inset. */
  body.pos-app:not(.sidebar-hide) .ro-titlebar { padding-left: 0; }
}

/* Below 992px the rail is an overlay opened with .sidebar-show, and it has
   its own close control while open. */
body.pos-app.sidebar-show .pos-edge-btn--menu { display: none; }


/* ==========================================================================
   48  CHARGE TO ROOM SITS ON THE SAME RHYTHM AS ITS NEIGHBOURS
   ========================================================================== */

.pos-charge-room {
  min-height: var(--pos-control-h);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  white-space: nowrap;
}




/* ==========================================================================
   50  NO CONTROL IN THE TRAY IS EVER BARE
   --------------------------------------------------------------------------
   Every button in the running-orders tray was painted by its own id rule -
   #modify-order, #print-bill, #print-invoice, #View-order-f, #cancel-order-f
   and so on - so a control whose id was not on that list rendered as plain
   text with no border and no background. It happened to Room Service and
   R/Checkout before (fixed with .pos-tray-link) and then to Add to Room, for
   exactly the same reason: the list is maintained by hand and adding a button
   does not add it to the list.

   So the tray has a DEFAULT now. Anything dropped into it is a proper button
   immediately, and the id rules above are overrides for the few controls that
   need to look different. A new button can no longer arrive bare.
   ========================================================================== */

.pos-order-actions > .btn {
  background: var(--pos-surface);
  border: 1px solid var(--pos-border-strong);
  color: var(--pos-text-secondary);
  font-weight: 600;
}
.pos-order-actions > .btn:hover:not(:disabled) {
  background: var(--pos-surface-hover);
  border-color: var(--pos-primary);
  color: var(--pos-primary);
}

/* Add to Room settles a bill, like Complete Bill, but onto a folio rather
   than into the drawer - so it reads as an action in its own right without
   competing with the green one. */
#add-to-room {
  background: var(--pos-surface) !important;
  border: 1px solid var(--pos-primary) !important;
  color: var(--pos-primary) !important;
  font-weight: 700 !important;
}
#add-to-room:hover:not(:disabled) {
  background: var(--pos-primary) !important;
  border-color: var(--pos-primary) !important;
  color: #fff !important;
}
/* Disabled still has to look like a button, just an unavailable one - it was
   indistinguishable from a label. */
#add-to-room:disabled {
  background: var(--pos-surface) !important;
  border: 1px solid var(--pos-border) !important;
  color: var(--pos-text-secondary) !important;
  opacity: .55;
}

/* Split and Merge are declared without !important above, so the default rule
   that now precedes them would win on specificity ties. Restate them. */
#split-bill {
  background: var(--pos-info) !important;
  border: 1px solid var(--pos-info) !important;
  color: #fff !important;
}
#merge-bill {
  background: var(--pos-surface) !important;
  border: 1px solid var(--pos-border-strong) !important;
  color: var(--pos-text-secondary) !important;
}


/* ==========================================================================
   51  CHARGE TO ROOM SHARES THE CART'S BUTTON ROW
   --------------------------------------------------------------------------
   It sat in a row of its own, so it carried its own gutters and its own
   bottom margin and lined up with nothing. It is a column in the SAME row as
   Clear / Q-Invoice / Add-Order now, which is what makes the edges agree.
   ========================================================================== */

.pos-charge-room {
  min-height: var(--pos-control-h);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  white-space: nowrap;
  font-weight: 700;
}


/* ==========================================================================
   52  REPORTS HUB
   --------------------------------------------------------------------------
   Cards rather than a list: a report is chosen by what it answers, and a bare
   list of 23 names does not say what any of them answer.
   ========================================================================== */

.rep-section {
  margin: 22px 0 10px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--pos-text-secondary);
}
.rep-section:first-of-type { margin-top: 6px; }

.rep-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
  gap: 12px;
}

.rep-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px;
  border: 1px solid var(--pos-border);
  border-radius: var(--pos-radius-md, 10px);
  background: var(--pos-surface);
  color: var(--pos-text-primary);
  text-decoration: none;
  transition: border-color var(--pos-t), box-shadow var(--pos-t), transform var(--pos-t);
}
.rep-card:hover,
.rep-card:focus-visible {
  border-color: var(--pos-primary);
  box-shadow: 0 4px 16px rgba(0, 0, 0, .08);
  transform: translateY(-1px);
  color: var(--pos-text-primary);
}

.rep-card__icon {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  background: var(--pos-primary-soft, rgba(37, 99, 235, .1));
  color: var(--pos-primary);
  font-size: 20px;
}
.rep-card__body { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.rep-card__title { font-weight: 700; font-size: 14px; }
.rep-card__desc {
  font-size: 12px;
  color: var(--pos-text-secondary);
  line-height: 1.35;
  margin-top: 2px;
}
.rep-card__go { flex: 0 0 auto; color: var(--pos-text-secondary); font-size: 18px; }
.rep-card:hover .rep-card__go { color: var(--pos-primary); }

@media print { .rep-grid, .rep-section { display: none !important; } }


/* ==========================================================================
   53  BACK-OFFICE FLASH - FLOATING, SO IT CANNOT MISALIGN
   --------------------------------------------------------------------------
   Rendered from the shared header, which sits BEFORE .main - and .main carries
   a 72px top offset plus a sidebar margin-left that changes with the
   breakpoint and with whether the rail is collapsed. A block placed here
   inherits none of that, so it spanned the whole window, sat under the sidebar
   and lined up with nothing on the page.

   Matching those offsets would mean copying every breakpoint rule and keeping
   the copies in step forever. Floating removes the problem instead: a
   transient message does not need to occupy layout at all, and this is the
   same shape as the toast the till already uses.
   ========================================================================== */

.hms-flash {
  position: fixed;
  top: calc(var(--pos-header-h) + 12px);
  right: 16px;
  z-index: 1200;                 /* over content, under modals (2000000+) */
  width: min(460px, calc(100vw - 32px));
  pointer-events: none;          /* never blocks what is underneath */
}
.hms-flash .alert {
  pointer-events: auto;          /* but the message itself is dismissible */
  margin: 0;
  box-shadow: 0 8px 26px rgba(0, 0, 0, .18);
  border-radius: 10px;
  animation: hmsFlashIn .18s ease-out both;
}
@keyframes hmsFlashIn {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: none; }
}

/* A licence banner is already pinned directly under the header, so a flash
   arriving at the same moment would land on top of it. */
.lic-banner ~ .hms-flash,
body:has(.lic-banner) .hms-flash { top: calc(var(--pos-header-h) + 46px); }

@media (max-width: 575.98px) {
  .hms-flash { right: 8px; left: 8px; width: auto; }
}

@media print { .hms-flash { display: none !important; } }


/* ==========================================================================
   54  FLASH ITEM - A ROW, WITH A VISIBLE LIFETIME
   --------------------------------------------------------------------------
   The close control used to be an <a class="btn btn-close"> appended after the
   message text, so it was part of the flowing line: it wrapped onto its own
   line under a message of any length and sat there as a large stray cross.
   Icon, text and close are three flex children now and only the text grows.
   ========================================================================== */

/* CENTRED, not top-aligned.
 Pinning the icon to the first line is right for a long notice that wraps to
 four lines. These do not: they are one or two lines, and against two lines a
 first-line icon reads as sitting high - the eye has the second line to
 measure it against. Centring the row puts the tick, the message block and the
 close on one axis, which is what a short toast wants. */
.hms-flash__item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 12px 12px 14px;
  position: relative;
  overflow: hidden;
  transition: opacity .2s ease, transform .2s ease;
}
.hms-flash__item.is-out { opacity: 0; transform: translateY(-8px); }

/* OPTICAL ALIGNMENT AGAINST THE FIRST LINE.
   The icon is 18px in a 1.35 line box (24.3px); the text is 13.5px in a
   1.4 box (18.9px). align-items: flex-start lines up the two BOXES, so
   the glyph - which sits centred inside the taller one - ended up ~3px
   below the first line of text. Pinning both flanking elements to the
   height of ONE text line and centring inside that makes the icon, the
   first line and the close button share a centre, and keeps doing so
   however many lines the message wraps to. */
.hms-flash__item { --hms-line: 19px; }
.hms-flash__icon {
  flex: 0 0 auto;
  font-size: 18px;
  height: var(--hms-line);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}
.hms-flash__text {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13.5px;
  line-height: 1.4;
  overflow-wrap: anywhere;      /* a long reference must not push the X out */
}
.hms-flash__text strong { font-weight: 700; }

.hms-flash__x {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: inherit;
  opacity: .6;
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
}
.hms-flash__x:hover { opacity: 1; background: rgba(0, 0, 0, .08); }

/* The remaining lifetime, so a message that is about to vanish says so rather
   than disappearing mid-sentence. Pausing on hover pauses the bar with it. */
.hms-flash__bar {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  width: 100%;
  background: currentColor;
  opacity: .35;
  transform-origin: left center;
  animation: hmsFlashBar var(--hms-flash-ttl, 5000ms) linear forwards;
}
.hms-flash__item.is-held .hms-flash__bar { animation-play-state: paused; }
@keyframes hmsFlashBar { from { transform: scaleX(1); } to { transform: scaleX(0); } }

/* Motion is decoration here; the timer still runs. */
@media (prefers-reduced-motion: reduce) {
  .hms-flash__item { transition: none; }
  .hms-flash__bar  { animation: none; opacity: 0; }
}

/* ---------------------------------------------------------------------------
 * COMPLETE BILL - the discount control.
 *
 * Sits inside the Summary, directly under the Discount row it drives, so the
 * number being changed and the control changing it are read as one thing.
 * Everything is in --pos-* tokens, so it follows the theme like the rest of
 * the modal rather than being a light island in a dark till.
 * ------------------------------------------------------------------------- */
.fs-discount {
  margin: 8px 0 12px;
  padding: 10px 12px;
  border: 1px dashed var(--pos-border);
  border-radius: var(--pos-radius-md);
  background: var(--pos-surface-alt);
}
.fs-discount__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 8px;
  font-size: 12px;
  font-weight: 600;
  color: var(--pos-text-secondary);
}
.fs-discount__clear {
  border: 0;
  background: none;
  padding: 0;
  font-size: 12px;
  font-weight: 600;
  color: var(--pos-text-secondary);
  cursor: pointer;
}
.fs-discount__clear:hover { color: var(--pos-primary); text-decoration: underline; }

.fs-discount__row { display: flex; align-items: center; gap: 8px; }
/* The type toggle must not shrink: the currency symbol can be two characters
   and the whole control then reflows onto a second line. */
.fs-discount__row .btn-group { flex: 0 0 auto; }

/* A REAL SELECTED STATE.
 *
 * The theme styles .btn-outline-secondary and its :hover but declares
 * nothing for .active - so clicking % or the currency changed the value
 * that would be used and left the two buttons looking identical. The
 * operator had no way to tell which mode the discount was in, on the one
 * screen where getting it wrong means charging the wrong amount.
 *
 * This is the same pattern the order-type row already uses - soft primary
 * on hover, solid primary when chosen - so the two toggles in the product
 * behave and look alike rather than each inventing their own. */
.fs-discount__row .btn-group .btn {
  min-width: 44px;
  font-weight: 700;
  padding: 4px 10px;
  border: 1px solid var(--pos-border-strong);
  background: var(--pos-surface);
  color: var(--pos-text-secondary);
  transition: background var(--pos-t), color var(--pos-t), border-color var(--pos-t);
}
.fs-discount__row .btn-group .btn:hover {
  background: var(--pos-primary-soft);
  border-color: rgba(var(--pos-primary-rgb), .4);
  color: var(--pos-primary);
}
/* Chosen beats hovered: moving the pointer over the selected button must
   not make it look unselected. */
.fs-discount__row .btn-group .btn.active,
.fs-discount__row .btn-group .btn.active:hover,
.fs-discount__row .btn-group .btn.active:focus {
  background: var(--pos-primary);
  border-color: var(--pos-primary);
  color: #fff;
}
/* Keyboard users must see what the pointer shows. */
.fs-discount__row .btn-group .btn:focus-visible {
  outline: 2px solid var(--pos-primary);
  outline-offset: 1px;
  z-index: 1;
}
/* The pressed instant, so a tap on a till registers visibly. */
.fs-discount__row .btn-group .btn:active { transform: translateY(1px); }
.fs-discount__row .form-control { flex: 1 1 auto; min-width: 0; text-align: right; }

/* Says what the typed figure came to, or why it was capped. Silent when there
   is nothing to say, so an ordinary sale gains no clutter. */
.fs-discount__note {
  margin-top: 6px;
  font-size: 11.5px;
  line-height: 1.4;
  color: var(--pos-text-secondary);
  min-height: 0;
}
.fs-discount__note:empty { display: none; }
.fs-discount__note.is-capped { color: var(--pos-danger, #B42318); font-weight: 600; }
