/* =============================================================================
   Cash Bill POS -- web/css/pos.css (WP6)
   Port of the approved Phase 0 shared stylesheet (shell/component classes,
   minus the watermark shown only in the approved look) plus the page-only
   rules of the approved POS screen and Checkout modal screen, plus the
   shell/login/modal-form/flash rules the approved screens never had to show.
   Selectors that came from a single approved-look page
   are scoped under that view's root class (.v-pos) or under a dedicated
   wrapper (#coModal, .cust-modal) per R16, because plain names such as
   .lines-table / .cust-row / .empty-row are reused with different rules on
   other screens once everything lives in one SPA document.
   ============================================================================= */

*{ box-sizing: border-box }

html, body{
  margin: 0;
  height: 100%;
  background: var(--cb-page);
  color: var(--cb-ink);
  font-family: var(--cb-font);
  font-size: var(--cb-fs-14);
  line-height: 1.4;
  letter-spacing: -.01em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, p, ul, ol{ margin: 0 }

/* [hidden] must win over mock.css's .sidenav{display:flex} etc. */
[hidden]{ display: none !important }

/* ---------- static background colour blocks (mock.css, unchanged) -------- */
.bg-arcs{
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
  background-image:
    radial-gradient(46vw 50vh at 2% 4%,   var(--cb-a1), transparent 70%),
    radial-gradient(42vw 46vh at 99% 10%, var(--cb-a2), transparent 70%),
    radial-gradient(44vw 48vh at 14% 98%, var(--cb-a3), transparent 70%),
    radial-gradient(40vw 44vh at 96% 94%, var(--cb-a4), transparent 70%);
  background-repeat: no-repeat;
}

/* ---------- app shell: fixed viewport, page itself never scrolls --------- */
.app-shell{
  position: relative;
  height: 100vh;
  width: 100%;
  overflow: hidden;
  display: grid;
  grid-template-rows: auto 1fr;
  grid-template-columns: 1fr;
  background: transparent;
}
.app-shell.with-sidenav{
  grid-template-columns: 184px 1fr;
  grid-template-rows: auto 1fr;
}
.app-shell.with-sidenav .topbar{ grid-column: 1 / -1 }

/* bare layout (login): no topbar, body centres its one glass card */
.app-shell[data-layout="bare"]{ grid-template-rows: 1fr }
.app-shell[data-layout="bare"] .topbar{ display: none }
.app-shell[data-layout="bare"] #cb-body{ display: flex; align-items: center; justify-content: center }
.app-shell[data-layout="bare"] .banner-slot{ position: absolute; top: 0; left: 0; right: 0 }

#cb-body{
  display: grid;
  grid-template-rows: auto 1fr;
  position: relative;
  z-index: 1;
  min-height: 0;
  overflow: hidden;
}
.app-body{ min-height: 0 }
.banner-slot{ position: relative; min-height: 0 }
/* flash() banners (app.js) overlay the top of the view instead of pushing it
   down; persistent banners (#cb-persist) stay in flow above them. The overlay
   shows ONE message (app.js marks the older ones flash-old) on ONE line of a
   fixed height, and the POS keeps a strip that tall at its top
   (.v-pos .pos-columns padding-top), so a message never covers the scan box
   or the customer bar. */
.flash-slot{ position: absolute; top: 100%; left: 0; right: 0; z-index: 4; pointer-events: none }
.flash-slot .banner{
  pointer-events: auto;
  box-shadow: 0 6px 16px rgba(14,20,27,.14);
  height: 26px;
  padding-top: 0;
  padding-bottom: 0;
  white-space: nowrap;
  overflow: hidden;
}
.flash-slot .banner > span{ min-width: 0; overflow: hidden; text-overflow: ellipsis }
.flash-slot .flash-old{ display: none }

/* ---------- topbar: solid, carries text/controls -------------------------- */
.topbar{
  position: relative;
  z-index: 2;
  background: var(--cb-topbar-bg);
  border-bottom: 1px solid var(--cb-divider);
  padding: var(--cb-sp-12) var(--cb-sp-20);
  display: flex;
  align-items: center;
  gap: var(--cb-sp-16);
  flex-wrap: wrap;
  min-height: 52px;
}
.topbar h1{
  font-size: var(--cb-fs-16);
  font-weight: 640;
  letter-spacing: -.02em;
  white-space: nowrap;
}
.topbar .spacer{ flex: 1 }
.topbar-right{
  display: flex;
  align-items: center;
  gap: var(--cb-sp-12);
  color: var(--cb-ink2);
  font-size: var(--cb-fs-13);
}
.clock{
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--cb-ink);
  padding: 4px 10px;
  border: 1px solid var(--cb-divider);
  border-radius: var(--cb-r-sm);
  background: var(--cb-panel-2);
  white-space: nowrap;
}

/* compact text nav in the POS topbar (D3 -- the POS screen has no sidenav) */
.topnav{ display: flex; align-items: center; gap: 2px }
.topnav .navlink{
  padding: 5px 10px;
  border-radius: var(--cb-r-sm);
  color: var(--cb-ink2);
  text-decoration: none;
  font-size: var(--cb-fs-13);
  font-weight: 560;
  white-space: nowrap;
}
.topnav .navlink:hover{ background: var(--cb-fill); color: var(--cb-ink) }
.topnav .navlink.active{ background: var(--cb-brand); color: #fff }

/* ---------- sidenav: solid, carries text ----------------------------------- */
.sidenav{
  position: relative;
  z-index: 2;
  background: var(--cb-sidenav-bg);
  border-right: 1px solid var(--cb-divider);
  padding: var(--cb-sp-12) var(--cb-sp-8);
  display: flex;
  flex-direction: column;
  gap: 2px;
  overflow: auto;
}
.sidenav a{
  display: block;
  padding: 8px 12px;
  border-radius: var(--cb-r-sm);
  color: var(--cb-ink2);
  text-decoration: none;
  font-size: var(--cb-fs-14);
  font-weight: 520;
}
.sidenav a:hover{ background: var(--cb-fill); color: var(--cb-ink) }
.sidenav a.active{ background: var(--cb-brand); color: #fff }
.sidenav a.disabled{ color: var(--cb-ink3); pointer-events: none }

/* ---------- panel: solid surface for any block that carries text ---------- */
.panel{
  position: relative;
  z-index: 1;
  background: var(--cb-panel);
  border: 1px solid var(--cb-divider);
  border-radius: var(--cb-r-lg);
  box-shadow: var(--cb-shadow-sm);
}
.panel-2{ background: var(--cb-panel-2) }
.panel-pad{ padding: var(--cb-sp-16) }

/* ---------- .glass: a panel that floats over .bg-arcs, never over text ---- */
.glass{
  position: relative;
  z-index: 1;
  background: var(--cb-glass-panel);
  backdrop-filter: var(--cb-glass-blur);
  -webkit-backdrop-filter: var(--cb-glass-blur);
  border: 1px solid var(--cb-edge);
  box-shadow:
    inset 0 1px 0 var(--cb-hi),
    inset 0 -1px 0 var(--cb-lo),
    0 1px 2px rgba(14,20,27,.08),
    0 10px 28px -6px var(--cb-edge);
}
.glass::after{
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: inherit;
  pointer-events: none;
  padding: 1px;
  background: linear-gradient(145deg, rgba(255,255,255,.95), rgba(255,255,255,.22) 40%, rgba(255,255,255,.05) 62%, rgba(255,255,255,.32));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  opacity: .85;
}

/* ---------- table on glass -------------------------------------------------- */
.glass .tbl thead th{
  background: var(--cb-glass-thead);
  border-bottom-color: var(--cb-glass-divider);
}
.glass .tbl tbody td{ border-bottom-color: var(--cb-glass-divider) }
.glass .tbl tbody tr:nth-child(even){ background: var(--cb-glass-zebra) }
.glass .tbl tbody tr:hover{ background: var(--cb-glass-hover) }

/* ---------- buttons -------------------------------------------------------- */
.btn{
  font: inherit;
  font-size: var(--cb-fs-14);
  font-weight: 590;
  cursor: pointer;
  color: var(--cb-ink);
  background: var(--cb-panel);
  border: 1px solid var(--cb-divider);
  border-radius: var(--cb-r-sm);
  padding: 8px 16px;
  transition: transform .12s ease, filter .12s ease, background .12s ease;
}
a.btn{ display: inline-block; text-decoration: none }
.btn:hover{ background: var(--cb-panel-2) }
.btn:active{ transform: scale(.97) }
.btn:disabled{ color: var(--cb-ink3); cursor: not-allowed }

.btn-primary{
  background: var(--cb-brand);
  border-color: transparent;
  color: #fff;
  font-weight: 620;
  box-shadow: var(--cb-shadow-sm);
}
.btn-primary:hover{ filter: brightness(1.06) }
.btn-primary:disabled{ background: var(--cb-ink3); box-shadow: none }

.btn-danger{
  background: var(--cb-bad-fg);
  border-color: transparent;
  color: #fff;
  font-weight: 620;
}
.btn-danger:hover{ filter: brightness(1.08) }

/* ---------- kbd: keyboard-shortcut hint ------------------------------------ */
kbd{
  font-family: var(--cb-font-mono);
  font-size: var(--cb-fs-12);
  font-weight: 600;
  color: var(--cb-ink2);
  background: var(--cb-panel-2);
  border: 1px solid var(--cb-divider);
  border-bottom-width: 2px;
  border-radius: 5px;
  padding: 1px 6px;
  white-space: nowrap;
}

/* ---------- pill (status capsule) ------------------------------------------ */
.pill{
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--cb-pill-fs);
  font-weight: 640;
  letter-spacing: .01em;
  padding: var(--cb-pill-pad-y) var(--cb-pill-pad-x);
  border-radius: var(--cb-r-pill);
  line-height: 1.5;
  white-space: nowrap;
}
.pill.ok{   color: var(--cb-ok-fg);   background: var(--cb-ok-bg) }
.pill.warn{ color: var(--cb-warn-fg); background: var(--cb-warn-bg) }
.pill.bad{  color: var(--cb-bad-fg);  background: var(--cb-bad-bg) }
.pill.info{ color: var(--cb-info-fg); background: var(--cb-info-bg) }
.pill.void{ color: var(--cb-void-fg); background: var(--cb-void-bg) }

/* ---------- table: high density, numeric columns right-aligned ------------ */
.tbl{
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--cb-fs-13);
}
.tbl thead th{
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--cb-panel-2);
  border-bottom: 1px solid var(--cb-divider);
  text-align: left;
  padding: 6px 10px;
  font-size: var(--cb-fs-12);
  font-weight: 640;
  color: var(--cb-ink2);
  letter-spacing: .02em;
  height: var(--cb-row-36);
}
.tbl tbody td{
  height: var(--cb-row-36);
  padding: 4px 10px;
  border-bottom: 1px solid var(--cb-divider-soft);
  vertical-align: middle;
}
.tbl.row-40 thead th,
.tbl.row-40 tbody td{ height: var(--cb-row-40) }
.tbl tbody tr:last-child td{ border-bottom: none }
.tbl tbody tr:nth-child(even){ background: rgba(14,20,27,.015) }
.tbl tbody tr:hover{ background: var(--cb-fill) }
.tbl .num{
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
}
.tbl .code{
  font-family: var(--cb-font-mono);
  font-size: var(--cb-fs-12);
  color: var(--cb-ink2);
}

/* ---------- ellipsis -------------------------------------------------------- */
.ellipsis{
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: block;
  min-width: 0;
}

/* ---------- modal: solid dialog over a translucent scrim ------------------ */
.modal-scrim{
  position: fixed;
  inset: 0;
  z-index: 50;
  background: var(--cb-modal-scrim);
  display: flex;
  align-items: center;
  justify-content: center;
}
.modal{
  position: relative;
  z-index: 51;
  background: var(--cb-modal-bg);
  border-radius: var(--cb-r-lg);
  box-shadow: var(--cb-shadow-lg);
  padding: var(--cb-sp-20);
  min-width: 320px;
  max-width: 92vw;
  max-height: calc(100vh - 32px);
  overflow: auto;
}
/* A dialog's width is set once (spec.width in app.js modalOpen, or the
   #coModal rule); no block inside the body may widen it past the padding
   (owner 2026-09-25: the F4 picker scrolled sideways). */
.modal .modal-body > *{ max-width: 100% }
.modal h2{
  font-size: var(--cb-fs-20);
  font-weight: 640;
  letter-spacing: -.02em;
  margin-bottom: var(--cb-sp-8);
}
.modal-actions{
  display: flex;
  justify-content: flex-end;
  gap: var(--cb-sp-8);
  margin-top: var(--cb-sp-16);
}
.modal-error{
  color: var(--cb-bad-fg);
  font-size: var(--cb-fs-13);
  font-weight: 640;
  margin: var(--cb-sp-8) 0;
}
/* form fields in the generic modals (pay / void / reverse) and the day-close
   form: label on its own line above a full-width box */
.modal-field{ margin: var(--cb-sp-12) 0 0 }
.modal-field label{ display: block; font-size: var(--cb-fs-13); font-weight: 600; color: var(--cb-ink2); margin-bottom: 6px }
.modal-field input, .modal-field textarea{ width: 100% }
.modal-field textarea{ min-height: 64px }
/* the longer "Day closed at ..." result must not squeeze the button onto two lines */
.v-dayclose .dc-actions .btn{ flex: 0 0 auto; white-space: nowrap }

/* ---------- banner: full-width strip, solid, always on top of content ----- */
.banner{
  position: relative;
  z-index: 3;
  padding: 8px var(--cb-sp-20);
  font-size: var(--cb-fs-13);
  font-weight: 590;
  display: flex;
  align-items: center;
  gap: var(--cb-sp-8);
}
.banner.offline{ background: var(--cb-bad-bg); color: var(--cb-bad-fg) }
.banner.update{  background: var(--cb-info-bg); color: var(--cb-info-fg) }
.banner .banner-action{
  font-weight: 640;
  text-decoration: underline;
  cursor: pointer;
}
/* transient flash() banners (app.js), kind = ok|warn|bad|info */
.banner-ok{   background: var(--cb-ok-bg);   color: var(--cb-ok-fg) }
.banner-warn{ background: var(--cb-warn-bg); color: var(--cb-warn-fg) }
.banner-bad{  background: var(--cb-bad-bg);  color: var(--cb-bad-fg) }
.banner-info{ background: var(--cb-info-bg); color: var(--cb-info-fg) }

/* ---------- misc form controls, kept consistent across mockups ------------ */
input, select, textarea{
  font: inherit;
  font-size: var(--cb-fs-14);
  color: var(--cb-ink);
  background: var(--cb-panel);
  border: 1px solid var(--cb-divider);
  border-radius: var(--cb-r-sm);
  padding: 8px 10px;
}
input:focus, select:focus, textarea:focus{
  outline: none;
  border-color: var(--cb-brand);
  box-shadow: 0 0 0 3px rgba(11,97,87,.14);
}

/* =============================================================================
   login (bare layout) -- no mockup; built from tokens.css + mock.css
   conventions. Scoped under .v-login (the view root class).
   ============================================================================= */
.v-login{ display: flex; align-items: center; justify-content: center; width: 100%; height: 100% }
.v-login .login-card{
  width: min(360px, calc(100vw - 32px));
  padding: var(--cb-sp-24);
  border-radius: var(--cb-r-lg);
  display: flex;
  flex-direction: column;
  gap: var(--cb-sp-16);
}
.v-login .login-title{ font-size: var(--cb-fs-20); font-weight: 640; text-align: center; letter-spacing: -.02em }
.v-login .login-field{ display: flex; gap: 8px }
.v-login .login-show{ flex: 0 0 auto }
.v-login .login-kb{ flex: 0 0 auto; min-width: 52px }
.v-login .login-hint{ margin-top: 8px; font-size: var(--cb-fs-13); color: var(--cb-ink2) }
.v-login .login-caps{ margin-top: 6px; font-size: var(--cb-fs-13); font-weight: 640; color: var(--cb-warn-fg) }
.v-login .login-code{
  width: 100%;
  font-size: var(--cb-fs-16);
  padding: 11px 14px;
  border-radius: var(--cb-r-md);
}
.v-login .login-remember{ display: flex; align-items: center; gap: 8px; font-size: var(--cb-fs-13); color: var(--cb-ink2) }
.v-login .login-error{ color: var(--cb-bad-fg); font-size: var(--cb-fs-13); font-weight: 640 }
/* UX round 2: why the login screen came back (401 / server restart) */
.v-login .login-notice{
  padding: 8px 12px;
  border-radius: var(--cb-r-md);
  font-size: var(--cb-fs-13);
  font-weight: 600;
  color: var(--cb-warn-fg);
  background: var(--cb-warn-bg);
}
.v-login .login-btn{ width: 100%; padding: 12px; font-size: var(--cb-fs-16) }

/* =============================================================================
   POS (approved Phase 0 POS screen) -- scoped under .v-pos, the view's own
   root class. Ported 1:1 from the approved page rules; only the selector
   prefix changed.
   ============================================================================= */
.view.v-pos{ display: grid; grid-template-rows: 1fr auto; min-height: 0 }

.v-pos .pos-columns{
  min-height: 0;
  min-width: 0;
  display: grid;
  grid-template-columns: 1fr 560px;
  gap: var(--cb-sp-16);
  padding: var(--cb-sp-16) var(--cb-sp-20);
  /* room for one flash line (.flash-slot .banner height) plus 2px */
  padding-top: 28px;
}
@media (max-width: 1100px){
  .v-pos .pos-columns{ grid-template-columns: 1fr 420px }
}
@media (min-width: 1600px){
  .v-pos .pos-columns{ grid-template-columns: 1fr 720px }
}

.v-pos .left-col{
  min-height: 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--cb-sp-8);
}
.v-pos .search-bar{ flex: 0 0 auto }
.v-pos .search-input{
  width: 100%;
  font-size: var(--cb-fs-16);
  padding: 11px 14px;
  border-radius: var(--cb-r-md);
  border: 1.5px solid var(--cb-brand);
  box-shadow: 0 0 0 3px rgba(11,97,87,.14);
}

.v-pos .results-card{
  flex: 1;
  min-height: 0;
  padding: 6px;
  border-radius: var(--cb-r-lg);
  display: flex;
}
.v-pos .results-inner{
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border-radius: 10px;
}
.v-pos .results-hint{
  flex: 0 0 auto;
  padding: 6px 12px;
  font-size: var(--cb-fs-12);
  font-weight: 640;
  color: var(--cb-info-fg);
  background: var(--cb-info-bg);
  border-bottom: 1px solid var(--cb-divider);
}
.v-pos .results-scroll{ flex: 1; min-height: 0; overflow: auto }
.v-pos .results-table{ table-layout: fixed; min-width: 498px }
.v-pos .results-table .col-code{ width: 82px }
/* UOM is not shown in the item list (owner 04/10); the cells stay in the DOM so sort/search code is unchanged */
.v-pos .results-table .col-uom{  display: none }
.v-pos .results-table .col-cost{ width: 80px }
.v-pos .results-table .col-list{ width: 80px }
.v-pos .results-table .col-last{ width: 150px }
.v-pos .results-table thead th{ white-space: nowrap }
.v-pos .results-table th.sortable{ cursor: pointer; user-select: none }
.v-pos .results-table th.sort-asc, .v-pos .results-table th.sort-desc{ color: var(--cb-brand) }
.v-pos .results-table th.sort-asc::after{ content: "\00a0\2191" }
.v-pos .results-table th.sort-desc::after{ content: "\00a0\2193" }

.v-pos .row-selected td{ background: var(--cb-glass-row-selected) !important }
.v-pos .row-selected td:first-child{ box-shadow: inset 3px 0 0 0 var(--cb-brand) }
/* the line just added / merged flashes green once (UX round 1). An inset
   shadow paints over the !important row-selected background. */
.v-pos .row-flash td{ animation: cb-rowflash .9s ease-out }
.v-pos .row-flash td:first-child{ animation-name: cb-rowflash-first }
@keyframes cb-rowflash{
  from{ box-shadow: inset 0 0 0 100vmax rgba(11,107,58,.30) }
  to{ box-shadow: inset 0 0 0 100vmax rgba(11,107,58,0) }
}
@keyframes cb-rowflash-first{
  from{ box-shadow: inset 3px 0 0 0 var(--cb-brand), inset 0 0 0 100vmax rgba(11,107,58,.30) }
  to{ box-shadow: inset 3px 0 0 0 var(--cb-brand), inset 0 0 0 100vmax rgba(11,107,58,0) }
}
.v-pos .row-dim td{ color: var(--cb-ink3) }
.v-pos .row-dim .ellipsis{ color: var(--cb-ink3) }
.v-pos .dim{ color: var(--cb-ink3) }

.v-pos .right-col{
  min-height: 0;
  min-width: 0;
  border-radius: var(--cb-r-lg);
  display: flex;
  flex-direction: column;
  gap: var(--cb-sp-8);
}
.v-pos .cust-bar{
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 10px;
  padding-bottom: var(--cb-sp-12);
  border-bottom: 1px solid var(--cb-divider);
}
/* name + pills: the only part of the bar that shrinks, so F4 Change and
   Clear bill never leave the column. Inside it no size depends on a
   percentage (.cust-who is only as wide as its content, so a percentage of
   it shrinks with a short name): when name and pills fit, nothing shrinks.
   When they do not, the name gives way first (its shrink weight dwarfs the
   pills'), with an ellipsis, down to its floor: its own width when short
   (CASH stays whole), 60 px when long. Only below that the pills box gives:
   the Copy pill first (ellipsis), then the right end is clipped (only when
   even the floor leaves no room: <= 1100 px with a pill beside the Copy
   pill, or three pills). */
.v-pos .cust-who{
  flex: 0 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  overflow: hidden;
}
.v-pos .cust-pills{
  flex: 0 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  overflow: hidden;
}
.v-pos .cust-pills .pill{ flex: 0 0 auto }
/* the Copy pill (last, least urgent) gives way first, with an ellipsis; its
   title still holds the whole bill number */
.v-pos .cust-pills #copiedPill{ flex: 0 1 auto; min-width: 0; display: block; overflow: hidden; text-overflow: ellipsis }
/* E1: the Editing pill of an edit draft clips the same way (its title says
   how the edit is saved; the sale number is in the pill and the dialogs) */
.v-pos .cust-pills #editPill{ flex: 0 1 auto; min-width: 0; display: block; overflow: hidden; text-overflow: ellipsis }
.v-pos .cust-change, .v-pos .cust-clear{ flex: 0 0 auto }
/* floor min(own width, 60px); a browser without calc-size() keeps the
   plain 60 px floor (a shorter name then only gets some space after it) */
.v-pos .cust-name{
  font-size: var(--cb-fs-16);
  font-weight: 640;
  flex: 0 1000000 auto;
  min-width: 60px;
  min-width: calc-size(max-content, min(size, 60px));
}
/* the 420 px column (<= 1100 px): tighter gaps, so a long name at its floor
   still has both warning pills (Not linked, Not in list) whole beside it */
@media (max-width: 1100px){
  .v-pos .cust-who, .v-pos .cust-pills{ gap: 6px }
}
.v-pos .cust-placeholder{ color: var(--cb-ink2) }
.v-pos .cust-change{
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: var(--cb-fs-13);
  color: var(--cb-ink2);
  white-space: nowrap;
  text-decoration: none;
}
.v-pos .cust-change:hover{ color: var(--cb-ink) }
/* UX round 2: Clear bill sits in the customer bar, at the top of the bill
   panel, far from Hold at the bottom (owner); red, like the action it does */
.v-pos .cust-clear{
  font-size: var(--cb-fs-13);
  color: var(--cb-bad-fg);
  white-space: nowrap;
  text-decoration: none;
  margin-left: 14px;
}
.v-pos .cust-clear:hover{ text-decoration: underline }
/* the saved Refer No / remark under the customer; a click edits it (F6) */
.v-pos .remark-row{
  flex: 0 0 auto;
  margin-top: 4px;
  font-size: var(--cb-fs-13);
  color: var(--cb-ink2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}
.v-pos .remark-row:hover{ color: var(--cb-ink) }

.v-pos .lines-scroll{ flex: 1; min-height: 0; overflow: auto }
.v-pos .lines-table{ table-layout: fixed; min-width: 480px }
.v-pos .lines-table .col-no{   width: 30px }
.v-pos .lines-table .col-code{ width: 80px }
.v-pos .lines-table .col-qty{  width: 64px }
.v-pos .lines-table .col-price{ width: 76px }
.v-pos .lines-table .col-amount{ width: 84px }
.v-pos .lines-table tbody td{ height: auto; padding: 6px 8px; vertical-align: top; white-space: nowrap }
/* QTY / U.PRICE cells can be edited in place (click; Enter saves, Esc cancels) */
.v-pos .lines-table td.cell-edit{ cursor: text }
.v-pos .lines-table td.cell-edit:hover{ box-shadow: inset 0 0 0 1px var(--cb-brand); border-radius: var(--cb-r-sm) }
.v-pos .lines-table td.cell-edit .inline-edit{
  width: 100%;
  box-sizing: border-box;
  margin: -3px 0;
  padding: 2px 6px;
  font: inherit;
  text-align: right;
  color: var(--cb-ink);
  background: var(--cb-panel);
  border: 1px solid var(--cb-brand);
  border-radius: var(--cb-r-sm);
  outline: none;
}
.v-pos .lines-table td.cell-edit .inline-edit.inline-bad{ border-color: var(--cb-bad-fg); box-shadow: 0 0 0 2px rgba(179, 38, 30, .18) }
/* below cost, warned once: the next Enter will sell under cost */
.v-pos .lines-table td.cell-edit .inline-edit.inline-armed{ border-color: var(--cb-warn-fg); background: var(--cb-warn-bg); box-shadow: 0 0 0 2px rgba(180, 83, 9, .22) }
.v-pos .lines-table thead th{ height: var(--cb-row-36); padding: 6px 8px }
.v-pos .line-sub-row{
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 2px;
  min-width: 0;
}
.v-pos .line-sub{
  flex: 1;
  min-width: 0;
  font-size: 11px;
  color: var(--cb-ink2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.v-pos .line-sub-row .pill{ flex: 0 0 auto }
.v-pos .row-below-cost td{ background: var(--cb-glass-below-cost) }
.v-pos .row-editing td{ background: var(--cb-warn-bg) }
.v-pos .row-confirm td{ border-top: none; padding-top: 0 }
.v-pos .confirm-msg{
  white-space: normal;
  color: var(--cb-warn-fg);
  font-size: var(--cb-fs-12);
  font-weight: 640;
  padding-bottom: 8px;
}
.v-pos .empty-row td{ color: var(--cb-ink2); font-style: normal; text-align: center; padding: 24px 10px }
/* the first-bill steps (NO_LINES) wrap instead of being cut off (UX round 1) */
.v-pos .lines-table .empty-row td{ white-space: normal; line-height: 1.5 }
/* UX round 2: the x that removes a line sits over the line number of the
   selected (or hovered) row; absolute, so it never changes the layout */
.v-pos .lines-table td.line-no-cell{ position: relative }
.v-pos .lines-table .line-x{
  position: absolute;
  top: 5px;
  left: 4px;
  width: 22px;
  height: 22px;
  padding: 0;
  border: none;
  border-radius: var(--cb-r-sm);
  background: transparent;
  color: var(--cb-bad-fg);
  font-family: inherit;
  font-size: 18px;
  font-weight: 700;
  line-height: 22px;
  text-align: center;
  cursor: pointer;
  visibility: hidden;
}
.v-pos .lines-table tbody tr.row-selected .line-x,
.v-pos .lines-table tbody tr:hover .line-x{ visibility: visible }
.v-pos .lines-table tbody tr.row-selected .line-no,
.v-pos .lines-table tbody tr:hover .line-no{ visibility: hidden }
.v-pos .lines-table .line-x:hover{ background: var(--cb-bad-bg) }

.v-pos .totals-row{
  flex: 0 0 auto;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  padding-top: var(--cb-sp-12);
  border-top: 1px solid var(--cb-divider);
}
.v-pos .total-block{ display: flex; flex-direction: column; gap: 2px }
.v-pos .total-label{ font-size: var(--cb-fs-12); font-weight: 640; letter-spacing: .02em; color: var(--cb-ink2) }
.v-pos .total-value{ font-size: var(--cb-fs-20); font-weight: 640; font-variant-numeric: tabular-nums }
.v-pos .total-value.big{ font-size: var(--cb-fs-28); color: var(--cb-brand) }

.v-pos .checkout-btn{
  flex: 0 0 auto;
  width: 100%;
  margin-top: var(--cb-sp-8);
  padding: 14px;
  font-size: var(--cb-fs-16);
}
.v-pos .hold-row{ display: flex; gap: 8px; margin-bottom: 8px }
.v-pos .hold-row .btn{ flex: 1 1 0; min-width: 0 }
/* three buttons (Hold, Held bills, Remark) must fit the 420 px column */
.v-pos .hold-row .btn{
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  padding-left: 10px;
  padding-right: 10px;
}
.v-pos .cost-stale-note{
  flex: 0 0 auto;
  margin-top: var(--cb-sp-8);
  color: var(--cb-bad-fg);
  font-size: var(--cb-fs-12);
  font-weight: 640;
}

.topbar .outlet-tag{
  font-size: var(--cb-fs-12);
  font-weight: 640;
  letter-spacing: .02em;
  padding: 3px 10px;
  border-radius: var(--cb-r-pill);
  background: var(--cb-panel-2);
  border: 1px solid var(--cb-divider);
  color: var(--cb-ink2);
}
.topbar .staff-name{ font-size: var(--cb-fs-13); color: var(--cb-ink2) }
/* UX round 2: the help button beside Log out, and the help dialog */
.topbar .topbar-help{ padding: 6px 12px; font-weight: 700; min-width: 34px }
/* UX round 6: the "New version ready" pill is a button in the pill group.
   Below 1600 px the topbar has no room for it on one line, so while it is
   shown (.has-update) the group gaps and the freshness pills get tighter and
   the pill itself wraps onto two short lines (Log out stays on line one).
   Its text is the same size as the freshness pills (--cb-pill-fs) at every
   width, and an outline drawn inside the box (no size change) marks it as
   the one pill that can be clicked. */
.topbar-right .update-pill{
  border: 0; margin: 0; font-family: inherit; cursor: pointer;
  box-shadow: inset 0 0 0 1px currentColor;
}
.topbar-right .update-pill:hover{ text-decoration: underline }
@media (max-width: 1599px){
  .topbar-right.has-update{ gap: 4px }
  .topbar-right.has-update .pill{ padding-left: 6px; padding-right: 6px }
  .topbar-right.has-update .update-pill{
    white-space: normal;
    box-sizing: border-box;
    max-width: calc(5.9em + 12px);  /* "New version" on line one, "ready" below */
    padding: 2px 6px;
    font-size: var(--cb-pill-fs);
    line-height: 1.15;
    text-align: center;
    border-radius: 8px;
  }
}
/* with the help button the POS topbar (nav + four pills + clock) no longer
   fit one line at 1280 px: tighter spacing below 1366 px keeps Log out on
   the first line instead of wrapping it onto a second one */
@media (max-width: 1365px){
  .topbar{ gap: 10px; padding-left: 16px; padding-right: 16px }
  .topbar-right{ gap: 8px }
}
.help-grid{
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px 20px;
  font-size: var(--cb-fs-13);
  line-height: 1.4;
}
.help-grid .help-sec{
  grid-column: 1 / -1;
  margin-top: 6px;
  font-size: var(--cb-fs-12);
  font-weight: 640;
  letter-spacing: .02em;
  color: var(--cb-ink2);
  text-transform: uppercase;
}
.help-row{ display: flex; align-items: baseline; gap: 10px; min-width: 0 }
.help-row kbd{ flex: 0 0 auto; min-width: 64px; text-align: center }
.help-row span{ flex: 1; min-width: 0 }
.topbar .staff-name .staff-label{ font-weight: 640; color: var(--cb-ink3); margin-right: 4px }

.v-pos .shortcut-bar{
  background: var(--cb-panel);
  border-top: 1px solid var(--cb-divider);
  display: flex;
  align-items: center;
  gap: 0;
  padding: 8px var(--cb-sp-20);
  font-size: 12.5px;
  color: var(--cb-ink2);
  flex-wrap: wrap;
}
.v-pos .shortcut-bar span{
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 14px;
  border-left: 1px solid var(--cb-divider);
  white-space: nowrap;
}
.v-pos .shortcut-bar span:first-child{ border-left: none; padding-left: 0 }

/* =============================================================================
   F4 customer picker -- rendered through ctx.modal.open() into
   #cb-modal-root, so it cannot be scoped under .v-pos (see file header);
   scoped instead under its own wrapper class .cust-modal, which nothing
   else in the app uses as a root class.
   ============================================================================= */
.cust-modal{ width: auto }
.cust-modal .cust-modal-sub{ font-size: var(--cb-fs-12); color: var(--cb-ink2); margin-bottom: var(--cb-sp-8) }
.cust-modal .cust-filter-input{ width: 100%; font-size: var(--cb-fs-14); padding: 9px 12px; margin-bottom: var(--cb-sp-8) }
.cust-modal .cust-list{
  max-height: 362px;
  overflow: auto;
  border: 1px solid var(--cb-divider);
  border-radius: var(--cb-r-md);
}
.cust-modal .cust-row{
  display: flex;
  align-items: center;
  gap: 8px;
  height: 40px;
  padding: 0 12px;
  border-bottom: 1px solid var(--cb-divider-soft);
  cursor: pointer;
}
.cust-modal .cust-row:last-child{ border-bottom: none }
.cust-modal .cust-row .cust-row-name{ flex: 1; min-width: 0; font-weight: 590 }
.cust-modal .cust-row.selected{ background: rgba(11,97,87,.10) }
.cust-modal .cust-row.selected .cust-row-name{ color: var(--cb-brand); font-weight: 640 }

/* Held bills list (F1 on an empty bill / the Held bills button). Rendered
   through ctx.modal.open(), so scoped under its own wrapper class. */
.held-modal{ width: auto }
.held-modal .cust-modal-sub{ font-size: var(--cb-fs-12); color: var(--cb-ink2); margin-bottom: var(--cb-sp-8) }
.held-list{ max-height: 50vh; overflow-y: auto }
.held-item{ display: flex; gap: 8px; align-items: center; margin-bottom: 6px }
.held-row{
  flex: 1;
  min-width: 0;
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 12px;
  border: 1px solid var(--cb-divider);
  border-radius: var(--cb-r-md);
  background: var(--cb-panel);
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.held-row:focus-visible{ outline: 2px solid var(--cb-brand); outline-offset: 1px }
.held-row .held-cust{ font-weight: 590; min-width: 0 }
.held-row .held-meta{ font-size: var(--cb-fs-12); color: var(--cb-ink2) }

/* =============================================================================
   Checkout modal (approved Phase 0 Checkout screen) -- unique prefix .co- / #coModal,
   no further scoping needed.
   ============================================================================= */
#coModal{ width: 520px }
.co-head{ display: flex; align-items: baseline; justify-content: space-between; margin-bottom: var(--cb-sp-4) }
.co-head h2{ margin: 0 }
.co-sub{ font-size: var(--cb-fs-13); color: var(--cb-ink2) }

.co-methods{ display: flex; gap: var(--cb-sp-8); margin: var(--cb-sp-16) 0 var(--cb-sp-4) }
.co-method{
  flex: 1;
  display: flex; align-items: center; gap: 10px;
  padding: 14px 14px;
  border-radius: var(--cb-r-md);
  border: 1.5px solid var(--cb-divider);
  background: var(--cb-panel-2);
  font-weight: 620; font-size: var(--cb-fs-16); color: var(--cb-ink2);
  cursor: pointer;
}
.co-method .co-method-label{ flex: 1 }
.co-method .co-check{ color: var(--cb-brand); opacity: 0; font-weight: 700 }
.co-method.selected{ border-color: var(--cb-brand); background: var(--cb-ok-bg); color: var(--cb-brand) }
.co-method.selected .co-check{ opacity: 1 }

.co-row{
  display: flex; justify-content: space-between; align-items: baseline;
  padding: 9px 2px; border-bottom: 1px solid var(--cb-divider-soft);
}
.co-row:last-of-type{ border-bottom: none }
.co-row .co-lbl{ color: var(--cb-ink2); font-size: var(--cb-fs-14) }
.co-row .co-val{ font-weight: 650; font-size: var(--cb-fs-16); font-variant-numeric: tabular-nums }
/* UX round 3 (C1): the credit step's informational 'already owes' line. No
   display rule here, so the hidden attribute keeps working. */
.co-owes{ margin: 10px 0 0; padding: 8px 12px; border-radius: var(--cb-r-md); background: var(--cb-warn-bg); color: var(--cb-warn-fg); font-size: var(--cb-fs-13); font-weight: 600; line-height: 1.4 }

.co-field{ margin: var(--cb-sp-16) 0 0 }
.co-field label{ display: block; font-size: var(--cb-fs-13); font-weight: 600; color: var(--cb-ink2); margin-bottom: 6px }
.co-field input{ width: 100%; font-size: var(--cb-fs-20); font-weight: 650; text-align: right; padding: 10px 14px; font-variant-numeric: tabular-nums }

.co-big{ text-align: center; padding: var(--cb-sp-20) 0 var(--cb-sp-4) }
.co-big .co-biglbl{ font-size: var(--cb-fs-12); font-weight: 650; letter-spacing: .06em; text-transform: uppercase; color: var(--cb-ink2) }
.co-big .co-bigval{ font-size: 40px; font-weight: 760; letter-spacing: -.02em; color: var(--cb-brand); margin-top: 2px; font-variant-numeric: tabular-nums }
.co-big.owing .co-bigval{ color: var(--cb-warn-fg) }

.co-checking{ text-align: center; padding: 30px 6px 12px }
.co-spinner{
  width: 42px; height: 42px; margin: 0 auto 18px;
  border-radius: 50%; border: 4px solid var(--cb-divider); border-top-color: var(--cb-brand);
  animation: coSpin .8s linear infinite;
}
@keyframes coSpin{ to{ transform: rotate(360deg) } }
.co-checking h3{ font-size: var(--cb-fs-20); font-weight: 650; margin: 0 0 8px }
.co-checking p{ color: var(--cb-ink2); font-size: var(--cb-fs-13); max-width: 380px; margin: 0 auto; line-height: 1.5 }

.co-done{ text-align: center; padding: 6px 4px 2px }
.co-done .co-billno{ font-size: 36px; font-weight: 780; letter-spacing: -.01em; font-variant-numeric: tabular-nums }
.co-done .co-date{ color: var(--cb-ink2); font-size: var(--cb-fs-14); margin-top: 6px }
.co-done .co-printing{
  display: inline-flex; align-items: center; gap: 8px; margin-top: 16px;
  padding: 6px 14px; border-radius: var(--cb-r-pill);
  background: var(--cb-info-bg); color: var(--cb-info-fg); font-weight: 640; font-size: var(--cb-fs-13);
}
.co-done .co-printing .co-spinner{ width: 14px; height: 14px; border-width: 2px; margin: 0 }
.co-done .co-printed{
  display: inline-flex; align-items: center; gap: 8px; margin-top: 16px;
  padding: 6px 14px; border-radius: var(--cb-r-pill);
  background: var(--cb-ok-bg); color: var(--cb-ok-fg); font-weight: 640; font-size: var(--cb-fs-13);
}
.co-done .co-row{ margin-top: 10px }

.co-failed{ text-align: center; padding: 10px 4px 2px }
.co-failed h3{ color: var(--cb-bad-fg); font-size: var(--cb-fs-20); font-weight: 700; margin: 0 }
.co-failed .co-reason{ color: var(--cb-ink2); font-size: var(--cb-fs-14); margin-top: 10px; line-height: 1.5 }

.co-choose{ text-align: center; padding: 4px 4px 0 }
.co-choose h3{ font-size: var(--cb-fs-20); font-weight: 700; margin: 0 }
.co-choose .co-reason{ color: var(--cb-ink2); font-size: var(--cb-fs-14); margin: 6px 0 0; line-height: 1.5 }
.co-grp{ margin: 10px 0 0; font-size: var(--cb-fs-12); font-weight: 650; letter-spacing: .06em; text-transform: uppercase; color: var(--cb-ink2) }
.co-grp ~ .co-row{ padding: 5px 2px }
.co-grp ~ .co-hint{ margin-top: 8px }

.co-hint{ margin-top: var(--cb-sp-12); text-align: center; font-size: var(--cb-fs-12); color: var(--cb-ink3) }

.co-stale{
  margin: 0 0 var(--cb-sp-12); padding: 7px 12px; border-radius: var(--cb-r-sm);
  background: var(--cb-bad-bg); color: var(--cb-bad-fg);
  font-size: var(--cb-fs-13); font-weight: 640;
}

/* =============================================================================
   UX round 7: the POS in a half-screen window (owner 02/10: about 930 px wide).
   Layout only, and only below 1280 px, so 1280 px and wider look exactly as
   before. Nothing is removed from the page: what gives way is hidden by CSS,
   its text stays in the DOM and its name in a title.
   ============================================================================= */
@media (max-width: 1279px){
  /* the item list fits its column instead of scrolling sideways (1101-1279
     px: the left column is 485-663 px, under the 498 px minimum, which hid
     LAST @ CUSTOMER); NAME (ellipsis + title) takes what is left */
  .pos-columns .results-table{ min-width: 0 }
  /* the POS topbar (nav + four freshness pills + clock + help + Log out)
     needs about 1230 px on one line: below 1280 px each freshness pill is a
     status dot in the pill's own colour (ok / warn / bad). Its text stays in
     the DOM and its title carries the whole text plus the reason (Items 3h:
     ok ...); the Cost dot keeps
     cost-only. Only the POS topbar (the one with the nav links); the New
     version pill is a button, not a span, and keeps its text. */
  .app-shell[data-layout="pos"] .topbar-right span.pill{
    flex: 0 0 auto;
    width: 12px;
    height: 12px;
    padding: 0;
    font-size: 0;
    line-height: 0;
    border-radius: 50%;
    background: currentColor;
    cursor: help;
  }
  /* while the New version pill (two short lines) is shown the clock gives
     way, so a long staff name still leaves Log out on the first line */
  .app-shell[data-layout="pos"] .topbar-right.has-update ~ .clock{ display: none }
  /* tighter gaps and nav links; a long staff name ends in an ellipsis (the
     span's title holds the whole name) instead of pushing Log out down */
  .app-shell .topbar{ gap: 8px }
  .app-shell .topbar .topnav .navlink{ padding-left: 7px; padding-right: 7px }
  /* the staff name is the one item of the topbar that gives way: it counts
     as nothing when the line is filled (flex-basis 0), then grows back to its
     full width (max-content) as far as the line has room, so it never pushes
     Log out onto a second line whatever the name, the outlet or the New
     version pill (no fixed cap tuned to one width); a short name keeps its
     own width */
  .app-shell[data-layout="pos"] .topbar .staff-name{
    flex: 1 1 0;
    min-width: 0;
    max-width: max-content;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}
/* below the promised 900 px a long name keeps at least 3em and the topbar
   wraps (as before round 7) instead of squeezing the name to nothing */
@media (max-width: 899px){
  .app-shell[data-layout="pos"] .topbar .staff-name{ min-width: 3em }
}
@media (max-width: 1100px){
  /* the 420 px bill column has a 386 px content box, under the 480 px
     minimum of the lines table, which hid AMOUNT and cut U.PRICE and the
     empty-bill hint; DESCRIPTION (ellipsis + title) takes what is left */
  .pos-columns .lines-table{ min-width: 0 }
  /* in that narrower column a line that carries a Below cost / No price pill
     shows its code, cost and last-price text in full: the text wraps and the
     pill follows it, instead of the pill cutting the text to the item code.
     Lines without a pill keep the one-line sub-row (text in the title). A
     browser without :has() keeps the one-line sub-row too. */
  .pos-columns .line-sub-row:has(.pill){ display: block; white-space: normal }
  .pos-columns .line-sub-row:has(.pill) .line-sub{ white-space: normal; overflow: visible; margin-right: 4px }
}
/* below 1000 px COST gives way in the item list (the bill line still shows
   it); CODE, NAME, PRICE and LAST @ CUSTOMER stay. UOM is hidden at every width.
   NAME keeps a floor: CODE 82 + PRICE 80 + LAST 150 = 312 px of fixed columns
   plus 88 px of NAME (about 11 characters); in a window narrower than about
   890 px the list scrolls sideways (as before round 7) instead of NAME
   shrinking to nothing. */
@media (max-width: 999px){
  .pos-columns .results-table .col-cost{ display: none }
  .pos-columns .results-table{ min-width: 400px }
}
/* topbar and shortcut bar stay on one line down to 900 px */
@media (max-width: 1023px){
  .app-shell .topbar{ gap: 6px; padding-left: 12px; padding-right: 12px }
  .app-shell .topbar .topnav .navlink{ padding-left: 6px; padding-right: 6px }
  /* the word Staff gives way (the name stays, its title holds it whole) */
  .app-shell[data-layout="pos"] .topbar .staff-name .staff-label{ display: none }
  .app-shell .v-pos .shortcut-bar{ padding-left: 16px; padding-right: 16px }
  .app-shell .v-pos .shortcut-bar span{ gap: 4px; padding-left: 8px; padding-right: 8px }
  .app-shell .v-pos .shortcut-bar span:first-child{ padding-left: 0 }
}
