/* ============================================================================
 * Cartonwise portal — the shell and the 47 components of the v0.64 hand-off
 * (cartonwise-portal-handoff-2/, Figma XaopLjjJ7OhNf4GqXSLRvl, 28 Sep 2026).
 *
 * Load order: fonts/fonts.css → tokens.css → base.css → this file.
 *
 * 🔒 THIS FILE READS ROLE TOKENS ONLY — `--color-*`, `--spacing-*`, `--radius-*`,
 *    `--size-*`, `--type-*`, `--shadow-*`, `--duration-*`, `--ease-*`. No hex, no
 *    primitive (`--v7-*`, `--cw-*`). tokens.css is the one file that holds a hex,
 *    and it is generated: change Figma, run `tokens/build.py`, copy it here.
 *    PortalShellTest scans this file for a raw colour.
 *
 * Section order follows 02-components.md: shell, actions, inputs, status and
 * tags, feedback and content, tables, menus and dialogs, stepper / radio tile /
 * progress step, combobox, cards / facts / action bar, then the page recipes of
 * 03-patterns-and-rules.md §2. Nothing here decides FLOW — which screen follows
 * which is the controllers' business, and was not touched by the redesign.
 * ========================================================================== */


/* ── Density ──────────────────────────────────────────────────────────────────
 * The app pages use the DESKTOP column of `v7 / Layout`, which is what
 * tokens.css declares: 40 controls and rows, 36 small buttons and nav items, 48
 * L buttons, a 56 page bar (01 §1). The v0.60 screens had been drawn at the
 * Touch column and this block used to raise every size to match them; the v0.64
 * screens are drawn at Desktop and were measured again at 2x: the page bar rule
 * at 55 (s-01), rows 40 apart (s-04 217 → 257 → 297), the nav wash 36 (s-01
 * 60–96), filters 40 (s-04 122–162), the pager 48 (s-04 858–906).
 *
 * The one exception is signed out: the eight sign-in screens are drawn in the
 * Touch mode (01 §1, 03 §1) — fields 48 (a-01 370–418) and the Log in button 56
 * (a-01 566–622). `.cw-auth` below raises the sizes for that card alone.
 */
:root {
  /* How far a sticky header or filter bar sits under the page bar. The layout
     raises it for a page bar with a meta line; cartonwise-portal.js measures it. */
  --cw-pagebar-h: var(--size-header);
}
.cw-auth {
  --size-target-min: 44px;
  --size-control-md: 48px;
  --size-control-lg: 56px;
}


/* ── Base additions ───────────────────────────────────────────────────────────
 * base.css keeps every rule inside :where(), so anything here overrides it.
 */
html { background: var(--color-bg-canvas); }
body { min-height: 100vh; }
a { color: var(--color-text-link); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 2px; }
button { cursor: pointer; }
button:disabled { cursor: not-allowed; }
table { border-collapse: separate; border-spacing: 0; }
[hidden] { display: none !important; }
strong, b { font-weight: 600; }
small { font: var(--type-caption); }
code, kbd, samp { font-family: var(--font-mono); }
hr { border: 0; height: 1px; background: var(--color-border-subtle); margin: var(--spacing-05) 0; }

/* The icon: one sprite symbol, stroke only, taking its colour from the text. */
.cw-icon {
  display: inline-block;
  flex: none;
  width: var(--size-icon-sm);
  height: var(--size-icon-sm);
  vertical-align: middle;
}
.cw-icon--16 { width: 16px; height: 16px; }
.cw-icon--24 { width: var(--size-icon); height: var(--size-icon); }
.cw-icon--28 { width: 28px; height: 28px; }

/* Words in ink, figures in mono. `.cw-num` (base.css) is Plex Mono with tabular
   digits; these add the weights the table and row styles call for. */
.cw-mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.cw-mono-strong { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-weight: 500; }
.cw-unit { font-family: var(--font-sans); }            /* 6<span class="cw-unit"> s left</span> */
.cw-text-secondary { color: var(--color-text-secondary); }
.cw-text-tertiary { color: var(--color-text-tertiary); }
.cw-text-danger { color: var(--color-text-danger); }
.cw-text-warning { color: var(--color-text-warning); }
.cw-text-success { color: var(--color-text-success); }
.cw-text-primary { color: var(--color-text-primary); }
.cw-nowrap { white-space: nowrap; }
.cw-end { text-align: end; }
.cw-center { text-align: center; }
.cw-empty-value { color: var(--color-text-tertiary); }   /* 0 and – in a cell */

/* A flagged figure: text/warning beside its triangle (01 §2.2). */
.cw-flag {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-02);
  color: var(--color-text-warning);
}
.cw-flag .cw-icon { color: var(--color-status-attention); width: 16px; height: 16px; }
.cw-flag--danger { color: var(--color-text-danger); }
.cw-flag--danger .cw-icon { color: var(--color-status-stopped); }


/* ============================================================================
 * 6. SHELL — side nav, page bar, canvas, footer (02 §6, 03 §1)
 * ========================================================================== */

.cw-shell {
  display: grid;
  grid-template-columns: var(--size-side-nav) minmax(0, 1fr);
  min-height: 100vh;
}

/* Side nav: 240, full height, never collapses. The rule is inside its end edge. */
.cw-sidenav {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  padding: 0 var(--spacing-04) var(--spacing-05);
  background: var(--color-bg-surface);
  box-shadow: inset -1px 0 0 var(--color-border-subtle);
  overflow-y: auto;
  overscroll-behavior: contain;
  z-index: 4;
}

.cw-sidenav__brand {
  display: flex;
  align-items: center;
  flex: none;
  height: var(--size-header);
  padding-inline-start: var(--spacing-04);
}
.cw-sidenav__brand a { display: inline-flex; border-radius: var(--radius-xs); }

.cw-lockup { display: block; flex: none; align-self: flex-start; height: 24px; width: auto; aspect-ratio: 203 / 32; }
.cw-lockup--32 { height: 32px; }
.cw-lockup .cw-lockup__mark { fill: var(--color-action-brand); }
.cw-lockup .cw-lockup__word { fill: var(--color-text-primary); }

.cw-sidenav__nav {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-02);
  margin: 0;
  padding: var(--spacing-02) 0 0;
  list-style: none;
}
.cw-sidenav__nav ul { display: flex; flex-direction: column; gap: var(--spacing-02); margin: 0; padding: 0; list-style: none; }

/* Nav item: 36 (size/target-min), 12 padding, 12 between icon and label, radius md. */
.cw-nav-item {
  display: flex;
  align-items: center;
  gap: var(--spacing-04);
  min-height: var(--size-target-min);
  padding: 0 var(--spacing-04);
  border-radius: var(--radius-md);
  color: var(--color-text-secondary);
  font: var(--type-body-s);
  text-decoration: none;
  transition: background-color var(--duration-state) var(--ease-state);
}
.cw-nav-item .cw-icon { color: var(--color-icon-secondary); }
.cw-nav-item:hover { background: var(--color-state-hover); text-decoration: none; }
.cw-nav-item[aria-current="page"] {
  background: var(--color-state-selected);
  color: var(--color-text-link);
  font: var(--type-body-s-strong);
}
.cw-nav-item[aria-current="page"] .cw-icon { color: var(--color-icon-link); }
.cw-nav-item__label { flex: 1 1 auto; min-width: 0; }

/* Nav group, static (supplier and QC): 40 tall, 16 above and 8 below the eyebrow. */
.cw-nav-group__title {
  display: block;
  margin: 0;
  padding: var(--spacing-05) var(--spacing-04) var(--spacing-03);
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-tracking);
  text-transform: uppercase;
  color: var(--color-text-tertiary);
}

/* v0.77 (4 Oct 2026): the admin nav no longer folds. Its ten items sit straight
   under Dashboard in a group with no title, then the static OWNER group. */
.cw-nav-group--untitled .cw-nav-group__items { padding-top: 0; }
.cw-nav-item .cw-badge { margin-inline-start: auto; }

/* Account row at the foot: rule, 8, then a 54 row that opens the account menu. */
.cw-sidenav__account {
  flex: none;
  margin-top: var(--spacing-05);
  padding-top: var(--spacing-03);
  box-shadow: inset 0 1px 0 var(--color-border-subtle);
}
.cw-account-row {
  display: flex;
  align-items: center;
  gap: var(--spacing-04);
  width: 100%;
  min-height: 54px;
  padding: var(--spacing-03);
  border: 0;
  border-radius: var(--radius-md);
  background: none;
  text-align: start;
  color: var(--color-text-primary);
  transition: background-color var(--duration-state) var(--ease-state);
}
.cw-account-row:hover { background: var(--color-state-hover); }
.cw-account-row__text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--spacing-01); }
.cw-account-row__name { font: var(--type-body-s-strong); overflow-wrap: anywhere; }
.cw-account-row__portal { font: var(--type-caption); color: var(--color-text-secondary); }
.cw-account-row > .cw-icon { color: var(--color-icon-secondary); }

/* Avatar: initials on the tertiary surface. S 32 in the account row. */
.cw-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-full);
  background: var(--color-bg-surface-tertiary);
  color: var(--color-text-secondary);
  font: var(--type-caption-strong);
}
.cw-avatar--m { width: 40px; height: 40px; }
.cw-avatar--l { width: 56px; height: 56px; font: var(--type-body-strong); }

/* The work area: page bar, canvas, footer. */
.cw-main {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 100vh;
}

/* Page bar: sticky, white, a rule under it; 8 top and bottom, 24 sides. */
.cw-pagebar {
  position: sticky;
  top: 0;
  z-index: 3;
  display: flex;
  align-items: flex-start;
  gap: var(--spacing-05);
  min-height: var(--size-header);
  padding: var(--spacing-03) var(--spacing-06);
  background: var(--color-bg-surface);
  box-shadow: inset 0 -1px 0 var(--color-border-subtle);
}
.cw-pagebar__back { flex: none; }
.cw-pagebar__heading {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-02);
}
/* The title sits on a line as tall as the actions beside it. */
.cw-pagebar__title {
  display: flex;
  align-items: center;
  min-height: var(--size-control-md);
  margin: 0;
  font: var(--type-title-m);
  letter-spacing: var(--type-title-m-tracking);
  color: var(--color-text-primary);
  text-wrap: balance;
}
.cw-pagebar--meta .cw-pagebar__title { min-height: 40px; }
.cw-pagebar__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--spacing-03);
  font: var(--type-body-s);
  color: var(--color-text-secondary);
}
.cw-pagebar__actions {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--spacing-04);
  min-height: var(--size-control-md);
}
.cw-pagebar__actions form { display: contents; }

/* v0.77 (4 Oct 2026) — Tabs: a sectioned admin page's views, a white band 42 tall
   under the title row (ad-07: the page bar's rule at 110, the band's at 196, at 2x),
   inside the page bar so the two stay together and the sticky table header still
   sits under both (the script measures the whole bar). Each tab is a link, 4 a
   side; the current one is ink, strong, with a 2-px underline on the band's foot. */
.cw-pagebar--tabs { flex-wrap: wrap; padding-bottom: 0; }
.cw-pagebar--tabs > .cw-tabs {
  flex: 0 0 calc(100% + 2 * var(--spacing-06));
  margin: var(--spacing-03) calc(-1 * var(--spacing-06)) 0;
}
.cw-tabs {
  display: flex;
  align-items: stretch;
  gap: var(--spacing-06);
  min-height: 42px;
  padding: 0 var(--spacing-06);
  box-shadow: inset 0 1px 0 var(--color-border-subtle);
  overflow-x: auto;
}
.cw-tab {
  display: inline-flex;
  align-items: center;
  padding: 0 var(--spacing-02);
  color: var(--color-text-secondary);
  font: var(--type-body-s);
  white-space: nowrap;
  text-decoration: none;
  box-shadow: inset 0 -2px 0 transparent;
  transition: color var(--duration-state) var(--ease-state), box-shadow var(--duration-state) var(--ease-state);
}
.cw-tab:hover { color: var(--color-text-primary); text-decoration: none; }
.cw-tab[aria-current="page"] {
  color: var(--color-text-primary);
  font: var(--type-body-s-strong);
  box-shadow: inset 0 -2px 0 var(--color-action-brand);
}

/* The canvas: grey, padded 24, everything on white cards 24 apart. */
.cw-canvas {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-06);
  padding: var(--spacing-06);
}

/* A note about the whole page, on the canvas under the cards. */
.cw-page-note { font: var(--type-body-s); color: var(--color-text-secondary); }

/* Footer: 40, on the canvas, text 24 in, a rule over it. It follows the content. */
.cw-footer {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-05);
  min-height: 40px;
  padding: 0 var(--spacing-06);
  box-shadow: inset 0 1px 0 var(--color-border-subtle);
  font: var(--type-caption);
  color: var(--color-text-tertiary);
}
.cw-footer a { color: inherit; }


/* ── Signed out: the sign-in card (03 §1, A-01 to A-07) ─────────────────────
 * One card centred on the canvas, 962 wide: the form half and the picture half,
 * 480 each, the picture as tall as the form. Drawn in the Touch mode, which the
 * `.cw-auth` block at the top sets for this page alone.
 */
.cw-auth { display: flex; flex-direction: column; min-height: 100vh; }
.cw-auth__canvas {
  flex: 1 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--spacing-09) var(--spacing-07) var(--spacing-06);
}
.cw-auth__card {
  position: relative;
  display: grid;
  grid-template-columns: 480px 480px;
  width: 962px;
  max-width: 100%;
  border-radius: var(--radius-xl);
  background: var(--color-bg-surface);
  box-shadow: inset 0 0 0 1px var(--color-border-hairline), var(--shadow-elevation-1);
  overflow: hidden;
}
.cw-auth__form {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-06);
  padding: var(--spacing-09) var(--spacing-09) var(--spacing-08);
}
.cw-auth__form form { display: flex; flex-direction: column; gap: var(--spacing-06); }
.cw-auth__fields { display: flex; flex-direction: column; gap: var(--spacing-05); }
.cw-auth__title { margin: 0; font: var(--type-title-l); letter-spacing: var(--type-title-l-tracking); color: var(--color-text-primary); }
.cw-auth__lead { margin: calc(var(--spacing-06) * -1 + var(--spacing-03)) 0 0; font: var(--type-body); color: var(--color-text-secondary); }
.cw-auth__caption { margin: 0; font: var(--type-caption-strong); color: var(--color-text-tertiary); }
.cw-auth__link { align-self: flex-start; font: var(--type-label-s); }
.cw-auth__picture { position: relative; background: var(--color-bg-surface-secondary); }
.cw-auth__picture img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
  object-position: center;
}
.cw-auth__footer { width: 962px; max-width: calc(100% - 64px); margin: 0 auto var(--spacing-06); padding: 0 var(--spacing-07); }
@media (max-width: 1000px) {
  .cw-auth__card { grid-template-columns: minmax(0, 1fr); width: 480px; }
  .cw-auth__picture { display: none; }
  .cw-auth__footer { width: 480px; flex-wrap: wrap; justify-content: center; padding: var(--spacing-03) 0; }
}


/* ============================================================================
 * 1. ACTIONS — Button, Icon button, Text action (02 §1)
 * ========================================================================== */

.cw-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--spacing-03);
  flex: none;
  min-height: var(--size-control-md);
  padding: 0 var(--spacing-06);
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  font: var(--type-label);
  letter-spacing: var(--type-label-tracking);
  color: var(--color-text-primary);
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--duration-state) var(--ease-state),
              color var(--duration-state) var(--ease-state),
              box-shadow var(--duration-state) var(--ease-state);
}
.cw-btn:hover { text-decoration: none; }
.cw-btn--lg { min-height: var(--size-control-lg); }
.cw-btn--sm { min-height: var(--size-target-min); padding: 0 var(--spacing-05); font: var(--type-label-s); }
.cw-btn--block { width: 100%; }

.cw-btn--primary { background: var(--color-action-primary); color: var(--color-text-on-inverse); }
.cw-btn--primary:hover { background: var(--color-action-primary-hover); }
.cw-btn--primary:active { background: var(--color-action-primary-pressed); }

.cw-btn--secondary {
  background: var(--color-bg-surface);
  box-shadow: inset 0 0 0 1px var(--color-border-strong), var(--shadow-elevation-1);
  color: var(--color-text-primary);
}
.cw-btn--secondary:hover { background: linear-gradient(var(--color-state-hover), var(--color-state-hover)), var(--color-bg-surface); }
/* v0.63: a Secondary carries the fields' edge and sits a little raised; pressed
   drops the shadow, disabled stays flat, the focus ring takes the shadow's place. */
.cw-btn--secondary:active {
  background: linear-gradient(var(--color-state-pressed), var(--color-state-pressed)), var(--color-bg-surface);
  box-shadow: inset 0 0 0 1px var(--color-border-strong);
}

/* Outline: a Secondary whose edge and label take the brand blue. Drawn from the
   owner's own picture of "Fill from PO PDF" (7 Oct 2026); the design system has
   no such kind, and the owner's picture decides this one. Blue-60 on white is
   5.00:1, so the label passes AA. */
.cw-btn--secondary.cw-btn--outline {
  box-shadow: inset 0 0 0 1px var(--color-action-primary), var(--shadow-elevation-1);
  color: var(--color-action-primary);
}
.cw-btn--secondary.cw-btn--outline:active { box-shadow: inset 0 0 0 1px var(--color-action-primary); }
.cw-btn--secondary.cw-btn--outline:disabled { box-shadow: inset 0 0 0 1px var(--color-border-subtle); color: var(--color-text-disabled); }

.cw-btn--tertiary { background: transparent; color: var(--color-text-link); }
.cw-btn--tertiary:hover { background: var(--color-state-hover); }
.cw-btn--tertiary:active { background: var(--color-state-pressed); }

.cw-btn--danger { background: var(--color-action-danger); color: var(--color-text-on-danger); }
.cw-btn--danger:hover { background: linear-gradient(var(--color-state-hover), var(--color-state-hover)), var(--color-action-danger); }
.cw-btn--danger:active { background: var(--color-action-danger-pressed); }

.cw-btn:disabled,
.cw-btn[aria-disabled="true"] { cursor: not-allowed; color: var(--color-text-disabled); }
.cw-btn--primary:disabled, .cw-btn--primary[aria-disabled="true"],
.cw-btn--danger:disabled, .cw-btn--danger[aria-disabled="true"] { background: var(--color-action-disabled); }
.cw-btn--secondary:disabled, .cw-btn--secondary[aria-disabled="true"] {
  background: var(--color-bg-surface);
  box-shadow: inset 0 0 0 1px var(--color-border-subtle);   /* flat: no elevation */
}
.cw-btn--tertiary:disabled, .cw-btn--tertiary[aria-disabled="true"] { background: transparent; }
.cw-btn:disabled .cw-icon, .cw-btn[aria-disabled="true"] .cw-icon { opacity: .38; }
/* A secondary keeps its edge; the keyboard focus ring replaces its shadow. */
.cw-btn--secondary:focus-visible { box-shadow: inset 0 0 0 1px var(--color-border-strong), var(--shadow-focus-ring); }

/* Icon button: square, radius md, the icon 24. */
.cw-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--size-control-md);
  height: var(--size-control-md);
  padding: 0;
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--color-icon-primary);
  text-decoration: none;
  transition: background-color var(--duration-state) var(--ease-state);
}
.cw-icon-btn .cw-icon { width: var(--size-icon); height: var(--size-icon); }
.cw-icon-btn--sm { width: var(--size-target-min); height: var(--size-target-min); }
.cw-icon-btn:hover { background: var(--color-state-hover); text-decoration: none; }
.cw-icon-btn:active { background: var(--color-state-pressed); }
/* Outline: as Secondary since v0.63 — the fields' edge, raised on elevation-1. */
.cw-icon-btn--outline { background: var(--color-bg-surface); box-shadow: inset 0 0 0 1px var(--color-border-strong), var(--shadow-elevation-1); }
.cw-icon-btn--outline:hover { background: linear-gradient(var(--color-state-hover), var(--color-state-hover)), var(--color-bg-surface); }
.cw-icon-btn--outline:active { background: linear-gradient(var(--color-state-pressed), var(--color-state-pressed)), var(--color-bg-surface); box-shadow: inset 0 0 0 1px var(--color-border-strong); }
.cw-icon-btn--outline:focus-visible { box-shadow: inset 0 0 0 1px var(--color-border-strong), var(--shadow-focus-ring); }
.cw-icon-btn--filled { background: var(--color-bg-surface-secondary); }
.cw-icon-btn--filled:hover { background: linear-gradient(var(--color-state-hover), var(--color-state-hover)), var(--color-bg-surface-secondary); }
.cw-icon-btn--row { color: var(--color-icon-secondary); }
.cw-icon-btn:disabled, .cw-icon-btn[aria-disabled="true"] { cursor: not-allowed; background: transparent; }
.cw-icon-btn:disabled .cw-icon, .cw-icon-btn[aria-disabled="true"] .cw-icon { opacity: .38; }
.cw-icon-btn--outline:disabled,
.cw-icon-btn--outline[aria-disabled="true"] { background: var(--color-bg-surface); box-shadow: inset 0 0 0 1px var(--color-border-subtle); }

/* Text action: label-s in text/link, plain at rest and on focus, underlined on
   hover only (v0.64); its target reaches 44 tall (13 above and below its 18
   line, 8 to each side) without moving anything. */
.cw-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-02);
  padding: 0;
  border: 0;
  border-radius: var(--radius-xs);
  background: none;
  font: var(--type-label-s);
  letter-spacing: var(--type-label-s-tracking);
  color: var(--color-text-link);
  text-align: start;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}
.cw-link::after { content: ""; position: absolute; inset: -13px -8px; }
.cw-link:hover { text-decoration: underline; text-underline-offset: 2px; }
.cw-link .cw-icon { color: var(--color-icon-link); }
.cw-link--danger, .cw-link--danger .cw-icon { color: var(--color-text-danger); }
.cw-link:disabled, .cw-link[aria-disabled="true"] { color: var(--color-text-disabled); cursor: not-allowed; text-decoration: none; }
.cw-link:disabled .cw-icon { color: var(--color-icon-disabled); }
/* In running text a link is not a control: no padding box, no nowrap. */
.cw-link--inline { display: inline; font: inherit; white-space: normal; }
.cw-link--inline::after { content: none; }

/* A row of text actions or buttons. */
.cw-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--spacing-04); }
.cw-actions--end { justify-content: flex-end; }
.cw-actions--between { justify-content: space-between; }
.cw-actions--links { gap: var(--spacing-05); }
.cw-actions form { display: contents; }


/* ============================================================================
 * 2. INPUTS — Field, Select, Text area, Form field, Checkbox, Segmented (02 §2)
 * ========================================================================== */

/* Form field: label, 8, control, 8, message. */
.cw-form-field { display: flex; flex-direction: column; gap: var(--spacing-03); min-width: 0; }
.cw-label {
  display: block;
  font: var(--type-label-s);
  letter-spacing: var(--type-label-s-tracking);
  color: var(--color-text-secondary);
}
.cw-label__optional { font-weight: inherit; }
.cw-required { color: var(--color-text-danger); }
.cw-message { margin: 0; font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-message--error { color: var(--color-text-danger); }
.cw-form-field--disabled .cw-label,
.cw-form-field--disabled .cw-message { color: var(--color-text-disabled); }

/* Field: 40 (size/control-md), 16 start, 12 end, the border drawn inside. */
.cw-input,
.cw-select,
.cw-textarea {
  display: block;
  width: 100%;
  min-width: 0;
  min-height: var(--size-control-md);
  margin: 0;
  padding: 0 var(--spacing-04) 0 var(--spacing-05);
  border: 0;
  border-radius: var(--radius-md);
  background: var(--color-bg-surface);
  box-shadow: inset 0 0 0 1px var(--color-border-strong);
  font: var(--type-body);
  color: var(--color-text-primary);
  outline: 2px solid transparent;
  transition: box-shadow var(--duration-state) var(--ease-state);
}
.cw-input::placeholder,
.cw-textarea::placeholder { color: var(--color-text-tertiary); opacity: 1; }
.cw-input:focus,
.cw-select:focus,
.cw-textarea:focus,
.cw-input:focus-visible,
.cw-select:focus-visible,
.cw-textarea:focus-visible { box-shadow: inset 0 0 0 2px var(--color-border-focus); }
.cw-input[aria-invalid="true"],
.cw-select[aria-invalid="true"],
.cw-textarea[aria-invalid="true"] { box-shadow: inset 0 0 0 2px var(--color-status-stopped); }
.cw-input:disabled,
.cw-select:disabled,
.cw-textarea:disabled,
.cw-input[readonly].cw-input--readonly {
  background: var(--color-bg-surface-secondary);
  box-shadow: inset 0 0 0 1px var(--color-border-subtle);
  color: var(--color-text-disabled);
  cursor: not-allowed;
}
.cw-input--mono { font: var(--type-mono-m); font-variant-numeric: tabular-nums; }
.cw-input--sm { min-height: 40px; }
/* Field Compact (v0.62): 32 on size/control-sm, ONLY for an input inside a table
   row, so the row stays 40. 12 start, 8 end, radius sm; body-s, or mono-s for
   codes and figures. A number aligns right, like its column, and is 112 wide —
   every grid input on s-07 measures 112 (x 387–499, 684–796). */
.cw-input--compact,
.cw-select--compact {
  min-height: var(--size-control-sm);
  height: var(--size-control-sm);
  padding: 0 var(--spacing-03) 0 var(--spacing-04);
  border-radius: var(--radius-sm);
  font: var(--type-body-s);
}
.cw-input--compact.cw-input--mono,
input[type="number"].cw-input--compact { font: var(--type-mono-s); font-variant-numeric: tabular-nums; }
.cw-input--num { text-align: end; }
.cw-input--112 { width: 112px; }
/* v0.77 ad-28: a quantity in a table row (x 1111–1213 of a 2000-wide drawing of a
   2880 screen — 72 at 1x), set like text, under its left-aligned header. */
.cw-input--72 { width: 72px; }
input[type="number"].cw-input { font-variant-numeric: tabular-nums; }
input[type="date"].cw-input { font-family: var(--font-sans); }

.cw-textarea {
  min-height: 120px;
  padding: var(--spacing-04) var(--spacing-05);
  resize: vertical;
  field-sizing: content;
}
.cw-textarea--short { min-height: 72px; }

/* A field with an icon at either end: search at the start, a calendar or the
   password eye at the end. The icon is 24 in icon/secondary. */
.cw-field { position: relative; display: block; min-width: 0; }
.cw-field > .cw-icon {
  position: absolute;
  top: 50%;
  width: var(--size-icon);
  height: var(--size-icon);
  margin-top: calc(var(--size-icon) / -2);
  color: var(--color-icon-secondary);
  pointer-events: none;
}
.cw-field--start > .cw-icon { inset-inline-start: var(--spacing-04); }
.cw-field--start > .cw-input { padding-inline-start: calc(var(--spacing-04) + var(--size-icon) + var(--spacing-04)); }
.cw-field--end > .cw-icon { inset-inline-end: var(--spacing-04); }
.cw-field--end > .cw-input { padding-inline-end: calc(var(--spacing-04) + var(--size-icon) + var(--spacing-04)); }
/* The password eye: a 32 button 8 in from the end, so its 24 icon sits 12 from
   the edge like any end icon (s-32), inside the 40 field and the 48 sign-in one. */
.cw-field__button {
  position: absolute;
  top: 50%;
  inset-inline-end: var(--spacing-03);
  transform: translateY(-50%);
  width: 32px;
  height: 32px;
  border-radius: var(--radius-sm);
}
.cw-field--password > .cw-input { padding-inline-end: 52px; }
/* The date field (the owner, 29 Sep 2026: "Build the design's field"): the
   script's text field over the native date input, reading "24 Sep 2026", its
   calendar button where the password eye sits. */
.cw-field--date > .cw-input { padding-inline-end: 52px; }

/* Select: a native select dressed as a Field, the chevron drawn over it. */
.cw-select-wrap { position: relative; display: block; min-width: 0; }
.cw-select {
  appearance: none;
  -webkit-appearance: none;
  padding-inline-end: calc(var(--spacing-04) + var(--size-icon) + var(--spacing-03));
  text-overflow: ellipsis;
  cursor: pointer;
}
.cw-select-wrap > .cw-icon {
  position: absolute;
  top: 50%;
  inset-inline-end: var(--spacing-04);
  width: var(--size-icon);
  height: var(--size-icon);
  margin-top: calc(var(--size-icon) / -2);
  color: var(--color-icon-primary);
  pointer-events: none;
}
.cw-select:disabled + .cw-icon { color: var(--color-icon-disabled); }
.cw-select--mono { font: var(--type-mono-m); }
/* An unchosen value reads as a placeholder. */
.cw-select.is-placeholder { color: var(--color-text-tertiary); }
/* Disabled wins over the placeholder, and Chrome's own sheet dims a disabled
   <select> to 0.7 on top of our colour: reset it, so it reads as drawn (s-06:
   text/disabled, 168, not 193). */
.cw-select:disabled,
.cw-select.is-placeholder:disabled { color: var(--color-text-disabled); opacity: 1; }
.cw-select option { color: var(--color-text-primary); }

/* Checkbox: 20 box, radius xs, 1.5 ring; the whole 44 row is the target — 44 at
   the Desktop density too (01 §9 Targets: "Checkbox rows are 44 tall"). */
.cw-check {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-04);
  min-height: 44px;
  font: var(--type-body);
  color: var(--color-text-primary);
  cursor: pointer;
}
.cw-check--start { align-items: flex-start; }
.cw-check--start .cw-checkbox { margin-top: 2px; }
.cw-checkbox {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  flex: none;
  width: 20px;
  height: 20px;
  margin: 0;
  border-radius: var(--radius-xs);
  background: var(--color-bg-surface);
  box-shadow: inset 0 0 0 1.5px var(--color-border-strong);
  cursor: pointer;
  transition: background-color var(--duration-state) var(--ease-state);
}
.cw-checkbox::before {
  content: "";
  position: absolute;
  inset: 2px;
  background: var(--color-icon-on-inverse);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center / 16px 16px no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center / 16px 16px no-repeat;
  opacity: 0;
}
.cw-checkbox:checked,
.cw-checkbox:indeterminate { background: var(--color-action-brand); box-shadow: none; }
.cw-checkbox:checked::before { opacity: 1; }
.cw-checkbox:indeterminate::before {
  opacity: 1;
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round'%3E%3Cpath d='M5 12h14'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round'%3E%3Cpath d='M5 12h14'/%3E%3C/svg%3E");
}
.cw-checkbox:focus-visible { box-shadow: inset 0 0 0 1.5px var(--color-border-strong), var(--shadow-focus-ring); }
.cw-checkbox:checked:focus-visible { box-shadow: var(--shadow-focus-ring); }
.cw-checkbox:disabled { background: var(--color-bg-surface-secondary); box-shadow: inset 0 0 0 1.5px var(--color-border-subtle); cursor: not-allowed; }
.cw-checkbox:disabled:checked { background: var(--color-action-disabled); }
.cw-check:has(.cw-checkbox:disabled) { color: var(--color-text-disabled); cursor: not-allowed; }
.cw-check__text { display: flex; flex-direction: column; gap: var(--spacing-01); }
.cw-check__hint { font: var(--type-body-s); color: var(--color-text-secondary); }

/* Radio (v0.61): a 20 ring, 1.5 border/strong on bg/surface; selected is the ring
   in border/selected round a 10 dot in action/brand, as in the Radio tile. Always
   inside a Radio group, whose 44 rows are the target. */
.cw-radio {
  appearance: none;
  -webkit-appearance: none;
  flex: none;
  width: 20px;
  height: 20px;
  margin: 0;
  border-radius: var(--radius-full);
  background: var(--color-bg-surface);
  box-shadow: inset 0 0 0 1.5px var(--color-border-strong);
  cursor: pointer;
}
.cw-radio:checked { box-shadow: inset 0 0 0 2px var(--color-border-selected), inset 0 0 0 5px var(--color-bg-surface); background: var(--color-action-brand); }
.cw-radio:focus-visible { box-shadow: inset 0 0 0 1.5px var(--color-border-strong), var(--shadow-focus-ring); }
.cw-radio:checked:focus-visible { box-shadow: inset 0 0 0 2px var(--color-border-selected), inset 0 0 0 5px var(--color-bg-surface), var(--shadow-focus-ring); }
.cw-radio:disabled { cursor: not-allowed; background: var(--color-bg-surface-secondary); box-shadow: inset 0 0 0 1.5px var(--color-border-subtle); }
/* A disabled one that is selected keeps its dot, in icon/disabled. */
.cw-radio:disabled:checked { background: var(--color-icon-disabled); box-shadow: inset 0 0 0 1.5px var(--color-border-subtle), inset 0 0 0 5px var(--color-bg-surface-secondary); }
.cw-radio:not(:disabled):hover { background: linear-gradient(var(--color-state-hover), var(--color-state-hover)), var(--color-bg-surface); }
.cw-radio:checked:not(:disabled):hover { background: var(--color-action-brand); }
.cw-check:has(.cw-radio:disabled) { color: var(--color-text-disabled); cursor: not-allowed; }

/* Radio group (v0.61): a fieldset whose legend is the label, the options in a
   row 24 apart, wrapping, and one message line. The 44 rows sit straight under
   the label and over the message (s-07: label 332–350, row 350–394, message
   394–414). A fixed choice shows the chosen option selected and the rest
   disabled, with the reason in the message. */
.cw-radio-group { min-width: 0; margin: 0; padding: 0; border: 0; }
.cw-radio-group > legend { float: left; width: 100%; margin: 0; padding: 0; }
.cw-radio-group > legend + * { clear: both; }
.cw-radio-group__options { display: flex; flex-wrap: wrap; gap: 0 var(--spacing-06); }
.cw-radio-group .cw-message { margin: 0; }

/* Segmented: views of one content. Links, so every view has a URL. The one
   control that keeps its desktop size in the screens: 48 track, 40 segments. */
.cw-segmented {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-02);
  min-height: 48px;
  padding: var(--spacing-02);
  border-radius: var(--radius-md);
  background: var(--color-bg-surface-tertiary);
}
.cw-segment {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding: 0 var(--spacing-05);
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  font: var(--type-label-s);
  color: var(--color-text-secondary);
  text-decoration: none;
  white-space: nowrap;
  transition: background-color var(--duration-state) var(--ease-state), color var(--duration-state) var(--ease-state);
}
.cw-segment:hover { background: var(--color-state-hover); text-decoration: none; }
.cw-segment[aria-current="page"],
.cw-segment[aria-current="true"],
.cw-segment[aria-pressed="true"] {
  background: var(--color-bg-surface);
  box-shadow: var(--shadow-elevation-1);
  color: var(--color-text-primary);
}


/* ============================================================================
 * 3. STATUS AND TAGS — Status, Badge, Tag (02 §3). Avatar is in the shell.
 * ========================================================================== */

/* Status: a dot and an ink word. The tone lives in the dot and the pill's wash. */
.cw-status {
  --cw-dot: var(--color-status-offline);
  --cw-wash: var(--color-bg-surface-secondary);
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-03);
  font: var(--type-body-s-strong);
  color: var(--color-text-primary);
  white-space: nowrap;
}
.cw-status::before {
  content: "";
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--cw-dot);
}
.cw-status--ready { --cw-dot: var(--color-status-ready); --cw-wash: var(--color-wash-success); }
.cw-status--working { --cw-dot: var(--color-status-working); --cw-wash: var(--color-wash-info); }
.cw-status--attention { --cw-dot: var(--color-status-attention); --cw-wash: var(--color-wash-warning); }
.cw-status--stopped { --cw-dot: var(--color-status-stopped); --cw-wash: var(--color-wash-danger); }
.cw-status--offline { --cw-dot: var(--color-status-offline); --cw-wash: var(--color-bg-surface-secondary); }
.cw-status--neutral { --cw-dot: var(--color-icon-secondary); --cw-wash: var(--color-bg-surface-secondary); }
.cw-status--pill {
  min-height: 28px;
  padding: 0 var(--spacing-04);
  border-radius: var(--radius-full);
  background: var(--cw-wash);
  font: var(--type-caption-strong);
}

/* Badge: a count of things waiting, never a total. */
.cw-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--radius-full);
  background: var(--color-action-brand);
  color: var(--color-text-on-inverse);
  font: var(--type-caption-strong);
  font-variant-numeric: tabular-nums;
}
.cw-badge--neutral { background: var(--color-bg-surface-tertiary); color: var(--color-text-primary); }
.cw-badge--danger { background: var(--color-action-danger); color: var(--color-text-on-danger); }

/* Tag: a KIND of thing, not a state. Ink on a wash, radius xs. */
.cw-tag {
  display: inline-flex;
  align-items: center;
  flex: none;
  min-height: 24px;
  padding: 0 var(--spacing-03);
  border-radius: var(--radius-xs);
  background: var(--color-bg-surface-tertiary);
  color: var(--color-text-primary);
  font: var(--type-caption-strong);
  white-space: nowrap;
  vertical-align: middle;
}
.cw-tag--info { background: var(--color-wash-info); }
.cw-tag--success { background: var(--color-wash-success); }
.cw-tag--warning { background: var(--color-wash-warning); }
.cw-tag--danger { background: var(--color-wash-danger); }


/* ============================================================================
 * 4. FEEDBACK AND CONTENT — Banner, Toast, Empty state, Row, Section header,
 *    Attention item, Figure (02 §4)
 * ========================================================================== */

/* Banner: the wash and the icon carry the tone; no border, no shadow. */
.cw-banner {
  --cw-tone: var(--color-status-working);
  display: flex;
  align-items: flex-start;
  gap: var(--spacing-04);
  padding: var(--spacing-05);
  border-radius: var(--radius-md);
  background: var(--color-wash-info);
  color: var(--color-text-primary);
}
.cw-banner--success { --cw-tone: var(--color-status-ready); background: var(--color-wash-success); }
.cw-banner--warning { --cw-tone: var(--color-status-attention); background: var(--color-wash-warning); }
.cw-banner--danger { --cw-tone: var(--color-status-stopped); background: var(--color-wash-danger); }
.cw-banner__icon { color: var(--cw-tone); }
.cw-banner__main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--spacing-02); }
.cw-banner__head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--spacing-05); }
.cw-banner__title { margin: 0; font: var(--type-body-s-strong); }
.cw-banner__body { margin: 0; font: var(--type-body-s); }
.cw-banner__body + .cw-banner__body { margin-top: var(--spacing-01); }
.cw-banner__body ul, .cw-banner__main ul { margin: 0; padding-inline-start: var(--spacing-06); font: var(--type-body-s); }
.cw-banner__links { display: flex; flex-wrap: wrap; gap: var(--spacing-02) 20px; margin-top: var(--spacing-02); }
.cw-banner__links form { display: contents; }
.cw-banner__detail summary {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-02);
  font: var(--type-body-s-strong);
  cursor: pointer;
  list-style: none;
}
.cw-banner__detail summary::-webkit-details-marker { display: none; }
.cw-banner__detail summary .cw-icon { width: 16px; height: 16px; transition: transform var(--duration-state) var(--ease-state); }
.cw-banner__detail[open] summary .cw-icon { transform: rotate(90deg); }
.cw-banner__detail pre,
.cw-banner__detail code { display: block; margin: var(--spacing-03) 0 0; white-space: pre-wrap; word-break: break-word; font: var(--type-mono-xs); }
.cw-banner__close { margin: -10px -10px -10px 0; }

/* Toast: bottom centre, inverse; announced politely. Failures are banners. */
.cw-toast-region {
  position: fixed;
  inset-inline: 0;
  bottom: var(--spacing-06);
  z-index: 50;
  display: flex;
  justify-content: center;
  pointer-events: none;
}
.cw-toast {
  display: flex;
  align-items: center;
  gap: var(--spacing-05);
  min-height: 52px;
  min-width: 300px;
  max-width: 480px;
  padding: var(--spacing-04) var(--spacing-05);
  border-radius: var(--radius-md);
  background: var(--color-bg-inverse);
  color: var(--color-text-on-inverse);
  font: var(--type-body-s-strong);
  box-shadow: var(--shadow-elevation-2);
  pointer-events: auto;
  animation: cw-toast-in var(--duration-enter) var(--ease-enter) both;
}
.cw-toast .cw-icon { color: var(--color-icon-on-inverse); }
.cw-toast__message { flex: 1 1 auto; }
.cw-toast__action { color: var(--color-text-link-on-inverse); font: var(--type-label-s); }
.cw-toast.is-leaving { animation: cw-toast-out var(--duration-exit) var(--ease-exit) both; }
@keyframes cw-toast-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@keyframes cw-toast-out { from { opacity: 1; } to { opacity: 0; transform: translateY(8px); } }

/* Empty state: what this place is and the next step, centred in its card. */
.cw-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: var(--spacing-07) var(--spacing-06);
  text-align: center;
}
.cw-empty__disc {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 64px;
  margin-bottom: var(--spacing-05);
  border-radius: var(--radius-full);
  background: var(--color-bg-surface-secondary);
  color: var(--color-icon-secondary);
}
.cw-empty__disc .cw-icon { width: 28px; height: 28px; }
.cw-empty__graphic { width: 96px; height: 96px; margin-bottom: var(--spacing-05); }
.cw-empty__title { margin: 0; font: var(--type-title-s); color: var(--color-text-primary); text-wrap: balance; }
.cw-empty__line { max-width: 400px; margin: var(--spacing-03) 0 0; font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-empty__action { margin-top: var(--spacing-06); display: flex; gap: var(--spacing-04); flex-wrap: wrap; justify-content: center; }
/* Before a report runs: in the table body under the header, disc off. */
.cw-empty--prerun { padding: 72px var(--spacing-06); }

/* Row: a fact as a row, label then value, a rule under each but the last. */
.cw-rows { margin: 0; padding: 0; }
.cw-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-04);
  min-height: 52px;
  padding: var(--spacing-03) 0;
  box-shadow: inset 0 -1px 0 var(--color-border-subtle);
}
.cw-row:last-child { box-shadow: none; }
.cw-row__label { flex: 1 1 auto; min-width: 0; margin: 0; font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-row__note { display: block; margin-top: var(--spacing-01); font: var(--type-caption); color: var(--color-text-tertiary); }
.cw-row__value {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: var(--spacing-03);
  margin: 0;
  font: var(--type-body-s-strong);
  color: var(--color-text-primary);
  text-align: end;
}
.cw-row__value--mono { font: var(--type-mono-s-strong); font-variant-numeric: tabular-nums; }
/* Compact, for dialogs: a 128 label column, the value after it. */
.cw-rows--compact .cw-row {
  display: grid;
  grid-template-columns: 128px minmax(0, 1fr);
  justify-content: stretch;
  min-height: 0;
  padding: var(--spacing-03) 0;
}
.cw-rows--compact .cw-row__value { justify-content: flex-start; text-align: start; }
/* Rows inside a padded card keep their own side inset. */
.cw-rows--inset { padding: 0 var(--spacing-06); }

/* Section header: the title of a group inside a card. */
.cw-section-head {
  display: flex;
  align-items: center;
  gap: var(--spacing-04);
  min-height: 24px;
}
.cw-section-head__title { margin: 0; font: var(--type-title-s); color: var(--color-text-primary); }
.cw-section-head__sub { font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-section-head__end { margin-inline-start: auto; display: flex; align-items: center; gap: var(--spacing-04); font: var(--type-body-s); color: var(--color-text-secondary); }

/* Attention item: one count that needs someone. The whole row is the link, and
   it renders only when the count is not zero. The rule is inset 24 each side. */
.cw-attention { margin: 0; padding: 0 0 var(--spacing-03); list-style: none; }
.cw-attention__item {
  display: flex;
  align-items: center;
  gap: var(--spacing-04);
  min-height: 48px;
  margin: 0 var(--spacing-06);
  box-shadow: inset 0 -1px 0 var(--color-border-subtle);
  color: var(--color-text-primary);
  font: var(--type-body-s);
  text-decoration: none;
}
.cw-attention li:last-child .cw-attention__item { box-shadow: none; }
.cw-attention__item:hover { text-decoration: none; background: var(--color-state-hover); }
.cw-attention__item .cw-icon:first-child { color: var(--cw-tone, var(--color-status-working)); }
.cw-attention__item--danger { --cw-tone: var(--color-status-stopped); }
.cw-attention__item--warning { --cw-tone: var(--color-status-attention); }
.cw-attention__item--info { --cw-tone: var(--color-status-working); }
.cw-attention__count { font: var(--type-mono-s-strong); font-variant-numeric: tabular-nums; }
.cw-attention__words { flex: 1 1 auto; min-width: 0; }
.cw-attention__window { font: var(--type-caption); color: var(--color-text-tertiary); white-space: nowrap; }
.cw-attention__item > .cw-icon:last-child { color: var(--color-icon-secondary); }

/* Figure: a label over one ink number, side by side in one card, split by rules. */
.cw-figures { display: flex; align-items: stretch; }
.cw-figure {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-02);
  padding: var(--spacing-05) var(--spacing-06);
  box-shadow: inset 1px 0 0 var(--color-border-subtle);
}
.cw-figure:first-child { box-shadow: none; }
.cw-figures--hug .cw-figure { flex: 0 0 300px; }
.cw-figure__label { margin: 0; font: var(--type-caption-strong); color: var(--color-text-secondary); }
.cw-figure__value {
  margin: 0;
  font: var(--type-title-l);
  letter-spacing: var(--type-title-l-tracking);
  color: var(--color-text-primary);
  font-variant-numeric: tabular-nums;
}
.cw-figure__value a { color: var(--color-text-link); }
.cw-figure__note { margin: 0; font: var(--type-caption); color: var(--color-text-secondary); }
/* The first cell names the period: a title and its date line. */
.cw-figure--period { justify-content: center; }
.cw-figure--period .cw-figure__title { margin: 0; font: var(--type-title-s); color: var(--color-text-primary); }
.cw-figure--period .cw-figure__when { margin: 0; font: var(--type-body-s); color: var(--color-text-secondary); }


/* ============================================================================
 * 5. TABLES, PAGINATION, MENUS, DIALOGS, FILTERS (02 §5, 03 §4–5)
 * ========================================================================== */

/* The wrapper scrolls sideways when the table is wider than its card. While it
   fits, cartonwise-portal.js marks it `is-fit` so the header can stick under the
   page bar (an element that scrolls on either axis traps a sticky header). */
.cw-table-wrap { overflow-x: auto; }
.cw-table-wrap.is-fit { overflow: visible; }

.cw-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font: var(--type-body-s);
  color: var(--color-text-primary);
  font-variant-numeric: tabular-nums;
}
.cw-table caption { caption-side: top; text-align: start; }
.cw-table th,
.cw-table td {
  height: var(--size-table-row);
  padding: var(--spacing-03) 20px;
  text-align: start;
  vertical-align: middle;
  white-space: nowrap;          /* values never wrap (03 §4) */
}
/* A LIST (v0.62, 03 §4): each column as wide as its widest value, 40 between
   neighbours — 20 inline padding a side — so a row reads as one record; the
   spare width goes to the LAST data column (`cw-fill` on its <th>), after its
   value, so the actions keep the right edge. When that column holds figures, an
   empty `cw-spare` cell after it takes the spare instead. s-04: columns start
   288.5 / 404.5 / 546 / 654.5 / 776, each 40–42 after the widest value before it.
   A REPORT hugs its columns at 12 a side (8 when dense) and gives the rest to its
   words column. */
.cw-table--report th,
.cw-table--report td { padding-inline: var(--spacing-04); }
/* Row actions in fixed slots, one cell each, 16 apart (s-04: View 1260–1291,
   Edit 1309, Delete 1351–1392 — the last ends 24 in, like every last cell). */
.cw-table td.cw-actions-cell { padding-inline: var(--spacing-03); }
.cw-table .cw-spare { width: 100%; padding: 0; }
.cw-table th {
  position: sticky;
  top: var(--cw-pagebar-h);
  z-index: 1;
  background: var(--color-bg-surface-secondary);
  box-shadow: inset 0 -1px 0 var(--color-border-default);
  font: var(--type-caption-strong);
  color: var(--color-text-secondary);
  white-space: nowrap;
}
.cw-table-wrap:not(.is-fit) .cw-table th { position: static; }
.cw-table td { box-shadow: inset 0 -1px 0 var(--color-border-subtle); }
.cw-table tbody tr:last-child > td { box-shadow: none; }
.cw-table tbody tr:hover > td { background: var(--color-state-hover); }
.cw-table tr > :first-child { padding-inline-start: var(--spacing-06); }
.cw-table tr > :last-child { padding-inline-end: var(--spacing-06); }
.cw-table tr > .cw-spare:last-child { padding: 0; }
.cw-table .cw-num,
.cw-table th.cw-num,
.cw-table .is-num { text-align: end; }
.cw-table td.cw-num,
.cw-table td.is-num { font: var(--type-mono-s); font-variant-numeric: tabular-nums; }
.cw-table td.cw-key { font: var(--type-mono-s-strong); font-variant-numeric: tabular-nums; }
.cw-table td.cw-code { font: var(--type-mono-s); font-variant-numeric: tabular-nums; }
.cw-table td.cw-strong,
.cw-table td .cw-strong { font: var(--type-body-s-strong); }
/* LONG TEXT WRAPS — a description, an address, a note (the owner, 29 Sep 2026,
   for these columns alone; 03 §4's "values never wrap" holds for the rest:
   figures, codes, dates and states stay on one line). The column that wraps also
   takes the spare (`cw-fill` on its <th>), so it wraps only when the row would not
   fit otherwise; beside another `cw-fill` it would be squeezed to its min-width
   and wrap at every word. `anywhere` lets an unbroken run — a URL, a pasted
   code — wrap too, rather than widening the column. */
.cw-table td.cw-wrap,
.cw-table th.cw-wrap { white-space: normal; min-width: 200px; overflow-wrap: anywhere; }
.cw-table .cw-fill { width: 100%; }                      /* the one words column that takes the rest */
.cw-table .cw-shrink { width: 1%; white-space: nowrap; }
.cw-table td .cw-cell-sub { display: block; margin-top: var(--spacing-01); font: var(--type-caption); color: var(--color-text-secondary); }   /* 8 + 20 + 2 + 18 + 8: the two-line row is 56 (02 §5) */
.cw-table td.cw-actions-cell { width: 1%; white-space: nowrap; }
/* A row icon button sits inside the 40 row: its cell gives up its padding. */
.cw-table td.cw-actions-cell:has(> .cw-icon-btn),
.cw-table td:has(> .cw-icon-btn--row:only-child) { padding-block: 0; }
.cw-table td.cw-actions-cell .cw-actions { flex-wrap: nowrap; gap: var(--spacing-05); justify-content: flex-end; }
.cw-table td .cw-status--pill { margin-block: -6px; }
.cw-table tr.is-selected > td { background: var(--color-state-selected); }
.cw-table tr.is-flagged > td { background: var(--color-wash-danger); }
.cw-table tr.is-warning > td { background: var(--color-wash-warning); }
.cw-table tr.is-muted > td { color: var(--color-text-tertiary); }

/* Sortable header: a link, done on the server. The sorted one turns ink. */
.cw-sort {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-02);
  color: inherit;
  text-decoration: none;
}
.cw-sort:hover { color: var(--color-text-primary); text-decoration: none; }
.cw-sort .cw-icon { width: 16px; height: 16px; }
th[aria-sort] { color: var(--color-text-primary); }

/* Dense: ten or more columns pad 8 at the sides; the outer cells keep 24. */
.cw-table--dense th,
.cw-table--dense td { padding-inline: var(--spacing-03); }
.cw-table--dense tr > :first-child { padding-inline-start: var(--spacing-06); }
.cw-table--dense tr > :last-child { padding-inline-end: var(--spacing-06); }
/* A cell with an input pads 4 and holds a Compact field, so its row stays 40
   (v0.62). */
.cw-table td.cw-input-cell { padding-block: var(--spacing-02); }

/* Report: lanes and bands. A rule ends every column; no row dividers; every
   second row on the secondary surface; the header band tertiary. */
.cw-table--report th { background: var(--color-bg-surface-tertiary); box-shadow: inset -1px -1px 0 var(--color-border-default); }
.cw-table--report td { box-shadow: inset -1px 0 0 var(--color-border-subtle); }
.cw-table--report tr > :last-child { box-shadow: none; }
.cw-table--report th:last-child { box-shadow: inset 0 -1px 0 var(--color-border-default); }
.cw-table--report tbody tr:nth-child(even) > td { background: var(--color-bg-surface-secondary); }
.cw-table--report tbody tr:hover > td { background: linear-gradient(var(--color-state-hover), var(--color-state-hover)), var(--color-bg-surface); }
.cw-table--report tbody tr:nth-child(even):hover > td { background: linear-gradient(var(--color-state-hover), var(--color-state-hover)), var(--color-bg-surface-secondary); }
.cw-table--report tbody tr.is-flagged > td { background: var(--color-wash-danger); }
.cw-table--report tbody tr.is-warning > td { background: var(--color-wash-warning); }
.cw-table--report th.cw-group { text-align: center; box-shadow: inset -1px -1px 0 var(--color-border-default); }
.cw-table--report th.cw-group-edge,
.cw-table--report td.cw-group-edge { box-shadow: inset -1px 0 0 var(--color-border-default); }
.cw-table--report th.cw-group-edge { box-shadow: inset -1px -1px 0 var(--color-border-default); }
.cw-table--report .cw-rownum { text-align: end; color: var(--color-text-secondary); width: 1%; }
/* An empty report cell draws an en dash in text/tertiary (03 §8; the owner, 29 Sep
   2026, README Still open #8). DRAWN, NOT PRINTED: the HTML keeps the old
   reports' empty cell, so a table copied into a spreadsheet still pastes an empty
   cell, not a dash. `:empty` needs the cell TRULY empty — whitespace defeats it —
   and `ReportEmptyCellTest` fails on a report cell that holds only whitespace. */
.cw-table--report tbody td:empty::before { content: "–"; color: var(--color-text-tertiary); }
/* A report's second header row sits under the first: offset its stickiness. */
.cw-table thead tr + tr th { top: calc(var(--cw-pagebar-h) + var(--size-table-row)); }
.cw-table tfoot td { font: var(--type-body-s-strong); background: var(--color-bg-surface-secondary); box-shadow: inset 0 1px 0 var(--color-border-default); }
.cw-table--report tfoot td { box-shadow: inset -1px 1px 0 var(--color-border-default); }
.cw-table--report tfoot td:last-child { box-shadow: inset 0 1px 0 var(--color-border-default); }

/* The empty body of a report before it runs, or a filter with no results. */
.cw-table td.cw-table-empty { height: auto; padding: 0; background: none; }
.cw-table tbody tr:hover > td.cw-table-empty { background: none; }

/* Pagination: the foot of a list card. The range stays when every row fits. */
.cw-pager {
  display: flex;
  align-items: center;
  gap: var(--spacing-03);
  min-height: calc(var(--size-target-min) + 12px);
  padding: 6px var(--spacing-03) 6px var(--spacing-06);
  box-shadow: inset 0 1px 0 var(--color-border-subtle);
}
.cw-pager__range { flex: 1 1 auto; margin: 0; font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-pager__range strong { font-weight: 400; color: inherit; }
.cw-pager .cw-btn--tertiary[aria-disabled="true"] { color: var(--color-text-disabled); pointer-events: none; }

/* Filter bar: the top of a list or report card, labels above the controls.
 * v0.69 (3 Oct 2026, the hand-off's filter-bars.png): every list and report keeps
 * its filters, its button and Clear on ONE row at 1440 — 16 top and bottom, 24
 * sides, fields 12 apart, a border/subtle rule under it, 94 high (16 + an 18
 * label + 8 + a 36 control + 16). Narrower, it wraps 12 apart, and the last field
 * keeps the buttons: they are one item, `.cw-filters__tail`, so a button never
 * stands on a row of its own (03 §5).
 */
.cw-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--spacing-04);
  padding: var(--spacing-05) var(--spacing-06);
  box-shadow: inset 0 -1px 0 var(--color-border-subtle);
  background: var(--color-bg-surface);
}
.cw-filters--sticky { position: sticky; top: var(--cw-pagebar-h); z-index: 2; }
.cw-filter { display: flex; flex-direction: column; gap: var(--spacing-03); flex: none; width: 200px; }
/* Widths, measured on the longest value, never the placeholder (v0.69). */
.cw-filter--72 { width: 72px; }     /* a number */
.cw-filter--96 { width: 96px; }     /* a short choice */
.cw-filter--112 { width: 112px; }   /* a code, an ID, or Active and Inactive */
.cw-filter--144 { width: 144px; }   /* a part-of search, a date, a choice of a word or two */
.cw-filter--192 { width: 192px; }   /* a long choice: Awaiting approval, NEFT / bank transfer */
.cw-filter--232 { width: 232px; }   /* a date range */
.cw-filter--248 { width: 248px; }   /* a list of names */
.cw-filter--auto { width: auto; }
.cw-filters__tail { display: flex; align-items: flex-end; gap: var(--spacing-04); flex: none; }
.cw-filters__actions { display: flex; align-items: center; gap: var(--spacing-04); }
.cw-filters__note { flex-basis: 100%; margin: 0; font: var(--type-body-s); color: var(--color-text-secondary); }

/* A filter's control: 36 high (`--size-target-min`, the height of an S button),
   its value in body-s, a 20 icon, 12 start and 8 end, radius md — one size on
   every list and report. An end icon sits 8 in, its value stops 8 before it. */
.cw-filters .cw-input,
.cw-filters .cw-select {
  min-height: var(--size-target-min);
  height: var(--size-target-min);
  padding-inline: var(--spacing-04) var(--spacing-03);
  font: var(--type-body-s);
}
.cw-filters .cw-select,
.cw-filters .cw-combobox > .cw-input,
.cw-filters .cw-field--date > .cw-input { padding-inline-end: calc(var(--spacing-03) + var(--size-icon-sm) + var(--spacing-03)); }
.cw-filters .cw-select-wrap > .cw-icon,
.cw-filters .cw-combobox > .cw-icon {
  inset-inline-end: var(--spacing-03);
  width: var(--size-icon-sm);
  height: var(--size-icon-sm);
  margin-top: calc(var(--size-icon-sm) / -2);
}
/* The date field's calendar button: 28, 4 in from the end, so its 20 icon sits
   8 from the edge like the select's chevron. */
.cw-filters .cw-field__button {
  inset-inline-end: var(--spacing-02);
  width: 28px;
  height: 28px;
}
.cw-filters .cw-field__button > .cw-icon { width: var(--size-icon-sm); height: var(--size-icon-sm); }

/* Date range (v0.69, the BDC & POS audit report's From and To as one field): the
   two <input type="date">, `from` and `to`, in one bordered box with an en dash
   between, each named for a screen reader, both still required. Without the
   script they are the browser's own date inputs sharing the box; with it they
   become the date field's text inputs ("1 Sep 2026 – 26 Sep 2026", placeholder
   From – To), each as wide as its words, and one calendar at the end chooses
   the range (cartonwise-portal.js, `buildDateRange`). */
.cw-range {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--spacing-02);
  height: var(--size-target-min);
  padding-inline: var(--spacing-04) var(--spacing-03);
  border-radius: var(--radius-md);
  background: var(--color-bg-surface);
  box-shadow: inset 0 0 0 1px var(--color-border-strong);
  font: var(--type-body-s);
  color: var(--color-text-primary);
  transition: box-shadow var(--duration-state) var(--ease-state);
}
.cw-range:focus-within { box-shadow: inset 0 0 0 2px var(--color-border-focus); }
.cw-range__input {
  flex: 1 1 0;
  min-width: 0;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  font: inherit;
  font-family: var(--font-sans);
  color: inherit;
  outline: 0;
}
.cw-range__input::placeholder { color: var(--color-text-tertiary); opacity: 1; }
.cw-range__dash { flex: none; color: var(--color-text-primary); }
.cw-range:has(.cw-range__input:placeholder-shown) .cw-range__dash { color: var(--color-text-tertiary); }
.cw-range--built { padding-inline-end: calc(var(--spacing-03) + var(--size-icon-sm) + var(--spacing-03)); }
.cw-range--built > .cw-range__input { flex: none; }
.cw-range > .cw-field__button { inset-inline-end: var(--spacing-02); width: 28px; height: 28px; }
.cw-range > .cw-field__button > .cw-icon { width: var(--size-icon-sm); height: var(--size-icon-sm); }
/* The range in the calendar: its ends as a chosen day, the days between washed. */
.cw-calendar__day.is-in-range { background: var(--color-state-selected); }

/* Menu: over the page, opened from a button, aligned to its end. The native
   `popover` attribute puts it in the top layer; the script places it. */
.cw-menu {
  position: fixed;
  inset: auto;
  min-width: 240px;
  margin: 0;
  padding: var(--spacing-03);
  border: 0;
  border-radius: var(--radius-lg);
  background: var(--color-bg-surface);
  box-shadow: inset 0 0 0 1px var(--color-border-hairline), var(--shadow-elevation-2);
  color: var(--color-text-primary);
  overflow: visible;
}
.cw-menu:popover-open { animation: cw-pop-in var(--duration-enter) var(--ease-enter) both; }
.cw-menu__item {
  display: flex;
  align-items: center;
  gap: var(--spacing-04);
  width: 100%;
  min-height: 36px;
  padding: 0 var(--spacing-04);
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  font: var(--type-body-s);
  color: var(--color-text-primary);
  text-align: start;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}
.cw-menu__item .cw-icon { color: var(--color-icon-primary); }
.cw-menu__item:hover,
.cw-menu__item:focus-visible { background: var(--color-state-hover); text-decoration: none; }
.cw-menu__item--danger,
.cw-menu__item--danger .cw-icon { color: var(--color-text-danger); }
.cw-menu__divider { height: 1px; margin: var(--spacing-03) 0; background: var(--color-border-subtle); border: 0; }
.cw-menu__heading { display: flex; flex-direction: column; gap: var(--spacing-01); padding: var(--spacing-04); }
.cw-menu__heading-name { font: var(--type-body-s-strong); }
.cw-menu__heading-portal { font: var(--type-caption); color: var(--color-text-secondary); }
.cw-menu form { margin: 0; }
.cw-menu--account { width: 264px; }
@keyframes cw-pop-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }

/* Dialog: the native <dialog>, modal, over the scrim. */
.cw-dialog {
  width: min(480px, calc(100vw - 32px));
  max-height: calc(100vh - 48px);
  margin: auto;
  padding: var(--spacing-07);
  border: 0;
  border-radius: var(--radius-xl);
  background: var(--color-bg-surface);
  box-shadow: var(--shadow-elevation-3);
  color: var(--color-text-primary);
  overflow: auto;
}
.cw-dialog--wide { width: min(560px, calc(100vw - 32px)); }
.cw-dialog--xwide { width: min(720px, calc(100vw - 32px)); }
.cw-dialog::backdrop { background: var(--color-bg-scrim); }
.cw-dialog[open] { animation: cw-dialog-in var(--duration-enter) var(--ease-enter) both; }
@keyframes cw-dialog-in { from { opacity: 0; transform: translateY(8px) scale(.98); } to { opacity: 1; transform: none; } }
.cw-dialog__form { display: flex; flex-direction: column; gap: var(--spacing-05); margin: 0; }
.cw-dialog__inner { display: flex; flex-direction: column; gap: var(--spacing-05); }
.cw-dialog__heading { display: flex; flex-direction: column; gap: var(--spacing-02); }
.cw-dialog__eyebrow { margin: 0; font: var(--type-eyebrow); letter-spacing: var(--type-eyebrow-tracking); text-transform: uppercase; color: var(--color-text-tertiary); }
.cw-dialog__title { margin: 0; font: var(--type-title-l); letter-spacing: var(--type-title-l-tracking); color: var(--color-text-primary); text-wrap: balance; overflow-wrap: anywhere; }
.cw-dialog__body { margin: 0; font: var(--type-body); color: var(--color-text-secondary); }
.cw-dialog__actions {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--spacing-06);
  margin-top: var(--spacing-03);
}
.cw-dialog__actions > * { width: 100%; }
.cw-dialog__actions form { display: contents; }
/* v0.77: a label that names a customer ("Move to Benchmark Inspection Services")
 * can be wider than its half of the row. It wraps rather than being cut off; a
 * short one still sits on one line, and the grid keeps both buttons one height. */
.cw-dialog__actions .cw-btn { white-space: normal; padding-block: var(--spacing-02); }
/* A content dialog keeps its buttons at their own widths on the right. */
.cw-dialog__actions--content { display: flex; justify-content: flex-end; flex-wrap: wrap; gap: var(--spacing-04); }
.cw-dialog__actions--content > * { width: auto; }
.cw-dialog__actions--content .cw-dialog__actions-start { margin-inline-end: auto; }


/* ============================================================================
 * 7. STEPPER, RADIO TILE, PROGRESS STEP (02 §7)
 * ========================================================================== */

.cw-stepper {
  display: inline-flex;
  align-items: center;
  width: 152px;
  min-height: 40px;
  padding: 0 2px;
  border-radius: var(--radius-md);
  background: var(--color-bg-surface);
  box-shadow: inset 0 0 0 1px var(--color-border-default);
}
.cw-stepper .cw-icon-btn { width: 36px; height: 36px; }
.cw-stepper .cw-icon-btn .cw-icon { width: 20px; height: 20px; }
.cw-stepper__value {
  flex: 1 1 auto;
  min-width: 0;
  width: 100%;
  height: 36px;
  padding: 0;
  border: 0;
  background: transparent;
  font: var(--type-mono-m-strong);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-primary);
  text-align: center;
  -moz-appearance: textfield;
  appearance: textfield;
}
.cw-stepper__value::-webkit-outer-spin-button,
.cw-stepper__value::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.cw-stepper__value:focus-visible { box-shadow: none; outline: none; }
.cw-stepper:focus-within { box-shadow: inset 0 0 0 2px var(--color-border-focus); }
/* Its value refused: the field's error edge (AD-05, AD-17, S-23 each needed it). */
.cw-stepper:has(.cw-stepper__value[aria-invalid="true"]) { box-shadow: inset 0 0 0 2px var(--color-status-stopped); }
/* Its value pinned (a replacement is one, S-23b): the buttons go quiet too. */
.cw-stepper:has(.cw-stepper__value:disabled) { background: var(--color-bg-surface-secondary); box-shadow: inset 0 0 0 1px var(--color-border-subtle); }
.cw-stepper:has(.cw-stepper__value:disabled) .cw-icon-btn { pointer-events: none; }
.cw-stepper:has(.cw-stepper__value:disabled) .cw-icon-btn .cw-icon { opacity: .38; }
.cw-stepper__value:disabled { color: var(--color-text-disabled); }

/* Radio tile: a radio whose choice needs a sentence. The whole tile is the
   target, and the native radio stays in the markup. */
.cw-tiles { display: flex; flex-direction: column; gap: var(--spacing-04); }
.cw-tile {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--spacing-04);
  padding: var(--spacing-05) 20px var(--spacing-05) var(--spacing-05);
  border-radius: var(--radius-lg);
  background: var(--color-bg-surface);
  box-shadow: inset 0 0 0 1px var(--color-border-default);
  cursor: pointer;
  transition: background-color var(--duration-state) var(--ease-state), box-shadow var(--duration-state) var(--ease-state);
}
.cw-tile:hover { background: linear-gradient(var(--color-state-hover), var(--color-state-hover)), var(--color-bg-surface); }
.cw-tile .cw-radio,
.cw-tile .cw-checkbox { margin-top: 2px; }
.cw-tile:has(input:checked) {
  background: var(--color-state-selected);
  box-shadow: inset 0 0 0 2px var(--color-border-selected);
}
.cw-tile:has(input:checked) .cw-tile__title { color: var(--color-text-link); }
.cw-tile:has(input:disabled) { cursor: not-allowed; background: var(--color-bg-surface-secondary); }
.cw-tile:has(input:focus-visible) { box-shadow: inset 0 0 0 1px var(--color-border-default), var(--shadow-focus-ring); }
.cw-tile:has(input:checked:focus-visible) { box-shadow: inset 0 0 0 2px var(--color-border-selected), var(--shadow-focus-ring); }
.cw-tile input:focus-visible { box-shadow: inset 0 0 0 1.5px var(--color-border-strong); }
.cw-tile__text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--spacing-02); }
.cw-tile__title { font: var(--type-body-strong); color: var(--color-text-primary); }
.cw-tile__body { font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-tile__meta { font: var(--type-caption-strong); color: var(--color-text-secondary); }
.cw-tile__end { flex: none; margin-inline-start: auto; }

/* Progress step: where a request or an onboarding has got to. */
.cw-steps { margin: 0; padding: 0; list-style: none; }
.cw-step { position: relative; display: flex; gap: var(--spacing-04); }
.cw-step__rail { position: relative; flex: none; width: 24px; }
.cw-step__disc {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: var(--radius-full);
  background: var(--color-bg-surface);
  box-shadow: inset 0 0 0 1.5px var(--color-border-default);
  color: var(--color-icon-on-inverse);
}
.cw-step__disc .cw-icon { width: 16px; height: 16px; }
.cw-step__rail::after {
  content: "";
  position: absolute;
  top: 28px;
  bottom: 0;
  left: 11px;
  width: 2px;
  min-height: 32px;
  background: var(--color-border-subtle);
}
.cw-step:last-child .cw-step__rail::after { content: none; }
.cw-step--complete .cw-step__disc { background: var(--color-action-brand); box-shadow: none; }
.cw-step--complete .cw-step__rail::after { background: var(--color-action-brand); }
.cw-step--current .cw-step__disc { box-shadow: inset 0 0 0 2px var(--color-border-selected); }
.cw-step--current .cw-step__disc::after {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--color-action-brand);
}
/* 2 from the top; 16 under the value, so a step is 60 — the rail's 24 disc, 4 and
   its 32 connector (s-24 and q-18: discs at 288, 348, 408, 468, 528). */
.cw-step__text { display: flex; flex-direction: column; gap: var(--spacing-01); padding: 2px 0 var(--spacing-05); min-width: 0; }
.cw-step__label { font: var(--type-body-s-strong); color: var(--color-text-primary); }
.cw-step--incomplete .cw-step__label { color: var(--color-text-secondary); }
.cw-step__value { font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-step a.cw-step__label { color: var(--color-text-primary); }
.cw-step a.cw-step__label:hover { color: var(--color-text-link); }


/* ============================================================================
 * 8. COMBOBOX AND LISTBOX (02 §8)
 * The field is an <input role="combobox"> the script builds over a native
 * <select>, which stays in the form: without the script, the select submits.
 * ========================================================================== */

.cw-combobox { position: relative; display: block; min-width: 0; }
.cw-combobox > .cw-input { padding-inline-end: calc(var(--spacing-04) + var(--size-icon) + var(--spacing-03)); }
.cw-combobox > .cw-icon {
  position: absolute;
  top: 50%;
  inset-inline-end: var(--spacing-04);
  width: var(--size-icon);
  height: var(--size-icon);
  margin-top: calc(var(--size-icon) / -2);
  color: var(--color-icon-primary);
  pointer-events: none;
}
.cw-combobox--mono > .cw-input { font: var(--type-mono-m); }
.cw-listbox {
  position: absolute;
  inset-inline: 0;
  top: calc(100% + var(--spacing-02));
  z-index: 20;
  margin: 0;
  padding: var(--spacing-03);
  border-radius: var(--radius-lg);
  background: var(--color-bg-surface);
  box-shadow: inset 0 0 0 1px var(--color-border-hairline), var(--shadow-elevation-2);
  list-style: none;
  animation: cw-pop-in var(--duration-enter) var(--ease-enter) both;
}
.cw-listbox__options { max-height: calc(36px * 8); overflow-y: auto; margin: 0; padding: 0; list-style: none; }
.cw-listbox__option {
  display: flex;
  align-items: center;
  gap: var(--spacing-03);
  min-height: 36px;
  padding: 0 var(--spacing-04);
  border-radius: var(--radius-sm);
  font: var(--type-body-s);
  color: var(--color-text-primary);
  cursor: pointer;
}
.cw-listbox--mono .cw-listbox__option { font: var(--type-mono-s); }
.cw-listbox__label { flex: 1 1 auto; min-width: 0; }   /* the words and their <mark> stay one item */
.cw-listbox__option mark { background: none; color: inherit; font-weight: 600; }
.cw-listbox__option[aria-selected="true"]::after {
  content: "";
  flex: none;
  width: 20px;
  height: 20px;
  margin-inline-start: auto;
  background: var(--color-icon-primary);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center / 20px 20px no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center / 20px 20px no-repeat;
}
.cw-listbox__option.is-active,
.cw-listbox__option:hover { background: var(--color-state-hover); }
.cw-listbox__count {
  margin: var(--spacing-03) 0 0;
  padding: var(--spacing-03) var(--spacing-04) var(--spacing-01);
  box-shadow: inset 0 1px 0 var(--color-border-subtle);
  font: var(--type-caption);
  color: var(--color-text-secondary);
}
.cw-listbox__none { padding: var(--spacing-03) var(--spacing-04); font: var(--type-body-s); color: var(--color-text-secondary); }

/* The date field's calendar: a popover under the field's end, placed like a
   menu, on the listbox's surface (radius lg, the hairline, elevation-2). The
   hand-off draws no calendar, so it is made of the parts it does draw: the month
   in body-strong between Ghost chevrons, the days of the week in caption-strong,
   40 day targets in body-s — the chosen day filled Primary, today ringed in
   border/strong. Sunday first. */
.cw-calendar {
  position: fixed;
  inset: auto;
  margin: 0;
  padding: var(--spacing-04);
  border: 0;
  border-radius: var(--radius-lg);
  background: var(--color-bg-surface);
  box-shadow: inset 0 0 0 1px var(--color-border-hairline), var(--shadow-elevation-2);
  color: var(--color-text-primary);
  overflow: visible;
}
.cw-calendar:popover-open { animation: cw-pop-in var(--duration-enter) var(--ease-enter) both; }
.cw-calendar__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-03);
  margin-bottom: var(--spacing-03);
}
.cw-calendar__title { margin: 0; font: var(--type-body-strong); color: var(--color-text-primary); }
.cw-calendar__grid { border-collapse: collapse; }
.cw-calendar__grid th {
  width: var(--size-control-md);
  height: 32px;
  padding: 0;
  font: var(--type-caption-strong);
  color: var(--color-text-secondary);
  text-align: center;
}
.cw-calendar__grid td { width: var(--size-control-md); height: var(--size-control-md); padding: 0; text-align: center; }
.cw-calendar__day {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--size-control-md);
  height: var(--size-control-md);
  padding: 0;
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  font: var(--type-body-s);
  color: var(--color-text-primary);
  cursor: pointer;
}
.cw-calendar__day:hover { background: var(--color-state-hover); }
.cw-calendar__day:active { background: var(--color-state-pressed); }
/* A day stands alone in its 40 cell, so its figure is Plex Sans's own, not the
   tabular one, whose 1 carries a foot serif and reads as the mono face (03 §8:
   dates in Sans). */
.cw-calendar__day[aria-current="date"] { font: var(--type-body-s-strong); box-shadow: inset 0 0 0 1px var(--color-border-strong); }
.cw-calendar__day.is-selected { background: var(--color-action-primary); color: var(--color-text-on-inverse); font: var(--type-body-s-strong); box-shadow: none; }
.cw-calendar__day.is-selected:hover { background: var(--color-action-primary-hover); }
.cw-calendar__day:disabled { background: transparent; color: var(--color-text-disabled); cursor: not-allowed; }
.cw-calendar__day:focus-visible { position: relative; z-index: 1; outline: 2px solid transparent; box-shadow: var(--shadow-focus-ring); }


/* ============================================================================
 * 9. CARDS, CARD HEADER, FACT, ACTION BAR (02 §9)
 * ========================================================================== */

/* Card: white on the canvas, a hairline drawn OVER the content (so a table's
   header band cannot cover it), radius lg, elevation-1, clipping its content. */
.cw-card {
  position: relative;
  min-width: 0;
  border-radius: var(--radius-lg);
  background: var(--color-bg-surface);
  box-shadow: var(--shadow-elevation-1);
  overflow: clip;
}
.cw-card::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px var(--color-border-hairline);
  pointer-events: none;
  z-index: 2;
}
/* An open Combobox list hangs below its field, past the bottom of a short card
   (a picker card holding only the field — AD-34, AD-38): the card stops clipping
   while a list is open, so the list is never cut off. */
.cw-card:has(.cw-listbox:not([hidden])) { overflow: visible; }

/* A table card lets its sticky header escape the clip. It then has to round its
   own first child: the filter bar's white would square off the card's corners
   (every list with filters showed it — E2, G2). */
.cw-card:has(.cw-table-wrap.is-fit) { overflow: visible; }
.cw-card:has(.cw-table-wrap.is-fit) > .cw-filters:first-child {
  border-start-start-radius: var(--radius-lg);
  border-start-end-radius: var(--radius-lg);
}
.cw-card:has(.cw-table-wrap.is-fit) > .cw-table-wrap:last-child .cw-table tbody tr:last-child > td:first-child { border-end-start-radius: var(--radius-lg); }
.cw-card:has(.cw-table-wrap.is-fit) > .cw-table-wrap:last-child .cw-table tbody tr:last-child > td:last-child { border-end-end-radius: var(--radius-lg); }
.cw-card:has(.cw-table-wrap.is-fit) > .cw-table-wrap:first-child .cw-table thead tr:first-child > th:first-child { border-start-start-radius: var(--radius-lg); }
.cw-card:has(.cw-table-wrap.is-fit) > .cw-table-wrap:first-child .cw-table thead tr:first-child > th:last-child { border-start-end-radius: var(--radius-lg); }
.cw-card--pad { padding: var(--spacing-06); }
.cw-card--pad > * + * { margin-top: var(--spacing-06); }
.cw-card--pad > .cw-card__head + * { margin-top: var(--spacing-06); }
.cw-card--hug { align-self: flex-start; width: fit-content; max-width: 100%; }
.cw-card--560 { width: 560px; max-width: 100%; }
.cw-card--720 { width: 720px; max-width: 100%; }
.cw-card__body { padding: var(--spacing-06); }
.cw-card__body > * + * { margin-top: var(--spacing-05); }
.cw-card__body--tight { padding: var(--spacing-05) var(--spacing-06); }
.cw-card__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--spacing-04);
  padding: var(--spacing-05) var(--spacing-06);
  box-shadow: inset 0 1px 0 var(--color-border-subtle);
}

/* Card header: title-s, an optional tag and meta, a description, an action. */
.cw-card__head {
  display: flex;
  align-items: flex-start;
  gap: var(--spacing-05);
}
/* In a table card the header pads 20/24/16/24 and the table runs to the edges. */
.cw-card > .cw-card__head { padding: 20px var(--spacing-06) var(--spacing-05); }
.cw-card--pad > .cw-card__head { padding: 0; }
.cw-card__heading { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--spacing-02); }
.cw-card__title-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--spacing-03); }
.cw-card__title { margin: 0; font: var(--type-title-s); letter-spacing: var(--type-title-s-tracking); color: var(--color-text-primary); }
.cw-card__meta { font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-card__desc { margin: 0; font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-card__aside { margin: 0; align-self: center; font: var(--type-body-s); color: var(--color-text-secondary); text-align: end; }   /* v0.77: AD-07's "25 scanned this year · 21 dormant…" */
.cw-card__action { flex: none; display: flex; align-items: center; gap: var(--spacing-04); margin-inline-start: auto; }
.cw-card__action form { display: contents; }

/* Fill from PO PDF (po-import.js): one line under the Options card's header, in
   the card description's type and colour; red when the PO was refused. Empty, it
   takes no room. */
.cw-po-status { margin: 0; padding: 0 var(--spacing-06) var(--spacing-05); font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-po-status--error { color: var(--color-text-danger); }
.cw-po-status:empty { display: none; }

/* Fact: a read-only value where a field would be; never a box. */
.cw-fact { display: flex; flex-direction: column; gap: var(--spacing-02); min-width: 0; }
.cw-fact__label { font: var(--type-label-s); color: var(--color-text-secondary); }
.cw-fact__value { font: var(--type-body-strong); color: var(--color-text-primary); overflow-wrap: anywhere; }
.cw-fact__value--mono { font: var(--type-mono-m-strong); font-variant-numeric: tabular-nums; }
.cw-fact__note { font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-fact__action { align-self: flex-start; margin-top: var(--spacing-03); font: var(--type-label); }

/* Action bar: the end of a long form, pinned to the bottom of the window. */
.cw-action-bar {
  position: sticky;
  bottom: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: var(--spacing-05);
  min-height: 64px;
  margin: 0 calc(var(--spacing-06) * -1) calc(var(--spacing-06) * -1);
  padding: var(--spacing-04) var(--spacing-06);
  background: var(--color-bg-surface);
  box-shadow: inset 0 1px 0 var(--color-border-subtle);
}
.cw-action-bar__buttons { display: flex; align-items: center; gap: var(--spacing-04); }
.cw-action-bar__divider { width: 1px; height: 24px; background: var(--color-border-subtle); }
.cw-action-bar__status { display: flex; align-items: center; gap: var(--spacing-03); font: var(--type-body-s); color: var(--color-text-primary); }
.cw-action-bar__status .cw-icon { color: var(--color-icon-secondary); }
.cw-action-bar__status--ready .cw-icon { color: var(--color-status-ready); }
.cw-action-bar__status--attention .cw-icon { color: var(--color-status-attention); }
.cw-action-bar__status--error .cw-icon { color: var(--color-status-stopped); }


/* ============================================================================
 * PAGE RECIPES (03 §2). Card widths are at 1440, where the column is 1152.
 * ========================================================================== */

/* Cards side by side in two equal columns, in reading order; a card may span. */
.cw-grid-2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--spacing-06);
  align-items: stretch;
}
.cw-grid-2 > .cw-span-2 { grid-column: 1 / -1; }
.cw-grid-2--start { align-items: start; }
.cw-grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--spacing-06); align-items: stretch; }

/* A column of cards 24 apart (inside a grid cell or an aside). */
.cw-stack { display: flex; flex-direction: column; gap: var(--spacing-06); min-width: 0; }
.cw-stack--16 { gap: var(--spacing-05); }
.cw-stack--8 { gap: var(--spacing-03); }

/* Detail with an aside: the main column fills, the aside keeps 336–400. */
.cw-detail {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(336px, 400px);
  gap: var(--spacing-06);
  align-items: start;
}
.cw-detail--aside-start { grid-template-columns: minmax(280px, 326px) minmax(0, 1fr); }
/* v0.77 AD-27 / AD-28: "Proforma terms" is drawn 354 wide beside the lines (x 1473–1965
   of a 2000-wide drawing of a 2880 screen), so the lines keep their six columns. */
.cw-detail--terms { grid-template-columns: minmax(0, 1fr) minmax(336px, 360px); }

/* The packing list build form: rows of 564 + 564, 708 + 420, then full width. */
.cw-build-row { display: grid; gap: var(--spacing-06); align-items: stretch; }
.cw-build-row--halves { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cw-build-row--wide { grid-template-columns: minmax(0, 708fr) minmax(0, 420fr); }

/* Fields inside a card: two to a row, three where short, 24 apart. */
.cw-fields {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--spacing-06);
  align-items: start;
}
.cw-fields--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.cw-fields--1 { grid-template-columns: minmax(0, 1fr); }
.cw-fields > .cw-span-2,
.cw-fields > .cw-span-all { grid-column: 1 / -1; }

/* A short form: one card 560 or 720 wide; the actions follow it. */
.cw-short { width: 560px; max-width: 100%; display: flex; flex-direction: column; gap: var(--spacing-06); }
.cw-short--720 { width: 720px; }

/* The actions after a form's cards. */
.cw-form-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--spacing-04); }
.cw-form-actions--between { justify-content: space-between; }
.cw-form-actions--end { justify-content: flex-end; }
.cw-form-actions form { display: contents; }

/* A printout to check: its toolbar, then a grey stage with the document centred. */
.cw-viewer__toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--spacing-04);
  padding: var(--spacing-04) var(--spacing-06);
  box-shadow: inset 0 -1px 0 var(--color-border-subtle);
}
.cw-viewer__toolbar-end { margin-inline-start: auto; display: flex; align-items: center; gap: var(--spacing-04); }
.cw-viewer__stage { padding: var(--spacing-07) var(--spacing-06); background: var(--color-bg-surface-tertiary); overflow-x: auto; }   /* s-11, q-03, q-04 draw the stage #e8e8e8 */

/* A printed card: alone on the canvas, centred, no card around it. */
.cw-printed { align-self: center; }

/* Key-value pairs inline: two facts at most, joined by a middle dot. */
.cw-dot-sep::before { content: "·"; margin: 0 var(--spacing-02); color: var(--color-text-tertiary); }

/* A confirmation PAGE — the path without the script — is its dialog, on the canvas. */
.cw-confirm { width: 480px; max-width: 100%; padding: var(--spacing-07); border-radius: var(--radius-xl); }
.cw-confirm--wide { width: 560px; }

/* ── Dashboards (S-01, Q-01, AD-01) ─────────────────────────────────────────── */

/* AD-01's cards: a title, the count as a Tag, See all; then the customers, each
   with its two facts; "and N more"; or the zero line. Cards in a row share its
   height (the grid stretches them). */
.cw-board-card { display: flex; flex-direction: column; }
.cw-board-card > .cw-card__head { padding: var(--spacing-06) var(--spacing-06) var(--spacing-05); align-items: center; }
.cw-board-card__zero,
.cw-board-card__headline,
.cw-board-card__more { margin: 0; padding: 0 var(--spacing-06); font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-board-card__zero { padding-bottom: var(--spacing-06); }
.cw-board-card__headline { padding-bottom: var(--spacing-04); font: var(--type-body-s-strong); color: var(--color-text-primary); }
/* v0.77 AD-01: Receivables' total in Plex Sans at the card title's size, as drawn (it was mono). */
.cw-board-card__headline--figure { font: var(--type-title-s); letter-spacing: var(--type-title-s-tracking); }
.cw-board-card__headline.cw-text-danger { color: var(--color-text-danger); }
.cw-board-card__headline.cw-text-warning { color: var(--color-text-warning); }
.cw-board-card__more { padding-top: var(--spacing-04); padding-bottom: var(--spacing-06); color: var(--color-text-secondary); }
.cw-board-card__rows { margin: 0 var(--spacing-06); padding: 0 0 var(--spacing-03); list-style: none; }
.cw-board-card__rows li {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-01);
  padding: var(--spacing-04) 0;
  box-shadow: inset 0 -1px 0 var(--color-border-subtle);
}
.cw-board-card__rows li:first-child { padding-top: 0; }
.cw-board-card__rows li:last-child { box-shadow: none; }
.cw-board-card__who { font: var(--type-body-s-strong); color: var(--color-text-primary); }
.cw-board-card__what { font: var(--type-body-s); color: var(--color-text-secondary); }
/* v0.77 AD-01: the name is a link (the customer, or what is to be decided), and a long one wraps. */
.cw-board-card__who .cw-link { white-space: normal; }
/* Needs a decision runs without rules between its rows, as drawn. */
.cw-board-card__rows--plain li { box-shadow: none; }

/* S-01b: the first screen a new customer sees — the steps beside the G2 drawing. */
.cw-setup { display: grid; grid-template-columns: minmax(0, 1fr) 400px; gap: var(--spacing-06); align-items: stretch; }
.cw-setup > * + * { margin-top: 0; }
.cw-setup__text { display: flex; flex-direction: column; gap: var(--spacing-05); align-items: flex-start; }
.cw-setup__text .cw-card__desc { font: var(--type-body); }
.cw-setup__steps { margin: 0; padding: 0; list-style: none; counter-reset: cw-step; display: flex; flex-direction: column; gap: var(--spacing-04); }
.cw-setup__steps li { counter-increment: cw-step; display: flex; align-items: center; gap: var(--spacing-05); font: var(--type-body); color: var(--color-text-primary); }
.cw-setup__steps li::before {
  content: counter(cw-step);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-full);
  background: var(--color-bg-surface-secondary);
  font: var(--type-mono-s-strong);
  color: var(--color-text-primary);
}
.cw-setup__graphic {
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-lg);
  background: var(--color-bg-surface-secondary);
}
.cw-setup__graphic img { width: 160px; height: 160px; }


/* ── The carton barcode (S-18) ──────────────────────────────────────────────
 * The symbol is drawn at a fixed 2 px per module because that is what makes it
 * read; it may SHRINK on a narrow screen but never grows past its natural size.
 * A barcode scaled down is still scannable; one clipped at the edge is not.
 */
.cw-barcode {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-03);
  padding: var(--spacing-06);
  border-radius: var(--radius-lg);
  background: var(--color-bg-surface);
  box-shadow: inset 0 0 0 1px var(--color-border-default);
  color: var(--color-text-primary);
}
.cw-barcode svg { display: block; max-width: 100%; height: auto; }
.cw-barcode__raw { font: var(--type-mono-m); letter-spacing: .08em; color: var(--color-text-primary); overflow-wrap: anywhere; text-align: center; }
.cw-barcode__verdict { display: flex; align-items: center; gap: var(--spacing-04); margin: 0; }

@media print {
  body.barcode-printing > *:not(#barcode-modal) { display: none !important; }
  body.barcode-printing #barcode-modal { position: static; display: block; width: auto; max-height: none; padding: 0; box-shadow: none; }
  body.barcode-printing #barcode-modal::backdrop { background: none; }
  body.barcode-printing #barcode-modal .cw-dialog__heading,
  body.barcode-printing #barcode-modal .cw-dialog__actions,
  body.barcode-printing #barcode-modal .cw-barcode__verdict { display: none !important; }
  body.barcode-printing .cw-barcode { box-shadow: none; }
}

/* Print: the page, not the shell. */
@media print {
  .cw-sidenav, .cw-pagebar, .cw-footer, .cw-toast-region, .cw-no-print { display: none !important; }
  .cw-shell { display: block; }
  .cw-canvas { padding: 0; }
  .cw-card { box-shadow: none; }
  .cw-card::after { content: none; }
  html, body { background: none; }
}

/* Narrower than drawn (only 1440 is): the nav keeps 240, the column shrinks. */
@media (max-width: 1100px) {
  .cw-detail { grid-template-columns: minmax(0, 1fr); }
  .cw-build-row--wide { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 900px) {
  .cw-grid-2, .cw-grid-3, .cw-build-row--halves, .cw-fields, .cw-fields--3 { grid-template-columns: minmax(0, 1fr); }
  .cw-figures { flex-wrap: wrap; }
  .cw-figures .cw-figure { flex: 1 1 200px; }
}


/* ============================================================================
 * THE SCREENS' OWN RULES — merged 29 Sep 2026 from the twelve per-group files
 * (`pages/*.css`) the v0.64 conversion was built in, in the order the layout
 * loaded them, so the cascade is exactly what was verified screen by screen.
 * Every class is prefixed with its screen (`cw-packing-`, `cw-billing-`, …) and
 * reads role tokens only. A rule that became shared (a list card's filter-bar
 * corners, a card clipping an open Combobox list, a disabled select) was taken
 * out of its page on the way in.
 * ========================================================================== */

/* ── A — the packing list build form and printouts (S-06 to S-11, Q-03 to Q-06) ── */
/* ============================================================================
 * Redesign scaffold, group A — the packing list build form (S-06 to S-10, Q-06)
 * and the printout to check (S-11, Q-03, Q-04). Role tokens only; every class is
 * the page's own (`cw-packing-…`) or scoped under one.
 * ========================================================================== */

/* Contract and delivery: the combobox column is wider than the select column —
   s-07 measures Contract and Client 260, Delivery number and Shipping mode 232,
   24 apart, where Other details splits its 516 evenly. */
.cw-packing-fields { grid-template-columns: minmax(0, 260fr) minmax(0, 232fr); }
@media (max-width: 900px) {
  .cw-packing-fields { grid-template-columns: minmax(0, 1fr); }
}

/* A placeholder is words, in Sans, even in a field whose value is a code: s-06
   "Type or choose a contract" and "Enter invoice number" over mono values. */
.cw-packing-form .cw-combobox--mono > .cw-input::placeholder,
.cw-packing-form .cw-input--mono::placeholder { font: var(--type-body); }

/* A disabled select's label reads disabled too (s-06: Delivery number before a
   contract is chosen). `:has`, because the script enables the select later. */
.cw-packing-form .cw-form-field:has(> .cw-select-wrap > .cw-select:disabled) > .cw-label { color: var(--color-text-disabled); }

/* S-06: before a delivery is chosen the page is short. The form fills the canvas
   and the Cartons card with its empty state takes the height left, so the Action
   bar sits at the foot of the window as drawn. Only a grid of ONE card grows (the
   empty states); a built grid keeps its own height. */
.cw-packing-form { flex: 1 0 auto; }
.cw-packing-grid:has(> .cw-card:only-child) { flex: 1 0 auto; display: flex; flex-direction: column; }
.cw-packing-grid > .cw-card:only-child { flex: 1 0 auto; display: flex; flex-direction: column; }
.cw-packing-grid > .cw-card:only-child > .cw-empty { flex: 1 0 auto; justify-content: center; }

/* S-09: the refused fields listed under the banner's title, the bullets on the
   title's edge rather than indented. */
.cw-banner__body .cw-packing-errors { padding-inline-start: 0; list-style-position: inside; }

/* An option scanned after the list was built: the row on wash/info, its New tag
   after the option code (s-09, s-10). */
.cw-table tr.cw-packing-new > td { background: var(--color-wash-info); }
.cw-packing-option { display: inline-flex; align-items: center; gap: var(--spacing-06); }

/* The printout's stage (s-11): bg/surface-tertiary, where the shared viewer rule
   draws surface-secondary. 20 a side, not 24, so the A4 page at the Readable
   magnification (1.4 × 793.7 = 1111 px) fits the 1152 column without scrolling.
   The magnification itself is the page's `--sheet-zoom`: Readable until the
   script (or the operator) chooses another. */
.cw-packing-viewer .cw-viewer__stage {
  --sheet-zoom: 1.4;
  padding-inline: 20px;
  background: var(--color-bg-surface-tertiary);
}
/* Print: the page at 1:1 on no desk, as the view's own print rule always said. */
@media print {
  .cw-packing-viewer .cw-viewer__stage { --sheet-zoom: 1; background: none; padding: 0; overflow: visible; }
}

/* ── B — the SKU, QC, BDC & POS audit and invalid scan reports (S-15 to S-20, Q-10 to Q-14) ── */
/* ============================================================================
 * The report screens of agent B — SKU report (S-15), SKUs in package (S-16,
 * S-17), BDC & POS audit report (S-19, S-20, Q-13, Q-14), QC report (Q-10,
 * Q-11) and Invalid scan report (Q-12), v0.64 hand-off.
 *
 * Role tokens only; every class is prefixed with its screen (`cw-report-`,
 * `cw-audit-`, `cw-sku-`). Nothing here re-styles a shared component outside
 * the element that carries one of these classes. To be merged into portal.css
 * with the other page files (layouts/portal-head.blade.php).
 * ========================================================================== */


/* ── A report header with column groups (S-15, S-19/S-20, Q-10/Q-11, Q-13) ──
 * The group band row is 30 tall and names its group in text/primary (s-15: the
 * band 178–207, the column labels 208–247, "Packing" in ink over "Qty" in
 * secondary). A column outside every group spans both rows and sets its label
 * on the lower one, level with the groups' own labels (s-15: all at 224–233),
 * so the second row sticks 30 under the first, not 40.
 */
.cw-report-card .cw-table--report th.cw-group {
  height: calc(var(--size-table-row) * .75);
  padding-block: var(--spacing-02);
  color: var(--color-text-primary);
}
.cw-report-card .cw-table--report thead tr:first-child > th[rowspan] {
  vertical-align: bottom;
  padding-block-end: var(--spacing-04);
}
.cw-report-card .cw-table thead tr + tr th {
  top: calc(var(--cw-pagebar-h) + var(--size-table-row) * .75);
}


/* ── A remark on a QC audit row (Q-13, Q-14) ───────────────────────────────
 * A Compact field and its Save, 8 apart, so Save's 44 target (8 to each side)
 * never lies over the field. q-14: the field 1142–1277, Save from 1283.
 */
.cw-audit-remark {
  display: flex;
  align-items: center;
  gap: var(--spacing-03);
  margin: 0;
}
.cw-audit-remark .cw-input--compact { flex: none; width: 136px; }


/* ── The grand totals (S-20, Q-13) ─────────────────────────────────────────
 * The design draws Row components; the markup stays the two-column table legacy
 * prints (ReportScreenTest reads it cell by cell). So the table takes the Row's
 * look: the label in body-s text/secondary, its note under it (`cw-row__note`),
 * the figure in mono-s-strong at the end, 52 tall, a rule under each but the last.
 */
.cw-audit-totals {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
}
.cw-audit-totals td {
  height: 52px;
  padding: var(--spacing-03) 0;
  vertical-align: middle;
  box-shadow: inset 0 -1px 0 var(--color-border-subtle);
  font: var(--type-body-s);
  color: var(--color-text-secondary);
}
.cw-audit-totals td + td {
  padding-inline-start: var(--spacing-04);
  text-align: end;
  white-space: nowrap;
  font: var(--type-mono-s-strong);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-primary);
}
.cw-audit-totals tr:last-child > td { box-shadow: none; }


/* ── SKUs in a package (S-16 dialog, S-17 page) ────────────────────────────
 * A list table in its own box, border/subtle and radius md, that scrolls inside
 * the box with its header held at the top: ten rows on the page (s-17: 487–927),
 * six in the dialog (s-16: 444–725). The row number is right-aligned in
 * text/secondary; cells pad 12, the first 16 (s-17: SKU at 346 in a box from 288).
 */
.cw-sku-list {
  max-height: calc(var(--size-table-row) * 11 + 2px);
  overflow-y: auto;
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-md);
}
.cw-dialog .cw-sku-list { max-height: calc(var(--size-table-row) * 7 + 2px); }
.cw-sku-list .cw-table th { top: 0; }
.cw-sku-list .cw-table th,
.cw-sku-list .cw-table td { padding-inline: var(--spacing-04); }
.cw-sku-list .cw-table tr > :first-child { padding-inline-start: var(--spacing-05); }
.cw-sku-list .cw-sku-list__num {
  width: 1%;
  text-align: end;
  color: var(--color-text-secondary);
}
/* The fragment's empty and no-match states sit in the dialog's flow: 24 above
   and below, not the 72 of a report before it runs. */
.cw-empty.cw-sku-empty { padding-block: var(--spacing-06); }

/* ── C1 — devices, device requests and licences (S-21 to S-25, Q-15 to Q-19) ── */
/* ============================================================================
 * C1 — Devices (S-21, Q-15), Device requests (S-22, Q-16), Request a device
 * (S-23, S-23b, Q-17), Device request (S-24, Q-18), Licences (S-25, Q-19).
 *
 * Role tokens only. Every rule is scoped to a class these pages set themselves,
 * because the layout loads every file in pages/ on every page.
 * ========================================================================== */

/* S-21 / Q-15, Last sync: a quiet scanner's date keeps its ink and the triangle
   before it carries the colour — unlike a flagged figure (.cw-flag), whose text
   turns text/warning too. */
.cw-devices-sync {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-02);
}
.cw-devices-sync > .cw-icon { color: var(--color-status-attention); }

/* S-21 / Q-15 (v0.69), the delete PIN's card: a Card header and nothing under it,
   so it pads 20 all round and Change PIN sits level with the middle of its two
   lines (s-21: the card 160–334 at 2x, the button 212–283). */
.cw-card.cw-devices-pin > .cw-card__head { align-items: center; padding-block-end: 20px; }

/* S-23, Add-on features: the checkbox rows sit 4 under the group's label (s-23:
   label 242–260, the 44 row from 264). Padding, because the options clear the
   floated legend and a clearing element's top margin is absorbed. */
.cw-devreq-features > .cw-radio-group__options { padding-top: var(--spacing-02); }

/* S-23b: a replacement is one, so the page's own script disables the `quantity`
   number input. Its Stepper then reads as disabled — the icons at 38 %, as on a
   disabled Icon button (02 §1) — and its buttons take no clicks. */
.cw-devreq-quantity .cw-stepper:has(.cw-stepper__value:disabled) .cw-icon-btn { pointer-events: none; }
.cw-devreq-quantity .cw-stepper:has(.cw-stepper__value:disabled) .cw-icon-btn .cw-icon { opacity: .38; }
/* A rejected quantity takes the Field's error edge, as the input did before. */
.cw-devreq-quantity .cw-stepper:has(.cw-stepper__value[aria-invalid="true"]) { box-shadow: inset 0 0 0 2px var(--color-status-stopped); }

/* S-24 / Q-18: a Row list and the Progress steps start closer under the Card
   header than a field grid's 24 — s-24 draws the first Row 6 under it (the Row's
   own 8 and its centred 52 carry the rest) and the first step 16 under it. */
.cw-devreq-detail > .cw-card--pad > .cw-card__head + .cw-rows { margin-top: var(--spacing-03); }
.cw-devreq-detail > .cw-card--pad > .cw-card__head + .cw-steps { margin-top: var(--spacing-05); }

/* S-24 / Q-18: free text the customer or Iteas typed keeps its line breaks — the
   note, and the reason on a decline or on an approval with changes. */
.cw-devreq-lines {
  display: block;
  white-space: pre-line;
}

/* S-24, Your note: a Row whose long value sits under its label, 24 below the
   rule over it. */
.cw-rows > .cw-devreq-note {
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-start;
  gap: var(--spacing-02);
  min-height: 0;
  padding: var(--spacing-06) 0 0;
}
.cw-devreq-note > .cw-row__label { flex: none; }
.cw-devreq-note > .cw-row__value { text-align: start; }

/* S-24, under the Progress steps: one Row per invoice or key, its number a code
   in Plex Mono (03 §4), the amount or the state at the end. */
.cw-devreq-doc > .cw-row__label {
  font: var(--type-mono-s);
  font-variant-numeric: tabular-nums;
}

/* ── C2 — billing, payment history, renew, add-ons and the Supervisor PIN (S-26 to S-31b, Q-20 to Q-24) ── */
/* ============================================================================
 * C2 — Billing (S-26, S-27, S-28 · Q-20, Q-21, Q-22), Add-ons (S-29) and the
 * Supervisor PIN (S-31, S-31b · Q-24) of the v0.64 hand-off.
 *
 * ⏳ A REDESIGN SCAFFOLD, like every file in pages/: only what portal.css has no
 *    class for, measured on the PNGs at 1x. Loaded on every page, so every class
 *    is prefixed `cw-billing-`. Role tokens only — PortalShellTest scans this
 *    file for a raw colour. Nothing here decides flow.
 * ========================================================================== */


/* ── S-26 · Q-20: Billing ─────────────────────────────────────────────────── */

/* The Payments card: its header's text action, Payment history, sits centred on
   the title and the description beside it, as s-26 draws it. */
.cw-billing-payments > .cw-card__head { align-items: center; }

/* S-26 PDF, S-27 Receipt: a text action with its icon, its label on the row's
   text line as drawn. Left on the baseline, the inline-flex link sits on its
   icon's bottom edge, its label rides 3 px high and the row grows to 41. */
.cw-billing-doc > .cw-link { vertical-align: middle; }

/* An empty table shows its Empty state alone, without the header band, as the
   list recipe and the dashboard's cards do (s-05, s-01). The rows still come
   from the view's own @forelse; only the band is not drawn. */
.cw-billing-table:has(> tbody > tr > td.cw-table-empty) > thead { display: none; }


/* ── S-28 · S-29: the tick column ─────────────────────────────────────────── */

/* The box sits 24 in (the first cell's padding) and the scanner's name 24 after
   it (s-28: box 288–308, name 332.5). The tick's 44 row is its target, which is
   what makes these rows 60 tall in the drawings. */
.cw-table th.cw-billing-pick,
.cw-table td.cw-billing-pick { width: 1%; padding-inline-end: var(--spacing-02); }
.cw-billing-pick .cw-check { gap: 0; vertical-align: middle; }

/* A Status on its own line under the cell's value: s-28's "Expired · uploads
   blocked" under the term end. */
.cw-table .cw-billing-cell-status { display: flex; margin-top: var(--spacing-01); }


/* ── S-28 · Q-22: Renew scanners ──────────────────────────────────────────── */

/* Each add-on: the tick and its name, "You have this", then the price at the
   end of the card (s-28: box 288, pill 500, price ending 991). One grid for all
   of them, so the pills and the prices line up down the card. */
.cw-billing-addons {
  display: grid;
  grid-template-columns: minmax(200px, max-content) max-content minmax(0, 1fr);
  align-items: center;
  column-gap: var(--spacing-04);
}
.cw-billing-addons > .cw-check { grid-column: 1; }
.cw-billing-addon-held { grid-column: 2; justify-self: start; }
.cw-billing-addon-price {
  grid-column: 3;
  justify-self: end;
  font: var(--type-body-s);
  color: var(--color-text-secondary);
  white-space: nowrap;
}
/* What ticking changed — "Adding …" — shown only once there is something to say. */
.cw-billing-delta { margin: 0; font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-billing-delta:empty { display: none; }


/* ── S-28 · S-29: the request panel ───────────────────────────────────────── */

/* s-28 and s-29 draw the request — Your quote request, Your add-on request — as a
   400 panel (size/panel), the window's full height at its end, beside the page
   bar: the page bar, the canvas and the footer end where it starts (its rule at
   x 1040). The shell has no slot for one, so the panel is fixed there and the
   work area gives up its width while the page holds it — the same answer as the
   admin's proforma panel (AD-27, pages/G1.css), so the portal's panels match.
   The panel stays inside its page's form, so the note and the button post with it.

   Its title in title-m ("panel title", 01 §3) 32 from the top, then the fields
   16 apart; at its foot, over a rule across the panel (s-28 y 699), the total in
   price-l ("totals in a panel footer"; 716–756), the tax line 12 under it, the
   L button (798–846) and the last line 12 under that, 24 from the bottom. */
.cw-main:has(.cw-billing-panel) { padding-inline-end: var(--size-panel); }
.cw-billing-panel {
  position: fixed;
  inset-block: 0;
  inset-inline-end: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-05);
  width: var(--size-panel);
  padding: var(--spacing-07) var(--spacing-06) var(--spacing-06);
  background: var(--color-bg-surface);
  box-shadow: inset 1px 0 0 var(--color-border-subtle);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.cw-billing-panel__title {
  margin: 0 0 var(--spacing-03);
  font: var(--type-title-m);
  letter-spacing: var(--type-title-m-tracking);
  color: var(--color-text-primary);
}
.cw-billing-panel__foot {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-03);
  margin: auto calc(var(--spacing-06) * -1) 0;
  padding: var(--spacing-05) var(--spacing-06) 0;
  box-shadow: inset 0 1px 0 var(--color-border-subtle);
}
.cw-billing-panel__total {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-05);
  margin: 0;
  font: var(--type-body-s);
  color: var(--color-text-secondary);
}
.cw-billing-panel__value {
  font: var(--type-price-l);
  letter-spacing: var(--type-price-l-tracking);
  color: var(--color-text-primary);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.cw-billing-panel__line { margin: 0; font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-billing-panel__total + .cw-billing-panel__line,
.cw-billing-panel__foot > .cw-btn + .cw-billing-panel__line { margin-top: var(--spacing-02); }
.cw-billing-panel__foot > .cw-btn { margin-top: var(--spacing-01); }


/* ── S-29: Add-ons ────────────────────────────────────────────────────────── */

/* A request waiting on Iteas: the add-on and its facts, then its Status and
   Withdraw at the end of the row, kept on one line. */
.cw-billing-request { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--spacing-01); }
.cw-billing-request__name { margin: 0; font: var(--type-body-strong); color: var(--color-text-primary); }
.cw-billing-request__meta { margin: 0; font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-billing-waiting .cw-row > .cw-actions { flex: none; flex-wrap: nowrap; }

/* The scanners table runs to the card's edges inside the padded card (s-29). */
.cw-billing-bleed { margin-inline: calc(var(--spacing-06) * -1); }

/* Past requests beside the panel: 03 §4 — "In a narrow card the gap shrinks,
   never below 24." Six columns 40 apart are 806 wide with s-29's own rows, in a
   752 column; at the 24 floor they fit (s-29 draws them 26–40 apart). The outer
   cells keep their 24. */
.cw-main:has(.cw-billing-panel) .cw-billing-past .cw-table th,
.cw-main:has(.cw-billing-panel) .cw-billing-past .cw-table td { padding-inline: var(--spacing-04); }
.cw-main:has(.cw-billing-panel) .cw-billing-past .cw-table tr > :first-child { padding-inline-start: var(--spacing-06); }
.cw-main:has(.cw-billing-panel) .cw-billing-past .cw-table tr > :last-child { padding-inline-end: var(--spacing-06); }


/* Narrower than drawn (only 1440 is), at portal.css's own breakpoint: the panel
   joins the page under the cards, as a card. */
@media (max-width: 1100px) {
  .cw-main:has(.cw-billing-panel) { padding-inline-end: 0; }
  .cw-billing-panel {
    position: static;
    width: auto;
    margin-top: var(--spacing-06);
    padding: var(--spacing-06);
    border-radius: var(--radius-lg);
    box-shadow: inset 0 0 0 1px var(--color-border-hairline), var(--shadow-elevation-1);
    overflow: visible;
  }
  .cw-billing-panel__foot { margin-top: var(--spacing-03); }
}

/* ── D — which app to use (Q-09) ── */
/* ============================================================================
 * Redesign scaffold, group D — Q-09 "Which app to use"
 * (resources/views/portal/supplier-directory/index.blade.php). Role tokens only.
 * Merged into portal.css with the rest of pages/ once the conversion is done.
 * ========================================================================== */

/* q-09: an inactive factory's Status follows its name 24 on (28 with the space
   between them, as drawn: name ends 418.5, the dot starts 447). */
.cw-directory-inactive { margin-inline-start: var(--spacing-06); }

/* The sheet's title on paper only. On screen the page bar holds the title, and
   the page bar does not print (portal.css "Print: the page, not the shell"). */
.cw-directory-print-title { display: none; }

@media print {
  .cw-directory-print-title { display: block; }

  /* Tighter rows on paper, so an agency's whole list fits one sheet — the
     Tabler page printed its rows at the same pitch (`.table td { padding: .3rem
     .5rem }` in the view's own print block). */
  .cw-directory-table th,
  .cw-directory-table td {
    height: auto;
    padding-block: var(--spacing-02);
  }
}

/* ── E1 — onboarding (AD-02 to AD-06) ── */
/* ============================================================================
 * E1 — Onboard a customer and its wizard: AD-02, AD-02b and AD-03 to AD-06 of
 * the v0.64 hand-off (resources/views/admin/onboarding/*).
 *
 * Page styles for the parallel conversion, loaded on every page by the layout,
 * so every class is prefixed `cw-onboarding`. Role tokens only.
 * ========================================================================== */

/* The wizard (03 §2 "Wizard"): the step rail in a card of its own, 236 wide at
   1440 (ad-03: 264–500), then the step's form — two columns of cards, 434 each
   (524–958 and 982–1416), each column its own stack, so a short card does not
   stretch to its neighbour (ad-03: Factory 280 tall beside Tax and contact 398).
   The step's notes and its own Back and next step follow the cards. */
.cw-onboarding {
  display: grid;
  grid-template-columns: 236px minmax(0, 1fr);
  gap: var(--spacing-06);
  align-items: start;
}
.cw-onboarding__main {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-06);
  min-width: 0;
  margin: 0;
}
.cw-onboarding__cols {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--spacing-06);
  align-items: start;
}
.cw-onboarding .cw-page-note { margin: 0; }

/* The one Primary scanner, a value nobody can change, drawn at the Stepper's
   152 beside it (ad-05: 548–700 and 982–1134). */
.cw-onboarding-short { width: 152px; }

/* A date field's calendar (ad-04): the Lucide calendar, 24 in icon/primary, 12
   from the end. It is drawn ON the browser's own picker button, so the native
   date picker, its keyboard and its value format stay exactly as they were. */
.cw-onboarding-date input[type="date"]::-webkit-calendar-picker-indicator {
  width: var(--size-icon);
  height: var(--size-icon);
  margin: 0;
  padding: 0;
  background: var(--color-icon-primary);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 2v3'/%3E%3Cpath d='M16 2v3'/%3E%3Crect x='3' y='3' width='18' height='18' rx='2'/%3E%3Cpath d='M3 9h18'/%3E%3C/svg%3E") center / 24px 24px no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 2v3'/%3E%3Cpath d='M16 2v3'/%3E%3Crect x='3' y='3' width='18' height='18' rx='2'/%3E%3Cpath d='M3 9h18'/%3E%3C/svg%3E") center / 24px 24px no-repeat;
  cursor: pointer;
}

/* The Stepper has no error look of its own; a refused count takes the Field's,
   2 px status/stopped inside (02 §2), as its message under it turns red. */
.cw-onboarding .cw-stepper:has(.cw-stepper__value[aria-invalid="true"]) { box-shadow: inset 0 0 0 2px var(--color-status-stopped); }

/* Place of supply (ad-04): derived from the GSTIN and never typed, so it is a
   grey panel inside its card, not a field — 16 padding, radius md, the label
   in body-strong over two body-s lines, 4 apart. */
.cw-onboarding-place {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-02);
  padding: var(--spacing-05);
  border-radius: var(--radius-md);
  background: var(--color-bg-surface-secondary);
}
.cw-onboarding-place p { margin: 0; }
.cw-onboarding-place__label { font: var(--type-body-strong); color: var(--color-text-primary); }
.cw-onboarding-place__line { font: var(--type-body-s); color: var(--color-text-secondary); }

/* Step 3's running total (ad-05): a border/subtle rule across the column, 16,
   then Before tax (body-s, secondary) over the count (caption, tertiary) at the
   start, and the figure in title-l at the end, on the same bottom edge. */
.cw-onboarding-total {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--spacing-05);
  padding-top: var(--spacing-05);
  box-shadow: inset 0 1px 0 var(--color-border-subtle);
}
.cw-onboarding-total p { margin: 0; }
.cw-onboarding-total__label { font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-onboarding-total__detail { margin-top: var(--spacing-01); font: var(--type-caption); color: var(--color-text-tertiary); }
.cw-onboarding-total__value {
  font: var(--type-title-l);
  letter-spacing: var(--type-title-l-tracking);
  color: var(--color-text-primary);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Step 3's scanner rows (ad-05): a bare checkbox in a cell is centred in the
   row rather than sitting on the text baseline, 4 above the middle. */
.cw-onboarding-slots td > .cw-checkbox { display: block; }

/* The unfinished drafts' GSTIN under the name (ad-02): the caption line, in mono. */
.cw-table td .cw-cell-sub.cw-onboarding-gstin { font: var(--type-mono-xs); font-variant-numeric: tabular-nums; }

/* Step 4's invoice lines (ad-06). ⚠️ The one table here whose words wrap: a
   line's description is the proforma's own printed text (`Quote::forOnboarding()`,
   "Cartonwise licence — additional scanner (12 months)"), far longer than the
   sample ad-06 draws, and the card is 434 wide — kept on one line, the table
   scrolls sideways and the amounts, the point of the card, leave the view. */
.cw-onboarding-invoice td:first-child { white-space: normal; }

/* Step 4's Total (ad-06): the last row of the invoice, white like the lines
   above it, its word and figure one size up. */
.cw-table tr.cw-onboarding-invoice-total > td { font: var(--type-body-strong); }
.cw-table tr.cw-onboarding-invoice-total > td.cw-num { font: var(--type-mono-m-strong); font-variant-numeric: tabular-nums; }

/* The review's address keeps its three lines inside the Row's value. */
.cw-onboarding-address { display: block; }

/* Narrower than drawn (only 1440 is): the rail goes above the form, then the
   two columns become one — the same breakpoints as portal.css. */
@media (max-width: 1100px) {
  .cw-onboarding { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 900px) {
  .cw-onboarding__cols { grid-template-columns: minmax(0, 1fr); }
}

/* ── E2 — suppliers, QC agencies and clients (AD-07 to AD-12) ── */
/* ============================================================================
 * E2 — the admin tenant registers: Suppliers, QC agencies, Clients and their
 * forms (AD-07 to AD-12 of the v0.64 hand-off). Role tokens only.
 * ========================================================================== */

/* AD-08, AD-10, AD-12: the edit forms stack their cards in a main column beside
   an aside of 356 (measured at 1440: cards 264–1036 and 1060–1416) — the Detail
   recipe's aside (03 §2, 336–400) at the width these screens draw it. */
.cw-tenants-detail { grid-template-columns: minmax(0, 1fr) 356px; }
@media (max-width: 1100px) {
  .cw-tenants-detail { grid-template-columns: minmax(0, 1fr); }
}

/* The aside cards (Account standing, Supervisor PIN): the status pill at the end
   of the Card header is centred on the title and its line (ad-08). */
.cw-tenants-aside-card > .cw-card__head { align-items: center; }

/* "Clear notice: they have paid" sits 8 under Update notice (ad-08), not 24. */
.cw-tenants-aside-card > .cw-tenants-clear { margin-top: var(--spacing-03); }

/* A placeholder is words, and words never take mono (01 §3): ad-08b draws
   "15 characters", "REX number", "Pincode" and "Phone" in Sans inside the fields
   whose values are Plex Mono (both styles are 16/24, so only the family moves). */
.cw-tenants-form .cw-input--mono::placeholder { font: var(--type-body); }

/* Balance pending sits 8 after the name on ad-07 and ad-09; the word space gives
   about 5 of it. */
.cw-tenants-list td > .cw-status--pill { margin-inline-start: var(--spacing-01); }

/* ── F1 — fleet, devices and licence keys (AD-13 to AD-19) ── */
/* ============================================================================
 * F1 — Fleet (AD-13), Devices (AD-14, AD-15), Licence keys (AD-16 to AD-19).
 *
 * ⏳ Redesign scaffold, like every file in this folder: one stylesheet per group
 * of screens while they are converted in parallel, merged into portal.css
 * afterwards. Role tokens only, never a hex (PortalShellTest scans this file).
 * Measurements are the v0.64 PNGs at 1x.
 * ========================================================================== */


/* ── A card head holding a Segmented of views (AD-13, AD-22) ────────────────
 * The Segmented centred on the title, with a rule under it like a filter bar's
 * (ad-13: 15 · 48 · 15 over a subtle rule; 16, the nearest token, here) — the
 * Scanners list, a customer's Scanners tab, and Requests.
 */
.cw-card > .cw-card__head:has(> .cw-card__action > .cw-segmented) {
  align-items: center;
  padding-block: var(--spacing-05);
  box-shadow: inset 0 -1px 0 var(--color-border-subtle);
}


/* ── AD-17 Issue licence keys ────────────────────────────────────────────────
 * v0.77: a dialog over Keys waiting (480), so the scanner labels sit two to a
 * row, 12 apart (ad-17: 200 · 12 · 200 at 1x). A refused How many scanners?
 * takes the Stepper's own error rule.
 */
.cw-keys-labels {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--spacing-04);
}

/* A numbered list: a 24 disc on the secondary surface, the words 12 after it,
   items 24 apart, or 10 (ad-19, the setup steps — its one use since v0.77's
   AD-17 dropped What happens next). */
.cw-keys-steps {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-06);
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: cw-keys-step;
}
.cw-keys-steps--tight { gap: calc(var(--spacing-03) + var(--spacing-01)); }
.cw-keys-steps > li {
  counter-increment: cw-keys-step;
  display: flex;
  align-items: flex-start;
  gap: var(--spacing-04);
  font: var(--type-body-s);
  color: var(--color-text-primary);
}
.cw-keys-steps > li::before {
  content: counter(cw-keys-step);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--size-icon);
  height: var(--size-icon);
  border-radius: var(--radius-full);
  background: var(--color-bg-surface-secondary);
  font: var(--type-caption-strong);
  color: var(--color-text-primary);
}


/* ── AD-18 Licence keys · shown once ─────────────────────────────────────────
 * The portal password card: its parts 12 apart; the password on the secondary
 * surface in a 560 box, 52 tall (ad-18).
 */
.cw-keys-password > * + *,
.cw-keys-password > .cw-card__head + * { margin-top: var(--spacing-04); }
.cw-keys-password__box {
  display: flex;
  align-items: center;
  width: 560px;
  max-width: 100%;
  min-height: 52px;
  padding: 0 var(--spacing-05);
  border-radius: var(--radius-md);
  background: var(--color-bg-surface-secondary);
}
.cw-keys-password__box code { font: var(--type-mono-m); color: var(--color-text-primary); overflow-wrap: anywhere; }

/* A key in the table: Plex Mono title size, selected whole on a click, so it can
   be read off the screen onto a keypad (the rows grow to 44 around it). */
.cw-keys-code {
  font: var(--type-mono-title);
  letter-spacing: var(--type-mono-title-tracking);
  color: var(--color-text-primary);
  user-select: all;
}

/* The card's foot: the address and Email to customer at the start; Copy all,
   Download CSV and Done at the end, 16 apart, on the controls' bottom edge. */
.cw-keys-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--spacing-05);
  padding: var(--spacing-09) var(--spacing-06) var(--spacing-06);
  box-shadow: inset 0 1px 0 var(--color-border-subtle);
}
.cw-keys-foot__deliver { display: flex; align-items: flex-end; gap: var(--spacing-05); margin: 0; }
.cw-keys-foot__email { width: 360px; }
.cw-keys-foot__end { display: flex; align-items: center; gap: var(--spacing-05); margin-inline-start: auto; }


/* ── AD-19 Installation card ─────────────────────────────────────────────────
 * The printed card: alone on the canvas and centred (`.cw-printed`), 618 wide,
 * padded 40, its parts 20 apart (ad-19), with no card around it.
 */
.cw-keys-card {
  display: flex;
  flex-direction: column;
  gap: calc(var(--spacing-05) + var(--spacing-02));
  width: 618px;
  max-width: 100%;
  padding: var(--spacing-08);
  border-radius: var(--radius-lg);
  background: var(--color-bg-surface);
  box-shadow: inset 0 0 0 1px var(--color-border-hairline), var(--shadow-elevation-2);
}
.cw-keys-card__head { display: flex; align-items: center; justify-content: space-between; gap: var(--spacing-05); }
.cw-keys-card__heading { display: flex; flex-direction: column; gap: var(--spacing-02); min-width: 0; }
.cw-keys-card__eyebrow {
  font: var(--type-eyebrow);
  letter-spacing: var(--type-eyebrow-tracking);
  text-transform: uppercase;
  color: var(--color-text-tertiary);
}
.cw-keys-card__title {
  font: var(--type-title-l);
  letter-spacing: var(--type-title-l-tracking);
  color: var(--color-text-primary);
  overflow-wrap: anywhere;
}
.cw-keys-card__mark { flex: none; width: 40px; height: 40px; }
.cw-keys-card__tier {
  display: flex;
  align-items: center;
  gap: var(--spacing-04);
  font: var(--type-body-s);
  color: var(--color-text-secondary);
}
.cw-keys-card__panel {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-03);
  padding: var(--spacing-05) calc(var(--spacing-05) + var(--spacing-02));
  border-radius: var(--radius-md);
  background: var(--color-bg-surface-secondary);
}
.cw-keys-card__hint { font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-keys-card__code {
  font: var(--type-mono-l);
  letter-spacing: var(--type-mono-l-tracking);
  color: var(--color-text-primary);
  overflow-wrap: anywhere;
}
.cw-keys-card__code.cw-text-secondary { color: var(--color-text-secondary); }
.cw-keys-card__note { font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-keys-card__subtitle { margin: 0; font: var(--type-title-s); letter-spacing: var(--type-title-s-tracking); color: var(--color-text-primary); }
.cw-keys-card__foot { font: var(--type-caption); color: var(--color-text-secondary); }

@media print {
  .cw-keys-card { box-shadow: inset 0 0 0 1px var(--color-border-default); }
}

/* ── F2 — licences and device requests (AD-20 to AD-23) ── */
/* ============================================================================
 * F2 — the admin licence and device request screens of the v0.64 hand-off:
 * AD-20 Licences, AD-21 Licence for …, AD-22 Device requests, AD-23 Device
 * request · decide. Role tokens only (portal.css's rule); every class is prefixed
 * with its page. Nothing here decides flow.
 * ========================================================================== */


/* ── A record's state as a Pill, from a shared status partial ────────────────
 * v0.64 puts a record's state at the start of the page bar's meta line (02 §6,
 * 03 §10). The licence's and the request's words come from `admin._licence-status`
 * and `admin._request-status`, which draw the PLAIN Status, and the partials are
 * shared: so the pill is drawn around their output here, with the same size and
 * the same washes as `.cw-status--pill` (02 §3: 28 tall, 12 a side, radius full,
 * caption-strong on the tone's wash). The word and its tone stay the partials'.
 */
.cw-licence-state > .cw-status,
.cw-device-request-state > .cw-status {
  min-height: 28px;
  padding: 0 var(--spacing-04);
  border-radius: var(--radius-full);
  background: var(--color-bg-surface-secondary);
  font: var(--type-caption-strong);
}
.cw-licence-state > .cw-status--ready,
.cw-device-request-state > .cw-status--ready { background: var(--color-wash-success); }
.cw-licence-state > .cw-status--working,
.cw-device-request-state > .cw-status--working { background: var(--color-wash-info); }
.cw-licence-state > .cw-status--attention,
.cw-device-request-state > .cw-status--attention { background: var(--color-wash-warning); }
.cw-licence-state > .cw-status--stopped,
.cw-device-request-state > .cw-status--stopped { background: var(--color-wash-danger); }


/* ── A red Tertiary S button ─────────────────────────────────────────────────
 * AD-23's Decline is drawn as a Tertiary S button whose icon and label are
 * text/danger (16 in from the fields' edge, 8 between icon and label).
 * `x-cw.button` has no such kind, so the colour is set here. (AD-21's Suspend
 * was one until v0.77 drew it as a blue text action.)
 */
.cw-btn--tertiary.cw-device-request-decline { color: var(--color-text-danger); }


/* ── AD-21: the scanners on a licence ────────────────────────────────────────
 * One row per scanner, a rule under each: the label, then its facts joined by
 * middle dots; v0.77 puts the Scanners list's text actions at the end (Rename ·
 * New key · Refresh token · Revoke, `.cw-row-actions`), the last on the card's
 * inner edge.
 */
.cw-licence-scanners { margin: 0; }
.cw-licence-scanner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-05);
  padding: var(--spacing-04) 0;
  box-shadow: inset 0 -1px 0 var(--color-border-subtle);
}
.cw-licence-scanner__main { min-width: 0; display: flex; flex-direction: column; gap: var(--spacing-02); }
.cw-licence-scanner__name {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--spacing-03);
  margin: 0;
  font: var(--type-body-s-strong);
  color: var(--color-text-primary);
}
.cw-licence-scanner__facts { margin: 0; font: var(--type-body-s); color: var(--color-text-secondary); }
/* The facts as flex items, so the source's line breaks add no space before a
   middle dot (`.cw-dot-sep` sets 4 either side of it). */
.cw-licence-scanner__facts { display: flex; flex-wrap: wrap; }

/* The paid features: Tags in a row that wraps. */
.cw-licence-features { display: flex; flex-wrap: wrap; align-items: center; gap: var(--spacing-03); }

/* The administration card: each action a section, 16 either side of a rule. */
.cw-licence-admin > * + * {
  margin-top: var(--spacing-05);
  padding-top: var(--spacing-05);
  box-shadow: inset 0 1px 0 var(--color-border-subtle);
}
/* v0.77 (ad-21): a row — what it is (strong) over what it does (secondary), and
   its action on the right: a text action with its icon, or Revoke licence. */
.cw-licence-admin__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-05);
}
.cw-licence-admin__row > form { display: contents; }
.cw-licence-admin__text { min-width: 0; display: flex; flex-direction: column; gap: var(--spacing-02); }
.cw-licence-admin__title { margin: 0; font: var(--type-body-s-strong); color: var(--color-text-primary); }
.cw-licence-admin__desc { margin: 0; font: var(--type-body-s); color: var(--color-text-secondary); }


/* ── AD-22: the queue ────────────────────────────────────────────────────────
 * The Card header holds the Waiting / All switch (48 tall): 16 above and below
 * it, the title centred on it.
 */
.cw-device-request-queue > .cw-card__head { align-items: center; padding-top: var(--spacing-05); }
/* 03 §4: "In a narrow card the gap shrinks, never below 24." Nine columns 40 apart
   run past the 1152 column, so this table sets them 24 apart, as ad-22 draws it; the
   first and last cells keep 24 at the card's edges, the action cell its own 8. */
.cw-device-request-queue .cw-table th,
.cw-device-request-queue .cw-table td:not(.cw-actions-cell) { padding-inline: var(--spacing-04); }
.cw-device-request-queue .cw-table tr > :first-child { padding-inline-start: var(--spacing-06); }
.cw-device-request-queue .cw-table tr > :last-child { padding-inline-end: var(--spacing-06); }
.cw-device-request-fleet { font: var(--type-body-s-strong); }
.cw-table td .cw-cell-sub.cw-device-request-quiet { color: var(--color-text-warning); }


/* ── AD-23: the customer's note and Iteas's answer keep their line breaks ── */
.cw-device-request-note .cw-row__value,
.cw-device-request-said { white-space: pre-line; }
.cw-device-request-said { margin: 0; font: var(--type-body); color: var(--color-text-primary); }

/* ── G1 — renewal and add-on requests, invoices (AD-24 to AD-29) ── */
/* ============================================================================
 * G1 — the admin requests and invoices of the v0.64 hand-off: AD-24 / AD-24b
 * Renewal requests, AD-25 Add-on requests, AD-26 / AD-26b Invoices, AD-27 Quote
 * a renewal, AD-28 Create invoice, AD-29 an invoice.
 *
 * ⏳ A REDESIGN SCAFFOLD, like every file in pages/: only what portal.css has no
 *    class for, measured on the PNGs at 1x. Loaded on every page, so every class
 *    is prefixed `cw-renewals`, `cw-addon-requests` or `cw-invoice`. Role tokens
 *    only — PortalShellTest scans this file for a raw colour.
 * ========================================================================== */


/* ── AD-24 Renewal requests · AD-25 Add-on requests ──────────────────────── */

/* A quoted request's Status, after the customer's name. */
.cw-renewals-quoted { margin-inline-start: var(--spacing-04); }

/* AD-24b, and the add-on's Decline: each dialog sits in the row it declines, so
   it would inherit the table cell's `white-space: nowrap`. Its question, its
   line and its field wrap like any dialog's. */
.cw-renewals-decline,
.cw-addon-requests-decline { white-space: normal; }


/* ── AD-26b Quote a renewal · choose a customer ──────────────────────────── */

/* The customer is a Combobox, and its list opens under the field, past the
   dialog's bottom edge. A dialog scrolls what overflows it, which clipped the
   list to the dialog; this one holds a single field, so its overflow shows. */
.cw-invoices-quote { overflow: visible; }


/* ── AD-27 Quote a renewal · AD-28 Create invoice ────────────────────────── */

/* The proforma panel: 400 wide (size/panel), the window's full height at its
   end, beside the page bar — the page bar, the canvas and the footer end where
   it starts (ad-27, ad-28: its rule at x 1040). The panel title in title-m 32
   from the top, the fields 16 apart (ad-27: 110, 192, 274), and at the foot,
   over a rule across the panel (y 699), Before tax and its total in price-l
   ("totals in a panel footer", 01 §3; 716–756), the tax line 12 under it, the
   L button 8 under that (798–846) and what a proforma is, 24 from the bottom.
   The panel is an element of the page's form, so its fields post with it. */
.cw-main:has(.cw-invoice-panel) { padding-inline-end: var(--size-panel); }
.cw-invoice-panel {
  position: fixed;
  inset-block: 0;
  inset-inline-end: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-05);
  width: var(--size-panel);
  padding: var(--spacing-07) var(--spacing-06) var(--spacing-06);
  background: var(--color-bg-surface);
  box-shadow: inset 1px 0 0 var(--color-border-subtle);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.cw-invoice-panel__title {
  margin: 0 0 var(--spacing-03);
  font: var(--type-title-m);
  letter-spacing: var(--type-title-m-tracking);
  color: var(--color-text-primary);
}
.cw-invoice-panel__foot {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-03);
  margin: auto calc(var(--spacing-06) * -1) 0;
  padding: var(--spacing-05) var(--spacing-06) 0;
  box-shadow: inset 0 1px 0 var(--color-border-subtle);
}
.cw-invoice-panel__total {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-05);
  margin: 0;
}
.cw-invoice-panel__total-label { font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-invoice-panel__total-value {
  font: var(--type-price-l);
  letter-spacing: var(--type-price-l-tracking);
  color: var(--color-text-primary);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* The total is price-l in Sans, as ad-27 draws it, though `admin._money` marks
   every amount `.cw-num` (Plex Mono). */
.cw-invoice-panel__total-value .cw-num { font-family: inherit; }
.cw-invoice-panel__line { margin: 0; font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-invoice-panel__total + .cw-invoice-panel__line { margin-top: var(--spacing-02); }
/* What a proforma is: 12 under the button (ad-27: 846 → 858). */
.cw-invoice-panel__foot + .cw-invoice-panel__line--last { margin-top: calc(var(--spacing-04) - var(--spacing-05)); }
/* v0.77 AD-27: the same terms as a card beside the licences, under a full-width
   page bar — no window panel, so the foot is not a ruled band across one. */
.cw-quote-terms .cw-invoice-panel__foot { margin: var(--spacing-06) 0 0; padding: 0; box-shadow: none; }
.cw-quote-terms .cw-invoice-panel__foot + .cw-invoice-panel__line--last { margin-top: var(--spacing-04); }

/* A date field's calendar (ad-27, ad-28): the Lucide calendar, 24 in
   icon/primary, drawn ON the browser's own picker button, so the native date
   picker, its keyboard and its value format stay exactly as they were. */
.cw-invoice-date input[type="date"]::-webkit-calendar-picker-indicator {
  width: var(--size-icon);
  height: var(--size-icon);
  margin: 0;
  padding: 0;
  background: var(--color-icon-primary);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 2v3'/%3E%3Cpath d='M16 2v3'/%3E%3Crect x='3' y='3' width='18' height='18' rx='2'/%3E%3Cpath d='M3 9h18'/%3E%3C/svg%3E") center / 24px 24px no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 2v3'/%3E%3Cpath d='M16 2v3'/%3E%3Crect x='3' y='3' width='18' height='18' rx='2'/%3E%3Cpath d='M3 9h18'/%3E%3C/svg%3E") center / 24px 24px no-repeat;
  cursor: pointer;
}


/* ── AD-29 an invoice ────────────────────────────────────────────────────── */

/* The document beside the balance: 768 and 360, 24 apart (ad-29: 264–1031,
   1057–1415). `.cw-detail` alone would give the aside its 400. */
.cw-invoice-detail { grid-template-columns: minmax(0, 1fr) 360px; }

/* Billed to and From, side by side, a rule under them (ad-29: y 277): the label,
   then the name in body-strong, then its lines in body-s, 4 apart; the GSTINs
   in mono. */
.cw-invoice-parties {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--spacing-06);
  box-shadow: inset 0 -1px 0 var(--color-border-subtle);
}
.cw-card__body.cw-invoice-parties > * + * { margin-top: 0; }
.cw-invoice-party { display: flex; flex-direction: column; gap: var(--spacing-02); min-width: 0; }
.cw-invoice-party > p { margin: 0; }
.cw-invoice-party__label { font: var(--type-label-s); letter-spacing: var(--type-label-s-tracking); color: var(--color-text-secondary); }
.cw-invoice-party__name { font: var(--type-body-strong); color: var(--color-text-primary); }
.cw-invoice-party__line { font: var(--type-body-s); color: var(--color-text-secondary); overflow-wrap: anywhere; }
.cw-invoice-party__line.cw-mono { font-family: var(--font-mono); }

/* The totals are rows of the lines table, white and ruled like them (ad-29: y
   477–637), not portal.css's grey totals band: the word under Description, the
   figure under Amount; Total one size up, body-strong over mono-m-strong. */
.cw-table.cw-invoice-lines tfoot th,
.cw-table.cw-invoice-lines tfoot td {
  position: static;
  background: none;
  box-shadow: inset 0 1px 0 var(--color-border-subtle);
  font: var(--type-body-s);
  color: var(--color-text-primary);
}
.cw-table.cw-invoice-lines tfoot td.cw-num { font: var(--type-mono-s); }
.cw-table.cw-invoice-lines tfoot td.cw-text-secondary { color: var(--color-text-secondary); }
.cw-table.cw-invoice-lines tfoot .cw-invoice-lines__total > th { font: var(--type-body-strong); }
.cw-table.cw-invoice-lines tfoot .cw-invoice-lines__total > td.cw-num { font: var(--type-mono-m-strong); }

/* The LUT reference, or the line saying it is missing, on a line of its own. */
.cw-invoice-lut { display: block; margin-top: var(--spacing-02); }

/* What can still be done to the document, after its totals (ad-29: Expire this
   proforma at y 663): a rule, 24, the title-s title, then its line and its
   button 12 apart. */
.cw-invoice-section {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--spacing-04);
  padding: var(--spacing-06);
  box-shadow: inset 0 1px 0 var(--color-border-subtle);
}
.cw-invoice-section__title { margin: 0; font: var(--type-title-s); letter-spacing: var(--type-title-s-tracking); color: var(--color-text-primary); }
.cw-invoice-section__line { margin: 0; font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-invoice-section__form { display: flex; flex-direction: column; align-items: flex-start; gap: var(--spacing-04); align-self: stretch; }
.cw-invoice-section__form > .cw-form-field { align-self: stretch; }

/* The aside: the balance as a Figure's label, value and note (ad-29: 136–216),
   then each section 48 under it (Related documents at 265), its title-s title
   over its Rows. The value is title-l in Sans, as drawn, though `admin._money`
   marks it `.cw-num`; money owed stays red, as it was. */
.cw-invoice-balance { display: flex; flex-direction: column; gap: var(--spacing-02); }
.cw-invoice-balance .cw-figure__value .cw-num { font-family: inherit; }
.cw-invoice-balance .cw-figure__value.cw-text-danger { color: var(--color-text-danger); }
.cw-invoice-aside > .cw-invoice-aside__section { margin-top: var(--spacing-09); }
.cw-invoice-aside__section { display: flex; flex-direction: column; gap: var(--spacing-02); }
.cw-invoice-aside__title { margin: 0; font: var(--type-title-s); letter-spacing: var(--type-title-s-tracking); color: var(--color-text-primary); }
/* v0.77 ad-29: under Related documents, what paying an outstanding proforma does
   ("Paid in full, it becomes a tax invoice.") — over a rule when rows precede it. */
.cw-invoice-aside__note { margin: 0; font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-rows + .cw-invoice-aside__note { padding-top: var(--spacing-04); box-shadow: inset 0 1px 0 var(--color-border-subtle); }


/* Narrower than drawn (only 1440 is), at portal.css's own breakpoints: the panel
   joins the page under the cards, as a card; the aside goes under the document. */
@media (max-width: 1100px) {
  .cw-main:has(.cw-invoice-panel) { padding-inline-end: 0; }
  .cw-invoice-panel {
    position: static;
    width: auto;
    padding: var(--spacing-06);
    border-radius: var(--radius-lg);
    box-shadow: inset 0 0 0 1px var(--color-border-hairline), var(--shadow-elevation-1);
    overflow: visible;
  }
  .cw-invoice-panel__foot { margin-top: var(--spacing-03); }
  .cw-invoice-detail { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 900px) {
  .cw-invoice-parties { grid-template-columns: minmax(0, 1fr); }
}

/* ── G2 — payments, ledger, plans & pricing, services & accessories (AD-30 to AD-33) ── */
/* ============================================================================
 * G2 — the admin billing screens of the v0.64 hand-off: AD-30 / AD-30b Payments,
 * AD-31 Ledger, AD-32 Plans & pricing, AD-33 Services & accessories.
 *
 * ⏳ A REDESIGN SCAFFOLD, like every file in pages/: only what portal.css has no
 *    class for, measured on the PNGs at 1x. Role tokens only — PortalShellTest
 *    scans this file for a raw colour.
 * ========================================================================== */


/* ── AD-30 Payments ──────────────────────────────────────────────────────── */

/* v0.77 Record a payment (AD-30c, AD-29b): Amount and Reference 176 wide,
   Method and Received on the rest, 16 apart (ad-30c: x 711–955 and 978–1288 of
   a 2000-wide drawing of a 2880 screen — 176 and 224 at 1x). */
.cw-payment-pair {
  display: grid;
  grid-template-columns: 176px minmax(0, 1fr);
  gap: var(--spacing-05);
  align-items: start;
}

/* AD-30d and AD-30b sit in the row they act on, so they would inherit the table
   cell's `white-space: nowrap`: their question and line must wrap like any dialog's. */
.cw-payments-dialog { white-space: normal; }


/* ── AD-31 Ledger ────────────────────────────────────────────────────────── */

/* The figure strip: Balance keeps a figure's 300, the ageing cell takes the
   rest (ad-31: x 264–564, 564–1416). */
.cw-ledger-figures > .cw-figure:first-child { flex: 0 0 300px; }

/* The balance is a Figure value — title-l in Sans, as ad-31 draws it — though
   `admin._money` marks every amount `.cw-num` (Plex Mono). */
.cw-ledger-figures .cw-figure__value .cw-num { font-family: inherit; }

/* Outstanding, by age past due: four label-over-amount pairs, 24 apart, 8
   under the cell's label; each label caption in text/secondary, 2 above its
   amount in mono-m (ad-31: x 590 / 796.5 / 1003 / 1210). */
.cw-ledger-ageing {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0 var(--spacing-06);
  margin: var(--spacing-02) 0 0;
}
.cw-ledger-ageing dt { font: var(--type-caption); color: var(--color-text-secondary); }
.cw-ledger-ageing dd { margin: var(--spacing-01) 0 0; font: var(--type-mono-m); color: var(--color-text-primary); }
.cw-ledger-ageing dd.cw-text-danger { color: var(--color-text-danger); }



/* ── AD-32 Plans & pricing ───────────────────────────────────────────────── */

/* The main column beside Change a price at 336, 24 apart (ad-32: 264–1056 and
   1080–1416). `.cw-detail` would give the aside its 400. */
.cw-pricing-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 336px;
  gap: var(--spacing-06);
  align-items: start;
}

/* The tier and its In force pill on one line, 8 apart; the pill keeps its 28
   here, because the row is already two lines tall. */
.cw-pricing-tier { display: flex; align-items: center; gap: var(--spacing-03); }
.cw-table td .cw-pricing-tier .cw-status--pill { margin-block: 0; }

/* An unpriced add-on: its line under the name — v0.77 ad-32 draws it amber
   (text/warning), in the sub-line's type; v0.64 had it red. */
.cw-table td .cw-cell-sub.cw-pricing-unpriced { color: var(--color-text-warning); }


/* ── AD-33 Services & accessories ────────────────────────────────────────── */

/* v0.77 AD-33b: Add and Edit are dialogs; an Edit sits in its row's cell, so it
   would inherit the table's `white-space: nowrap` — it wraps like any dialog. */
.cw-services-dialog { white-space: normal; }


/* Narrower than drawn (only 1440 is), as portal.css does for its own recipes. */
@media (max-width: 1100px) {
  .cw-pricing-layout { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 900px) {
  .cw-ledger-ageing { grid-template-columns: minmax(0, 1fr); }
}

/* ── H — usage, audit log, performance, migration, notifications (AD-34 to AD-38) ── */
/* ============================================================================
 * Admin · Insight and System — Usage history (AD-34), Audit log (AD-35),
 * Performance (AD-36), Migration (AD-37) and Notifications (AD-38) of the v0.64
 * hand-off. Role tokens only; every class is prefixed with its page.
 * ========================================================================== */


/* ── Usage history and Notifications: the customer picker (ad-34, ad-38) ──────
 * A card of its own holding one field and its button: the filter bar's parts,
 * without the rule under the bar (the card's edge is the end), 12 between the
 * field and the button, the field 360 wide as drawn.
 */
.cw-usage-picker,
.cw-notify-picker { gap: var(--spacing-04); box-shadow: none; background: none; }
.cw-usage-picker .cw-filter,
.cw-notify-picker .cw-filter { width: 360px; }
/* The Combobox's list opens below the field, past the bottom of this short card:
   the card must not clip it (a list card unclips itself the same way once its
   table fits, portal.css). Nothing in a picker card needs the clip once the form
   lets the card's own rounded surface show through (background: none above). */
.cw-card:has(> .cw-usage-picker),
.cw-card:has(> .cw-notify-picker) { overflow: visible; }


/* ── Usage history: the month bar (ad-34) ─────────────────────────────────────
 * 360 × 8, a pill: `data/track` under a `data/fill` length (01 §2.1). The length
 * is the SVG rect's own width attribute, so no inline style is needed.
 */
.cw-usage-bar {
  display: block;
  width: 360px;
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--color-data-track);
  overflow: hidden;
}
.cw-usage-bar svg { width: 100%; height: 100%; }
.cw-usage-bar__fill { fill: var(--color-data-fill); }

/* Two facts on a row's second line: a word, then a Status (ad-34, ad-37). */
.cw-table td .cw-usage-facts,
.cw-table td .cw-mig-facts { display: flex; align-items: center; gap: var(--spacing-03); }

/* One line under a table, inside its card (ad-34 "Days used", ad-37 "Live on"). */
.cw-usage-note,
.cw-mig-note {
  margin: 0;
  padding: var(--spacing-05) var(--spacing-06) var(--spacing-06);
  font: var(--type-body-s);
  color: var(--color-text-secondary);
}


/* ── Audit log: the source switch (ad-35) ─────────────────────────────────────
 * The Segmented control sits in a band of its own at the top of the list card,
 * over a rule, and the filters follow under it. It is the first item of the
 * filter bar, taking a whole line and reaching the card's edges.
 */
.cw-audit-sources {
  flex: 0 0 calc(100% + 2 * var(--spacing-06));
  margin: calc(var(--spacing-05) * -1) calc(var(--spacing-06) * -1) 0;
  padding: var(--spacing-05) var(--spacing-06);
  box-shadow: inset 0 -1px 0 var(--color-border-subtle);
  display: flex;
  align-items: center;
  gap: var(--spacing-05);
}
/* v0.77 ad-38: on Notices, a line at the band's far end, in text/secondary. */
.cw-audit-sources__note { margin: 0 0 0 auto; font: var(--type-body-s); color: var(--color-text-secondary); }


/* v0.77 ad-37: a long header wraps onto two lines ("QC scans in / window",
   "Old scanners / enabled"), so the factory table keeps the card's width. */
.cw-table th.cw-mig-head-wrap { white-space: normal; max-width: 112px; }

/* ── Performance, Migration, Notifications: lines under a Card header ─────────
 * ad-36, ad-37 and ad-38 put one or two short lines 16 under a table card's
 * title, 8 apart, and 16 over the table.
 */
.cw-perf-lines,
.cw-mig-lines,
.cw-notify-lines {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-03);
  padding: 0 var(--spacing-06) var(--spacing-05);
  font: var(--type-body-s);
  color: var(--color-text-secondary);
}
.cw-perf-lines strong,
.cw-mig-lines strong { font-weight: 500; color: var(--color-text-primary); }


/* ── Performance: the archive file (ad-36) ────────────────────────────────────
 * The path is truncated at 400 and its `title` carries the whole of it; the
 * digest under it is a code, so it stays in mono.
 */
.cw-perf-archive {
  display: block;
  max-width: var(--size-panel);
  overflow: hidden;
  text-overflow: ellipsis;
}
.cw-table td .cw-perf-sha { font: var(--type-mono-xs); }


/* ── Migration (ad-37) ────────────────────────────────────────────────────────
 * The inspection window: Measured and Measure again 24 apart in the card header;
 * the dates 220 wide, 12 apart, no rule under them — the figures follow in the
 * same card.
 */
.cw-mig-window .cw-card__action { gap: var(--spacing-06); }
.cw-mig-window__filters { gap: var(--spacing-04); box-shadow: none; }
.cw-mig-window__filters .cw-filter { width: 220px; }

/* QC coverage by agency: the plain column's header sits on the sub-headers'
   line, under the group band (ad-37). */
.cw-mig-coverage thead th[rowspan] { vertical-align: bottom; }

/* What is wrong: the Status, then its fact in ink on the line under it. */
.cw-mig-detail { display: block; }
.cw-mig-facts strong { font-weight: 500; }

/* A word in a figures column ("not read") is Sans, never mono (03 §8). */
.cw-mig-words { font: var(--type-body-s); }


/* ── Notifications (ad-38) ────────────────────────────────────────────────────
 * The event name and the mail server's answer are codes: mono, on the second
 * line. Allow a retry follows on its own line. The two cards under the log
 * share one height.
 */
.cw-table td .cw-notify-code { font: var(--type-mono-xs); }
.cw-notify-retry { margin-top: var(--spacing-02); }
.cw-notify-pair { align-items: stretch; }

/* ============================================================================
 * v0.77 (4 Oct 2026) — A CUSTOMER'S PAGE, AD-42 / AD-43: the Overview's rows
 * ========================================================================== */
/* A row of a card's list (a licence, a scanner, a document, an event): two lines
   on the start, its state and its text action on the end, a rule under each. */
.cw-customer-rows { display: flex; flex-direction: column; margin: 0; padding: 0; list-style: none; }
.cw-customer-row {
  display: flex;
  align-items: center;
  gap: var(--spacing-04);
  min-height: 56px;
  padding: var(--spacing-03) 0;
  box-shadow: inset 0 -1px 0 var(--color-border-subtle);
}
.cw-customer-row__text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--spacing-01); }
.cw-customer-row__title { font: var(--type-body-s); color: var(--color-text-primary); }
.cw-customer-row__sub { font: var(--type-caption); color: var(--color-text-secondary); }
.cw-customer-row > .cw-status { flex: none; }
.cw-customer-empty { margin: 0; font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-customer-more { margin: var(--spacing-04) 0 0; }
.cw-customer-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--spacing-05); margin-top: var(--spacing-05); }
.cw-customer-actions form { display: contents; }
/* AD-42 "Sign-in and PIN": the email reads as a value under its label (README §4). */
.cw-customer-stacked {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-01);
  padding: var(--spacing-03) 0;
  box-shadow: inset 0 -1px 0 var(--color-border-subtle);
}
.cw-customer-stacked dt { font: var(--type-body-s); color: var(--color-text-secondary); }
.cw-customer-stacked dd { margin: 0; font: var(--type-body-s-strong); color: var(--color-text-primary); overflow-wrap: anywhere; }
.cw-customer-note { margin: calc(-1 * var(--spacing-03)) 0 0; font: var(--type-caption); color: var(--color-text-secondary); }
/* "Set a password yourself": a disclosure that reads as a text action until open. */
.cw-customer-disclosure > summary {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-02);
  min-height: var(--size-target-min);
  font: var(--type-label-s);
  color: var(--color-text-link);
  cursor: pointer;
  list-style: none;
}
.cw-customer-disclosure > summary::-webkit-details-marker { display: none; }
.cw-customer-disclosure > summary::before { content: "+"; font: var(--type-body-s-strong); }
.cw-customer-disclosure[open] > summary::before { content: "–"; }
.cw-customer-disclosure[open] > .cw-fields { margin-top: var(--spacing-04); }
/* v0.77 AD-42b: "Send a password link" with its grey line beside it. */
.cw-customer-inline { display: flex; flex-wrap: wrap; align-items: center; gap: var(--spacing-04) var(--spacing-05); }
.cw-customer-inline__note { margin: 0; font: var(--type-body-s); color: var(--color-text-secondary); }
/* v0.77: a pager at the foot of a PADDED card (a customer's Activity tab) sits on
 * the card's own edge, under the last row's rule — not indented a second time
 * and not ruled twice, as it is at the foot of a full-bleed table. */
.cw-card--pad > .cw-pager { padding-inline: 0; box-shadow: none; }
/* v0.77 AD-03: "Kind of customer" is the card's title, so the radio group's own
 * legend is kept for a screen reader and not drawn twice. */
.cw-radio-group--bare > legend { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
/* v0.77 AD-04: "Total (bold, top rule)". */
.cw-table tr.cw-onboarding-invoice-total > td { box-shadow: inset 0 1px 0 var(--color-border-strong); }
/* v0.77 AD-13: a row's text actions, inline as drawn — 16 apart, a form among
 * them taking no room of its own. */
.cw-row-actions { display: inline-flex; flex-wrap: nowrap; align-items: center; gap: var(--spacing-05); white-space: nowrap; }
.cw-row-actions form { display: contents; }
/* A second, short words column that may wrap (a scanner's label) — narrower than
 * the spare-taking one, so the two never squeeze each other to a word a line. */
.cw-table td.cw-wrap--120,
.cw-table th.cw-wrap--120 { min-width: 120px; }
/* A code that may be long but reads whole — a payment's UTR or cheque reference:
   it wraps at a space or a hyphen ("IDFB-NEFT-UTR 2026093012345"), never inside
   "HDFCN52026092188", so the column is as wide as its longest code (AD-30). */
.cw-table td.cw-wrap--words { overflow-wrap: break-word; }
/* A name that is a link wraps with its cell (a link is otherwise one line). */
.cw-table td.cw-wrap .cw-link { white-space: normal; text-align: start; }
