/* ==========================================================================
 * pos-rowentry.css — the POS line-entry ROW (design P1, step 1: layout only).
 *
 * WHAT THIS IS
 * The sell screen composes ONE cart line across 13 stacked controls and ~600px
 * of screen, five of which are readonly display fields that still take a Tab
 * (readonly is not disabled). This stylesheet re-flows those SAME controls into
 * a single horizontal strip: the four fields a cashier actually types, two
 * read-only badges, and the Add/Cancel buttons — all on one row, above the cart.
 *
 * THE ONE RULE THAT MAKES IT SAFE
 * Nothing here changes the markup's CONTENT. Every id stays in the DOM, in
 * exactly one copy, in its original position. This is a pure re-flow, so
 * formToJSON("Sell"), #addInviceItem, calculateNetSell() and loadStock() keep
 * reading and writing the same ids with no modification whatsoever. A second
 * copy of #sellItems living in an alternate layout would be a duplicate id and
 * a silently wrong invoice — hence "re-flow", never "rebuild".
 *
 * EVERYTHING IS SCOPED TO .pos-rowentry
 * The class is put on #sellDiv by loadPosFeatureFlags() only when the org has
 * turned pos.keyboard.enabled ON. With the flag off, not one declaration in
 * this file matches anything, and the screen is byte-identical to today. That
 * is the whole safety story: to revert, remove the class.
 *
 * WHY display:contents
 * The controls are spread across sibling Bootstrap-3 .form-group blocks, each
 * holding <label> + <div class="col-sm-*"> pairs. Making the wrappers
 * display:contents promotes labels and field-columns to direct children of one
 * flex container WITHOUT moving a single node in the DOM. The alternative —
 * physically regrouping the markup — is the rebuild this file exists to avoid.
 *
 * FIELD ORDER IS THE DOM ORDER, DELIBERATELY
 * The strip reads:  Item · Serial · Qty · Stock · Expiry · Price · Total · Disc
 * Stock/Expiry land next to Qty rather than at the end because that is where
 * the DOM already puts them, and no CSS reordering is needed. It also happens
 * to be the better arrangement: on-hand sits beside the quantity being typed,
 * which is exactly when a cashier needs to see it.
 *
 * SER-3d moved Serial in front of Qty — in the markup, not with CSS `order`,
 * so the tab sequence and the visual sequence stay the same thing. It goes
 * first because it DECIDES the quantity: entering a serial locks Qty to 1, and
 * with the old order the cashier typed a number and watched it be overwritten.
 * Serial is hidden entirely for a tenant without serialTracking, or one that
 * switched pos.entry.showSerial off, so most tills never see this column.
 *
 * PHONES KEEP THE STACKED FORM
 * Below 992px every rule here is switched off. A seven-column strip on a phone
 * is unusable, and 991 is the existing breakpoint in responsive.css.
 * ========================================================================== */

/* ── Per-tenant field visibility (NOT scoped to .pos-rowentry) ─────────────
 * One POS is sold to a corner shop, a wholesale distributor and a pharmacy, so
 * which fields belong on a sale is the tenant's answer. Controls that can be
 * switched off carry `data-pos-field="<name>"` on their `.pos-cell` — ONE
 * element holding both the caption and the field, so they cannot be hidden out
 * of step — and applyPosFieldVisibility() adds this class to the ones the org
 * turned off. (It used to be repeated on the label AND the column, which worked
 * only for as long as nobody edited one of the two.)
 *
 * Why a class and not jQuery .hide()/.toggle(): those write an INLINE display,
 * and an inline display:block beats the .pos-more rule further down, which
 * would drag fields back onto the compact row. Config decides IF a field
 * exists; the row layout decides WHERE. Neither writes inline styles the other
 * then has to fight.
 *
 * !important because it must also beat .pos-rowentry's `display:contents` on
 * the wrappers — "the shop switched this off" outranks any layout rule.
 *
 * The control itself is NEVER removed or disabled: see the FormData note below.
 */
.pos-hidden{ display:none !important; }

/* ── Keyboard flow hint ────────────────────────────────────────────────────
 * One quiet line stating the whole path: item → line → payment → done. Shown
 * only while the keyboard feature is on. Deliberately understated — it is a
 * reference for the first week, not a banner to read on every sale.
 */
.sell-kbdhint{ margin-top:6px; }
.sell-kbdhint > div{
  display:flex; flex-wrap:wrap; align-items:center; gap:6px 10px;
  padding:9px 14px; border:1px dashed #d3dceb; border-radius:9px;
  background:#f7f9fc; font-size:11.5px; color:#5a6677;
}
.sell-kbdhint .kh-step{ display:inline-flex; align-items:center; gap:5px; }
.sell-kbdhint .kh-step b{ color:#22303f; font-weight:700; }
.sell-kbdhint .kh-arrow{ color:#95a2b5; }
.sell-kbdhint .kh-esc{ margin-left:auto; display:inline-flex; align-items:center; gap:5px; color:#7a889c; }
.sell-kbdhint kbd{
  font-family:ui-monospace,Consolas,monospace; font-size:10.5px; font-weight:700;
  color:#0D3B8C; background:#fff; border:1px solid #c3cddd; border-bottom-width:2px;
  border-radius:4px; padding:1px 6px; line-height:1.4;
}
/* A phone has no keyboard flow to speak of — the hint is desktop-till guidance. */
@media (max-width:991px){ .sell-kbdhint{ display:none !important; } }

/* Where the cursor IS matters more than usual when the whole sale is driven by Enter: the operator
   is not looking at the mouse to know where they are. A ring on the focused control, everywhere on
   this screen, so the next keystroke is never a guess. */
#sellDiv input:focus, #sellDiv select:focus,
#sellDiv .bootstrap-select.open > .btn, #sellDiv .bootstrap-select > .btn:focus{
  border-color:#1565C0 !important;
  box-shadow:0 0 0 3px rgba(21,101,192,.15) !important;
  outline:none;
}

/* ── Quick-pick tiles (P3) ─────────────────────────────────────────────────
 * The shop's best sellers, above the cart, one keystroke each. NOT scoped to
 * .pos-rowentry: quick pick is its own setting and works with either layout.
 * The wrapper is display:none in the markup and shown by renderQuickPick().
 */
.qp-wrap{
  border:1px solid #e6eaf0; border-radius:10px; background:#f7f9fc;
  padding:10px 12px; margin:12px 0;
}
.qp-head{
  display:flex; align-items:baseline; gap:10px; flex-wrap:wrap;
  font-weight:700; font-size:13px; color:#0D3B8C; margin-bottom:8px;
}
.qp-head .qp-hint{ font-weight:normal; font-size:11px; color:#7a889c; }
.qp-grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(132px,1fr)); gap:8px; }
.qp-tile{
  position:relative; text-align:left; cursor:pointer;
  border:1px solid #d3dceb; border-radius:9px; background:#fff;
  padding:10px 11px; min-height:62px;
  display:flex; flex-direction:column; gap:4px;
}
.qp-tile:hover{ border-color:#1565C0; background:#eef3fb; }
.qp-tile:active{ transform:translateY(1px); }
.qp-tile .qp-key{
  position:absolute; top:6px; right:6px;
  font-size:10px; font-weight:700; color:#1565C0; background:#e8f0fb;
  border:1px solid #1565C0; border-radius:4px; padding:1px 5px; line-height:1.3;
}
.qp-tile .qp-name{
  font-size:12px; font-weight:700; color:#22303f; line-height:1.3;
  padding-right:34px;                       /* clear of the key badge */
  overflow-wrap:anywhere;
}
.qp-tile .qp-price{ font-size:11.5px; font-weight:700; color:#0f6e56; font-variant-numeric:tabular-nums; }
.qp-msg{ font-size:12px; color:#7a889c; margin-top:6px; }

/* Touch tills: no Alt key to press, so the badge is noise — and a bigger tap target matters more. */
@media (hover:none){
  .qp-tile .qp-key{ display:none; }
  .qp-tile .qp-name{ padding-right:0; }
  .qp-tile{ min-height:70px; }
}

/* ── The strip ─────────────────────────────────────────────────────────────
 * One flex row. `align-items:flex-end` keeps the inputs on a common baseline
 * even though their labels differ in length and some wrap to two lines.
 */
.pos-rowentry #Sell{
  display:flex;
  flex-wrap:wrap;
  align-items:flex-end;
  gap:10px 12px;
  padding:12px 14px;
  background:#f7f9fc;
  border:1px solid #e6eaf0;
  border-radius:10px;
}

/* ── The CELL ──────────────────────────────────────────────────────────────
 * Each label and its field live in ONE .pos-cell. In the CLASSIC Bootstrap
 * layout the cell must be invisible — `display:contents` makes the browser
 * treat the label and the .col-* as though the wrapper were not there, so the
 * horizontal form is byte-for-byte what it always was. In the row layout the
 * same cell becomes a little column: caption on top, field beneath.
 *
 * WHY A CELL AT ALL. The captions used to be siblings of their fields, held
 * beside them by negative margins — the comment this replaces admitted flex has
 * no "keep these two adjacent" primitive. Worse, per-tenant visibility needed
 * `data-pos-field` repeated on the label AND the column, and the pair only
 * stayed together while nobody edited one of them. One element cannot come
 * apart from itself: the attribute now sits on the cell, and hiding it takes
 * the caption and the field together in BOTH layouts.
 */
.pos-cell{ display:contents; }

.pos-rowentry #Sell .pos-cell{
  display:flex;
  flex-direction:column;
  gap:4px;
  min-width:0;                    /* a flex item defaults to min-width:auto and
                                     would refuse to shrink, forcing a page scroll */
}

/* The remaining wrappers still dissolve: .form-group groups cells, it is not a cell. */
.pos-rowentry #Sell .form-group{
  display:contents;
}

/* Field columns: Bootstrap floats them at fixed percentage widths. Neither
 * applies in a flex row — clear both and let each field state its own width. */
.pos-rowentry #Sell [class*="col-sm-"],
.pos-rowentry #Sell [class*="col-xs-"],
.pos-rowentry #Sell [class*="col-md-"]{
  float:none;
  width:auto;
  padding:0;
  flex:0 0 auto;
  min-width:0;                    /* a flex item's default min-width:auto would
                                     refuse to shrink and force a page scroll */
}

/* Labels sit ABOVE their field as a small caption. `display:block` + order is
 * not needed: label and field are already adjacent in the DOM, so wrapping the
 * pair is achieved by giving the label a full-width break before its field. */
.pos-rowentry #Sell .control-label{
  float:none;
  width:auto;
  padding:0 0 3px;
  text-align:left;
  font-size:10px;
  font-weight:700;
  letter-spacing:.06em;
  text-transform:uppercase;
  color:#7a889c;
  /* align-self was flex-end, which was right for the OLD flex ROW: the cross axis ran vertically and
     flex-end sat the caption on the row's baseline. Inside a .pos-cell the direction is COLUMN, so the
     cross axis is HORIZONTAL and flex-end pushed every caption to the right edge of its own field —
     a ~40px indent on each one. Stretch puts the caption over the field it belongs to. */
  align-self:stretch;
  line-height:1.2;
}
/* No more negative margins: a caption cannot drift from its field, because it is
 * inside it. What the caption DOES need is a common height, so that every one of
 * them sits on a single band across the strip — that band is the whole point of
 * the table look, and one label wrapping to two lines is what used to break it. */
.pos-rowentry #Sell .pos-cell > .control-label{
  height:13px;
  overflow:hidden;
  white-space:nowrap;
  text-overflow:ellipsis;
  margin:0;
}

/* ── Field widths ──────────────────────────────────────────────────────────
 * Explicit rather than proportional: a POS row is read by position, and columns
 * that resize as values are typed make it unreadable. Item is the only elastic
 * one because product names vary wildly in length.
 */
.pos-rowentry #Sell .form-control{
  height:36px;
  padding:6px 9px;
  font-size:13px;
}
.pos-rowentry #sellItemDD,
.pos-rowentry #Sell .bootstrap-select{ width:100% !important; }
.pos-rowentry #Sell .bootstrap-select > .btn{ height:36px; padding:7px 9px; font-size:13px; }

/* The Item column carries the picker. It has a generous FIXED width and does NOT grow: a column that
 * absorbs spare width stretches the strip until the later fields wrap onto a second line, which is
 * exactly how Stock/Expiry/Discount/Type ended up on a band of their own with a gap in front of them.
 * A till is read by position, and a column that resizes as the window does is a column nobody can
 * learn the position of. */
.pos-rowentry #Sell .pos-cell-item{ flex:0 0 210px; }
.pos-rowentry #Sell .pos-cell-item .col-sm-4{ width:100%; }

/* SER-3d — the serial sits between Item and Qty, and it was the ONE typed field in the strip with no
 * stated width. Every other one is explicit for the reason at the top of this block: a till is read by
 * POSITION, and a column that sizes itself to its content is a column nobody can learn the position of.
 * Left unset, a text input falls back to the browser's ~20-character default and pushed the fields after
 * it onto a second band.
 *
 * 148px fits a 15-digit IMEI at 13px without clipping, which is what this field is for. It does not grow:
 * an over-long code scrolls inside the box rather than widening the strip mid-scan.
 */
.pos-rowentry #sellSerials{ width:148px; }

.pos-rowentry #sellItems{ width:72px; }
.pos-rowentry #sellSellRate{ width:98px; }
.pos-rowentry #sellDiscount{ width:84px; }
.pos-rowentry #sellStock,
.pos-rowentry #bexpDate,
.pos-rowentry #sellTotalAmount{ width:96px; }

/* ── Read-only values become badges, not inputs ────────────────────────────
 * This is the part that removes the dead tab stops STRUCTURALLY. They are still
 * <input readonly> elements — still written by calculateNetSell() and
 * loadStock() exactly as before — but they no longer look or behave like
 * something you can type into. (Taking them OUT of the tab order is a
 * tabindex attribute, which belongs to the behaviour step, not this file.)
 */
.pos-rowentry #sellStock,
.pos-rowentry #bexpDate,
.pos-rowentry #sellTotalAmount{
  border:1px solid #e6eaf0;
  background:#eef3fb;
  color:#22303f;
  font-weight:700;
  font-variant-numeric:tabular-nums;
  box-shadow:none;
  cursor:default;
}

/* ── Fields that move off the row ──────────────────────────────────────────
 * Description, Bonus, discount TYPE and Receiveable are rarely touched during a
 * sale and cost a tab stop each. They are hidden from the strip, NOT removed:
 * every one is still in the DOM and still written to, so the submitted payload
 * is unchanged. They are marked with .pos-more on their .pos-cell, so this file
 * addresses caption and field together by addressing the one element that is both.
 *
 * (Step 2 gives .pos-more a "More ▾" popover. Until then these are simply off
 * the row — which is already the correct behaviour for a scan-driven till.)
 *
 * ⚠ WHY display:none IS SAFE HERE, AND WHAT WOULD NOT BE
 * formToJSON("Sell") builds the payload from `new FormData(form)`. FormData
 * omits DISABLED controls but includes ones hidden with display:none — so every
 * value below is still submitted, byte for byte, exactly as when the field was
 * visible:
 *     #sellItemDesc        name="description"              → in FormData
 *     #sellDiscountTypeDD  name="stock.bsellDiscountType"  → in FormData
 *     #sellBonus           no name — read by $("#sellBonus").val()  → unaffected
 *     #sellrm              no name — written by calculateNetSell()  → unaffected
 * Verified too: none of the four carries `required`, so hiding them cannot make
 * validateForm()'s form.checkValidity() fail on a control the user cannot see.
 *
 * So do NOT "tidy" this by adding `disabled`, by removing the elements, or by
 * moving them outside <form id="Sell">. Any of those silently drops a field
 * from the invoice. Hidden-but-present is the whole trick.
 */
.pos-rowentry #Sell .pos-more{ display:none; }

/* The FEFO batch/expiry notices and the scan box are full-width: they are
 * messages about the row, not columns within it.
 *
 * BUT ONLY WHEN THEY HAVE SOMETHING TO SAY. `display:block` was unconditional, so the two batch-info
 * wrappers — empty on a fresh screen, and empty for every shop that does not track batches — still
 * claimed a full-width flex line EACH. The strip was cut into four bands with ~170px of blank space
 * between them, which is what the row layout looked like on a real till.
 *
 * `:has()` is the honest test: the wrapper is a container whose CHILDREN are the alerts, and those are
 * shown and hidden individually by loadStock(). A wrapper with no visible alert inside it is not a
 * message, so it takes no line. Supported everywhere this app runs; a browser without it simply gets
 * the previous behaviour, which is a spaced-out row rather than a broken one.
 */
.pos-rowentry #Sell .pos-fullrow{
  flex:1 0 100%;
  display:block;
  width:100%;
}

/* ── Painting ORDER, so a full-width block never splits the field strip ─────
 * The scan box LEADS and the notice FOLLOWS the fields. In the DOM the notice sits between the QTY
 * group and the price group — put there because that is where it is populated from — and a full-width
 * flex item in the middle of a wrapping row cuts the row in half. That is exactly what happened: the
 * strip rendered as four bands with ~170px of dead space between them.
 *
 * `order` moves PAINTING only. Tab order, the Enter chain and formToJSON all read the DOM, so none of
 * them can notice — which is what makes this safe to do to a live till.
 */
.pos-rowentry #Sell #sellScanRow{ order:-1; }   /* leads */
.pos-rowentry #Sell .pos-cell,
.pos-rowentry #Sell .form-group.text-center{ order:0; }
.pos-rowentry #Sell .pos-fullrow:not(#sellScanRow){ order:1; }   /* follows the fields */

/* The NOTICE row, when it has nothing to say.
 *
 * A full-width block sitting BETWEEN the QTY fields and the price fields cuts the strip in two, so an
 * EMPTY notice must take no line at all. The class is written by syncSellNoticeRow() in business.js,
 * which already owns showing and hiding the two alerts inside it.
 *
 * A `:has()` selector was tried first and did not work: the wrapper's inner .col-* is itself a flex
 * item and takes a line whether or not its children are visible. Inferring the state from inline
 * styles was a second mechanism guessing at what the first one already knows. */
.pos-rowentry #Sell .pos-fullrow.pos-notice-empty{ display:none; }

/* ── Action buttons, at the end of the row ─────────────────────────────────
 * "Add to Cart" and "Cancel" ride on the same row as the fields, so committing
 * a line never means travelling to a different part of the screen.
 */
.pos-rowentry #Sell .form-group.text-center{ display:contents; }
.pos-rowentry #addInviceItem,
.pos-rowentry #resetInviceItem{
  height:36px;
  padding:7px 14px;
  font-size:12px;
  font-weight:700;
  white-space:nowrap;
  margin-left:2px;
}
/* NO `margin-left:auto` here. It was meant to push the buttons to the end of the strip, and on a flex
 * row an auto margin absorbs ALL the free space — so the buttons went to the far right and everything
 * after them was forced onto the next line. The buttons simply follow the last field; with the fields
 * at fixed widths the strip ends where the content ends, which is what a till should look like. */
.pos-rowentry #addInviceItem{ margin-left:8px; }

/* The contract-price explanation sits under the price field and must not widen
 * the column or push the row taller when it appears. */
.pos-rowentry #sellPriceReason{
  display:block;
  font-size:10px;
  line-height:1.25;
  margin:2px 0 0;
  max-width:160px;
}

/* ── Phones and small tablets: revert to the stacked form ──────────────────
 * Everything above is undone. A strip needs horizontal room the device does not
 * have, and the existing stacked layout is already responsive.
 */
@media (max-width:991px){
  .pos-rowentry #Sell{
    display:block;
    padding:0;
    background:none;
    border:0;
    border-radius:0;
  }
  .pos-rowentry #Sell .form-group,
  .pos-rowentry #Sell .form-group.text-center{ display:block; }
  /* The cell dissolves again on a phone. Here the classic stacked form is the right layout, and it
     needs the label and the .col-* to be children of the .form-group — which is exactly what
     display:contents restores. Leaving the cell as a flex column would keep the clipped 13px caption
     band, which is a till affordance and wrong on a stacked form. */
  .pos-rowentry #Sell .pos-cell,
  .pos-rowentry #Sell .pos-cell.pos-more{ display:contents; }
  .pos-rowentry #Sell .pos-more{ display:block; }
  .pos-rowentry #Sell .pos-cell > .control-label{
    height:auto; overflow:visible; white-space:normal;
  }
  .pos-rowentry #Sell .control-label{
    margin-left:0;
    margin-right:0;
    font-size:inherit;
    letter-spacing:normal;
    text-transform:none;
    color:inherit;
    padding-top:7px;
  }
  .pos-rowentry #Sell [class*="col-sm-"],
  .pos-rowentry #Sell [class*="col-xs-"],
  .pos-rowentry #Sell [class*="col-md-"]{ width:100%; }
  .pos-rowentry #sellItems,
  .pos-rowentry #sellSellRate,
  .pos-rowentry #sellDiscount,
  .pos-rowentry #sellStock,
  .pos-rowentry #bexpDate,
  .pos-rowentry #sellTotalAmount{ width:100%; }
  .pos-rowentry #addInviceItem{ margin-left:2px; }
}

/* ── SER-4: what the register knows about the unit being sold ──────────────────────────────────
   Sits under the serial input on the sale screen. Three states, and the colours carry meaning
   rather than decoration: a USED handset changes what the customer is buying, so it must not look
   the same as a new one at a glance on a busy counter. */
/* ⚠ In the compact row this badge shares a flex cell with the serial input, and a flex item's default
 * min-width:auto means its CONTENT can force the cell wider than the input above it — "REFURBISHED · not
 * in stock" would have dragged the whole strip out and misaligned every field after it. Bounded to the
 * cell and allowed to wrap instead. */
.pos-rowentry #sellSerialInfo{
  max-width: 148px;
  white-space: normal;
  overflow-wrap: anywhere;
}

.serial-info {
  margin-top: 4px;
  padding: 3px 8px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.4;
  display: inline-block;
}
/* NEW — confirmation, not an alert. Quiet on purpose: it is the ordinary case. */
.serial-info-ok   { background: #e8f5e9; color: #1B5E20; border: 1px solid #a5d6a7; }
/* USED / REFURBISHED — the cashier must register this BEFORE taking the money. */
.serial-info-warn { background: #fff3e0; color: #E65100; border: 1px solid #ffcc80; }
/* Not on the shelf. Advisory: the server is what actually refuses the sale. */
.serial-info-miss { background: #fdecea; color: #B71C1C; border: 1px solid #ef9a9a; }
