﻿/* ============================================================
   Custom WGL screens — the quotation builder, and the POS and
   product screens that will sit on the same WglPage shell.

   RULE (from tokens.css): never name a color literal here. Every
   value below resolves through --wgl-*, or through the --bs-* and
   --s-* that the theme files map onto them, so light and dark both
   follow without a second set of rules.

   Never let a star be followed by a slash inside a comment. Writing
   a wildcard such as "--bs-" plus star, then a slash, then "--s-",
   closes the comment right there. That exact typo lived on the line
   above and silently deleted the `.wgl-builder` rule that follows
   this block: CSS recovers from the garbage by discarding tokens up
   to the next open brace, so the whole next rule goes with it, and
   nothing reports an error. The builder's panels lost their gap and
   sat welded together. Name the two prefixes separately, as above.

   The page frame, title bar and toolbar are NOT here: WglPage
   reuses Serenity's own s-DataGrid / grid-title / grid-toolbar
   markup so the theme already styles them.
   ============================================================ */

/* ── The page body ──────────────────────────────────────────
   WglPage's own div, inside Serenity's `.s-DataGrid` card. It had no rule at
   all, so whatever a screen ended with — the Products pager, the Supplier
   directory pager, the expense list — sat welded to the card's bottom hairline
   with nowhere to rest.

   THE FOOT IS SINGLE-SOURCED HERE. `.wgl-builder`, `.wgl-pos`, `.wgl-crm` and
   `.wgl-dash` used to pad themselves top and bottom, which gave four screens a
   foot and the other four none, and left a new screen with nothing to inherit.
   They now pad the top only; every screen's foot is this one value.

   That value is now ZERO. The foot was written for a card whose bottom hairline
   sits just under the pager — but this div overflows its `.s-DataGrid` card on
   every list screen, so there is no hairline down there to rest against. All the
   padding ever did was park two row-gaps of blank page under the pager, which
   is the last thing you see when you scroll a 97-page catalogue to the end. Keep
   the rule rather than deleting it: it is the one place a foot would go back, and
   a screen adding its own bottom padding is the regression this block prevents. */
.wgl-page-content {
  padding-bottom: 0;
}

/* No max-width. This rule carried `max-width: 1180px` from the day it was written
   and, because of the comment bug above, was never once applied — so the builder has
   only ever been seen full width. Restoring the cap made it worse, not better: the
   page frame around it (the black title bar and the toolbar) is full-bleed, so a
   1180px column left 400px of dead space beside a header band that ran past it, and
   the header's field grid dropped from one row to two with "Box renter" orphaned on
   the second. The other page-root containers — .wgl-pos, .wgl-crm, .wgl-dash,
   .wgl-products — are all full width, so this now agrees with them and with its own
   frame. Measure is capped on the one element here that is actually prose,
   `.wgl-terms-full`, which is where it belongs. */
.wgl-builder {
  display: flex;
  flex-direction: column;
  gap: var(--s-row-gap);
  padding-top: var(--s-row-gap);
}

/* The `.is-new` reorder that lived here is gone — see the note in
   QuotationBuilderPage.apply(). It pushed the archive below the document whenever
   nothing was loaded, which was a workaround for the header, the items and the terms
   being loose siblings of the list. They are one `.wgl-doc` container now, so the
   order is plain DOM order and the case it compensated for — a blank quotation drawn
   under the list on arrival — does not arise. */

/* ── Panel — the card every section of a custom screen sits in ── */

/* The horizontal padding is a variable because full-bleed children have to
   cancel it exactly. Hard-coding the same number in two places is how you get
   a 2px white sliver down each side of the filter band: legacy-shapes.css
   repaints this panel at 22px, and a band bleeding by a literal 1.25rem (20px)
   misses the edge by 2. Anything that bleeds reads the value from here. */
.wgl-panel {
  --wgl-panel-pad-x: 1.25rem;
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-card-border-radius);
  box-shadow: var(--s-shadow-xs);
  padding: 1.15rem var(--wgl-panel-pad-x);
}

.wgl-panel + .wgl-panel { margin-top: 0; }

/* A panel with nothing in it is not a panel. The screens build their panels up
   front and fill them from a response, and any that never gets filled — the
   dashboard's weekly report waits for a toolbar click, and is made visible before
   it has anything in it — was left on the page as an empty outlined box with 22px
   of padding around no content. Drawing the frame is a promise that something is
   there. `wglSkeleton()` covers the panels that ARE loading; this covers the rest. */
.wgl-panel:empty { display: none; }

/* ── Loading placeholder ────────────────────────────────────
   See WglSkeleton.tsx. Bars rather than a spinner: they reserve the height the
   content is about to take, so the panel does not jump when it arrives. */

.wgl-skeleton {
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
}

.wgl-skeleton-bar {
  position: relative;
  overflow: hidden;
  height: 0.85rem;
  border-radius: var(--bs-border-radius-sm);
  background: var(--bs-tertiary-bg);
}

/* The title bar stands in for the panel heading, so it is heavier and shorter than
   the body lines and carries the same bottom margin the real heading does. */
.wgl-skeleton-bar.is-title {
  width: 38%;
  height: 1.1rem;
  margin-bottom: 0.35rem;
  background: var(--bs-secondary-bg);
}

.wgl-skeleton-bar::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, var(--bs-secondary-bg), transparent);
  animation: wgl-shimmer 1.4s ease-in-out infinite;
}

@keyframes wgl-shimmer {
  to { transform: translateX(100%); }
}

/* The bars still reserve their space and still read as "not content yet" without
   the sweep, which is the whole job. */
@media (prefers-reduced-motion: reduce) {
  .wgl-skeleton-bar::after { animation: none; }
}

/* ── Header ────────────────────────────────────────────────── */

.wgl-header-top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-col-gap);
  margin-bottom: 1.1rem;
}

.wgl-header-id {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  min-width: 0;
}

.wgl-h-number {
  font-size: 1.35rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--bs-body-color);
}

.wgl-h-meta {
  color: var(--bs-secondary-color);
  font-variant-numeric: tabular-nums;
}

/* ── Status pill ───────────────────────────────────────────────
   Draft is deliberately the quiet one: most quotations on screen
   are drafts, and a loud pill on every one of them says nothing.
   Only the states that ended the quotation carry colour.         */

.wgl-status-pill {
  display: inline-flex;
  align-items: center;
  padding: 0.2rem 0.7rem;
  border-radius: var(--bs-border-radius-pill);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  border: 1px solid transparent;
  white-space: nowrap;
}

.wgl-status-pill.st-draft {
  background: var(--bs-tertiary-bg);
  color: var(--bs-secondary-color);
  border-color: var(--bs-border-color);
}

/* Submitted is the one state that is waiting on a person, so it is
   the one that gets the brand fill. Yellow is a FILL and always
   carries black text — see the contrast invariants in tokens.css. */
.wgl-status-pill.st-submitted {
  background: var(--wgl-yellow);
  color: var(--wgl-black);
}

.wgl-status-pill.st-approved {
  background: rgba(var(--wgl-green-rgb), 0.14);
  color: var(--s-success-text, var(--wgl-green));
  border-color: rgba(var(--wgl-green-rgb), 0.35);
}

.wgl-status-pill.st-rejected,
.wgl-status-pill.st-cancelled {
  background: rgba(var(--wgl-red-rgb), 0.12);
  color: var(--s-danger-text, var(--wgl-red));
  border-color: rgba(var(--wgl-red-rgb), 0.3);
}

/* The two readability twins from tokens.css — --wgl-green and --wgl-red both
   fall below 4.5:1 as text on the dark surface — now arrive through
   --s-success-text / --s-danger-text, which the theme files map. The
   `:root.theme-cosmos-dark` pair that used to sit here did the same job at a
   specificity (0,3,0) that no scoped rule could get past, so legacy-shapes.css
   had to carry a counter-override to stop it recolouring the pills it gives a
   solid fill to. Both are gone; the token is the single place this is decided. */

/* ── Status actions ────────────────────────────────────────── */

.wgl-status-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.wgl-btn {
  appearance: none;
  border: 1px solid var(--bs-border-color);
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
  border-radius: var(--bs-border-radius-sm);
  padding: 0.4rem 0.9rem;
  font-size: 0.9rem;
  font-weight: 500;
  line-height: 1.4;
  cursor: pointer;
  transition: background var(--s-transition), border-color var(--s-transition);
}

.wgl-btn:hover { background: var(--bs-tertiary-bg); }
.wgl-btn:focus-visible { outline: none; box-shadow: var(--s-focus-ring); }
.wgl-btn:disabled { opacity: 0.55; cursor: not-allowed; }

.wgl-btn-primary {
  background: var(--wgl-yellow);
  border-color: var(--wgl-yellow);
  color: var(--wgl-black);
}
.wgl-btn-primary:hover {
  background: var(--wgl-yellow-hi);
  border-color: var(--wgl-yellow-hi);
}
.wgl-btn-primary:active {
  background: var(--wgl-yellow-lo);
  border-color: var(--wgl-yellow-lo);
}

.wgl-btn-ghost { background: transparent; }
.wgl-btn-ghost:hover { background: var(--bs-tertiary-bg); }

/* Destructive actions read as text until hovered. They sit next to
   the ordinary ones and should not compete with them for the eye. */
.wgl-btn-danger {
  background: transparent;
  border-color: transparent;
  color: var(--s-danger-text, var(--wgl-red));
}
.wgl-btn-danger:hover {
  background: rgba(var(--wgl-red-rgb), 0.1);
  border-color: rgba(var(--wgl-red-rgb), 0.3);
}

/* The one destructive action in the app that cannot be undone. It is filled where
   .wgl-btn-danger is text, because "Delete" and "Erase forever" sit side by side on
   a tombstone row and one of them is reversible. */
.wgl-btn-danger-solid {
  background: var(--wgl-red);
  border-color: var(--wgl-red);
  color: var(--wgl-white);
}
.wgl-btn-danger-solid:hover {
  background: var(--wgl-red-dk);
  border-color: var(--wgl-red-dk);
  color: var(--wgl-white);
}

/* A row action, next to text rather than in a toolbar. */
.wgl-btn-sm {
  padding: 0.2rem 0.55rem;
  font-size: 0.8rem;
}

/* ── Panel head ────────────────────────────────────────────── */

.wgl-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-col-gap);
  margin-bottom: 1rem;
}

/* THE COUNT CHIP BELONGS TO THE TITLE IT COUNTS.
 
   `space-between` is right for a head of title-and-actions and wrong the moment a
   `.wgl-tagno` sits between them: the chip is a qualifier on the heading — "Products
   2,932", "Weekly report 18–24 Aug" — and the default flung it to the far edge of the
   panel, a hand's width from the word it modifies, where it reads as an unrelated
   badge.
 
   Switched only when the head actually holds that pair, so every other head keeps
   `space-between`. Anything that still belongs on the right already carries its own
   `margin-inline-start: auto` (`.wgl-panel-actions`, `.wgl-dash-head-control`) and
   keeps working — which is why this is a `justify-content` change and NOT an auto
   margin on the chip: two autos split the free space between them and leave the
   actions floating mid-panel. Same trap the note above `.wgl-panel-note` describes.
 
   Six heads had it: Products, Suppliers, Expenses, Studio images and two on Reports.
   The Dashboard's "Key numbers" had already been fixed one-off with `.wgl-dash-head`,
   which this makes redundant but harmless. */
.wgl-panel-head:has(> .wgl-panel-title + .wgl-tagno) {
  justify-content: flex-start;
  gap: 0.6rem;
  flex-wrap: wrap;
}

.wgl-panel-title {
  font-size: 1.05rem;
  font-weight: 600;
  margin: 0;
  color: var(--bs-body-color);
}

/* ── Add item ──────────────────────────────────────────────────
   The three fields that make a line real. Everything else about
   an item is edited on its card once it exists.                 */

.wgl-add-item {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--s-col-gap);
  padding: 0.85rem;
  margin-bottom: 1rem;
  border: 1px dashed var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  background: var(--bs-tertiary-bg);
}

.wgl-add-desc  { flex: 3 1 240px; }
.wgl-add-qty   { flex: 0 0 5.5rem; }
.wgl-add-price { flex: 0 0 9rem; }

.wgl-add-item input.editor {
  width: 100%;
  padding: var(--s-input-padding-v) 0.65rem;
  line-height: var(--s-input-line-height);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius-sm);
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
}

.wgl-add-item input.editor:focus {
  outline: none;
  border-color: var(--s-wgl-accent-fg);
  box-shadow: var(--s-focus-ring);
}

/* ── Items ─────────────────────────────────────────────────── */

.wgl-items {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}

.wgl-item {
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  background: var(--bs-body-bg);
  overflow: hidden;
}

.wgl-item-head {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.55rem 0.85rem;
  background: var(--bs-tertiary-bg);
  border-bottom: 1px solid var(--bs-border-color);
}

.wgl-item-index {
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--bs-secondary-color);
}

.wgl-item-tags {
  display: flex;
  gap: 0.35rem;
  flex-wrap: wrap;
  flex: 1;
  min-width: 0;
}

.wgl-tag {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 0.12rem 0.45rem;
  border-radius: var(--bs-border-radius-pill);
}

/* Store credit changes when the supplier is paid, so it earns the brand fill.
   Yellow is a fill and carries black text — tokens.css contrast invariants. */
.wgl-tag-credit { background: var(--wgl-yellow); color: var(--wgl-black); }

.wgl-tag-quiet {
  background: var(--bs-secondary-bg);
  color: var(--bs-secondary-color);
}

/* `.wgl-tag-returned` is styled in legacy-shapes.css beside its sibling chips, not
   here: the base `.s-WglPage .wgl-tag` there sets the brand yellow at a specificity
   this file cannot beat. See the note on it. */

/* Emptied when the card's actions moved out of the corner — Print label went inline
   beside the barcode, Enhance onto the photograph, and the rest to the foot. Kept as an
   element rather than deleted: it is where a future card-level action would go, and an
   empty flex box costs nothing.

   THE HEAD STILL HAS TO BE TALL ENOUGH FOR THE TAB. `.wgl-item-index` is absolutely
   positioned at `top: 12px` and stands 26px tall, so it needs 38px of card above the
   body. Six 1.9rem icon buttons used to supply that by accident; with the row empty the
   head collapsed to its text height and the tab spilled over the AI suggestion strip
   below it, hiding "Read from the photo" behind the black tab. Found by driving it in a
   browser — nothing else would have. */
.wgl-item-head-actions { display: flex; gap: 0.2rem; }

/* 2.75rem = 44px. The tab is fixed-size (an 11px face plus 4px of padding) and its
   bottom lands 39px down the card, so this clears it by 5. Not `auto` with a spacer
   inside the head: the tab is out of flow, so nothing in the head can measure it. */
.wgl-item-head { min-height: 2.75rem; }

.wgl-icon-btn {
  appearance: none;
  border: none;
  background: transparent;
  color: var(--bs-secondary-color);
  width: 1.9rem;
  height: 1.9rem;
  border-radius: var(--bs-border-radius-sm);
  cursor: pointer;
  transition: background var(--s-transition), color var(--s-transition);
}

.wgl-icon-btn:hover {
  background: var(--bs-secondary-bg);
  color: var(--bs-body-color);
}

.wgl-icon-btn:focus-visible { outline: none; box-shadow: var(--s-focus-ring); }

/* Picture on the left, form on the right — the legacy's
   `grid-template-columns: 150px 1fr`. It had been a single stacked column, so a
   220px photograph sat on top of the first field on every card and a forty-line
   consignment was forty photo-height scrolls before any two descriptions could be
   compared. Collapses to one column on a narrow screen, as the legacy's does at
   640px. */
/* What the item list is doing with the edits. There is no Save button on a card —
   every field posts as it is typed — and an autosave nobody can see reads exactly
   like a screen that is dropping the work. Quiet by default, because it is true
   almost all the time; it only raises its voice when a save has actually failed. */
.wgl-save-state {
  /* No auto margin. `.wgl-panel-actions` carries `margin-inline-start: auto` and
     that single auto is what pushes the buttons right; a second one here would split
     the free space between the two and leave this floating mid-panel — which is the
     spread the actions group was introduced to stop. This sits where it belongs, next
     to the title it captions. */
  font-size: 0.78rem;
  color: var(--bs-secondary-color);
  white-space: nowrap;
}

.wgl-save-state.is-saving { opacity: 0.7; }

.wgl-save-state.is-failed {
  color: var(--s-danger-text);
  font-weight: 600;
}

/* Picture lane, then everything that is not the picture.

   The lane was 13.75rem for a preview that drew at 128px and a thumbnail strip that
   drew at 180px, so ~40px of it held nothing at any time. It is now 11rem: the
   preview fills it (see `.wgl-item-photo .file-item` in legacy-shapes.css), three
   3rem tiles fill it, and the 44px it gives up goes to the form — which is where
   CONDITION was clipping "Used — very good condition — 50%" mid-word.

   Not narrower than this, and the limit is not the picture: the editor's own toolbar
   is a SEGMENTED pair — Select File beside the delete button — and `.tool-group` is
   `flex-wrap: wrap`, so the moment the pair does not fit, the delete button drops to
   a second line and the segment reads as two loose controls. 11rem clears it with the
   tightened padding below. */
.wgl-item-body {
  display: grid;
  grid-template-columns: 11rem minmax(0, 1fr);
  gap: 1.1rem;
  padding: 0.9rem 0.85rem;
}

@media (max-width: 45rem) {
  .wgl-item-body { grid-template-columns: minmax(0, 1fr); }
}

/* The legacy's `.pcol`: main shot, thumbnail strip, "+ Photos", in that order. */
.wgl-item-photo-col {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  min-width: 0;
}

/* The cropper editor brings its own s-ImageUploadEditor styling; this only keeps
   it inside its lane. */
.wgl-item-photo { max-width: 100%; }

/* The rows of the form, stacked. The row classes below handle the columns. */
.wgl-item-fields {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  min-width: 0;
}

/* Explicit column ratios, not `auto-fit`.

   `.wgl-field-grid` is `repeat(auto-fit, minmax(210px, 1fr))`, which is right for a
   panel of unrelated fields and wrong for this card: it fits as many 210px columns as
   the width allows and wraps the rest, so the legacy's opening line — description,
   category, condition, qty, price, the five things somebody says while holding the
   garment — came out as two-and-a-bit rows with the price under the description. The
   ratios below are the legacy's own `.frow`, which sizes each field to what it holds
   rather than to a uniform minimum: description widest, qty barely there, and
   condition wide enough for "Very good — 50%" without the select clipping it. */
/* Scoped through the parent, and that is not decoration: `.wgl-field-grid` carries
   the auto-fit template at one class of specificity and is declared LATER in this
   file, so a bare `.wgl-item-row1` ties and loses. Nothing reports a rule that is
   merely outranked — the row simply kept drawing three equal columns. */
/* Condition takes the slack the narrower picture lane freed, and a little from
   Description. It is the longest string in the row and the only one that cannot be
   read past — "Used — very good condition — 50%" was clipping to "Used — very good
   condi" in a select the operator has to choose from. Qty gives up 0.05fr: it holds
   one or two digits and is disabled outright the moment variants exist. */
.wgl-item-fields > .wgl-item-row1 { grid-template-columns: 1.75fr 1fr 1.55fr 0.5fr 1fr; }
/* Four columns holding ONE field, and that is the legacy's `.frow4` exactly — it kept
   `repeat(4,1fr)` when `5ff6f37` moved size, colour and material onto the variants and
   left Brand behind. The three empty columns are not a bug to tidy: Brand holds "Zara",
   and stretching it across the card would make the shortest value on the item the
   widest box on it. */
.wgl-item-fields > .wgl-item-row2 { grid-template-columns: repeat(4, 1fr); }
.wgl-item-fields > .wgl-item-row3 { grid-template-columns: 1.2fr 1fr 1fr; }

/* The legacy collapses its rows to two columns at 760px rather than letting the
   ratios squeeze five fields into a phone. */
@media (max-width: 62rem) {
  .wgl-item-fields > .wgl-item-row1,
  .wgl-item-fields > .wgl-item-row2,
  .wgl-item-fields > .wgl-item-row3 { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 36rem) {
  .wgl-item-fields > .wgl-item-row1,
  .wgl-item-fields > .wgl-item-row2,
  .wgl-item-fields > .wgl-item-row3 { grid-template-columns: 1fr; }
}

.wgl-span-2 { grid-column: span 2; }

.wgl-item-notes { margin-top: 0.8rem; }

.wgl-item-notes textarea.editor {
  width: 100%;
  padding: var(--s-input-padding-v) 0.65rem;
  line-height: var(--s-input-line-height);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius-sm);
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
  resize: vertical;
}

.wgl-item-notes textarea.editor:focus {
  outline: none;
  border-color: var(--s-wgl-accent-fg);
  box-shadow: var(--s-focus-ring);
}

.wgl-item-flags {
  display: flex;
  flex-wrap: wrap;
  gap: 1.1rem;
  margin-top: 0.85rem;
}

.wgl-check {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  cursor: pointer;
  font-size: 0.9rem;
}

/* A tick that changes who owns the garment, with the sentence saying so — the
   legacy's `.credchk`, which put the consequence beside the box because neither of
   these is reversible from this card. Boxed and aligned to the top so the sentence
   wraps under itself rather than under the tick. */
.wgl-check-explained {
  align-items: flex-start;
  flex: 1 1 18rem;
  gap: 0.55rem;
  padding: 0.5rem 0.65rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius-sm);
  background: var(--bs-body-bg);
}

/* ONE PER LINE inside a field grid, which is the legacy's `.credchk.full`.
   `.wgl-field-grid` is `repeat(auto-fit, minmax(210px, 1fr))`, so without this an
   explained tick is just another 210px cell: it lands in whatever column happens to
   be free — beside a textarea, or sharing a row with the next tick — and a sentence
   that needs the width gets a third of it. These are the flags that decide whether a
   supplier is paid; they are not fields to be tetris'd into a spare column.

   `1 / -1` rather than a span count, because the column count is what the width
   decides and neither this rule nor anything else knows it. */
.wgl-field-grid > .wgl-check-explained { grid-column: 1 / -1; }

/* Same intent one level out, for a tick that belongs to the SECTION rather than to
   the field grid inside it — the product editor's Validated, which is not a
   settlement decision and sits below the note about the two that are.
   `.wgl-editor-section` is a plain block, and `.wgl-check` is an inline-flex label,
   so without this the box shrinks to its own text and reads as a stray chip rather
   than the third row of a stack. */
.wgl-editor-section > .wgl-check-explained {
  display: flex;

  /* Wider than the gap inside the grid above, and that gap is the point: the hint
     between them ("these decide what the supplier is owed") describes the two ticks
     ABOVE it and not this one. Spaced evenly, a reader attaches the sentence to
     whichever box it happens to sit nearest. */
  margin-top: 1.1rem;
}

/* A tick the supplier has already signed for. The box still reads — what it says is
   part of the record — but nothing about it invites a press.

   `:has()` and not a class the screen adds, because `disabled` is set on the INPUT by
   whoever owns the rule (the product editor disables these once the quotation is
   decided) and a second, parallel signal on the label is a second thing to forget. */
.wgl-check-explained:has(input:disabled) {
  opacity: 0.55;
  cursor: default;
  background: var(--s-wgl-tint-bg-2, var(--bs-body-bg));
}

.wgl-check-explained:has(input:disabled) input[type="checkbox"] { cursor: default; }

/* `flex: none` is the whole fix, and it is not cosmetic.
   `.wgl-check` is an inline-flex label, so the box is a flex item with the default
   `flex-shrink: 1`. Beside a two-line sentence competing for the same width, the
   browser squeezed it to a couple of pixels — a tall thin sliver nobody could tell
   was a checkbox, let alone whether it was ticked. The sized rule that already
   exists for these is scoped to `.wgl-field input[type="checkbox"]`, and an
   explained tick is a bare `<label class="wgl-check">` with no field around it, so
   none of it applied. legacy-shapes now extends that selector to `.wgl-check`
   rather than declaring a second one; this is the same guarantee for any host
   outside a WGL page. */
.wgl-check input[type="checkbox"] {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
}

/* Aligned to the first line of a two-line label, not to the middle of the block. */
.wgl-check-explained input[type="checkbox"] { margin-block-start: 2px; }

.wgl-check-explained > span {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}

.wgl-check-hint,
.wgl-field-hint {
  font-size: 0.78rem;
  line-height: 1.45;
  color: var(--bs-secondary-color);
}

/* Sits under the control inside its `.wgl-field`, which is already a column. */
.wgl-field-hint { margin-top: 0.1rem; }

/* ── Derived figures ───────────────────────────────────────────
   These arrive computed from the server. Tabular numerals so a
   column of them lines up on the decimal point.                 */

.wgl-item-foot {
  display: flex;
  flex-wrap: wrap;
  gap: 1.4rem;
  padding: 0.6rem 0.85rem;
  border-top: 1px solid var(--bs-border-color);
  background: var(--bs-tertiary-bg);
}

.wgl-item-derived {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  font-size: 0.88rem;
}

.wgl-item-derived-label {
  color: var(--bs-secondary-color);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* The supplier's figure, marked — the legacy draws a yellow highlighter stroke
   under it (`linear-gradient(transparent 55%, yellow 55%)`), because on a bar
   holding two similar numbers the one the conversation is about should be the one
   the eye lands on. The gradient rather than a fill: it reads as a marker over the
   figure, and it leaves the digits on the bar's own ground so the contrast is the
   body colour's, not black-on-yellow. */
.wgl-item-derived-you .wgl-money {
  background: linear-gradient(transparent 55%, var(--wgl-yellow) 55%);
  padding: 0 3px;
  font-weight: 600;
}

/* A tick whose meaning has lapsed — Keep off Shopify while the piece is Website
   only. Greyed rather than removed: the box still shows what was recorded, and a
   control that vanishes on a neighbouring choice is one nobody finds again. */
.wgl-check-explained.is-muted {
  opacity: 0.5;
}

.wgl-money {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--bs-body-color);
}

/* ── Empty state ───────────────────────────────────────────── */

.wgl-empty {
  padding: 2rem 1rem;
  text-align: center;
}

.wgl-empty-title {
  margin: 0 0 0.25rem;
  font-weight: 600;
  color: var(--bs-body-color);
}

.wgl-empty-body {
  margin: 0;
  color: var(--bs-secondary-color);
}

/* A load that failed, not a thing that is genuinely empty. Same block, but the
   heading takes the danger colour so the two are not read as the same news — "no
   expenses this period" and "the expenses could not be fetched" mean opposite
   things about whether to trust the screen. */
.wgl-empty-failed .wgl-empty-title { color: var(--s-danger-text, var(--wgl-red)); }
.wgl-empty-failed .wgl-empty-body { margin-top: 0.75rem; }

/* ── Totals bar ────────────────────────────────────────────────
   Sticky at the foot: the item list grows past the fold during a
   real intake, and these are the numbers being discussed.        */

.wgl-totals {
  position: sticky;
  bottom: 0;
  z-index: 3;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.6rem;
  padding: 0.7rem 1.1rem;
  margin-top: var(--s-row-gap);
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-card-border-radius);
  box-shadow: var(--s-shadow-md); 
}

.wgl-total {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;
}

.wgl-total-label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--bs-secondary-color);
  white-space: nowrap;
}

.wgl-total-value {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--bs-body-color);
}

.wgl-total-strong { font-size: 1.15rem; }

/* ── Terms ─────────────────────────────────────────────────── */

/* Two jobs under one class, and they want opposite alignment.

   As a <span> in a `.wgl-panel-head` it is the right-hand caption of a
   space-between row — the date range on the expense report, the "frozen when
   this was raised" line beside Signed terms. There, end-alignment and a tight
   measure are the whole point: it has to stay out of the title's way.

   As a standalone <p> under the head it is a sentence introducing the panel —
   the dashboard's markdown-ladder note, the rental commission line. End-aligned
   and capped at 42ch, that rendered as a 339px block ragged-left inside a 784px
   card, which read as the card itself being narrower than the one beside it.
   Sentences start at the start. */
.wgl-panel-note {
  font-size: 0.8rem;
  color: var(--bs-secondary-color);
  max-width: 60ch;
}

.wgl-panel-head .wgl-panel-note {
  max-width: 42ch;
  text-align: end;
}

.wgl-terms-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 0.8rem var(--s-col-gap);
}

.wgl-terms-item {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}

.wgl-terms-value {
  color: var(--bs-body-color);
  font-size: 0.9rem;
}

.wgl-terms-details { margin-top: 1.1rem; }

.wgl-terms-details > summary {
  cursor: pointer;
  font-size: 0.88rem;
  color: var(--bs-secondary-color);
  padding: 0.35rem 0;
}

/* A quotation with no terms text on it would otherwise paint an empty tinted strip
   under the summary — a box that says a document exists where none does. */
.wgl-terms-full:empty { display: none; }

.wgl-terms-full b { color: var(--bs-body-color); }

/* `.terms h3` — the section titles. */
.wgl-terms-full h3 {
  font-size: 12px;
  color: var(--bs-body-color);
  margin: 12px 0 4px;
  letter-spacing: 0.02em;
  font-weight: 700;
}

/* The document's own title line, above the first section. */
.wgl-terms-full h4 {
  font-size: 12px;
  color: var(--bs-body-color);
  margin: 0 0 6px;
  font-weight: 700;
}

.wgl-terms-full ol { margin: 2px 0 2px 18px; padding: 0; }
.wgl-terms-full li { margin-bottom: 2px; }

.wgl-terms-empty { margin: 0; color: var(--bs-secondary-color); }

/* The legacy's `.terms` card: 11px, 1.7 line height, grey body with black headings
   — small, dense and readable as a contract rather than as page copy. No scroller
   and no measure cap: the legacy prints the whole agreement into the page, and a
   22rem box with its own scrollbar turns eight clauses somebody is meant to READ
   before signing into something to be skipped past. */
.wgl-terms-full {
  margin: 0.6rem 0 0;
  font-size: 11px;
  line-height: 1.7;
  color: var(--bs-secondary-color);
}

/* ── Approval ──────────────────────────────────────────────── */

.wgl-appr-status {
  font-size: 0.82rem;
  font-weight: 600;
  padding: 0.2rem 0.65rem;
  border-radius: var(--bs-border-radius-pill);
  white-space: nowrap;
}

/* It was `--bs-tertiary-bg` behind `--bs-secondary-color` — grey lettering on a grey
   chip, in the top-right corner of a tall panel. This is the line that tells the
   operator why the button will be refused, so it gets a border and body-weight ink
   rather than reading as a disabled label. */
.wgl-appr-waiting {
  background: var(--bs-tertiary-bg);
  border: 1px solid var(--bs-border-color);
  color: var(--bs-body-color);
}

/* Everything is collected and the button will go through. Amber rather than green:
   green is what Approved wears once it has happened, and two greens one click apart
   would say the thing was already done. */
.wgl-appr-ready {
  background: rgba(var(--wgl-yellow-rgb, 255, 212, 0), 0.18);
  border: 1px solid rgba(var(--wgl-yellow-rgb, 255, 212, 0), 0.55);
  color: var(--bs-body-color);
}

.wgl-appr-done {
  background: rgba(var(--wgl-green-rgb), 0.14);
  color: var(--s-success-text, var(--wgl-green));
}


/* Three columns, in the order the paperwork is done: who is signing, the ID they
   produced, and the signature itself. The last two have a SHAPE and so get fixed
   tracks — an ID card is a card and a signature pad is a box on a form; stretched
   across a share of a wide screen they stop looking like either. Everything flexible
   belongs to the fields, which are the only things here that can use the room. */
.wgl-appr-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 17rem 20rem;
  gap: 18px;
  align-items: start;
  margin-top: 1.1rem;
}

/* The pad drops under the paperwork first, then the ID card. */
@media (max-width: 78rem) {
  .wgl-appr-row { grid-template-columns: minmax(0, 1fr) 17rem; }
  .wgl-appr-right { grid-column: 1 / -1; max-width: 20rem; }
}

@media (max-width: 45rem) {
  .wgl-appr-row { grid-template-columns: minmax(0, 1fr); }
  .wgl-appr-right { grid-column: auto; }
}

.wgl-appr-right {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  min-width: 0;
}

/* Two across, in two rows.

   The class is doubled up deliberately: `.wgl-field-grid`'s own
   `repeat(auto-fit, minmax(210px, 1fr))` is declared further down this file at the
   same specificity, so a single class here loses to it and all four fields ran across
   one line — leaving the column one row tall beside a 175px photo well. */
.wgl-field-grid.wgl-appr-who {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-content: start;
}

@media (max-width: 34rem) {
  .wgl-field-grid.wgl-appr-who { grid-template-columns: minmax(0, 1fr); }
}

.wgl-appr-idphoto { min-width: 0; }

/* A rule closing the panel, with the one action on it. */
.wgl-appr-foot {
  display: flex;
  justify-content: flex-end;
  margin-top: 1.25rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--bs-border-color);
}

/* `flex: none`, against `.wgl-check-explained`'s `1 1 18rem`. That basis is for the
   item card's three-across flags row, where the ticks share a line and should grow
   into it; here the tick is one full-width cell of a grid and growing means growing
   TALL — a 250px box around two lines of text. */
.wgl-appr-own {
  flex: none;
  align-items: flex-start;
  margin: 0;
}


/* ── Signature pad ─────────────────────────────────────────────
   color: is read by the canvas script for the ink, so the stroke
   follows the theme instead of being a hard-coded black that
   disappears on the dark surface.                                */

.wgl-sign { margin-top: 0.3rem; }

/* 150px and a 2px dashed edge — the legacy's `.sigpad`. It was 1px at 9.5rem,
   which on a 14px root is 133px: shorter than the legacy's box and drawn in a
   hairline, so it read as an empty input rather than as somewhere to sign. */
/* Paper white and near-black ink in BOTH themes, deliberately not `--bs-body-bg` /
   `--bs-body-color`.

   The canvas reads its own computed `color` for the stroke, so on the dark theme a
   supplier signed in near-WHITE — and the PNG that is kept has a transparent
   background. That signature is then drawn on the white quotation document, on the
   supplier statement and in the record below, where white ink on white paper is
   nothing at all. The pad is a box on a form; making it look like one costs nothing
   and makes the stored evidence printable whatever theme the tablet was on. */
.wgl-sign-pad {
  position: relative;
  height: 150px;
  border: 2px dashed var(--bs-border-color);
  border-radius: 12px;
  background: var(--wgl-white);
  color: var(--wgl-black);
  overflow: hidden;
}

.wgl-sign-canvas {
  display: block;
  width: 100%;
  height: 100%;
  cursor: crosshair;
}

/* Centred in the pad, not parked along its bottom edge: it is the instruction to
   sign, and the legacy sets it across the middle of the box in tracked capitals. */
.wgl-sign-hint {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
  pointer-events: none;
}

.wgl-sign-inked .wgl-sign-hint,
.wgl-sign-signed .wgl-sign-hint { display: none; }

.wgl-sign-inked .wgl-sign-pad,
.wgl-sign-signed .wgl-sign-pad { border-style: solid; }

/* The signature on file, painted into the pad.

   This used to be a `::after` reading "A signature is already on file for this
   quotation" across an empty box — a description of the evidence standing in for the
   evidence, and the operator's report was exactly that: signed it, cannot see it.

   `pointer-events: none` so a replacement stroke reaches the canvas underneath, and
   `contain` so a wide signature is never cropped: a clipped signature is a different
   signature. */
.wgl-sign-img {
  position: absolute;
  inset: 6px;
  width: calc(100% - 12px);
  height: calc(100% - 12px);
  object-fit: contain;
  pointer-events: none;
  display: none;
}

.wgl-sign-signed .wgl-sign-img { display: block; }

.wgl-sign-readonly .wgl-sign-canvas { cursor: default; }
.wgl-sign-readonly .wgl-sign-actions { display: none; }

/* -- The ID photo well ------------------------------------------
   `.wgl-id-photo` had NO rules at all, so the cropper editor arrived wearing the
   framework's own `.s-ImageUploadEditor` chrome and stretched the full width of the
   left-hand column: a box the width of the panel holding a 128px thumbnail, two
   buttons and a great deal of nothing. It is one small landscape photograph of an ID
   card, so it gets a well the size of one — the same dashed recess the item card's
   photo lane uses, at the shape the crop profile actually produces (1600x1000). */
.wgl-id-photo {
  max-width: 100%;
  padding: 0.5rem 0.6rem 0.35rem;
  border: 2px dashed var(--bs-border-color);
  border-radius: 12px;
  background: var(--bs-tertiary-bg);
  transition: border-color 0.15s;
}

.wgl-id-photo:hover { border-color: var(--wgl-yellow); }

/* The editor brings its own 1px framed white card, which inside the well above drew
   a box inside a box — two frames around one small photograph. The well is the
   frame; the editor is what stands in it. */
.wgl-id-photo .s-ImageUploadEditor {
  border: 0;
  background: transparent;
  padding: 0;
}

/* Select File and the bin stay on one row — `.tool-group` wraps by default, and at
   this width the bin dropped under the button and read as an unrelated red icon.
   Same fix, and for the same reason, as `.wgl-item-photo` in legacy-shapes.css. */
.wgl-id-photo .s-Toolbar,
.wgl-id-photo .tool-group {
  display: flex;
  flex-wrap: nowrap;
  align-items: flex-start;
  column-gap: 0.35rem;
  margin-right: 0;
  min-width: 0;
}

.wgl-id-photo .tool-button {
  padding-left: 0.45rem;
  padding-right: 0.45rem;
  min-width: 0;
  font-size: 11px;
}

.wgl-id-photo .s-ImageUploadEditor ul {
  min-height: 0;
  padding: 0.4rem 0 0 0;
}

/* The whole card, centred in its own frame. The framework paints the thumbnail as a
   128x96 background with no `background-size`, which shows the top-left corner of a
   1600x1000 photograph — on an ID that is the laminate above the name. */
.wgl-id-photo .file-item { width: 100%; }

.wgl-id-photo .file-item .thumb {
  width: 100%;
  height: auto;
  aspect-ratio: 1.6;
  background-size: contain;
  background-position: center;
  background-repeat: no-repeat;
  border-radius: 8px;
}

/* The crop target is worth saying — this editor really does crop to it — but it is a
   footnote, not a caption. */
.wgl-id-photo .crop-dimensions {
  font-size: 10px;
  color: var(--bs-secondary-color);
}


/* -- The signed record ------------------------------------------
   What an approved quotation shows in place of its own form greyed out: four facts,
   the two photographs, and the sentence saying what was accepted. */

.wgl-signed {
  display: grid;
  gap: 1.1rem;
  margin-top: 1.1rem;
}

.wgl-signed-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
  gap: 0.85rem 1.4rem;
  padding: 0.9rem 1.1rem;
  background: var(--bs-tertiary-bg);
  border-radius: 12px;
}

.wgl-signed-fact {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}

.wgl-signed-value {
  font-weight: 600;
  line-height: 1.4;
  overflow-wrap: anywhere;
}

.wgl-signed-media {
  display: flex;
  flex-wrap: wrap;
  gap: 1.1rem;
}

.wgl-signed-shot {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  flex: 1 1 15rem;
  max-width: 22rem;
  min-width: 0;
}

/* `line-height: 0` on the anchor, or the inline box adds a descender strip under the
   picture that looks clickable and is not. */
.wgl-signed-shot a {
  display: block;
  line-height: 0;
  border-radius: 10px;
}

.wgl-signed-shot a:focus-visible {
  outline: 3px solid var(--wgl-yellow);
  outline-offset: 2px;
}

/* Paper, for the same reason the pad is: the stored ink is transparent-backed, and
   the point of showing it here is that somebody can hold it against the signature on
   the printed sheet. `contain`, never `cover` — a cropped signature is a different
   signature. */
.wgl-signed-shot img {
  display: block;
  width: 100%;
  height: 8.5rem;
  object-fit: contain;
  background: var(--wgl-white);
  border: 1px solid var(--bs-border-color);
  border-radius: 10px;
  padding: 0.35rem;
}

.wgl-signed-terms {
  margin: 0;
  font-size: 0.86rem;
  line-height: 1.6;
  color: var(--bs-secondary-color);
}

.wgl-signed-terms .fa {
  color: var(--s-success-text, var(--wgl-green));
  margin-right: 0.15rem;
}


/* The legacy's `.rlbl` — a tracked caption, not a sentence. */
.wgl-remote-label {
  font-size: 9.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
  font-weight: 700;
}

/* The legacy's `.sigtools`: caption left, Clear right. */
.wgl-sign-actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 8px;
}

.wgl-sign-line {
  font-size: 9px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
}

/* ── Saved quotations ──────────────────────────────────────── */

/* ── The filter band ────────────────────────────────────────
   STATUS and AGE used to be two flex children of one row, with the label
   stacked over its own strip. On a wide screen that put "90 days and over"
   past the middle of the page from the word AGE, and left both groups
   floating on the panel with nothing saying they belonged together.

   The band is one object: a recessed strip that bleeds to the panel's inner
   edges with a hairline top and bottom. Toolbar material — no radius, no
   shadow — so it groups the controls without nesting a card inside a card.
   The label becomes a column beside its own chips, not a caption above a
   row that runs off to the right. */
.wgl-filter-band {
  margin: 0 calc(-1 * var(--wgl-panel-pad-x)) 1.1rem;
  padding: 0.85rem var(--wgl-panel-pad-x) 0.95rem;
  background: var(--bs-tertiary-bg);
  border-top: 1px solid var(--bs-border-color);
  border-bottom: 1px solid var(--bs-border-color);
  display: grid;
  grid-template-columns: 4.6rem 1fr;
  gap: 0.55rem 0.85rem;
  align-items: center;
}

/* Search and the result count share the band's first row. */
.wgl-filter-lead {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  flex-wrap: wrap;
}

.wgl-filter-key {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
  align-self: center;
}

.wgl-filter-set {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  align-items: center;
  min-width: 0;
}

.wgl-filter-sep {
  width: 1px;
  height: 20px;
  flex: none;
  background: var(--bs-border-color);
  margin: 0 0.25rem;
}

/* The count belongs to the filters that produce it, not to the panel heading
   a screen-width away from them. It reads "12 of 49" once something is on,
   which is the number you were actually asking for. */
.wgl-filter-result {
  margin-inline-start: auto;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  font-size: 0.85rem;
  color: var(--bs-secondary-color);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.wgl-filter-result b {
  color: var(--bs-body-color);
  font-weight: 600;
}

/* Under 30 / 30-59 / 60-89 / 90+ are the markdown ladder's own steps: ordered,
   mutually exclusive, one point on a line. A connected track says that.
   Detached chips say "toggle any of these", which is not what they do.
   "Needs action" is the terms engine's answer rather than an age at all, so it
   stays outside the track. */
.wgl-filter-track {
  display: inline-flex;
  align-items: center;
  padding: 2px;
  border-radius: var(--bs-border-radius-pill);
  background: var(--bs-body-bg);
  box-shadow: inset 0 0 0 1px var(--bs-border-color);
}

.wgl-saved-filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-col-gap);
  align-items: flex-start;
  margin-bottom: 1rem;
}

.wgl-saved-search { flex: 1 1 260px; }
.wgl-filter-lead .wgl-saved-search { flex: 1 1 300px; max-width: 440px; }

.wgl-saved-search input.editor {
  width: 100%;
  padding: var(--s-input-padding-v) 0.65rem;
  line-height: var(--s-input-line-height);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius-sm);
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
}

.wgl-saved-search input.editor:focus {
  outline: none;
  border-color: var(--s-wgl-accent-fg);
  box-shadow: var(--s-focus-ring);
}

.wgl-chips { display: flex; flex-wrap: wrap; gap: 0.35rem; }

.wgl-chip {
  appearance: none;
  border: 1px solid var(--bs-border-color);
  background: var(--bs-body-bg);
  color: var(--bs-secondary-color);
  border-radius: var(--bs-border-radius-pill);
  padding: 0.28rem 0.8rem;
  font-size: 0.85rem;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--s-transition), color var(--s-transition);
}

.wgl-chip:hover { background: var(--bs-tertiary-bg); }
.wgl-chip:focus-visible { outline: none; box-shadow: var(--s-focus-ring); }

.wgl-chip-on {
  background: var(--wgl-yellow);
  border-color: var(--wgl-yellow);
  color: var(--wgl-black);
  font-weight: 600;
}

/* The gap here separates one quotation from the next, and it has to be clearly
   wider than the 0.25rem inside `.wgl-saved-row`, which separates a row from its
   OWN advice lines. At 0.35rem the two were 5.25px and 3.75px — near enough to
   identical that the list read as one undifferentiated block and an advice line
   looked equally attached to the row above it and the row below it. The supplier
   directory shares this container, where the same value welded 43 two-line cards
   into a single slab. */
.wgl-saved-list {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

/* A `.wgl-panel` is a plain block, so anything above the list — the search field on
   the till's Orders panel, the "25 supplier(s) · owed $0.00" summary on the supplier
   directory — sat directly on top of the first card with nothing between them. The
   list needs to separate from whatever precedes it by more than the cards separate
   from each other, or the search box reads as the first row of the results. */
.wgl-saved-list:not(:first-child) { margin-top: 0.9rem; }

/* Six columns of unlabelled data, and the last three are a bare date, a bare
   count and a bare figure sitting a long way from anything that says what they
   are. One hairline header row on the same grid anchors them.

   `aria-hidden`: the cards below are buttons, not table cells, so a screen
   reader gets each row read as one labelled action. A visual header repeated
   into that stream would be noise. */
/* Seven tracks, not six: the last is empty and exists only to reserve the same
   width the rows give their verbs, so the six headings stay over the six columns.
   Both sides read --wgl-actions-w, so they cannot be changed apart. */
.wgl-saved-head {
  display: grid;
  grid-template-columns: 7.5rem 6.5rem 1fr 6rem 5rem 6.5rem var(--wgl-actions-w);
  gap: 0.6rem;
  align-items: center;
  padding: 0 0.7rem 0.4rem;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
  border-bottom: 1px solid var(--bs-border-color);
}

.wgl-saved-head .is-end { text-align: end; }

@media (max-width: 860px) {
  .wgl-saved-head { display: none; }
}

/* A row per quotation. A button, not a div: it opens something, so it should be
   reachable by keyboard and announce itself as an action. */
.wgl-saved {
  display: grid;
  grid-template-columns: 7.5rem 6.5rem 1fr 6rem 5rem 6.5rem;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  text-align: start;
  appearance: none;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius-sm);
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
  padding: 0.5rem 0.7rem;
  font: inherit;
  cursor: pointer;
  transition: background var(--s-transition), border-color var(--s-transition);
}

.wgl-saved:hover {
  background: var(--bs-tertiary-bg);
  border-color: var(--s-wgl-accent-fg);
}

/* The row's trailing icon buttons, in the last grid cell rather than as siblings
   of it. .wgl-saved has six explicit columns; a seventh child wraps to a second
   row, which is what put the orders list's Return button under its order number.
   End-aligned so the icons hang off the row's trailing edge whatever the cell's
   width. */
.wgl-saved-tools {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.25rem;
}

.wgl-saved:focus-visible { outline: none; box-shadow: var(--s-focus-ring); }

.wgl-saved-no { font-weight: 600; font-variant-numeric: tabular-nums; }

/* Smaller inside a row than as a heading. A status pill here is a label on a line
   of data, not a thing being announced, and at the panel size it was the loudest
   object in a list where the quotation number should lead. Scoped to the row's own
   status cell so the pill keeps its size everywhere it IS the announcement — the
   builder header, the advice lines, the dashboard. */
.wgl-saved-status .wgl-status-pill {
  padding: 0.1rem 0.45rem;
  font-size: 0.7rem;
  letter-spacing: 0.02em;
}

/* The Status cell, which may hold one pill or two — see SavedQuotations.tsx for
   why they are never two grid children. Being a flex container is also what
   stops them stretching: a grid item is stretched to its cell by default, and
   `.wgl-status-pill` is blockified out of `inline-flex` the moment it becomes
   one, which is how a two-word Shopify pill came to be drawn 900px wide across
   the supplier column. Flex items take their content width instead. They wrap
   rather than widen the track, because the track is shared with the header row
   above and with three other screens. */
.wgl-saved-status {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0.25rem;
  min-width: 0;
}

.wgl-saved-who {
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.wgl-saved-phone,
.wgl-saved-date,
.wgl-saved-count {
  font-size: 0.82rem;
  color: var(--bs-secondary-color);
  font-variant-numeric: tabular-nums;
}

/* End-aligned so each sits under the header that names it, and so the figures
   form a column the eye can run down instead of a ragged left edge. */
.wgl-saved-date,
.wgl-saved-count,
.wgl-saved-total { text-align: end; }

.wgl-saved-total { font-weight: 600; }

.wgl-saved-age { display: block; }

@media (max-width: 860px) {
  .wgl-saved-date,
  .wgl-saved-count,
  .wgl-saved-total { text-align: start; }
}

/* The row is a button plus, below it, however many lines of advice apply. The
   advice is deliberately outside the button — see SavedQuotations.tsx.

   THE CARD IS THE ROW, NOT THE BUTTON. The frame used to sit on the button, so
   everything that is a sibling of it — the advice lines, and the Approve and
   Delete — hung underneath the frame in the gap between one quotation and the
   next, belonging to nothing you could point at. The button cannot simply
   contain them (a button inside a button is not a thing, which is the reason
   they are siblings at all), so the frame moves out here and the button becomes
   the card's first line. */
/* A quotation and its verbs on ONE line where the width allows it — the legacy's
   `.svrow { grid-template-columns: 1fr auto auto }`, which put the row, its status
   and its buttons across a single line and dropped the advice underneath.

   This had been a flex COLUMN, so the action bar was on a second line by
   construction: every row was two lines tall whether or not the buttons needed the
   space, and a list of twenty-five was fifty lines of scrolling. The buttons are
   short and the right-hand side of a row is empty.

   The actions track is a FIXED width, shared with `.wgl-saved-head` through
   --wgl-actions-w, and that is what keeps the six data columns under their headings:
   an `auto` track would be as wide as whichever verbs that row happens to carry, so
   every row's Total would land in a different place. */
.wgl-saved-list { --wgl-actions-w: 11.5rem; }

.wgl-saved-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--wgl-actions-w);
  align-items: center;
  column-gap: 0.6rem;
  row-gap: 0.25rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius-sm);
  background: var(--bs-body-bg);
  padding: 0.5rem 0.7rem;
  transition: border-color var(--s-transition);
}

/* A paragraph about somebody's property, full width under the row it belongs to.
   Without the span it would be squeezed into the 1fr track beside the buttons. */
.wgl-saved-row > .wgl-advice { grid-column: 1 / -1; }

/* The tombstone view keeps stacking. Its bar is not two short verbs but "Deleted
   3 Aug by admin" plus Restore plus Erase forever, and the meta line is written to
   push the buttons away with `margin-inline-end: auto` — that only means anything
   across a full-width bar. */
.wgl-saved-row.is-deleted { grid-template-columns: minmax(0, 1fr); }

/* Below the width where the headings hide there is nothing left to align to, and
   the six columns have collapsed anyway. */
@media (max-width: 860px) {
  .wgl-saved-row { grid-template-columns: minmax(0, 1fr); }
}

/* The archive sits above the document it belongs to, so its list is a scroller
   rather than a column that grows — the legacy's `.savedlist { max-height: 320px;
   overflow-y: auto }`, in rem so it tracks the text size.

   26rem rather than the legacy's 320px because these rows are taller: each carries
   its advice lines and its action bar under the button, where the legacy's carried a
   single line. Roughly four rows either way, which is what the box is for — enough to
   see the neighbours of the one you came for without hiding the quotation below.

   The padding and the negative margin are for the scrollbar: without them a row's
   focus ring and its hover edge are clipped by the overflow box. */
.wgl-archive-panel .wgl-saved-list {
  max-height: 26rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 0.15rem;
  margin: 0 -0.15rem;
}

/* Hover belongs to the whole card, not to the part of it that happens to be the
   button. The button bleeds left/top/bottom only — it has to, or its tint would run
   under the verbs beside it — so painting the hover on the button stopped it dead at
   the actions track and left a pale rectangle clipped out of the right-hand end of
   every row. The row is the object the pointer is over; the row takes the fill.

   Not on a tombstone: its button is disabled and `cursor: default`, so a hover
   affordance there would promise something that does not happen. It keeps the edge. */
.wgl-saved-row:hover { border-color: var(--s-wgl-accent-fg); }

.wgl-saved-row:not(.is-deleted):hover { background: var(--bs-tertiary-bg); }

/* The open row is already yellow; hovering deepens it rather than replacing it with
   a grey that would read as "not the open one any more". */
.wgl-saved-row.is-current:not(.is-deleted):hover {
  background: rgba(var(--wgl-yellow-rgb), 0.2);
}

/* The consignment the builder below has open — the legacy's `.svrow.cur`, which
   was `border-color: var(--yellow); background: var(--ytint)`.

   Built from --wgl-yellow-rgb at the same 0.13 the theme uses for --s-row-bg-hover,
   so it reads as the same family of "this one" the grids use, and so it survives
   both themes: a literal tint would be invisible on the dark one. The tint is on the
   WRAPPER because the wrapper is the card here (see the rule above) — put on the
   button it would stop short of the advice lines and the action bar, marking the top
   third of a row and leaving the rest looking like a different row. */
.wgl-saved-row.is-current {
  border-color: var(--s-wgl-accent-fg);
  background: rgba(var(--wgl-yellow-rgb), 0.13);
}



/* Stripped of the frame it no longer owns. It keeps its own padding and takes a
   matching negative margin, so the columns still land under `.wgl-saved-head`
   and the hover tint still reaches the card's edge — without the negative
   margin the tint stops 0.7rem short on both sides and reads as a second box
   drawn inside the first.

   The bleed is left/top/bottom only now that the verbs sit to the right: bleeding
   into them would run the hover tint under the buttons, which are not part of what
   this button opens. `padding-inline-end: 0` for the same reason the negative
   margin exists — with the trailing padding still in place the six columns would
   end 0.7rem short of the six headings above them. */
.wgl-saved-row > .wgl-saved {
  width: auto;
  margin: -0.5rem 0 -0.5rem -0.7rem;
  padding-inline-end: 0;
  border-color: transparent;
  border-radius: var(--bs-border-radius-sm) 0 0 var(--bs-border-radius-sm);
  background: transparent;
}

/* Stacked again: the bleed goes back to both sides and the trailing padding
   returns, because there is nothing beside it to leave room for.

   Deliberately keyed on the tombstone view and not on "the bar happens to be
   empty". A Rejected row carries no verbs at all — it can be neither approved,
   un-approved nor deleted — and it must still reserve the track, or its six columns
   would widen out from under the six headings on the strength of its own status. */
.wgl-saved-row.is-deleted > .wgl-saved {
  margin: -0.5rem -0.7rem 0;
  padding-inline-end: 0.7rem;
  border-radius: var(--bs-border-radius-sm) var(--bs-border-radius-sm) 0 0;
}

@media (max-width: 860px) {
  .wgl-saved-row > .wgl-saved {
    margin: -0.5rem -0.7rem 0;
    padding-inline-end: 0.7rem;
    border-radius: var(--bs-border-radius-sm) var(--bs-border-radius-sm) 0 0;
  }
}

/* Transparent, so the row's fill shows through unbroken. A fill here would be a
   second, darker box drawn inside the row's — visibly clipped at the actions
   track. `.wgl-saved:hover` still applies to the standalone lists that use the
   button without a row wrapper. */
.wgl-saved-row > .wgl-saved:hover { background: transparent; }

/* A tombstone. Muted rather than struck through: the row still has to be readable
   enough to decide whether to put it back, and the actions under it must not be. */
.wgl-saved-row.is-deleted .wgl-saved {
  opacity: 0.62;
  cursor: default;
}

/* Right-aligned so the destructive verbs sit away from the row's own click target
   rather than under its leading edge. No trailing padding: the verbs now sit
   inside the card, so their edge is the card's inner edge — the same one the
   total above them ends on. */
.wgl-saved-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 0.4rem;
}

/* Inline with the row rather than under it, so the verbs must not grow the track
   they were given. `nowrap` keeps two short verbs on one line — wrapping them is
   what would put the row back on two lines, which is the thing this layout exists
   to stop.

   Only where the row IS the two-track layout. A tombstone's bar is "Deleted 3 Aug by
   admin" plus Restore plus Erase forever across a full-width row, and `nowrap` there
   gave it a min-content width no container could satisfy — it pushed the whole
   archive wider than the viewport and took the Total column off the right-hand
   edge. */
.wgl-saved-row:not(.is-deleted) > .wgl-saved-actions {
  flex-wrap: nowrap;
  justify-self: end;
}

@media (max-width: 860px) {
  .wgl-saved-row > .wgl-saved-actions {
    flex-wrap: wrap;
    justify-self: stretch;
  }
}

/* A row's verb, sized for a track rather than for a toolbar. */
.wgl-saved-row > .wgl-saved-actions > .wgl-btn-sm {
  padding: 0.2rem 0.55rem;
  font-size: 0.78rem;
  white-space: nowrap;
}

.wgl-saved-deleted-meta {
  margin-inline-end: auto;
  font-size: 0.82rem;
  color: var(--bs-secondary-color);
}

/* ── The supplier statement's two lists ─────────────────────
   They borrow `.wgl-saved` for its row chrome, but they are NOT archive rows:
   the item list has five cells and the payout list four, and neither has a
   status pill. Track 2 of the archive grid is the 6.5rem status column, so an
   item's DESCRIPTION landed in it — clipped to about a quarter of itself, while
   "In stock" sat in the 1fr track with room to spare. Two suppliers' worth of
   "Vivid white h…" is not a statement anybody can check. Each list therefore
   declares the columns it actually has. */
.wgl-saved.wgl-stmt-line {
  grid-template-columns: 7.5rem minmax(0, 1fr) 8.5rem 6rem 6.5rem;
}

/* date · method · fee-and-consignment · gross · net (+ the Reverse button in an
   implicit track). Five explicit tracks now that gross rides beside net — one
   short and the last column takes whatever lands in it, which is the .wgl-saved
   trap the CRM row was carved out for. */
.wgl-saved.wgl-stmt-payout {
  grid-template-columns: 7.5rem minmax(0, 1fr) 9rem 7rem 6.5rem;
}

@media (max-width: 860px) {
  .wgl-saved {
    grid-template-columns: 1fr auto;
    row-gap: 0.2rem;
  }

  /* Same collapse as the archive — the modifiers are more specific than the
     rule above, so they have to say so themselves. */
  .wgl-saved.wgl-stmt-line,
  .wgl-saved.wgl-stmt-payout {
    grid-template-columns: 1fr auto;
    row-gap: 0.2rem;
  }
}

/* ── The terms engine's advice ──────────────────────────────
   One vocabulary on all three surfaces: the archive row, the builder banner and
   the dashboard. If they ever look different it is because they ARE different,
   which is the bug this slice exists to prevent. */

/* An advice line on an archive row. The legacy's .advline: a tint and a radius,
   and NO border — the chip already carries the colour, and a bar down one side
   is neither in this app's shape language (2px outlines, hard offset shadows)
   nor in the legacy's. */
.wgl-advice {
  display: flex;
  align-items: flex-start;
  gap: 0.55rem;
  padding: 0.5rem 0.7rem;
  border-radius: var(--bs-border-radius-sm);
  background: var(--s-wgl-tint-bg-2);
  font-size: 0.85rem;
  line-height: 1.45;
}

.wgl-advice.is-over { background: rgba(var(--wgl-red-rgb), 0.07); }

.wgl-advice .wgl-status-pill { flex: none; }

.wgl-advice-text { min-width: 0; }

/* The builder's own banner, at reading size: it is the first line on a
   consignment somebody has just opened. The legacy's .advbanner — a dashed
   yellow rule all the way round, going red when the terms have been missed.
   Dashed because it is advice rather than a document. */
.wgl-advice-lg {
  padding: 0.7rem 0.85rem;
  font-size: 0.92rem;
  border: 1.5px dashed var(--s-wgl-accent-fg);
  background: var(--s-wgl-tint-bg);
}

.wgl-advice-lg.is-over {
  border-color: var(--s-danger-text);
  border-style: dashed;
}

/* ── The ladder strip ───────────────────────────────────────
   Approved consignments only. Every date on it was recorded by a person. */

.wgl-ladder-panel {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.wgl-advice-banner:empty { display: none; }

/* Inset on the BANNER, not on `.wgl-ladder-panel` around it.

   The panel has no padding of its own, which is right for the strip below — that is a
   full-bleed band between two of the sheet's hairlines. But the banner's child is
   `.wgl-advice-lg`, a dashed-bordered card, and a bordered card running flush to the
   sheet's edges reads as one somebody forgot to inset. Padding the panel instead would
   inset the strip too and put the card-in-a-card back.

   0.85rem is the strip's own horizontal padding, so the banner's border lines up with
   the text inside the band under it rather than landing at some third measure. */
.wgl-advice-banner {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  padding: 0 0.85rem;
}

/* No outline and no radius of its own. The strip is a BAND inside the document
   sheet, and the sheet already separates its children with a hairline
   (`.wgl-doc-sheet > * + *` in legacy-shapes.css) — a boxed strip inside that reads
   as a card in a card. The tint alone carries it. */
.wgl-ladder {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem 0.9rem;
  padding: 0.65rem 0.85rem;
  background: var(--bs-body-bg);
}

.wgl-ladder-title {
  font-family: var(--header-font), 'Space Grotesk', sans-serif;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
}

.wgl-ladder-rungs {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.4rem 0.7rem;
}

.wgl-rung {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.3rem 0.6rem;
  border-radius: var(--wgl-r-chip);
  background: var(--bs-tertiary-bg);
  color: var(--bs-secondary-color);
  font-size: 0.82rem;
}

/* Due but unmarked is the only state that asks for anything, so it is the only
   one that carries the brand fill. */
.wgl-rung.is-due {
  background: var(--wgl-yellow);
  color: var(--wgl-black);
  font-weight: 600;
}

.wgl-rung.is-done {
  background: rgba(var(--wgl-green-rgb), 0.12);
  color: var(--bs-body-color);
}

.wgl-rung-date { font-variant-numeric: tabular-nums; font-weight: 600; }

.wgl-rung-note {
  font-size: 0.78rem;
  opacity: 0.85;
  font-variant-numeric: tabular-nums;
}

.wgl-rung-btn { padding: 0.15rem 0.5rem; font-size: 0.72rem; }

/* ── Counter-offer ─────────────────────────────────────────── */

.wgl-counter {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-top: 1.1rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--bs-border-color);
}

.wgl-counter-entry {
  display: flex;
  gap: 0.5rem;
  align-items: flex-start;
}

.wgl-counter-entry textarea.editor {
  flex: 1;
  padding: var(--s-input-padding-v) 0.65rem;
  line-height: var(--s-input-line-height);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius-sm);
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
  resize: vertical;
}

.wgl-counter-entry textarea.editor:focus {
  outline: none;
  border-color: var(--s-wgl-accent-fg);
  box-shadow: var(--s-focus-ring);
}

/* Once recorded it is part of the document, so it reads as settled text
   rather than as a form field somebody might still be typing into. The
   filled surface carries that on its own — same 1px border and radius as
   every other block on the page, so it sits in the panel rather than
   announcing itself. */
.wgl-counter-recorded {
  padding: 0.6rem 0.85rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius-sm);
  background: var(--bs-tertiary-bg);
  color: var(--bs-body-color);
}

/* ── Fields ────────────────────────────────────────────────── */

.wgl-field-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: var(--s-col-gap) var(--s-col-gap);
  align-items: start;
}

.wgl-field {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  min-width: 0;
  margin-bottom: 0;
}

.wgl-field-label {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--bs-secondary-color);
  letter-spacing: 0.01em;
}

.wgl-field-static {
  min-height: var(--s-input-inner-height);
  display: flex;
  align-items: center;
}

/* The customer has not given enough. Read across a counter with somebody waiting,
   so it earns weight as well as colour — the figure has to be noticed before the
   cashier reaches for the drawer, not after. */
.wgl-short {
  color: var(--s-danger-text, var(--wgl-red));
  font-weight: 700;
}

.wgl-field-hint {
  display: block;
  font-size: 0.78rem;
  font-weight: 400;
  color: var(--bs-secondary-color);
}

/* The consequence of what is currently SAVED in this field, under the hint that says
   what the field means. Today only the label width has one — whether a barcode will
   actually scan off the loaded roll.

   Three levels, and the colour is the whole signal, so each one is a token that
   survives both themes rather than a literal. --wgl-yellow-deep for "warn" because
   plain yellow is a fill in this palette and never legible as text on light. */
.wgl-field-advice {
  display: block;
  margin-top: 0.25rem;
  font-size: 0.78rem;
  line-height: 1.45;
  font-weight: 400;
}

.wgl-field-advice.adv-ok { color: var(--bs-secondary-color); }

.wgl-field-advice.adv-warn {
  color: var(--wgl-yellow-deep);
  font-weight: 600;
}

.wgl-field-advice.adv-bad {
  color: var(--s-danger-text, var(--wgl-red));
  font-weight: 700;
}

/* What the shop already knows about the chosen supplier. It appears under the picker
   after a choice, so it needs breathing room above it and must not shift the grid
   row when it is empty — which is why it has no min-height and the field is already
   flow-laid. */
.wgl-supplier-hint:not(:empty) {
  margin-top: 0.3rem;
  line-height: 1.4;
}

/* "New supplier — this creates a supplier record." Typing a name nobody has used
   before is a normal thing to do at a counter, not a warning — so it is marked, not
   alarmed. Emphasis rather than a colour of its own keeps it legible in both themes
   and needs no new token. */
.wgl-supplier-hint.is-new {
  color: var(--bs-body-color);
  font-weight: 500;
}

/* Offered beside a photograph that skipped the crop dialog on the way in. */
.wgl-crop-btn {
  margin-top: 0.4rem;
}

/* What the AI read off the photograph, offered rather than applied. It sits between
   the card head and its fields so it reads as a question about THIS item, and it is
   quiet by design — a suggestion competing with the fields it wants to fill would be
   read as an error. */
.wgl-item-suggest {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.55rem 0.75rem;
  border-top: 1px solid var(--bs-border-color);
  background: var(--bs-tertiary-bg);
}

.wgl-item-suggest-body {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
  flex: 1 1 20rem;
}

.wgl-item-suggest-label {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
}

.wgl-item-suggest-text {
  font-size: 0.86rem;
  line-height: 1.4;
}

.wgl-item-suggest-why {
  font-size: 0.78rem;
  color: var(--bs-secondary-color);
  line-height: 1.4;
}

.wgl-item-suggest-actions {
  display: flex;
  gap: 0.4rem;
  flex-shrink: 0;
}

/* Said after the write, never in front of it. The legacy asked its duplicate
   question once the record existed, so nothing typed could be lost by answering. */
.wgl-notice {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius, 0.375rem);
  background: var(--bs-tertiary-bg);
}

.wgl-notice-text {
  flex: 1 1 22rem;
  min-width: 0;
  font-size: 0.86rem;
  line-height: 1.45;
}

.wgl-notice-actions {
  display: flex;
  gap: 0.4rem;
  flex-shrink: 0;
}

.wgl-field-check {
  flex-direction: row;
  align-items: flex-start;
  gap: 0.55rem;
  padding-top: 1.35rem;
}

.wgl-field-check input { margin-top: 0.2rem; }
/* The caption is a span (a nested label would not toggle the box), so it has to be
   named here too — and the row itself takes the pointer, because the whole label is
   the hit target. */
.wgl-field-check,
.wgl-field-check label,
.wgl-field-check > span:not(.wgl-field-label) { cursor: pointer; }
.wgl-field-check label,
.wgl-field-check > span:not(.wgl-field-label) { font-weight: 500; }

/* The lookup editor renders its own select2 markup; it only needs
   to be told to fill the column it was dropped into. */
.wgl-field-editor,
.wgl-field-editor .select2-container { width: 100%; }

/* A checkbox is not a text field. It reaches these rules because WglDialog gives
   every control `class="editor"`, and `width: 100%` on a checkbox is what turned
   the Label layout dialog into five empty full-width boxes: the 1em control
   stretched across the row and pushed its own label out of the flex line. The
   `:not()` is what lets `.wgl-field-check input { width: auto }` win. */
.wgl-field input.editor:not([type="checkbox"]),
.wgl-field select.editor {
  width: 100%;
  padding: var(--s-input-padding-v) 0.65rem;
  line-height: var(--s-input-line-height);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius-sm);
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
}

.wgl-field input.editor:focus,
.wgl-field select.editor:focus {
  outline: none;
  border-color: var(--s-wgl-accent-fg);
  box-shadow: var(--s-focus-ring);
}

.wgl-field input.editor:disabled,
.wgl-field select.editor:disabled {
  background: var(--bs-secondary-bg);
  color: var(--bs-secondary-color);
  cursor: not-allowed;
}

/* ── Products: the full item editor ─────────────────────────
   A dialog, not a page: the card still edits the four fields somebody corrects
   standing at a rail, and this is for everything else. */

.wgl-dlg-wide .modal-dialog { max-width: 60rem; }

.wgl-editor-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.4rem;
  padding: 0.7rem 0.85rem;
  margin-bottom: 1rem;
  border: var(--wgl-edge-thin);
  border-radius: var(--wgl-r-card);
  background: var(--s-wgl-tint-bg-2);
}

.wgl-editor-summary:empty { display: none; }

.wgl-editor-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-inline-start: auto;
}

.wgl-editor-section { margin-bottom: 1.25rem; }

.wgl-editor-title {
  font-family: var(--header-font), 'Space Grotesk', sans-serif;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
  margin: 0 0 0.6rem;
}

.wgl-editor-photos {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 1rem;
  margin-bottom: 0.6rem;
}

.wgl-editor-photo { flex: none; }

/* The shared strip — see Products/ItemGallery, which both the product editor and
   the quotation builder's item cards mount. `.wgl-thumbs` is the row of tiles;
   `.wgl-gallery` is that plus its "+ Photos" button and its progress note. */
.wgl-gallery {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.5rem;
  min-width: 0;
}

.wgl-editor-gallery,
.wgl-thumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0.5rem;
  min-width: 0;
}

/* Nothing filed yet: the strip collapses rather than leaving a gap above the
   button, so an item with one photograph does not look like one with a broken
   gallery. */
.wgl-gallery.is-empty .wgl-thumbs { display: none; }

.wgl-gallery.is-busy { opacity: 0.6; pointer-events: none; }

.wgl-gallery-note {
  font-size: 0.78rem;
  color: var(--bs-secondary-color);
}

/* On a card the tiles are smaller than in the product editor's own panel — the
   lane is 11rem, so three 3rem tiles and their two 0.5rem gaps come to 10rem and
   still fit three to a row, which is the shape the legacy's 44px thumbs under a
   150px photo had. At the old 3.75rem they would now fit two. */
.wgl-item-photo-col .wgl-thumb {
  width: 3rem;
  height: 3rem;
}

.wgl-editor-hint {
  font-size: 0.8rem;
  line-height: 1.45;
  color: var(--bs-secondary-color);
  margin: 0;
}

/* A thumbnail. The main shot carries the brand fill so which one the listing
   leads with is visible without reading a label. */
.wgl-thumb {
  position: relative;
  width: 5rem;
  height: 5rem;
  border: var(--wgl-edge-thin);
  border-radius: var(--wgl-r-chip);
  overflow: hidden;
  background: var(--bs-tertiary-bg);
}

.wgl-thumb.is-primary {
  border: var(--wgl-edge);
  box-shadow: var(--wgl-pop);
}

.wgl-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  cursor: pointer;
}

.wgl-thumb.is-primary img { cursor: default; }

.wgl-thumb-del {
  position: absolute;
  inset-block-start: 0.15rem;
  inset-inline-end: 0.15rem;
  opacity: 0;
  transition: opacity var(--s-transition);
}

/* Opposite corner from delete, on purpose: these tiles are 5rem square and the
   destructive control must never be where the eye expects the harmless one. */
.wgl-thumb-zoom {
  position: absolute;
  inset-block-end: 0.15rem;
  inset-inline-end: 0.15rem;
  opacity: 0;
  transition: opacity var(--s-transition);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
}

/* Both of these sit ON a photograph, so neither can inherit the panel's
   foreground and expect to be seen — a --bs-secondary-color glyph is invisible
   over a white dress and nearly so over a dark bag. They get their own ground:
   a translucent black disc with a paper glyph, which holds over any photo in
   either theme, because what is behind them is the image and never the page. */
.wgl-thumb-del,
.wgl-thumb-zoom {
  width: 1.55rem;
  height: 1.55rem;
  border-radius: 50%;
  background: rgba(var(--wgl-black-rgb), 0.62);
  color: var(--wgl-paper);
  backdrop-filter: blur(2px);
}

.wgl-thumb-del:hover,
.wgl-thumb-zoom:hover {
  background: rgba(var(--wgl-black-rgb), 0.82);
  color: var(--wgl-paper);
}

/* Delete keeps its warning colour, but only once it is the thing being
   pointed at — a red dot on every tile in a gallery is a wall of alarm. */
.wgl-thumb-del:hover { color: var(--s-danger-text); }

.wgl-thumb:hover .wgl-thumb-del,
.wgl-thumb:hover .wgl-thumb-zoom,
.wgl-thumb-del:focus-visible,
.wgl-thumb-zoom:focus-visible { opacity: 1; }

/* ── Images as lightbox triggers ─────────────────────────────
   Every photo on a WGL screen is rendered small — a 6rem card crop, a 5rem
   tile, a signature at 22mm — and each is something somebody has a real reason
   to look at closely: repricing an item, checking an enhancement, matching a
   signature to a contract. The overlay is Modules/Common/Lightbox.ts, wired
   globally from ScriptInit.ts, so the markup here is just the anchor. */

a[data-lightbox] {
  cursor: zoom-in;
  text-decoration: none;
}

a[data-lightbox]:focus-visible {
  outline: 3px solid var(--wgl-yellow);
  outline-offset: 2px;
}

/* The product card's well. The anchor has to FILL it, or the clickable area is
   the image's intrinsic box and the `object-fit: cover` crop around it is dead
   space that looks clickable and is not. */
.wgl-photo-link {
  display: block;
  width: 100%;
  height: 100%;
}

/* The enhance dialog's two panes: the anchor stands in for the block-level img
   it wraps, so the 1:1 aspect box is unchanged. */
.wgl-enh-side a[data-lightbox] { display: block; }

/* The signature. `line-height: 0` keeps the inline box from adding a descender
   strip under the image, which would otherwise show as clickable dead space
   below a control that is only 22mm tall to begin with. */
.wgl-sign-link {
  display: inline-block;
  line-height: 0;
}

/* Scan straight into a search box — the same component the till uses. */
.wgl-search-row {
  display: flex;
  gap: 0.4rem;
  align-items: stretch;
}

.wgl-search-row input.editor { flex: 1 1 auto; min-width: 0; }
.wgl-search-scan { flex: none; padding-inline: 0.7rem; }

/* ── Workstream F: the AI buttons and the enhance modal ─────
   Ghost buttons throughout. These propose; they never decide, and a button that
   spends money should not be the loudest thing on the panel. */

.wgl-editor-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

.wgl-ai-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

/* The legacy's `pmGenStatus`: what the last AI action did, said in the dialog rather
   than in a toast that is gone before anyone looks back at it. Its own row, so a
   sentence does not squeeze the buttons beside it. */
.wgl-ai-status {
  flex: 1 0 100%;
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--bs-secondary-color);
  min-height: 1.2em;
}

/* What the studio tracks already think of this item — on model, excluded. Facts, so
   they sit above the photographs rather than among the buttons that change them. */
.wgl-editor-flags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}

.wgl-editor-flags:empty { display: none; }

/* Original against enhanced, the same size, because the difference is the only
   thing being judged. */
.wgl-enh {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: 1rem;
  margin-bottom: 0.9rem;
}

.wgl-enh-side { margin: 0; }

.wgl-enh-side img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: contain;
  background: var(--bs-tertiary-bg);
  border: var(--wgl-edge-thin);
  border-radius: var(--wgl-r-card);
  display: block;
}

.wgl-enh-side figcaption {
  margin-top: 0.35rem;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
  text-align: center;
}

.wgl-enh-choices {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-top: 0.9rem;
}

.wgl-enh-choice {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.55rem 0.7rem;
  border: var(--wgl-edge-thin);
  border-radius: var(--wgl-r-btn);
  cursor: pointer;
}

.wgl-enh-choice:hover { background: var(--bs-tertiary-bg); }
.wgl-enh-choice input { margin-top: 0.2rem; }
.wgl-enh-choice > span { display: flex; flex-direction: column; gap: 0.1rem; }
.wgl-enh-choice:has(input:checked) { border-color: var(--s-wgl-accent-fg); background: var(--s-wgl-tint-bg-2); }

/* ── Drop photos to start items ─────────────────────────────
   The legacy's addItemsFromPhotos zone. Dashed, because it is a target rather
   than a control, and it is the one place on the builder where a whole batch of
   work begins. */

.wgl-drop {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  padding: 0.9rem 1rem;
  margin-bottom: 1rem;
  border: 1.5px dashed var(--bs-border-color);
  border-radius: var(--wgl-r-card);
  background: var(--bs-tertiary-bg);
  cursor: pointer;
  transition: border-color var(--s-transition), background var(--s-transition);
}

.wgl-drop:hover,
.wgl-drop.is-over {
  border-color: var(--s-wgl-accent-fg);
  background: var(--s-wgl-tint-bg-2);
}

.wgl-drop:focus-visible { outline: none; box-shadow: var(--s-focus-ring); }

/* Mid-upload the zone stops being a target, and says so. */
.wgl-drop.is-busy {
  cursor: progress;
  border-style: solid;
  border-color: var(--s-wgl-accent-fg);
}

.wgl-drop > i {
  font-size: 1.4rem;
  color: var(--s-wgl-accent-fg);
  flex: none;
}

.wgl-drop-text {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;
  font-size: 0.85rem;
  line-height: 1.45;
}

.wgl-drop-text > span { color: var(--bs-secondary-color); font-size: 0.8rem; }

.wgl-drop-note {
  margin-inline-start: auto;
  font-variant-numeric: tabular-nums;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--bs-secondary-color);
  white-space: nowrap;
}

.wgl-drop-note:empty { display: none; }

/* A second pill on an archive row: this consignment is entirely on the Shopify
   store. Deliberately quieter than the status pill beside it — it is a fact about
   the listing, not about where the consignment stands. */
.wgl-status-pill.wgl-pill-shopify {
  background: var(--s-wgl-tint-bg-2);
  color: var(--bs-body-color);
  border-color: var(--s-wgl-accent-fg);
}

/* ── The variants repeater ─────────────────────────────────────
   The legacy's `.vartbl`: an item that is really several pieces, each with its own
   size, colour, material, quantity, price and barcode.

   It sits between the attributes row and `.frow3`, directly under the Qty and Unit
   price boxes it supersedes — see ItemCard.applyVariantOverrides, which greys the
   quantity box out because ItemVariants.Units stops reading it the moment a row
   appears here.

   Inset rather than boxed. A card already carries a border, a header tab and a totals
   bar; a sixth framed region inside it reads as a separate panel that happens to be
   nested. A tinted well with one rule above it says "part of this item" instead.      */

.wgl-var {
  margin: 0.15rem 0 0.55rem;
  padding: 0.55rem 0.7rem 0.6rem;
  border-radius: var(--bs-border-radius);
  background: var(--bs-tertiary-bg);
  border: 1px solid var(--bs-border-color);
}

.wgl-var-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.45rem;
}

.wgl-var-title {
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--bs-body-color);
}

.wgl-var-note {
  font-size: 0.74rem;
  line-height: 1.35;
  color: var(--bs-secondary-color);
  flex: 1 1 16rem;
  min-width: 0;
}

.wgl-var-host {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

/* Six inputs and a remove button on one line. The three attribute boxes share the
   slack and the two numbers are fixed, because a quantity box wide enough for four
   digits is a quantity box that has taken width from "Material". The barcode column
   is fixed at what a ten-digit code needs plus a SOLD OUT chip.                      */
.wgl-var-row {
  display: grid;
  grid-template-columns:
    minmax(0, 1.1fr) minmax(0, 1.1fr) minmax(0, 1.1fr)
    4.2rem 5.4rem minmax(7.5rem, 0.9fr) 1.9rem;
  gap: 0.3rem;
  align-items: center;
}

.wgl-var-row input {
  min-width: 0;
  width: 100%;
  font-size: 0.82rem;
  padding: 0.28rem 0.45rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius-sm);
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
}

.wgl-var-row input:focus-visible {
  outline: none;
  border-color: var(--bs-primary-border-subtle, var(--bs-border-color));
  box-shadow: var(--s-focus-ring);
}

.wgl-var-row input:disabled {
  background: var(--bs-secondary-bg);
  color: var(--bs-secondary-color);
}

/* A price the operator typed, against one inherited from the item. The legacy marks
   this too ("show at a glance which variants are priced on their own") and the reason
   is that the two look identical otherwise: an empty box showing the item's price as
   a placeholder and a filled box showing the same figure are one keystroke apart and
   mean different things the next time the item's price changes.                      */
.wgl-var-unitpriceusd.is-overridden {
  border-color: var(--wgl-yellow-deep);
  background: var(--wgl-tint-2);
  font-weight: 600;
}

.wgl-var-code {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  min-width: 0;
  font-size: 0.72rem;
}

.wgl-var-bc {
  font-family: var(--bs-font-monospace);
  letter-spacing: 0.04em;
  color: var(--bs-body-color);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Not yet stamped. ItemIdentity issues a variant's code when something needs one —
   printing a label, exporting the CSV — rather than at save time, so this is the
   normal state of a row that has just been typed, not an error.                      */
.wgl-var-bc.is-pending {
  font-style: italic;
  color: var(--bs-secondary-color);
  letter-spacing: 0;
}

/* A colourway with nothing left. It is NOT hidden and NOT removed: zero is how the
   shop records "this one sold out", the row keeps its barcode, and putting stock back
   is typing a number into it.                                                        */
.wgl-var-row.is-empty input:not(:focus) { opacity: 0.72; }

.wgl-var-add {
  margin-top: 0.45rem;
  font-size: 0.78rem;
  padding: 0.22rem 0.6rem;
}

.wgl-var-hint {
  margin-top: 0.45rem;
  font-size: 0.76rem;
  color: var(--bs-secondary-color);
}

.wgl-var-hint b { color: var(--bs-body-color); }

/* Under ~48rem the seven-column grid stops being a row and becomes a queue of
   slivers. Two lines: the three attributes, then the numbers, the code and remove. */
@media (max-width: 48rem) {
  .wgl-var-row {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr);
    row-gap: 0.25rem;
    padding-bottom: 0.35rem;
    border-bottom: 1px dashed var(--bs-border-color);
  }

  .wgl-var-row > :nth-child(4) { grid-column: 1; }
  .wgl-var-row > :nth-child(5) { grid-column: 2; }
  .wgl-var-code { grid-column: 3; }
  .wgl-var-rm { grid-column: 3; justify-self: end; }
}

/* ── The card's actions ────────────────────────────────────────
   Six unlabelled icons in the corner became: Print label inline beside the barcode,
   Enhance on the photograph, and Return / reorder / Remove at the foot. See
   ItemCard.renderHeadActions for why — two of the six glyphs were not pictures of
   anything, and the operator named both.                                             */

.wgl-item-label-slot { display: inline-flex; }

/* An action with a word on it. The icon stays — it is a fast target once learned —
   and the word is what makes it learnable at all.                                    */
.wgl-item-act {
  display: inline-flex;
  align-items: center;
  gap: 0.32rem;
  font-size: 0.78rem;
  padding: 0.2rem 0.55rem;
  white-space: nowrap;
}

.wgl-item-act .fa { font-size: 0.82em; }

/* Removing a line is not tidying. It says the item was never part of the deal and
   closes the numbering up behind it, where Return says the deal happened and the
   garment came back — so the two must not read as neighbours. Quiet until hovered:
   a permanently red button in every card is thirty red buttons on a consignment.     */
.wgl-item-act.wgl-btn-danger:hover,
.wgl-item-act.wgl-btn-danger:focus-visible {
  background: var(--wgl-red);
  border-color: var(--wgl-red);
  color: var(--wgl-white);
}

.wgl-item-foot-actions {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  margin-left: auto;
}

/* Pushes Remove away from the two reorder arrows. `margin-left: auto` on the group
   already right-aligns it; this is the separation INSIDE it.                         */
.wgl-item-foot-gap { flex: 0 0 0.5rem; }

@media (max-width: 40rem) {
  .wgl-item-foot-actions {
    margin-left: 0;
    width: 100%;
    justify-content: flex-end;
  }
}

/* The sparkle over the photograph — the legacy's `.ph-enh`. Top-right of the shot it
   acts on, which is the only placement that makes a bare wand unambiguous.           */
.wgl-item-photo { position: relative; }

/* Top-right of the PICTURE, and that is why it is a slot the editor owns rather than
   a corner of the photo well — see `CroppedImageUploadEditor.overlay`. The well's own
   top-right corner belongs to the editor's toolbar, so a wand placed there sat over
   Select File and read as a third toolbar button, which is the opposite of the reason
   this action is placed rather than pooled with the other five. */
.wgl-shot-overlay {
  position: absolute;
  top: 0.3rem;
  right: 0.3rem;
  z-index: 2;
}

.wgl-shot-overlay:empty { display: none; }

/* On the picture rather than on the panel, so it needs its own ground to stay legible
   over a dark photograph as well as a pale one.                                      */
.wgl-shot-overlay .wgl-icon-btn {
  width: 1.7rem;
  height: 1.7rem;
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  box-shadow: var(--bs-box-shadow-sm);
}

.wgl-shot-overlay .wgl-icon-btn:hover {
  background: var(--wgl-yellow);
  border-color: var(--wgl-yellow-deep);
  color: var(--wgl-black);
}

/* A field the variants have taken over. Greyed, and its tooltip says why — an input
   that is simply disabled with no explanation reads as a bug.

   BELOW the control, not appended to the label. `.wgl-field-grid` is `align-items:
   start`, so every field in a row hangs from the top and its control sits under
   whatever height the label came out at. Both of these annotations landed on the two
   NARROWEST boxes in the opening row — Qty at 0.5fr, Unit price at 1fr — so both
   labels wrapped to a second line and both inputs dropped about 19px below
   Description, Category and Condition beside them. The row read as two rows.

   Under the control the same words cost the row height they always cost and cost the
   alignment nothing: five labels of one line each, five boxes on one baseline, and
   the note hangs off the bottom of the field it belongs to. `.wgl-field` is already
   `flex-direction: column`, so the pseudo-element is simply its last item.           */
.wgl-field.is-superseded .wgl-field-label { color: var(--bs-secondary-color); }

.wgl-field.is-superseded::after,
.wgl-field.is-inherited-by::after {
  font-size: 0.72rem;
  line-height: 1.3;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--bs-secondary-color);
}

.wgl-field.is-superseded::after { content: 'Counted from the variants'; }
.wgl-field.is-inherited-by::after { content: 'Variants inherit this'; }

/* ── The document container ────────────────────────────────────
   Header, ladder, items, e-com, terms and approval, in one collapsible section.

   The screen used to render a complete blank quotation under the archive whether or
   not anybody was working on one, so arriving to look a consignment up meant reading
   past an empty document to reach the list. Now the URL decides: an id opens it, no
   id shows the list alone.

   NOT a dialog, deliberately. This is the screen's primary work surface — it runs to
   175 cards on a big consignment, it owns the photo drop zone, and its totals bar is
   pinned outside the scrolling column. A modal would trap the scroll, fight the drop
   zone and hide the totals, and a modal is the wrong container for a primary flow in
   any case.                                                                          */

.wgl-doc {
  display: flex;
  flex-direction: column;
  gap: var(--s-row-gap);
  min-width: 0;
}

/* Enter only, and cheap. The container can hold a hundred and seventy-five cards, so
   animating its height would reflow the lot on every frame — this moves `transform`
   and `opacity`, which do not. There is no exit animation on purpose: Close is a
   navigation, and the fastest exit is the one that has already happened. */
@keyframes wgl-doc-in {
  from { opacity: 0; transform: translateY(-0.4rem); }
  to   { opacity: 1; transform: none; }
}

.wgl-builder.has-doc > .wgl-doc {
  animation: wgl-doc-in 180ms ease-out;
}

@media (prefers-reduced-motion: reduce) {
  .wgl-builder.has-doc > .wgl-doc { animation: none; }
}

/* ── The sheet: header + ladder + items, as one card ──────
   These three are one subject — who consigned this, what the shelf is charging, and
   what is on it — and they were drawn as three separately bordered panels with a gap
   between each, which said they were three. The sheet carries the chrome; the panels
   inside it give theirs up and are separated by a hairline instead.

   Structural rather than cosmetic: the gap between the header and the items is where
   the operator's eye travels on every single line they type at intake. */
.wgl-doc-sheet {
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-card-border-radius);
  box-shadow: var(--s-shadow-xs);
  overflow: hidden;
}

/* The children stop being cards. `border-radius: 0` matters as much as the border:
   a rounded panel inside a rounded sheet leaves four pale notches at the corners. */
/* `:where()` keeps this reset from outranking the divider rule below — see the note
   on the same pair in legacy-shapes.css. */
.wgl-doc-sheet > :where(.wgl-panel) {
  border: none;
  border-radius: 0;
  box-shadow: none;
  background: transparent;
}

/* `> * + *` rather than panel-to-panel: the ladder strip between the header and the
   items carries no `.wgl-panel` class, so the narrower form matched nothing and the
   sheet drew with no seam in it at all. On the FOLLOWING child, so a hidden one takes
   its rule with it — the ladder is display:none on anything but Approved, and a
   display:none child draws no border. */
.wgl-doc-sheet > * + * {
  border-top: 1px solid var(--bs-border-color);
}

/* ── Close, beside Save ───────────────────────────
   Immediately after Save in the DOM, so it sits next to it and needs no positioning of
   its own. It was briefly pushed to the far end of the row with `margin-left: auto` to
   keep it clear of "Cancel quotation" — which put it as far from Save as the row
   allowed, the opposite of what it is for. Second position keeps it beside Save AND
   away from Cancel, because the status buttons fall between the two. */
.wgl-header-close {
  /* Marks where "finish with this" ends and "change its status" begins, without
     spending an element on a divider. */
  margin-right: 0.35rem;
}

/* ── The archive, when it is the only thing on the page ────────
   It is a 26rem scroller because it normally shares the screen with a document. With
   nothing else here that cap turns a full-height page into a small box floating in
   white space, so it grows — still bounded, so the page never becomes one long scroll
   of every consignment the shop has taken in. */
.wgl-builder:not(.has-doc) .wgl-archive-panel .wgl-saved-list {
  max-height: min(60vh, 44rem);
}

/* Air under the archive. It is the last thing in the column, and the page's own
   padding does not reach past it — so the final row of the list sat flush against the
   bottom of the viewport with nothing to say the list had ended rather than been cut
   off. The fixed totals bar makes it worse when a document is open, which is why the
   two cases get different figures. */
.wgl-archive-panel { margin-bottom: 2rem; }

.wgl-builder.has-doc .wgl-archive-panel { margin-bottom: 4.5rem; }
