/* Billing: three skins, one bill.
 *
 * What changes between the skins is where things sit — never what a number
 * means, and never how far the hand travels for the thing it reaches for
 * most. Every editable figure is an input, so correcting a quantity is
 * typing, not hunting for a pencil. */

.view { flex: 1; display: flex; flex-direction: column; min-height: 0; }

/* Every skin fills the window: the bill grows, the bar stays at the foot.
 * Without this the screen stopped where the rows stopped and the money bar
 * floated wherever that happened to be. */
.ph-skin, .vo-skin, .cl-skin { flex: 1; display: flex; min-height: 0; }
.ph-skin, .vo-skin { flex-direction: column; min-width: 0; }
.spacer { flex: 1; }
.hint { font-size: 11px; color: var(--muted); }
.small { padding: 5px 9px; font-size: 11.5px; }

/* The strip above the bill. Flat and greyed like a program's own toolbar —
 * rounded pills on a chrome bar were the one thing on this screen that could
 * not be mistaken for a Windows application. */
.skinbar {
  display: flex;
  gap: 6px;
  align-items: center;
  padding: 5px 10px;
  background: linear-gradient(#fbfcfe, #eef1f6);
  border-bottom: 1px solid #cdd5e2;
}
.skinbar .chip {
  padding: 4px 12px;
  font-size: 12px;
  border-radius: 3px;
  border-color: #c3ccdb;
  background: linear-gradient(#ffffff, #eef1f6);
}
.skinbar .chip:hover { background: linear-gradient(#ffffff, #e3e9f2); }
.skinbar .chip.on {
  color: #fff;
  background: linear-gradient(#1d3f77, #102450);
  border-color: #102450;
}
.skinbar .lbl {
  font-size: 11px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .4px;
}

.chip {
  padding: 5px 11px;
  font: var(--font);
  font-size: 12px;
  color: var(--ink);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 20px;
  cursor: pointer;
}
.chip:hover { background: var(--bg); }
.chip.on { color: #fff; background: var(--accent); border-color: var(--accent); }

/* What is being typed, and what the shop has that matches it. */
.entry {
  position: relative;
  padding: 6px 10px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.entry > input {
  width: 100%;
  padding: 8px 11px;
  font: var(--font);
  font-size: 14px;
  border: 2px solid var(--accent);
  border-radius: 7px;
  background: #fff;
}
.entry > input:focus { outline: none; box-shadow: 0 0 0 4px rgba(15, 118, 110, .13); }

/* "amox*5" understood, said back before Enter is pressed. Without this the
 * shorthand is a thing you try once, cannot tell whether it worked, and stop
 * using. */
.qbadge {
  position: absolute;
  right: 22px;
  top: 50%;
  transform: translateY(-50%);
  padding: 2px 9px;
  font-size: 12px;
  font-weight: 800;
  color: #3a2c00;
  background: #ffe9a8;
  border: 1px solid #d8a800;
  border-radius: 4px;
  pointer-events: none;
}
.vo-body > .entry .qbadge { right: 10px; }

.suggest {
  position: absolute;
  z-index: 20;
  left: 12px; right: 12px; top: calc(100% - 4px);
  max-height: 320px;
  overflow: auto;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: 0 14px 34px rgba(0, 0, 0, .16);
}
/* Five columns, or six when the shop has asked to see what it paid. The
 * template is written from the row it is styling — a fixed five-column grid
 * with a sixth cell in it silently stacks the last two on top of each other. */
.sug {
  display: grid;
  /* name+salt · batch · expiry · stock · [cost] · margin · rate
   * The rate column carries two lines for a medicine that can be cut — the
   * whole-pack price and the per-unit one under it — so it is wider than the
   * single figure it used to hold. */
  grid-template-columns: 1fr 110px 90px 90px 56px 108px;
  gap: 8px;
  align-items: center;
  width: 100%;
  padding: 8px 11px;
  font: var(--font);
  text-align: left;
  background: none;
  border: 0;
  border-bottom: 1px solid #f2f4f8;
  cursor: pointer;
}
.suggest.withcost .sug { grid-template-columns: 1fr 110px 90px 90px 80px 56px 108px; }
.sug:hover, .sug.on { background: #eefaf7; }
/* The brand and its composition, stacked — one grid cell, two lines. */
.s-who { display: flex; flex-direction: column; min-width: 0; gap: 1px; }
.s-name { font-weight: 600; }
/* The WHOLE composition. It wraps rather than truncating: a combination cut
   off after its first salt is not a composition, and the second salt is
   often the one being asked for. */
.s-salt {
  font-size: 10.5px;
  line-height: 1.25;
  color: var(--accent);
  font-weight: 600;
  overflow-wrap: anywhere;
}
/* What the shop keeps. Coloured because a chemist choosing between two
   brands of one salt is choosing on this number: red is a loss, amber is
   barely paying for the shelf. */
.s-margin {
  font-size: 11px;
  font-weight: 800;
  text-align: right;
  color: #15803d;
}
.s-margin.thin { color: var(--warn); }
.s-margin.loss { color: var(--danger); }
/* Not zero — unknown. Nobody entered a cost, and "0%" would read as
   "we make nothing on this". */
.s-margin.unknown { color: var(--muted); font-weight: 600; }
.s-batch, .s-exp { font-size: 11.5px; color: var(--muted); }
.s-exp.soon { color: var(--warn); font-weight: 700; }
.s-stock { font-size: 11.5px; text-align: right; }
.s-stock.out { color: var(--danger); font-weight: 700; }
.s-cost { font-size: 11.5px; text-align: right; color: var(--muted); }
.s-rate { text-align: right; font-weight: 700; }

/* The bill itself */
.billwrap { flex: 1; overflow: auto; background: var(--surface); }
.bill { width: 100%; border-collapse: collapse; }
.bill thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: 5px 8px;
  font-size: 11px;
  font-weight: 700;
  text-align: left;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .4px;
  background: #eef1f6;
  border-bottom: 1px solid var(--line);
}
/* A number's heading belongs over the number.
 *
 * `.bill thead th` sets text-align on a two-class selector, which quietly
 * beats the `.num` right-align that every figure cell uses — so QTY, MRP and
 * AMOUNT sat on the left of columns whose values were on the right, in all
 * three skins. */
.bill thead th.num { text-align: right; }
.bill td { padding: 2px 8px; border-bottom: 1px solid #f0f2f7; vertical-align: middle; }
.bill tbody tr:nth-child(even) { background: var(--row-alt); }
.bill tr.over { background: #fff7ed; }
.bill .idx { width: 34px; color: var(--muted); }
.bill .nm { font-weight: 600; }
.bill .bt, .bill .ex { font-size: 11.5px; color: var(--muted); }
.bill .amt { font-weight: 700; white-space: nowrap; }
.bill .none td { padding: 30px 12px; color: var(--muted); text-align: center; font-weight: 400; }
.tag { font-size: 10.5px; color: var(--accent); font-weight: 600; }

/* ── A line coming back on the same bill ─────────────────────────────
 *
 * A customer swapping a wrong strength, or handing in yesterday's strip while
 * buying today's. It belongs on the same document rather than on a separate
 * credit note twenty minutes later — which is what a shop otherwise does not
 * bother to raise at all.
 *
 * Marked plainly, because a row that quietly subtracts is a row somebody adds
 * up wrong in their head. */
/* Written `tbody tr.gaveback` and not `tr.gaveback`, which is the whole
 * reason this was invisible half the time: the striping above it is
 * `.bill tbody tr:nth-child(even)`, and a pseudo-class counts as a class.
 * So the stripe outranked the state, and EVERY SECOND returned row showed
 * the ordinary background — the chemist saw a row that quietly subtracted
 * and looked no different from the ones that did not. Same for `.over`. */
.bill tbody tr.gaveback {
  background: #fff1f1;
  /* An edge as well as a tint. A tint alone is what gets lost against a
   * stripe, a highlight, or a screen a shop has turned the brightness down
   * on; an edge survives all three. */
  box-shadow: inset 3px 0 0 var(--danger);
}
.bill tbody tr.gaveback .idx { color: var(--danger); font-weight: 700; }
.bill tbody tr.gaveback .amt { color: var(--danger); font-weight: 700; }
.bill tbody tr.gaveback td { border-bottom-color: #f6cccc; }
.bill tbody tr.over { background: #fff7ed; }
.tag.back {
  margin-left: 5px;
  padding: 0 5px;
  color: var(--danger);
  background: #fee2e2;
  border-radius: 3px;
}

/* Turns the line round. Beside the remove button, because the two are the same
 * kind of decision about a row already on the bill. */
/* A word, not a glyph.
 *
 * It was a 13-pixel ⇄ at the end of a row, and a shop that had been told the
 * feature existed still could not find it. Nobody finds a control by reading
 * rows — so it says what it does, and the bar above the bill says it once
 * more in a sentence. */
.turn {
  margin-left: 6px;
  padding: 1px 7px;
  font: var(--font);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--muted);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 3px;
  cursor: pointer;
}
.turn:hover { color: var(--danger); border-color: #fecaca; background: #fef2f2; }
/* On, it is the state of the row rather than an offer.
 *
 * Loud on purpose. A chemist pressed it and could not tell whether anything
 * had happened — and on a row that has just been turned into a subtraction,
 * "did that work?" is the one question the screen must not leave open. So:
 * filled red, an arrow that points the way the goods are going, and a single
 * flash at the moment of the press so the eye catches the change even when it
 * was looking at the total instead of at the button. */
.turn.on {
  color: #fff;
  background: var(--danger);
  border-color: var(--danger);
  box-shadow: 0 0 0 2px #fee2e2;
  animation: turned .45s ease-out;
}
.turn.on::before { content: '↩ '; }
.turn.on:hover { color: #fff; background: #b91c1c; border-color: #b91c1c; }
@keyframes turned {
  0%   { transform: scale(.86); box-shadow: 0 0 0 0 rgba(220, 38, 38, .55); }
  55%  { transform: scale(1.06); box-shadow: 0 0 0 7px rgba(220, 38, 38, .16); }
  100% { transform: scale(1); box-shadow: 0 0 0 2px #fee2e2; }
}
@media (prefers-reduced-motion: reduce) {
  .turn.on { animation: none; }
}

/* The purchase voucher's own note, in the same words. */
.pu-note {
  padding: 5px 12px;
  font-size: 11.5px;
  color: #46618f;
  background: #eef2f9;
  border-bottom: 1px solid #dde4ef;
}
.pu-note b { color: var(--danger); }
.pu-acts { white-space: nowrap; }
.warn { font-size: 11px; color: var(--warn); }

.cell {
  width: 68px;
  padding: 3px 6px;
  font: var(--font);
  font-size: 13px;
  text-align: right;
  border: 1px solid var(--line);
  border-radius: 5px;
  background: #fff;
}
.cell:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(15, 118, 110, .12); }

.x {
  margin-left: 6px;
  padding: 0 5px;
  font-size: 15px;
  line-height: 1;
  color: var(--muted);
  background: none;
  border: 0;
  cursor: pointer;
}
.x:hover { color: var(--danger); }

/* Eight boxes on one line is eight boxes wide. It WRAPS: without this the row
 * ran off the right of the window and took the whole bill with it. */
.cust {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 6px 10px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
/* Each field is a caption and a box. The caption is written into every skin
 * and shown only by the voucher — see the note in billing.js — so the Counter
 * and Classic skins look exactly as they did, with the placeholder doing the
 * work. */
.cust .fld { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cust .fl { display: none; }

.cust input, .cust select {
  width: 100%;
  min-width: 0;
  padding: 6px 9px;
  font: var(--font);
  font-size: 13px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: var(--bg);
}
.cust input:focus, .cust select:focus { outline: none; border-color: var(--accent); background: #fff; }

/* The money.
 *
 * One bar along the foot: the figures read left to right the way they are
 * said out loud, and Save sits at the end of the sentence. Stacked, this
 * block was taller than the bill it belonged to. */
.totals {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 16px;
  padding: 7px 12px;
  background: var(--surface);
  border-top: 2px solid var(--line);
}
.totals .row {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--muted);
  white-space: nowrap;
}
.totals .row b { color: var(--ink); font-size: 12.5px; }
.totals .grand {
  padding: 2px 12px;
  font-size: 13px;
  font-weight: 700;
  color: var(--ink);
  background: #eefaf7;
  border: 1px solid #cdeee7;
  border-radius: 7px;
}
.totals .grand b { font-size: 20px; }
.totals .good b { color: #15803d; }
.totals .bad b { color: var(--danger); }
.totals input {
  width: 76px;
  padding: 4px 7px;
  font: var(--font);
  font-size: 12.5px;
  text-align: right;
  border: 1px solid var(--line);
  border-radius: 6px;
}
.totals .unit {
  width: 24px;
  padding: 2px 0;
  font: var(--font);
  font-weight: 700;
  color: var(--accent);
  background: #eefaf7;
  border: 1px solid #cdeee7;
  border-radius: 5px;
  cursor: pointer;
}
/* Two groups, so a wrap moves a whole group instead of dropping one control
 * on top of another. */
.t-left, .t-right { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px; }
.t-right { margin-left: auto; }
.pay { display: flex; gap: 4px; }
.save {
  padding: 9px 22px;
  font: var(--font);
  font-size: 14px;
  font-weight: 800;
  color: #fff;
  background: var(--accent);
  border: 0;
  border-radius: 7px;
  cursor: pointer;
  white-space: nowrap;
}
.save:hover { background: var(--accent-dk); }
.save:disabled { opacity: .6; cursor: default; }
.totals .link { color: var(--muted); }

/* The register's side panel.
 *
 * Its own class on purpose. It began as a variant of the bottom money bar
 * (`.totals side`), and the bar's layout — centred alignment, wrapping, its
 * paddings — kept leaking in and shrink-wrapping every block to its own
 * width. A panel and a bar share arithmetic, not layout, so they no longer
 * share a class, and nothing the bar does can reach this again.
 *
 * Read top to bottom it answers one question per line — the figures, the
 * four ways to pay, what was received, the total, the button — every block
 * between the same two edges, and only the total loud. */
.sidepanel {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
  height: 100%;
  padding: 12px;
  overflow: auto;
}

/* t-left / t-right are the BAR's group names too, and the bar's generic rule
 * for them centres and wraps — which is the shrink-wrap bug one level down.
 * Everything the bar sets on these names is overridden here explicitly. */
.sidepanel .t-left,
.sidepanel .t-right {
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  align-items: stretch;
  gap: 10px;
  margin: 0;
}

/* The figures, together in one quiet box. */
.sidepanel .t-left {
  gap: 2px;
  padding: 9px 11px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 8px;
}

.sidepanel .row {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 26px;
  font-size: 12px;
}
.sidepanel .row > span:first-child { color: var(--muted); }
.sidepanel .row b {
  margin-left: auto;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
}
.sidepanel .good b { color: #15803d; }
.sidepanel .bad b { color: var(--danger); }

.sidepanel input {
  width: 96px;
  margin-left: auto;
  padding: 4px 8px;
  font: var(--font);
  font-size: 12.5px;
  text-align: right;
  border: 1px solid var(--line);
  border-radius: 6px;
}
.sidepanel input:focus { outline: none; border-color: var(--accent); }

/* The rupee/percent switch belongs to the label it qualifies. */
.sidepanel .unit {
  width: 24px;
  padding: 2px 0;
  font: var(--font);
  font-weight: 700;
  color: var(--accent);
  background: #eefaf7;
  border: 1px solid #cdeee7;
  border-radius: 5px;
  cursor: pointer;
}

/* Four ways to pay, four identical buttons, edge to edge. */
.sidepanel .pay {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
}
.sidepanel .pay .chip {
  width: 100%;
  padding: 9px 0;
  font-weight: 600;
  text-align: center;
  border-radius: 7px;
}

/* Received lines up with the boxed figures above it. */
.sidepanel .t-right > .row:not(.grand) { padding: 0 11px; }

/* The answer. The only loud thing on the panel. */
.sidepanel .grand {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 13px 14px;
  background: #0f2a2a;
  border-radius: 9px;
}
.sidepanel .grand > span:first-child {
  color: rgba(255, 255, 255, .82);
  font-size: 11.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .6px;
}
.sidepanel .grand b {
  color: #fff;
  font-size: 21px;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.sidepanel .save { width: 100%; padding: 13px; font-size: 15px; }
.sidepanel .link {
  display: block;
  width: 100%;
  text-align: center;
  color: var(--muted);
}

/* Skin 2 — the register: bill in the middle, totals down the side. */
.cl-skin { flex: 1; display: flex; min-height: 0; }
.cl-main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.cl-side {
  width: 268px;
  flex: 0 0 268px;
  border-left: 1px solid var(--line);
  background: var(--surface);
  overflow: auto;
}

/* Skin 3 — the voucher.
 *
 * A page, not a screen. The frame is the blue border a Tally or Marg window
 * has always had; inside it is white paper with a title band, the party in a
 * box on the left, the voucher's own details in a box on the right, a ruled
 * grid and a footer with the amount written out. Every one of those is there
 * because a chemist who has kept books for fifteen years already knows where
 * to look for it. */
.vo-skin {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: #1f3a6e;
  padding: 7px;
}

/* Title band: what this voucher is, whose shop it is, and when. Three parts,
 * the middle one centred, so the shop's name sits in the middle of the page
 * the way it does on the printed copy. */
.vo-head {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  padding: 6px 12px;
  font-size: 12px;
  font-weight: 700;
  color: #fff;
  background: #102450;
  border-bottom: 2px solid #ffd479;
}
.vo-kind { letter-spacing: .09em; text-transform: uppercase; color: #ffd479; }
.vo-shop { font-size: 13.5px; text-align: center; }
.vo-date { font-weight: 400; text-align: right; opacity: .85; }

.vo-body { flex: 1; display: flex; flex-direction: column; min-height: 0; min-width: 0; background: #fff; }

/* The two boxes under the title. The party takes the room; the voucher's own
 * details are a narrow column, because they are four short facts. */
.vo-cols {
  display: flex;
  align-items: stretch;
  border-bottom: 1px solid #b9c6dd;
}
.vo-party { flex: 1; min-width: 0; }
.vo-info { flex: 0 0 216px; border-left: 1px solid #b9c6dd; }

/* The little heading on a boxed block — the printed equivalent of a legend. */
.vo-cap {
  padding: 3px 10px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: #46618f;
  background: #eef2f9;
  border-bottom: 1px solid #dde4ef;
}
/* ── The party block, the way a register draws it ──────────────────
 *
 * The other two skins draw a row of rounded boxes with the label inside them
 * as a placeholder. That is a web form, and it is what a chemist who has kept
 * books in Marg for fifteen years does not recognise: on his screen a field
 * is a caption, a colon and a thin ruled box, and the one he is standing in
 * is lit amber. So that is what this skin draws.
 *
 * A GRID, not a wrapping row. Eight captions and eight boxes cannot share one
 * flex line — the captions took their 82px each and the boxes were squeezed to
 * nothing, so Age, Sex, Doctor and Address were captions with no box after
 * them at all. A grid gives every cell the same width whatever is in it and
 * wraps whole fields onto the next line instead of shaving them.
 *
 * Nothing here changes what the fields ARE — same ids, same values, same
 * saving. Only how they are drawn. */
.vo-party .cust {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
  gap: 0;
  padding: 0;
  border-bottom: 0;
  background: #fff;
}

.vo-party .fld {
  position: relative;
  display: grid;
  grid-template-columns: 88px 1fr;
  align-items: center;
  gap: 0;
  padding: 0;
  min-width: 0;
  border-right: 1px solid #dde4ef;
  border-bottom: 1px solid #dde4ef;
}
/* The two that are read as sentences rather than as values get the room. */
.vo-party .c-name,
.vo-party .c-firm,
.vo-party .c-addr { grid-column: span 2; }

/* The caption. A fixed column so every box on the block starts at the same
 * place — a ragged left edge is what makes a form look assembled rather than
 * drawn. */
.vo-party .fl {
  display: block;
  padding: 6px 8px 6px 10px;
  font-size: 11px;
  font-weight: 600;
  color: #46618f;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.vo-party .fl::after { content: ':'; }

/* Flat, square, and the same height as a line of the grid below it. */
.vo-party .cust input,
.vo-party .cust select {
  width: 100%;
  min-width: 0;
  padding: 6px 8px;
  font-size: 12.5px;
  color: var(--ink);
  background: #fff;
  border: 0;
  border-radius: 0;
}
/* The caption already says it — a placeholder would say it a second time. */
.vo-party .cust input::placeholder { color: transparent; }

/* Where the caret is. Marg lights the live field amber and so does this: on a
 * screen of twenty identical boxes it is the only thing that says where you
 * are, and a browser's own focus ring is a hairline nobody sees. */
.vo-party .cust input:focus,
.vo-party .cust select:focus {
  outline: none;
  color: #3a2c00;
  background: #fff6d8;
  box-shadow: inset 0 0 0 1px #d8a800;
}

.vo-party .taxtag { margin: 5px 10px; align-self: center; justify-self: start; }

/* ── Somebody who has been here before ─────────────────────────────
 *
 * Under the name or the phone box, over everything else. It is drawn by hand
 * rather than by the screen's own redraw, because the party boxes are the one
 * part of billing that must NOT redraw as you type — see the note in
 * billing.js. */
.psug {
  position: absolute;
  z-index: 25;
  left: 0;
  right: 0;
  top: 100%;
  max-height: 260px;
  overflow: auto;
  background: #fff;
  border: 1px solid #b9c6dd;
  box-shadow: 0 12px 28px rgba(16, 36, 80, .22);
}

.prow {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 1px 10px;
  width: 100%;
  padding: 6px 10px;
  font: var(--font);
  text-align: left;
  background: none;
  border: 0;
  border-bottom: 1px solid #eef1f6;
  cursor: pointer;
}
.prow:hover, .prow.on { background: #fff6d8; }
.p-name { font-size: 13px; font-weight: 600; color: var(--ink); }
/* The phone first, because two people share a name far more often than they
 * share a number. */
.p-info {
  grid-column: 1;
  font-size: 11px;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* How often they have been in. A regular is nearly always the right answer. */
.p-bills {
  grid-row: 1 / span 2;
  grid-column: 2;
  align-self: center;
  font-size: 11px;
  color: #46618f;
  white-space: nowrap;
}

/* The other two skins get the same list, in their own clothes. */
.ph-skin .fld, .cl-skin .fld { position: relative; }
.ph-skin .psug, .cl-skin .psug { border-radius: 8px; border-color: var(--line); }
.ph-skin .prow:hover, .ph-skin .prow.on,
.cl-skin .prow:hover, .cl-skin .prow.on { background: #eefaf7; }

/* The item box, drawn the same way. A caption on the left, a flat field
 * beside it, lit amber where the caret is — the same field the party block
 * above it uses, because on a voucher they ARE the same kind of thing. */
.vo-body > .entry {
  padding: 0;
  background: #fff;
  border-bottom: 1px solid #b9c6dd;
}
.vo-body > .entry::before {
  content: 'Item';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  min-width: 82px;
  padding: 0 8px 0 10px;
  font-size: 11px;
  font-weight: 600;
  color: #46618f;
  pointer-events: none;
}
.vo-body > .entry > input {
  padding: 7px 10px 7px 92px;
  font-size: 13px;
  border: 0;
  border-radius: 0;
  background: #fffdf4;
}
.vo-body > .entry > input:focus {
  background: #fff6d8;
  box-shadow: inset 0 0 0 1px #d8a800;
}
/* The list of matches is part of the page here, not a floating card. */
.vo-body .suggest {
  left: 0;
  right: 0;
  top: 100%;
  border-radius: 0;
  border-color: #b9c6dd;
  box-shadow: 0 10px 24px rgba(16, 36, 80, .22);
}


.vo-meta { padding: 6px 10px 8px; }
.vm-row {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding: 2px 0;
  font-size: 12px;
  color: var(--muted);
}
.vm-row b { color: var(--ink); font-variant-numeric: tabular-nums; }
.vm-row .dim { font-weight: 400; color: var(--muted); font-style: italic; }

/* The grid. Ruled all the way down, and every column separated — a register
 * is read across, and a table with no vertical rules is read down. */
.vo-grid { flex: 1; }
.vo-grid thead th { background: #dfe6f2; color: #21365e; border-bottom: 1px solid #b9c6dd; }
.vo-grid th + th, .vo-grid td + td { border-left: 1px solid #e6eaf2; }
.vo-grid td { border-bottom: 1px solid #eceff5; }
.vo-grid tbody tr:nth-child(even) { background: transparent; }
.vo-grid tbody tr.over { background: #fff7ed; }
/* The voucher grid clears its own striping with `tr:nth-child(even)`, which
 * outranks the returned-row tint the same way — and clears it to
 * TRANSPARENT, so on this skin the mark disappeared rather than merely
 * washing out. Restated here at the weight the grid uses. */
.vo-grid tbody tr.gaveback {
  background: #fff1f1;
  box-shadow: inset 3px 0 0 var(--danger);
}
.vo-grid tbody tr.gaveback td { border-bottom-color: #f6cccc; }
/* The empty rows that keep it a full page. They are ruled and nothing else:
 * a filler row that could be clicked or typed into would be a row a chemist
 * fights with. */
.vo-grid tr.fill td { height: 25px; color: transparent; pointer-events: none; }

/* The foot: what it comes to, in words, over the figures. */
.vo-foot { background: #fff; border-top: 1px solid #b9c6dd; }
.vo-words {
  padding: 6px 12px 5px;
  font-size: 12px;
  color: var(--ink);
  border-bottom: 1px dashed #d9e0ec;
}
.vo-words span {
  margin-right: 8px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--muted);
}

/* The key strip.
 *
 * It is at the foot because that is where the eye goes when it does not know
 * what to do next, and each one is a real button: somebody who has not
 * learned the keys yet can click it and watch where the caret lands. That is
 * how the keys get learned. */
.vo-keys {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  padding: 4px 4px 2px;
  /* Two rows at most. Nine keys used to wrap onto as many rows as the window
   * allowed, and every one of those rows came off the bill — which is the
   * part of the screen anybody is actually reading. */
  max-height: 30px;
  overflow: hidden;
}
.vk {
  display: flex;
  align-items: baseline;
  gap: 4px;
  padding: 2px 6px;
  font: var(--font);
  font-size: 11px;
  color: #cbd8ee;
  background: #16305c;
  border: 1px solid #2c4a80;
  border-radius: 4px;
  cursor: pointer;
  white-space: nowrap;
}
.vk:hover { color: #fff; background: #24467d; }
/* Printed, not offered: this key is pressed on the item box and there is
 * nothing here for a click to act on. It must not look like a button. */
.vk.flat { cursor: default; background: transparent; border-color: #2c4a80; }
.vk.flat:hover { color: #cbd8ee; background: transparent; }
.vk b { font-size: 11px; color: #ffd479; }
.vk span { font-size: 11px; }

.keytable {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 4px 18px;
  margin-top: 12px;
}
.keytable div { display: flex; align-items: center; gap: 9px; padding: 3px 0; }
.keytable kbd {
  flex: 0 0 86px;
  padding: 3px 6px;
  font: 11.5px/1.3 var(--font);
  text-align: center;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--line);
  border-bottom-width: 2px;
  border-radius: 4px;
}
.keytable span { font-size: 12.5px; }

/* Dialogs */
.modal-back {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: 20px;
  background: rgba(10, 20, 25, .45);
}
.modal {
  /* Wide enough for the batch list, which is the widest thing that opens in
     one: a batch number, an expiry, a rate, and how many are left WITH the
     unit named — because that row means a different thing depending on which
     way "Sell as" is set, and the unit is what says which. At 420 the last
     column fell off the end. */
  width: 520px;
  max-width: 100%;
  max-height: 88vh;
  overflow: auto;
  padding: 20px 22px;
  background: #fff;
  border-radius: 10px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .3);
}
.modal h3 { margin: 0 0 4px; font-size: 17px; }
.modal h4 {
  margin: 18px 0 6px;
  font-size: 13px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .4px;
}
.modal .sub { margin: 0 0 14px; font-size: 12.5px; color: var(--muted); }
.modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 18px; }
.modal-actions .primary, .modal-actions .ghost { width: auto; margin: 0; padding: 9px 18px; }

.batch-list { display: flex; flex-direction: column; gap: 6px; }
.batch-row {
  display: grid;
  /* The last column names the unit as well as the count — "4 Strip left", not
   * "4 left" — because the same row means two different things depending on
   * which way "Sell as" is set. It needs the room to say so. */
  grid-template-columns: 22px 1fr 96px 88px 104px;
  gap: 10px;
  align-items: center;
  padding: 10px 12px;
  font: var(--font);
  text-align: left;
  background: #f7f9fc;
  border: 1px solid var(--line);
  border-radius: 8px;
  cursor: pointer;
}
.batch-row:hover { background: #eefaf7; border-color: var(--accent); }
/* The row the arrows are standing on. :focus alone is not enough — a browser
   focus ring reads as an accident, not as "this one will be taken". */
.batch-row.picked, .batch-row:focus {
  background: #eefaf7;
  border-color: var(--accent);
  outline: 2px solid var(--accent);
  outline-offset: -1px;
}
.b-key {
  font-size: 11px;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 1px 5px;
  text-align: center;
  background: #fff;
}
.b-no { font-weight: 700; }
/* The price this batch is charged at — the figure being compared across rows. */
.b-mrp { font-weight: 700; text-align: right; }
.b-mrp small { font-weight: 500; font-size: 10.5px; color: var(--muted); }

/* ── Whole pack, or cut units ──────────────────────────────────────── */
/* Two buttons carrying their own price, above the batches. It was a tick-box
 * labelled "Selling loose tablets" with no rate on it, which asked the chemist
 * to know the box was there and to divide in their head. */
.sellas {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: 12px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line);
}
.sa-lbl { font-size: 12.5px; color: var(--muted); }
.sa-mode {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 7px 14px;
  font: var(--font);
  text-align: left;
  background: #f7f9fc;
  border: 1px solid var(--line);
  border-radius: 8px;
  cursor: pointer;
}
.sa-mode b { font-size: 13px; }
.sa-mode span { font-size: 12px; color: var(--muted); }
.sa-mode:hover { border-color: var(--accent); }
/* The one in force. Filled, not merely outlined — at a counter the question
 * "which of these two am I on" has to be answerable from across the room. */
.sa-mode.on {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.sa-mode.on span { color: rgba(255, 255, 255, .85); }
.sa-note { margin-left: auto; font-size: 12px; color: var(--muted); }
.sa-note kbd {
  padding: 1px 5px;
  font-size: 11px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 4px;
}

/* The quantity and its unit, on one line. The select is small and quiet — it
 * is right by the figure being read, and a full-size control there would look
 * like the thing to fill in rather than the thing to check. */
.qtycell { white-space: nowrap; }
.qtycell .unitsel {
  margin-left: 5px;
  padding: 2px 3px;
  font: var(--font);
  font-size: 11.5px;
  color: var(--muted);
  vertical-align: middle;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 3px;
  cursor: pointer;
}
.qtycell .unitsel:hover { border-color: var(--accent); color: var(--ink); }
.qtycell .cell { width: 58px; }

/* The cut rate, under the whole-pack rate, in the item list. Small and grey:
 * it is an answer to a question most sales do not ask, and it must not compete
 * with the figure that is being compared across rows. */
.s-loose {
  display: block;
  margin-top: 1px;
  font-size: 10.5px;
  font-weight: 500;
  color: var(--muted);
  white-space: nowrap;
}
.s-loose kbd {
  margin-left: 3px;
  padding: 0 3px;
  font-size: 9.5px;
  border: 1px solid var(--line);
  border-radius: 3px;
}
.b-exp, .b-qty { font-size: 12px; color: var(--muted); }
.b-qty { text-align: right; }
.toast {
  position: fixed;
  left: 50%;
  bottom: 26px;
  z-index: 80;
  transform: translateX(-50%);
  padding: 11px 18px;
  font-size: 13px;
  color: #fff;
  background: #15803d;
  border-radius: 8px;
  box-shadow: 0 10px 26px rgba(0, 0, 0, .25);
}
.toast.bad { background: var(--danger); }

/* A toast that can be acted on. The button is inside the message rather than
 * beside it, so it moves with the toast and cannot be missed by somebody
 * reading the sentence it belongs to. */
.toast-act {
  margin-left: 12px;
  padding: 4px 11px;
  font: inherit;
  font-size: 12.5px;
  color: #fff;
  background: rgba(255, 255, 255, .18);
  border: 1px solid rgba(255, 255, 255, .55);
  border-radius: 5px;
  cursor: pointer;
}
.toast-act:hover { background: rgba(255, 255, 255, .3); }


/* The patient block while billing.
 *
 * Widths are set per field so eight boxes fit one row and none of them is
 * bigger than what goes in it — an “Age” box the width of a name is a box
 * that looks broken. */
.cust .c-name { flex: 2 1 180px; }
.cust .c-phone { flex: 0 1 130px; }
.cust .c-age { flex: 0 1 74px; }
/* The bill's own date. Narrow, and first in the row: it is the one field on
   this bar that is usually left alone, so it should not push the name and
   phone out of reach of somebody billing at speed. */
.cust .c-date { flex: 0 1 150px; }
.cust .c-sex { flex: 0 0 56px; }
.cust .c-reg { flex: 0 1 110px; }
.cust .c-doc { flex: 1 1 130px; }
.cust .c-addr { flex: 1 1 140px; }


/* The trade this bill belongs to, and the buyer's row.
 *
 * Only a counter that does both ever sees the switch — for a retail chemist
 * or a distributor it is not a question, and a question nobody has is a
 * control in the way. */
.tradebar {
  display: flex;
  gap: 6px;
  padding: 6px 10px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.cust.wholesale { background: #f6f8ff; }
.cust .c-firm { flex: 2 1 190px; }
.cust .c-gst { flex: 0 1 165px; text-transform: uppercase; }
.cust .c-dl { flex: 1 1 140px; }

/* Which tax head this sale falls under, worked out from the two GSTINs and
 * shown rather than asked. A distributor billing four states in a morning
 * cannot be expected to remember a switch. */
.taxtag {
  align-self: center;
  padding: 4px 9px;
  font-size: 11px;
  font-weight: 700;
  color: #0b3b34;
  background: #dff5ef;
  border-radius: 999px;
  white-space: nowrap;
}
.taxtag.inter { color: #7c2d12; background: #ffedd5; }


/* The national list, where it shows itself: as suggestions under the medicine
 * name, and as a second half of the billing search when the shelf has nothing. */
.m-suggest {
  position: absolute;
  z-index: 30;
  left: 0; right: 0; top: 100%;
  max-height: 300px;
  overflow: auto;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: 0 14px 34px rgba(0, 0, 0, .16);
}
.m-sug {
  display: grid;
  grid-template-columns: 1fr 150px 1fr 110px 80px;
  gap: 10px;
  align-items: center;
  width: 100%;
  padding: 7px 10px;
  font: var(--font);
  text-align: left;
  background: none;
  border: 0;
  border-bottom: 1px solid #f2f4f8;
  cursor: pointer;
}
.m-sug:hover, .m-sug.on { background: #eefaf7; }
.ms-name { font-weight: 600; }
.ms-mfr, .ms-salt, .ms-pack { font-size: 11px; color: var(--muted); }
.ms-mrp { font-size: 11.5px; font-weight: 700; text-align: right; }

.sug-head {
  padding: 6px 11px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .4px;
  text-transform: uppercase;
  color: var(--warn);
  background: #fff7ed;
  border-bottom: 1px solid #fde3c4;
}
.sug-none { padding: 14px 11px; font-size: 12px; color: var(--muted); }
.suggest.frommaster .sug.ms { grid-template-columns: 1fr 150px 1fr 90px 80px; }

.ms-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 14px; margin-top: 8px; }

/* ── The purchase voucher ─────────────────────────────────────────────
   The same cloth as the sales voucher: dense grid, ruled rows, keys on
   show. What is new is only what a purchase asks that a sale does not —
   the supplier strip on top and the paid/due strip at the foot. */
.pu-skin .vo-date { font-size: 12px; color: var(--muted); }
.pu-party { display: flex; gap: 12px; margin-bottom: 8px; }
.pu-party label {
  display: flex; flex-direction: column; gap: 3px;
  font-size: 11.5px; color: var(--muted); font-weight: 600;
}
.pu-party label:first-child { flex: 1; }
.pu-party input { padding: 7px 9px; font: var(--font); border: 1px solid var(--line); border-radius: 6px; }
.pu-grid input { width: 74px; }
.pu-grid input[data-batch] { width: 96px; }
.pu-name { font-weight: 700; }
/* The figure the whole screen exists to get right. */
.pu-cost { font-weight: 700; color: var(--accent); }
.pu-none { text-align: center; color: var(--muted); padding: 26px 0; }
.pu-foot { display: flex; flex-direction: column; gap: 8px; }
.pu-tot {
  display: flex; gap: 16px; align-items: center; justify-content: flex-end;
  font-size: 13px;
}
.pu-tot label { display: flex; gap: 6px; align-items: center; font-size: 12px; color: var(--muted); }
.pu-tot input { width: 90px; padding: 6px 8px; font: var(--font); border: 1px solid var(--line); border-radius: 6px; }
/* The round-off tick and what it did. The amount is shown beside it because a
 * total that quietly moved by a rupee is a total somebody has to reconcile by
 * hand later. */
.pu-tot .pu-round { cursor: pointer; white-space: nowrap; }
.pu-tot .pu-round input { width: auto; }
.pu-tot .pu-roundamt { color: var(--ink); font-weight: 600; }
.pu-tot b { font-size: 16px; }
.pu-pay { display: flex; gap: 8px; align-items: center; justify-content: flex-end; flex-wrap: wrap; }
.pu-pay input { width: 110px; padding: 6px 8px; font: var(--font); border: 1px solid var(--line); border-radius: 6px; }
.pu-pay .primary { width: auto; margin: 0; padding: 9px 18px; }
/* The number the supplier's man reads out at the next delivery. */
.pu-due { font-size: 12.5px; font-weight: 700; color: #b06000; }

/* ── Narrower than a desktop ──────────────────────────────────────
 *
 * A counter machine is often a 13-inch laptop, and both of the two-column
 * skins had a fixed side panel: the register's totals at 268px and the
 * voucher's details at 216px. Below about a thousand pixels the bill itself —
 * the part being read and typed into — was down to a third of the window
 * while the panel kept every pixel it had asked for. */
@media (max-width: 1080px) {
  /* The register's totals go under the bill. They are read at the END of a
   * sale; the item grid is read all the way through it. */
  .cl-skin { flex-direction: column; }
  .cl-side {
    width: auto;
    flex: 0 0 auto;
    border-left: 0;
    border-top: 1px solid var(--line);
  }
  .sidepanel { flex-direction: row; flex-wrap: wrap; height: auto; align-items: flex-end; }
  .sidepanel .t-left, .sidepanel .t-right { flex: 1 1 260px; }

  /* The voucher's own details drop under the party block. */
  .vo-cols { flex-wrap: wrap; }
  .vo-info { flex: 1 1 100%; border-left: 0; border-top: 1px solid #b9c6dd; }
  .vo-meta { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 0 14px; }
}

@media (max-width: 860px) {
  /* The party block goes to one field per row rather than shaving each of
   * them until nothing can be read. */
  .vo-party .cust { grid-template-columns: 1fr; }
  .skinbar { flex-wrap: wrap; }
  .vo-keys { max-height: 64px; overflow: auto; }
}

/* The rate a firm is charged.
 *
 * It looks exactly like a retail rate until it is marked, and the difference
 * between the two IS the firm's reason to buy here rather than anywhere else.
 * So the cell says which one it is — quietly, because it is the normal case
 * on a wholesale bill and a whole column shouting is a column nobody reads. */
.bill td.rate.trade { position: relative; }
.bill td.rate.trade input { color: #0f766e; font-weight: 600; }
.bill td.rate.trade::after {
  content: 'trade';
  position: absolute;
  top: 1px;
  left: 4px;
  font-size: 8.5px;
  font-weight: 700;
  letter-spacing: .06em;
  color: #0f766e;
  opacity: .75;
  text-transform: uppercase;
  pointer-events: none;
}
/* Under cost is louder, because it is not the normal case. A line that loses
 * money on every strip otherwise reads as an ordinary sale. */
.bill td.rate.undercost { background: #fff7ed; }
.bill td.rate.undercost input { color: #b45309; }
.bill td.rate.undercost::after { content: none; }
.bill td.rate .uc {
  display: block;
  margin-top: 1px;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: .04em;
  color: #b45309;
  text-transform: uppercase;
}

/* The same answer in the dropdown: the rate THIS bill would charge, not the
 * printed MRP, because that is the number a firm is choosing on. */
.suggest .s-rate.trade { color: #0f766e; }
