/* ===========================================================================
   NEXMORIM — design system
   ---------------------------------------------------------------------------
   Direction: Precision. White rails, one violet accent, Sora for headings.

   Violet is the accent on purpose. This product is mostly about status —
   which stage a job is at, what is low on stock, what is overdue — and those
   need green, amber and red to read instantly. A violet brand colour never
   competes with any of them.

   Everything here is written for this product. One stylesheet, one set of
   tokens, no framework.
   =========================================================================== */

/* ---------------------------------------------------------------------------
   1. TOKENS
   --------------------------------------------------------------------------- */
:root {
  /* brand */
  --violet:        #6d28d9;
  --violet-hover:  #5b21b6;
  --violet-soft:   #f4f0ff;
  --violet-line:   #e4d9ff;
  --violet-glow:   rgba(109, 40, 217, 0.16);

  /* neutrals */
  --ink:           #14141c;
  --ink-2:         #32323f;
  --muted:         #71718a;
  --faint:         #a0a0b0;
  --line:          #ececf2;
  --line-strong:   #dcdce6;
  --surface:       #fafafd;
  --card:          #ffffff;
  --raised:        #f7f7fb;

  /* status — never reassign these to the brand */
  /* the brand as a status-style pair, so a tone name can be swapped in
     anywhere the others are used */
  --brand:   var(--violet);  --brand-bg: var(--violet-soft);  --brand-line: var(--violet-line);

  --ok:      #11623a;  --ok-bg:     #e7f8ee;  --ok-line:     #bfe8d0;
  --warn:    #8a4d05;  --warn-bg:   #fef3e2;  --warn-line:   #f7dcae;
  --danger:  #a01b1b;  --danger-bg: #fdecec;  --danger-line: #f6c9c9;
  --info:    #1d4ed8;  --info-bg:   #e8efff;  --info-line:   #c7d8ff;
  --calm:    #62627a;  --calm-bg:   #f2f2f7;  --calm-line:   #e2e2ec;

  /* type */
  --font:      'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-head: 'Sora', 'Inter', system-ui, sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Consolas, monospace;

  /* shape — a 4px rhythm, never arbitrary values */
  --r-sm: 8px;
  --r:    12px;
  --r-lg: 16px;
  --r-xl: 20px;

  /* ---- size ---------------------------------------------------------------
     Written down rather than typed into each rule, so a change is one edit and
     every screen moves together.

     Measured against two systems built for the same kind of screen - dense,
     tabular business software - and against what this app used to be:

                        was      now    Polaris (Shopify)   Primer (GitHub)
       control height   44px     36     32                  32 / 40
       body + input     14.5     14     13                  14
       label            13.5     13     13                  -
       card padding     18       16     16                  16
       table cell       13/18    8/12   6                   -
       spacing          13,15,18 4-grid 4/8/12/16/24        4/8/12/16/24

     The size was never really the problem. 7, 13, 14.5, 15.5, 18 are on no
     grid at all, and that is what reads as hand-made rather than systematic.
     Everything here snaps to 4.

     The text is barely touched. Measured, it was half a pixel to a pixel and
     a half over Polaris or Primer; it was the SPACE that was big. Shrinking
     the letters would cost more than it saves: these screens are read by
     shopkeepers, often on a cheap phone in poor light, and the thing being
     read is a fifteen digit IMEI where one wrong digit files a phone under
     the wrong number. So 14px, not Polaris's 13.

     Touch is not handled here. The block at the bottom of this file floors
     every control at 44px under @media (pointer: coarse), and min-height
     beats a smaller fixed height - so 36 on a mouse is 44 on a thumb. Apple
     asks for 44, Material for 48, WCAG 2.2 AA sets a floor of 24.
     ------------------------------------------------------------------------- */
  --h-control:   36px;   /* what a text box or a button comes to */
  /* The one box a counter types into all day. Every other control here is
     sized for reading a form; this one is sized for a hand that does not
     look down at it, and for a phone - 20px is well past the 16 below which
     iOS zooms the page in on focus. */
  --h-till:      48px;
  --fs-till:     20px;
  --fs-control:  14px;
  --fs-label:    13px;
  --fs-body:     14px;
  --fs-small:    12px;
  --fs-card-h:   14px;
  --fs-h1:       20px;
  --pad-card:    16px;
  --pad-card-y:  12px;
  --pad-cell-y:  8px;
  --pad-cell-x:  12px;
  --pad-control: 0 12px;
  /* The space BETWEEN things, which has to move with them. Shrinking the
     boxes and leaving the gaps where they were is what makes a screen read as
     caved in - small controls floating in spacing sized for bigger ones. */
  --gap-field:   16px;
  --gap-card:    16px;

  /* depth — tinted with the brand rather than pure black, so cards sit on
     the page instead of looking cut out of it */
  --lift-1: 0 1px 2px rgba(20, 20, 45, 0.05);
  --lift-2: 0 2px 6px rgba(20, 20, 45, 0.06), 0 1px 2px rgba(20, 20, 45, 0.04);
  --lift-3: 0 10px 26px -6px rgba(20, 20, 45, 0.12), 0 2px 6px rgba(20, 20, 45, 0.05);
  --lift-4: 0 24px 48px -12px rgba(20, 20, 45, 0.22);

  --rail: 232px;
  --bar:  62px;
}

html[data-theme="dark"] {
  --violet:        #a78bfa;
  --violet-hover:  #b9a3fb;
  --violet-soft:   #221c3d;
  --violet-line:   #362b5c;
  --violet-glow:   rgba(167, 139, 250, 0.22);

  --ink:           #ecebf5;
  --ink-2:         #c3c2d4;
  --muted:         #9695ab;
  --faint:         #6e6d84;
  --line:          #262536;
  --line-strong:   #343349;
  --surface:       #0e0e16;
  --card:          #16161f;
  --raised:        #1c1b27;

  --ok:      #6ee7a8;  --ok-bg:     #102b1e;  --ok-line:     #1e4d36;
  --warn:    #fbbf3c;  --warn-bg:   #2e2109;  --warn-line:   #543c10;
  --danger:  #fb8686;  --danger-bg: #30161a;  --danger-line: #5a2730;
  --info:    #93b4fd;  --info-bg:   #14203c;  --info-line:   #253a68;
  --calm:    #9695ab;  --calm-bg:   #1e1d29;  --calm-line:   #2d2c3c;

  --lift-1: 0 1px 2px rgba(0, 0, 0, 0.5);
  --lift-2: 0 2px 6px rgba(0, 0, 0, 0.5);
  --lift-3: 0 10px 26px -6px rgba(0, 0, 0, 0.6);
  --lift-4: 0 24px 48px -12px rgba(0, 0, 0, 0.7);
}

/* ---------------------------------------------------------------------------
   2. RESET
   --------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }
/* The reset above takes away the auto margin a browser centres a modal
   <dialog> with, and every dialog here opened in the top-left corner. */
dialog { margin: auto; }

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

body {
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.55;
  color: var(--ink);
  background: var(--surface);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-feature-settings: 'cv02', 'cv03', 'cv04';
  overflow-x: hidden;
}

img, svg { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: none; cursor: pointer; }
table { border-collapse: collapse; width: 100%; }
[hidden] { display: none !important; }

a { color: var(--violet); text-decoration: none; }
a:hover { text-decoration: underline; }

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

::selection { background: var(--violet-soft); color: var(--violet); }

/* ---------------------------------------------------------------------------
   3. TYPE
   Headings use Sora, which is squarer than the body face. The contrast is
   what gives the product a voice instead of looking like default Inter.
   --------------------------------------------------------------------------- */
h1, h2, h3, h4 {
  font-family: var(--font-head);
  line-height: 1.22;
  letter-spacing: -0.025em;
  font-weight: 700;
}
h1 { font-size: var(--fs-h1); }
h2 { font-size: 18px; }
h3 { font-size: 15px; }

.muted { color: var(--muted); }
.faint { color: var(--faint); }
.mono  { font-family: var(--font-mono); font-size: 0.92em; letter-spacing: -0.01em; }
.num   { font-variant-numeric: tabular-nums; }
.nowrap{ white-space: nowrap; }

/* ---------------------------------------------------------------------------
   4. BUTTONS
   --------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: var(--h-control);
  padding: 0 16px;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.4;
  white-space: nowrap;
  transition: background-color .14s, border-color .14s, color .14s,
              box-shadow .14s, transform .06s;
}
.btn:hover { text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn[disabled] { opacity: .5; cursor: not-allowed; transform: none; }

.btn--primary {
  background: var(--violet);
  color: #fff;
  box-shadow: 0 1px 2px var(--violet-glow);
}
.btn--primary:hover { background: var(--violet-hover); box-shadow: 0 3px 10px var(--violet-glow); }
html[data-theme="dark"] .btn--primary { color: #16161f; }

.btn--ghost {
  background: var(--card);
  color: var(--ink);
  border-color: var(--line-strong);
}
.btn--ghost:hover { background: var(--raised); border-color: var(--violet-line); }

.btn--quiet { background: transparent; color: var(--muted); }
.btn--quiet:hover { background: var(--raised); color: var(--ink); }

.btn--danger { background: var(--danger-bg); color: var(--danger); border-color: var(--danger-line); }
.btn--danger:hover { background: var(--danger); color: #fff; }

.btn--block { width: 100%; }
.btn--sm { padding: 6px 11px; font-size: 13px; border-radius: 6px; }
.btn--icon { padding: 8px; color: var(--muted); border-radius: var(--r-sm); }
.btn--icon:hover { background: var(--raised); color: var(--ink); }

/* ---------------------------------------------------------------------------
   5. FORMS
   --------------------------------------------------------------------------- */
.field { display: block; }
.field + .field { margin-top: 16px; }

.field__label {
  display: inline-block;
  margin-bottom: 6px;
  font-size: var(--fs-label);
  font-weight: 600;
  color: var(--ink-2);
}
.field__req  { color: var(--danger); }
.field__wrap { position: relative; }
.field__icon {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  color: var(--faint); pointer-events: none;
}
.field__toggle {
  position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
  font-size: 12.5px; font-weight: 600; color: var(--violet); padding: 3px 5px;
}
.field__hint  { margin-top: 6px; font-size: 12.5px; color: var(--muted); }
/* A hint with nothing in it yet must not reserve the space anyway - that is
   what left the photos field taller than the two beside it in the same row. */
.field__hint:empty { display: none; }
.field__error { margin-top: 6px; font-size: 13px; color: var(--danger); font-weight: 500; }

/* The height is set, not left to fall out of padding plus line-height. A
   control is a size in the design system, and "whatever 10px of padding and a
   14.5px font happen to add up to" is not a size anybody chose. A textarea is
   the exception below - it grows. */
.input, .select { height: var(--h-control); }

.input, .select, .textarea {
  display: block;
  width: 100%;
  padding: var(--pad-control);
  font-size: var(--fs-control);
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  transition: border-color .14s, box-shadow .14s;
}
.input::placeholder, .textarea::placeholder { color: var(--faint); }

.input:hover, .select:hover, .textarea:hover { border-color: var(--violet-line); }
.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--violet);
  box-shadow: 0 0 0 3px var(--violet-glow);
}
.input[aria-invalid="true"] {
  border-color: var(--danger);
  box-shadow: 0 0 0 3px rgba(160, 27, 27, .12);
}
.input:disabled { background: var(--raised); color: var(--faint); cursor: default; }

.input--icon { padding-left: 38px; }
.input--pw   { padding-right: 62px; }

.select {
  appearance: none;
  padding-right: 34px;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23a0a0b0' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
}
/* The one control that grows, so it keeps its own padding rather than the
   height every other one is pinned to. */
.textarea { min-height: 88px; padding: 10px 12px; resize: vertical; line-height: 1.5; }

.check, .radio {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 14px; cursor: pointer;
}
.check input, .radio input { accent-color: var(--violet); width: 16px; height: 16px; cursor: pointer; }

/* A column of cards with one consistent gap. Used by the long forms, so the
   spacing between sections is decided once instead of per page. */
/* minmax(0, 1fr), not 1fr. A grid column is min-content wide by default, so
   one wide child sets the width of every card in the stack - the bill lines
   table is 560px at its narrowest, and that was stretching every card on the
   page to 597px inside a 375px phone. The whole page scrolled sideways, and
   the table that needs to scroll has its own .table-wrap to do it in. */
.stack { display: grid; gap: var(--gap-card); grid-template-columns: minmax(0, 1fr); }

/* ---- a page that is a document, not a dashboard --------------------------
   A bill, an invoice, a receipt. ONE column, because a two column dashboard
   makes it read as a report ABOUT a bill rather than as the bill - a short
   table floating in a half-empty card on the left with the totals parked in a
   column of their own on the right.
   Full width, not a narrow measure. It was capped at 860px for one version and
   the cap read as a bigger fault than the one it fixed: a page of dead space
   down the right of a wide screen. The width is earned instead - the lines
   table spreads, and the foot below it carries the total in words on the left
   of the figures rather than leaving a white rectangle there. */
.doc { display: grid; gap: var(--gap-card); grid-template-columns: minmax(0, 1fr); }

/* The foot of a bill: what it comes to, and the same amount written out.
   Two columns on a wide screen, because the figures stack into a narrow block
   at the right and the space beside them is the natural home for the words -
   which is exactly where a printed invoice puts them. One column on a phone,
   words under figures. */
.docfoot { display: grid; gap: var(--gap-card); grid-template-columns: minmax(0, 1fr); align-items: end; }
@media (min-width: 720px) {
  .docfoot { grid-template-columns: minmax(0, 1fr) auto; }
}
.docfoot__words { max-width: 46ch; }

/* minmax(0, 1fr), not 1fr. A grid column is min-content wide by default, so
   one wide child - a segmented control with four options, a long select -
   pushes the column past its share and the page scrolls sideways. The same
   trap .stack fell into. */
.form-grid { display: grid; gap: var(--gap-field); grid-template-columns: minmax(0, 1fr); }
.form-grid .field + .field { margin-top: 0; }
@media (min-width: 580px) {
  .form-grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .form-grid--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .form-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .form-grid .span-2  { grid-column: span 2; }
  .form-grid--3 .span-3 { grid-column: span 2; }
}
@media (min-width: 900px) {
  .form-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .form-grid--3 .span-3 { grid-column: span 3; }
  /* Four short answers that belong to one question - who, which paper, what
     day, which shelf - on one row. Three columns left the fourth alone with
     two empty cells beside it, which is a whole row of nothing. */
  .form-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .form-grid--4 .span-2 { grid-column: span 2; }
}

/* A segmented control: one row, one box, the choice filled in.
   For two or three options this beats a dropdown - one click instead of two,
   and every option is visible without opening anything, which matters when
   the person filling this in is holding somebody else's phone in the other
   hand. Loose chips were tried first and wrapped into ragged two-line blocks
   at every width; a single box cannot do that. */
.seg {
  display: flex; width: 100%; margin-top: 7px;
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  background: var(--card); overflow: hidden;
}
.seg__opt {
  flex: 1 1 auto;
  display: flex; align-items: center; justify-content: center; gap: 7px;
  padding: 8px 11px; white-space: nowrap;
  font-size: 12.5px; font-weight: 500; color: var(--ink-2); cursor: pointer;
  border-left: 1px solid var(--line-strong);
  transition: background-color .14s, color .14s;
}
.seg__opt:first-child { border-left: none; }
.seg__opt:hover { background: var(--violet-soft); color: var(--violet); }
/* The radio stays visible. A filled panel alone reads as a button that has
   been pressed; the dot is what says "one of these, and it is this one". */
.seg__opt input {
  flex: none; margin: 0; width: 14px; height: 14px;
  accent-color: var(--violet); cursor: pointer;
}
.seg__opt:has(input:checked) { background: var(--violet-soft); color: var(--violet); font-weight: 600; }
.seg__opt:has(input:focus-visible) { outline: 2px solid var(--violet); outline-offset: -2px; }

/* A dropdown that takes more than one answer.
   A native <select multiple> is a scrolling list box nobody can work out, and
   thirteen loose chips are three rows of furniture on a form that already has
   plenty. This is one line when closed and a checklist when open. */
.ms { position: relative; margin-top: 7px; }
.ms__field {
  display: flex; align-items: center; gap: 8px; width: 100%;
  min-height: var(--h-control); padding: 8px 34px 8px 12px;
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  background: var(--card); text-align: left; font-size: 14px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23a0a0b0' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 11px center;
  transition: border-color .14s, box-shadow .14s;
}
.ms__field:hover { border-color: var(--violet-line); }
.ms.is-open .ms__field,
.ms__field:focus-visible { border-color: var(--violet); box-shadow: 0 0 0 3px var(--violet-glow); outline: none; }
.ms__value { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ms__value.is-empty { color: var(--faint); }
.ms__n {
  flex: none; font-size: 11.5px; font-weight: 700; padding: 1px 7px;
  border-radius: 999px; background: var(--violet-soft); color: var(--violet);
}

.ms__menu {
  position: absolute; z-index: 30; top: calc(100% + 5px); left: 0; right: 0;
  max-height: 264px; overflow-y: auto; padding: 5px;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--lift-3);
}
.ms__search { padding: 3px 3px 7px; position: sticky; top: -5px; background: var(--card); z-index: 1; }
.ms__search .input { height: var(--h-control); font-size: 13.5px; }

.ms__opt {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 10px; border-radius: 7px;
  font-size: 13.5px; cursor: pointer;
}
.ms__opt:hover { background: var(--violet-soft); }
.ms__opt input { flex: none; width: 15px; height: 15px; margin: 0; accent-color: var(--violet); cursor: pointer; }
.ms__opt:has(input:checked) { color: var(--violet); font-weight: 600; }

/* Typing something the list does not have offers to add it. It is kept for
   the shop afterwards, so the next person picks it instead of inventing a
   second spelling. */
.ms__create {
  display: flex; align-items: center; gap: 9px; width: 100%;
  margin-top: 4px; padding: 9px 10px; border-radius: 7px;
  border-top: 1px solid var(--line);
  font-size: 13.5px; color: var(--violet); text-align: left;
}
.ms__create:hover { background: var(--violet-soft); }
.ms__create b { font-weight: 700; }
.ms__empty { padding: 13px; font-size: 13px; color: var(--muted); text-align: center; }

/* Loose chips, for a multi-select where wrapping is the point. */
.pick-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 7px; }
.pick {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 13px; border-radius: var(--r-sm);
  border: 1px solid var(--line-strong); background: var(--card);
  font-size: 13.5px; font-weight: 500; cursor: pointer;
  transition: border-color .14s, background-color .14s, color .14s;
}
.pick:hover { border-color: var(--violet-line); background: var(--violet-soft); }
.pick input { accent-color: var(--violet); width: 15px; height: 15px; cursor: pointer; flex: none; }
/* Browsers without :has() simply get plain outlined choices, which still work. */
.pick:has(input:checked) {
  border-color: var(--violet); background: var(--violet-soft); color: var(--violet);
  font-weight: 600;
}

/* A handset on the shelf, offered on a bill. The number is the name here, so
   it keeps the mono face; the colour beside it is what somebody actually
   goes by when two identical boxes are on the counter. Smaller than the rest
   of a .pick because fifteen digits is a wide thing to fit three of. */
.pick .mono  { font-size: var(--fs-small); }
.pick small  { color: var(--muted); font-weight: 500; }
.pick:has(input:checked) small { color: inherit; opacity: .8; }
/* Nothing to choose from yet - a row with only its own gap in it reads as a
   gap somebody forgot to fill. */
.pick-row:empty { display: none; }

/* The handsets under a bill line, which live in a cell spanning a table that
   is wider than a phone. Content in that cell wraps at the TABLE's width, so
   on a 375px screen the chips wrapped at 560 and half of them sat out past
   the right edge where no thumb reaches - the row scrolls sideways and they
   were off the end of it.
   min() rather than a media query: where the table is the narrower of the
   two - every desktop - this is 100% and changes nothing. 64px is the page
   gutter and the card's padding, both sides. */
.handsets { margin: 0; max-width: min(100%, calc(100vw - 64px)); }

/* ---------------------------------------------------------------------------
   6. CARDS
   --------------------------------------------------------------------------- */
.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--lift-1);
}
.card__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: var(--pad-card-y) var(--pad-card); border-bottom: 1px solid var(--line);
}
.card__head h2 { font-size: var(--fs-card-h); }
.card__head p  { margin-top: 2px; font-size: 12.5px; color: var(--muted); }
.card__body { padding: var(--pad-card); }
/* Admin: the database card. Green and quiet when there is nothing to do; the
   warn colours and the one primary button when a release is waiting, so the
   eye lands on it first and only then. */
.dbcard__body { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.dbcard__icon {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 44px; height: 44px; border-radius: var(--r);
  background: var(--ok-bg); color: var(--ok);
}
.dbcard--due { border-color: var(--warn-line); }
.dbcard--due .dbcard__icon { background: var(--warn-bg); color: var(--warn); }
.dbcard__main { flex: 1; min-width: 220px; }
.dbcard__title { display: block; font-size: var(--fs-card-h); }
.dbcard__list { margin: 4px 0 0; padding: 0; list-style: none; font-size: var(--fs-label); color: var(--muted); }
.dbcard__list li { padding: 2px 0; }
.dbcard__list .mono { color: var(--ink); font-weight: 600; margin-right: 4px; }
.dbcard__drift { display: flex; align-items: center; gap: 8px; color: var(--danger); }

/* A whole card that goes somewhere - a step of a record kept on its own page.
   The row is the target, not a button inside it. */
.linkcard { display: block; color: inherit; text-decoration: none; transition: border-color .14s; }
.linkcard:hover { border-color: var(--violet-line); text-decoration: none; }
.linkcard__body { display: flex; align-items: center; gap: 12px; }
.linkcard__icon {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 36px; height: 36px; border-radius: var(--r-sm);
  background: var(--violet-soft); color: var(--violet);
}
.card__foot {
  padding: var(--pad-card-y) var(--pad-card);
  border-top: 1px solid var(--line); font-size: var(--fs-small);
}

/* The total written out. Left-aligned under the figures it restates, and
   deliberately not styled up: it is there to be checked against the number,
   not to be read first. */
.amount-words {
  margin: 12px 0 0;
  padding-top: 11px;
  border-top: 1px solid var(--line);
  font-size: 12.5px;
  color: var(--muted);
}

.grid  { display: grid; gap: 15px; }
.grid--kpi { grid-template-columns: repeat(auto-fit, minmax(212px, 1fr)); }
.grid--3   { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
/* minmax(0, ...) and not a bare 1fr: a bare fr track grows to its longest
   line, and on a phone that pushed Reports' cards to 641px across a 375px
   screen. With a zero floor the card holds the screen and its table scrolls
   inside .table-wrap instead. */
.grid--2   { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 1020px) {
  .grid--2 { grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); }
  .grid--2.grid--even { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

/* KPI — the violet hairline on hover is the one flourish in the system */
.kpi { padding: 16px; position: relative; overflow: hidden; transition: box-shadow .16s, transform .16s; }
.kpi::after {
  content: ""; position: absolute; inset: 0 auto 0 0; width: 3px;
  background: var(--violet); opacity: 0; transition: opacity .16s;
}
/* The lift and the violet edge belong to the ones that actually go
   somewhere. Every .kpi had them, on the dashboard and on the customer list,
   which is a card leaning towards the cursor to promise a click it cannot
   honour. A number with a destination is .kpi--go and is an <a>; a number
   still waiting for its module sits still. */
.kpi--go { display: block; color: inherit; text-decoration: none; }
.kpi--go:hover { box-shadow: var(--lift-3); transform: translateY(-1px); text-decoration: none; }
.kpi--go:hover::after { opacity: 1; }
/* The one you are looking at. A tile that is also the way into a view has to
   say when you are already in that view, or it reads as a button that did
   nothing. The edge is the same one hover promises, kept on. */
.kpi--go.is-active { border-color: var(--violet-line); background: var(--violet-soft); }
.kpi--go.is-active::after { opacity: 1; }
.kpi__top   { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.kpi__label { font-size: 12.5px; font-weight: 500; color: var(--muted); }
.kpi__icon  { display: grid; place-items: center; width: 32px; height: 32px; border-radius: 9px; flex: none; }
.kpi__value { margin-top: 10px; font-family: var(--font-head); font-size: 27px; font-weight: 700; letter-spacing: -.035em; font-variant-numeric: tabular-nums; }
.kpi__note  { margin-top: 5px; font-size: 12px; color: var(--faint); }

/* ---------------------------------------------------------------------------
   7. PILLS
   --------------------------------------------------------------------------- */
.pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 10px; border-radius: 999px;
  font-size: 11.5px; font-weight: 700; letter-spacing: .01em;
  white-space: nowrap; border: 1px solid transparent;
}
.pill--ok     { background: var(--ok-bg);     color: var(--ok);     border-color: var(--ok-line); }
.pill--warn   { background: var(--warn-bg);   color: var(--warn);   border-color: var(--warn-line); }
.pill--danger { background: var(--danger-bg); color: var(--danger); border-color: var(--danger-line); }
.pill--info   { background: var(--info-bg);   color: var(--info);   border-color: var(--info-line); }
.pill--calm   { background: var(--calm-bg);   color: var(--calm);   border-color: var(--calm-line); }
.pill--brand  { background: var(--violet-soft); color: var(--violet); border-color: var(--violet-line); }
.pill .dot { width: 5px; height: 5px; border-radius: 999px; background: currentColor; flex: none; }

/* ---------------------------------------------------------------------------
   8. TABLES
   --------------------------------------------------------------------------- */
.table-wrap { overflow-x: auto; }
.table { font-size: 14px; min-width: 640px; }
/* One column more than the rest carry - the inventory list names the kind,
   because its rows are mixed. Squeezing it out of 640 took it out of the item
   name, which is the one column nobody can afford to lose. */
.table--mixed { min-width: 780px; }
/* And one the other way. Three columns in half a card have no six columns to
   keep readable, so the floor is nothing there but a sideways scrollbar.
   After .table and not beside it: same specificity, and the later rule is
   the one that wins - written above, this quietly did nothing, which is
   exactly how .chip--warn went wrong further down this file. */
.table--slim { min-width: 0; }
.table th {
  padding: 10px var(--pad-cell-x); text-align: left;
  font-size: 10.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--faint); background: var(--raised);
  border-bottom: 1px solid var(--line); white-space: nowrap;
}
.table td { padding: var(--pad-cell-y) var(--pad-cell-x); border-bottom: 1px solid var(--line); vertical-align: middle; }
.table tbody tr:last-child td { border-bottom: none; }
.table tbody tr { transition: background-color .12s; }
.table tbody tr:hover td { background: var(--raised); }

/* ---- a row that goes somewhere -------------------------------------------
   A list of bills carried an Open button on every row: twenty identical
   buttons doing the job the row itself should do, and twenty extra tab stops
   announcing "Open, Open, Open" to anybody listening to the page.
   The number in the first cell is the real link - one tab stop, one thing to
   read out - and the rest of the row follows it for a mouse. Hence the cursor:
   a row that moves when you click it has to look like one before you do. */
.table--go tbody tr { cursor: pointer; }
.dues-table tbody tr[data-go] { cursor: pointer; }
.table--go tbody tr:focus-within td { background: var(--raised); }
.table .t-num   { text-align: right; font-variant-numeric: tabular-nums; }
.table .t-right { text-align: right; }

/* A figure that is no longer money. A cancelled bill still carries its total
   and, in a column the eye runs straight down, it reads as a sale. The number
   stays - it is what the bill said - and is struck out so it stops counting.
   Faint alone was not enough: grey is quieter, not different. */
.table .t-num--void { color: var(--faint); text-decoration: line-through; }

.cell-main   { font-weight: 600; }
.cell-sub    { font-size: 12.5px; color: var(--muted); margin-top: 1px; }
.cell-person { display: flex; align-items: center; gap: 11px; }

/* ---- the numbers under a line --------------------------------------------
   IMEIs and serials, one chip each. They used to be a comma separated run,
   which is unreadable at exactly the moment it is read: somebody holding a
   phone, checking whether THIS is the one on the bill. A fifteen digit number
   is found by its shape and a comma is not a shape. */
.numrow { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.numrow__n {
  padding: 2px 8px; border-radius: var(--r-sm);
  background: var(--raised); border: 1px solid var(--line);
  font-size: var(--fs-small); color: var(--ink-2);
}

.avatar {
  display: grid; place-items: center; flex: none;
  width: 32px; height: 32px; border-radius: 999px;
  background: var(--violet-soft); color: var(--violet);
  font-size: 12px; font-weight: 700; letter-spacing: -.01em;
}

.toolbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  padding: 13px 18px; border-bottom: 1px solid var(--line);
}
.toolbar .field__wrap { flex: 1; min-width: 190px; }

/* What is ticked in the list below, and the one thing to do with it. Built as
   a second toolbar row rather than a bar floating over the bottom of the
   page: the rows being chosen from are the last thing that should be covered
   while somebody is still choosing. Tinted, so an empty list and a list with
   three bills held in it do not look the same at a glance. */
.picked {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 12px; padding: 12px 18px;
  border-bottom: 1px solid var(--brand-line);
  background: var(--brand-bg);
  font-size: var(--fs-label); color: var(--ink-2);
}
/* The tick itself, in a table cell of its own. Sized with the form ones so a
   row of them does not read as a different control to the ones on a form. */
.tick, #tickAll { accent-color: var(--violet); width: 16px; height: 16px; cursor: pointer; }

/* The "..." at the end of a row. What can be done to this bill without
   opening it - and the menu itself is `fixed`, placed by the script, because
   .table-wrap scrolls sideways and an overflow on one axis takes the other
   one with it: a menu positioned against its own cell is clipped by the
   table, which on the last row means an item you can see and cannot reach. */
.rowmenu { position: relative; }
.rowmenu .dropdown__menu { position: fixed; right: auto; top: auto; min-width: 188px; }
.rowmenu .btn--icon { color: var(--ink-2); border: 1px solid var(--line); }
.rowmenu .btn--icon:hover,
.rowmenu .btn--icon[aria-expanded="true"] { color: var(--ink); background: var(--raised); }

.pagination { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px; }
.pagination__pages { display: flex; gap: 5px; align-items: center; }
.page-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 32px; height: 32px; padding: 0 10px;
  border: 1px solid var(--line-strong); border-radius: 7px;
  font-size: 13px; font-weight: 600; color: var(--muted); text-decoration: none;
  transition: background-color .12s, border-color .12s, color .12s;
}
.page-btn:hover { background: var(--raised); color: var(--ink); border-color: var(--violet-line); text-decoration: none; }
.page-btn.is-active { background: var(--violet); border-color: var(--violet); color: #fff; }
html[data-theme="dark"] .page-btn.is-active { color: #16161f; }
.page-btn.is-disabled { opacity: .4; pointer-events: none; }
.flip { transform: rotate(180deg); }

/* ---------------------------------------------------------------------------
   9. EMPTY STATE
   --------------------------------------------------------------------------- */
.blank { display: grid; place-items: center; text-align: center; padding: 50px 20px; }
.blank__icon {
  display: grid; place-items: center; width: 48px; height: 48px; margin-bottom: 15px;
  border-radius: var(--r); background: var(--violet-soft);
  border: 1px solid var(--violet-line); color: var(--violet);
}
.blank h3 { font-size: 15.5px; margin-bottom: 5px; }
.blank p  { font-size: 13.5px; color: var(--muted); max-width: 42ch; }

/* ---------------------------------------------------------------------------
   10. ALERTS
   --------------------------------------------------------------------------- */
.alert {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 15px; margin-bottom: 16px;
  border: 1px solid transparent; border-radius: var(--r);
  font-size: 14px; font-weight: 500;
}
.alert--ok  { background: var(--ok-bg);     color: var(--ok);     border-color: var(--ok-line); }
.alert--err { background: var(--danger-bg); color: var(--danger); border-color: var(--danger-line); }
.alert--warn{ background: var(--warn-bg);   color: var(--warn);   border-color: var(--warn-line); }
.alert--info{ background: var(--info-bg);   color: var(--info);   border-color: var(--info-line); }

/* A bill's lines. A table rather than stacked cards: the whole point is to
   read down the rate column and check it against the paper in your hand. */
/* Fixed layout, or the item column collapses to nothing: an empty input can
   shrink, and an auto table hands its width to the columns that cannot. */
.lines { width: 100%; border-collapse: collapse; table-layout: fixed; min-width: 560px; }
.lines th {
  padding: 0 10px 7px; text-align: left;
  font-size: 11.5px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--faint);
}
.lines th.t-num, .lines td.t-num { text-align: right; }
.lines td { padding: 4px 10px; vertical-align: top; }
.lines tr.line--imei td { padding-top: 0; }

/* The handsets on a line. Pinned to the left edge of the table's scroller and
   given the width that is actually on screen - see the note where the row is
   built. Without this the chips wrap at the table's width, which on a phone
   puts everything past the first one, and both of its buttons, out of reach
   to the right. */
.lines .imei-cell { position: sticky; left: 0; }

/* Memory and storage, above the handsets rather than beside the item: they
   belong to the phone, and every handset on the line is the same model and so
   the same 8/128. Narrow on purpose - it is one short value, and a box the
   width of the table would read as the most important thing here, which it is
   not. */
/* The box is the size of the answer. "8/128" is six characters, and in a box
   the width of the item name above it the value sits stranded against the
   left edge with a hand's width of nothing beside it - so the box is cut to
   fit and the value sits in the middle of it, the way a quantity does.
   The sentence underneath keeps a readable width of its own; tying it to the
   box would break one line of English across four. */
/* A bare number with its unit beside it, so the box takes "4" and the screen
   still says what four means. The unit is not a placeholder: a placeholder
   disappears the moment somebody types, which is exactly when they want to
   check they have not written days. */
.months { display: flex; align-items: center; gap: 8px; }
.months .input { width: 84px; text-align: center; }
.months > span { font-size: 13px; color: var(--muted); }

/* The price rounding ladder on Purchases > Configure. One row per band, and
   under each one the sentence it comes to - because "up to 500, nearest,
   multiple of, 5" is four dropdowns and not yet an English sentence, and the
   sentence is the thing somebody checks. */
.round-rules { display: grid; gap: 14px; margin-top: 16px; }
.round-rule {
  display: grid; gap: 10px; align-items: end;
  grid-template-columns: 1fr 1fr 1fr 1fr 34px;
  padding: 12px 14px 10px;
  border: 1px solid var(--line); border-radius: 12px; background: var(--raised);
}
.round-rule__cell { display: grid; gap: 4px; min-width: 0; }
.round-rule__cell > span {
  font-size: 11px; font-weight: 600; letter-spacing: .02em; color: var(--muted);
}
.round-rule .input, .round-rule .select { height: var(--h-control); font-size: 13px; }
.round-rule__says {
  grid-column: 1 / -1; margin: 2px 0 0;
  font-size: 12.5px; color: var(--muted);
}
@media (max-width: 760px) {
  .round-rule { grid-template-columns: 1fr 1fr; }
  .round-rule .js-drop { grid-column: 2; justify-self: end; }
}

/* Not decoration. These rules do surprising things at the edges, and this is
   where somebody meets that rather than at the counter. */
.round-try {
  display: flex; align-items: flex-end; gap: 16px; flex-wrap: wrap;
  margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--line);
}
.round-try .field { max-width: 180px; }
.round-try__out { display: flex; align-items: center; gap: 10px; padding-bottom: 8px; }
.round-try__arrow { display: grid; place-items: center; color: var(--faint); }
.round-try__out strong { font-size: 19px; font-variant-numeric: tabular-nums; }

/* A bill that was typed and never reached the server. Amber rather than red:
   nothing is wrong, something was saved - and the two buttons are the whole
   point, so they are not allowed to wrap away from the sentence on a phone. */
.wip-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-bottom: 14px; padding: 11px 14px; font-size: 13px;
  border: 1px solid var(--warn-line); border-radius: 10px;
  background: var(--warn-bg); color: var(--warn);
}
.wip-bar span { flex: 1 1 240px; }
.wip-bar b { font-weight: 600; }

/* The three numbers decided while the box is open: what it is, what it sells
   for, what is printed on it. Side by side because they are read together -
   a selling price means nothing without the cost on the line above it. */
.lines .spec-row { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 4px; }
.lines .ram-field { margin: 0 0 12px; }
/* "Rack 3" fits in the same box as "8/128"; "under the counter" does not, and
   a shelf that has to be abbreviated to fit is a shelf nobody writes down. */
.lines .ram-field--wide .input { width: 220px; text-align: left; }
.lines .ram-field .input {
  width: 140px; height: var(--h-control); font-size: 13px; text-align: center;
}
.lines .ram-field .field__hint { max-width: 340px; }
/* Already answered by the product, so it is shown rather than asked. Reads as
   a fact on the page instead of an empty box somebody has to decide about. */
.lines .ram-field .input.is-locked {
  color: var(--muted); background: var(--raised); border-style: dashed;
}
/* Waiting for an item, not broken. Fainter than a placeholder somebody is
   meant to type over, because this one is never typed over. */
.lines .ram-field .input.is-locked::placeholder { color: var(--faint); font-style: italic; }
.lines .input { min-width: 0; }
.line__x {
  display: grid; place-items: center; width: 32px; height: var(--h-control);
  color: var(--faint); background: none; border-radius: 8px;
}
.line__x:hover { background: var(--danger-bg); color: var(--danger); }
.line__amount { padding-top: 12px; font-variant-numeric: tabular-nums; font-weight: 600; }

/* The item search on a line. Sits over the row rather than pushing it down,
   so the rest of the bill does not jump while somebody is picking.

   Not .pick - that is already the accessory chip above, and borrowing the
   name gave every line's item box an inline-flex 55px wide. */
.linepick { position: relative; }

/* The row at the top of a bill that nothing has been picked into yet. It is a
   search box, not a line - so it does not get to show a quantity of one, a
   rate of nothing and a total of nothing above every line that is real.
   Hidden rather than removed: the cells hold the column widths, so the box
   does not change size the moment something lands in it. */
.lines tr.line--new td:not(:first-child) { visibility: hidden; }

/* The column titles stay, even on a bill with nothing on it. They were
   hidden for a while - four headings standing over an empty row read as a
   form waiting to be filled in - and the owner looked at both and kept them:
   on an empty bill they say what a line is going to be, which is worth the
   row it costs. The blank row's own cells still go (above), so there is no
   quantity of one over a thing that does not exist. */

/* The box itself, at till size. Only the row nothing has been picked into:
   once a line is real it is a row in a list and goes back to the size every
   other row is. The card above it lost its heading for the same reason - the
   box says what to do with it, and a title over one control is a label for a
   label. */
.lines tr.line--new td:first-child .input {
  height: var(--h-till);
  font-size: var(--fs-till);
  padding: 0 16px;
  border-radius: var(--r);
}

/* Fixed, and hung off <body> rather than off the cell.
   It was absolutely positioned inside the row, and the rows live in
   .table-wrap, which scrolls sideways. A box that scrolls in one direction
   clips in both - that is what overflow-x: auto means - so the menu was cut
   off at the edge of the card. The shop typed "redmi", saw one and a half
   results with the second one sliced through the middle, and had no way to
   reach the rest.

   Nothing inside a scrolling area can be trusted to overflow it, so the menu
   leaves the area entirely and the script puts it where the input is. */
.linepick__menu {
  position: fixed; z-index: 120;
  max-height: 46vh; overflow-y: auto; overscroll-behavior: contain;
  padding: 5px;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--card); box-shadow: var(--lift-3);
}
.linepick__opt {
  display: block; width: 100%; text-align: left;
  padding: 9px 10px; border-radius: 8px; font-size: 13.5px; background: none;
}
.linepick__opt:hover, .linepick__opt.is-on { background: var(--violet-soft); color: var(--violet); }
.linepick__opt small { display: block; font-size: 12px; color: var(--muted); }
.linepick__opt:hover small, .linepick__opt.is-on small { color: inherit; opacity: .8; }

/* The name on the left, what it sells for on the right. The price is the
   second thing anybody checks before picking a row, and buried in the muted
   line under the name it has to be hunted for. Lining figures, so a column of
   prices reads as a column. */
.linepick__top { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.linepick__top > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.linepick__top b { flex: none; font-weight: 600; font-variant-numeric: tabular-nums; }

/* What the line turned out to be, under the box it was typed into: the stock
   behind a picked part, or that nothing was picked and this is labour. It
   carries a fact rather than advice, which is the only kind of line allowed
   to sit under a field. */
.linepick__what { display: block; margin-top: 4px; font-size: var(--fs-small); color: var(--muted); }
.linepick__what--no { color: var(--danger); }

/* The numbers on a finished handset line. The shelf folds away the moment a
   handset is picked - a block of chips and a scan box under every finished
   line is half a screen of controls for decisions already made - and this is
   the way back into it, the same way a folded line on a purchase reopens by
   its own row. Dotted, because it is a way in rather than a link away. */
.linepick__what--open { cursor: pointer; text-decoration: underline dotted; text-underline-offset: 3px; }
.linepick__what--open:hover { color: var(--violet); }

/* The letters that matched, so the eye confirms rather than re-reads. No
   colour of its own - the row already has one when it is highlighted, and a
   second colour inside it would be two signals for one fact. */
.linepick__opt mark { background: none; color: inherit; font-weight: 700; }

/* Nothing matched. Worth a row of its own: a menu that simply does not
   appear looks like a page that has stopped working, and the shop types the
   same three letters again. */
.linepick__empty { padding: 10px 11px; font-size: 13px; color: var(--muted); }

/* Which key does what, on the strip at the bottom of the menu. It costs one
   line and it is the only place somebody finds out the arrows work. */
.linepick__keys {
  display: flex; gap: 12px; padding: 7px 11px 4px; margin-top: 3px;
  border-top: 1px solid var(--line); font-size: 11px; color: var(--muted);
}
.linepick__keys kbd {
  font: inherit; font-weight: 600; padding: 1px 5px; border-radius: 4px;
  border: 1px solid var(--line-strong); background: var(--bg);
}

/* Notes on the left, the money on the right. The totals column is 320px and
   whatever sits above or below it leaves the rest of the card empty for its
   whole height - so the one thing that stretches goes beside it instead. */
.closeup { display: grid; gap: 18px; align-items: start; }
@media (min-width: 720px) {
  .closeup { grid-template-columns: minmax(0, 1fr) 320px; }
  .closeup .textarea { min-height: 150px; }
  /* Fill the column it was given. Left to shrink to its content it came out
     at 202px, and a bill total is the one number on the page that should not
     have to be hunted for. */
  .closeup .totals { max-width: none; width: 100%; }
}

/* The running total, off to one side so it does not compete with the lines. */
.totals { display: grid; gap: 7px; max-width: 320px; margin-left: auto; }
.totals__row { display: flex; align-items: center; justify-content: space-between; gap: 14px; font-size: 13.5px; }
.totals__row .input { max-width: 130px; text-align: right; }
.totals__row b { font-variant-numeric: tabular-nums; }
.totals__grand {
  padding-top: 9px; border-top: 1px solid var(--line);
  font-family: var(--font-head); font-size: 17px; font-weight: 700;
}

/* ---------------------------------------------------------------------------
   11. SHELL
   --------------------------------------------------------------------------- */
.app { display: flex; min-height: 100vh; }

.sidebar {
  position: fixed; z-index: 60; inset: 0 auto 0 0;
  width: var(--rail);
  display: flex; flex-direction: column;
  background: var(--card); border-right: 1px solid var(--line);
  transform: translateX(-100%);
  transition: transform .22s cubic-bezier(.4, 0, .2, 1);
}
.sidebar.is-open { transform: none; }

.sidebar__head {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  height: var(--bar); padding: 0 16px; flex: none;
}
.sidebar__nav  { flex: 1; overflow-y: auto; padding: 4px 12px 20px; }
.sidebar__foot { flex: none; padding: 13px; border-top: 1px solid var(--line); }

.sidebar__section {
  margin: 16px 0 6px; padding: 0 10px;
  font-size: 10px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--faint);
}
.sidebar__section:first-child { margin-top: 2px; }

.navlink {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 11px; border-radius: 9px;
  font-size: 13.5px; font-weight: 500; color: var(--muted);
  transition: background-color .12s, color .12s;
}
.navlink + .navlink { margin-top: 1px; }
.navlink:hover { background: var(--raised); color: var(--ink); text-decoration: none; }
.navlink.is-active { background: var(--violet-soft); color: var(--violet); font-weight: 600; }
.navlink svg { flex: none; }
.navlink__soon {
  margin-left: auto; padding: 1px 6px; border-radius: 999px;
  background: var(--calm-bg); border: 1px solid var(--calm-line); color: var(--faint);
  font-size: 9.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
}
.navlink__badge {
  margin-left: auto; padding: 1px 7px; border-radius: 999px;
  background: var(--danger-bg); color: var(--danger);
  font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums;
}

/* A module that opens where it stands. It is a button, so it needs the width
   and the left alignment a link gets for free. */
.navlink--parent { width: 100%; text-align: left; background: none; cursor: pointer; }
.navlink__caret {
  margin-left: auto; display: grid; place-items: center;
  color: var(--faint); transition: transform .16s;
}
.navlink--parent[aria-expanded="true"] .navlink__caret { transform: rotate(90deg); }
.navlink--parent.is-active .navlink__caret { color: inherit; }

/* Inventory briefly had its row split - name a link to its page, caret a
   button beside it - so that one tap could still reach the page. Two targets
   on one line is a thing you have to be told about, and it made one module
   behave unlike every other. Every parent is a whole-row toggle again, and a
   module whose own page is worth visiting names that page in its list. */

/* The sub-items of whichever module is open.
   Indented under a rule rather than given their own icons: they belong to the
   line above, and nine icons in a column stops any of them meaning anything.
   The number is the point of them - a menu that says there are eight things
   to reorder has done its job before it is clicked. */
.subnav {
  display: flex; align-items: center; gap: 8px;
  margin-left: 21px; padding: 7px 11px 7px 13px;
  border-left: 1.5px solid var(--line);
  border-radius: 0 8px 8px 0;
  font-size: 13px; font-weight: 500; color: var(--muted);
  transition: background-color .12s, color .12s, border-color .12s;
}
.subnav-group { margin-top: 2px; }
.subnav-group + .navlink, .subnav-group + .navlink--parent { margin-top: 3px; }
.subnav:hover { background: var(--raised); color: var(--ink); text-decoration: none; }
.subnav.is-active { color: var(--violet); font-weight: 600; border-left-color: var(--violet); }
/* Named rather than :first-child, which grabbed the plus icon the moment a
   child had one and squeezed the label down to nothing. */
.subnav__label { flex: 1; min-width: 0; }
.subnav__n {
  flex: none; font-size: 11.5px; font-weight: 700; color: var(--faint);
  font-variant-numeric: tabular-nums;
}
.subnav.is-active .subnav__n { color: var(--violet); }
/* Amber only when there is actually something to buy. A zero in warning
   colours every day is a warning people stop seeing. */
.subnav__n--warn {
  padding: 1px 7px; border-radius: 999px;
  background: var(--warn-bg); color: var(--warn);
}

.brandmark { display: flex; align-items: center; gap: 9px; color: inherit; min-width: 0; }
.brandmark:hover { text-decoration: none; }
.brandmark__text { font-family: var(--font-head); font-size: 16px; font-weight: 700; letter-spacing: -.03em; white-space: nowrap; }

.plan-card { padding: 13px; border: 1px solid var(--line); border-radius: var(--r); background: var(--raised); }
.plan-card__row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.plan-card h4 { font-size: 13px; }
.plan-card p  { margin: 4px 0 9px; font-size: 11.5px; color: var(--muted); }

.meter { height: 5px; border-radius: 999px; background: var(--line-strong); overflow: hidden; }
.meter__fill { height: 100%; border-radius: 999px; background: var(--violet); transition: width .3s; }

.scrim { position: fixed; inset: 0; z-index: 55; background: rgba(20, 20, 35, .5); backdrop-filter: blur(2px); }

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.topbar {
  position: sticky; top: 0; z-index: 40;
  display: flex; align-items: center; gap: 10px;
  height: var(--bar); padding: 0 16px;
  background: color-mix(in srgb, var(--card) 86%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}
.topbar__search { position: relative; flex: 1; max-width: 400px; }
.topbar__search .input { background: var(--raised); border-color: transparent; }
.topbar__search .input:hover:not(:disabled) { border-color: var(--line-strong); }
.topbar__spacer { flex: 1; }

.branch-switch .select {
  min-width: 168px; background: var(--raised); border-color: transparent;
  font-size: 13.5px; font-weight: 500;
}

.usermenu { position: relative; }
.userbtn { display: flex; align-items: center; gap: 9px; padding: 4px 9px 4px 4px; border-radius: 999px; transition: background-color .12s; }
.userbtn:hover { background: var(--raised); }
.userbtn__meta { display: none; text-align: left; line-height: 1.25; }
.userbtn__meta strong { display: block; font-size: 13px; }
.userbtn__meta span   { font-size: 11.5px; color: var(--muted); }
@media (min-width: 640px) { .userbtn__meta { display: block; } }

.dropdown__menu {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 50;
  min-width: 215px; padding: 6px;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--lift-4);
}
.dropdown__item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 11px; border-radius: 7px; font-size: 14px; color: var(--ink); text-align: left;
}
.dropdown__item:hover { background: var(--raised); text-decoration: none; }
.dropdown__head { padding: 9px 11px; border-bottom: 1px solid var(--line); margin-bottom: 5px; }
.dropdown__head strong { display: block; font-size: 13.5px; }
.dropdown__head span   { font-size: 12px; color: var(--muted); }
.dropdown__meta { padding: 3px 11px 8px; font-size: 12px; color: var(--muted); }
.dropdown__sep  { height: 1px; margin: 5px 0; background: var(--line); }

.content { flex: 1; padding: 22px 16px 52px; }

.page-head {
  display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between;
  gap: 14px; margin-bottom: 20px;
}
/* Back arrow beside the title, not stacked above it. A page that opens with a
   link on its own line spends a whole row saying "you can go back". */
.page-head__nav { display: flex; align-items: flex-start; gap: 11px; min-width: 0; }
.page-back {
  display: grid; place-items: center; flex: none; margin-top: 2px;
  width: 30px; height: 30px; border-radius: 8px;
  border: 1px solid var(--line-strong); background: var(--card); color: var(--muted);
  transition: border-color .14s, color .14s;
}
.page-back:hover { border-color: var(--violet); color: var(--violet); text-decoration: none; }

/* Two dots and a line, so step one does not end on a button that saves and
   then opens a screen nobody was told about. */
.steps-rail { display: flex; align-items: center; gap: 9px; margin-top: 7px; font-size: 12.5px; font-weight: 600; }
.steps-rail__i { display: flex; align-items: center; gap: 7px; color: var(--faint); }
.steps-rail__i.is-now  { color: var(--violet); }
.steps-rail__i.is-done { color: var(--ok); }
.steps-rail__n {
  display: grid; place-items: center; flex: none;
  width: 20px; height: 20px; border-radius: 999px;
  border: 1.5px solid currentColor; font-size: 11px;
}
.steps-rail__i.is-now .steps-rail__n  { background: var(--violet); border-color: var(--violet); color: #fff; }
.steps-rail__i.is-done .steps-rail__n { background: var(--ok);     border-color: var(--ok);     color: #fff; }
html[data-theme="dark"] .steps-rail__i.is-now .steps-rail__n,
html[data-theme="dark"] .steps-rail__i.is-done .steps-rail__n { color: #16161f; }
.steps-rail__line { width: 26px; height: 1.5px; background: var(--line-strong); }
/* A finished step you can go back to. Underlined on hover only: a permanent
   one would make the rail look like a row of links, including the step you
   are already on. */
a.steps-rail__i.is-link { text-decoration: none; border-radius: 999px; padding: 2px 8px 2px 2px; margin-left: -2px; }
a.steps-rail__i.is-link:hover { background: var(--raised); text-decoration: underline; text-underline-offset: 2px; }
a.steps-rail__i.is-link:focus-visible { outline: 2px solid var(--violet); outline-offset: 2px; }

.page-head h1 { font-size: var(--fs-h1); }
.page-head p  { margin-top: 3px; font-size: 13.5px; color: var(--muted); }
.page-head__actions { display: flex; flex-wrap: wrap; gap: 8px; }

/* ---------------------------------------------------------------------------
   12. ONBOARDING
   --------------------------------------------------------------------------- */
.onboard {
  border: 1px solid var(--violet-line); border-radius: var(--r-lg);
  background: linear-gradient(150deg, var(--violet-soft) 0%, var(--card) 46%);
  padding: 22px; margin-bottom: 18px;
}
.onboard__head { display: flex; align-items: flex-start; gap: 14px; margin-bottom: 18px; }
.onboard__icon {
  display: grid; place-items: center; flex: none; width: 40px; height: 40px;
  border-radius: 12px; background: var(--violet); color: #fff;
  box-shadow: 0 4px 12px var(--violet-glow);
}
html[data-theme="dark"] .onboard__icon { color: #16161f; }
.onboard__head h2 { font-size: 17px; }
.onboard__head p  { margin-top: 3px; font-size: 13.5px; color: var(--muted); }

.steps { display: grid; gap: 8px; }
.step {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--card); font-size: 14px; color: inherit;
  transition: border-color .14s, box-shadow .14s;
}
a.step:hover { border-color: var(--violet); box-shadow: var(--lift-2); text-decoration: none; }
.step__tick {
  display: grid; place-items: center; flex: none; width: 21px; height: 21px;
  border-radius: 999px; border: 2px solid var(--line-strong); color: transparent;
}
.step.is-done .step__tick { background: var(--ok); border-color: var(--ok); color: #fff; }
html[data-theme="dark"] .step.is-done .step__tick { color: #102b1e; }
.step.is-done .step__label { color: var(--muted); text-decoration: line-through; }
.step__label { flex: 1; font-weight: 600; }
.step__sub   { display: block; font-weight: 400; font-size: 12.5px; color: var(--muted); text-decoration: none; }

.quick { display: grid; grid-template-columns: repeat(auto-fit, minmax(196px, 1fr)); gap: 12px; }
.quick__item {
  display: flex; align-items: center; gap: 12px;
  padding: 14px; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--card); color: inherit;
  transition: border-color .14s, box-shadow .14s, transform .14s;
}
.quick__item:hover { border-color: var(--violet); box-shadow: var(--lift-3); transform: translateY(-1px); text-decoration: none; }
.quick__item strong { display: block; font-size: 14px; }
.quick__icon {
  display: grid; place-items: center; flex: none; width: 34px; height: 34px;
  border-radius: 9px; background: var(--violet-soft); color: var(--violet);
}

/* ---------------------------------------------------------------------------
   12b. REFERRER PICKER
   A typeahead that stores an id, not typed text, so a referral can only ever
   point at a customer who actually exists.
   --------------------------------------------------------------------------- */
.ref-chosen {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 11px; border: 1px solid var(--violet-line);
  border-radius: var(--r-sm); background: var(--violet-soft);
}

.ref-results {
  position: absolute; z-index: 20; top: calc(100% + 5px); left: 0; right: 0;
  max-height: 248px; overflow-y: auto; padding: 5px;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--lift-3);
}
.ref-result {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 8px 10px; border-radius: 7px; text-align: left;
}
.ref-result:hover, .ref-result.is-active { background: var(--violet-soft); }
.ref-result__name { font-size: 13.5px; font-weight: 600; }
.ref-result__meta { font-size: 12px; color: var(--muted); }
.ref-empty { padding: 13px; font-size: 13px; color: var(--muted); text-align: center; }

/* ---------------------------------------------------------------------------
   13. MODAL
   --------------------------------------------------------------------------- */
.modal {
  position: fixed; inset: 0; z-index: 80;
  display: grid; place-items: center; padding: 16px;
  background: rgba(16, 16, 30, .55); backdrop-filter: blur(3px);
  overflow-y: auto;
}
/* The BOX is what has to fit on the screen, not its middle.
   The cap used to be 66vh on the body alone, with the head and the foot
   sitting outside it - so a dialog could come to 66vh plus 128px and be taller
   than the window it was in. On a 700px laptop the New customer dialog ran to
   595px and the first field, Name, was scrolled out of sight above the fold
   with nothing to say so.
   Bounding the box and letting the body take what is left is the whole fix:
   the title and the buttons stay put, and only the fields between them move. */
.modal__box {
  width: 100%; max-width: 520px;
  max-height: calc(100vh - 32px);          /* the .modal padding, both sides */
  display: flex; flex-direction: column;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-xl); box-shadow: var(--lift-4);
  animation: rise .18s cubic-bezier(.2, .8, .3, 1);
}
.modal__head, .modal__foot { flex: none; }
/* .modal__box--wide was here, at 740px, and the customer form was the only
   thing wide enough to need it. A dialog that has to be widened is a dialog
   that wanted to be a page, so that form is customer-form.php now and the
   modifier went with it. */
@keyframes rise { from { opacity: 0; transform: translateY(10px) scale(.985); } }

.modal__head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  padding: 18px; border-bottom: 1px solid var(--line);
}
.modal__head h2 { font-size: 17px; }
.modal__head p  { margin-top: 3px; font-size: 13px; color: var(--muted); }
/* min-height: 0 because a flex child will not shrink below its content
   without it, which is exactly how an overflow: auto box ends up never
   scrolling and pushing the buttons off the screen instead. */
.modal__body { padding: 18px; flex: 1; min-height: 0; overflow-y: auto; }
.modal__foot { display: flex; justify-content: flex-end; gap: 9px; padding: 15px 18px; border-top: 1px solid var(--line); }

/* ---------------------------------------------------------------------------
   14. AUTH
   --------------------------------------------------------------------------- */
.auth { position: relative; min-height: 100vh; background: var(--surface); overflow: hidden; }

/* A single soft violet wash behind the pitch, instead of a busy pattern. */
.auth::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(760px 520px at 12% 8%, var(--violet-soft) 0%, transparent 62%),
    radial-gradient(620px 460px at 92% 96%, var(--violet-soft) 0%, transparent 58%);
}

.auth__inner {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-height: 100vh;
}
.auth__pitch { display: none; flex: 1; padding: 48px; max-width: 620px; }
.auth__panel { flex: 1; display: flex; align-items: center; justify-content: center; width: 100%; padding: 26px 16px; }

.auth__title { font-size: 33px; letter-spacing: -.04em; margin: 22px 0 13px; max-width: 16ch; }
.auth__lede  { color: var(--muted); font-size: 15.5px; max-width: 50ch; }

.auth__features { display: flex; flex-direction: column; gap: 15px; margin-top: 26px; }
.auth__feature  { display: flex; align-items: flex-start; gap: 13px; }
.auth__feature-icon {
  flex: none; display: grid; place-items: center; width: 32px; height: 32px;
  border-radius: 9px; background: var(--violet-soft);
  border: 1px solid var(--violet-line); color: var(--violet);
  font-size: 12px;
}
.auth__feature h3 { font-size: 14.5px; }
.auth__feature p  { font-size: 13.5px; color: var(--muted); margin-top: 1px; }

.auth-card {
  width: 100%; max-width: 27rem; padding: 28px;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-xl); box-shadow: var(--lift-3);
}
.auth-card__title { font-size: 21px; text-align: center; margin-bottom: 5px; }
.auth-card__sub   { font-size: 13.5px; font-weight: 400; text-align: center; color: var(--muted); margin-bottom: 24px; }
.auth-card form > * + * { margin-top: 17px; }
.auth-card__foot  { margin-top: 20px; text-align: center; font-size: 13.5px; }
.auth-card__foot p + p { margin-top: 9px; }

.group-label { font-size: 13.5px; font-weight: 600; color: var(--ink-2); }
.radio-row   { display: flex; align-items: center; gap: 18px; margin-top: 7px; }

.demo-note {
  margin-top: 20px; padding: 11px 13px;
  border: 1px dashed var(--line-strong); border-radius: var(--r-sm);
  background: var(--raised); font-size: 12.5px; color: var(--muted);
}
.demo-note b { color: var(--ink-2); }

@media (min-width: 940px) {
  .auth__inner { flex-direction: row; justify-content: center; gap: 30px; }
  .auth__pitch { display: block; }
}

/* ---------------------------------------------------------------------------
   15. COMING SOON
   --------------------------------------------------------------------------- */
.soon-hero {
  display: grid; place-items: center; text-align: center; padding: 62px 20px;
  border: 1px solid var(--violet-line); border-radius: var(--r-lg);
  background: linear-gradient(170deg, var(--violet-soft) 0%, var(--card) 44%);
}
.soon-hero__icon {
  display: grid; place-items: center; width: 56px; height: 56px; margin-bottom: 18px;
  border-radius: 16px; background: var(--violet); color: #fff;
  box-shadow: 0 8px 22px var(--violet-glow);
}
html[data-theme="dark"] .soon-hero__icon { color: #16161f; }
.soon-hero h1 { font-size: 22px; margin-bottom: 7px; }
.soon-hero p  { font-size: 14.5px; color: var(--muted); max-width: 46ch; }

.soon-list { display: grid; gap: 8px; margin-top: 26px; text-align: left; width: 100%; max-width: 430px; }
.soon-list div {
  display: flex; gap: 10px; align-items: center;
  padding: 11px 14px; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--card); font-size: 13.5px;
}
.soon-list svg { color: var(--violet); flex: none; }

/* ---------------------------------------------------------------------------
   15b. JOBSHEETS

   Nine stages, five looks. A shop owner glancing at the list from across the
   counter needs to read "waiting", "being worked on", "ready" and "finished"
   instantly - nine separate colours would say nothing at all. Violet is
   deliberately absent here: the brand never competes with a status.
   --------------------------------------------------------------------------- */
.jstat {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3.5px 10px; border-radius: 999px;
  font-size: 11.5px; font-weight: 700; white-space: nowrap;
  border: 1px solid transparent;
}
.jstat::before {
  content: ""; width: 5px; height: 5px; border-radius: 999px;
  background: currentColor; flex: none;
}
.jstat--new   { background: var(--calm-bg);   color: var(--calm);   border-color: var(--calm-line); }
.jstat--work  { background: var(--info-bg);   color: var(--info);   border-color: var(--info-line); }
.jstat--hold  { background: var(--warn-bg);   color: var(--warn);   border-color: var(--warn-line); }
.jstat--ready { background: var(--ok-bg);     color: var(--ok);     border-color: var(--ok-line); }
.jstat--dead  { background: var(--danger-bg); color: var(--danger); border-color: var(--danger-line); }
/* Delivered is stamped rather than tinted: the job is closed and filed. */
.jstat--done  { background: var(--ink); color: var(--card); border-color: var(--ink); }

/* Stage filter row. Counts are part of the label, so nobody has to click a
   stage to find out it is empty. */
.chips { display: flex; flex-wrap: wrap; gap: 7px; padding: 12px 18px 0; }
.chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 12px; border-radius: 999px;
  border: 1px solid var(--line-strong); background: var(--card);
  font-size: 12.5px; font-weight: 600; color: var(--ink-2);
  transition: border-color .14s, background-color .14s;
}
.chip:hover { border-color: var(--violet-line); background: var(--violet-soft); text-decoration: none; }
.chip.is-active { border-color: var(--violet); background: var(--violet-soft); color: var(--violet); }
.chip__n {
  font-variant-numeric: tabular-nums; font-size: 11.5px;
  padding: 0 6px; border-radius: 999px; background: var(--raised); color: var(--muted);
}
.chip.is-active .chip__n { background: var(--violet); color: #fff; }

/* A chip that is not a view of the shelf but a problem on it. Amber rather
   than the usual neutral, because "three handsets should have sold" is money
   standing still and the chips beside it are not.
   It has to sit AFTER .chip, not with the rest of the purchase styles - same
   specificity means the later rule wins, and the first version of this was
   written above .chip and quietly did nothing. */
.chip--warn {
  border-color: var(--warn-line); background: var(--warn-bg); color: var(--warn);
}
.chip--warn:hover { border-color: var(--warn); background: var(--warn-bg); }
.chip--warn .chip__n { background: var(--warn); color: var(--surface); }
html[data-theme="dark"] .chip.is-active .chip__n { color: #16161f; }

/* ---- a bar, for a report -------------------------------------------------
   The whole charting library this app needs. A number next to a bar whose
   width is that number's share of the biggest one reads faster than a pie
   and survives dark mode, printing and a phone without a single script. The
   figure stays on the row: a bar is for comparing, not for reading off. */
.bar {
  height: 6px; border-radius: 999px; background: var(--raised);
  overflow: hidden; margin-top: 6px;
}
.bar__fill { display: block; height: 100%; background: var(--violet); border-radius: 999px; }
.bar__fill--warn { background: var(--warn); }
.bar__fill--ok   { background: var(--ok); }

/* A row that needs attention gets a hairline, not a shouting background. */
.table tr.is-overdue td:first-child { box-shadow: inset 3px 0 0 var(--danger); }
.table tr.is-urgent  td:first-child { box-shadow: inset 3px 0 0 var(--warn); }

.jobno { font-family: var(--font-mono); font-size: 13px; font-weight: 700; letter-spacing: -.02em; }

/* ---- detail page ---------------------------------------------------------- */
.job-head {
  display: flex; flex-wrap: wrap; align-items: flex-start; gap: 14px;
  padding: 18px; border-bottom: 1px solid var(--line);
}
.job-head__id { flex: 1; min-width: 200px; }
.job-head__id h1 { font-size: 21px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.job-head__id p  { margin-top: 4px; font-size: 13.5px; color: var(--muted); }

/* Key/value pairs. A label above its value reads faster than a two-column
   table when half the values are one word long. */
.kv { display: grid; gap: 14px 18px; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.kv__k { font-size: 11px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--faint); }
.kv__v { margin-top: 3px; font-size: 14px; font-weight: 500; word-break: break-word; }
.kv__v.is-empty { color: var(--faint); font-weight: 400; }

/* ---- the totals on a bill being typed ------------------------------------
   The same rows the finished bill prints, in the same order, so the screen
   is a preview of the paper rather than a second opinion about it.

   The discount lives in here as a row you can type in. It was a field up in
   the form beside the tax switches, which read as a setting - it is not one,
   it is arithmetic on the money, and money belongs beside the money it
   changes. Right-aligned and narrow, because a column of figures is read
   down its right edge. */
.tot { margin-left: auto; max-width: 340px; }
.tot__row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding: 4px 0;
}
.tot__row--sum { margin-top: 4px; padding-top: 12px; border-top: 1px solid var(--line); }
.tot__k { font-size: var(--fs-small); color: var(--muted); }
.tot__v {
  font-size: var(--fs-control); font-weight: 500;
  font-variant-numeric: tabular-nums; text-align: right;
}
.tot__v--big { font-family: var(--font-head); font-size: 20px; font-weight: 700; }
/* What was typed, and what it comes to - "10%" beside "âˆ’ â‚¹4,700", which is
   the pair that makes a per cent worth typing. */
.tot__pair { display: flex; align-items: center; gap: 12px; }
.tot__in { width: 92px; text-align: right; }
.tot__v--off { color: var(--danger); }

/* ---- a barcode label, 50 x 25mm ------------------------------------------
   Everything here is in millimetres, and deliberately: this block is not
   laid out on a screen, it is laid out on a thermal roll. The px scale and
   its tokens are for pixels - a sticker that came out 4% small because a
   printer's idea of a pixel differs from a browser's is a sticker that does
   not scan.

   A label is 25mm tall and the barcode wants nine of them. What is left is
   one line of name and one line under the bars, which is why neither is
   allowed to wrap: a second line of name would take the height out of the
   thing the label exists for. */
.label-sheet { display: none; }
.label-sheet--one { display: block; }
/* The size is set per label by the script, from what the shop chose. These
   are the fallback for a page that draws one before the settings arrive. */
.label {
  box-sizing: border-box;
  width: 50mm; height: 25mm;
  padding: 1.5mm 2mm;
  display: flex; flex-direction: column; justify-content: space-between;
  background: #fff; color: #000;
  font-family: var(--font-body);
  overflow: hidden;
}
/* On screen only. On the roll the edge of the sticker is the edge. */
.label-sheet--one .label { border: 1px solid var(--line); border-radius: var(--r-sm); }

/* A line is its own height and no less. Flex items shrink by default, so a
   sticker asked for more than it holds squeezed every line instead of
   overflowing: the type came out sliced through the middle, the bars came
   out shorter than the height they were set to - which is a scan that fails
   on paper - and nothing could be told, because the box still measured full.
   Refusing to shrink is what turns all of that into a plain overflow the
   line below the preview can name. */
.label > * { flex: none; }

/* More lines than the height holds. space-between spreads them edge to edge,
   so once they stop fitting the first is pushed out of the TOP and the last
   out of the bottom - content lost at both ends, and the label looks like a
   rendering fault rather than a sticker somebody asked too much of. Stacked
   from the top it overflows downwards, where it reads as what it is. */
.label--tight { justify-content: flex-start; }
/* Every line on a sticker is one line. A name that wrapped would take its
   second line out of the barcode, which is the thing the label exists for -
   so it is cut instead, and the shop can see that it is cut while it is
   choosing rather than after a hundred of them have printed. */
.label__line { line-height: 1.15; white-space: nowrap; overflow: hidden;
               text-overflow: ellipsis; }
.label__line--head { font-weight: 700; }
.label__line--code { font-family: var(--font-mono); letter-spacing: .02em; }
/* The price this is not. Struck rather than greyed: a thermal head prints
   black or nothing, so a grey would come out as a dither pattern that reads
   worse than the number it was trying to soften. */
.label__line--was  { text-decoration: line-through; }
/* Out over the side padding, so the symbol is drawn from the label's own
   edge - on the printer's dot grid - and its blank quiet zones use the margin. */
.label__bars  { line-height: 0; text-align: center; margin: 0 -2mm; }

/* ---- the canvas ----------------------------------------------------------
   Free placement: every field sits where the shop put it rather than in the
   stack. The sticker stops being a flex column and becomes the frame that
   positions are measured from, so `padding` goes too - a coordinate is from
   the edge of the label, which is the edge the printer knows.

   Nothing here is on screen only. A spot is drawn at exactly these numbers
   on the roll as well, because the preview IS the print output on this
   screen - one renderer, so there is nothing to drift. */
.label--free { display: block; position: relative; padding: 0; }
.label__spot {
  position: absolute; box-sizing: border-box;
  line-height: 1.15; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
/* The handles, and only while somebody is arranging. `.is-editing` is put on
   the sticker by the screen and is never on a printed one, so a roll cannot
   come out with a dotted box round every line. */
.label--free.is-editing .label__spot {
  outline: 1px dashed var(--line-strong); outline-offset: 1px;
  cursor: grab; touch-action: none;
}
.label--free.is-editing .label__spot:hover { outline-color: var(--violet-line); }
.label--free.is-editing .label__spot.is-picked {
  outline: 1.5px solid var(--violet); outline-offset: 1px;
}
.label--free.is-editing .label__spot.is-dragging { cursor: grabbing; }
/* A box that has left the sticker, or landed on top of another one. Named in
   the line under the preview as well - this is so the eye finds which one
   without reading. */
.label--free.is-editing .label__spot.is-clash { outline: 1.5px solid var(--danger); }

/* ---- the five ready-made stickers ---------------------------------------
   A template is a picture, not a name. "With MRP" tells a counter nothing
   about where the MRP lands or what it costs the barcode, so each tile is the
   real sticker - same renderer, this shop's roll, this shop's name - drawn at
   full size and scaled down by the script. Which is why nothing in here sets
   a font size: the type inside a tile is in millimetres, the same as the
   thing it is a picture of.

   The row scrolls sideways rather than wrapping. Five tiles is one line on a
   desktop and two on a phone, and a second row of stickers pushes the
   editor - the thing somebody is actually working in - off the screen. */
.tpl-row {
  display: flex; gap: 8px;
  overflow-x: auto; padding-bottom: 4px;
  scroll-snap-type: x proximity;
}
.tpl {
  flex: none; padding: 4px; border-radius: var(--r-sm);
  border: 1px solid var(--line-strong); background: var(--card);
  cursor: pointer; scroll-snap-align: start;
  transition: border-color .14s, background-color .14s;
}
.tpl:hover { border-color: var(--violet-line); background: var(--violet-soft); }
.tpl.is-on { border-color: var(--violet); background: var(--violet-soft); }
.tpl:disabled { cursor: default; opacity: .6; }
.tpl:disabled:hover { border-color: var(--line-strong); background: var(--card); }
/* The paper. Its width is the tile's, its height is whatever that leaves the
   roll's own proportion - set by the script, which is the only thing that
   knows how big this shop's sticker is. */
.tpl__paper {
  display: block; width: 96px; overflow: hidden;
  background: #fff; border: 1px solid var(--line);
}
.tpl__paper .label { border: 0; transform-origin: top left; }
/* A sheet rather than a sticker. Wider, because what tells one invoice design
   from another is the letterhead's arrangement and the table's rules, and at a
   sticker's 96px those close up into one grey block. The type inside is
   unreadable at this size and is meant to be: the tile answers "how is this
   sheet SET", and the preview beside it answers everything else. */
.tpl__paper--sheet { width: 112px; }
.tpl__paper--sheet .doc {
  transform-origin: top left; box-shadow: none; margin: 0;
}
.tpl__name {
  display: block; margin-top: 4px; text-align: center;
  font-size: var(--fs-small); font-weight: 600; color: var(--ink-2);
}
.tpl.is-on .tpl__name { color: var(--violet); }
/* The design that is actually saved - what prints right now. Separate from
   is-on, which is only what is picked on the screen: somebody trying designs
   out must still be able to see which one the bills are going out in. */
.tpl { position: relative; }
.tpl__saved {
  position: absolute; top: 8px; right: 8px; z-index: 1;
  display: none; align-items: center; gap: 4px;
  height: 20px; padding: 0 8px; border-radius: 999px;
  /* The card colour for the text, so it is white on the dark green in light
     mode and dark on the pale green in dark mode - both read. */
  background: var(--ok); color: var(--card);
  font-size: var(--fs-small); font-weight: 700; line-height: 1;
  box-shadow: 0 0 0 2px var(--card);
}
.tpl.is-saved .tpl__saved { display: inline-flex; }
.tpl.is-saved { border-color: var(--ok-line); }
.tpl.is-saved.is-on { border-color: var(--violet); }

/* A field on the label, in the order it prints. Dragged with a mouse and
   moved with the arrows without one - both, and not one or the other: HTML5
   drag does not exist on a touchscreen, and half of this is used on a
   counter phone, so dragging alone would be a label nobody there could
   rearrange. The arrows are also the only way to it from a keyboard. */
/* On the canvas the row is the other way to reach a field - a 2mm box is not
   a target on a phone, so tapping the row picks the same thing tapping the
   sticker does. */
.fieldrow.is-picked { border-color: var(--violet); background: var(--violet-soft); }
.fieldrow {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 8px; border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--card); font-size: var(--fs-control);
  /* The line that shows where it would land is drawn inside the row, so it
     cannot push the rows around while somebody is aiming at them. */
  box-shadow: inset 0 0 0 0 var(--violet);
}
.fieldrow__grip {
  flex: none; letter-spacing: -2px; color: var(--faint);
  cursor: grab; user-select: none;
}
.fieldrow:active .fieldrow__grip { cursor: grabbing; }
.fieldrow__n {
  flex: none; width: 20px; text-align: center;
  font-size: var(--fs-small); font-weight: 700; color: var(--faint);
  font-variant-numeric: tabular-nums;
}
.fieldrow__what { flex: 1; min-width: 0; }

/* The five controls, as one thing - and on a desktop, as no thing at all.
   display: contents means the wrapper is not in the layout, so the row stays
   the flat flex line it has always been and every rule above still aims at
   the buttons themselves. */
.fieldrow__acts { display: contents; }

/* And on a phone they take a line of their own.
   Every control on this row floors at 44px on a touchscreen, which is right -
   they are what rearranges a label where drag does not exist. But five of them
   is 220px of buttons and 48px of gaps, and with the grip and the number that
   is 300 of the 310px a 375px phone leaves the row. The name was flexed to
   NOTHING - every row read as a grip, a number and five icons, with the last
   one hanging off the right-hand end - so on a counter phone there was no way
   to tell which field any row was.
   480px is where the arithmetic runs out (96px holds the longest label, "Job
   no in text"), not a round number picked for the look of it. */
@media (max-width: 480px) {
  .fieldrow { flex-wrap: wrap; }
  .fieldrow__acts {
    display: flex; flex: 1 0 100%; gap: 8px; justify-content: space-between;
  }
}

/* A setting that is on, on a button whose icon cannot say so by itself -
   unlike the alignment one, where the icon IS the answer. */
.fieldrow .btn--icon.is-on { background: var(--violet-soft); color: var(--violet); }

/* Being carried, and where it would land. */
.fieldrow.is-carried, .pick.is-carried { opacity: .4; }
.fieldrow.is-over-top    { box-shadow: inset 0 2px 0 0 var(--violet); }
.fieldrow.is-over-bottom { box-shadow: inset 0 -2px 0 0 var(--violet); }

@media print {
  /* The roll, and nothing else on the page. The sheet is moved onto <body>
     by the script for exactly this rule to be one line.

     The size here is only the fallback. Where a shop has set its own roll up
     the page writes a literal @page of its own - a custom property cannot be
     used in one, because @page sits outside the element tree and has nothing
     to read it from. A shop on a 38 x 25 roll printing 50mm pages would feed
     12mm of blank between every sticker. */
  /* ...and only on a page printing the roll. The script that moves the sheet
     onto <body> marks the body `is-labels`, and every rule here waits for it.
     Unscoped, "hide every child of body" ran on EVERY print in the app: the
     invoice, the jobsheet and the purchase bill all came out as one blank
     page, since the release that added barcode labels (0d213a6). The page
     size went the same way - no @page here any more; both label pages write
     their own from the shop's roll, and every other page from its paper. */
  body.is-labels, html:has(> body.is-labels) { margin: 0; padding: 0; background: #fff; }
  body.is-labels > * { display: none !important; }
  body.is-labels > .label-sheet { display: block !important; }
  /* One sticker, one label. A roll feeds them one at a time. */
  .label { break-after: page; page-break-after: always; }
  .label:last-child { break-after: auto; page-break-after: auto; }
}

/* The money strip. Three numbers, and the one that is owed is the loud one. */
.money-strip { display: grid; grid-template-columns: repeat(3, 1fr); border-top: 1px solid var(--line); }
.money-strip > div { padding: 14px 18px; text-align: center; }
.money-strip > div + div { border-left: 1px solid var(--line); }
.money-strip__k { font-size: 11px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--faint); }
.money-strip__v { margin-top: 4px; font-family: var(--font-head); font-size: 19px; font-weight: 700; font-variant-numeric: tabular-nums; }
.money-strip__v.is-due { color: var(--danger); }
.money-strip__v.is-clear { color: var(--ok); }

/* ---- timeline ------------------------------------------------------------- */
.timeline { position: relative; padding: 4px 18px 18px; }
.timeline::before {
  content: ""; position: absolute; left: 25px; top: 14px; bottom: 26px;
  width: 1px; background: var(--line);
}
.tl-item { position: relative; display: flex; gap: 14px; padding: 10px 0; }
.tl-dot {
  position: relative; z-index: 1; flex: none;
  display: grid; place-items: center; width: 15px; height: 15px; margin-top: 3px;
  border-radius: 999px; background: var(--card);
  border: 2px solid var(--line-strong);
}
.tl-item--status .tl-dot { border-color: var(--info); }
.tl-item--payment .tl-dot { border-color: var(--ok); }
.tl-item--created .tl-dot { border-color: var(--violet); }
.tl-body { min-width: 0; flex: 1; }
.tl-body strong { font-size: 13.5px; font-weight: 600; }
.tl-body p { font-size: 13px; color: var(--muted); margin-top: 1px; word-break: break-word; }
.tl-meta { font-size: 11.5px; color: var(--faint); margin-top: 3px; }

/* ---- intake form ---------------------------------------------------------- */
.check-grid {
  display: grid; gap: 8px 14px; margin-top: 8px;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
}

/* The customer picker reuses the referrer typeahead, so it only needs the
   green confirmation treatment that says "this one is locked in". */
.cust-chosen {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 11px; border: 1px solid var(--ok-line);
  border-radius: var(--r-sm); background: var(--ok-bg);
}
.cust-chosen .avatar { background: var(--ok); color: var(--ok-bg); }

.actions-row { display: flex; flex-wrap: wrap; gap: 8px; padding: 14px 18px; border-top: 1px solid var(--line); }

/* ---- photos ---------------------------------------------------------------
   Square tiles, because a phone photographed at the counter is portrait, the
   same phone on a laptop is landscape, and a grid that changes shape with its
   contents looks like a mistake. */
.shots { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 10px; }
/* A strip of thumbnails the height of a text input, for a photos field that
   shares a row with two ordinary ones. Square tiles in that spot left the
   other two fields floating against a column three times their height, which
   reads as a mistake however tidy each piece is on its own.

   44px is not a round number picked by eye - it is what .input measures with
   its own padding and line-height. Two pixels short was enough to read as
   wrong: the eye lines up the tops and bottoms of a row before it reads any
   of it, so a box that matches neither is the first thing it catches on. */
.shots--row { display: flex; flex-wrap: wrap; gap: 6px; }
.shots--row .shot,
.shots--row .picker--tile { flex: none; width: 44px; height: 44px; aspect-ratio: auto; }
.shots--row .shot__x { top: 2px; right: 2px; width: 17px; height: 17px; }
.shots--row .shot--add { gap: 0; }
/* No room for a word next to the plus at this size. */
.shots--row .shot--add span { display: none; }

/* The add control as the square after the last photo, rather than a button
   floating beside an empty grid. */
.shot--add {
  display: grid; place-items: center; align-content: center; gap: 2px;
  width: 100%; height: 100%;
  border: 1px dashed var(--line-strong); border-radius: var(--r-sm);
  background: var(--card); color: var(--muted);
  font-size: 11px; font-weight: 600;
  transition: border-color .14s, color .14s, background-color .14s;
}
.shot--add:hover { border-color: var(--violet); color: var(--violet); background: var(--violet-soft); }
.picker--tile { display: block; aspect-ratio: 1; }
.shot {
  position: relative; aspect-ratio: 1; overflow: hidden;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--raised);
}
.shot img { width: 100%; height: 100%; object-fit: cover; cursor: zoom-in; }
.shot.is-busy { opacity: .4; }
.shot__x {
  position: absolute; top: 5px; right: 5px;
  display: grid; place-items: center; width: 24px; height: 24px;
  border-radius: 999px; background: rgba(12, 12, 22, .66); color: #fff;
  opacity: 0; transition: opacity .14s;
}
.shot:hover .shot__x, .shot__x:focus-visible { opacity: 1; }
/* No hover on a touch screen, so the button has to be there already. */
@media (hover: none) { .shot__x { opacity: 1; } }

.lightbox {
  position: fixed; inset: 0; z-index: 90;
  display: grid; place-items: center; padding: 22px;
  background: rgba(8, 8, 16, .92);
}
.lightbox img { max-width: 100%; max-height: 84vh; border-radius: var(--r); }
.lightbox__bar { position: absolute; top: 14px; right: 14px; display: flex; gap: 8px; }
.lightbox__bar .btn { background: rgba(255, 255, 255, .12); color: #fff; border-color: transparent; }
.lightbox__bar .btn:hover { background: rgba(255, 255, 255, .22); }
.lightbox__meta { position: absolute; bottom: 16px; left: 0; right: 0; text-align: center; color: rgba(255,255,255,.72); font-size: 12.5px; }

.cam__stage {
  position: relative; overflow: hidden; aspect-ratio: 4 / 3;
  border-radius: var(--r); background: #000;
}
.cam__stage video { width: 100%; height: 100%; object-fit: cover; }

/* ---- handover -------------------------------------------------------------
   A pattern is drawn, not described. "L shape from the top left" is a sentence
   a technician has to decode with somebody's phone in one hand. */
.pattern {
  position: relative; display: grid; gap: 0;
  grid-template-columns: repeat(3, 80px); grid-template-rows: repeat(3, 80px);
  width: 240px; padding: 0; margin-top: 7px;
  border: 1px solid var(--line-strong); border-radius: var(--r);
  background: var(--raised); touch-action: none; user-select: none;
}
.pattern__ink { position: absolute; inset: 0; width: 100%; height: 100%; color: var(--violet); pointer-events: none; }
.pattern__dot {
  position: relative; display: grid; place-items: center;
  width: 80px; height: 80px; background: none;
}
.pattern__dot::before {
  content: ""; width: 18px; height: 18px; border-radius: 999px;
  background: var(--line-strong);
  transition: background-color .1s ease-out, transform .1s ease-out, box-shadow .1s ease-out;
}
.pattern__dot:hover::before { background: var(--violet-line); }
/* The halo is the confirmation. A dot that only changes colour under a
   fingertip is a dot you cannot see land, and on a pattern that is the
   difference between drawing it once and drawing it four times. */
.pattern__dot.is-on::before {
  background: var(--violet); transform: scale(1.3);
  box-shadow: 0 0 0 8px var(--violet-soft);
}
/* The order is what makes a drawn pattern readable back: 1, 2, 3, 4. */
.pattern__dot.is-on::after {
  content: attr(data-order); position: absolute;
  font-size: 10px; font-weight: 700; color: #fff;
}
html[data-theme="dark"] .pattern__dot.is-on::after { color: #16161f; }
.pattern__foot {
  display: flex; align-items: center; gap: 6px;
  width: 240px; margin-top: 8px; font-size: 13px; color: var(--muted);
}
.pattern__foot .mono { flex: 1; }

/* Read back, not drawn: on the jobsheet the grid is a picture of the pattern,
   so nothing in it should look like it wants pressing. */
.pattern--static { cursor: default; }
.pattern--static .pattern__dot { pointer-events: none; }
/* While it plays back, the dots it has not reached yet stay quiet - the point
   is to watch one line arrive at a time. */
.pattern.is-replaying .pattern__dot:hover::before { background: var(--line-strong); }

/* The question Add product opens on. Three doors, because a form can only be
   one shape: a handset is asked for its IMEIs, a cover for nothing of the
   sort, a part for what it fits and what grade it is.
   Inventory itself used to open on the same three cards and no longer does -
   a list of all three is a useful thing to look at, so the kind is a .chip
   over one list there. */
.pickcard {
  display: flex; align-items: center; gap: 13px;
  padding: 18px; text-decoration: none; color: inherit;
  transition: box-shadow .16s, transform .16s, border-color .16s;
}
.pickcard:hover {
  border-color: var(--violet-line);
  box-shadow: var(--lift-3);
  transform: translateY(-1px);
}
.pickcard__icon {
  display: grid; place-items: center; flex: none;
  width: 42px; height: 42px; border-radius: 11px;
  background: var(--violet-soft); color: var(--violet);
}
.pickcard__body { flex: 1; min-width: 0; }
.pickcard__body b { display: block; font-family: var(--font-head); font-size: 16px; }
.pickcard__body small { display: block; margin-top: 3px; font-size: 12.5px; color: var(--muted); line-height: 1.45; }
.pickcard__n {
  flex: none; font-family: var(--font-head); font-size: 21px; font-weight: 700;
  color: var(--ink-2); font-variant-numeric: tabular-nums;
}

/* Picking one of the three kinds of stock was a radio card at the top of the
   form; it is a screen of its own now, and the only screen that still asks. */

/* ---- one of a few, chosen deliberately -----------------------------------
   Rows, not a <select>. When the choice IS the step - how a bill was settled,
   which way a thing is being done - a closed dropdown hides two thirds of it
   behind a click, and the shopkeeper has to open it to find out what the
   question even was.
   The whole row is the target. A thumb should not have to find the dot, and
   44px comes free from the touch block at the end of this file. */
.choose { display: grid; gap: 8px; }
.choose__opt {
  display: flex; align-items: center; gap: 12px;
  padding: var(--pad-card-y) var(--pad-card);
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--card); cursor: pointer;
  transition: border-color .12s, background-color .12s;
}
.choose__opt:hover { border-color: var(--violet-line); }
.choose__opt.is-on { border-color: var(--violet); background: var(--violet-soft); }
.choose__opt input[type="radio"] { flex: none; width: 18px; height: 18px; accent-color: var(--violet); }
.choose__what { flex: 1; min-width: 0; font-size: var(--fs-body); font-weight: 500; }
.choose__sub  { display: block; margin-top: 2px; font-weight: 400; font-size: var(--fs-small); color: var(--muted); }
.choose__fig  { flex: none; font-variant-numeric: tabular-nums; font-weight: 700; }
.choose__fig .input { width: 132px; text-align: right; }

/* The category list. A built-in and one the shop added look the same, because
   in the box that uses them they behave the same - only one of them is a row
   that can be removed, so only that one carries an x. Not .chip, which is a
   filter you can press: these are labels, and a hover that lights up would
   promise a click that is not there. */
.cats { display: flex; flex-wrap: wrap; gap: 7px; }
.cat {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 6px 5px 12px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--card);
  font-size: 13px; font-weight: 500;
}
.cat--fixed { padding-right: 12px; }
.cat__n { font-variant-numeric: tabular-nums; font-size: 11.5px; color: var(--muted); }
.cat__x {
  display: grid; place-items: center; flex: none;
  width: 20px; height: 20px; padding: 0;
  border: 0; border-radius: 999px; background: none;
  color: var(--faint); font-size: 15px; line-height: 1; cursor: pointer;
  transition: background-color .12s, color .12s;
}
.cat__x:hover { background: var(--danger-bg); color: var(--danger); }

/* The box that adds one, under the list it adds to. */
.catadd { display: flex; align-items: center; gap: 8px; margin-top: 14px; flex-wrap: wrap; }
.catadd .input { max-width: 260px; }

/* The four documents that get printed. Each is a slot rather than a row in a
   list, because there is exactly one of each and what matters is whether it is
   there - which a picture answers and a filename does not. */
.slots { display: grid; gap: 12px; grid-template-columns: 1fr; }
@media (min-width: 640px) { .slots { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1000px) { .slots { grid-template-columns: repeat(4, 1fr); } }
/* Three of them - a team member's Aadhaar front, back and PAN. Side by side
   from a tablet up, so the two halves of one card sit next to each other. */
@media (min-width: 640px) { .slots--3 { grid-template-columns: repeat(3, 1fr); } }
a.slot__art { color: inherit; text-decoration: none; }
.slot {
  display: flex; flex-direction: column; gap: 10px;
  padding: 14px; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--card);
}
.slot__head b { display: block; font-size: 14px; }
.slot__head small { display: block; margin-top: 3px; font-size: 12px; color: var(--muted); line-height: 1.45; }
/* A fixed box so four slots line up whatever shape the images are, and so an
   empty one is the same size as a filled one rather than collapsing. */
.slot__art {
  display: grid; place-items: center;
  height: 110px; padding: 8px; border-radius: var(--r-sm);
  background: var(--raised); overflow: hidden;
}
.slot__art img { max-width: 100%; max-height: 94px; object-fit: contain; }
/* A QR drawn from the UPI ID is square and must be whole to scan, so the box
   grows to it instead of cropping it to the 110px the photos share. */
.slot__art:has(svg) { height: auto; }
.slot__art svg { width: 144px; height: 144px; display: block; border-radius: var(--r-sm); }
.slot__none { font-size: 12.5px; color: var(--faint); }
.slot__do { display: flex; flex-wrap: wrap; gap: 6px; margin-top: auto; }

/* Signing with a finger, for an owner who has no scan of their own signature. */
.signbox { margin-top: 16px; }
.signbox__pad { position: relative; }
.signbox__pad canvas {
  display: block; width: 100%; height: auto; touch-action: none;
  border: 1px solid var(--line-strong); border-radius: var(--r);
  background: var(--card); cursor: crosshair;
}
.signbox__hint {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-size: 13px; color: var(--faint); pointer-events: none;
}

/* Entering handsets. One row is one PHONE, and all three of its numbers are
   on screen at once.

   The version before this kept a single scan box and hid the second IMEI and
   the serial behind a "+" on each chip. Fewer boxes, but somebody had to know
   the "+" was there and what it was for. Three labelled boxes need nobody to
   explain them. */
.phones { margin-top: 9px; }

.phones__head {
  display: grid; gap: 8px; padding: 0 2px 5px;
  grid-template-columns: 20px 1fr 1fr 1fr 34px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--muted);
}

.phones__list { display: grid; gap: 7px; }

.phone {
  display: grid; gap: 8px; align-items: center;
  grid-template-columns: 20px 1fr 1fr 1fr 34px;
}
.phone__n { font-size: 11.5px; color: var(--muted); text-align: right; }
.phone__cell { display: block; min-width: 0; }
/* On a wide screen the column headings say what each box is, so repeating
   them on every row would be the same word forty times. On a phone the
   headings are gone and the labels are the only thing telling three numbers
   apart, so they come back - see the block at the bottom. */
.phone__lab { display: none; }
.phone .input { height: var(--h-control); font-size: 13px; }

.phone__x {
  display: grid; place-items: center; width: 30px; height: 30px;
  border-radius: 8px; color: var(--muted); background: none;
}
.phone__x:hover { background: var(--danger-bg); color: var(--danger); }

.phones__add {
  margin-top: 9px; padding: 7px 12px; font-size: 12.5px; font-weight: 600;
  border: 1px dashed var(--line); border-radius: 9px;
  color: var(--muted); background: none;
}
.phones__add:hover { border-color: var(--brand); color: var(--brand); }

/* Narrow: one card per phone, its three numbers stacked and labelled. Three
   boxes side by side on a 375px screen is three boxes too small to read a
   fifteen digit number in. */
@media (max-width: 720px) {
  .phones__head { display: none; }
  .phones__list { gap: 10px; }
  .phone {
    grid-template-columns: 1fr 34px;
    gap: 8px 10px; padding: 10px 10px 12px;
    border: 1px solid var(--line); border-radius: 12px; background: var(--raised);
  }
  .phone__n {
    grid-column: 1; text-align: left; font-weight: 700; color: var(--brand);
  }
  .phone__n::before { content: 'Phone '; font-weight: 600; color: var(--muted); }
  .phone__x { grid-column: 2; grid-row: 1; justify-self: end; }
  .phone__cell { grid-column: 1 / -1; }
  .phone__lab {
    display: block; margin-bottom: 3px;
    font-size: 11px; font-weight: 600; color: var(--muted);
  }
}

/* ---- the other way of entering units -------------------------------------
   assets/js/scanbox.js. One box, one number per unit, the numbers held as
   chips while they go in and as boxes once they are in. Which of the two a
   shop gets is set in Purchases > Configure; see migration 031.
   Kept apart from .phones on purpose rather than shared: the two look alike
   in a screenshot and are not the same thing, and a rule written to suit both
   is a rule that will be bent the next time either one moves. */
.units { margin-top: 8px; }
/* Under a field label the label already sets the gap, the same as for any
   box - an extra margin sat the Opening stock box lower than its row. */
.field .units { margin-top: 0; }
.units__list { display: grid; gap: 8px; }

.unitbox {
  display: grid; gap: 8px; align-items: center;
  grid-template-columns: 20px 1fr auto 30px;
}
.unitbox__n { font-size: 11.5px; color: var(--muted); text-align: right; }
.unitbox .input { height: var(--h-control); font-size: 13px; }
.unitbox__tag {
  font-size: 10px; font-weight: 700; letter-spacing: .05em;
  padding: 3px 7px; border-radius: 6px;
  background: var(--raised); color: var(--muted);
}
.unitbox__x {
  display: grid; place-items: center; width: 30px; height: 30px;
  border-radius: 8px; color: var(--muted); background: none;
}
.unitbox__x:hover { background: var(--danger-bg); color: var(--danger); }

/* A row, not a button. It sits where the three labelled boxes sit in the other
   style and it is labelled the same way - a dashed outline saying "Enter 1
   number" read as a prompt nagging for something rather than as part of the
   form. What goes in it is named inside it, because a row with no heading is
   a row nobody knows what to put in. */
.units__add {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center;
  gap: 10px; width: 100%; min-height: var(--h-control); margin-top: 8px; padding: 0 11px;
  text-align: left; border-radius: 9px;
  border: 1px solid var(--line-strong); background: var(--card);
}
.units__add:hover { border-color: var(--brand); }
/* Nothing scanned yet: the button is the first thing under the label, so it
   lines up with every other box on the row. */
.units__list:empty + .units__add { margin-top: 0; }
.units__add:hover .units__go { color: var(--brand); }
.units__lab {
  font-size: 10px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--muted);
}
.units__say { font-size: 13px; color: var(--ink-2); }
.units__go  { display: grid; place-items: center; color: var(--muted); }

/* ---- the popup the numbers go into --------------------------------------- */
.scanpop {
  position: fixed; inset: 0; z-index: 90;
  display: grid; place-items: center; padding: 16px;
  background: rgba(16, 16, 26, .55);
}
.scanpop__box {
  width: 100%; max-width: 560px; max-height: 88vh; overflow: auto;
  padding: 18px; border-radius: 16px;
  border: 1px solid var(--line); background: var(--surface);
  box-shadow: 0 18px 50px rgba(0, 0, 0, .28);
}
.scanpop__head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; margin-bottom: 12px;
}
.scanpop__title { font-size: 15px; }
.scanpop__count {
  font-size: 12.5px; font-weight: 700; color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.scanpop__count.is-full { color: var(--brand); }

.scanpop__chips {
  display: flex; flex-wrap: wrap; gap: 7px;
  min-height: var(--h-control); margin-bottom: 10px;
}
.scanpop__entry { display: flex; gap: 8px; }
.scanpop__in { flex: 1; height: var(--h-control); font-size: 15px; }
.scanpop__cam {
  display: grid; place-items: center; width: 44px; height: 44px; flex: none;
  border: 1px solid var(--line-strong); border-radius: 10px;
  color: var(--ink-2); background: var(--card);
}
.scanpop__cam:hover { border-color: var(--brand); color: var(--brand); }
.scanpop__cam.is-on { border-color: var(--brand); background: var(--violet-soft); color: var(--brand); }

.scanpop__keys { margin-top: 7px; }
.scanpop__keys b { font-weight: 600; color: var(--text); }
/* Not every line under this box is a complaint. "12 added" is news. */
.scanpop__err.is-note { color: var(--muted); }

.scanpop__foot {
  display: flex; justify-content: flex-end; gap: 8px; margin-top: 14px;
}

/* A number that is in, with what the software decided it was. After .chip, so
   the later rule wins at equal specificity - the same trap .chip--warn fell
   into above. */
.chip--unit { padding-right: 5px; font-family: var(--font-mono); font-weight: 600; }
.chip--unit i { font-style: normal; font-size: 9.5px; font-weight: 700;
                letter-spacing: .05em; color: var(--muted); }
.chip--unit button {
  display: grid; place-items: center; width: 20px; height: 20px;
  border-radius: 999px; color: var(--muted); background: none;
}
.chip--unit button:hover { background: var(--danger-bg); color: var(--danger); }

/* A button that reads as a sentence rather than as a control, for the offer
   to go past the quantity somebody typed. */
.linkbtn {
  padding: 0; font: inherit; font-weight: 700;
  color: var(--brand); background: none; text-decoration: underline;
}

/* ---- a finished line, folded away ----------------------------------------
   A line carries a dozen boxes. Three items filled the page and nobody could
   see what had already been entered, so a finished one folds to a single row
   and a fresh one opens under it. The row itself reopens it - a pencil beside
   an x is two small targets where one big one does. */
.foldrow {
  display: flex; align-items: center; gap: 8px;
  padding: 2px 0;
}
.foldrow__open {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 9px;
  padding: 8px 11px; text-align: left; border-radius: 9px;
  border: 1px solid var(--line); background: var(--card);
}
.foldrow__open:hover { border-color: var(--brand); }
.foldrow__open > svg { flex: none; color: var(--ok); }
.foldrow__what { flex: 1; min-width: 0; display: flex; align-items: baseline; gap: 8px; }
.foldrow__what b { font-size: 13.5px; }
.foldrow__what small {
  font-size: 11.5px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.foldrow__amt { flex: none; font-size: 13.5px; font-variant-numeric: tabular-nums; }
.foldrow__act {
  display: grid; place-items: center; width: 30px; height: 30px; flex: none;
  border-radius: 8px; color: var(--muted); background: none;
}
.foldrow__act:hover { background: var(--danger-bg); color: var(--danger); }

.donerow { display: flex; justify-content: flex-end; padding: 4px 0 2px; }

.linefoot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-top: 12px;
}
.linefoot__sum {
  font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums;
}

/* ---- the rate, with or without GST ---------------------------------------
   The toggle sits inside the box because it is about that one number. The
   working goes under it, every figure on screen, so a wrong one is findable
   instead of having to be trusted. See assets/js/gst.js. */
/* Under the box, not inside it. The rate column is about 130px wide and the
   two words take most of that - put over the input and the number it is about
   disappears behind them, which was the first thing tried. */
.ratebox { display: grid; gap: 4px; }

.ratebox__gst {
  display: grid; grid-template-columns: 1fr 1fr;
  border: 1px solid var(--line); border-radius: 7px; overflow: hidden;
}
.ratebox__gst button {
  padding: 4px 2px; font-size: 10px; font-weight: 600; white-space: nowrap;
  color: var(--muted); background: var(--surface);
}
.ratebox__gst button:hover { color: var(--brand); }
.ratebox__gst button.is-on { background: var(--brand); color: #fff; }

/* The number that is not in the box, said loudly enough to check against the
   dealer's paper without leaning in. Muted 10px was there first and nobody
   read it - which defeats the point of showing the working at all. */
.ratebox__says {
  display: flex; align-items: baseline; gap: 5px;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.ratebox__says small { font-size: 10px; color: var(--muted); }
.ratebox__says b     { font-size: 13px; color: var(--brand); font-weight: 700; }

/* ---- picking one of the two, on Configure --------------------------------
   Shown as small pictures of the real thing. "One box" and "three boxes"
   describe nothing to somebody who has not seen either, and this is a choice
   made once a year - worth the room. */
.styles { display: grid; gap: 10px; }
@media (min-width: 720px) { .styles { grid-template-columns: 1fr 1fr; } }

.styles__opt {
  display: grid; grid-template-columns: 18px 1fr; gap: 10px;
  /* Top, not centre. The two options are different heights and a radio
     floating beside the middle of a paragraph does not read as belonging to
     the heading above it. */
  align-items: start;
  padding: 13px; cursor: pointer;
  border: 1px solid var(--line); border-radius: 12px; background: var(--card);
}
.styles__opt:hover { border-color: var(--violet-line); }
.styles__opt:has(input:checked) { border-color: var(--violet); background: var(--violet-soft); }
.styles__opt input { margin-top: 3px; }
.styles__body { display: grid; gap: 7px; min-width: 0; }
.styles__body strong { font-size: 13.5px; }
.styles__body small { font-size: 11.5px; line-height: 1.45; color: var(--muted); }
.styles__warn { color: var(--warn) !important; font-weight: 600; }

.styles__pic {
  padding: 9px 10px; border-radius: 9px; overflow: hidden;
  border: 1px dashed var(--line-strong); background: var(--raised);
  font-size: 10px; line-height: 1.9; color: var(--ink-2); white-space: nowrap;
}
.styles__hd {
  display: block; font-size: 8.5px; font-weight: 700;
  letter-spacing: .06em; color: var(--muted);
}

/* ---- the viewfinder ------------------------------------------------------ */
.camscan { margin-top: 10px; }
.camscan__v {
  display: block; width: 100%; max-height: 42vh; border-radius: 12px;
  background: #000; object-fit: cover;
}
/* The frame the person lines the barcode up inside. It is also the only part
   that is decoded - on a phone box the second IMEI is printed a few
   millimetres under the first. See assets/js/camscan.js. */
.camscan__aim {
  position: relative; margin-top: -42vh; height: 42vh;
  display: grid; place-items: center; pointer-events: none;
}
.camscan__aim span {
  width: 86%; height: 34%; border-radius: 8px;
  border: 2px solid rgba(255, 255, 255, .9);
  box-shadow: 0 0 0 100vmax rgba(0, 0, 0, .35);
}
.camscan__bar { display: flex; gap: 8px; margin-top: 8px; }
.camscan__torch, .camscan__off {
  padding: 6px 12px; font-size: 12.5px; font-weight: 600;
  border: 1px solid var(--line-strong); border-radius: 8px;
  color: var(--ink-2); background: var(--card);
}
.camscan__torch.is-on { border-color: var(--warn); background: var(--warn-bg); color: var(--warn); }

@media (max-width: 720px) {
  .units__list { gap: 8px; }
  .unitbox { grid-template-columns: 18px 1fr auto 30px; gap: 6px; }
  .scanpop { padding: 0; place-items: end stretch; }
  .scanpop__box { max-width: none; max-height: 92vh; border-radius: 16px 16px 0 0; }
}

.sign-pad {
  position: relative; max-width: 560px;
  border: 1px dashed var(--line-strong); border-radius: var(--r);
  background: #fff; overflow: hidden;
}
.sign-pad canvas { display: block; width: 100%; height: auto; touch-action: none; cursor: crosshair; }
.sign-pad__hint {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-size: 14px; color: #b4b4c4; pointer-events: none;
}
.sign-done { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; }
.sign-done img {
  max-width: 340px; width: 100%;
  border: 1px solid var(--line); border-radius: var(--r); background: #fff;
}

/* One button, and a menu that answers "how" only once it has been pressed. */
.picker { position: relative; display: inline-block; }
.picker__menu {
  position: absolute; z-index: 30; top: calc(100% + 6px); left: 0;
  min-width: 170px; padding: 5px;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--lift-3);
}
/* The button often sits at the bottom of a card near the bottom of the
   screen, where a menu that only ever drops downwards is a menu nobody can
   see. The script adds this when there is no room below. */
.picker__menu.is-up { top: auto; bottom: calc(100% + 6px); }
.picker__opt {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 11px; border-radius: 7px;
  font-size: 13.5px; font-weight: 500; color: var(--ink-2); text-align: left;
}
.picker__opt:hover { background: var(--violet-soft); color: var(--violet); }
.picker__opt svg { flex: none; color: var(--faint); }
.picker__opt:hover svg { color: var(--violet); }

/* The save bar on a full-page form.
   Sticky, because intake is taller than a screen and a Save button you have to
   go looking for is a Save button people stop trusting. */
.form-bar {
  position: sticky; bottom: 14px; z-index: 30;
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin-top: 18px; padding: 13px 16px;
  background: color-mix(in srgb, var(--card) 92%, transparent);
  backdrop-filter: blur(9px);
  border: 1px solid var(--line-strong); border-radius: var(--r-lg);
  box-shadow: var(--lift-3);
}
.form-bar__msg { flex: 1; min-width: 150px; font-size: 13px; margin: 0; }
@media (max-width: 560px) {
  .form-bar { flex-direction: column; align-items: stretch; }
  .form-bar .btn { width: 100%; justify-content: center; }
  /* On a phone the bar floats over the form, so it earns its height. The
     hint is nice to have; an error is not, and stays. */
  .form-bar__hint { display: none; }
}

@media (max-width: 560px) {
  .money-strip { grid-template-columns: 1fr; }
  .money-strip > div + div { border-left: none; border-top: 1px solid var(--line); }
}

/* ---------------------------------------------------------------------------
   16. RESPONSIVE
   --------------------------------------------------------------------------- */
@media (min-width: 1024px) {
  .sidebar { position: sticky; height: 100vh; transform: none; flex: none; }
  .sidebar__close, .topbar__burger, .scrim { display: none; }
  .content { padding: 26px 26px 60px; }
  .topbar  { padding: 0 26px; }

  /* Focus: one job gets the whole width. The menu is the drawer it is on a
     phone - out of the way, one tap on the burger brings it back. */
  .is-focus .sidebar { position: fixed; height: auto; transform: translateX(-100%); }
  .is-focus .sidebar.is-open { transform: none; }
  .is-focus .sidebar__close, .is-focus .topbar__burger { display: inline-flex; }
  .is-focus .scrim:not([hidden]) { display: block; }
}

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

/* ---------------------------------------------------------------------------
   A FINGER IS NOT A MOUSE POINTER
   ---------------------------------------------------------------------------
   Everything above is sized for a cursor, which can land on a 32px button all
   day. A thumb cannot. The counter phone is where half of this software is
   used, and a control somebody has to aim at twice is a control they stop
   using.

   The floor is 44x44 CSS pixels: Apple's Human Interface Guidelines and WCAG
   2.5.5 (AAA) both say 44, Material says 48 where a thumb is expected, and
   WCAG 2.5.8 (AA) will accept 24 only when the spacing around it makes up the
   difference. 24 is the floor you are allowed, not the one to aim at.

   `pointer: coarse` and not a width breakpoint, because the question is what
   is touching the screen rather than how wide it is - a touchscreen laptop
   gets the bigger targets and a narrow desktop window does not need them.
   --------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------
   THE BENCH (bench.php) and MY WORK (my-work.php)
   One job from the technician's side: the job card on the left, the six
   steps on the right, only the current one open. Below 900px the card turns
   into a strip on top - due, lock, call - and opens for the rest.
   --------------------------------------------------------------------------- */
.bench { display: flex; flex-direction: column; gap: var(--gap-card); }
.bench__bar {
  display: flex; align-items: center; gap: 12px;
  padding: 8px 12px; background: var(--card); border: 1px solid var(--line); border-radius: var(--r);
}
.bench__title { display: flex; flex-direction: column; min-width: 0; }
.bench__title .jobno { color: var(--muted); font-size: var(--fs-small); }
.bench__title strong { font-size: var(--fs-card-h); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bench__acts { display: flex; gap: 8px; margin-left: auto; flex: none; }

.bench__track { display: flex; gap: 4px; list-style: none; margin: 0 0 0 16px; padding: 0; flex: 1; min-width: 0; }
.btrack__i { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 4px; font-size: var(--fs-small); color: var(--faint); position: relative; }
.btrack__i::before { content: ''; position: absolute; top: 12px; left: -50%; width: 100%; height: 2px; background: var(--line); z-index: 0; }
.btrack__i:first-child::before { display: none; }
.btrack__i span {
  position: relative; z-index: 1; width: 24px; height: 24px; border-radius: 50%;
  display: grid; place-items: center; font-size: var(--fs-small); font-weight: 600;
  background: var(--card); border: 1px solid var(--line-strong);
}
.btrack__i em { font-style: normal; }
.btrack__i button {
  position: relative; z-index: 1; display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 0; background: none; border: 0; font: inherit; color: inherit; cursor: pointer;
}
.btrack__i button:hover span { border-color: var(--violet); box-shadow: 0 0 0 4px var(--violet-glow); }
.btrack__i button:focus-visible { outline: 2px solid var(--violet); outline-offset: 4px; border-radius: var(--r-sm); }
.btrack__i--todo button { cursor: default; }
.btrack__i--todo button:hover span { border-color: var(--line-strong); box-shadow: none; }
.btrack__i--done.is-here span { border: 2px solid var(--ok); box-shadow: 0 0 0 4px var(--ok-bg); }
.btrack__i.is-here em { font-weight: 700; }
.btrack__i--done { color: var(--ok); }
.btrack__i--done::before, .btrack__i--now::before { background: var(--ok-line); }
.btrack__i--done span { background: var(--ok-bg); border-color: var(--ok-line); color: var(--ok); }
.btrack__i--now { color: var(--violet); font-weight: 600; }
.btrack__i--now span { background: var(--violet-soft); border: 2px solid var(--violet); color: var(--violet); }

.bench__grid { display: grid; grid-template-columns: minmax(280px, 340px) minmax(0, 1fr); gap: var(--gap-card); align-items: start; }

/* ---- the job card ---- */
.jcard { background: var(--card); border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; position: sticky; top: 76px; }
.jcard__head { display: flex; align-items: center; gap: 12px; padding: var(--pad-card); }
.jcard__icon { width: 40px; height: 40px; flex: none; border-radius: var(--r-sm); display: grid; place-items: center; background: var(--violet-soft); color: var(--violet); }
.jcard__dev { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.jcard__dev strong { font-size: var(--fs-card-h); }
.jcard__dev span { font-size: var(--fs-small); color: var(--muted); }
.jcard__tags { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }

.jcard__due { padding: 12px 16px; border-top: 1px solid var(--line); }
.jcard__due--late { background: var(--danger-bg); color: var(--danger); }
.jcard__due--soon { background: var(--warn-bg); color: var(--warn); }
.jcard__due--ok   { background: var(--ok-bg); color: var(--ok); }
.jcard__due--calm { background: var(--raised); color: var(--muted); }
.jcard__dueline { display: flex; justify-content: space-between; gap: 8px; font-size: var(--fs-small); }
.jcard__big { display: flex; align-items: center; gap: 8px; font-size: var(--fs-h1); font-weight: 700; margin: 4px 0 8px; }
.jcard__bar { height: 4px; border-radius: 4px; background: var(--card); overflow: hidden; margin-bottom: 4px; }
.jcard__bar i { display: block; height: 100%; background: currentColor; }

.jcard__quick { display: none; }
.jcard__sec { padding: 12px 16px; border-top: 1px solid var(--line); }
.jcard__lb { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-bottom: 8px; font-size: var(--fs-small); color: var(--muted); }
.jcard__lb span:last-child { display: inline-flex; align-items: center; gap: 4px; }
.jcard__who { display: flex; align-items: center; gap: 12px; }
.jcard__who > div { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.jcard__who strong { font-size: var(--fs-body); }
.jcard__who .mono { font-size: var(--fs-small); color: var(--muted); }
.jcard__note { margin-top: 8px; font-size: var(--fs-small); color: var(--muted); }
.jcard__lock { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 8px 12px; border: 1px solid var(--line-strong); border-radius: var(--r-sm); }
.jcard__lock .mono { font-size: var(--fs-body); letter-spacing: .12em; }
.jcard__code { font-size: var(--fs-h1) !important; font-weight: 700; color: var(--violet); }
.jcard__pat { width: 72px; height: 72px; flex: none; }
.jcard__pat circle { fill: var(--line-strong); }
.jcard__pat polyline { fill: none; stroke: var(--violet); stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
.jcard__pat .jcard__pat0 { fill: none; stroke: var(--violet); stroke-width: 2; }
.jcard__kv { display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; margin: 0; font-size: var(--fs-label); }
.jcard__kv dt { color: var(--muted); }
.jcard__kv dd { margin: 0; text-align: right; overflow-wrap: anywhere; }
.jcard__chips { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 8px; }
.jcard__chips .chip { padding: 2px 8px; font-size: var(--fs-small); }
.jcard__pics { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
.jcard__pics img { width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: var(--r-sm); border: 1px solid var(--line); display: block; }
.jcard__memo { padding: 8px 12px; border-radius: var(--r-sm); background: var(--warn-bg); color: var(--warn); font-size: var(--fs-label); }

/* ---- the steps ---- */
.bsteps { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.bstep { background: var(--card); border: 1px solid var(--line); border-radius: var(--r-sm); }
.bstep__row {
  display: flex; align-items: center; gap: 12px; width: 100%; padding: 8px 12px;
  background: none; border: 0; text-align: left; font: inherit; font-size: var(--fs-label); color: inherit; min-height: var(--h-control);
}
button.bstep__row { cursor: pointer; }
.bstep__dot { width: 24px; height: 24px; flex: none; border-radius: 50%; display: grid; place-items: center; font-size: var(--fs-small); font-weight: 600; background: var(--raised); border: 1px solid var(--line); }
.bstep--done .bstep__dot { background: var(--ok-bg); border-color: var(--ok-line); color: var(--ok); }
.bstep__sum { color: var(--muted); min-width: 0; flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bstep__caret { color: var(--faint); transition: transform .15s; }
.bstep.is-open .bstep__caret { transform: rotate(180deg); }
.bstep__peek { padding: 0 12px 12px; }
.bstep--todo { color: var(--faint); background: transparent; border-style: dashed; }
.bstep--now { border: 2px solid var(--violet); border-radius: var(--r); padding: var(--pad-card); display: flex; flex-direction: column; gap: 12px; box-shadow: var(--lift-1); }
.bstep__head { display: flex; align-items: center; gap: 12px; }
.bstep__head strong { font-size: var(--fs-card-h); }
.bstep--now .bstep__dot { background: var(--violet-soft); border-color: var(--violet); color: var(--violet); }
.bstep__aside { margin-left: auto; font-size: var(--fs-small); color: var(--muted); }
.bstep__back { margin-left: auto; }
.bstep--again { border-color: var(--ok); }
.bstep--again .bstep__head .bstep__dot { background: var(--ok-bg); border-color: var(--ok-line); color: var(--ok); }
.bstep--here { border: 1px dashed var(--violet-line); background: var(--violet-soft); }
.bstep--here .bstep__dot { background: var(--card); border-color: var(--violet); color: var(--violet); }
.bstep--here .bstep__sum { color: var(--violet); font-weight: 600; }

.bchecks { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.bcheck { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 4px 4px 4px 12px; border: 1px solid var(--line); border-radius: var(--r-sm); font-size: var(--fs-label); min-height: var(--h-control); }
.bcheck.is-ok  { border-color: var(--ok-line); background: var(--ok-bg); }
.bcheck.is-bad { border-color: var(--danger-line); background: var(--danger-bg); }
.bchecks--read .bcheck { padding-right: 12px; }
.bchecks--read .is-ok svg { color: var(--ok); }
.bchecks--read .is-bad svg { color: var(--danger); }
.bcheck__seg { display: inline-flex; gap: 4px; }
.bcheck__seg button { width: 32px; height: 28px; display: grid; place-items: center; border: 1px solid var(--line-strong); border-radius: var(--r-sm); background: var(--card); color: var(--faint); cursor: pointer; }
.bcheck__seg button[aria-pressed="true"]:first-child { background: var(--ok); border-color: var(--ok); color: #fff; }
.bcheck__seg button[aria-pressed="true"]:last-child  { background: var(--danger); border-color: var(--danger); color: #fff; }
.bchecks__all { align-self: flex-start; background: none; border: 0; padding: 0; color: var(--violet); font: inherit; font-size: var(--fs-label); font-weight: 600; cursor: pointer; }

.bacts { display: flex; justify-content: space-between; gap: 8px; flex-wrap: wrap; }
.bacts .btn--primary { margin-left: auto; }
.bdiag { min-height: 72px; }
.bsub { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.bsub .field__label { margin: 0; }
.bsub__link { display: inline-flex; align-items: center; gap: 4px; background: none; border: 0; padding: 0; font: inherit; font-size: var(--fs-label); font-weight: 600; color: var(--warn); cursor: pointer; }
.bsub__link:hover { text-decoration: underline; }
.bamt { display: flex; align-items: center; gap: 8px; max-width: 240px; }
.bamt span { font-size: var(--fs-h1); color: var(--muted); }
.bamt .input { font-size: var(--fs-h1); font-weight: 700; }
.bhint { font-size: var(--fs-small); color: var(--muted); min-height: 16px; }
.bhint--warn { color: var(--warn); font-weight: 600; }
.bwait { display: flex; align-items: center; gap: 12px; padding: 12px; border-radius: var(--r-sm); background: var(--warn-bg); color: var(--warn); }
.bwait > div { display: flex; flex-direction: column; }
.bwait span { font-size: var(--fs-small); }
.bfind { position: relative; }
.bfind__ic { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--muted); display: flex; pointer-events: none; }
.bfind .input { padding-left: 36px; }
.bparts { display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: var(--r-sm); }
.bpart { display: flex; align-items: center; gap: 12px; padding: 8px 12px; font-size: var(--fs-label); }
.bpart + .bpart { border-top: 1px solid var(--line); }
.bpart > div { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.bpart .mono { font-size: var(--fs-small); color: var(--muted); }
.bpart__amt { font-weight: 600; white-space: nowrap; }
.bpart--total { background: var(--raised); color: var(--muted); justify-content: space-between; }
.bempty { padding: 12px; text-align: center; font-size: var(--fs-label); color: var(--faint); border: 1px dashed var(--line); border-radius: var(--r-sm); }
.btimer { display: flex; align-items: center; gap: 16px; padding: 12px 16px; border-radius: var(--r-sm); background: var(--raised); }
.btimer__n { font-size: 32px; font-weight: 700; letter-spacing: -.02em; }
.btimer__s { font-size: var(--fs-small); color: var(--muted); }
.btimer .btn { margin-left: auto; }
.bchips { display: flex; flex-wrap: wrap; gap: 8px; }
.chip--btn { cursor: pointer; font: inherit; font-size: var(--fs-small); }
.bpics { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.bpic { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 12px; border: 1px dashed var(--line-strong); border-radius: var(--r-sm); font-size: var(--fs-label); cursor: pointer; color: var(--ink-2); }
.bpic em { font-style: normal; font-size: var(--fs-small); color: var(--muted); }
.bpic:hover { border-color: var(--violet); color: var(--violet); }
.bnote { margin: 0; font-size: var(--fs-label); color: var(--ink-2); }
.bready { display: flex; align-items: center; gap: 12px; padding: 16px; border-radius: var(--r-sm); background: var(--ok-bg); color: var(--ok); }
.bready > div { display: flex; flex-direction: column; }
.bready strong { font-size: var(--fs-card-h); }
.bready span { font-size: var(--fs-small); }
.bask { display: flex; flex-direction: column; gap: 4px; padding-top: 12px; border-top: 1px solid var(--line); }
.bask__row { display: flex; gap: 8px; }
.bask__row .input { flex: 1; min-width: 0; }

.btoast { position: fixed; z-index: 90; left: 50%; bottom: 24px; transform: translateX(-50%); max-width: calc(100vw - 32px);
  padding: 12px 16px; border-radius: var(--r-sm); background: var(--ink); color: var(--surface); font-size: var(--fs-label); box-shadow: var(--lift-3); }
.btoast--err { background: var(--danger); color: #fff; }

/* ---- my work ---- */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
.mw { display: flex; flex-direction: column; gap: var(--gap-card); }
.mw-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.mw-head__who { display: flex; align-items: center; gap: 12px; }
.mw-head h1 { font-size: var(--fs-h1); margin: 0; }
.mw-head p { margin: 0; font-size: var(--fs-label); color: var(--muted); }
.mw-avatar { width: 40px; height: 40px; flex: none; border-radius: var(--r); display: grid; place-items: center; background: var(--violet-soft); color: var(--violet); font-weight: 700; font-size: var(--fs-label); }
.mw-head__acts { display: flex; gap: 8px; align-items: center; }
.mw-who .select { min-width: 200px; }

/* Up next and the numbers: one strip, one border. */
.mw-strip { display: grid; grid-template-columns: minmax(0, 2.4fr) repeat(4, minmax(0, 1fr)); border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--card); overflow: hidden; }
.mw-up { display: flex; align-items: center; gap: 12px; padding: 12px 16px; background: var(--violet-soft); color: inherit; transition: background-color .12s; }
.mw-up:hover { text-decoration: none; background: var(--violet-glow); }
.mw-up__icon { width: 40px; height: 40px; flex: none; border-radius: var(--r-sm); display: grid; place-items: center; background: var(--card); color: var(--violet); }
.mw-up__txt { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.mw-up__lb { display: inline-flex; align-items: center; gap: 4px; font-size: var(--fs-small); font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--violet); }
.mw-up__lb .mono { text-transform: none; letter-spacing: 0; font-weight: 500; }
.mw-up__txt strong { font-size: var(--fs-card-h); display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.mw-up__sub { font-size: var(--fs-small); color: var(--muted); }
.mw-up__go { display: inline-flex; align-items: center; gap: 8px; height: var(--h-control); padding: 0 16px; flex: none; border-radius: var(--r-sm); background: var(--ink); color: var(--surface); font-weight: 600; font-size: var(--fs-label); }
.mw-up:hover .mw-up__go svg { transform: translateX(2px); }
.mw-up--empty { background: var(--raised); }
.mw-num { padding: 12px 16px; border-left: 1px solid var(--line); display: flex; flex-direction: column; justify-content: center; gap: 4px; }
.mw-num span { font-size: var(--fs-small); color: var(--muted); }
.mw-num strong { font-size: var(--fs-h1); line-height: 1; font-variant-numeric: tabular-nums; }
.mw-num--late strong { color: var(--danger); }
.mw-num--soon strong { color: var(--warn); }
.mw-num--ok strong { color: var(--ok); }

/* The tabs - the four columns of the old board - over one table. */
.mw-list { padding: 0; overflow: hidden; }
.mw-tabs { display: flex; gap: 4px; padding: 0 12px; border-bottom: 1px solid var(--line); overflow-x: auto; overflow-y: hidden; scrollbar-width: none; }
.mw-tabs::-webkit-scrollbar { display: none; }
.mw-tab { display: inline-flex; align-items: center; gap: 8px; padding: 12px; font-size: var(--fs-label); font-weight: 500; color: var(--muted); border-bottom: 2px solid transparent; margin-bottom: -1px; white-space: nowrap; }
.mw-tab:hover { color: var(--ink); text-decoration: none; }
.mw-tab b { padding: 0 8px; border-radius: 999px; background: var(--raised); font-size: var(--fs-small); color: var(--ink-2); }
.mw-tab__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--line-strong); }
.mw-tab--working .mw-tab__dot { background: var(--violet); }
.mw-tab--new .mw-tab__dot { background: var(--info); }
.mw-tab--waiting .mw-tab__dot { background: var(--warn); }
.mw-tab--qc .mw-tab__dot { background: var(--ok); }
.mw-tab--done { margin-left: auto; }
.mw-tab.is-on { color: var(--ink); font-weight: 600; border-bottom-color: var(--violet); }
.mw-tab--waiting.is-on { border-bottom-color: var(--warn); }
.mw-tab--qc.is-on, .mw-tab--done.is-on { border-bottom-color: var(--ok); }
.mw-tab--new.is-on { border-bottom-color: var(--info); }

.mw-why { display: flex; gap: 8px; padding: 12px 16px; background: var(--raised); border-bottom: 1px solid var(--line); flex-wrap: wrap; }
.mw-chip { display: inline-flex; align-items: center; gap: 8px; padding: 4px 12px; border-radius: 999px; border: 1px solid var(--line-strong); background: var(--card); font-size: var(--fs-label); color: var(--ink-2); }
.mw-chip:hover { text-decoration: none; border-color: var(--violet-line); }
.mw-chip b { font-weight: 600; }
.mw-chip.is-on { background: var(--ink); border-color: var(--ink); color: var(--surface); }

.mw-table { display: flex; flex-direction: column; }
.mw-tr { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1.2fr) minmax(0, 1fr) 168px; gap: 16px; align-items: center; padding: 12px 16px; border-top: 1px solid var(--line); font-size: var(--fs-label); }
.mw-table--waiting .mw-tr { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1.8fr) minmax(0, .8fr) 196px; }
.mw-th { border-top: 0; padding-top: 8px; padding-bottom: 8px; background: var(--raised); font-size: var(--fs-small); font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--faint); }
.mw-tr:not(.mw-th):hover { background: var(--raised); }
.mw-tr--late { box-shadow: inset 4px 0 0 var(--danger); }
.mw-tr--soon { box-shadow: inset 4px 0 0 var(--warn); }
.mw-tr--ok   { box-shadow: inset 4px 0 0 var(--ok); }
.mw-job { display: flex; flex-direction: column; min-width: 0; color: inherit; }
.mw-job:hover { text-decoration: none; }
.mw-job:hover strong { color: var(--violet); }
.mw-no { font-family: var(--font-mono); font-size: var(--fs-small); color: var(--violet); display: flex; align-items: center; gap: 8px; }
.mw-job strong { font-size: var(--fs-body); }
.mw-sub { display: block; font-size: var(--fs-small); color: var(--muted); font-weight: 400; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mw-urgent { padding: 0 8px; border-radius: 999px; background: var(--danger-bg); color: var(--danger); font-size: var(--fs-small); font-weight: 700; font-family: var(--font); }
.mw-stagecell { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }
.mw-stage { display: inline-flex; align-items: center; gap: 4px; padding: 2px 10px; border-radius: 999px; background: var(--raised); color: var(--ink-2); font-size: var(--fs-small); font-weight: 600; }
.mw-stage::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.mw-stage--live { background: var(--violet-soft); color: var(--violet); }
.mw-stage--ok { background: var(--ok-bg); color: var(--ok); }
.mw-steps { display: inline-flex; gap: 2px; width: 72px; }
.mw-steps i { flex: 1; height: 4px; border-radius: 2px; background: var(--line); }
.mw-steps i.is-done { background: var(--ok); }
.mw-steps i.is-now { background: var(--violet); }
.mw-when { font-weight: 600; }
.mw-when.is-late { color: var(--danger); }
.mw-when.is-soon { color: var(--warn); }
.mw-clock { font-family: var(--font-mono); color: var(--faint); display: inline-flex; align-items: center; gap: 4px; }
.mw-clock.is-live { color: var(--violet); font-weight: 600; }
.mw-clock i { width: 8px; height: 8px; border-radius: 50%; background: var(--violet); animation: mwPulse 1.4s ease-in-out infinite; }
@keyframes mwPulse { 50% { opacity: .3; } }
.mw-act { display: flex; flex-direction: column; align-items: stretch; gap: 8px; }
.mw-pair { display: flex; gap: 8px; }
.mw-pair .mw-btn { flex: 1; }
.mw-btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; height: 32px; padding: 0 12px; border-radius: var(--r-sm);
  border: 1px solid var(--line-strong); background: var(--card); color: var(--ink-2); font: inherit; font-size: var(--fs-label); font-weight: 600; white-space: nowrap; cursor: pointer; }
.mw-btn:hover { text-decoration: none; border-color: var(--violet-line); color: var(--violet); }
.mw-btn--dark { background: var(--ink); border-color: var(--ink); color: var(--surface); }
.mw-btn--dark:hover { background: var(--ink-2); color: var(--surface); }
.mw-btn--ok { background: var(--ok); border-color: var(--ok); color: #fff; }
.mw-btn--ok:hover { color: #fff; filter: brightness(.95); }
.mw-btn[disabled] { opacity: .5; }
.mw-wait { display: flex; align-items: flex-start; gap: 12px; min-width: 0; }
.mw-wait > span:last-child { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; min-width: 0; }
.mw-wait__ic { width: 32px; height: 32px; flex: none; border-radius: var(--r-sm); display: grid; place-items: center; background: var(--raised); color: var(--muted); }
.mw-wait__ic--ok { background: var(--ok-bg); color: var(--ok); }
.mw-wait__ic--cust { background: var(--warn-bg); color: var(--warn); }
.mw-pill { padding: 2px 8px; border-radius: 999px; background: var(--raised); color: var(--muted); font-size: var(--fs-small); font-weight: 600; }
.mw-pill--ok { background: var(--ok-bg); color: var(--ok); }
.mw-empty { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 48px 16px; color: var(--faint); }
.mw-empty strong { color: var(--muted); font-weight: 500; font-size: var(--fs-label); }
@media (max-width: 1100px) {
  .bench__track em { display: none; }
}
@media (max-width: 900px) {
  .bench__bar { flex-wrap: wrap; }
  .bench__track { order: 3; flex-basis: 100%; margin: 4px 0 0; }
  .bench__track em { display: block; }
  .bench__grid { grid-template-columns: minmax(0, 1fr); }
  .jcard { position: static; }
  .jcard__quick { display: grid; grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); border-top: 1px solid var(--line); }
  .jcard__q { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 8px; background: none; border: 0; font: inherit; font-size: var(--fs-small); color: var(--violet); cursor: pointer; }
  .jcard__q + .jcard__q { border-left: 1px solid var(--line); }
  .jcard__q:hover { text-decoration: none; background: var(--violet-soft); }
  .jcard.is-open .jcard__more svg { transform: rotate(180deg); }
  .jcard__rest { display: none; }
  .jcard.is-open .jcard__rest { display: block; }
  .jcard__head { padding: 12px; }
  .jcard__big { font-size: var(--fs-card-h); margin: 2px 0 4px; }
  .mw-strip { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .mw-up { grid-column: 1 / -1; }
  .mw-num:nth-child(2) { border-left: 0; }
  .mw-num { border-top: 1px solid var(--line); }
}
@media (max-width: 560px) {
  .bench__acts .btn:not(.btn--icon) { display: none; }
  .btrack__i:not(.btrack__i--now):not(.is-here) em { display: none; }
  .bchecks { grid-template-columns: minmax(0, 1fr); }
  .mw-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .mw-num:nth-child(4) { border-left: 0; }
  .mw-up { flex-wrap: wrap; }
  .mw-up__go { width: 100%; justify-content: center; }
  .mw-th { display: none; }
  .mw-tr, .mw-table--waiting .mw-tr { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 8px 12px; }
  .mw-tr .mw-job, .mw-tr .mw-wait, .mw-tr .mw-act { grid-column: 1 / -1; }
  .mw-tr .mw-act { flex-direction: row; }
  .mw-tr .mw-act > * { flex: 1; }
  .mw-tab--done { margin-left: 0; }
  .mw-head__acts { width: 100%; }
  .mw-who { flex: 1; }
  .mw-who .select { min-width: 0; width: 100%; }
  .btimer__n { font-size: 24px; }
}

/* The work and parts on a job: a service or a part per line. */
.blines { display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: var(--r-sm); }
.bline { display: grid; grid-template-columns: 32px minmax(0, 1fr) 120px 96px 32px; gap: 12px; align-items: center; padding: 8px 12px; }
.bline + .bline { border-top: 1px solid var(--line); }
.bline__ic { width: 32px; height: 32px; border-radius: var(--r-sm); display: grid; place-items: center; }
.bline__ic--svc { background: var(--violet-soft); color: var(--violet); }
.bline__ic--part { background: var(--ok-bg); color: var(--ok); }
.bline__what { display: flex; flex-direction: column; min-width: 0; }
.bline__what strong { font-size: var(--fs-label); }
.bline__what > span { font-size: var(--fs-small); color: var(--muted); display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.bline__tag { font-style: normal; font-size: var(--fs-small); font-weight: 600; padding: 0 8px; border-radius: 999px; margin-left: 4px; }
.bline__tag--svc { background: var(--violet-soft); color: var(--violet); }
.bline__tag--part { background: var(--ok-bg); color: var(--ok); }
.bline__amt { text-align: right; font-weight: 600; }
.blines__w { height: 32px; font-size: var(--fs-small); padding: 0 8px; }
.blines__p { height: 32px; text-align: right; font-weight: 600; padding: 0 8px; }
.blines__mat { width: 64px; height: 24px; padding: 0 4px; border: 1px solid var(--line-strong); border-radius: 4px; font: inherit; font-size: var(--fs-small); background: var(--card); color: var(--ink); }
.btotal { display: grid; grid-template-columns: 1fr auto; gap: 4px 16px; padding: 12px; border-radius: var(--r-sm); background: var(--raised); font-size: var(--fs-label); color: var(--muted); }
.btotal b { text-align: right; color: var(--ink); font-weight: 600; }
.btotal .btotal__big { color: var(--ink); font-size: var(--fs-card-h); font-weight: 700; }
.btotal b.is-over { color: var(--warn); }
.chip--add { border-style: dashed; color: var(--ink-2); }
.chip--add:hover { border-style: solid; }
.bwait__go { margin-left: auto; flex: none; }
@media (max-width: 640px) {
  .bline { grid-template-columns: 32px minmax(0, 1fr) minmax(0, 1fr) auto; grid-template-areas: 'ic what what x' 'ic w p p'; gap: 8px; }
  .bline__ic { grid-area: ic; align-self: start; }
  .bline__what { grid-area: what; }
  .bline .blines__w { grid-area: w; }
  .bline .blines__p, .bline .bline__amt { grid-area: p; }
  .bline [data-unpart] { grid-area: x; align-self: start; }
}
@media (pointer: coarse) {
  .input, .select, .textarea, .btn, .btn--sm, .btn--icon, .page-btn,
  .chip, .pick, .radio, .linepick__opt, .field__toggle {
    min-height: 44px;
  }

  /* Up, down, Enter, Esc - none of which a thumb has. The strip is help for
     a keyboard and clutter for everybody else, and it is taking a row off a
     menu that already has half a window to live in. */
  .linepick__keys { display: none; }

  /* Show/hide on a password box. It sits inside the field, so it gets its
     width from the text rather than from a box - and at 25px high it was
     the smallest thing on the sign-in screen, which is the first screen
     anybody touches. Found by measuring the rendered page rather than by
     reading this file. */
  .field__toggle { display: inline-flex; align-items: center; padding: 3px 10px; }

  /* min-height cannot lift a fixed one, and these two set theirs outright:
     the search box inside a multi-select at 34px and a pagination arrow at
     32px. Raised rather than floored. */
  .ms__search .input { height: 44px; }
  .page-btn          { height: 44px; min-width: 44px; }
  .btn--icon         { min-width: 44px; }

  /* A textarea is typed into rather than aimed at, so it is given room
     instead of a floor - a 44px box for three lines of notes is a box
     somebody scrolls inside. */
  .textarea, textarea.input { min-height: 96px; }

  /* A handset row. The x drops a phone off a delivery, so it is not a 30px
     target on something held in one hand. */
  .phone .input { height: 44px; font-size: 14px; }
  .phone__x     { width: 44px; height: 44px; }
  .phones__add  { min-height: 44px; }

  /* The bench's check marks: a thumb on a phone held in the other hand. */
  .bcheck__seg button { width: 44px; height: 44px; }
  .blines__w, .blines__p { height: 44px; }

  /* Proximity is what groups a form, and controls that have all grown
     taller stop reading as groups and start reading as one stack of bars.
     The gap grows with them. */
  .form-grid { gap: 20px; }
}

/* ---- the document -----------------------------------------------------------

   A bill and a jobsheet on paper. Not a screen that prints: a sheet, laid out
   in the units a sheet is measured in.

   Nothing here uses the size tokens at the top of this file, deliberately.
   Those are a 4px rhythm for controls somebody clicks at 96dpi; this is
   210mm of paper going through a 600dpi laser, and a px has no length there.
   The same argument the label makes in millimetres, one size up.

   Everything inside is in `em`, and the root's font-size is the only number
   that changes between A4 and A5, normal and compact. One number moves the
   whole document, which is why a shop gets a size chooser and not a page of
   font boxes.

   The accent arrives as a custom property and is used in three places - the
   rule under the letterhead, the table head, and the total. Three, because
   the research is unanimous that one accent reads as designed and four reads
   as a template somebody filled in.

   And the total does NOT depend on its fill. Office printers and every
   thermal head drop background colour by default, so a total that is only
   legible because of the band behind it is a total that vanishes on half the
   printers in India. The rule above it and the type size do the work; the
   band is a bonus.
   --------------------------------------------------------------------------- */
.doc {
  --accent: #3f3fd1;
  --accent-ink: #fff;
  --doc-line: #d8d8e2;
  --doc-faint: #5a5a68;
  box-sizing: border-box;
  /* 18mm at the sides, not 14: text that close to the edge of the sheet
     read as running off it, on screen and on paper. */
  width: 210mm; min-height: 297mm; padding: 14mm 18mm 12mm;
  margin: 0 auto;
  background: #fff; color: #111;
  font-family: var(--font); font-size: 10pt; line-height: 1.45;
  display: flex; flex-direction: column;
}
.doc--a5 { width: 148mm; min-height: 210mm; padding: 10mm 13mm 9mm; }
/* The summary sheet, turned on its side. Seventeen columns of figures do not
   go down a portrait page at a size anybody reads, and a summary that drops
   columns to fit is a summary somebody has to go back to the file for. */
.doc--land { width: 297mm; min-height: 210mm; padding: 10mm 12mm 9mm; }
/* A sheet in a batch, on its own page. Before rather than after, so printing
   one bill does not feed a blank page out behind it. On screen the batch is a
   stack with a gap, which is what a stack of paper looks like. */
.doc--next { margin-top: 24px; }
@media print { .doc--next { break-before: page; page-break-before: always; margin-top: 0; } }
.doc * { box-sizing: border-box; }

/* The letterhead. The shop's name is the largest thing on the sheet after the
   total, because the first of the five questions a buyer's desk asks is who
   this is from. */
.doc__head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 1.5em; padding-bottom: 1.1em;
  border-bottom: 2.5pt solid var(--accent);
}
.doc__who { display: flex; align-items: flex-start; gap: 1em; }
.doc__logo { max-height: 5.5em; max-width: 16em; object-fit: contain; }
.doc__name { display: block; font-size: 1.55em; line-height: 1.2; font-weight: 700; }
.doc__who span { display: block; font-size: .85em; color: var(--doc-faint); }
.doc__title { text-align: right; flex: none; }
.doc__title h1 {
  margin: 0; font-size: 1.5em; line-height: 1.15; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--accent);
}
.doc__no { display: block; font-size: 1.05em; font-weight: 700; margin-top: .15em; }
/* Rule 48's own words. Small and boxed rather than shouted: it is for the
   file it ends up in, not for the person being handed it. */
.doc__copy {
  display: inline-block; margin-top: .45em; padding: .15em .5em;
  border: .5pt solid var(--doc-line); border-radius: 2pt;
  font-size: .72em; letter-spacing: .04em; text-transform: uppercase;
  color: var(--doc-faint);
}

/* Who it is for, and the facts about it. Two columns, because that is how
   every document of this kind has been read for a hundred years. */
.doc__parties {
  display: grid; grid-template-columns: 1.15fr 1fr; gap: 1.5em;
  padding: .9em 0 1.1em;
}
.doc__party b { display: block; font-size: 1.1em; }
.doc__party span { display: block; font-size: .88em; color: var(--doc-faint); }
.doc__lbl {
  display: block; font-size: .7em; letter-spacing: .09em; text-transform: uppercase;
  color: var(--doc-faint); margin-bottom: .3em;
}
.doc__facts { margin: 0; font-size: .9em; }
.doc__facts > div {
  display: flex; justify-content: space-between; gap: 1em;
  padding: .12em 0;
}
.doc__facts dt { color: var(--doc-faint); }
.doc__facts dd { margin: 0; text-align: right; font-weight: 600; }

/* The lines. A head band that survives losing its fill, and rules between
   rows rather than a box around every cell - a grid of boxes is a spreadsheet
   and this is a document. */
.doc__items { width: 100%; border-collapse: collapse; }
.doc__items thead th {
  background: var(--accent); color: var(--accent-ink);
  font-size: .75em; letter-spacing: .06em; text-transform: uppercase;
  font-weight: 700; text-align: left; padding: .7em .75em;
  border-top: .5pt solid var(--accent); border-bottom: .5pt solid var(--accent);
}
.doc__items tbody td {
  padding: .7em .75em; border-bottom: .5pt solid var(--doc-line);
  vertical-align: top; font-size: .95em; line-height: 1.5;
}
.doc__items .t-num, .doc__items th.t-num { text-align: right; }
.doc__n { width: 2.5em; color: var(--doc-faint); }
/* The IMEI under a line, and "Service" under a charge. Not shrunk as far as
   it could be: a fifteen digit number where one wrong digit files a phone
   under the wrong handset is read off paper, often in a shop's own light. */
.doc__sub { display: block; font-size: .85em; color: var(--doc-faint); margin-top: .1em; }
.doc__items tbody tr { break-inside: avoid; }

/* ---------------------------------------------------------------------------
   The summary sheet's table. Many narrow columns rather than a few wide ones,
   so the type comes down and the padding with it.

   `table-header-group` is the whole reason a long summary is usable: without
   it the second page is a block of figures under no headings, and the column
   somebody is reading down becomes unidentifiable exactly where it matters.
   --------------------------------------------------------------------------- */
.doc__items--tight { font-size: .68em; table-layout: fixed; }
.doc__items--tight thead { display: table-header-group; }
.doc__items--tight tfoot { display: table-footer-group; }
.doc__items--tight thead th { padding: .45em .35em; font-size: .82em; letter-spacing: .03em; }
.doc__items--tight tbody td { padding: .34em .35em; }
/* A GSTIN is fifteen unbroken characters and a branch name can be longer.
   Wrapping is what keeps them inside their own column instead of pushing the
   money off the edge of the paper. */
.doc__items--tight td, .doc__items--tight th { overflow-wrap: anywhere; }
.doc__items--tight .t-num { font-variant-numeric: tabular-nums; white-space: nowrap; }
.doc__items--tight tfoot th, .doc__items--tight tfoot td {
  border-top: 1.5pt solid var(--accent); border-bottom: 0;
  padding: .5em .35em; font-weight: 700; color: #111;
}
.doc__items--tight tfoot th { text-align: left; }
.doc__made { font-size: .78em; color: var(--doc-faint); }

/* The words and the money, side by side. The totals never split across a page
   break: half a total on one sheet and half on the next is the one thing on a
   bill nobody can work around. */
.doc__close {
  display: grid; grid-template-columns: 1fr minmax(0, 20em); gap: 1.5em;
  padding-top: .9em; break-inside: avoid;
}
.doc__words p { margin: .1em 0 0; font-size: .92em; font-style: italic; }
/* The invoice's close: the words level with the Total, not at the top. */
.doc__close--split { row-gap: 0; }
.doc__close--split .doc__words { align-self: start; padding-top: .5em; }
.doc__sums { width: 100%; border-collapse: collapse; font-size: .95em; }
/* Room above and below every figure. At .2em the SGST line sat on the rule
   under it and the total pressed against the rule above it, which reads as
   text being cut - on paper most of all. */
.doc__sums th { text-align: left; font-weight: 400; color: var(--doc-faint); padding: .32em 0; }
.doc__sums td { text-align: right; padding: .32em 0; font-variant-numeric: tabular-nums; }
.doc__sums tr.is-total th, .doc__sums tr.is-total td {
  border-top: 1.5pt solid var(--accent);
  padding-top: .6em; padding-bottom: .45em; font-size: 1.25em; font-weight: 700; color: #111;
}
.doc__sums tr.is-total + tr th, .doc__sums tr.is-total + tr td { padding-top: .45em; }

/* One inner edge for the whole sheet. The item rows sit 7pt inside their
   rules; everything else - the letterhead, who it is for, the totals, the
   foot - ran to the very end of its rule, so the total stood further right
   than the amount above it. Now every line of text starts and ends where the
   item rows do, and the rules still run the full width. */
.doc { --doc-inset: 12pt; }
.doc__head, .doc__parties, .doc__foot, .doc__terms { padding-left: var(--doc-inset); padding-right: var(--doc-inset); }
.doc__words { padding-left: var(--doc-inset); }
.doc__note {
  display: flex; align-items: baseline; gap: .8em;
  padding: .7em var(--doc-inset); border-bottom: .5pt solid var(--doc-line);
}
.doc__note .doc__lbl { margin: 0; flex: none; }
.doc__note p { margin: 0; font-size: .92em; }
.doc__sums th { padding-left: var(--doc-inset); }
.doc__sums td { padding-right: var(--doc-inset); }
/* The totals' rule starts where "Subtotal" does, and runs to the sheet's
   right edge like every other rule. */
.doc__sums { margin-left: var(--doc-inset); width: calc(100% - var(--doc-inset)); }
.doc__sums th { padding-left: 0; }
/* What is still owed is the figure somebody acts on, so it gets its own
   thin rule, like the line under the items. */
.doc__sums tr.is-due th, .doc__sums tr.is-due td { border-top: .5pt solid var(--doc-line); padding-top: .45em; }
.doc__terms { border-top: .5pt solid var(--doc-line); margin-top: 1em; }
/* The head band is painted per cell, and where a cell edge falls between two
   pixels - on screen, or in a PDF at an odd scale - a hairline of white shows
   between the cells. Each cell's colour runs 1px into the next to close it. */
.doc__items thead th { box-shadow: 1px 0 0 var(--accent); }
.doc--st-banded .doc__items thead th { box-shadow: 1px 0 0 color-mix(in srgb, var(--accent) 12%, #fff); }
.doc--st-ledger .doc__items thead th,
.doc--st-minimal .doc__items thead th, .doc--st-mono .doc__items thead th { box-shadow: none; }
.doc__items thead th:last-child { box-shadow: none; }
.doc__items th:first-child, .doc__items td:first-child { padding-left: var(--doc-inset); }
.doc__items th:last-child,  .doc__items td:last-child  { padding-right: var(--doc-inset); }
.doc__sums tr.is-due td, .doc__sums tr.is-due th { font-weight: 700; color: #111; }

/* How to pay, and who signed it. The signature block is always here, empty or
   not: rule 46(q) asks for the supplier's signature, and what an unsigned
   paper bill needs is not an image, it is a line with room above it for a
   pen. The same block on a jobsheet carries two, because the customer's
   signature is the authorisation to open their phone. */
.doc__foot {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 2em; margin-top: auto; padding-top: 1.2em; break-inside: avoid;
}
.doc__pay { display: flex; align-items: flex-end; gap: 1.2em; font-size: .85em; }
.doc__qr { text-align: center; }
.doc__qr img { width: 8em; height: 8em; object-fit: contain; display: block; }
.doc__qr--live svg { width: 8em; height: 8em; display: block; margin: 0 auto; }
.doc__qr small { font-size: .85em; color: var(--doc-faint); }
.doc__bank b { display: block; }
.doc__bank span { display: block; color: var(--doc-faint); }
.doc__signs { display: flex; gap: 2.5em; margin-left: auto; text-align: right; }
.doc__sign { min-width: 13em; }
.doc__sign > span { display: block; font-size: .85em; }
/* The stamp overlaps the signature the way it does on paper, which is also
   why the drawn one is stored without a white background. */
.doc__ink { position: relative; height: 4.5em; }
.doc__ink img { max-height: 4.5em; max-width: 14em; object-fit: contain; }
.doc__ink .doc__stamp { position: absolute; right: -.4em; bottom: -.2em; opacity: .85; }
.doc__sign small {
  display: block; border-top: .5pt solid #111; padding-top: .3em;
  font-size: .8em; color: var(--doc-faint);
}
.doc__sign--left { text-align: left; }
.doc__sign--left small { border-top-color: #111; }

.doc__terms { padding-top: 1em; font-size: .82em; break-inside: avoid; }
.doc__terms ol { margin: 0; padding-left: 1.3em; color: var(--doc-faint); }
.doc__terms li { padding: .05em 0; }
/* A long set on an A4, in two columns: at 7px a line the whole width of the
   sheet is too long for the eye to find the start of the next. */
.doc__terms--cols { line-height: 1.3; }
.doc__terms--cols ol { columns: 2; column-gap: 8mm; }
.doc__terms--cols li { break-inside: avoid; padding: 0; }

/* A jobsheet's middle. Boxes of facts rather than a table of money, because
   what it records is a device and a promise. */
.doc__panels { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .9em; }
.doc__panel {
  border: .5pt solid var(--doc-line); border-left: 2pt solid var(--accent);
  border-radius: 2pt; padding: .6em .8em; break-inside: avoid;
}
.doc__panel--wide { grid-column: 1 / -1; }
.doc__panel p { margin: .15em 0 0; font-size: .92em; white-space: pre-line; }
.doc__pairs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .1em .9em; font-size: .9em; }
.doc__pairs > div { display: flex; justify-content: space-between; gap: .6em; }
.doc__pairs dt { color: var(--doc-faint); }
.doc__pairs dd { margin: 0; font-weight: 600; text-align: right; }
/* A pair that takes the whole width keeps its value beside its label instead
   of against the far edge. Pushed right it lands under the column next to it
   and reads as that column's answer - an IMEI sitting under "Colour". */
.doc__pairs > div.is-wide { grid-column: 1 / -1; justify-content: flex-start; }
.doc__pairs > div.is-wide dd { text-align: left; }

/* A block a shop switched off. One rule per block, so turning one off is one
   class on the sheet and the preview can do it without asking the server. */
.doc--no-hsn      .doc__hsn,
.doc--no-mrp      .doc__mrp,
.doc--no-imei     .doc__imei,
.doc--no-taxsplit .doc__taxsplit,
.doc--no-words    .doc__words,
.doc--no-paid     .doc__paid,
.doc--no-qr       .doc__qr,
.doc--no-bank     .doc__bank,
.doc--no-copies   .doc__copy,
.doc--no-terms    .doc__terms,
.doc--no-device      .doc__device,
.doc--no-problem     .doc__problem,
.doc--no-accessories .doc__accessories,
.doc--no-condition   .doc__condition,
.doc--no-estimate    .doc__estimate,
.doc--no-promised    .doc__promised,
.doc--no-advance     .doc__advance,
.doc--no-gstcol      .doc__gstcol,
.doc--no-slabs       .doc__slabs,
.doc--no-payments    .doc__payments,
.doc--no-bank        .doc__dbank,
.doc--no-office      .doc__copy,
.doc--no-checks      .doc__checks { display: none; }

/* ---- the purchase bill -------------------------------------------------------
   The shop's own record of a dealer's bill. Migration 048.

   One GST row stands in for CGST and SGST when the breakdown is switched off.
   A purchase adds its tax on top of the taxable value, so taking the split
   away without a row in its place would leave a column that no longer adds
   up to the total under it. */
.doc__taxone { display: none; }
.doc--no-taxsplit .doc__taxone { display: table-row; }
.doc__dbank b { display: block; }
.doc__dbank span { display: block; color: var(--doc-faint); }
.doc__state dd { color: #b42318; }
.doc__slabs, .doc__payments { padding-top: 1em; break-inside: avoid; }
/* The small tables under the totals - tax by slab and what was paid out.
   Ruled rows and no fill, so they read as the working behind the figures and
   never compete with the table of goods above them. */
.doc__grid { width: 100%; border-collapse: collapse; font-size: .85em; }
.doc__grid th {
  text-align: left; font-weight: 600; font-size: .85em; letter-spacing: .06em;
  text-transform: uppercase; color: var(--doc-faint);
  padding: .3em .5em; border-bottom: .5pt solid #111;
}
.doc__grid td { padding: .3em .5em; border-bottom: .5pt solid var(--doc-line); }
.doc__grid .t-num { text-align: right; font-variant-numeric: tabular-nums; }
.doc__grid th:first-child, .doc__grid td:first-child { padding-left: 0; }
.doc__grid th:last-child,  .doc__grid td:last-child  { padding-right: 0; }
/* The slabs sit under the amount in words, in the column beside the totals
   that is otherwise white paper. Below the totals they pushed the notes onto a
   second A4 sheet on the plainer designs. */
.doc__close .doc__slabs { padding-top: .9em; }
/* Three people sign a goods received note, so the three lines share the width
   rather than crowding the right-hand corner the way one signatory does. */
.doc__checks { gap: 1.6em; }
.doc__checks .doc__sign { min-width: 10.5em; }

/* ---- five designs for the same sheet -------------------------------------
   Migration 044. Everything above is `classic`, which is what every Nexmorim
   bill has looked like so far and is kept as one of the five rather than
   replaced - a shop that liked it has not had its bill redesigned underneath
   it. What follows are the other four, as overrides.

   Nothing here adds or removes a particular. Rule 46's list is in the markup
   either way; a style decides how the sheet is SET - the letterhead's
   arrangement, whether the table is ruled or hairlined, whether the total sits
   under a rule or in a box, how the labels are cut.

   And every one of them obeys the same rule the base does: the total never
   depends on a fill behind it. Office printers and every thermal head drop
   background colour, so in all five the rule above it and the type size carry
   it and the fill is a bonus. `mono` is that argument taken to its end.
   --------------------------------------------------------------------------- */

/* Minimal - no fills anywhere, hairlines, the title in ink rather than accent.
   The cheapest of the five to print and the one that survives a bad printer
   best, because nothing on it is carried by a block of colour. */
.doc--st-minimal { --doc-line: #e3e3ea; }
.doc--st-minimal .doc__head {
  border-bottom: .5pt solid var(--doc-line); padding-bottom: 1.4em;
}
.doc--st-minimal .doc__title h1 {
  color: #111; font-weight: 600; font-size: 1.3em; letter-spacing: .14em;
}
.doc--st-minimal .doc__items thead th {
  background: none; color: var(--doc-faint);
  border-top: 0; border-bottom: .5pt solid #111;
  padding-left: 0; padding-right: 0;
}
.doc--st-minimal .doc__items thead th:last-child,
.doc--st-minimal .doc__items tbody td:last-child { padding-right: 0; }
.doc--st-minimal .doc__items thead th:first-child,
.doc--st-minimal .doc__items tbody td:first-child { padding-left: 0; }
.doc--st-minimal .doc__items tbody td { border-bottom: .5pt solid var(--doc-line); }
.doc--st-minimal .doc__sums tr.is-total th,
.doc--st-minimal .doc__sums tr.is-total td { border-top: .5pt solid #111; }
.doc--st-minimal .doc__parties { padding: 1.4em 0 1.6em; }

/* Banded - the shop's name reversed out of a full-width accent band, and the
   table head tinted rather than solid so the band above it stays the loudest
   thing on the sheet. The most branded of the five.
   The band is pulled out to the sheet's edge with a negative margin, which is
   the one place a document here bleeds. */
.doc--st-banded .doc__head {
  background: var(--accent); color: var(--accent-ink);
  margin: -14mm -18mm 0; padding: 12mm 18mm 1.2em;
  border-bottom: 0;
}
.doc--a5.doc--st-banded .doc__head { margin: -10mm -13mm 0; padding: 8mm 13mm 1em; }
.doc--st-banded .doc__who span,
.doc--st-banded .doc__title h1 { color: inherit; opacity: .92; }
.doc--st-banded .doc__title h1 { opacity: 1; }
.doc--st-banded .doc__copy { border-color: currentColor; color: inherit; opacity: .8; }
.doc--st-banded .doc__items thead th {
  background: color-mix(in srgb, var(--accent) 12%, #fff);
  color: #111;
  border-top: .5pt solid var(--doc-line);
  border-bottom: .5pt solid var(--accent);
}
.doc--st-banded .doc__parties { padding-top: 1.3em; }

/* Ledger - the formal Indian tax invoice. A ruled grid with vertical rules
   between the columns, the party blocks in boxes, the totals in a box of their
   own. This is what a chartered accountant expects to be handed and what a
   B2B buyer's desk files without looking twice. */
.doc--st-ledger .doc__head { border-bottom-width: 1pt; }
.doc--st-ledger .doc__title h1 { font-size: 1.35em; }
.doc--st-ledger .doc__parties {
  gap: 0; border: .5pt solid var(--doc-line); margin-top: .4em;
}
.doc--st-ledger .doc__party,
.doc--st-ledger .doc__facts { padding: .7em .8em; }
.doc--st-ledger .doc__party { border-right: .5pt solid var(--doc-line); }
.doc--st-ledger .doc__items { border: .5pt solid var(--doc-line); }
.doc--st-ledger .doc__items thead th,
.doc--st-ledger .doc__items tbody td { border-right: .5pt solid var(--doc-line); }
.doc--st-ledger .doc__items thead th:last-child,
.doc--st-ledger .doc__items tbody td:last-child { border-right: 0; }
.doc--st-ledger .doc__items thead th {
  background: color-mix(in srgb, var(--accent) 10%, #fff);
  color: #111; border-top-color: var(--doc-line);
  border-bottom-color: var(--doc-line);
}
.doc--st-ledger .doc__sums {
  border: .5pt solid var(--doc-line); border-collapse: collapse;
}
.doc--st-ledger .doc__sums th { padding: .25em .7em; }
.doc--st-ledger .doc__sums td { padding: .25em .7em; }
.doc--st-ledger .doc__sums tr.is-total th,
.doc--st-ledger .doc__sums tr.is-total td {
  border-top: .5pt solid var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, #fff);
}
.doc--st-ledger .doc__panel { border-color: var(--doc-line); }

/* Mono - no accent at all, and that is the design rather than a shortage of
   one. Most bills in most shops come out of a laser that prints black, and a
   sheet built from greys and hairlines is sharper there than one built from a
   colour the machine is going to approximate. Small caps, letterspaced, and a
   heavy rule where the other four use the accent. */
.doc--st-mono { --accent: #111111; --accent-ink: #fff; --doc-line: #c9c9d1; }
.doc--st-mono .doc__head { border-bottom: 1.5pt solid #111; }
.doc--st-mono .doc__title h1 { color: #111; letter-spacing: .12em; font-size: 1.3em; }
.doc--st-mono .doc__items thead th {
  background: none; color: #111;
  border-top: .5pt solid #111; border-bottom: .5pt solid #111;
  letter-spacing: .1em;
}
.doc--st-mono .doc__lbl { letter-spacing: .14em; }
.doc--st-mono .doc__sums tr.is-total th,
.doc--st-mono .doc__sums tr.is-total td { border-top: 1.5pt solid #111; }




/* ---- the purchase bill: six sheets, not six colourings ----------------------
   Migration 048. Each design is its own markup (views/document.php,
   purchase_<style>()), because a voucher, a goods received note and a bill
   book are different pieces of paper rather than one piece set six ways.

   Everything is in em off the sheet's font size, so A5 and Compact move the
   whole sheet the way they move an invoice. Nothing depends on a fill to be
   read - a meter and a band are bonuses, the figures beside them carry it.
   --------------------------------------------------------------------------- */
.doc--pur { gap: 0; }
.doc--pur .mono { font-family: var(--font-mono); font-size: .92em; }
.pd-lbl {
  display: block; font-size: .68em; letter-spacing: .1em; text-transform: uppercase;
  color: var(--accent); font-weight: 600; margin-bottom: .3em;
}
.pd-faint { color: var(--doc-faint); }
.pd-sub { display: block; font-size: .82em; color: var(--doc-faint); margin-top: .1em; }
.pd-block { margin-top: 1.2em; break-inside: avoid; }
.pd-state {
  margin: .8em 0 0; padding: .3em .6em; border: .75pt solid #b42318; color: #b42318;
  font-weight: 700; font-size: .85em; letter-spacing: .06em; text-transform: uppercase;
  display: inline-block; align-self: flex-start;
}
.pd-mark {
  display: inline-block; margin-top: .5em; padding: .1em .5em;
  border: .5pt solid var(--doc-line); border-radius: 2pt;
  font-size: .68em; letter-spacing: .06em; text-transform: uppercase; color: var(--doc-faint);
}
.pd-shop { display: flex; align-items: flex-start; gap: .8em; }
.pd-logo { max-height: 4em; max-width: 12em; object-fit: contain; }
.pd-shop b { display: block; font-size: 1.2em; line-height: 1.2; }
.pd-shop span { display: block; font-size: .8em; color: var(--doc-faint); }
.pd-dealer { display: block; font-size: 1.08em; }
.pd-dealer ~ span { display: block; font-size: .85em; color: var(--doc-faint); }
.pd-words { margin: .4em 0 0; font-style: italic; font-size: .9em; }

/* Tables. Ruled rows, the head a rule rather than a band. */
.pd-table { width: 100%; border-collapse: collapse; margin-top: 1.2em; }
.pd-table th {
  text-align: left; font-size: .7em; letter-spacing: .08em; text-transform: uppercase;
  font-weight: 700; padding: .5em .5em; border-bottom: 1pt solid #111; color: #111;
}
.pd-table td { padding: .45em .5em; border-bottom: .5pt solid var(--doc-line); vertical-align: top; font-size: .93em; }
.pd-table tbody tr { break-inside: avoid; }
.pd-table .t-num, .pd-grid .t-num, .pd-drcr .t-num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.pd-n { width: 2.4em; color: var(--doc-faint); }
.pd-grid { width: 100%; border-collapse: collapse; font-size: .85em; }
.pd-grid th {
  text-align: left; font-size: .8em; letter-spacing: .06em; text-transform: uppercase;
  color: var(--doc-faint); padding: .3em .4em; border-bottom: .5pt solid #111;
}
.pd-grid td { padding: .3em .4em; border-bottom: .5pt solid var(--doc-line); }

/* Signatures. A line with room above it for a pen, the label under it. */
.pd-foot { margin-top: auto; padding-top: 1.6em; break-inside: avoid; }
.pd-signs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.6em; }
.pd-signs--four { grid-template-columns: repeat(4, 1fr); gap: 1.2em; }
.pd-sign small {
  display: block; border-top: .5pt solid #111; padding-top: .3em;
  font-size: .78em; color: var(--doc-faint);
}
.pd-ink { position: relative; height: 3.6em; }
.pd-ink img { max-height: 3.6em; max-width: 12em; object-fit: contain; }
.pd-ink .pd-stamp { position: absolute; right: 0; bottom: -.2em; opacity: .85; }
.doc--pur .doc__terms { padding-top: 1em; }

/* Blocks a design reads that the base sheet does not have. */
.pd-slabone { display: none; }
.doc--no-slabs .pd-slabone { display: table-row; }

/* ---- 1. Voucher --------------------------------------------------------------
   Books of account. A boxed voucher head, the party and the reference side by
   side, then the goods, then the posting with Dr and Cr in their own columns
   and equal on the last line - which is what an accountant checks first. */
.pd-vhead { display: flex; justify-content: space-between; align-items: flex-start; gap: 1.5em; }
.pd-vbox { border: 1pt solid var(--accent); padding: .6em .9em; min-width: 15em; text-align: right; }
.pd-vtitle {
  display: block; color: var(--accent); font-size: .95em; letter-spacing: .16em;
  text-transform: uppercase; margin-bottom: .35em;
}
.pd-vbox dl { margin: 0; font-size: .9em; }
.pd-vbox dl div { display: flex; justify-content: space-between; gap: 1em; }
.pd-vbox dt { color: var(--doc-faint); }
.pd-vbox dd { margin: 0; font-weight: 700; }
.pd-vparty {
  display: grid; grid-template-columns: 1.3fr 1fr; gap: 1.5em; margin-top: 1.2em;
  padding: .8em 0; border-top: .5pt solid var(--doc-line); border-bottom: .5pt solid var(--doc-line);
}
.pd-vparty b { display: block; font-size: 1.05em; }
.pd-vparty span { display: block; font-size: .85em; color: var(--doc-faint); }
.pd-drcr { width: 100%; border-collapse: collapse; font-size: .92em; }
.pd-drcr th {
  text-align: left; font-size: .75em; letter-spacing: .08em; text-transform: uppercase;
  padding: .4em .5em; border-bottom: 1pt solid #111;
}
.pd-drcr th.t-num { text-align: right; }
.pd-drcr td { padding: .3em .5em; border-bottom: .5pt dotted var(--doc-line); }
.pd-drcr td:nth-child(2), .pd-drcr td:nth-child(3) { width: 9.5em; }
.pd-drcr td:first-child { padding-left: .5em; }
.pd-drcr .pd-cr td:first-child { padding-left: 2.5em; }
.pd-drcr tfoot th, .pd-drcr tfoot td {
  border-top: 1pt solid #111; border-bottom: 2.25pt double #111;
  font-weight: 700; padding: .4em .5em; font-size: 1.05em;
}
.pd-narr p { margin: 0; font-size: .95em; }
.pd-narr .pd-words { margin-top: .3em; }

/* ---- 2. Goods received -------------------------------------------------------
   Built for a pen. Four boxed facts, the count large; an empty box beside each
   line for what was actually counted; a tick square before every IMEI; ruled
   lines for a shortage. */
.pd-ghead { display: flex; justify-content: space-between; align-items: flex-start; gap: 1.5em;
  padding-bottom: .8em; border-bottom: 2.5pt solid var(--accent); }
.pd-gtitle { text-align: right; }
.pd-gtitle b, .pd-ptitle b, .pd-rtitle b, .pd-ttitle b {
  display: block; font-size: 1.3em; letter-spacing: .04em; color: var(--accent);
}
.pd-gtitle > span:not(.pd-mark), .pd-ptitle > span:not(.pd-mark),
.pd-rtitle > span:not(.pd-mark), .pd-ttitle > span:not(.pd-mark) {
  display: block; font-weight: 700; margin-top: .1em;
}
.pd-gfacts { display: grid; grid-template-columns: 1.4fr 1fr 1fr .7fr; margin-top: 1.2em; border: .75pt solid #111; }
.pd-gfacts > div { padding: .6em .8em; border-right: .5pt solid var(--doc-line); }
.pd-gfacts > div:last-child { border-right: 0; }
.pd-gfacts b { display: block; }
.pd-gfacts span:not(.pd-lbl) { display: block; font-size: .82em; color: var(--doc-faint); }
.pd-gcount { text-align: center; }
.pd-gcount b { font-size: 2em; line-height: 1.1; color: var(--accent); }
.pd-c { text-align: center; width: 5.5em; }
.pd-write { display: inline-block; width: 3.6em; height: 1.6em; border: .75pt solid #111; border-radius: 2pt; }
.pd-box { display: inline-block; width: 1.3em; height: 1.3em; border: .75pt solid #111; border-radius: 2pt; }
.pd-gtable tr.pd-gimei td { border-bottom: .5pt solid var(--doc-line); padding-top: 0; }
.pd-gtable tbody tr:has(+ .pd-gimei) td { border-bottom: 0; }
.pd-ticks { display: flex; flex-wrap: wrap; gap: .35em .9em; }
.pd-tick { display: inline-flex; align-items: center; gap: .4em; font-size: .85em; }
.pd-tick i { width: .95em; height: .95em; border: .75pt solid #111; border-radius: 1.5pt; flex: none; }
.pd-remarks .pd-rule { height: 1.8em; border-bottom: .5pt solid #111; }

/* ---- 3. Payable --------------------------------------------------------------
   The balance is the biggest thing on the sheet. The meter is a bonus - the
   figures under it say the same thing, so a printer dropping the fill drops
   nothing that matters. */
.pd-phead { display: flex; justify-content: space-between; align-items: flex-start; gap: 1.5em; }
.pd-ptitle { text-align: right; }
.pd-hero {
  display: flex; justify-content: space-between; align-items: flex-end; gap: 2em;
  margin-top: 1.4em; padding: 1.1em 1.2em; border: 1pt solid var(--accent); border-radius: 3pt;
}
.pd-hero__sum { text-align: right; }
.pd-hero__sum > span:not(.pd-lbl):not(.pd-paidstamp) { display: block; font-size: .85em; color: var(--doc-faint); }
.pd-big { display: block; font-size: 2.6em; line-height: 1.05; font-weight: 800; letter-spacing: -.01em; }
.pd-paidstamp {
  display: inline-block; padding: .2em .7em; border: 2pt solid var(--accent); color: var(--accent);
  font-size: 1.5em; font-weight: 800; letter-spacing: .12em; text-transform: uppercase;
  transform: rotate(-4deg); margin-bottom: .3em;
}
.pd-meter {
  margin-top: .8em; height: .7em; border: .75pt solid #111; border-radius: 2pt; overflow: hidden;
}
.pd-meter i {
  display: block; height: 100%; background: var(--accent);
  print-color-adjust: exact; -webkit-print-color-adjust: exact;
}
.pd-meter__k { display: flex; justify-content: space-between; font-size: .8em; color: var(--doc-faint); margin-top: .3em; }
.pd-pgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5em; margin-top: 1.3em; }
.pd-facts { margin: 0; font-size: .92em; }
.pd-facts div { display: flex; justify-content: space-between; gap: 1em; padding: .2em 0; border-bottom: .5pt dotted var(--doc-line); }
.pd-facts dt { color: var(--doc-faint); }
.pd-facts dd { margin: 0; font-weight: 600; }
.pd-payto { border: .75pt dashed var(--accent); border-radius: 3pt; padding: .7em .9em; }
.pd-payto b { display: block; }
.pd-payto span:not(.pd-lbl) { display: block; font-size: .9em; }
.pd-brief { list-style: none; margin: 0; padding: 0; font-size: .92em; }
.pd-brief li { display: flex; justify-content: space-between; gap: 1em; padding: .25em 0; border-bottom: .5pt dotted var(--doc-line); }
.pd-brief b { font-weight: 600; font-variant-numeric: tabular-nums; }
.pd-brief__sum { border-bottom: 0 !important; border-top: 1pt solid #111; font-weight: 700; }
.pd-brief__sum b { font-weight: 800; }

/* ---- 4. GST register ---------------------------------------------------------
   One continuous ruled frame. The row the bill adds to the register sits on
   top, boxed, so it reads straight across the way it is matched; the lines
   under it carry their own tax in columns, in the mono face, adding up in the
   foot. */
.pd-rhead { display: flex; justify-content: space-between; align-items: center; gap: 1.5em;
  border: .75pt solid #111; padding: .7em .9em; }
.pd-rtitle { text-align: right; }
.pd-rtitle b { font-size: 1em; letter-spacing: .14em; text-transform: uppercase; }
.pd-rrow { width: 100%; border-collapse: collapse; border: .75pt solid #111; border-top: 0; font-size: .85em; }
.pd-rrow th {
  text-align: left; font-size: .75em; letter-spacing: .06em; text-transform: uppercase;
  color: var(--accent); padding: .4em .5em; border-bottom: .5pt solid var(--doc-line);
  border-right: .5pt solid var(--doc-line);
}
.pd-rrow td { padding: .45em .5em; border-right: .5pt solid var(--doc-line); }
.pd-rrow th:last-child, .pd-rrow td:last-child { border-right: 0; }
.pd-rrow .t-num { text-align: right; font-family: var(--font-mono); white-space: nowrap; }
.pd-rtable { margin-top: 0; border: .75pt solid #111; border-top: 0; font-size: .92em; }
.pd-rtable th, .pd-rtable td { border-right: .5pt solid var(--doc-line); }
.pd-rtable th:last-child, .pd-rtable td:last-child { border-right: 0; }
.pd-rtable th { border-top: .75pt solid #111; }
.pd-rtable .t-num { font-family: var(--font-mono); font-size: .88em; }
.pd-rtable tfoot th, .pd-rtable tfoot td {
  border-top: .75pt solid #111; border-bottom: 2.25pt double #111; font-weight: 700;
  padding: .45em .5em;
}
.pd-rtable tfoot th { font-size: .75em; letter-spacing: .08em; text-transform: uppercase; }
.pd-rclose { display: flex; justify-content: space-between; gap: 1.5em; align-items: baseline;
  border: .75pt solid #111; border-top: 0; padding: .6em .9em; }
.pd-rclose p { margin: 0; }
.pd-rpaid { font-size: .9em; white-space: nowrap; }

/* ---- 5. Bill book ------------------------------------------------------------
   The bill book before software. A counterfoil down the left behind a dashed
   cut line with scissors on it, the shop's name centred over a band, "M/s" on
   a dotted rule, and ruled rows padded out to a page. */
.pd-book { display: grid; grid-template-columns: 13.5em minmax(0, 1fr); flex: 1; }
.pd-stub {
  position: relative; padding: 0 1.1em 0 0; margin-right: 1.3em;
  border-right: 1pt dashed #111; display: flex; flex-direction: column;
}
.pd-stub::after {
  content: "\2702"; position: absolute; right: -.62em; top: 45%;
  background: #fff; font-size: 1.1em; line-height: 1; transform: rotate(90deg);
}
.pd-stub__no { font-size: 1.1em; margin-bottom: .6em; }
.pd-stub dl { margin: 0; font-size: .85em; }
.pd-stub dl div { padding: .35em 0; border-bottom: .5pt dotted var(--doc-line); }
.pd-stub dt { font-size: .75em; letter-spacing: .06em; text-transform: uppercase; color: var(--doc-faint); }
.pd-stub dd { margin: 0; font-weight: 600; }
.pd-stub__sign { margin-top: auto; padding-top: 3em; }
.pd-stub__sign small { display: block; border-top: .5pt solid #111; font-size: .75em; color: var(--doc-faint); padding-top: .2em; }
.pd-leaf { display: flex; flex-direction: column; }
.pd-bhead { text-align: center; }
.pd-bname { display: block; font-size: 1.7em; letter-spacing: .03em; color: var(--accent); }
.pd-bhead > span { display: block; font-size: .82em; color: var(--doc-faint); }
.pd-band {
  display: flex; justify-content: center; align-items: center; gap: .8em;
  margin-top: .6em; padding: .25em 0; border-top: 1pt solid var(--accent); border-bottom: 1pt solid var(--accent);
}
.pd-band > span:first-child { font-weight: 700; letter-spacing: .2em; text-transform: uppercase; font-size: .9em; }
.pd-band .pd-mark { margin-top: 0; }
.pd-bmeta { display: flex; justify-content: space-between; margin-top: .9em; font-size: .92em; }
.pd-ms { margin: .55em 0 0; display: flex; align-items: baseline; gap: .5em; font-size: .95em; white-space: nowrap; }
.pd-dots { flex: 1; border-bottom: 1pt dotted #111; padding: 0 .3em .05em; font-weight: 600; white-space: normal; }
.pd-btable { border: .75pt solid #111; }
.pd-btable th, .pd-btable td { border-right: .5pt solid #111; }
.pd-btable th:last-child, .pd-btable td:last-child { border-right: 0; }
.pd-btable tbody td { border-bottom: .5pt solid var(--doc-line); }
.pd-btable .pd-pad td { height: 1.9em; }
.pd-btable tfoot th { text-align: right; font-size: .8em; letter-spacing: .06em; text-transform: uppercase; padding: .35em .5em; border-top: .5pt solid #111; }
.pd-btable tfoot td { padding: .35em .5em; border-top: .5pt solid #111; }
.pd-btable .pd-btotal th, .pd-btable .pd-btotal td { border-top: 1pt solid #111; font-size: 1.05em; font-weight: 800; }
.pd-bfoot { display: flex; justify-content: space-between; align-items: flex-end; margin-top: auto; padding-top: 1.4em; }
.pd-sign--for { min-width: 14em; text-align: right; }
.pd-sign--for > span { font-size: .85em; }

/* ---- 6. Timeline -------------------------------------------------------------
   Where the bill has got to. A step that has happened is a filled dot; one
   that has not is a ring. The dots are joined by a rule rather than a bar of
   colour, so the line survives a printer that drops fills. */
.pd-thead { display: flex; justify-content: space-between; align-items: flex-start; gap: 1.5em; }
.pd-ttitle { text-align: right; }
.pd-steps {
  list-style: none; margin: 1.6em 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(5, 1fr); position: relative;
}
.pd-steps::before {
  content: ""; position: absolute; left: 10%; right: 10%; top: .55em;
  border-top: 1.25pt solid var(--doc-line);
}
.pd-steps li { position: relative; text-align: center; padding: 0 .3em; }
.pd-steps i {
  display: block; width: 1.1em; height: 1.1em; margin: 0 auto .6em; border-radius: 50%;
  border: 1.5pt solid var(--accent); background: #fff; position: relative;
}
/* Done is solid with a tick knocked out of it, not yet is an empty ring - so
   the two differ by shape as well as by fill, and still differ on a printer
   that drops the fill. */
.pd-steps li.is-done i {
  background: var(--accent);
  print-color-adjust: exact; -webkit-print-color-adjust: exact;
}
.pd-steps li.is-done i::after {
  content: ""; position: absolute; left: .32em; top: .14em; width: .26em; height: .48em;
  border: solid #fff; border-width: 0 1.5pt 1.5pt 0; transform: rotate(45deg);
}
.pd-steps li:not(.is-done) i { border-style: dashed; }
.pd-steps b { display: block; font-size: .95em; font-variant-numeric: tabular-nums; }
.pd-steps small { display: block; font-size: .78em; color: var(--doc-faint); }
.pd-steps .pd-lbl { color: var(--doc-faint); }
.pd-steps li.is-done .pd-lbl { color: var(--accent); }
.pd-tgrid {
  display: flex; justify-content: space-between; align-items: flex-start; gap: 1.5em;
  margin-top: 1.6em; padding-top: 1em; border-top: .5pt solid var(--doc-line);
}
.pd-twarranty { text-align: right; border-left: 2pt solid var(--accent); padding-left: .8em; }
.pd-twarranty b { font-size: 1.15em; }
.pd-ttable th { border-bottom-color: var(--accent); }
.pd-tclose { display: grid; grid-template-columns: 1fr minmax(0, 18em); gap: 1.5em; align-items: start; margin-top: .4em; break-inside: avoid; }
.pd-tclose .pd-tpays { margin-top: .8em; }
.pd-sums { width: 100%; border-collapse: collapse; font-size: .93em; margin-top: .6em; grid-column: 2; }
.pd-sums th { text-align: left; font-weight: 400; color: var(--doc-faint); padding: .2em 0; }
.pd-sums td { text-align: right; padding: .2em 0; font-variant-numeric: tabular-nums; }
.pd-sums .is-total th, .pd-sums .is-total td {
  border-top: 1.5pt solid var(--accent); padding-top: .4em; font-size: 1.2em; font-weight: 700; color: #111;
}
.pd-sums .is-due th, .pd-sums .is-due td { font-weight: 700; color: #111; }

/* ---- the counter receipt ----------------------------------------------------

   A bill on a till roll, which is not a small invoice. One narrow column, no
   letterhead, no signature, read once by somebody already walking away.

   The width is set inline from the PRINTER's printing width, not the paper
   width: an 80mm roll is 72mm of head, and a column laid out at 80 comes out
   with its right edge shaved off. Everything else here is in points, because
   what matters is how many characters fit a line - at 8.5pt a 72mm column
   takes about the 48 that every 3 inch printer quotes for its own Font A.
   --------------------------------------------------------------------------- */
.rcpt {
  box-sizing: border-box;
  margin: 0 auto; background: #fff; color: #000;
  font-family: var(--font); font-size: 8.5pt; line-height: 1.32;
}
.rcpt * { box-sizing: border-box; }
.rcpt__head { text-align: center; padding-bottom: 1.5mm; }
.rcpt__logo { max-height: 12mm; max-width: 40mm; object-fit: contain; margin-bottom: 1mm; }
.rcpt__head b { display: block; font-size: 1.35em; line-height: 1.2; }
.rcpt__head span { display: block; font-size: .88em; }
/* What the paper is. Ruled above and below rather than set big: on a receipt
   the largest thing has to be the total, and there is only one of those. */
.rcpt__what {
  margin-top: 1.5mm; padding: .8mm 0;
  border-top: .3mm solid #000; border-bottom: .3mm solid #000;
  font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
}
.rcpt__facts { margin: 1.5mm 0 0; font-size: .95em; }
.rcpt__facts > div { display: flex; justify-content: space-between; gap: 2mm; }
.rcpt__facts dt { font-weight: 400; }
.rcpt__facts dd { margin: 0; text-align: right; font-weight: 600; }

.rcpt__items { width: 100%; border-collapse: collapse; margin-top: 2mm; }
.rcpt__items thead th {
  font-size: .85em; font-weight: 700; text-align: left; padding: .8mm 0;
  border-top: .3mm dashed #000; border-bottom: .3mm dashed #000;
}
.rcpt__items .t-num, .rcpt__items th.t-num { text-align: right; }
/* The name gets the whole width and the figures go under it. Four columns
   across 72mm leaves a name eight characters wide, which is a receipt nobody
   can read back. */
.rcpt__name { padding: .8mm 0 0; font-weight: 600; }
.rcpt__imei { display: block; font-family: var(--font-mono); font-size: .85em; font-weight: 400; }
.rcpt__mrp  { display: block; font-size: .85em; font-weight: 400; }
.rcpt__figs td { padding: 0 0 .8mm; border-bottom: .2mm dotted #999; }

.rcpt__sums { width: 100%; border-collapse: collapse; margin-top: 1.5mm; font-size: .95em; }
.rcpt__sums th { text-align: left; font-weight: 400; padding: .3mm 0; }
.rcpt__sums td { text-align: right; padding: .3mm 0; font-variant-numeric: tabular-nums; }
.rcpt__sums tr.is-total th, .rcpt__sums tr.is-total td {
  border-top: .3mm solid #000; padding-top: 1mm;
  font-size: 1.3em; font-weight: 700;
}
.rcpt__sums tr.is-due th, .rcpt__sums tr.is-due td { font-weight: 700; }
.rcpt__words { margin: 1.5mm 0 0; text-align: center; font-size: .9em; font-style: italic; }

/* The customer comes back with the paper. A bill number typed by hand off a
   thermal receipt that has been in a pocket for a fortnight is the slowest
   thing that happens at a counter. */
.rcpt__bars { margin-top: 2.5mm; text-align: center; line-height: 0; }
.rcpt__bars span {
  display: block; margin-top: .8mm; line-height: 1;
  font-family: var(--font-mono); font-size: .9em; letter-spacing: .04em;
}
/* No bars, because at this width they would not have read. The number is
   what somebody was going to use either way, so it is set large enough to
   read off a thermal receipt that has been in a pocket. */
.rcpt__bars.is-text span { margin-top: 0; font-size: 1.15em; font-weight: 700; }

/* ---- what the shop has turned off ----------------------------------------
   Every block is drawn and hidden by a class, the same way .doc--no-* works
   on a sheet - so a block ticked back on during design appears, which it
   could not do if it had never been rendered. The printed slip is this same
   markup, so there is one receipt and not a preview and a print that drift.
   See app/DocTemplate.php BLOCKS['receipt'] and migration 043. */
.rcpt--no-logo     .rcpt__logo,
.rcpt--no-gstin    .rcpt__gstin,
.rcpt--no-customer .rcpt__who,
.rcpt--no-imei     .rcpt__imei,
.rcpt--no-mrp      .rcpt__mrp,
.rcpt--no-taxsplit .rcpt__tax,
.rcpt--no-words    .rcpt__words,
.rcpt--no-qr       .rcpt__qr,
.rcpt--no-terms    .rcpt__terms { display: none; }

/* The BARS go; the number never does. Turning the barcode off means the
   counter reads the bill number back instead of scanning it, not that the
   paper comes out with no way to find the bill again - which is the same
   thing that happens when the roll is too narrow for the symbol to read, and
   it should look the same on the paper. */
/* The character row on Printer config is one line, always. Wrapping it would
   hide the very fault it is there to show - a printing width set wider than
   the head, which comes out as a shaved right-hand column on every receipt. */
.cal__cols { white-space: nowrap; overflow: hidden; }

.rcpt--no-bars .rcpt__bars svg { display: none; }
.rcpt--no-bars .rcpt__bars span {
  margin-top: 0; font-size: 1.15em; font-weight: 700;
}
.rcpt__qr { margin-top: 2.5mm; text-align: center; }
.rcpt__qr img { width: 22mm; height: 22mm; object-fit: contain; }
.doc-bar__acts { display: inline-flex; gap: 8px; }
.rcpt__qr--live svg { width: 26mm; height: 26mm; display: block; margin: 0 auto; }
.rcpt__qr span { display: block; font-size: .85em; }
.rcpt__terms {
  margin: 2.5mm 0 0; padding: 1.5mm 0 0 4mm;
  border-top: .3mm dashed #000; font-size: .82em;
}
.rcpt__terms li { padding: .2mm 0; }
/* Run-on before the tear bar, which sits above the head. */
.rcpt__feed { width: 100%; }

/* The paper, with the strip of it the head can actually reach marked inside.
   Drawn rather than described: the gap between the two is where every shaved
   right-hand column comes from, and a shop that can see it is 4mm each side
   understands the setting in one look. */
.paper {
  position: relative; margin: 0 auto; padding: 4mm 0;
  background: #fff; border: 1px solid var(--line);
  border-radius: 2px; box-shadow: var(--shadow); overflow: hidden;
}
.paper__band {
  position: absolute; top: 0; bottom: 0; left: 50%; transform: translateX(-50%);
  background: var(--brand-bg); border-left: 1px dashed var(--brand-line);
  border-right: 1px dashed var(--brand-line);
}
.paper__in { position: relative; margin: 0 auto; }
.paper__label {
  display: flex; align-items: center; justify-content: center;
  border: 1px dashed var(--line-strong); border-radius: 2px;
  font-size: 11px; color: var(--muted);
}

/* What a test print puts on paper: a ruler, a line of characters and a
   barcode, all at the size the settings claim. */
.cal {
  box-sizing: border-box; margin: 0 auto; background: #fff; color: #000;
  font-family: var(--font); font-size: 8.5pt; line-height: 1.35; text-align: center;
}
.cal__what { display: block; font-size: 1.3em; }
.cal__set  { display: block; font-size: .88em; }
/* A verdict, on paper, where there is no colour to spend. Boxed instead. */
.cal__set.is-bad {
  display: inline-block; margin-top: .5mm; padding: .3mm 1.5mm;
  border: .3mm solid #000; font-weight: 700;
}
.cal__rule {
  position: relative; height: 7mm; margin: 2.5mm 0 1mm;
  border-top: .3mm solid #000;
}
.cal__tick { position: absolute; top: 0; width: 0; }
.cal__tick::before {
  content: ''; position: absolute; top: 0; left: 0;
  width: .3mm; height: 2mm; background: #000;
}
.cal__tick.is-big::before { height: 3.5mm; }
.cal__tick i {
  position: absolute; top: 3.8mm; left: 0; transform: translateX(-50%);
  font-style: normal; font-size: 6pt;
}
.cal__cols {
  margin-top: 2mm; font-family: var(--font-mono); font-size: 8.5pt;
  letter-spacing: 0; white-space: nowrap; overflow: visible;
}
.cal__bars { margin-top: 3mm; line-height: 0; }
.cal__bars span {
  display: block; margin-top: 1mm; line-height: 1;
  font-family: var(--font-mono); font-size: .9em;
}

/* Setting a document up: the choices on the left, the sheet on the right and
   bigger. The other way round from the label screen on purpose - a 50 x 25mm
   sticker is smaller than its own settings and an A4 bill is very much not,
   and a preview you cannot read the total on is a picture of a document. */
.docedit { display: grid; gap: 18px; align-items: start; }
/* A grid item will not shrink below its own content unless it is told it may,
   and the content here is a full A4 sheet - 210mm, which is 794px. Below the
   breakpoint, where the column is sized by what is in it, that made the whole
   card 794px wide on a 800px tablet: the controls ran off the right-hand edge
   and the page scrolled sideways. The preview scales itself to whatever width
   it is given, so the only thing it needed was permission to be given less. */
.docedit > * { min-width: 0; }
@media (min-width: 980px) {
  .docedit { grid-template-columns: 300px minmax(0, 1fr); }
}
/* A colour, picked as a colour. Sized like every other control so a row of
   them does not step, and square because that is what a swatch is. */
.swatch {
  width: var(--h-control); height: var(--h-control); flex: none;
  padding: 2px; border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  background: var(--card); cursor: pointer;
}
.swatch::-webkit-color-swatch-wrapper { padding: 0; }
.swatch::-webkit-color-swatch { border: none; border-radius: 5px; }
.swatch::-moz-color-swatch { border: none; border-radius: 5px; }

/* The page a document is printed from: a bar that does not print, and the
   sheet. A window narrower than the sheet zooms it down to fit
   (views/printpage.php) - scrolled sideways, its edge read as text cut off. */
.doc-page { background: var(--bg); margin: 0; }
.doc-bar {
  position: sticky; top: 0; z-index: 5;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 16px; background: var(--card); border-bottom: 1px solid var(--line);
}
.doc-page .doc-wrap { padding: 24px 16px 40px; overflow: auto; }

/* On screen the sheet sits on the page background with a shadow, so it reads
   as paper. On paper it is the paper. */
.doc-wrap { background: var(--bg); padding: 0; overflow: hidden; }
.doc-wrap .doc { box-shadow: var(--shadow); }
/* Scaled to whatever width it has been given. The transform does not change
   the box, so the wrapper is told the resulting height by the script. */
.doc-fit { transform-origin: top left; }

/* Two blocks that exist only on paper: the letterhead at the top of a printed
   jobsheet and the shop's own signature at the foot of it. On screen the
   sidebar already says whose shop this is, so both would be chrome; on paper
   they are the difference between a receipt and a slip of paper. */
.print-only { display: none; }

/* ---------------------------------------------------------------------------
   Integrations — a directory, scanned rather than read.

   A grid and not a column. The column version put every field on show, which
   reads fine at three accounts and becomes a page of boxes at eight. What a
   shop needs at a glance is the mark, the name, one line about what it does
   here and what state it is in; the credentials live behind Connect.

   auto-fit with a 300px floor, so it is three across on a desktop, two on a
   tablet and one on a phone without a breakpoint deciding any of it.
   --------------------------------------------------------------------------- */
.intg {
  display: grid; gap: var(--gap-card);
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  align-items: stretch;
}
.intg__card {
  background: var(--card); border: 1px solid var(--line);
  border-radius: 12px; padding: var(--gap-card);
  display: flex; flex-direction: column; gap: 10px;
  min-width: 0;
}
/* A connected account is marked down its edge rather than tinted: a whole
   card in a colour reads as a warning, and this is the ordinary state. */
.intg__card.is-on { border-left: 3px solid var(--violet); }

.intg__head { display: flex; align-items: center; gap: 10px; min-width: 0; }
/* A plain tile behind the mark, so a four-colour logo and a one-colour one
   sit at the same weight instead of one shouting. */
.intg__mark {
  display: grid; place-items: center; flex: none;
  width: 40px; height: 40px; border-radius: 10px;
  background: var(--raised); color: var(--muted);
}
.intg__name { font-size: 16px; font-weight: 600; min-width: 0; }

/* One line, and it says what connecting changes in THIS app - not what the
   company does. flex: 1 so the actions sit on the same line across a row of
   cards whose sentences are different lengths. */
.intg__blurb {
  margin: 0; flex: 1;
  font-size: 13px; line-height: 1.5; color: var(--muted);
}

/* Which account, not just that there is one. Two shops in a group and one
   wrong key id is a day of payments into the wrong account. */
.intg__ref {
  margin: 0; font-size: 12px; color: var(--faint);
  font-family: var(--font-mono);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.intg__state {
  margin: 0; display: flex; align-items: center; gap: 7px;
  font-size: 13px; font-weight: 600;
}
.intg__dot { width: 8px; height: 8px; border-radius: 50%; background: currentColor; flex: none; }
.intg__state .faint { font-weight: 400; font-size: 12px; }
.intg__state--good { color: var(--ok); }
.intg__state--bad  { color: var(--danger); }
.intg__state--warn { color: var(--warn); }
.intg__state--off  { color: var(--muted); }

.intg__acts {
  display: flex; flex-wrap: wrap; gap: 8px;
  padding-top: 4px; border-top: 1px solid var(--line); margin-top: 2px;
}
.intg__acts .btn { padding-top: 10px; }

/* ---- the dialog the credentials live in ---------------------------------- */
.intgform { display: grid; gap: var(--gap-field); }
.intgform__fields {
  display: grid; gap: var(--gap-field);
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
/* `.field + .field` adds a top margin so a COLUMN of fields is spaced. Inside
   a grid the gap already does that, and the rule instead pushes every field
   after the first 16px down its own row - so the first box in a row sat
   higher than the two beside it. */
.intgform__fields .field + .field { margin-top: 0; }
.intgform__fields .check { grid-column: 1 / -1; }

.intgform__acts {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding-top: 12px; border-top: 1px solid var(--line);
}
/* Pushed to the far end: it leaves the app, and it is the one thing here that
   is a pointer rather than an action. */
.intg__where { margin-left: auto; font-size: 13px; color: var(--muted); }
.intg__where:hover { color: var(--violet); }
.intg__note { margin: 0; font-size: 13px; color: var(--muted); }
.intg__note .faint { margin-left: 6px; }

@media (max-width: 480px) {
  .intg__where { margin-left: 0; }
}

/* ---------------------------------------------------------------------------
   A table where one thing owns the rows under it: a product and its units.

   The product is named once, on its own row, and its units follow. One flat
   row per unit reads as a list of numbers - forty lines of fifteen digits
   with the same handset repeated down the left - and somebody at the shelf is
   working the other way round: they have the product in their hand and want
   its units.
   --------------------------------------------------------------------------- */
.table--group tbody .grouprow > th {
  text-align: left; font-weight: 600; font-size: 14px;
  padding-top: 14px;
  /* A `th` inside the body, so it inherits the head's letter-spaced capitals
     and turned every product name into NOKIA G21. This is a product, not a
     column heading. */
  text-transform: none; letter-spacing: 0; color: var(--ink);
}
.table--group tbody .grouprow > th .faint { font-weight: 400; margin-left: 8px; font-size: 13px; }
/* A rule above the product, not under it, so the heading reads as belonging to
   what comes after rather than to the rows it just left. */
.table--group tbody .grouprow > * { border-top: 1px solid var(--line-strong); }
.table--group tbody tr:first-child > * { border-top: 0; }
/* The unit rows are hairlines between themselves and nothing at the product
   boundary, which the rule above already marks. */
.table--group tbody td { border-top: 1px solid var(--line); }
.table--group tbody .grouprow + tr > * { border-top: 0; }

/* ---------------------------------------------------------------------------
   The send list. One ticked bill per row, one tap each, because WhatsApp from
   a browser opens a single chat and ten ticked bills are ten customers.
   --------------------------------------------------------------------------- */
.walist { display: grid; gap: 2px; }
.walist__row {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 0; border-bottom: 1px solid var(--line);
}
.walist__row:last-child { border-bottom: 0; }
/* min-width: 0, or a long customer name pushes Send off the right edge of a
   phone instead of being cut where it runs out. */
.walist__who { min-width: 0; flex: 1; }
.walist__who b    { display: block; font-size: 14px; }
.walist__who span { display: block; font-size: 12px; color: var(--muted); }
.walist__no { font-size: 13px; color: var(--muted); text-align: right; }
/* Tapped, not sent - WhatsApp still needs its own send pressed. It marks the
   place in a list of twenty, which is the only thing this page can honestly
   claim to know. */
.walist__row.is-done .walist__who b { color: var(--muted); }
.walist__row.is-done .btn { opacity: .55; }
/* A row that did not go says so and keeps saying it. A failure that fades is
   a customer who never got their bill and nobody knows. */
.walist__row.is-bad .walist__who b { color: var(--danger); }
.walist__why { display: block; font-size: 12px; color: var(--danger); margin-top: 2px; }
/* Its own row above the list, full width, so it is not mistaken for the Send
   belonging to the first customer. */
.walist__all { width: 100%; margin-bottom: 8px; }

@media print {
  .sidebar, .topbar, .page-head__actions, .scrim,
  .no-print, .chips, .toolbar, .modal, .actions-row { display: none !important; }

  .print-only { display: block; }

  .letterhead {
    display: flex; align-items: center; gap: 14px;
    padding-bottom: 10px; margin-bottom: 14px;
    border-bottom: 1px solid #333;
  }
  .letterhead__logo { max-height: 56px; max-width: 180px; object-fit: contain; }
  .letterhead__who b    { display: block; font-size: 16px; }
  .letterhead__who span { display: block; font-size: 11px; color: #333; }

  .slipfoot {
    display: flex; align-items: flex-end; justify-content: space-between;
    gap: 24px; margin-top: 22px; padding-top: 10px; border-top: 1px solid #333;
    break-inside: avoid;
  }
  .slipfoot__pay { text-align: center; }
  .slipfoot__pay img   { width: 90px; height: 90px; object-fit: contain; }
  .slipfoot__pay small { display: block; font-size: 10px; }
  .slipfoot__bank b    { display: block; font-size: 11px; }
  .slipfoot__bank span { display: block; font-size: 10.5px; color: #333; }
  .slipfoot__sign { text-align: right; margin-left: auto; }
  /* The stamp overlaps the signature the way it does on paper, which is also
     why the drawn one is saved without a white background. */
  .slipfoot__ink { position: relative; height: 60px; }
  .slipfoot__ink img { max-height: 60px; max-width: 200px; object-fit: contain; }
  .slipfoot__stamp {
    position: absolute; right: -10px; bottom: -6px;
    max-height: 62px !important; opacity: .85;
  }
  .slipfoot__sign small { display: block; font-size: 11px; border-top: 1px solid #333; padding-top: 4px; }
  /* A document printed on its own. On screen the sheet sits on the page with
     a shadow and is scaled to fit a card; on paper the sheet IS the page, so
     all three go. 296 rather than 297: at exactly the page height a rounding
     of a fraction of a millimetre spills into a second, blank sheet, and a
     blank second sheet on every bill is a roll of paper a month. */
  .doc-bar { display: none !important; }
  .doc-page .doc-wrap,
  .doc-wrap { background: none; padding: 0; overflow: visible; }
  .doc-fit  { transform: none !important; width: auto !important; height: auto !important; }
  .doc-wrap .doc { zoom: 1 !important; }
  .doc { box-shadow: none; margin: 0; min-height: 296mm; }
  .doc--a5 { min-height: 209mm; }
  /* Ask for the accent to survive. Printers that refuse still get a document
     that reads, which is why nothing on it depends on a fill. */
  .doc { -webkit-print-color-adjust: exact; print-color-adjust: exact; }

  .app, .content { background: #fff; padding: 0; }
  .card { border: none; box-shadow: none; }
  /* The jobsheet slip is what actually gets printed, so it has to survive
     losing every background colour a printer drops. */
  .jstat { border: 1px solid #333 !important; background: none !important; color: #000 !important; }
  .grid--2 { grid-template-columns: minmax(0, 1fr) !important; }
}


/* ---- support -----------------------------------------------------------------
   The help screens: what a shop raises, and the conversation after it. The
   thread reads like a chat because that is what it is - their words on the
   left, ours on the right in the brand colour - and the category picker is
   five tiles rather than a dropdown, because "what is this about" is the one
   question a stuck person answers without reading. */
.sup-kinds { display: grid; gap: 8px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 900px) { .sup-kinds { grid-template-columns: repeat(5, minmax(0, 1fr)); } }
.sup-kind { position: relative; display: block; cursor: pointer; }
.sup-kind input { position: absolute; opacity: 0; pointer-events: none; }
.sup-kind__box {
  display: flex; flex-direction: column; gap: 8px; height: 100%;
  padding: 12px; border: 1px solid var(--line-strong); border-radius: var(--r);
  background: var(--card); transition: border-color .14s, background-color .14s, box-shadow .14s;
}
.sup-kind__box svg { color: var(--muted); }
.sup-kind__box b { font-size: var(--fs-label); color: var(--ink); line-height: 1.35; }
.sup-kind:hover .sup-kind__box { border-color: var(--violet-line); }
.sup-kind input:checked + .sup-kind__box {
  border-color: var(--violet); background: var(--violet-soft); box-shadow: 0 0 0 3px var(--violet-glow);
}
.sup-kind input:checked + .sup-kind__box svg { color: var(--violet); }
.sup-kind input:focus-visible + .sup-kind__box { box-shadow: 0 0 0 3px var(--violet-glow); }

.sup-urgent {
  display: flex; align-items: center; gap: 12px; padding: 12px;
  border: 1px solid var(--line-strong); border-radius: var(--r); cursor: pointer;
}
.sup-urgent:has(input:checked) { border-color: var(--danger-line); background: var(--danger-bg); }
.sup-urgent b { display: block; font-size: var(--fs-label); color: var(--ink); }
.sup-urgent span { font-size: var(--fs-small); color: var(--muted); }

.sup-shot { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.sup-shot img { width: 64px; height: 48px; object-fit: cover; border-radius: var(--r-sm); border: 1px solid var(--line); }

.sup-thread { display: grid; gap: 16px; padding: var(--pad-card); }
.sup-msg { display: flex; gap: 12px; max-width: 88%; }
.sup-msg--us { margin-left: auto; flex-direction: row-reverse; }
.sup-msg__who {
  flex: 0 0 32px; width: 32px; height: 32px; border-radius: 50%;
  display: grid; place-items: center; font-size: 12px; font-weight: 700;
  background: var(--raised); color: var(--ink-2);
}
.sup-msg--us .sup-msg__who { background: linear-gradient(135deg, #4f46e5, #0ea5e9); color: #fff; }
.sup-msg__bubble {
  padding: 12px 16px; border-radius: var(--r-lg); border-top-left-radius: 4px;
  background: var(--raised); border: 1px solid var(--line);
}
.sup-msg--us .sup-msg__bubble {
  border-top-left-radius: var(--r-lg); border-top-right-radius: 4px;
  background: var(--violet-soft); border-color: var(--violet-line);
}
.sup-msg__meta { font-size: var(--fs-small); color: var(--muted); margin-bottom: 4px; }
.sup-msg__meta b { color: var(--ink); font-weight: 600; }
.sup-msg__body { font-size: var(--fs-body); line-height: 1.6; color: var(--ink); white-space: pre-wrap; word-break: break-word; }
.sup-msg__shot { display: inline-block; margin-top: 8px; }
.sup-msg__shot img { display: block; max-width: 240px; max-height: 180px; border-radius: var(--r-sm); border: 1px solid var(--line); }

.sup-reply { border-top: 1px solid var(--line); padding: var(--pad-card); display: grid; gap: 12px; }
.sup-reply__bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sup-reply__send { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }

.sup-side { display: grid; gap: 12px; align-content: start; }
.sup-layout { display: grid; gap: var(--gap-card); grid-template-columns: minmax(0, 1fr); }
@media (min-width: 1000px) { .sup-layout { grid-template-columns: minmax(0, 1fr) 300px; } }

.sup-hero {
  display: flex; align-items: center; gap: 16px; padding: 20px;
  border-radius: var(--r-lg); color: #fff;
  background: linear-gradient(135deg, #4f46e5 0%, #0ea5e9 100%);
}
.sup-hero h2 { margin: 0; font-size: 18px; color: #fff; }
.sup-hero p { margin: 4px 0 0; color: #e0e7ff; font-size: var(--fs-body); }
.sup-hero .btn { margin-left: auto; background: #fff; color: #3730a3; border-color: #fff; }
.sup-hero__icon {
  flex: 0 0 44px; width: 44px; height: 44px; border-radius: var(--r);
  display: grid; place-items: center; background: rgba(255, 255, 255, .16);
}
@media (max-width: 640px) {
  .sup-hero { flex-wrap: wrap; }
  .sup-hero .btn { margin-left: 0; width: 100%; justify-content: center; }
  .sup-msg { max-width: 100%; }
}
.table tr.is-link { cursor: pointer; }
.table tr.is-link:hover td { background: var(--raised); }
/* The address at the foot of the help screens: a quiet line, the address
   the one thing in colour, so it reads as a way out rather than an advert. */
.sup-contact {
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 8px 12px;
  margin-top: 24px; padding: 16px; text-align: center;
  font-size: var(--fs-body); color: var(--muted);
}
.sup-contact__icon {
  display: grid; place-items: center; width: 32px; height: 32px; border-radius: 50%;
  background: var(--violet-soft); color: var(--violet);
}
.sup-contact a { font-weight: 700; color: var(--violet); }/* ---- the business at the top of the name menu ------------------------------
   An identity card rather than two lines of text: the shop's initials on a
   brand-gradient tile, the name with Nexmorim's own seal, and the address
   they sign in with. The seal carries a soft brand glow, and a band of
   light sweeps across it when the card is hovered - once, not on a loop,
   and not at all for anybody who asked for reduced motion. */
.dropdown__head.idcard {
  display: grid; grid-template-columns: 40px minmax(0, 1fr); gap: 12px; align-items: center;
  padding: 12px; margin: 4px 4px 8px; border: 1px solid var(--violet-line); border-radius: var(--r);
  background:
    radial-gradient(120% 140% at 100% 0%, rgba(14, 165, 233, .10), transparent 55%),
    radial-gradient(120% 140% at 0% 100%, rgba(79, 70, 229, .10), transparent 55%),
    var(--card);
}
.dropdown__head.idcard .idcard__tile {
  width: 40px; height: 40px; border-radius: var(--r); display: grid; place-items: center;
  font-size: 14px; font-weight: 800; letter-spacing: .5px; color: #fff;
  background: linear-gradient(135deg, #4f46e5 0%, #0ea5e9 100%);
  box-shadow: 0 4px 12px rgba(79, 70, 229, .28), inset 0 1px 0 rgba(255, 255, 255, .25);
}
.idcard__main { min-width: 0; }
.dropdown__head.idcard strong.dropdown__biz {
  display: flex; align-items: center; gap: 6px; font-size: 14px; line-height: 1.25;
}
.dropdown__head.idcard .dropdown__biz__name { font-size: 14px; color: var(--ink); overflow-wrap: anywhere; }
#userMenu:has(.idcard) { min-width: 296px; }
.dropdown__head.idcard .idcard__mail {
  display: block; margin-top: 2px; font-size: var(--fs-small); color: var(--muted); overflow-wrap: anywhere;
}

.verified {
  flex: 0 0 auto; position: relative; overflow: visible;
  filter: drop-shadow(0 0 3px rgba(79, 70, 229, .45)) drop-shadow(0 1px 1px rgba(14, 165, 233, .35));
}
.verified-wrap { position: relative; display: inline-flex; flex: 0 0 auto; border-radius: 50%; overflow: hidden; }
.verified-wrap::after {
  content: ""; position: absolute; inset: -2px; pointer-events: none;
  background: linear-gradient(115deg, transparent 35%, rgba(255, 255, 255, .85) 50%, transparent 65%);
  transform: translateX(-120%);
}
.idcard:hover .verified-wrap::after { animation: nx-seal-sweep .9s ease-out 1; }
@keyframes nx-seal-sweep { to { transform: translateX(120%); } }
@media (prefers-reduced-motion: reduce) {
  .idcard:hover .verified-wrap::after { animation: none; }
}
html[data-theme="dark"] .verified {
  filter: drop-shadow(0 0 4px rgba(129, 140, 248, .6)) drop-shadow(0 1px 1px rgba(56, 189, 248, .45));
}
/* ---- admin: one shop ---------------------------------------------------------
   The plan as four cards, because the limits are what the choice is about;
   and the two actions that stop a shop set apart in a red zone at the foot,
   each behind a code from the admin's mailbox (app/ShopAdmin.php). */
.plan-pick { display: grid; gap: 8px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 760px) { .plan-pick { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.plan-pick__opt { position: relative; cursor: pointer; }
.plan-pick__opt input { position: absolute; opacity: 0; pointer-events: none; }
.plan-pick__box {
  display: flex; flex-direction: column; gap: 4px; height: 100%; padding: 12px;
  border: 1px solid var(--line-strong); border-radius: var(--r); background: var(--card);
  transition: border-color .14s, background-color .14s, box-shadow .14s;
}
.plan-pick__box b { font-size: var(--fs-body); color: var(--ink); }
.plan-pick__box span { font-size: var(--fs-small); color: var(--muted); }
.plan-pick__opt:hover .plan-pick__box { border-color: var(--violet-line); }
.plan-pick__opt input:checked + .plan-pick__box {
  border-color: var(--violet); background: var(--violet-soft); box-shadow: 0 0 0 3px var(--violet-glow);
}
.plan-pick__opt input:focus-visible + .plan-pick__box { box-shadow: 0 0 0 3px var(--violet-glow); }

.danger-zone { border-color: var(--danger-line); }
.danger-zone .card__head h2 { color: var(--danger); }
.danger-zone__row {
  display: flex; align-items: center; gap: 16px; padding: 12px var(--pad-card);
  border-top: 1px solid var(--line);
}
.danger-zone__row > div { flex: 1; min-width: 0; }
.danger-zone__row b { display: block; font-size: var(--fs-body); color: var(--ink); }
/* The line under the title only - a pill beside it keeps its own colour. */
.danger-zone__row > div > span { font-size: var(--fs-small); color: var(--muted); }

.otp-panel {
  margin: 0 var(--pad-card) var(--pad-card); padding: 16px; display: grid; gap: 12px;
  border: 1px solid var(--danger-line); border-radius: var(--r); background: var(--danger-bg);
}
.otp-panel__head { display: flex; align-items: center; gap: 12px; }
.otp-panel__head > div > b { display: block; font-size: var(--fs-body); color: var(--ink); }
.otp-panel__head span b { color: var(--ink); font-weight: 600; }
.otp-panel__head span { font-size: var(--fs-small); color: var(--muted); }
.otp-panel__icon {
  flex: 0 0 36px; width: 36px; height: 36px; border-radius: 50%; display: grid; place-items: center;
  background: var(--card); color: var(--danger); border: 1px solid var(--danger-line);
}
.otp-panel__row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.otp-input {
  width: 180px; font-family: var(--font-mono); font-size: var(--fs-till); font-weight: 700;
  letter-spacing: 8px; text-align: center; height: var(--h-till);
}
/* What a reset takes, in this business's own numbers. A readout rather than a
   sentence: "1,284 jobsheets" is read, "all your data" is skimmed. */
.reset-counts { display: grid; grid-template-columns: repeat(auto-fit, minmax(112px, 1fr)); gap: 8px; }
.reset-counts > div {
  padding: 10px 12px; border-radius: var(--r); background: var(--card);
  border: 1px solid var(--danger-line);
}
.reset-counts b {
  display: block; font-family: var(--font-mono); font-size: var(--fs-body); font-weight: 700;
  color: var(--danger); font-variant-numeric: tabular-nums;
}
.reset-counts span {
  font-size: var(--fs-small); color: var(--muted); text-transform: uppercase; letter-spacing: .6px;
}
@media (max-width: 640px) {
  .danger-zone__row { flex-direction: column; align-items: stretch; }
  .otp-input { width: 100%; }
}
/* The chosen plan has to read as chosen at a glance: a filled check in the
   corner and a heavier edge, not only a tint. The plan the shop is on now
   carries its own small tag, so a change not yet saved is visibly a change. */
.plan-pick__box { position: relative; padding-right: 36px; }
.plan-pick__box::after {
  content: ""; position: absolute; top: 10px; right: 10px; width: 18px; height: 18px;
  border-radius: 50%; border: 1.5px solid var(--line-strong); background: var(--card);
}
.plan-pick__opt input:checked + .plan-pick__box { border-width: 2px; padding: 11px 35px 11px 11px; }
.plan-pick__opt input:checked + .plan-pick__box::after {
  border-color: transparent;
  background: linear-gradient(135deg, #4f46e5 0%, #0ea5e9 100%) center / cover no-repeat;
  box-shadow: 0 2px 6px rgba(79, 70, 229, .35);
}
.plan-pick__opt input:checked + .plan-pick__box::before {
  content: ""; position: absolute; top: 15px; right: 16px; width: 6px; height: 9px; z-index: 1;
  border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(45deg);
}
.plan-pick__now {
  margin-left: 6px; padding: 2px 8px; border-radius: 999px; vertical-align: 1px;
  font-style: normal; font-size: 11px; font-weight: 700; letter-spacing: .3px;
  background: var(--ok-bg); color: var(--ok);
}

/* ---- the owner's welcome ------------------------------------------------------
   Shown once after sign-in (app/Welcome.php). A brand-gradient panel with two
   soft lights drifting behind it, the day's greeting large, three glass tiles
   for the numbers that matter this morning, one white button to the thing
   that most needs doing, and the last sign-in underneath - amber when it came
   from somewhere else. It rises in on arrival and the tiles follow one by
   one; with reduced motion it simply appears. */
.welcome {
  position: relative; overflow: hidden; isolation: isolate;
  margin-bottom: var(--gap-card); padding: 24px; border-radius: var(--r-xl); color: #fff;
  background:
    radial-gradient(60% 90% at 100% 0%, rgba(14, 165, 233, .55), transparent 60%),
    radial-gradient(70% 100% at 0% 100%, rgba(99, 102, 241, .55), transparent 60%),
    linear-gradient(135deg, #3730a3 0%, #4f46e5 45%, #0284c7 100%);
  box-shadow: 0 18px 40px -18px rgba(55, 48, 163, .55), 0 2px 6px rgba(20, 20, 45, .08);
  animation: nx-welcome-in .5s cubic-bezier(.2, .8, .2, 1) both;
}
/* A faint grid, masked to fade out, so the panel has texture up close. */
.welcome::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background-image:
    linear-gradient(rgba(255, 255, 255, .07) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, .07) 1px, transparent 1px);
  background-size: 28px 28px;
  -webkit-mask-image: radial-gradient(80% 80% at 70% 20%, #000 20%, transparent 75%);
          mask-image: radial-gradient(80% 80% at 70% 20%, #000 20%, transparent 75%);
}
.welcome__glow {
  position: absolute; z-index: -1; width: 280px; height: 280px; right: -60px; top: -120px;
  border-radius: 50%; filter: blur(40px); pointer-events: none;
  background: radial-gradient(circle, rgba(125, 211, 252, .55), transparent 70%);
  animation: nx-welcome-drift 9s ease-in-out infinite alternate;
}
.welcome__glyph {
  position: absolute; z-index: -1; right: 24px; bottom: -12px; font-size: 120px; line-height: 1;
  opacity: .16; transform: rotate(-8deg); pointer-events: none; user-select: none;
  /* A white watermark, not a coloured sticker: the emoji's own colours at low
     opacity went muddy on the gradient. */
  filter: brightness(0) invert(1);
}
.welcome__close {
  position: absolute; top: 12px; right: 12px; width: 32px; height: 32px; border-radius: 50%;
  display: grid; place-items: center; border: 0; cursor: pointer;
  background: rgba(255, 255, 255, .14); color: #fff; transition: background-color .14s;
}
.welcome__close:hover { background: rgba(255, 255, 255, .26); }

.welcome__chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 4px 12px; border-radius: 999px;
  font-size: var(--fs-small); font-weight: 600; letter-spacing: .2px;
  background: rgba(255, 255, 255, .16); border: 1px solid rgba(255, 255, 255, .22);
}
.welcome__title { margin: 12px 40px 0 0; font-size: 28px; line-height: 1.2; font-weight: 800; color: #fff; }
.welcome__sub { margin: 8px 0 0; font-size: var(--fs-body); color: #e0e7ff; }
.welcome__wave { display: inline-block; transform-origin: 70% 70%; animation: nx-wave 1.6s ease-in-out .5s 1; }

.welcome__tiles { display: grid; gap: 12px; margin-top: 20px; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
.welcome__tile {
  display: grid; grid-template-columns: auto 1fr; gap: 4px 8px; align-items: center;
  padding: 12px 16px; border-radius: var(--r-lg);
  background: rgba(255, 255, 255, .12); border: 1px solid rgba(255, 255, 255, .20);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  animation: nx-welcome-in .5s cubic-bezier(.2, .8, .2, 1) both;
  animation-delay: calc(.12s + var(--i, 0) * .08s);
}
.welcome__tile-icon {
  grid-row: span 2; width: 32px; height: 32px; border-radius: var(--r-sm);
  display: grid; place-items: center; background: rgba(255, 255, 255, .18);
}
.welcome__tile-label { font-size: var(--fs-small); color: #c7d2fe; }
.welcome__tile-value { font-size: 20px; font-weight: 800; color: #fff; font-variant-numeric: tabular-nums; }

.welcome__foot { display: flex; align-items: center; gap: 12px 20px; flex-wrap: wrap; margin-top: 20px; }
.welcome__cta {
  display: inline-flex; align-items: center; gap: 8px; height: var(--h-till); padding: 0 20px;
  border-radius: var(--r); background: #fff; color: #3730a3; font-weight: 700; font-size: var(--fs-body);
  text-decoration: none; box-shadow: 0 6px 16px -6px rgba(15, 23, 42, .45);
  transition: transform .14s, box-shadow .14s;
}
.welcome__cta:hover { transform: translateY(-1px); box-shadow: 0 10px 22px -8px rgba(15, 23, 42, .5); text-decoration: none; }
.welcome__last { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: var(--fs-small); color: #c7d2fe; }
.welcome__last.is-new {
  padding: 6px 12px; border-radius: var(--r); color: #fff;
  background: rgba(251, 191, 36, .22); border: 1px solid rgba(251, 191, 36, .5);
}
.welcome__last a { color: #fff; font-weight: 700; text-decoration: underline; text-underline-offset: 2px; }

.welcome.is-leaving { animation: nx-welcome-out .26s ease-in both; }

@keyframes nx-welcome-in  { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@keyframes nx-welcome-out { to { opacity: 0; transform: translateY(-8px) scale(.98); } }
@keyframes nx-welcome-drift { to { transform: translate(-60px, 40px) scale(1.15); } }
@keyframes nx-wave { 0%, 100% { transform: rotate(0); } 15%, 45% { transform: rotate(16deg); } 30%, 60% { transform: rotate(-8deg); } }
@media (prefers-reduced-motion: reduce) {
  .welcome, .welcome__tile, .welcome__glow, .welcome__wave, .welcome.is-leaving { animation: none; }
}
@media (max-width: 640px) {
  .welcome { padding: 20px 16px; }
  .welcome__title { font-size: 22px; }
  .welcome__glyph { font-size: 88px; }
  .welcome__cta { width: 100%; justify-content: center; }
}
.welcome__keep { white-space: nowrap; }
/* Admin occasions: a festival whose two sources disagree reads as a question. */
.occ-row--check td { background: var(--warn-bg); }
.occ-glyph { display: inline-block; width: 20px; text-align: center; }
/* The welcome's own clock: a thin line along the foot that runs out over
   AUTO_CLOSE_MS, and stands still while the card is held. */
.welcome__timer {
  position: absolute; left: 0; bottom: 0; height: 3px; width: 100%; z-index: 1;
  transform-origin: left center; pointer-events: none;
  background: linear-gradient(90deg, rgba(255, 255, 255, .95), rgba(125, 211, 252, .9));
  animation: nx-welcome-timer var(--welcome-ms, 12000ms) linear forwards;
}
.welcome.is-held .welcome__timer { animation-play-state: paused; }
@keyframes nx-welcome-timer { from { transform: scaleX(1); } to { transform: scaleX(0); } }
@media (prefers-reduced-motion: reduce) {
  /* The card still closes on time; only the moving line goes. */
  .welcome__timer { animation: none; opacity: .5; }
}

/* ---- the team's welcome ------------------------------------------------------
   The same card, warmer: indigo running into a sunrise orange, for the
   people who do the work. A shabash sits in it as a quote with who said it;
   a birthday, years with the shop or a shabash bring a short fall of
   confetti, once. The lead's card lists the team's days as chips that open
   the shabash box for that person. */
.welcome--team {
  background:
    radial-gradient(60% 90% at 100% 0%, rgba(251, 146, 60, .55), transparent 60%),
    radial-gradient(70% 100% at 0% 100%, rgba(99, 102, 241, .55), transparent 60%),
    linear-gradient(135deg, #4338ca 0%, #7c3aed 45%, #ea580c 100%);
  box-shadow: 0 18px 40px -18px rgba(124, 58, 237, .5), 0 2px 6px rgba(20, 20, 45, .08);
}
.welcome--team .welcome__glow { background: radial-gradient(circle, rgba(253, 186, 116, .6), transparent 70%); }
.welcome--team .welcome__sub  { color: #fde7d3; font-weight: 600; }
.welcome--team .welcome__cta  { color: #6d28d9; }

.welcome__kudos {
  display: flex; gap: 12px; align-items: flex-start; margin: 16px 0 0; padding: 12px 16px;
  border-radius: var(--r-lg); background: rgba(255, 255, 255, .95); color: var(--ink);
  box-shadow: 0 10px 24px -12px rgba(15, 23, 42, .45);
  animation: nx-welcome-in .5s cubic-bezier(.2, .8, .2, 1) .15s both;
}
.welcome__kudos-badge {
  flex: 0 0 40px; width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center;
  font-size: 20px; background: linear-gradient(135deg, #fde68a, #fb923c);
}
.welcome__kudos blockquote { margin: 0; font-size: 16px; font-weight: 700; line-height: 1.4; color: var(--ink); }
.welcome__kudos figcaption { margin-top: 4px; font-size: var(--fs-small); color: var(--muted); }
html[data-theme="dark"] .welcome__kudos { background: rgba(22, 22, 31, .92); }

.welcome__days { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
.welcome__day {
  display: inline-flex; align-items: center; gap: 8px; padding: 6px 8px 6px 12px; border-radius: 999px;
  background: rgba(255, 255, 255, .16); border: 1px solid rgba(255, 255, 255, .28);
  color: #fff; font-size: var(--fs-small); text-decoration: none;
}
.welcome__day b { padding: 3px 10px; border-radius: 999px; background: #fff; color: #4338ca; font-size: 11px; }
.welcome__day:hover { background: rgba(255, 255, 255, .24); text-decoration: none; }

.welcome__confetti { position: absolute; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; }
.welcome__confetti i {
  position: absolute; top: -12px; left: calc(4% + var(--c) * 6.8%);
  width: 8px; height: 12px; border-radius: 2px; opacity: 0;
  background: hsl(calc(var(--c) * 47deg) 90% 65%);
  animation: nx-confetti 2.4s cubic-bezier(.3, .6, .4, 1) calc(var(--c) * .07s) 1 forwards;
}
.welcome__confetti i:nth-child(3n) { width: 6px; height: 6px; border-radius: 50%; }
@keyframes nx-confetti {
  0%   { opacity: 1; transform: translateY(0) rotate(0); }
  100% { opacity: 0; transform: translateY(260px) rotate(540deg); }
}
.welcome__top, .welcome__tiles, .welcome__foot, .welcome__kudos, .welcome__days { position: relative; z-index: 1; }
@media (prefers-reduced-motion: reduce) {
  .welcome__confetti { display: none; }
  .welcome__kudos { animation: none; }
}

/* The shabash box on the Team list. */
.kudos-btn { margin-right: 4px; }
.kudos-box {
  width: min(480px, calc(100vw - 32px)); padding: 0; border: 0; border-radius: var(--r-xl);
  background: var(--card); color: var(--ink); box-shadow: 0 24px 60px -20px rgba(20, 20, 45, .45);
}
.kudos-box::backdrop { background: rgba(20, 20, 45, .45); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); }
.kudos-box[open] { animation: nx-welcome-in .28s cubic-bezier(.2, .8, .2, 1) both; }
.kudos-box__inner { display: grid; gap: 12px; padding: 20px; }
.kudos-box__head { display: flex; align-items: center; gap: 12px; }
.kudos-box__head h2 { margin: 0; font-size: 17px; }
.kudos-box__badge {
  flex: 0 0 44px; width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center;
  font-size: 22px; background: linear-gradient(135deg, #fde68a, #fb923c);
}
.kudos-box__picks { display: flex; flex-wrap: wrap; gap: 8px; }
.kudos-box__picks .chip { cursor: pointer; }
.kudos-box__foot { display: flex; justify-content: flex-end; gap: 8px; }
@media (prefers-reduced-motion: reduce) { .kudos-box[open] { animation: none; } }

/* ---- the customer's rating page (rate.php) -------------------------------------
   Public and phone-first. A card on a faint grid, the shop's name and job
   number in mono at the top like a readout, five large stars that light in
   the brand gradient as they are chosen, and the comment box only after a
   star - one question first, the rest if they want. */
.rate-page {
  min-height: 100vh; margin: 0; background: var(--surface);
  background-image:
    radial-gradient(60% 50% at 50% 0%, rgba(79, 70, 229, .12), transparent 70%),
    linear-gradient(rgba(79, 70, 229, .05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(79, 70, 229, .05) 1px, transparent 1px);
  background-size: auto, 28px 28px, 28px 28px;
}
.rate { max-width: 440px; margin: 0 auto; padding: 32px 16px; }
.rate__card {
  display: grid; gap: 12px; padding: 24px 20px; border-radius: var(--r-xl); background: var(--card);
  border: 1px solid var(--line); box-shadow: 0 24px 60px -30px rgba(55, 48, 163, .45);
  animation: nx-welcome-in .45s cubic-bezier(.2, .8, .2, 1) both;
}
.rate__head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.rate__shop { font-weight: 800; font-size: 15px; color: var(--ink); }
.rate__job { font-size: var(--fs-small); color: var(--muted); padding: 2px 8px; border: 1px solid var(--line); border-radius: 999px; }
.rate__mark {
  width: 48px; height: 48px; border-radius: 50%; display: grid; place-items: center;
  background: var(--danger-bg); color: var(--danger);
}
.rate__mark--ok { color: #fff; background: linear-gradient(135deg, #4f46e5, #0ea5e9); box-shadow: 0 0 0 6px rgba(14, 165, 233, .15); }
.rate__title { margin: 4px 0 0; font-size: 22px; line-height: 1.25; color: var(--ink); }
.rate__sub { margin: 0; color: var(--muted); font-size: var(--fs-body); }
.rate__stars { display: flex; justify-content: space-between; gap: 4px; margin: 8px 0 0; }
.rate__stars.is-static { justify-content: flex-start; gap: 8px; }
.rate__star {
  width: 56px; height: 56px; border: 0; padding: 8px; border-radius: var(--r); cursor: pointer;
  background: var(--raised); color: var(--line-strong); display: grid; place-items: center;
  transition: transform .12s, background-color .12s, color .12s;
}
.rate__stars.is-static .rate__star { width: 40px; height: 40px; padding: 4px; cursor: default; }
.rate__star svg { width: 100%; height: 100%; }
.rate__star.is-on { color: #4f46e5; background: var(--violet-soft); }
.rate__star.is-on svg { fill: currentColor; filter: drop-shadow(0 0 6px rgba(14, 165, 233, .45)); }
.rate__star:hover { transform: translateY(-2px); }
.rate__star:focus-visible { outline: 2px solid var(--violet); outline-offset: 2px; }
.rate__word { margin: 0; min-height: 20px; text-align: center; font-size: var(--fs-small); color: var(--violet); }
.rate__more { display: grid; gap: 12px; animation: nx-welcome-in .3s ease-out both; }
.rate__send { height: var(--h-till); font-size: var(--fs-body); }
.rate__said { margin: 0; font-style: italic; color: var(--ink-2); }
.rate__by { text-align: center; margin-top: 16px; font-size: var(--fs-small); color: var(--faint); }
@media (prefers-reduced-motion: reduce) {
  .rate__card, .rate__more { animation: none; }
  .rate__star { transition: none; }
}
/* The rating on a jobsheet: stars lit in the brand colour, the score in mono. */
.rating-card__stars { display: flex; align-items: center; gap: 4px; color: var(--line-strong); }
.rating-card__stars .is-on { color: #4f46e5; }
.rating-card__stars .is-on svg { fill: currentColor; }
.rating-card__stars b { margin-left: 8px; color: var(--ink); }
.rating-card__said { margin: 8px 0 4px; font-style: italic; color: var(--ink-2); }

/* ---- the welcome, engineered (26 Sep 2026: "techy, not generic emoji") --------
   The occasion mark is our own line icon, drawn as a thin white watermark;
   the date chip is a mono readout with a live status dot; the name ends in a
   terminal cursor; a celebration is a burst of particles in the brand's
   indigo, sky and white, and the shabash badge carries a lit ring. */
.welcome__glyph { filter: none; opacity: .13; color: #fff; right: 16px; bottom: -24px; font-size: 0; }
.welcome__glyph svg { stroke-width: 1; width: 150px; height: 150px; }
.welcome__chip { font-family: var(--font-mono); letter-spacing: .6px; font-size: 11px; }
.welcome__chip svg { flex: 0 0 auto; }
.welcome__dot {
  width: 7px; height: 7px; border-radius: 50%; background: #34d399;
  box-shadow: 0 0 0 0 rgba(52, 211, 153, .6); animation: nx-dot 2s ease-out infinite;
}
@keyframes nx-dot { 0% { box-shadow: 0 0 0 0 rgba(52, 211, 153, .6); } 100% { box-shadow: 0 0 0 8px rgba(52, 211, 153, 0); } }
.welcome__cursor {
  display: inline-block; width: .5em; height: .95em; margin-left: .18em; vertical-align: -.08em;
  border-radius: 2px; background: linear-gradient(180deg, #a5f3fc, #7dd3fc);
  box-shadow: 0 0 10px rgba(125, 211, 252, .8); animation: nx-cursor 1.1s steps(1) infinite;
}
@keyframes nx-cursor { 50% { opacity: 0; } }

.welcome__confetti i { background: #7dd3fc; width: 3px; height: 14px; border-radius: 2px; box-shadow: 0 0 6px currentColor; }
.welcome__confetti i:nth-child(3n)   { background: #fff; width: 4px; height: 4px; border-radius: 1px; }
.welcome__confetti i:nth-child(3n+1) { background: #a5b4fc; }
.welcome--team .welcome__confetti i:nth-child(4n) { background: #fdba74; }

.welcome__kudos-badge {
  position: relative; color: #fff;
  background: linear-gradient(135deg, #4f46e5 0%, #0ea5e9 100%);
  box-shadow: 0 0 0 4px rgba(14, 165, 233, .18), 0 0 18px rgba(79, 70, 229, .45);
}
.welcome--team .welcome__kudos-badge { background: linear-gradient(135deg, #7c3aed 0%, #f97316 100%); box-shadow: 0 0 0 4px rgba(249, 115, 22, .18), 0 0 18px rgba(124, 58, 237, .45); }
.welcome__kudos-badge::after {
  content: ""; position: absolute; inset: -4px; border-radius: 50%;
  border: 1px solid rgba(125, 211, 252, .9); animation: nx-ring 1.8s ease-out 2;
}
@keyframes nx-ring { from { transform: scale(.9); opacity: 1; } to { transform: scale(1.5); opacity: 0; } }
.welcome__kudos--customer .welcome__kudos-badge svg { fill: currentColor; }
.welcome__day svg { flex: 0 0 auto; }

@media (prefers-reduced-motion: reduce) {
  .welcome__dot, .welcome__cursor, .welcome__kudos-badge::after { animation: none; }
}
.kudos-box__badge { color: #fff; background: linear-gradient(135deg, #4f46e5 0%, #0ea5e9 100%); box-shadow: 0 0 0 4px rgba(14, 165, 233, .15), 0 0 16px rgba(79, 70, 229, .4); }
.kudos-btn svg, .pill svg { vertical-align: -2px; }
.occ-glyph { color: var(--violet); vertical-align: -3px; }

/* ---- Dues (dues.php) ------------------------------------------------------------
   One row per customer. The amount reads first, in the head face; the age is a
   readout that changes colour at 30 and 60 days - the bands every collections
   desk uses - and the credit limit is a thin gauge under the amount that
   turns red past it. Dialogs share the shabash box's shape. */
.dues-amt { font-family: var(--font-head); font-size: 16px; font-weight: 700; font-variant-numeric: tabular-nums; }
.dues-limit { margin: 8px 0 0 auto; width: 88px; height: 4px; border-radius: 4px; background: var(--line); overflow: hidden; }
.dues-limit i { display: block; height: 100%; border-radius: 4px; background: var(--violet); }
.dues-limit.is-over i { background: var(--danger); }
.dues-age {
  display: inline-flex; align-items: center; gap: 8px; padding: 4px 12px 4px 8px; border-radius: 999px;
  font-family: var(--font-mono); font-size: var(--fs-small); border: 1px solid var(--line);
}
.dues-age::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: currentColor; box-shadow: 0 0 0 3px color-mix(in srgb, currentColor 18%, transparent); }
.dues-age b { font-weight: 700; }
.dues-age--fresh { color: var(--ok);     background: var(--ok-bg);     border-color: var(--ok-line); }
.dues-age--late  { color: var(--warn);   background: var(--warn-bg);   border-color: var(--warn-line); }
.dues-age--old   { color: var(--danger); background: var(--danger-bg); border-color: var(--danger-line); }
.credit-warn { margin: 12px 0 0; }
/* Six tiles on one row on a laptop: today's two sit beside the totals. */
/* Two rows of three: the money on top, who to chase underneath. */
.dues-kpis { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 640px) { .dues-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.dues-name { color: inherit; }
.dues-name:hover { color: var(--violet); }

/* ---- a customer's khata (dues-customer.php) ------------------------------------
   What they took reads as a list under its bill number; a payment is a green
   figure in its own column. The balance is the last column, as on paper. */
.khata td { vertical-align: top; }
.khata__head { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.khata__tag {
  display: inline-block; padding: 2px 8px; border-radius: var(--r-sm); font-size: 11px; font-weight: 700;
  letter-spacing: .6px; text-transform: uppercase; border: 1px solid var(--line); color: var(--muted);
}
.khata__tag--bill    { color: var(--violet); background: var(--violet-soft); border-color: var(--violet-line); }
.khata__tag--job     { color: var(--info);   background: var(--info-bg);     border-color: var(--info-line); }
.khata__tag--payment { color: var(--ok);     background: var(--ok-bg);       border-color: var(--ok-line); }
.khata__items { list-style: none; margin: 4px 0 0; padding: 0; display: grid; gap: 2px; font-size: var(--fs-label); color: var(--ink); }
.khata__items li::before { content: "›"; color: var(--faint); margin-right: 8px; }
.khata__credit { color: var(--ok); font-weight: 600; }
.khata__payment { background: color-mix(in srgb, var(--ok-bg) 35%, transparent); }
.dues-wa { color: #128c4b; }
.dues-wa:hover { color: #0b6b38; background: #e8f8ef; }
.dues-table .btn + .btn, .dues-table .btn + .rowmenu { margin-left: 4px; }
.dues-table .rowmenu { display: inline-block; vertical-align: middle; }

.seg__opt.is-on { background: var(--violet-soft); color: var(--violet); font-weight: 600; }

/* ---- one customer (customer.php) -------------------------------------------
   The record on a narrow column at the side, their history on the wide one.
   Below a laptop the side goes under, not squeezed next to it. */
.avatar--lg { width: 44px; height: 44px; font-size: var(--fs-body); flex: 0 0 44px; }
/* The side column only when the khata still has room beside it - a laptop at
   125% zoom is about 1250 wide, and there the table was cut off at Balance.
   Narrower than that, Details goes on top as a strip of facts, and the
   history below gets the full width. */
.cust-layout { display: grid; gap: var(--gap-card); grid-template-columns: minmax(0, 1fr); align-items: start; }
.cust-side { order: -1; }
@media (max-width: 1399px) {
  .cust-side .card__head { display: none; }
  .cust-facts { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); column-gap: 24px; }
  .cust-facts > div, .cust-facts > div:last-child { grid-template-columns: 1fr; gap: 0; border-bottom: 0; padding: 4px 0; }
  .cust-facts dt { font-size: var(--fs-small); }
}
@media (min-width: 1400px) {
  .cust-layout { grid-template-columns: minmax(0, 1fr) 300px; }
  .cust-side { order: 0; }
}
.khata .t-num { white-space: nowrap; }
.cust-main { padding: 16px; min-width: 0; }
.cust-facts { margin: 0; display: grid; gap: 0; }
.cust-facts > div {
  display: grid; grid-template-columns: 96px minmax(0, 1fr); gap: 12px;
  padding: 8px 0; border-bottom: 1px solid var(--line); font-size: var(--fs-label);
}
.cust-facts > div:last-child { border-bottom: 0; }
.cust-facts dt { color: var(--muted); }
.cust-facts dd { margin: 0; font-weight: 500; overflow-wrap: anywhere; }
.cust-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 12px; }
.cust-note {
  margin: 12px 0 0; padding: 12px; border-radius: var(--r-sm); background: var(--raised);
  font-size: var(--fs-label); color: var(--muted);
}
.cust-main .chips { padding: 0; }
.khata-show { display: inline-flex; width: auto; }
.stmt-doc { font-size: .8em; }
#payMode { width: 100%; }
#payMode .seg__opt { flex: 1; }

.dues-box {
  width: min(460px, calc(100vw - 32px)); padding: 0; border: 0; border-radius: var(--r-xl);
  background: var(--card); color: var(--ink); box-shadow: 0 24px 60px -20px rgba(20, 20, 45, .45);
}
.dues-box::backdrop { background: rgba(20, 20, 45, .45); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); }
.dues-box[open] { animation: nx-welcome-in .28s cubic-bezier(.2, .8, .2, 1) both; }
.dues-box__inner { display: grid; gap: 12px; padding: 20px; }
.dues-box__head { display: flex; align-items: center; gap: 12px; }
.dues-box__head h2 { margin: 0; font-size: 17px; }
.dues-box__head .mono { font-size: var(--fs-small); }
.dues-box__badge {
  flex: 0 0 40px; width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center; color: #fff;
  background: linear-gradient(135deg, #4f46e5 0%, #0ea5e9 100%); box-shadow: 0 0 0 4px rgba(14, 165, 233, .15);
}
.dues-box__amount { height: var(--h-till); font-size: var(--fs-till); font-weight: 700; }
.dues-box__foot { display: flex; justify-content: flex-end; gap: 8px; }
#issuePay { width: min(520px, calc(100vw - 32px)); }
#issuePay #ipBack { margin-right: auto; }
.ip-step { display: grid; gap: 12px; }
.ip-step[hidden] { display: none; }
#ipRel { width: 100%; margin-top: 4px; }
#ipRel .seg__opt { flex: 1; }
.ip-left {
  padding: 8px 12px; border-radius: var(--r-sm); background: var(--warn-bg); color: var(--warn);
  font-size: var(--fs-small);
}
.dues-box .kudos-box__picks .chip { cursor: pointer; }

/* Where the money goes before it goes there: oldest first, each line saying
   whether it clears or only comes down. */
.dues-spread { display: grid; gap: 4px; max-height: 220px; overflow: auto; }
.dues-spread:empty { display: none; }
.dues-spread__row {
  display: grid; grid-template-columns: 1fr auto auto; gap: 12px; align-items: center;
  padding: 8px 12px; border-radius: var(--r-sm); background: var(--raised); font-size: var(--fs-small);
}
.dues-spread__row b { font-weight: 600; color: var(--muted); }
.dues-spread__row.is-part  { background: var(--warn-bg); }
.dues-spread__row.is-part b { color: var(--warn); }
.dues-spread__row.is-clear { background: var(--ok-bg); }
.dues-spread__row.is-clear b { color: var(--ok); }
@media (prefers-reduced-motion: reduce) { .dues-box[open] { animation: none; } }

/* ---- the customer's statement (statement.php) - the rate page's shell ------- */
.stmt { max-width: 480px; }
.stmt__card { gap: 16px; }
.stmt__shop { display: flex; align-items: center; gap: 12px; }
.stmt__shop img, .stmt__mono { width: 44px; height: 44px; border-radius: var(--r); object-fit: contain; flex: 0 0 44px; }
.stmt__mono {
  display: grid; place-items: center; color: #fff; font-weight: 800;
  background: linear-gradient(135deg, #4f46e5 0%, #0ea5e9 100%);
}
.stmt__shop b { display: block; font-size: var(--fs-body); }
.stmt__shop div span { font-size: var(--fs-small); color: var(--muted); }
.stmt__shop .stmt__mono { font-size: 20px; }
.stmt__due {
  display: grid; gap: 4px; padding: 20px; border-radius: var(--r-lg); color: #fff;
  background: linear-gradient(135deg, #4f46e5 0%, #0ea5e9 100%);
  box-shadow: 0 16px 40px -20px rgba(79, 70, 229, .7);
}
.stmt__k { font-size: var(--fs-small); letter-spacing: .6px; text-transform: uppercase; color: #c7d2fe; }
.stmt__due strong { font-family: var(--font-head); font-size: 36px; line-height: 1.1; font-variant-numeric: tabular-nums; }
.stmt__due .muted { color: #e0e7ff; font-size: var(--fs-small); }
.stmt__pay { height: var(--h-till); font-size: var(--fs-till); justify-content: center; }
.stmt__vpa { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 8px 12px; border-radius: var(--r); background: var(--raised); }
.stmt__qr { display: grid; justify-items: center; gap: 8px; font-size: var(--fs-small); color: var(--muted); }
.stmt__qr img { width: 180px; height: 180px; object-fit: contain; border-radius: var(--r); border: 1px solid var(--line); background: #fff; padding: 8px; }
.stmt__list { display: grid; gap: 4px; }
.stmt__row {
  display: flex; justify-content: space-between; gap: 12px; padding: 12px; border-radius: var(--r-sm);
  background: var(--raised);
}
.stmt__row b { display: block; font-size: var(--fs-body); }
.stmt__row span { font-size: var(--fs-small); color: var(--muted); }
.stmt__paid { justify-content: center; color: #128c4b; }
.stmt__clear { display: flex; align-items: center; gap: 8px; margin: 0; color: var(--ok); font-weight: 600; }

/* ---- Permissions grid (team-form) ------------------------------------------
   Same four actions in the same four columns on every row, so the page is
   read by position; bands chunk the areas; amber marks what costs money or
   cannot be undone; a dot marks what was set by hand. */
.pm { overflow: hidden; container: pm / inline-size; }
.pm__head { align-items: center; gap: 16px; flex-wrap: wrap; }
.pm__title { display: flex; align-items: center; gap: 12px; min-width: 0; }
.pm__ring { width: 44px; height: 44px; flex: 0 0 44px; }
.pm__ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.pm__ring circle { fill: none; stroke-width: 4; }
.pm__ring-bg { stroke: var(--line); }
.pm__ring-fg { stroke: url(#pmGrad); stroke-linecap: round; transition: stroke-dasharray .4s ease; }
.pm__meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 4px; font-size: var(--fs-small); color: var(--muted); }
.pm__role {
  display: inline-flex; align-items: center; gap: 4px; padding: 0 8px; height: 20px;
  border-radius: 999px; background: var(--brand-bg); color: var(--brand); font-weight: 600;
}
.pm__changed { display: inline-flex; align-items: center; gap: 4px; color: var(--info); font-weight: 600; }
.pm__changed::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--info); box-shadow: 0 0 0 3px var(--info-bg); }
.pm__acts { display: flex; gap: 8px; margin-left: auto; }
#permSave.is-done { background: var(--ok); border-color: var(--ok); }

/* One line per area. The name on the left; everything it can do on the same
   line to its right - View, Add, Edit and Delete first, each in a slot of the
   same width so they stand in columns down the page, then a thin rule, then
   whatever else that area has. */
.pm__band + .pm__band { border-top: 1px solid var(--line); }
.pm__band-h {
  display: flex; align-items: center; gap: 8px; padding: 12px var(--pad-card) 4px;
  font-size: var(--fs-small); font-weight: 700; letter-spacing: .8px; text-transform: uppercase; color: var(--ink-2);
}
.pm__band-h::after { content: ""; flex: 1; height: 1px; background: linear-gradient(90deg, var(--line-strong), transparent); }
.pm__band-h i { font-style: normal; font-weight: 500; color: var(--muted); letter-spacing: 0; order: 2; }

.pm__row {
  display: flex; align-items: center; gap: 12px; padding: 8px var(--pad-card);
  border-radius: var(--r-sm); transition: background .15s ease;
}
.pm__row:hover { background: var(--surface); }
.pm__area { display: flex; align-items: center; gap: 12px; flex: 0 0 184px; min-width: 0; }
.pm__icon {
  display: grid; place-items: center; width: 32px; height: 32px; flex: 0 0 32px; border-radius: var(--r-sm);
  color: var(--brand); background: var(--brand-bg); border: 1px solid var(--brand-line);
}
.pm__name { display: grid; gap: 4px; min-width: 0; flex: 1; }
.pm__name .kv__k { margin: 0; font-size: var(--fs-body); font-weight: 600; color: var(--ink); text-transform: none; letter-spacing: 0; }
.pm__bar { display: block; height: 4px; max-width: 96px; border-radius: 4px; background: var(--line); overflow: hidden; }
.pm__bar i { display: block; height: 100%; width: 0; border-radius: inherit; background: linear-gradient(90deg, #4f46e5, #0ea5e9); transition: width .3s ease; }

.pm__all { display: grid; place-items: center; width: 20px; height: 20px; flex: 0 0 20px; padding: 0; border: 0; background: none; cursor: pointer; }
.pm__all-box { width: 16px; height: 16px; border-radius: 4px; border: 1.5px solid var(--line-strong); position: relative; transition: all .15s ease; }
.pm__all[data-state="all"] .pm__all-box,
.pm__all[data-state="some"] .pm__all-box { border-color: transparent; background: linear-gradient(135deg, #4f46e5, #0ea5e9); }
.pm__all[data-state="some"] .pm__all-box::after { content: ""; position: absolute; left: 3px; right: 3px; top: 6px; height: 2px; border-radius: 2px; background: #fff; }
.pm__all[data-state="all"] .pm__all-box::after {
  content: ""; position: absolute; left: 4px; top: 1px; width: 4px; height: 8px;
  border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(45deg);
}

/* The switches. Base is the narrow look - every switch a chip with its own
   name - and a card 720px or wider turns View, Add, Edit and Delete into
   tick cells under column heads, the way a corporate access matrix reads,
   with the area's other switches as small chips after a rule. */
.pm__cols { display: none; }
.pm__opts { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; flex: 1; min-width: 0; }
.pm__core, .pm__extra { display: contents; }

.pm-chip {
  position: relative; display: inline-flex; align-items: center; gap: 4px; height: 28px; padding: 0 8px;
  border-radius: 999px; border: 1px solid var(--line-strong); background: var(--card); cursor: pointer;
  font: inherit; font-size: var(--fs-small); color: var(--muted); white-space: nowrap;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
/* An action this area does not have keeps its slot, empty, so the columns hold. */
.pm-chip--none { visibility: hidden; }
.pm-chip:hover { border-color: var(--brand); color: var(--ink); }
.pm-chip:active { transform: scale(.97); }
.pm-chip__box { display: grid; place-items: center; width: 16px; height: 16px; flex: 0 0 16px; border-radius: 50%; border: 1.5px solid var(--line-strong); color: transparent; }
.pm-chip[aria-pressed="true"] { color: var(--brand); border-color: var(--brand-line); background: var(--brand-bg); font-weight: 600; }
.pm-chip[aria-pressed="true"] .pm-chip__box { border-color: transparent; color: #fff; background: linear-gradient(135deg, #4f46e5, #0ea5e9); }
.pm-chip.is-risk[aria-pressed="true"] { color: var(--warn); border-color: var(--warn-line); background: var(--warn-bg); }
.pm-chip.is-risk[aria-pressed="true"] .pm-chip__box { background: linear-gradient(135deg, #f59e0b, #ea580c); }

/* Set by hand: a dot on the corner, the same blue as the count in the head. */
.pm-chip:not([data-over=""]):not(.pm-chip--none)::after {
  content: ""; position: absolute; top: -2px; right: -2px; width: 8px; height: 8px; border-radius: 50%;
  background: var(--info); box-shadow: 0 0 0 2px var(--card);
}
[data-lock]:not([aria-pressed="true"]) { cursor: not-allowed; opacity: .5; }
[data-lock]:not([aria-pressed="true"]) .pm-chip__box { color: var(--muted); border-style: dashed; }
/* View off: the rest of the line waits for it, and looks like it. */
.pm__row.is-dim [data-code]:not([data-code$=".view"]) { opacity: .45; }
.pm-chip:focus-visible, .pm__all:focus-visible .pm__all-box { outline: 2px solid var(--brand); outline-offset: 2px; }

.pm__key {
  display: flex; flex-wrap: wrap; gap: 16px; padding: 12px var(--pad-card);
  border-top: 1px solid var(--line); font-size: var(--fs-small); color: var(--muted);
}
.pm__key span { display: inline-flex; align-items: center; gap: 8px; }
.pm__sw { width: 12px; height: 12px; border-radius: 4px; }
.pm__sw--on { background: linear-gradient(135deg, #4f46e5, #0ea5e9); }
.pm__sw--risk { background: linear-gradient(135deg, #f59e0b, #ea580c); }
.pm__sw--dot { width: 8px; height: 8px; border-radius: 50%; background: var(--info); }

/* Narrow: the name on its own line, its switches straight under it. */
@container pm (max-width: 719px) {
  .pm__row { flex-direction: column; align-items: stretch; gap: 8px; }
  .pm__area { flex-basis: auto; }
  .pm__opts { padding-left: 32px; }
  .pm-chip--none { display: none; }
}

/* Wide: the matrix. */
@container pm (min-width: 720px) {
  .pm__cols {
    display: flex; align-items: center; gap: 12px; padding: 8px var(--pad-card);
    background: var(--surface); border-bottom: 1px solid var(--line);
    font-size: var(--fs-small); font-weight: 600; color: var(--muted); letter-spacing: .4px; text-transform: uppercase;
  }
  .pm__cols-area { flex: 0 0 184px; }
  .pm__core { display: flex; flex: 0 0 auto; }
  .pm__cols .pm__core span { width: 56px; text-align: center; }
  .pm__opts { flex-wrap: nowrap; gap: 12px; }
  .pm__extra {
    display: flex; flex-wrap: wrap; gap: 4px; min-width: 0;
    padding-left: 12px; border-left: 1px solid var(--line);
  }
  .pm-chip--core {
    width: 56px; height: 32px; padding: 0; justify-content: center;
    border-color: transparent; background: none; border-radius: var(--r-sm);
  }
  .pm-chip--core .pm-chip__lbl { display: none; }
  .pm-chip--core .pm-chip__box { width: 20px; height: 20px; flex-basis: 20px; border-radius: 6px; }
  .pm-chip--core:hover { border-color: transparent; background: var(--surface); }
  .pm-chip--core[aria-pressed="true"],
  .pm-chip--core.is-risk[aria-pressed="true"] { border-color: transparent; background: none; }
  .pm-chip--core:not([data-over=""])::after { top: 2px; right: 12px; }
}
@media (prefers-reduced-motion: reduce) {
  .pm__ring-fg, .pm__bar i, .pm-chip, .pm__row { transition: none; }
}

/* The strip of people over the permissions grid: hop from one to the next
   without going back to the list. */
.pm-people {
  display: flex; gap: 8px; overflow-x: auto; padding: 4px 4px 12px; margin: 0 -4px 4px;
  scrollbar-width: thin;
}
.pm-person {
  display: flex; align-items: center; gap: 8px; flex: 0 0 auto; padding: 8px 16px 8px 8px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--card); color: var(--ink);
  text-decoration: none; transition: border-color .15s ease, box-shadow .15s ease;
}
.pm-person:hover { border-color: var(--brand-line); }
.pm-person .avatar { width: 28px; height: 28px; font-size: var(--fs-small); }
.pm-person b { display: block; font-size: var(--fs-label); font-weight: 600; white-space: nowrap; }
.pm-person i { display: block; font-style: normal; font-size: var(--fs-small); color: var(--muted); white-space: nowrap; }
.pm-person.is-on { border-color: var(--brand); box-shadow: 0 0 0 3px var(--violet-glow); }
.pm-person.is-on .avatar { color: #fff; background: linear-gradient(135deg, #4f46e5, #0ea5e9); }
@media (prefers-reduced-motion: reduce) { .pm-person { transition: none; } }
/* Roles (team-roles): the roles in one list down the left, the open one on
   the right as a single card - its name, its people, its switches. Under
   900px the list turns into a strip across the top. */
.rl { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: var(--gap-card); align-items: start; }
.rl__list {
  display: grid; gap: 4px; padding: 8px; position: sticky; top: 16px;
  border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--card);
}
.rl__item {
  display: flex; align-items: center; gap: 12px; padding: 8px; border-radius: var(--r-sm);
  color: var(--ink); text-decoration: none; transition: background .15s ease;
}
.rl__item:hover { background: var(--surface); }
.rl__item.is-on { background: var(--brand-bg); box-shadow: inset 3px 0 0 var(--brand); }
.rl__icon {
  display: grid; place-items: center; width: 28px; height: 28px; flex: 0 0 28px; border-radius: var(--r-sm);
  color: var(--brand); background: var(--brand-bg); border: 1px solid var(--brand-line);
}
.rl__item.is-on .rl__icon, .rl__icon--big { color: #fff; border-color: transparent; background: linear-gradient(135deg, #4f46e5, #0ea5e9); }
.rl__icon--big { width: 44px; height: 44px; flex-basis: 44px; border-radius: var(--r); }
.rl__body { display: grid; gap: 4px; flex: 1; min-width: 0; }
.rl__body b { font-size: var(--fs-label); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rl__meta { display: flex; justify-content: space-between; gap: 8px; font-size: var(--fs-small); color: var(--muted); }
.rl__body .pm__bar { max-width: none; }

/* The head of the one card: the role's name (typed over to rename) and the
   people on it, the count and the buttons on the right. */
.rl__who { display: grid; gap: 4px; min-width: 0; }
.rl__who h2 { font-family: var(--font-head); font-size: var(--fs-h1); margin: 0; }
.rl__name { display: flex; gap: 8px; align-items: center; }
.rl__name .input {
  font-family: var(--font-head); font-size: var(--fs-h1); font-weight: 700; height: var(--h-control);
  border-color: transparent; background: transparent; padding-left: 4px; margin-left: -4px; max-width: 320px;
}
.rl__name .input:hover { border-color: var(--line-strong); }
.rl__name .input:focus { border-color: var(--brand); background: var(--card); }
.rl__people { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; font-size: var(--fs-small); }
.rl__person {
  display: inline-flex; align-items: center; gap: 8px; height: 32px; padding: 0 12px 0 4px;
  border: 1px solid var(--line); border-radius: 999px; color: var(--ink); text-decoration: none; font-weight: 500;
}
.rl__person:hover { border-color: var(--brand-line); }
.rl__person .avatar { width: 24px; height: 24px; font-size: var(--fs-small); }
.pm__title:has(.pm__title-meta) { flex: 1; }
.pm__title-meta { margin-left: auto; text-align: right; }
.rl__del { color: var(--danger); }
.rl__all { display: flex; align-items: center; gap: 8px; color: var(--muted); }
.rl__all b { color: var(--ink); }
.rl__main > .pm { margin-top: 0 !important; }
@media (max-width: 900px) {
  .rl { grid-template-columns: minmax(0, 1fr); }
  .rl__list { position: static; display: flex; overflow-x: auto; }
  .rl__item { flex: 0 0 180px; }
  .pm__title-meta { margin-left: 0; padding-left: 0; border-left: 0; }
}
@media (prefers-reduced-motion: reduce) { .rl__item { transition: none; } }

/* Invoice template: the terms box, its word count and the text-size stepper. */
.terms-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.terms-head #termsCount { font-size: var(--fs-small); color: var(--muted); }
.terms-head #termsCount.is-over { color: var(--danger); font-weight: 600; }
.terms-box { min-height: 280px; resize: vertical; line-height: 1.5; }
.sizer { display: flex; align-items: center; gap: 8px; }
.sizer__bt { width: var(--h-control); height: var(--h-control); padding: 0; justify-content: center; font-size: 20px; line-height: 1; }
.sizer__val { min-width: 64px; text-align: center; font-weight: 600; }
.sizer__fit { display: block; margin-top: 4px; font-size: var(--fs-small); color: var(--warn); }
/* The terms under the bill preview, across the card: the box wide, the size
   and Save beside it. One column on a phone. */
.terms-panel {
  display: grid; grid-template-columns: minmax(0, 1fr) 240px; gap: var(--gap-card);
  margin-top: var(--gap-card); padding-top: var(--gap-card); border-top: 1px solid var(--line);
}
.terms-side { display: flex; flex-direction: column; gap: var(--gap-field); }
.terms-side__acts { display: grid; gap: 8px; margin-top: auto; }
@media (max-width: 900px) { .terms-panel { grid-template-columns: minmax(0, 1fr); } }

/* The row menu's column stays on screen when a list is wider than its card:
   the "..." is how a row is acted on, so it is never the part scrolled off. */
.table .t-menu { position: sticky; right: 0; background: var(--card); z-index: 1; width: 56px; padding-left: 8px; padding-right: 12px; }
.t-menu .rowmenu .btn--icon { width: 32px; height: 32px; padding: 0; justify-content: center; flex: 0 0 32px; }
.table thead .t-menu { background: var(--raised); }
.table tbody tr:hover .t-menu { background: var(--raised); }
.dropdown__item--danger { color: var(--danger); }
.dropdown__item--danger:hover { background: var(--danger-bg); }
/* ---- Update prices (inventory-prices.php) ----------------------------------
   The question in one card across the top - which items, then what to do to
   them, each on one line - and the answer at full width underneath. The
   preview is where the eye should land: it is the only colour on the page,
   green for what will move and red for what is locked out. */
.pu-build { padding: 0; }
.pu-block { padding: var(--pad-card) 20px; }
.pu-block + .pu-block { border-top: 1px solid var(--line); }
.pu-block__head {
  display: flex; align-items: center; gap: 8px; margin-bottom: 12px;
  font-size: var(--fs-card-h); font-weight: 700; color: var(--ink);
}
.pu__n {
  display: inline-grid; place-items: center; width: 20px; height: 20px;
  border-radius: 999px; background: var(--violet-soft); color: var(--violet);
  font-size: var(--fs-small); font-weight: 700;
}
.pu-fields { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 16px; }
.pu-fields .field + .field { margin-top: 0; }
/* Each field grows in proportion from a size its content fits in, so a line
   fills the card edge to edge at any width and only breaks when it must. */
.pu-f { min-width: 0; }
.pu-f--kind    { flex: 3 1 280px; }
.pu-f--sel     { flex: 2 1 160px; }
.pu-f--quality { flex: 4 1 400px; }
.pu-f--grow    { flex: 3 1 200px; }
.pu-f--price   { flex: 2 1 200px; }
.pu-f--change  { flex: 3 1 280px; }
.pu-f--round   { flex: 2 1 200px; }
.pu-f__label { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.pu-f__label a { font-size: var(--fs-small); font-weight: 600; margin-bottom: 6px; }

.pu-main { display: grid; gap: 12px; margin-top: var(--gap-card); }
/* A grid child is as wide as its widest content unless told otherwise, and
   the preview table is 560 - it pushed the card off a phone screen. */
.pu-main > * { min-width: 0; }

/* A row of answers where one is picked. Buttons to the eye, radios underneath,
   so the keyboard and a screen reader get the real thing. The same height as
   a text box, so a line of them and a line of inputs sit level. */
.pu-opts {
  display: grid; gap: 4px; height: var(--h-control); padding: 3px;
  border-radius: var(--r-sm); background: var(--raised); border: 1px solid var(--line);
}
.pu-opts--2 { grid-template-columns: repeat(2, 1fr); }
.pu-opts--4 { grid-template-columns: repeat(4, 1fr); }
.pu-opts--5 { grid-template-columns: repeat(5, 1fr); }
.pu-opt { position: relative; display: block; cursor: pointer; min-width: 0; }
.pu-opt input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: pointer; }
.pu-opt span {
  display: flex; align-items: center; justify-content: center; height: 100%; padding: 0 8px;
  border-radius: 6px; font-size: var(--fs-label); font-weight: 500; color: var(--muted);
  white-space: nowrap; transition: background .12s, color .12s, box-shadow .12s;
}
.pu-opt:hover span { color: var(--ink); }
.pu-opt input:checked + span { background: var(--card); color: var(--violet); font-weight: 600; box-shadow: var(--lift-2); }
.pu-opt input:focus-visible + span { outline: 2px solid var(--violet); outline-offset: 1px; }
.pu-off {
  display: flex; align-items: center; height: var(--h-control); padding: var(--pad-control);
  border: 1px dashed var(--line-strong); border-radius: var(--r-sm);
  font-size: var(--fs-label); color: var(--muted);
}

/* The number is the decision, so it is the heaviest thing on the line. What
   the box understood sits inside it on the right - green up, amber down - so
   "10" and "10%" can never be mistaken for each other. */
.pu-value { position: relative; display: flex; align-items: center; }
.pu-value__in { font-size: var(--fs-till); font-weight: 700; font-variant-numeric: tabular-nums; padding-right: 96px; }
.pu-says {
  position: absolute; right: 6px; padding: 2px 10px; border-radius: 999px; pointer-events: none;
  font-size: var(--fs-label); font-weight: 700; font-variant-numeric: tabular-nums;
}
.pu-says.is-up   { background: var(--ok-bg); color: var(--ok); }
.pu-says.is-down { background: var(--warn-bg); color: var(--warn); }
.pu-sum { display: flex; gap: 8px; flex-wrap: wrap; }
.pu-blank { padding: 40px 20px; }
.pu-table { min-width: 560px; }
.pu-table__tick { width: 44px; padding-right: 0; }
.pu-table__tick input { width: 16px; height: 16px; accent-color: var(--violet); cursor: pointer; vertical-align: middle; }
.pu-num { font-variant-numeric: tabular-nums; white-space: nowrap; }
.pu-now { color: var(--muted); }
.pu-new { font-weight: 700; }
.pu-cost { color: var(--muted); }
.pu-delta { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: var(--fs-small); font-weight: 700; font-variant-numeric: tabular-nums; }
.pu-delta.is-up   { background: var(--ok-bg); color: var(--ok); }
.pu-delta.is-down { background: var(--warn-bg); color: var(--warn); }
/* Picked by hand. A row that can move and is not ticked keeps its new price
   grey, so the eye separates "could" from "will"; a ticked one is tinted. */
.pu-row.is-picked, .pu-row.is-unpicked { cursor: pointer; }
.pu-row.is-picked td { background: var(--violet-soft); }
.pu-row.is-unpicked .pu-new { color: var(--faint); font-weight: 500; }
.pu-row.is-unpicked .pu-delta { background: transparent; color: var(--faint); }
.pu-row.is-idle td { color: var(--faint); }
.pu-row.is-idle .pu-new { font-weight: 500; }
.pu-row.is-look .pu-new { font-weight: 500; color: var(--faint); }
.pu-row.is-block { background: var(--danger-bg); }
.pu-row.is-block .pu-new, .pu-row.is-block .cell-main { color: var(--danger); }
.pu-row.is-block .pu-delta { background: transparent; color: var(--danger); }
.pu-why { margin-top: 2px; font-size: var(--fs-small); font-weight: 600; color: var(--danger); }
.pu-row.is-idle .pu-why { color: var(--faint); }
.pu-lock { display: inline-grid; place-items: center; width: 16px; color: var(--danger); }

.pu-foot {
  position: sticky; bottom: 0; display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: var(--pad-card-y) var(--pad-card); border-top: 1px solid var(--line);
  background: var(--card); border-radius: 0 0 var(--r-lg) var(--r-lg);
}
.pu-foot__says { font-size: var(--fs-label); font-weight: 600; color: var(--muted); }
.pu-done { margin-bottom: 0; }
.pu-done span { flex: 1; font-weight: 600; }
.pu-hist { margin-top: var(--gap-card); }
.pu-hist__acts { display: inline-flex; gap: 8px; }
@media (max-width: 760px) {
  .pu-block { padding: var(--pad-card); }
  .pu-f, .pu-f--grow { flex: 1 1 100%; }
  .pu-opt span { padding: 0 4px; }
}
@media (max-width: 560px) {
  .pu-f--quality .pu-opts--5 { height: auto; grid-template-columns: repeat(3, 1fr); }
  .pu-f--quality .pu-opt span { height: 28px; }
  .pu-foot { flex-direction: column; align-items: stretch; }
  /* Item, now and new is the whole story on a phone. The cost is on the item
     and the arrow of the change is in the two numbers. */
  .pu-table { min-width: 0; }
  .pu-table .pu-cost, .pu-table .pu-chg { display: none; }
}

/* ===========================================================================
   Demo mode - practice on a copy of the shop (app/Demo.php)

   Amber everywhere it shows, and nowhere else in the product uses amber as a
   frame: the colour alone has to say "this is not the real shop" before a
   word is read. The bar says it in words, the pill in the top bar keeps
   saying it once the bar has scrolled away, and the top bar's edge carries
   it on every screen.
   =========================================================================== */

.is-demo .topbar { border-bottom: 2px solid var(--warn); }

.demo-pill {
  display: inline-flex; align-items: center; gap: 8px; flex: none;
  height: 28px; padding: 0 12px;
  background: var(--warn-bg); color: var(--warn); border-color: var(--warn-line);
  font-size: var(--fs-small); font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
}
.demo-pill__dot {
  width: 8px; height: 8px; border-radius: 50%; background: var(--warn);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--warn) 22%, transparent);
  animation: demo-beat 1.6s ease-in-out infinite;
}
@keyframes demo-beat { 50% { box-shadow: 0 0 0 1px color-mix(in srgb, var(--warn) 10%, transparent); } }
@media (prefers-reduced-motion: reduce) { .demo-pill__dot { animation: none; } }

.demo-bar {
  display: flex; align-items: center; gap: 12px;
  margin: 16px 16px 0; padding: 8px 8px 8px 12px;
  background: var(--warn-bg); color: var(--warn);
  border: 1px solid var(--warn-line); border-radius: var(--r-sm);
  background-image: repeating-linear-gradient(135deg, transparent 0 12px,
                    color-mix(in srgb, var(--warn) 6%, transparent) 12px 24px);
}
.demo-bar__icon {
  display: grid; place-items: center; flex: none;
  width: 32px; height: 32px; border-radius: var(--r-sm);
  background: var(--card); border: 1px solid var(--warn-line);
}
.demo-bar__text { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; min-width: 0; font-size: var(--fs-label); }
.demo-bar__text b { font-weight: 600; }
.demo-bar__text span { color: var(--ink); opacity: .72; }
.demo-bar__acts { display: flex; gap: 8px; margin-left: auto; flex: none; }
.demo-bar__acts .btn { height: 32px; }
.demo-bar__acts .btn--ghost { background: var(--card); border-color: var(--warn-line); color: var(--warn); }
.demo-bar__acts .btn--ghost.is-sure { background: var(--warn); border-color: var(--warn); color: var(--card); }
.demo-bar__off { background: var(--warn); border-color: var(--warn); color: var(--card); }
.demo-bar__off:hover { filter: brightness(1.08); }

/* The switch in the menu. */
.demo-toggle .demo-sw {
  position: relative; flex: none; margin-left: auto;
  width: 32px; height: 20px; border-radius: 999px;
  background: var(--line-strong); transition: background-color .15s;
}
.demo-toggle .demo-sw::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 16px; height: 16px; border-radius: 50%; background: var(--card);
  box-shadow: var(--lift-1); transition: transform .15s;
}
.demo-toggle[aria-checked="true"] { color: var(--warn); }
.demo-toggle[aria-checked="true"] .demo-sw { background: var(--warn); }
.demo-toggle[aria-checked="true"] .demo-sw::after { transform: translateX(12px); }

/* While the copy is made - a few seconds on a big shop. */
.demo-wait {
  position: fixed; inset: 0; z-index: 90; display: grid; place-items: center;
  background: rgba(20, 20, 35, .5); backdrop-filter: blur(2px);
}
.demo-wait__card {
  display: flex; align-items: center; gap: 12px;
  padding: 16px 20px; border-radius: var(--r);
  background: var(--card); box-shadow: var(--lift-4); font-size: var(--fs-body);
}
.demo-wait__card b { font-weight: 600; }
.demo-wait__spin {
  width: 20px; height: 20px; border-radius: 50%;
  border: 2px solid var(--warn-line); border-top-color: var(--warn);
  animation: demo-spin .8s linear infinite;
}
@keyframes demo-spin { to { transform: rotate(360deg); } }

.demo-toast {
  position: fixed; left: 50%; bottom: 24px; z-index: 95; transform: translateX(-50%);
  padding: 8px 16px; border-radius: 999px;
  background: var(--ink); color: var(--card); font-size: var(--fs-label); font-weight: 500;
  box-shadow: var(--lift-3);
}

@media (max-width: 640px) {
  /* The bar under the top bar already says it in words; up here the dot
     alone keeps saying it without pushing the name off a phone screen. */
  .demo-pill { width: 28px; padding: 0; justify-content: center; font-size: 0; gap: 0; }
  .demo-bar { flex-wrap: wrap; margin: 12px 12px 0; }
  .demo-bar__acts { width: 100%; }
  .demo-bar__acts .btn { flex: 1; justify-content: center; }
}

/* ===========================================================================
   Accounts (accounts.php)

   Three questions, top to bottom: does the drawer match tonight, am I making
   money this month, who owes whom. The till is laid out the way a person
   does it on paper - a column of sums with the answer under a rule - so it
   reads as arithmetic, not as four unrelated numbers.
   =========================================================================== */

.acc-close { margin-bottom: var(--gap-card); }
.acc-close .card__head { flex-wrap: wrap; }
.acc-close .card__head h2 { white-space: nowrap; }
.acc-close__pick { display: flex; gap: 8px; flex-wrap: wrap; }
.acc-close__pick .input, .acc-close__pick .select { height: var(--h-control); width: auto; }
.acc-close__body {
  display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
}
.acc-close__body > * { min-width: 0; padding: var(--pad-card); }
.acc-close__body > * + * { border-left: 1px solid var(--line); }

.till { display: flex; flex-direction: column; gap: 4px; }
.till__row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 8px 0; font-size: var(--fs-body);
}
.till__row > span { display: block; color: var(--muted); }
.till__row span i { font-style: normal; display: inline-block; width: 16px; font-weight: 700; }
.till__row small { display: block; margin-top: 2px; padding-left: 16px; font-size: var(--fs-small); color: var(--faint); }
.till__row b { font-variant-numeric: tabular-nums; font-weight: 600; }
.till__row.is-in  i { color: var(--ok); }
.till__row.is-out i { color: var(--danger); }
.till__row--sum {
  margin-top: 4px; padding-top: 12px; border-top: 2px solid var(--ink);
}
.till__row--sum > span { color: var(--ink); font-weight: 600; }
.till__row--sum b { font-family: var(--font-head); font-size: 24px; font-weight: 700; }

.till__count { margin-top: 12px; padding-top: 16px; border-top: 1px dashed var(--line-strong); }
.till__count-row { display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap; }
.till__count-row .field { margin: 0; flex: 0 0 140px; }
.till__count-row .till__note { flex: 1 1 160px; }
.till__count-row .btn { height: var(--h-control); }
.till__diff {
  display: inline-flex; margin-top: 8px; padding: 4px 12px; border-radius: 999px;
  font-size: var(--fs-label); font-weight: 600; font-variant-numeric: tabular-nums;
}
.till__diff.is-ok, .till__stamp.is-ok .till__stamp-icon  { background: var(--ok-bg); color: var(--ok); }
.till__diff.is-short, .till__stamp.is-short .till__stamp-icon { background: var(--danger-bg); color: var(--danger); }
.till__diff.is-over, .till__stamp.is-over .till__stamp-icon  { background: var(--warn-bg); color: var(--warn); }

.till__stamp {
  display: flex; align-items: center; gap: 12px; margin-top: 12px; padding: 12px;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--raised);
}
.till__stamp-icon { display: grid; place-items: center; flex: none; width: 32px; height: 32px; border-radius: 50%; }
.till__stamp-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.till__stamp-main small { font-size: var(--fs-small); color: var(--muted); }
.till__stamp.is-short .till__stamp-main b { color: var(--danger); }
.till__stamp.is-over  .till__stamp-main b { color: var(--warn); }
.till__stamp.is-ok    .till__stamp-main b { color: var(--ok); }

.till__branches { margin-top: 12px; display: flex; flex-direction: column; gap: 4px; }
.till__branch {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 8px 12px; border: 1px solid var(--line); border-radius: var(--r-sm); color: var(--ink);
}
.till__branch:hover { border-color: var(--violet-line); background: var(--violet-soft); text-decoration: none; }

.acc-modes { display: flex; flex-direction: column; }
/* These tables are a few short rows inside a card, not a list to scroll
   sideways - the 640px floor every list table carries does not apply. */
.acc-close .table, .acc-split .table, .acc-dues .table { min-width: 0; width: 100%; }
.acc-modes__table th { font-size: var(--fs-small); }
.acc-modes__table td, .acc-modes__table th { padding-left: 0; padding-right: 0; }
.acc-modes__table tfoot td { border-top: 1px solid var(--line); font-weight: 600; }
.acc-modes__foot { margin-top: auto; padding-top: 12px; font-size: var(--fs-small); color: var(--muted); }
.acc-mode {
  display: inline-block; width: 8px; height: 8px; margin-right: 8px; border-radius: 2px;
  background: var(--faint); vertical-align: middle;
}
.acc-mode--cash { background: var(--ok); }
.acc-mode--upi  { background: var(--violet); }
.acc-mode--card { background: #2563eb; }
.acc-mode--bank { background: var(--warn); }
.is-neg { color: var(--danger); }
.is-pos { color: var(--ok); }

/* the period */
.acc-period {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  margin: 24px 0 12px;
}
.acc-period__chips { display: flex; gap: 8px; flex-wrap: wrap; }
.acc-period__custom { display: flex; align-items: center; gap: 8px; }
.acc-period__custom .input { height: 32px; width: 140px; font-size: var(--fs-label); }
.acc-period__custom .btn.is-active { border-color: var(--violet); color: var(--violet); }

.acc-kpis {
  display: grid; gap: 12px; margin-bottom: var(--gap-card);
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
.acc-kpi {
  display: flex; flex-direction: column; gap: 4px; padding: 16px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r);
  box-shadow: var(--lift-1);
}
.acc-kpi--lead { border-color: var(--violet-line); background: linear-gradient(180deg, var(--violet-soft), var(--card) 70%); }
.acc-kpi__k { font-size: var(--fs-small); font-weight: 600; color: var(--muted); }
.acc-kpi__v {
  font-family: var(--font-head); font-size: 24px; font-weight: 700;
  font-variant-numeric: tabular-nums; overflow-wrap: anywhere;
}
.acc-delta { font-size: var(--fs-small); font-weight: 600; color: var(--muted); }
.acc-delta.is-good { color: var(--ok); }
.acc-delta.is-bad  { color: var(--danger); }

/* cash flow - two thin bars a day, in beside out */
.acc-flow { margin-bottom: var(--gap-card); }
.acc-legend { display: flex; gap: 12px; font-size: var(--fs-small); color: var(--muted); }
.acc-legend span { display: inline-flex; align-items: center; gap: 4px; }
.acc-legend i, .acc-age__key i { display: inline-block; width: 8px; height: 8px; border-radius: 2px; }
.acc-legend__in  { background: var(--violet); }
.acc-legend__out { background: var(--warn); }
.acc-flow__chart { position: relative; padding: 16px 16px 8px 64px; }
.acc-flow__grid {
  position: absolute; left: 16px; right: 16px; top: 16px; height: 160px;
  display: flex; flex-direction: column; justify-content: space-between;
  font-size: 11px; color: var(--faint); font-variant-numeric: tabular-nums; pointer-events: none;
}
.acc-flow__grid span { display: block; border-top: 1px dashed var(--line); padding-top: 2px; width: 100%; }
.acc-flow__grid span:last-child { border-top-style: solid; border-color: var(--line-strong); }
.acc-flow__cols {
  position: relative; display: grid; grid-template-columns: repeat(var(--n), minmax(0, 1fr));
  align-items: end; gap: 2px; height: 160px;
}
.acc-flow__col {
  position: relative; display: flex; align-items: flex-end; justify-content: center; gap: 1px;
  height: 100%; border-radius: 4px; outline: none;
}
.acc-flow__col:hover, .acc-flow__col:focus-visible { background: var(--raised); }
.acc-flow__col i { display: block; flex: 0 1 8px; min-height: 0; border-radius: 2px 2px 0 0; }
.acc-flow__in  { background: var(--violet); }
.acc-flow__out { background: var(--warn); }
.acc-flow__tip {
  position: absolute; bottom: calc(100% + 4px); left: 50%; transform: translateX(-50%);
  z-index: 5; display: none; padding: 8px 12px; white-space: nowrap; border-radius: var(--r-sm);
  background: var(--ink); color: var(--card); font-size: var(--fs-small); line-height: 1.5;
  font-variant-numeric: tabular-nums; box-shadow: var(--lift-3); pointer-events: none;
}
.acc-flow__tip b { display: block; font-weight: 600; }
.acc-flow__col:hover .acc-flow__tip, .acc-flow__col:focus-visible .acc-flow__tip { display: block; }
.acc-flow__axis {
  display: flex; justify-content: space-between; padding-top: 8px;
  font-size: 11px; color: var(--faint);
}

/* profit and loss, GST, expenses */
.acc-split {
  display: grid; gap: var(--gap-card); margin-bottom: var(--gap-card); align-items: start;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}
.acc-stack { display: flex; flex-direction: column; gap: var(--gap-card); min-width: 0; }
.acc-pl td { padding-top: 12px; padding-bottom: 12px; font-variant-numeric: tabular-nums; }
.acc-pl tr.is-less td { color: var(--muted); }
.acc-pl tr.is-sub td { font-weight: 600; background: var(--raised); }
.acc-pl tr.is-sub .pill { margin-left: 8px; }
.acc-pl tr.is-total td { font-weight: 700; border-top: 2px solid var(--ink); }
.acc-pl tr.is-total b { font-family: var(--font-head); font-size: 18px; }

.acc-cats { list-style: none; margin: 0; padding: 8px 0; }
.acc-cats a {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px 12px;
  padding: 8px var(--pad-card); color: var(--ink);
}
.acc-cats a:hover { background: var(--raised); text-decoration: none; }
.acc-cats__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acc-cats__amt { font-variant-numeric: tabular-nums; font-weight: 600; }
.acc-cats__bar { grid-column: 1 / -1; height: 4px; border-radius: 999px; background: var(--raised); overflow: hidden; }
.acc-cats__bar i { display: block; height: 100%; border-radius: inherit; background: var(--warn); }

/* dues */
.acc-owed { padding: 16px var(--pad-card) 12px; border-bottom: 1px solid var(--line); }
.acc-owed__v { font-family: var(--font-head); font-size: 24px; font-weight: 700; color: var(--warn); font-variant-numeric: tabular-nums; }
.acc-owed__v--out { color: var(--ink); }
.acc-age { display: flex; height: 8px; margin-top: 12px; border-radius: 999px; overflow: hidden; background: var(--raised); }
.acc-age i { display: block; height: 100%; }
.acc-age__a { background: var(--ok); }
.acc-age__b { background: var(--warn); }
.acc-age__c { background: var(--danger); }
.acc-age__key { display: flex; flex-wrap: wrap; gap: 4px 16px; margin-top: 8px; font-size: var(--fs-small); color: var(--muted); }
.acc-age__key span { display: inline-flex; align-items: center; gap: 4px; }
.acc-age__key b { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }

@media (max-width: 1020px) {
  .acc-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .acc-split { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 760px) {
  .acc-close__body { grid-template-columns: minmax(0, 1fr); }
  .acc-close__body > * + * { border-left: 0; border-top: 1px solid var(--line); }
  .acc-period__custom { width: 100%; }
  .acc-period__custom .input { flex: 1; width: auto; min-width: 0; }
  .acc-kpi__v { font-size: 20px; }
  .acc-flow__chart { padding-left: 16px; }
  .acc-flow__grid span { color: transparent; }
  .till__count-row .field { flex: 1 1 120px; }
  .till__count-row .btn { flex: 1 1 100%; }
}

/* Sales: four tiles - today, the month against last month so far, what was
   collected, the average bill. Four across on a desk, two on a phone, never
   three and a lonely fourth. */
@media (min-width: 1100px) { .grid--kpi4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (min-width: 560px) and (max-width: 1099px) { .grid--kpi4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 559px) { .grid--kpi4 { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .grid--kpi4 .kpi__value { font-size: 20px; } }
.kpi__delta { margin-left: 4px; font-weight: 600; }
.kpi__delta.is-up   { color: var(--ok); }
.kpi__delta.is-down { color: var(--danger); }

/* Jobsheets: the strip under the four tiles - what work is waiting on, who
   has how much on the bench, and how long a repair takes. Read across in one
   glance, so it is one card with three groups rather than three cards. */
.kpi__note--warn { color: var(--warn); font-weight: 600; }
.kpi--alarm .kpi__value { color: var(--danger); }
.jb-pulse {
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px 24px;
  margin-bottom: var(--gap-card); padding: 12px var(--pad-card);
}
.jb-pulse__group { display: flex; align-items: center; gap: 12px; min-width: 0; }
.jb-pulse__group--grow { flex: 1 1 320px; }
.jb-pulse__group + .jb-pulse__group { padding-left: 24px; border-left: 1px solid var(--line); }
.jb-pulse__k {
  flex: none; font-size: 11px; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--faint);
}
.jb-pulse__chips { display: flex; flex-wrap: wrap; gap: 8px; min-width: 0; }
.jb-chip {
  position: relative; display: inline-flex; align-items: center; gap: 8px;
  height: 32px; padding: 0 12px; overflow: hidden;
  border: 1px solid var(--line-strong); border-radius: 999px; background: var(--card);
  font-size: var(--fs-label); color: var(--ink);
}
.jb-chip:hover { border-color: var(--violet-line); background: var(--violet-soft); text-decoration: none; }
.jb-chip.is-active { border-color: var(--violet); background: var(--violet-soft); color: var(--violet); }
.jb-chip b { font-variant-numeric: tabular-nums; }
.jb-chip--hold { border-color: var(--warn-line); background: var(--warn-bg); color: var(--warn); }
.jb-chip--tech { padding-left: 4px; }
.jb-chip__avatar {
  display: grid; place-items: center; width: 24px; height: 24px; border-radius: 50%;
  background: var(--violet-soft); color: var(--violet); font-size: 10px; font-weight: 700;
}
.jb-chip--tech.is-none .jb-chip__avatar { background: var(--warn-bg); color: var(--warn); }
.jb-chip__bar {
  position: absolute; left: 0; bottom: 0; height: 2px; background: var(--violet);
  opacity: .55; border-radius: 0 2px 0 0;
}
.jb-chip--tech.is-none .jb-chip__bar { background: var(--warn); }
.jb-speed { display: inline-flex; align-items: baseline; gap: 4px; font-size: var(--fs-label); color: var(--muted); }
.jb-speed b { font-family: var(--font-head); font-size: 20px; color: var(--ink); }
.jb-speed__d { margin-left: 4px; font-weight: 600; }
.jb-speed__d.is-good { color: var(--ok); }
.jb-speed__d.is-bad  { color: var(--danger); }
@media (max-width: 760px) {
  .jb-pulse__group { flex-wrap: wrap; width: 100%; }
  .jb-pulse__group + .jb-pulse__group { padding-left: 0; border-left: 0; padding-top: 12px; border-top: 1px solid var(--line); }
}

/* On the bench - one box per technician, replacing the chip row. */
.jb-bench { margin-bottom: var(--gap-card); padding: 12px var(--pad-card) var(--pad-card); }
.jb-bench__head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.jb-bench__head:last-child { margin-bottom: 0; }
.jb-bench__grid { display: grid; gap: 8px; grid-template-columns: repeat(auto-fill, minmax(176px, 1fr)); }
.jb-tech {
  display: grid; grid-template-columns: 32px minmax(0, 1fr) auto; align-items: center; gap: 4px 12px;
  padding: 12px; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--card); color: var(--ink);
  transition: border-color .12s, background-color .12s;
}
.jb-tech:hover { border-color: var(--violet-line); background: var(--violet-soft); text-decoration: none; }
.jb-tech.is-active { border-color: var(--violet); background: var(--violet-soft); }
.jb-tech__avatar {
  display: grid; place-items: center; width: 32px; height: 32px; border-radius: 50%;
  background: var(--violet-soft); color: var(--violet); font-size: 12px; font-weight: 700;
}
.jb-tech__who { display: flex; flex-direction: column; min-width: 0; }
.jb-tech__name { font-weight: 600; font-size: var(--fs-body); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.jb-tech__late { font-size: var(--fs-small); color: var(--danger); font-weight: 600; }
.jb-tech__n { font-family: var(--font-head); font-size: 20px; font-variant-numeric: tabular-nums; }
.jb-tech__bar { grid-column: 1 / -1; height: 4px; margin-top: 8px; border-radius: 999px; background: var(--raised); overflow: hidden; }
.jb-tech__bar i { display: block; height: 100%; border-radius: inherit; background: var(--violet); }
.jb-tech.is-none { border-style: dashed; border-color: var(--warn-line); }
.jb-tech.is-none .jb-tech__avatar { background: var(--warn-bg); color: var(--warn); }
.jb-tech.is-none .jb-tech__bar i { background: var(--warn); }
@media (max-width: 559px) { .jb-bench__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ===========================================================================
   Accounts, redesigned (1 Oct 2026) - one kind of card, one colour, one date
   control. Violet is money; its lighter shades tell the parts of a bar
   apart; green is only ever profit and red only ever a problem. Nothing else
   on this page is allowed a colour of its own.
   =========================================================================== */

.ac-head { align-items: center; flex-wrap: wrap; gap: 12px; }
.ac-head__ctl { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-left: auto; }
.ac-head__ctl .select { width: auto; min-width: 136px; height: var(--h-control); }
.ac-head__custom { display: inline-flex; gap: 8px; }
.ac-head__custom[hidden] { display: none; }
.ac-head__custom .input { width: 140px; height: var(--h-control); }

.ac-grid { display: grid; gap: 12px; margin-bottom: 12px; grid-template-columns: minmax(0, 1fr); }
.ac-grid > * { min-width: 0; }
@media (min-width: 760px) {
  .ac-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ac-grid--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1100px) {
  .ac-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .ac-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .ac-grid--galla { grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); }
  .ac-grid--src { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); }
}

.ac-card {
  display: flex; flex-direction: column; gap: 8px;
  padding: var(--pad-card); background: var(--card);
  border: 1px solid var(--line); border-radius: var(--r);
}
.ac-card__head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.ac-card__k { font-size: var(--fs-small); font-weight: 600; color: var(--muted); }
.ac-link { font-size: var(--fs-small); font-weight: 600; color: var(--violet); }
.ac-num {
  font-family: var(--font-head); font-size: 24px; font-weight: 700; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums; color: var(--ink); overflow-wrap: anywhere;
}
.ac-sub { display: flex; flex-wrap: wrap; gap: 4px 8px; align-items: baseline; }
.ac-muted { font-size: var(--fs-small); color: var(--muted); font-weight: 400; }
.ac-delta { font-size: var(--fs-small); font-weight: 600; color: var(--muted); }
.ac-delta.is-good { color: var(--ok); }
.ac-delta.is-bad  { color: var(--danger); }
.ac-card .is-pos { color: var(--ok); }
.ac-card .is-neg, .ac-table .is-neg { color: var(--danger); }
.ac-empty { margin: 0; font-size: var(--fs-label); color: var(--muted); }
.ac-empty--ok { display: inline-flex; align-items: center; gap: 8px; color: var(--ok); font-weight: 600; }
.ac-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: auto; padding-top: 4px; }

/* bars: one colour family, darkest first */
.ac-bar { display: flex; height: 6px; border-radius: 999px; overflow: hidden; background: var(--raised); }
.ac-bar--thick { height: 8px; }
.ac-bar i { display: block; height: 100%; }
.ac-bar__a { background: var(--violet); }
.ac-bar__m { background: color-mix(in srgb, var(--violet) 60%, var(--card)); }
.ac-bar__b { background: color-mix(in srgb, var(--violet) 30%, var(--card)); }
.ac-bar__c { background: color-mix(in srgb, var(--violet) 14%, var(--line)); }
.ac-bar__bad { background: var(--danger); }
.ac-legend { display: flex; flex-wrap: wrap; gap: 4px 16px; font-size: var(--fs-small); color: var(--muted); }
.ac-legend span { display: inline-flex; align-items: center; gap: 4px; }
.ac-legend b { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.ac-legend i, .ac-dot { display: inline-block; width: 8px; height: 8px; border-radius: 2px; }
.ac-dot { margin-right: 8px; vertical-align: 0; }
.ac-legend .is-neg, .ac-legend .is-neg b { color: var(--danger); }

/* today's galla */
.ac-live { margin-left: 8px; font-size: 11px; font-weight: 600; color: var(--ok); }
.ac-live::before { content: ""; display: inline-block; width: 6px; height: 6px; margin-right: 4px; border-radius: 50%; background: currentColor; vertical-align: 1px; }
.ac-galla { display: flex; align-items: center; gap: 16px; }
.ac-galla__main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.ac-galla__top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ac-pill { padding: 2px 8px; border-radius: 999px; font-size: 11px; font-weight: 600; }
.ac-pill.is-warn { background: var(--warn-bg); color: var(--warn); }
.ac-pill.is-ok   { background: var(--ok-bg); color: var(--ok); }
.ac-ring { position: relative; flex: none; width: 80px; height: 80px; }
.ac-ring svg { transform: rotate(-90deg); }
.ac-ring circle { fill: none; stroke-width: 8; }
.ac-ring__track { stroke: var(--raised); }
.ac-ring__fill  { stroke: var(--violet); stroke-linecap: round; }
.ac-ring > span {
  position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
  font-size: 10px; color: var(--muted);
}
.ac-ring b { font-family: var(--font-head); font-size: 16px; color: var(--ink); }

/* six months */
.ac-legend--head { gap: 12px; }
.ac-chart { display: grid; grid-template-columns: repeat(var(--n), minmax(0, 1fr)); gap: 12px; height: 140px; align-items: end; }
.ac-chart__col { position: relative; display: flex; flex-direction: column; height: 100%; outline: none; border-radius: var(--r-sm); }
.ac-chart__bars {
  flex: 1; display: flex; align-items: flex-end; justify-content: center; gap: 4px;
  border-bottom: 1px solid var(--line);
}
.ac-chart__bars i { display: block; width: 40%; max-width: 20px; border-radius: 4px 4px 0 0; }
.ac-chart__x { padding-top: 8px; text-align: center; font-size: 11px; color: var(--faint); }
.ac-chart__col:hover .ac-chart__bars, .ac-chart__col:focus-visible .ac-chart__bars { background: var(--raised); }
.ac-tip {
  position: absolute; bottom: calc(100% - 16px); left: 50%; transform: translateX(-50%); z-index: 5;
  display: none; padding: 8px 12px; white-space: nowrap; border-radius: var(--r-sm);
  background: var(--ink); color: var(--card); font-size: var(--fs-small); line-height: 1.5; box-shadow: var(--lift-3);
}
.ac-tip b { display: block; }
.ac-chart__col:hover .ac-tip, .ac-chart__col:focus-visible .ac-tip { display: block; }

/* tables inside cards */
.ac-table { width: 100%; border-collapse: collapse; font-size: var(--fs-label); }
.ac-table td { padding: 8px 0; border-top: 1px solid var(--line); vertical-align: middle; }
.ac-table td + td { padding-left: 12px; }
.ac-table tr:first-child td { border-top: 0; }
.ac-table b { font-variant-numeric: tabular-nums; }
.ac-table__total td { border-top: 1px solid var(--ink); font-weight: 700; }
.ac-table__total b { font-family: var(--font-head); font-size: 16px; }

/* the team */
.ac-team { list-style: none; margin: 0; padding: 0; }
.ac-team a {
  display: grid; grid-template-columns: 28px minmax(0, 1fr) auto; align-items: center; gap: 4px 12px;
  padding: 8px 0; color: var(--ink); border-top: 1px solid var(--line);
}
.ac-team li:first-child a { border-top: 0; }
.ac-team a:hover { text-decoration: none; }
.ac-team a:hover .ac-team__who { color: var(--violet); }
.ac-avatar {
  display: grid; place-items: center; width: 28px; height: 28px; border-radius: 50%;
  background: var(--violet-soft); color: var(--violet); font-size: 11px; font-weight: 700;
}
.ac-team__who { display: flex; flex-direction: column; font-weight: 600; font-size: var(--fs-label); min-width: 0; }
.ac-team b { font-variant-numeric: tabular-nums; }
.ac-team__bar { grid-column: 2 / -1; height: 4px; border-radius: 999px; background: var(--raised); overflow: hidden; }
.ac-team__bar i { display: block; height: 100%; background: var(--violet); border-radius: inherit; }

/* to do */
.ac-todo { list-style: none; margin: 0; padding: 0; }
.ac-todo li { display: flex; align-items: center; gap: 8px; padding: 8px 0; border-top: 1px solid var(--line); font-size: var(--fs-label); }
.ac-todo li:first-child { border-top: 0; }
.ac-todo span { flex: 1; min-width: 0; }
.ac-todo a { font-size: var(--fs-small); font-weight: 600; color: var(--violet); }
.ac-todo__dot { flex: none; width: 8px; height: 8px; border-radius: 50%; }
.ac-todo__dot.is-bad  { background: var(--danger); }
.ac-todo__dot.is-warn { background: var(--warn); }
.ac-todo__dot.is-info { background: var(--violet); }

/* the Close day panel */
.ac-dlg {
  width: min(640px, calc(100vw - 32px)); max-height: calc(100vh - 32px); padding: 0;
  border: 1px solid var(--line); border-radius: var(--r); background: var(--card); color: var(--ink);
  box-shadow: var(--lift-4);
}
.ac-dlg::backdrop { background: rgba(20, 20, 35, .5); backdrop-filter: blur(2px); }
.ac-dlg__head {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 12px var(--pad-card); border-bottom: 1px solid var(--line);
}
.ac-dlg__head h2 { margin: 0; font-size: var(--fs-card-h); }
.ac-dlg__pick { display: flex; gap: 8px; flex-wrap: wrap; margin-left: auto; }
.ac-dlg__pick .input, .ac-dlg__pick .select { width: auto; height: var(--h-control); }
.ac-dlg__x { display: grid; place-items: center; width: 32px; height: 32px; border-radius: var(--r-sm); color: var(--muted); background: none; }
.ac-dlg__x:hover { background: var(--raised); color: var(--ink); }
.ac-dlg__body { padding: var(--pad-card); display: flex; flex-direction: column; gap: 16px; }
.ac-modes { min-width: 0; width: 100%; }
.ac-modes th, .ac-modes td { padding-left: 0; padding-right: 0; }

@media (max-width: 759px) {
  /* Two numbers to a row on a phone - four stacked is a scroll before the
     galla is even in sight. */
  .ac-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ac-grid--4 .ac-card { padding: 12px; }
}
@media (max-width: 559px) {
  .ac-head__ctl { width: 100%; margin-left: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ac-head__ctl .select { width: 100%; min-width: 0; }
  .ac-head__ctl .btn { justify-content: center; }
  .ac-head__custom { grid-column: 1 / -1; display: grid; grid-template-columns: 1fr 1fr auto; }
  .ac-head__custom .input { width: 100%; }
  .ac-num { font-size: 18px; }
  .ac-chart { height: 112px; gap: 8px; }
  /* The per-job rate is a detail; the profit is the answer. */
  .ac-table .ac-rate { display: none; }
}

/* Customers: Individual or Business, and what the GSTIN makes them. */
.cust-kind { display: inline-flex; padding: 4px; gap: 4px; border-radius: var(--r-sm); background: var(--raised); }
.cust-kind__opt input { position: absolute; opacity: 0; pointer-events: none; }
.cust-kind__opt span {
  display: inline-flex; align-items: center; gap: 8px; height: 32px; padding: 0 12px;
  border-radius: 6px; font-size: var(--fs-label); font-weight: 600; color: var(--muted); cursor: pointer;
}
.cust-kind__opt input:checked + span { background: var(--card); color: var(--violet); box-shadow: var(--lift-1); }
.cust-kind__opt input:focus-visible + span { outline: 2px solid var(--violet); outline-offset: 2px; }
.cust-gst { display: flex; align-items: center; gap: 8px; margin: 8px 0 0; font-size: var(--fs-small); color: var(--muted); }
.cust-gst.is-bad { color: var(--danger); }
.cust-badge {
  display: inline-flex; align-items: center; height: 20px; padding: 0 8px; border-radius: 999px;
  font-size: 11px; font-weight: 700; letter-spacing: .02em; background: var(--violet-soft); color: var(--violet);
  vertical-align: middle;
}
.cust-badge--plain { background: var(--raised); color: var(--muted); }
.cell-main .cust-badge { margin-left: 8px; }
/* ---------------------------------------------------------------------------
   The finder - the box at the top of every page. What it finds drops down
   under it, and whatever is picked opens right there. See views/finder.php.
   --------------------------------------------------------------------------- */
.finder { max-width: 440px; }
.finder .input { padding-right: 48px; }
.finder.has-cam .input { padding-right: 84px; }
.finder.is-open .input { background: var(--card); border-color: var(--violet); box-shadow: 0 0 0 3px var(--violet-glow); }
.finder .input::-webkit-search-cancel-button { display: none; }
.finder__key {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  height: 20px; padding: 0 6px; border: 1px solid var(--line-strong); border-radius: 4px;
  font: 600 11px/18px var(--font-mono); color: var(--muted); background: var(--card); pointer-events: none;
}
.finder.is-open .finder__key { display: none; }
.finder__cam {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  width: 32px; height: 32px; display: grid; place-items: center;
  border: 0; border-radius: var(--r-sm); background: transparent; color: var(--violet); cursor: pointer;
}
.finder.has-cam .finder__key { right: 40px; }

.finder__pop {
  position: absolute; left: 0; top: calc(100% + 8px); z-index: 60;
  width: min(640px, calc(100vw - 32px));
  max-height: min(72vh, 640px); overflow: auto;
  background: var(--card); border: 1px solid var(--line-strong); border-radius: var(--r);
  box-shadow: var(--lift-4);
}
.finder__grp + .finder__grp { border-top: 1px solid var(--line); }
.finder__gh {
  display: flex; justify-content: space-between; padding: 12px 16px 4px;
  font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--faint);
}
.finder__row {
  display: flex; align-items: center; gap: 12px; width: 100%; padding: 8px 16px;
  border: 0; background: transparent; text-align: left; color: var(--ink); cursor: pointer;
  font: inherit; font-size: var(--fs-body);
}
.finder__row.is-on { background: color-mix(in srgb, var(--violet) 8%, var(--card)); box-shadow: inset 3px 0 0 var(--violet); }
.finder__ic {
  width: 32px; height: 32px; flex: none; display: grid; place-items: center;
  border-radius: var(--r-sm); background: var(--raised); color: var(--muted);
}
.finder__row.is-on .finder__ic { background: var(--card); color: var(--violet); }
.finder__ic.is-big { width: 44px; height: 44px; border-radius: var(--r); background: var(--violet-soft); color: var(--violet); }
.finder__txt { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.finder__t, .finder__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
.finder__name { font-size: 16px; font-weight: 600; }
.finder__s { font-size: var(--fs-small); color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.finder__end { flex: none; }
.finder__qty { font-weight: 600; font-variant-numeric: tabular-nums; min-width: 24px; display: inline-block; text-align: right; }
.finder__qty.is-out { color: var(--danger); }
.finder mark { background: color-mix(in srgb, var(--warn) 22%, transparent); color: inherit; border-radius: 2px; padding: 0 1px; }
.finder__none { padding: 24px 16px; text-align: center; color: var(--muted); font-size: var(--fs-body); }
.finder__foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 8px 16px;
  border-top: 1px solid var(--line); background: var(--raised); font-size: var(--fs-small); color: var(--muted);
  position: sticky; bottom: 0;
}
.finder__keys kbd, .finder__foot kbd {
  display: inline-block; min-width: 20px; height: 20px; padding: 0 4px; margin-right: 4px;
  border: 1px solid var(--line-strong); border-radius: 4px; background: var(--card);
  font: 600 11px/18px var(--font-mono); text-align: center; color: var(--ink-2);
}
.finder__more { display: flex; gap: 16px; margin-left: auto; }
.finder__more a { color: var(--violet); font-weight: 600; text-decoration: none; }

.finder__one { padding: 16px; }
.finder__back {
  display: inline-flex; align-items: center; gap: 4px; margin: 0 0 12px; padding: 0;
  border: 0; background: none; color: var(--violet); font: inherit; font-size: var(--fs-small); font-weight: 600; cursor: pointer;
}
.finder__oh { display: flex; align-items: center; gap: 12px; }
.finder__tiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; margin-top: 16px; }
.finder__tile {
  display: flex; flex-direction: column; gap: 4px; min-width: 0;
  padding: 8px 12px; border-radius: var(--r-sm); background: var(--raised);
}
.finder__tl { font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.finder__tv { font-size: 16px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-variant-numeric: tabular-nums; }
.finder__tv.is-bad { color: var(--danger); }
.finder__ts { font-size: var(--fs-small); color: var(--faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.finder__ts.is-ok { color: var(--ok); }
.finder__facts { display: flex; flex-wrap: wrap; gap: 4px 16px; margin-top: 12px; font-size: var(--fs-small); color: var(--muted); }
.finder__facts a { color: var(--violet); font-weight: 600; }
.finder__acts { display: flex; gap: 8px; margin-top: 16px; }
.finder__acts .finder__open { margin-left: auto; }
.finder .camscan { margin: 0 16px 16px; }

@media (max-width: 640px) {
  /* Fixed, and placed against the topbar: its blur makes the bar the box a
     fixed child is measured from. So the list hangs off the whole bar, full
     width under it, rather than off the narrow search box. */
  .finder { max-width: none; min-width: 0; }
  .finder .input { padding-right: 12px; }
  .finder.has-cam .input { padding-right: 44px; }
  .finder__key { display: none; }
  /* The box is the thing on this bar a phone uses most; the branch name
     gives up the room, not it. */
  .topbar .branch-switch .select { min-width: 0; max-width: 112px; }
  .topbar__spacer { display: none; }
  .finder__pop { position: fixed; left: 8px; right: 8px; top: calc(var(--bar) + 4px); width: auto; max-height: calc(100vh - var(--bar) - 16px); }
  .finder__tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .finder__keys { display: none; }
  .finder__acts .btn { flex: 1; justify-content: center; }
  .finder__acts .finder__open { margin-left: 0; }
}
/* Return & Exchange (returns.php): five tiles across, two per row on a phone. */
.rx-kpis { grid-template-columns: repeat(5, minmax(0, 1fr)); }
@media (max-width: 900px) { .rx-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.rx-table .cell-sub a { color: var(--violet); font-weight: 600; }

/* ---------------------------------------------------------------------------
   Return or exchange (return.php): three steps on the left, the sum on the
   right, which stays in view while the steps are filled in.
   --------------------------------------------------------------------------- */
.rx { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: var(--gap-card); align-items: start; }
.rx__steps { display: grid; gap: var(--gap-card); }
.rx-step { padding: var(--pad-card); }
.rx-step.is-done { opacity: .6; pointer-events: none; }
.rx-step__h { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: var(--fs-card-h); font-weight: 600; }
.rx-step__n {
  width: 24px; height: 24px; border-radius: 999px; display: grid; place-items: center;
  background: var(--ink); color: var(--card); font-size: var(--fs-small); font-weight: 700;
}
.rx-empty { margin: 0 0 12px; font-size: var(--fs-label); }
.rx-bill { margin-top: 12px; border: 1px solid var(--line); border-radius: var(--r-sm); overflow: hidden; }
.rx-bill__h { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 8px 12px; background: var(--raised); font-size: var(--fs-label); }
.rx-bill__h a { color: var(--violet); font-weight: 600; }
.rx-note { padding: 8px 12px; font-size: var(--fs-small); color: var(--ok); border-top: 1px solid var(--line); }
.rx-note.is-warn { color: var(--warn); }
.rx-none { margin: 0; padding: 8px 12px; border-top: 1px solid var(--line); font-size: var(--fs-label); }
.rx-line { display: flex; align-items: center; gap: 12px; padding: 8px 12px; border-top: 1px solid var(--line); font-size: var(--fs-body); cursor: pointer; }
.rx-line input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--violet); flex: none; }
.rx-line__n { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.rx-line__n small { color: var(--muted); font-size: var(--fs-small); }
.rx-line .rx-qty { width: 72px; }
.rx-cond { padding: 8px 0; border-bottom: 1px solid var(--line); }
.rx-cond:last-of-type { margin-bottom: 12px; }
.rx-cond__n { font-size: var(--fs-label); font-weight: 600; margin-bottom: 8px; }
.rx-cond__n small { color: var(--muted); font-weight: 400; }
.rx-states { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.rx-state {
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px; padding: 8px 12px;
  border: 1px solid var(--line-strong); border-radius: var(--r-sm); background: var(--card);
  font: inherit; font-size: var(--fs-label); text-align: left; cursor: pointer; color: var(--ink);
}
.rx-state small { color: var(--muted); font-size: var(--fs-small); }
.rx-state--good.is-on    { border-color: var(--ok-line);     background: var(--ok-bg);     color: var(--ok); }
.rx-state--faulty.is-on  { border-color: var(--warn-line);   background: var(--warn-bg);   color: var(--warn); }
.rx-state--damaged.is-on { border-color: var(--danger-line); background: var(--danger-bg); color: var(--danger); }
.rx-why { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.rx-why .chip { font: inherit; font-size: var(--fs-small); cursor: pointer; }
.rx-out { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.rx-opt {
  display: flex; align-items: center; justify-content: center; gap: 8px; height: 48px;
  border: 1px solid var(--line-strong); border-radius: var(--r-sm); background: var(--card);
  font: inherit; font-size: var(--fs-body); color: var(--ink); cursor: pointer;
}
.rx-opt b { font-weight: 600; }
.rx-opt.is-on { border: 2px solid var(--violet); background: var(--violet-soft); color: var(--violet); }
.rx-opt:disabled { opacity: .45; cursor: not-allowed; }
.rx-seg { margin-top: 12px; }
.rx-x { display: flex; align-items: center; gap: 8px; padding: 8px 0; border-bottom: 1px solid var(--line); }
.rx-x__n { flex: 1; min-width: 0; display: flex; flex-direction: column; font-size: var(--fs-body); }
.rx-x__n small { color: var(--muted); font-size: var(--fs-small); }
.rx-x__n .is-bad { color: var(--danger); }
.rx-x .rx-xq { width: 64px; } .rx-x .rx-xr { width: 112px; }
.rx-xunit { margin-top: 4px; max-width: 220px; }
.rx-sum { position: sticky; top: calc(var(--bar) + 16px); padding: var(--pad-card); }
.rx-sum__h { font-size: var(--fs-card-h); margin: 0 0 8px; }
.rx-sum__l { margin-top: 12px; font-size: var(--fs-small); color: var(--muted); }
.rx-r { display: flex; justify-content: space-between; gap: 12px; padding: 4px 0; font-size: var(--fs-label); }
.rx-r span:first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rx-r .is-ok { color: var(--ok); white-space: nowrap; }
.rx-r--big { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line); font-size: 16px; font-weight: 700; }
.rx-go { width: 100%; margin-top: 16px; justify-content: center; }
.rx-done { display: flex; flex-direction: column; align-items: center; gap: 4px; text-align: center; padding-top: 12px; font-size: var(--fs-label); }
.rx-done__ic { width: 44px; height: 44px; border-radius: 999px; display: grid; place-items: center; background: var(--ok-bg); color: var(--ok); margin-bottom: 8px; }
.rx-done__acts { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; margin-top: 12px; }
@media (max-width: 900px) {
  .rx { grid-template-columns: 1fr; }
  .rx-sum { position: static; }
}
@media (max-width: 640px) {
  .rx-states, .rx-out { grid-template-columns: 1fr; }
  .rx-opt { justify-content: flex-start; padding: 0 16px; }
}
/* ---------------------------------------------------------------------------
   Dealer returns (dealer-returns.php, dealer-return.php). An accounting
   document, not a form: neutral, ruled, figures right-aligned in tabular
   numerals, colour only where it says what happened to a piece.
   --------------------------------------------------------------------------- */
.dr-waiting {
  display: flex; align-items: center; gap: 12px; margin-bottom: var(--gap-card); padding: 12px 16px;
  border: 1px solid var(--warn-line); background: var(--warn-bg); color: var(--warn); border-radius: var(--r);
  font-size: var(--fs-body); text-decoration: none;
}
.dr-waiting:hover { text-decoration: none; filter: brightness(.98); }
.dr-waiting__go { margin-left: auto; display: inline-flex; align-items: center; gap: 4px; font-weight: 600; }
.dr-list .is-neg, .dr .is-neg { color: var(--danger); }
.cap { font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--faint); }
.mu { color: var(--muted); }

.dr-head {
  display: flex; align-items: center; gap: 16px; padding: 16px 20px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r) var(--r) 0 0;
}
.dr-head .page-back { margin-top: 0; }
.dr-head__t { flex: 1; min-width: 0; }
.dr-head__no { display: flex; align-items: center; gap: 12px; }
.dr-head__no .mono { font-size: 20px; font-weight: 600; letter-spacing: -.01em; }
.dr-head__sub { margin-top: 4px; font-size: var(--fs-body); color: var(--muted); }
.dr-head__sub a { color: inherit; text-decoration: underline; text-decoration-color: var(--line-strong); text-underline-offset: 3px; }
.dr-head__acts { display: flex; gap: 8px; flex: none; }
.dr-btn-dark { background: var(--ink); color: var(--card); border-color: var(--ink); }
.dr-btn-dark:hover:not(:disabled) { background: var(--ink-2); border-color: var(--ink-2); color: var(--card); }
.dr-tag { display: inline-flex; align-items: center; height: 24px; padding: 0 10px; border-radius: 999px; font-size: var(--fs-small); font-weight: 600; white-space: nowrap; }
.dr-tag--warn   { background: var(--warn-bg);   color: var(--warn); }
.dr-tag--ok     { background: var(--ok-bg);     color: var(--ok); }
.dr-tag--info   { background: var(--info-bg);   color: var(--info); }
.dr-tag--danger { background: var(--danger-bg); color: var(--danger); }
.dr-tag--calm   { background: var(--calm-bg);   color: var(--calm); }

.dr-steps {
  display: flex; align-items: center; gap: 0; margin: 0; padding: 16px 20px; list-style: none;
  background: var(--raised); border: 1px solid var(--line); border-top: 0; border-radius: 0 0 var(--r) var(--r);
  margin-bottom: var(--gap-card);
}
.dr-steps li { display: flex; align-items: center; gap: 12px; flex: none; }
.dr-steps li b { display: block; font-size: var(--fs-body); font-weight: 600; }
.dr-steps li small { display: block; font-size: var(--fs-small); color: var(--muted); }
.dr-steps__o {
  width: 28px; height: 28px; border-radius: 999px; display: grid; place-items: center; flex: none;
  border: 1.5px solid var(--ink); color: var(--ink); font-size: var(--fs-small); font-weight: 700; background: var(--card);
}
.dr-steps li.is-done .dr-steps__o { background: var(--ink); color: var(--card); }
.dr-steps__ln { flex: 1 !important; height: 2px; margin: 0 16px; background: linear-gradient(90deg, var(--ink) var(--fill, 0%), var(--line-strong) var(--fill, 0%)); }
.dr-steps__ln.is-done { background: var(--ink); }

.dr-body { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: var(--gap-card); align-items: start; }
.dr-main { padding: 0; overflow: hidden; }
.dr-kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); border-bottom: 1px solid var(--line); }
.dr-kpis > div { display: flex; flex-direction: column; gap: 4px; padding: 16px 20px; border-left: 1px solid var(--line); min-width: 0; }
.dr-kpis > div:first-child { border-left: 0; }
.dr-kpis b { font-size: 24px; font-weight: 600; line-height: 1.1; font-variant-numeric: tabular-nums; }
.dr-kpis small { font-size: var(--fs-small); color: var(--muted); }
.dr-bar-wrap { padding: 16px 20px; border-bottom: 1px solid var(--line); }
.dr-bar { display: flex; height: 8px; border-radius: 999px; background: var(--raised); overflow: hidden; }
.dr-bar i { display: block; height: 100%; }
.o-back { background: #1f7a4d; } .o-credit { background: #2556c4; } .o-reject { background: #b42323; }
.dr-legend { display: flex; flex-wrap: wrap; gap: 16px; margin-top: 8px; font-size: var(--fs-small); color: var(--muted); }
.dr-legend i { display: inline-block; width: 8px; height: 8px; border-radius: 2px; margin-right: 6px; vertical-align: 0; }
.dr-legend__r { margin-left: auto; }

.dr-tbl { width: 100%; border-collapse: collapse; table-layout: fixed; font-size: var(--fs-body); }
.dr-tbl th {
  padding: 10px 20px; text-align: left; font-size: 11px; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--faint); border-bottom: 1px solid var(--line); background: var(--card);
}
.dr-tbl td { padding: 12px 20px; border-bottom: 1px solid var(--line); vertical-align: middle; }
.dr-tbl .t-num { text-align: right; font-variant-numeric: tabular-nums; }
.dr-tbl tbody tr:last-child td { border-bottom: 0; }
.dr-row.is-pending { background: color-mix(in srgb, var(--warn-bg) 45%, var(--card)); }
.dr-row.is-picked { background: var(--card); }
.dr-seg { display: inline-grid; grid-template-columns: repeat(4, auto); border: 1px solid var(--line-strong); border-radius: var(--r-sm); overflow: hidden; background: var(--card); }
.dr-seg button {
  height: 32px; padding: 0 12px; border: 0; border-left: 1px solid var(--line-strong); background: transparent;
  font: inherit; font-size: var(--fs-label); color: var(--ink-2); cursor: pointer; white-space: nowrap;
}
.dr-seg button:first-child { border-left: 0; }
.dr-seg button:hover { background: var(--raised); }
.dr-seg button.is-on[data-o="replaced"], .dr-seg button.is-on[data-o="repaired"] { background: var(--ok-bg); color: var(--ok); font-weight: 600; }
.dr-seg button.is-on[data-o="credited"] { background: var(--info-bg); color: var(--info); font-weight: 600; }
.dr-seg button.is-on[data-o="reject"]   { background: var(--danger-bg); color: var(--danger); font-weight: 600; }
.dr-detail__in { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* One line a row: a settled row's detail is a fact, not a paragraph. */
.dr-tbl td:nth-child(3) { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dr-tbl td.dr-detail { white-space: normal; overflow: visible; }
.dr-detail .input, .dr-detail .select { height: 32px; }
.dr-imei { max-width: 180px; }
.dr-q { display: inline-flex; align-items: center; gap: 4px; }
.dr-q .input { width: 64px; }
.dr-more td { background: var(--raised); padding-top: 8px; padding-bottom: 8px; }
.dr-more .linkbtn { color: var(--muted); font-weight: 500; }

.dr-side { display: grid; gap: var(--gap-card); position: sticky; top: calc(var(--bar) + 16px); }
.dr-card { padding: 16px 20px; }
.dr-card > .cap { display: block; margin-bottom: 8px; }
.dr-rr { display: flex; justify-content: space-between; gap: 12px; padding: 6px 0; font-size: var(--fs-body); font-variant-numeric: tabular-nums; color: var(--ink-2); }
.dr-rr > span:first-child { color: var(--muted); }
.dr-rr--big { margin-top: 8px; padding-top: 12px; border-top: 1px solid var(--line); font-size: 16px; font-weight: 600; color: var(--ink); }
.dr-rr--big > span:first-child { color: var(--ink); }
.dr-rr.is-neg, .dr-rr.is-neg > span:first-child { color: var(--danger); }
.dr-rr.faint, .dr-rr.faint > span:first-child { color: var(--faint); }
.dr-cn { margin-top: 12px; }
.dr-dn { display: flex; align-items: center; gap: 8px; color: var(--info); }
.dr-dn .mono { color: var(--ink); font-weight: 600; }
.dr-dn .dr-tag { margin-left: auto; }
.dr-dn__sub { margin: 4px 0 8px; font-size: var(--fs-small); }
.dr-act { list-style: none; margin: 8px 0 0; padding: 0; display: grid; gap: 6px; font-size: var(--fs-label); color: var(--ink-2); }
.dr-fine { font-size: var(--fs-small); color: var(--muted); margin: 12px 0 0; }
.dr-wide { width: 100%; justify-content: center; margin-top: 16px; }

.drs { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: var(--gap-card); align-items: start; }
.drs__main, .drs__side { padding: var(--pad-card); }
.drs__side { position: sticky; top: calc(var(--bar) + 16px); }
.drs__row { display: grid; grid-template-columns: 280px minmax(0, 1fr); gap: var(--gap-field); align-items: start; }
.drs__why { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.drs__why .chip, .drs__fchip { font: inherit; font-size: var(--fs-small); cursor: pointer; }
.drs__faulty { margin-top: var(--gap-field); padding: 12px; border: 1px dashed var(--warn-line); border-radius: var(--r-sm); background: color-mix(in srgb, var(--warn-bg) 50%, var(--card)); }
.drs__chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.drs__fchip { display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; border-radius: 999px; border: 1px solid var(--line-strong); background: var(--card); color: var(--ink); }
.drs .dr-tbl { margin-top: var(--gap-field); border-top: 1px solid var(--line); }
.drs .dr-tbl td, .drs .dr-tbl th { padding-left: 8px; padding-right: 8px; }
.drs .dr-qty { width: 72px; margin-left: auto; }

@media (max-width: 1100px) {
  .dr-body, .drs { grid-template-columns: 1fr; }
  .dr-side, .drs__side { position: static; }
}
@media (max-width: 720px) {
  .dr-head { flex-wrap: wrap; }
  .dr-head__acts { width: 100%; }
  .dr-head__acts .btn { flex: 1; justify-content: center; }
  .dr-steps { flex-direction: column; align-items: flex-start; gap: 8px; }
  .dr-steps__ln { display: none; }
  .dr-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dr-kpis > div:nth-child(3) { border-left: 0; }
  .dr-kpis > div:nth-child(n+3) { border-top: 1px solid var(--line); }
  .dr-tbl colgroup { display: none; }
  .dr-tbl thead { display: none; }
  .dr-tbl, .dr-tbl tbody, .dr-tbl tr, .dr-tbl td { display: block; width: 100%; }
  .dr-tbl tr { border-bottom: 1px solid var(--line); padding: 8px 0; }
  .dr-tbl td { border: 0; padding: 4px 16px; }
  .dr-tbl .t-num { text-align: left; }
  .dr-seg { grid-template-columns: repeat(2, 1fr); width: 100%; }
  .dr-seg button { border-top: 1px solid var(--line-strong); }
  .drs__row { grid-template-columns: 1fr; }
}
/* Dealer notes (dealer-note.php) and the tab on dealer-returns.php. */
.dr-tabs { display: flex; gap: 4px; padding: 8px 12px 0; border-bottom: 1px solid var(--line); }
.dr-tabs__t {
  padding: 8px 12px; margin-bottom: -1px; border-bottom: 2px solid transparent;
  font-size: var(--fs-body); font-weight: 600; color: var(--muted); text-decoration: none;
}
.dr-tabs__t:hover { color: var(--ink); text-decoration: none; }
.dr-tabs__t.is-on { color: var(--ink); border-bottom-color: var(--ink); }

.dn-wrap { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: var(--gap-card); align-items: start; max-width: 1080px; }
.dn-main { padding: 20px; }
.dn-side { padding: 20px; position: sticky; top: calc(var(--bar) + 16px); }
.dn-side > .cap { display: block; margin-bottom: 8px; }
.dn-kind { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.dn-kind__o {
  display: flex; align-items: center; gap: 12px; padding: 12px 16px; text-align: left;
  border: 1px solid var(--line-strong); border-radius: var(--r); background: var(--card);
  font: inherit; color: var(--ink); cursor: pointer;
}
.dn-kind__o b { display: block; font-size: var(--fs-body); }
.dn-kind__o small { display: block; font-size: var(--fs-small); color: var(--muted); }
.dn-kind__o.is-on { border: 2px solid var(--ink); padding: 11px 15px; }
.dn-kind__ic {
  width: 32px; height: 32px; border-radius: 999px; display: grid; place-items: center; flex: none;
  background: var(--ok-bg); color: var(--ok); transform: rotate(90deg);
}
.dn-kind__ic.is-up { background: var(--warn-bg); color: var(--warn); transform: rotate(-90deg); }
.dn-grid { margin-top: var(--gap-field); }
.dn-field { margin-top: var(--gap-field); }
.dn-bill { display: flex; align-items: center; background: var(--raised); }
.dn-why { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.dn-why .chip { font: inherit; font-size: var(--fs-label); cursor: pointer; }
.dn-item { margin: 8px 0 0; font-size: var(--fs-small); color: var(--muted); }
.dn-bal { margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--line); }
.dn-bal__v { display: flex; align-items: center; gap: 8px; margin-top: 4px; font-size: 16px; font-variant-numeric: tabular-nums; }
.dn-eff { display: grid; gap: 4px; margin-top: 12px; font-size: var(--fs-label); color: var(--muted); }
@media (max-width: 900px) {
  .dn-wrap { grid-template-columns: 1fr; }
  .dn-side { position: static; }
}
@media (max-width: 640px) { .dn-kind { grid-template-columns: 1fr; } }

/* ==========================================================================
   Reports (reports.php)
   Five numbers in one strip, the chart of whichever was pressed, then the
   detail in tabs - the shape of the reports in Shopify, Stripe and
   Lightspeed. Colours are the app's tokens, so every chart follows dark
   mode; sizes are the app's tokens and the 4-step scale.
   ========================================================================== */
.rp [hidden] { display: none !important; }
.rp-head { margin-bottom: var(--gap-card); align-items: center; }
.rp-when { font-variant-numeric: tabular-nums; }

/* the tools: branch, period, export */
.rp-tools { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.rp-branch { display: flex; align-items: center; gap: 8px; color: var(--muted); }
.rp-branch .select { width: auto; min-width: 160px; }
.rp-seg {
  display: inline-flex; align-items: center; gap: 2px; height: var(--h-control); padding: 4px;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--raised);
}
.rp-seg__i {
  display: inline-flex; align-items: center; gap: 4px; height: 100%; padding: 0 12px; border: 0;
  border-radius: 6px; background: none; color: var(--muted); font: 500 var(--fs-label) var(--font);
  white-space: nowrap; cursor: pointer; text-decoration: none; list-style: none;
}
.rp-seg__i::-webkit-details-marker { display: none; }
.rp-seg__i:hover { color: var(--ink); text-decoration: none; }
.rp-seg__i.is-on { background: var(--card); color: var(--ink); box-shadow: var(--lift-1); }
.rp-seg--sm { height: 32px; }
.rp-pop { position: relative; height: 100%; }
.rp-pop > summary { list-style: none; }
.rp-pop > summary::-webkit-details-marker { display: none; }
.rp-pop__box, .rp-menu {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 30; min-width: 220px;
  padding: var(--pad-card-y); border: 1px solid var(--line); border-radius: var(--r);
  background: var(--card); box-shadow: var(--lift-3);
}
.rp-pop__box { display: grid; gap: var(--pad-cell-y); width: 240px; }
.rp-pop__box .field { margin: 0; }
.rp-menu { padding: 4px; min-width: 180px; }
.rp-menu a, .rp-menu button {
  display: flex; align-items: center; gap: 8px; width: 100%; height: var(--h-control); padding: 0 12px;
  border: 0; border-radius: 6px; background: none; color: var(--ink-2); font: 500 var(--fs-label) var(--font);
  text-align: left; cursor: pointer; text-decoration: none;
}
.rp-menu a:hover, .rp-menu button:hover { background: var(--raised); text-decoration: none; }

/* the tabs */
.rp-tabs { display: flex; gap: 4px; margin: 0 0 var(--gap-card); border-bottom: 1px solid var(--line); overflow-x: auto; scrollbar-width: none; }
.rp-tabs::-webkit-scrollbar { display: none; }
.rp-tabs__i {
  padding: 8px 12px 12px; margin-bottom: -1px; border-bottom: 2px solid transparent;
  color: var(--muted); font-weight: 500; white-space: nowrap; text-decoration: none;
}
.rp-tabs__i:hover { color: var(--ink); text-decoration: none; }
.rp-tabs__i.is-on { color: var(--ink); font-weight: 600; border-bottom-color: var(--violet); }

/* the strip of five */
.rp-kpis { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); overflow: hidden; margin-bottom: var(--gap-card); }
.rp-kpi {
  position: relative; display: flex; flex-direction: column; gap: 4px; min-width: 0;
  padding: var(--pad-card) 20px; border: 0; border-right: 1px solid var(--line);
  background: none; color: inherit; font: inherit; text-align: left; text-decoration: none; cursor: pointer;
  transition: background .15s;
}
.rp-kpi:last-child { border-right: 0; }
.rp-kpi:hover { background: var(--raised); text-decoration: none; }
.rp-kpi.is-on { background: linear-gradient(180deg, var(--violet-soft), transparent 80%); }
.rp-kpi.is-on::before { content: ""; position: absolute; inset: 0 0 auto; height: 3px; background: var(--violet); }
.rp-kpi:focus-visible { outline: 2px solid var(--violet); outline-offset: -2px; }
.rp-kpi__label { font-size: var(--fs-label); color: var(--muted); }
.rp-kpi__value {
  font-family: var(--font-head); font-size: 24px; font-weight: 600; letter-spacing: -.01em; line-height: 1.25;
  color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-variant-numeric: tabular-nums;
}
.rp-kpi__value.is-warn { color: var(--warn); }
.rp-kpi__foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 24px; }
.rp-kpi__sub { font-size: var(--fs-small); color: var(--muted); }
.rp-spark { width: 72px; height: 24px; flex: none; background: none !important; }
.rp-spark polyline { fill: none; stroke: currentColor; stroke-width: 1.6; vector-effect: non-scaling-stroke; stroke-linejoin: round; }

/* pills */
.rp-pill {
  display: inline-flex; align-items: center; gap: 2px; padding: 2px 8px; border-radius: 999px;
  font-size: var(--fs-small); font-weight: 600; white-space: nowrap; font-variant-numeric: tabular-nums;
}
.rp-pill svg { width: 12px; height: 12px; }
.rp-pill--up    { background: var(--ok-bg);     color: var(--ok); }
.rp-pill--down  { background: var(--danger-bg); color: var(--danger); }
.rp-pill--warn  { background: var(--warn-bg);   color: var(--warn); }
.rp-pill--calm  { background: var(--calm-bg);   color: var(--calm); }

/* the big chart */
.rp-hero { margin-bottom: var(--gap-card); }
.rp-hero__top { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--gap-card); flex-wrap: wrap; padding: 20px 20px 0; }
.rp-hero__label { font-size: var(--fs-label); color: var(--muted); }
.rp-hero__value {
  margin-top: 4px; font-family: var(--font-head); font-size: 28px; font-weight: 600; letter-spacing: -.015em;
  line-height: 1.2; font-variant-numeric: tabular-nums;
}
.rp-hero__vs { margin-left: 8px; font: 500 var(--fs-label) var(--font); color: var(--muted); letter-spacing: 0; vertical-align: 4px; }
.rp-hero__side { display: flex; align-items: center; gap: var(--gap-card); flex-wrap: wrap; }
.rp-legend { display: flex; gap: var(--gap-card); font-size: var(--fs-small); color: var(--muted); flex-wrap: wrap; }
.rp-key { display: inline-block; width: 12px; height: 0; margin-right: 8px; vertical-align: middle; }
.rp-key--now { border-top: 3px solid var(--violet); border-radius: 2px; }
.rp-key--was { border-top: 2px dashed var(--faint); }
.rp-trend { position: relative; margin: 8px 20px 12px; min-height: 200px; }
.rp-trend svg { display: block; width: 100%; overflow: visible; }
.rp-ch__grid { stroke: var(--line); stroke-width: 1; stroke-dasharray: 2 4; }
.rp-ch__grid.is-zero { stroke: var(--line-strong); stroke-dasharray: none; }
.rp-ch__ax { font: 11px var(--font); fill: var(--faint); }
.rp-ch__stop { stop-color: var(--violet); stop-opacity: .22; }
.rp-ch__stop.is-end { stop-opacity: 0; }
.rp-ch__area { fill: url(#rpFill); }
.rp-ch__now { fill: none; stroke: var(--violet); stroke-width: 2.2; stroke-linejoin: round; stroke-linecap: round; }
.rp-ch__was { fill: none; stroke: var(--faint); stroke-width: 1.5; stroke-dasharray: 4 3; }
.rp-ch__cross { stroke: var(--ink); stroke-opacity: .25; }
.rp-ch__dot { fill: var(--card); stroke: var(--violet); stroke-width: 2.2; }
.rp-ch__hit { fill: transparent; cursor: crosshair; }
.rp-tip {
  position: absolute; z-index: 3; pointer-events: none; opacity: 0; transition: opacity .1s;
  padding: 8px 12px; border-radius: var(--r-sm); background: var(--ink); color: var(--card);
  font-size: var(--fs-small); white-space: nowrap; box-shadow: var(--lift-3); font-variant-numeric: tabular-nums;
}
.rp-tip.is-on { opacity: 1; }
.rp-tip b { display: block; margin-bottom: 4px; font: 600 var(--fs-label) var(--font-head); }
.rp-tip div { display: flex; justify-content: space-between; gap: 12px; }
.rp-tip span { opacity: .7; }
.rp-tip em { font-style: normal; font-weight: 600; }
.rp-tip .is-up { color: #6ee7a8; } .rp-tip .is-down { color: #fb8686; }
html[data-theme="dark"] .rp-tip .is-up { color: var(--ok-bg); }
html[data-theme="dark"] .rp-tip .is-down { color: var(--danger-bg); }

/* grids of cards */
.rp-grid { display: grid; gap: var(--gap-card); margin-bottom: var(--gap-card); }
.rp-grid--3   { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.rp-grid--2   { grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); }
.rp-grid--2-1 { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
.rp-block { margin-bottom: var(--gap-card); }
.rp-card { min-width: 0; }
.rp-card__h { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: var(--pad-card) 20px 0; }
.rp-card__h h2 { margin: 0; font-size: var(--fs-card-h); }
.rp-card__b { padding: var(--pad-card) 20px 20px; }
.rp-card > .table-wrap { margin-top: var(--pad-cell-y); }
.rp-meta { font-size: var(--fs-small); color: var(--muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
a.rp-meta--go { color: var(--brand); }
.rp-sub { display: flex; align-items: center; gap: 8px; margin: 12px 0 0; font-size: var(--fs-small); color: var(--muted); }

/* the three facts */
.rp-fact { display: flex; align-items: flex-start; gap: 12px; padding: var(--pad-card); color: inherit; text-decoration: none; }
a.rp-fact:hover { border-color: var(--line-strong); text-decoration: none; }
.rp-fact__i { display: grid; place-items: center; flex: none; width: 32px; height: 32px; border-radius: var(--r-sm); }
.rp-fact__l { display: block; font-size: var(--fs-small); color: var(--muted); }
.rp-fact__v { display: block; margin-top: 2px; font-family: var(--font-head); font-weight: 600; font-variant-numeric: tabular-nums; }
.rp-fact__v em { font: 500 var(--fs-label) var(--font); font-style: normal; color: var(--muted); }
.rp-soft--ok    { background: var(--ok-bg);    color: var(--ok); }
.rp-soft--warn  { background: var(--warn-bg);  color: var(--warn); }
.rp-soft--brand { background: var(--brand-bg); color: var(--brand); }

/* tables inside cards */
.rp-t { width: 100%; border-collapse: collapse; font-size: var(--fs-label); font-variant-numeric: tabular-nums; }
.rp-t th { padding: 0 0 8px; border-bottom: 1px solid var(--line); color: var(--muted); font-weight: 500; font-size: var(--fs-small); text-align: left; }
.rp-t td { padding: 8px 0; border-bottom: 1px solid var(--line); vertical-align: middle; }
.rp-t tr:last-child td { border-bottom: 0; }
.rp-t td + td, .rp-t th + th { padding-left: 12px; }
.rp-t tfoot td { border-top: 1px solid var(--line-strong); border-bottom: 0; font-weight: 600; }
.rp-t .is-bad, .rp .is-bad { color: var(--danger); }
.rp .is-ok { color: var(--ok); }
.rp-bars + .rp-bars { margin-top: var(--gap-card); }
.rp-bars__l { width: 36%; }
.rp-who { display: flex; align-items: center; gap: 12px; }
.rp-who__main { flex: 1; min-width: 0; }
.rp-who__main > a, .rp-who__main > span:first-child { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; color: var(--ink); }
.rp-who__main > a:hover { color: var(--violet); }
.rp-rank {
  display: inline-grid; place-items: center; flex: none; width: 24px; height: 24px; border-radius: 6px;
  background: var(--raised); color: var(--muted); font-size: 11px; font-weight: 600;
}
.rp-track { display: block; height: 4px; margin-top: 4px; border-radius: 999px; background: var(--raised); overflow: hidden; }
.rp-track > span { display: block; height: 100%; border-radius: 999px; background: var(--violet); opacity: .75; }
.rp-track--fat { height: 8px; margin: 0; }
.rp-track--fat > span { opacity: 1; }

/* tones, for dots, stacks, donut, sparks */
.rp-tone--brand { color: var(--brand); background: var(--brand); stroke: var(--brand); }
.rp-tone--ok    { color: var(--ok);    background: var(--ok);    stroke: var(--ok); }
.rp-tone--bad   { color: var(--danger); background: var(--danger); stroke: var(--danger); }
.rp-tone--warn  { color: var(--warn);  background: var(--warn);  stroke: var(--warn); }
.rp-tone--info  { color: var(--info);  background: var(--info);  stroke: var(--info); }
.rp-tone--calm  { color: var(--calm);  background: var(--calm);  stroke: var(--calm); }
.rp-stack { display: flex; height: 12px; gap: 2px; margin-bottom: var(--gap-card); border-radius: 999px; overflow: hidden; background: var(--raised); }
.rp-stack > span { display: block; height: 100%; min-width: 4px; }
.rp-dot { display: inline-block; width: 8px; height: 8px; margin-right: 8px; border-radius: 3px; vertical-align: middle; }

/* payments */
.rp-pay { display: flex; align-items: center; gap: 20px; }
.rp-pay__ring { position: relative; flex: none; }
.rp-pay__ring > b {
  position: absolute; inset: 0; display: grid; place-content: center; text-align: center;
  font-family: var(--font-head); font-size: var(--fs-body); line-height: 1.2;
}
.rp-pay__ring small { display: block; font: 500 11px var(--font); color: var(--muted); }
.rp-donut { display: block; width: 128px; height: 128px; }
.rp-donut__hole { fill: none; stroke: var(--raised); stroke-width: 5; }
.rp-donut__seg { fill: none; stroke-width: 5; background: none !important; }

/* repair desk */
.rp-funnel { display: flex; flex-direction: column; gap: 12px; }
.rp-funnel__row { display: grid; grid-template-columns: 96px minmax(0, 1fr) 40px; align-items: center; gap: 12px; font-size: var(--fs-label); }
.rp-funnel__row b { text-align: right; font-variant-numeric: tabular-nums; }
.rp-funnel__track { height: 24px; border-radius: 6px; background: var(--raised); overflow: hidden; }
.rp-funnel__fill { display: block; height: 100%; border-radius: 6px; background: var(--brand); }
.rp-step-1 { opacity: .8; } .rp-step-2 { opacity: .62; } .rp-step-3 { opacity: .45; }
.rp-minis { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin-top: var(--gap-card); }
.rp-minis > div { display: flex; flex-direction: column; gap: 2px; padding: 8px 12px; border-radius: var(--r-sm); background: var(--raised); }
.rp-minis span { font-size: var(--fs-small); color: var(--muted); }
.rp-minis b { font-family: var(--font-head); font-size: 16px; font-variant-numeric: tabular-nums; }
.rp-minis .is-warn { color: var(--warn); }

/* stock age */
.rp-age { display: flex; flex-direction: column; gap: 12px; }
.rp-age__row { display: grid; grid-template-columns: 80px minmax(0, 1fr) auto; align-items: center; gap: 12px; font-size: var(--fs-label); }
.rp-age__label { color: var(--muted); }
.rp-age__track { height: 8px; border-radius: 999px; background: var(--raised); overflow: hidden; }
.rp-age__fill { display: block; height: 100%; border-radius: 999px; }
.rp-age--ok { background: var(--ok); } .rp-age--warn { background: var(--warn); opacity: .7; }
.rp-age--bad { background: var(--danger); opacity: .7; } .rp-age--worst { background: var(--danger); }
.rp-age__n { font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--ink-2); }
.rp-insight { display: flex; align-items: center; gap: 8px; margin: var(--gap-card) 0 0; padding: 8px 12px; border-radius: var(--r-sm); font-size: var(--fs-label); }
.rp-insight svg { flex: none; }
.rp-insight a { margin-left: auto; font-weight: 600; color: inherit; }
.rp-insight--warn { background: var(--warn-bg); color: var(--warn); }

/* busy hours */
.rp-heat { display: grid; grid-template-columns: 32px repeat(13, minmax(0, 1fr)); gap: 4px; font-size: 11px; color: var(--faint); }
.rp-heat__h { white-space: nowrap; overflow: visible; }
.rp-heat__d { color: var(--muted); line-height: 16px; }
.rp-heat__c { display: block; height: 16px; border-radius: 4px; background: var(--raised); }
.rp-heat--0 { background: var(--raised); }
.rp-heat--1 { background: var(--brand-line); }
.rp-heat--2 { background: var(--brand); opacity: .45; }
.rp-heat--3 { background: var(--brand); opacity: .72; }
.rp-heat--4 { background: var(--brand); }
.rp-scale { display: flex; align-items: center; justify-content: flex-end; gap: 4px; margin-top: 12px; font-size: 11px; color: var(--faint); }
.rp-scale i { width: 12px; height: 12px; border-radius: 4px; }

/* bars that open a list: categories, areas */
.rp-areas { display: flex; flex-direction: column; gap: 12px; }
.rp-area { display: block; color: inherit; text-decoration: none; }
.rp-area:hover { text-decoration: none; }
.rp-area__top { display: flex; justify-content: space-between; gap: 8px; margin-bottom: 4px; font-size: var(--fs-label); }
.rp-area__n { color: var(--muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.rp-area__track { display: block; height: 6px; border-radius: 999px; background: var(--raised); overflow: hidden; }
.rp-area__track > span { display: block; height: 100%; border-radius: 999px; background: var(--brand); }
.rp-area:hover .rp-area__top > span:first-child { color: var(--brand); }

@media (max-width: 1100px) {
  .rp-kpis { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .rp-kpi:nth-child(3) { border-right: 0; }
  .rp-kpi:nth-child(-n+3) { border-bottom: 1px solid var(--line); }
  .rp-grid--2-1 { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 640px) {
  .rp-tools { width: 100%; position: relative; }
  .rp-branch { flex: 1 1 0; } .rp-branch .select { flex: 1; min-width: 0; }
  .rp-tools > .rp-seg { order: 3; flex: 1 1 100%; overflow-x: auto; scrollbar-width: none; }
  .rp-tools > .rp-seg .rp-seg__i { flex: 1 0 auto; justify-content: center; }
  .rp-seg .rp-pop { position: static; }
  .rp-pop__box { left: 0; right: 0; width: auto; }
  .rp-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .rp-kpi { padding: 12px var(--pad-card); border-bottom: 1px solid var(--line); }
  .rp-kpi:nth-child(odd) { border-right: 1px solid var(--line); }
  .rp-kpi:nth-child(even) { border-right: 0; }
  .rp-kpi:last-child { grid-column: 1 / -1; border-right: 0; border-bottom: 0; }
  .rp-kpi__value { font-size: 20px; }
  .rp-spark { display: none; }
  .rp-hero__top { padding: var(--pad-card) var(--pad-card) 0; }
  .rp-hero__value { font-size: 24px; }
  .rp-trend { margin: 8px 8px 12px; }
  .rp-grid--2, .rp-grid--3 { grid-template-columns: minmax(0, 1fr); }
  .rp-card__h, .rp-card__b { padding-left: var(--pad-card); padding-right: var(--pad-card); }
  .rp-pay { flex-direction: column; align-items: stretch; }
  .rp-pay__ring { align-self: center; }
  .rp-hide-sm { display: none; }
}
@media print {
  .rp [data-tabs][hidden] { display: block !important; }
  .rp .rp-grid[data-tabs][hidden] { display: grid !important; }
  .rp .rp-kpis[hidden] { display: grid !important; }
  .rp-kpi.is-on { background: none; }
  .rp-card, .rp-kpis, .rp-hero, .rp-fact { break-inside: avoid; }
}
/* ---- Reports > Stock: by dealer and brand ---------------------------------
   Two pickers - a dealer, a brand - and everything under them follows. */
.rp .is-warn { color: var(--warn); }
.rp-sk__bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: var(--gap-card); }
.rp-pick { position: relative; }
.rp-pick__btn {
  display: flex; align-items: center; gap: 8px; height: var(--h-control); min-width: 240px; max-width: 320px; padding: var(--pad-control);
  border: 1px solid var(--line-strong); border-radius: var(--r-sm); background: var(--card); color: var(--ink);
  font-size: var(--fs-label); font-weight: 500; cursor: pointer; list-style: none;
}
.rp-pick__btn::-webkit-details-marker { display: none; }
.rp-pick__btn > svg:last-child { margin-left: auto; color: var(--faint); flex: none; }
.rp-pick__txt { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rp-pick__txt small { color: var(--muted); font-size: var(--fs-label); }
.rp-pick.is-set .rp-pick__btn { border-color: var(--violet); background: var(--violet-soft); color: var(--violet); }
.rp-pick__clear {
  display: grid; place-items: center; width: 32px; height: 32px; margin-left: -4px; border-radius: var(--r-sm);
  color: var(--muted); text-decoration: none;
}
.rp-pick__clear:hover { background: var(--raised); color: var(--danger); text-decoration: none; }
.rp-pick__pop {
  position: absolute; left: 0; top: calc(100% + 8px); z-index: 30; width: 320px; max-width: calc(100vw - 32px);
  border: 1px solid var(--line); border-radius: var(--r); background: var(--card); box-shadow: var(--lift-3); overflow: hidden;
}
.rp-pick__find { display: flex; align-items: center; gap: 8px; height: 44px; padding: 0 12px; border-bottom: 1px solid var(--line); color: var(--faint); }
.rp-pick__find input { flex: 1; min-width: 0; border: 0; outline: 0; background: none; color: var(--ink); font: var(--fs-body) var(--font); }
.rp-pick__list { max-height: 320px; overflow: auto; padding: 4px; }
.rp-pick__opt {
  display: flex; align-items: center; gap: 12px; padding: 8px 12px; border-radius: var(--r-sm);
  color: var(--ink-2); font-size: var(--fs-label); font-weight: 500; text-decoration: none;
}
.rp-pick__opt:hover, .rp-pick__opt.is-on { background: var(--violet-soft); color: var(--violet); text-decoration: none; }
.rp-pick__opt em { margin-left: auto; font-style: normal; color: var(--muted); font-variant-numeric: tabular-nums; }
.rp-pick__opt[hidden] { display: none; }
.rp-sk__kinds { display: flex; gap: 8px; flex-wrap: wrap; margin-left: 8px; }
.rp-sk__kinds .chip small { color: var(--faint); font-weight: 500; }
.rp-sk__crumb { margin: 0 0 12px; color: var(--muted); font-size: var(--fs-label); }
.rp-sk__crumb b { color: var(--ink-2); }
.rp-sk__crumb a { font-weight: 500; }

.rp-av {
  display: inline-grid; place-items: center; flex: none; width: 24px; height: 24px; border-radius: 6px;
  color: #fff; font: 600 11px var(--font-head);
}
.rp-pick__btn .rp-av { width: 20px; height: 20px; font-size: 9px; border-radius: 4px; }
.rp-av--lg { width: 48px; height: 48px; border-radius: var(--r); font-size: var(--fs-body); }
/* The chart palette: eight hues that stay apart in both themes. */
.rp-c0 { background: #6d28d9; } .rp-c1 { background: #0d9488; } .rp-c2 { background: #2563eb; } .rp-c3 { background: #d97706; }
.rp-c4 { background: #db2777; } .rp-c5 { background: #16a34a; } .rp-c6 { background: #ea580c; } .rp-c7 { background: #4b5563; }
.rp-c-rest { background: var(--line-strong); }

.rp-focus { display: flex; align-items: center; gap: var(--gap-card); flex-wrap: wrap; padding: var(--pad-card) 20px; margin-bottom: var(--gap-card); }
.rp-focus__who { min-width: 0; }
.rp-focus__who h2 { margin: 0; font-size: 18px; }
.rp-focus__who p { margin: 4px 0 0; color: var(--muted); font-size: var(--fs-label); }
.rp-focus__side { display: flex; align-items: center; gap: var(--gap-card); margin-left: auto; flex-wrap: wrap; }
.rp-focus__due { text-align: right; }
.rp-focus__due span { display: block; font-size: var(--fs-small); color: var(--muted); }
.rp-focus__due b { font-family: var(--font-head); font-size: 18px; font-variant-numeric: tabular-nums; }
.rp-focus__acts { display: flex; gap: 8px; flex-wrap: wrap; }

.rp-kpis--flat .rp-skpi { display: flex; flex-direction: column; gap: 4px; min-width: 0; padding: var(--pad-card) 20px; border-right: 1px solid var(--line); }
.rp-kpis--flat .rp-skpi:last-child { border-right: 0; }
.rp-stack--tall { height: 16px; }
.rp-sk__legend { display: flex; flex-wrap: wrap; gap: 8px 20px; margin-top: 12px; font-size: var(--fs-label); color: var(--ink-2); }
.rp-sk__legend span { display: inline-flex; align-items: center; }
.rp-sk__legend b { margin: 0 4px; font-weight: 600; }
.rp-sk__legend em { font-style: normal; color: var(--faint); }
.rp-sk__t tr.is-link { cursor: pointer; }
.rp-sk__t tr.is-link:hover td { background: var(--raised); }
.rp-sk__t tr.is-link td:last-child svg { color: var(--faint); }
.rp-sk__t tr.is-link:hover td:last-child svg { color: var(--violet); }
.rp-sk__name { display: flex; align-items: center; gap: 12px; color: inherit; text-decoration: none; min-width: 200px; }
.rp-sk__name:hover { text-decoration: none; }
.rp-sk__name .rp-av { width: 32px; height: 32px; border-radius: var(--r-sm); font-size: var(--fs-small); }
.rp-sk__name b { display: block; font-weight: 600; }
.rp-sk__name small { display: block; color: var(--faint); font-size: var(--fs-small); }
.rp-sk__share { display: inline-flex; align-items: center; gap: 8px; }
.rp-sk__share .rp-track { width: 72px; margin: 0; height: 6px; }
.rp-imei { font-family: var(--font-mono); font-size: var(--fs-small); color: var(--muted); }
@media (max-width: 1100px) {
  .rp-kpis--flat .rp-skpi:nth-child(3) { border-right: 0; }
  .rp-kpis--flat .rp-skpi:nth-child(-n+3) { border-bottom: 1px solid var(--line); }
}
@media (max-width: 640px) {
  .rp-pick { flex: 1 1 100%; display: flex; }
  .rp-pick__btn { flex: 1; min-width: 0; max-width: none; }
  .rp-pick.is-set { flex: 1 1 0; }
  .rp-sk__kinds { margin-left: 0; }
  .rp-focus { padding: var(--pad-card); }
  .rp-focus__side { margin-left: 0; width: 100%; justify-content: space-between; }
  .rp-focus__due { text-align: left; }
  .rp-kpis--flat .rp-skpi { padding: 12px var(--pad-card); border-bottom: 1px solid var(--line); }
  .rp-kpis--flat .rp-skpi:nth-child(odd) { border-right: 1px solid var(--line); }
  .rp-kpis--flat .rp-skpi:nth-child(even) { border-right: 0; }
  .rp-kpis--flat .rp-skpi:last-child { grid-column: 1 / -1; border-right: 0; border-bottom: 0; }
  .rp-sk__name { min-width: 0; }
  .rp-sk__name small { display: none; }
}

/* ---- Settings > HSN codes ------------------------------------------------ */
.hsn-strip { display: flex; align-items: center; justify-content: space-between; gap: var(--gap-field); flex-wrap: wrap; margin-bottom: var(--gap-card); }
.hsn-done { margin-bottom: var(--gap-card); }
.hsn-tabs { margin: 0 0 var(--gap-card); }
.hsn-count { color: var(--muted); font-weight: 400; }
.hsn-code { font-family: var(--font-mono); font-size: var(--fs-body); letter-spacing: .02em; white-space: nowrap; }
/* Adding and editing are the same four boxes: the name widest, the code
   next, the slab and the button as wide as they need. On a phone the name
   and the button take a line each. */
.hsn-form {
  display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1.4fr) 96px auto;
  gap: 8px; align-items: center;
}
.hsn-form--add { padding: var(--pad-card-y) var(--pad-card); border-top: 1px solid var(--line); background: var(--surface); border-radius: 0 0 var(--r-lg) var(--r-lg); }
.hsn-editing td { background: var(--surface); }
.hsn-err { margin: 0; padding: 0 var(--pad-card) var(--pad-card-y); }
@media (max-width: 640px) {
  .hsn-table { min-width: 0; }
  .hsn-table .t-menu { padding-left: 0; }
  .hsn-hide { display: none; }
  .hsn-form { grid-template-columns: minmax(0, 1fr) 96px; }
  .hsn-form > :first-child, .hsn-form > .btn { grid-column: 1 / -1; }
}
/* The shop's list, hanging under the HSN box on Add product. Under the box,
   not fixed like a bill line's: no table scrolls sideways around this one. */
.hsn-pick { position: absolute; top: 100%; left: 0; right: 0; margin-top: 4px; max-height: 280px; }
.hsn-pick__opt { display: flex; align-items: center; justify-content: space-between; gap: var(--gap-field); width: 100%; text-align: left; }
.hsn-pick__opt.is-on, .hsn-pick__opt:hover { background: var(--violet-soft); }
.hsn-pick__opt.is-on strong { color: var(--violet); }
.hsn-pick__right { display: inline-flex; align-items: center; gap: 8px; flex: none; }
.hsn-pick__foot { display: block; border-top: 1px solid var(--line); padding: var(--pad-cell-y) 11px; font-size: var(--fs-small); color: var(--violet); }
/* Sales by category - two columns of bars on a wide screen, one on a phone. */
.rp-cats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px calc(var(--gap-card) * 2); }
.rp-cat-tag {
  display: inline-block; margin-left: 4px; padding: 0 6px; border-radius: var(--r-sm);
  font-size: var(--fs-small); font-weight: 600; line-height: 18px;
  background: var(--violet-soft); color: var(--violet);
}
.rp-cat-tag--part { background: var(--calm-bg); color: var(--calm); }
.rp-cat-neg { color: var(--danger); }
@media (max-width: 640px) {
  .rp-cats { grid-template-columns: minmax(0, 1fr); }
}
