/* ==========================================================================
   Blacktip Industries - order portal and admin components.

   Section K of the specification, and the component library of the design
   system (section AI).

   This file is loaded by everything under /orders/: the temporary customer
   portal AND the PERMANENT admin application, which outlives customer ordering
   (AC.0). css/styles.css carries the design TOKENS and the button system and
   is the permanent public site's stylesheet; this file carries the components
   built on top of them. Removing this file removes the application's styling
   and leaves the public site untouched (C.7, RETIRE-1).

   Nothing here invents a colour, a radius, a focus ring or a button geometry.
   Every one of those comes from a token in styles.css, so a published design
   reaches every component at once and no page can drift.

   Nothing here introduces a new font, a new radius or a new accent hue (K.1).
   The four status colours are the only additions to the palette, and every
   status is also carried by a glyph and by words, so colour is never the only
   signal (K.2).
   ========================================================================== */

:root {
  --success:       #2E7D32;
  --success-wash:  #E8F3E9;
  --warning:       #B26A00;
  --warning-wash:  #FFF4E5;
  --danger:        #B3261E;
  --danger-wash:   #FCEBEA;
  --info:          #0080B8;
  --info-wash:     #E6F5FC;
  --table-stripe:  #F8F9FA;

  --app-shell:     1600px;
  --sidebar:       232px;
}

/* --------------------------------------------------------------------------
   App page frame. Application pages use 32 px section padding, not the
   marketing page's 64-110 px (K.4).
   -------------------------------------------------------------------------- */

/* The application shells sit on the neutral ground, and the BODY carries it as
   well as the shell. Without that the shell stops at its own height and the
   paper behind it shows as a white band across the bottom of any page shorter
   than the window — which is most of the signed-out screens. */
.app-body { background: var(--neutral); }

.app {
  padding: var(--space-8) 0 var(--space-10);
  background: var(--neutral);
  min-height: 60vh;
}

/* THE PAGE HEADER, ON EVERY APPLICATION SCREEN.

   One header, so a screen cannot look as though it belongs to a different
   application (AI.21). The title and its context sit on the left, the page's
   actions on the right of the same line, and a hairline closes the header off
   from the work beneath it. The actions used to sit UNDER the title, which
   pushed every page down by a button and left a band of empty grey across the
   top of the screen.

   `.page__head` is the same header. New markup uses the clearer name; the
   older `.app__head` is not renamed across forty templates for the sake of it. */
.app__head,
.page__head {
  /* A GRID, not a flex row, and the difference is not academic.
     `justify-content: space-between` spreads EVERY child across the line, and
     most of these headers carry their eyebrow, title and context as separate
     siblings — so the eyebrow went left, the title floated to the middle and
     the page looked as though it had been dropped. A grid keeps the heading
     stacked in the first column and gives the actions the second, whatever
     number of elements the heading is made of. */
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  column-gap: var(--space-6);
  margin-bottom: var(--section-gap);
  padding-bottom: var(--space-4);
  border-bottom: var(--border-width) solid var(--line);
}

/* Everything that is not an action stacks in the heading column. */
.app__head > *:not(.app__actions):not(.action-group),
.page__heading { grid-column: 1; min-width: 0; }

/* The actions sit in the second column, aligned to the bottom of the heading
   however tall it is. */
.app__head > .app__actions,
.app__head > .action-group,
.page__head > .app__actions,
.page__head > .action-group {
  grid-column: 2;
  grid-row: 1 / -1;
  align-self: end;
  justify-self: end;
}

.app__head .eyebrow,
.page__heading .eyebrow {
  margin-bottom: var(--space-1);
  font-size: var(--font-size-eyebrow);
  letter-spacing: var(--tracking-eyebrow);
  color: var(--grey-mid);
}

.app__head h1,
.page__heading h1 {
  /* the PAGE TITLE role */
  font-size: var(--font-size-page-title);
  line-height: var(--line-height-heading);
  letter-spacing: var(--tracking-heading);
  margin: 0;
}

.app__head > p,
.page__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-2) 0 0;
  max-width: 78ch;
  font-size: var(--font-size-small);
  color: var(--grey-mid);
}

/* Page-header actions sit on the header line, not under it. */
.app__actions {
  margin-top: 0;
  flex: 0 0 auto;
}

@media (max-width: 900px) {
  .app__head,
  .page__head { grid-template-columns: minmax(0, 1fr); }

  .app__head > .app__actions,
  .app__head > .action-group,
  .page__head > .app__actions,
  .page__head > .action-group {
    grid-column: 1;
    grid-row: auto;
    justify-self: stretch;
    margin-top: var(--space-4);
  }
}

/* --------------------------------------------------------------------------
   Panels and tiles (K.7)
   -------------------------------------------------------------------------- */

.panel {
  background: var(--paper);
  border: var(--border-width) solid var(--line);
  border-radius: var(--card-radius);
  padding: var(--card-padding);
  margin-bottom: var(--section-gap);
}

.panel--rule { border-top: 4px solid var(--blue); }

/* Semantic panels, from the same tokens as the notices. */
.panel--info    { border-left: 4px solid var(--info); }
.panel--warning { border-left: 4px solid var(--warning); }
.panel--danger  { border-left: 4px solid var(--danger); }

.panel__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
}

.panel h2 {
  font-size: 24px;
  letter-spacing: -0.02em;
  margin: 0;
}

.panel h3 {
  font-size: 19px;
  letter-spacing: -0.01em;
  margin: 0 0 8px;
}

.panel__note {
  color: var(--charcoal-soft);
  font-size: 14px;
  margin: 0 0 16px;
  max-width: 78ch;
}

.grid-2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 24px;
}

.grid-3 {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
}

.tile {
  display: block;
  min-height: 120px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 24px;
  text-decoration: none;
  color: inherit;
  transition: transform 150ms ease, box-shadow 150ms ease;
}

.tile:hover {
  transform: translateY(-3px);
  box-shadow: 0 6px 18px rgba(38, 36, 37, 0.08);
}

.tile__figure {
  display: block;
  font-size: 34px;
  font-weight: 800;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}

.tile__label {
  display: block;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--grey-mid);
  margin-bottom: 8px;
}

.tile__note {
  display: block;
  font-size: 14px;
  color: var(--charcoal-soft);
  margin-top: 8px;
}

/* --------------------------------------------------------------------------
   Forms (K.6). Labels above inputs; "(required)" is written out, because a
   colour or an asterisk alone is not an accessible signal.
   -------------------------------------------------------------------------- */

.field { margin-bottom: var(--field-gap); }

/* the LABEL role */
.field > label,
.fieldset__legend {
  display: block;
  font-size: var(--font-size-label);
  font-weight: var(--weight-label);
  line-height: 1.35;
  margin-bottom: 6px;
}

.field__req {
  font-weight: 400;
  color: var(--charcoal-soft);
}

/* ONE CONTROL FAMILY.

   Every single-line control is the SAME HEIGHT, stated rather than accumulated
   from padding. That is the fix for the filter bar the owner rejected, where a
   text input and a select sat side by side at visibly different heights: the
   input was sized by its padding and the select by whatever the browser thought
   a select should be, and no amount of matching padding could reconcile them.

   `height`, not `min-height`. A minimum lets a select grow to its intrinsic
   size and the mismatch comes straight back. */
.field input[type="text"],
.field input[type="email"],
.field input[type="tel"],
.field input[type="password"],
.field input[type="number"],
.field input[type="search"],
.field input[type="date"],
.field select,
.control {
  width: 100%;
  height: var(--control-height);
  padding: 0 var(--control-padding-x);
  font: inherit;
  font-size: var(--control-font);
  line-height: 1.2;
  color: var(--charcoal);
  background: var(--paper);
  border: var(--border-width) solid var(--line);
  border-radius: var(--control-radius);
  /* The browser's own select chrome is one of the primitive controls the owner
     objected to. The arrow below is drawn by the design system instead. */
  appearance: none;
  -webkit-appearance: none;
}

.field textarea {
  width: 100%;
  min-height: 96px;
  padding: var(--control-padding-y) var(--control-padding-x);
  font: inherit;
  font-size: var(--control-font);
  line-height: var(--line-height-body);
  color: var(--charcoal);
  background: var(--paper);
  border: var(--border-width) solid var(--line);
  border-radius: var(--control-radius);
  resize: vertical;
}

/* The select arrow, drawn once, in the system. Inline SVG as a data URI so it
   costs no request and cannot fail to load. */
.field select,
select.control {
  padding-right: calc(var(--control-padding-x) + 20px);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%237E7F82' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--control-padding-x) center;
}

/* A smaller control, for a dense toolbar or a grid. Same family, one step down. */
.field--sm input,
.field--sm select,
.control--sm { height: var(--control-height-sm); font-size: var(--font-size-small); }

.field input::placeholder,
.field textarea::placeholder { color: var(--grey-mid); }

.field input:hover,
.field select:hover,
.field textarea:hover { border-color: var(--grey-light); }

.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible,
.control:focus-visible {
  outline: var(--focus-width) solid var(--blue);
  outline-offset: 1px;
  border-color: var(--blue-dark);
}

.field__help {
  font-size: var(--font-size-helper);
  line-height: 1.5;
  color: var(--grey-mid);
  margin: 6px 0 0;
}

.field__error {
  font-size: var(--font-size-helper);
  font-weight: 600;
  color: var(--danger);
  margin: 6px 0 0;
}

.field__error::before { content: "! "; }

.field--invalid input,
.field--invalid select,
.field--invalid textarea { border-color: var(--danger); }

.field--check {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.field--check input { margin-top: 3px; }

.field--check label {
  font-weight: 400;
  font-size: 15px;
  margin: 0;
}

/* A honeypot: off-screen for a browser, unreachable for a keyboard, invisible
   to a screen reader. Never display:none, which some automated submitters
   look for. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.segmented {
  display: inline-flex;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}

.segmented label {
  margin: 0;
  padding: 10px 18px;
  font-size: 15px;
  font-weight: 600;
  background: var(--paper);
  cursor: pointer;
  border-right: 1px solid var(--line);
}

.segmented label:last-child { border-right: 0; }

.segmented input { position: absolute; opacity: 0; width: 1px; height: 1px; }

.segmented input:checked + span,
.segmented label:has(input:checked) {
  background: var(--charcoal);
  color: var(--paper);
}

/* Focus must be visible on the segmented control. The sibling rule works
   everywhere; the :has() rule is the nicer version where it is supported. */
.segmented input:focus-visible + span,
.segmented label:has(input:focus-visible) {
  outline: 3px solid var(--blue);
  outline-offset: -3px;
}

/* Layout comes from the shared action group in styles.css; only the space
   above it belongs to the form. */
.form-actions { margin-top: var(--section-gap); }

.form-row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 20px;
}

/* --------------------------------------------------------------------------
   Error summary at the top of a form, linked to the fields (K.14)
   -------------------------------------------------------------------------- */

.errors {
  border: 1px solid var(--danger);
  border-left-width: 4px;
  background: var(--danger-wash);
  border-radius: var(--radius);
  padding: 16px 20px;
  margin-bottom: 24px;
}

.errors h2 {
  font-size: 16px;
  margin: 0 0 8px;
  color: var(--danger);
}

.errors ul { margin: 0; padding-left: 20px; }

.errors li { margin-bottom: 4px; }

/* --------------------------------------------------------------------------
   Alerts (K.12)
   -------------------------------------------------------------------------- */

.alert {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  border: 1px solid var(--line);
  border-left-width: 4px;
  border-radius: var(--radius);
  padding: 14px 18px;
  margin-bottom: 20px;
  font-size: 15px;
}

.alert__glyph { font-weight: 800; }

.alert p { margin: 0; }

.alert--success { border-color: var(--success); background: var(--success-wash); }
.alert--info    { border-color: var(--info);    background: var(--info-wash); }
.alert--warning { border-color: var(--warning); background: var(--warning-wash); }
.alert--danger  { border-color: var(--danger);  background: var(--danger-wash); }

.alert code {
  word-break: break-all;
  font-size: 13px;
  background: rgba(255, 255, 255, 0.6);
  padding: 1px 4px;
}

/* --------------------------------------------------------------------------
   Status chips (K.10)
   -------------------------------------------------------------------------- */

.chip {
  display: inline-block;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 4px 10px;
  border-radius: var(--radius);
  border: 1px solid var(--grey-mid);
  background: var(--neutral);
  color: var(--charcoal);
  white-space: nowrap;
}

.chip--success { border-color: var(--success); background: var(--success-wash); color: var(--success); }
.chip--info    { border-color: var(--info);    background: var(--info-wash);    color: var(--info); }
.chip--warning { border-color: var(--warning); background: var(--warning-wash); color: var(--warning); }
.chip--danger  { border-color: var(--danger);  background: var(--danger-wash);  color: var(--danger); }

/* --------------------------------------------------------------------------
   Tables (K.8)
   -------------------------------------------------------------------------- */

.table-wrap { overflow-x: auto; }

.table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--font-size-small);
}

.table th {
  text-align: left;
  font-size: var(--font-size-table-head);
  font-weight: var(--weight-heading);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--grey-mid);
  /* A hairline under the head, not a rule across the page. The 2px charcoal
     bar belonged to the boxy treatment the owner rejected. */
  border-bottom: var(--border-width) solid var(--line);
  padding: var(--cell-padding-y) var(--cell-padding-x);
  white-space: nowrap;
}

.table td {
  padding: var(--cell-padding-y) var(--cell-padding-x);
  border-bottom: var(--border-width) solid var(--line);
  vertical-align: top;
}

/* First and last cells sit against the card edge, not indented from it. */
.table th:first-child,
.table td:first-child { padding-left: 0; }
.table th:last-child,
.table td:last-child  { padding-right: 0; }

/* No zebra striping. Stripes were a way to follow a row across a wide table
   before there was anything better; with adequate cell padding and a hairline
   they only add noise. The hover band still says which row is under the
   pointer. */
.table tbody tr:hover { background: var(--table-stripe); }

.table__num { text-align: right; font-variant-numeric: tabular-nums; }

.table__empty {
  padding: 24px 12px;
  color: var(--charcoal-soft);
}

.table__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* --------------------------------------------------------------------------
   Definition strips, reused from the marketing spec strip idiom
   -------------------------------------------------------------------------- */

.facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 16px 24px;
  margin: 0;
}

.facts dt {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--grey-mid);
}

.facts dd {
  margin: 4px 0 0;
  font-size: 16px;
  font-weight: 600;
  word-break: break-word;
}

/* --------------------------------------------------------------------------
   Buttons: the additions of K.5. The base .btn styles come from styles.css.
   -------------------------------------------------------------------------- */

/* .btn--sm, .btn--danger, .btn--confirm and the disabled treatment moved to
   css/styles.css, so the whole button system lives in ONE file and a page
   cannot pick up half of it. Only .btn-link remains here: it is not a button
   variant but a link that happens to be a <button> element. */

.btn-link {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--blue-dark);
  text-decoration: underline;
  cursor: pointer;
}

/* --------------------------------------------------------------------------
   Signed-out card screens: C-0A, C-1, C-2, C-3, A-1
   -------------------------------------------------------------------------- */

.card-page {
  background: var(--neutral);
  min-height: 78vh;
  padding: 48px 0 96px;
}

.card-page__inner {
  width: 100%;
  max-width: 420px;
  margin: 0 auto;
  padding: 0 20px;
}

.card-page--wide .card-page__inner { max-width: 560px; }

/* THE SHARED BRAND MARK, on the signed-out screens.

   These pages used to size the logo themselves — a 190px box with the image
   stretched to fill it — which is authentication-specific logo styling of
   exactly the kind AI.1 forbids, and it fought the configured branding
   container: a padded, bordered container cannot present a logo properly when
   the logo inside it has been told to be 100% wide.

   The logo is now sized by HEIGHT, from a shared token, the same way the site
   header sizes it. Whatever container Settings > Branding configures — its
   background, padding, border, radius and shadow — is what appears. */
.card-page__logo {
  display: flex;
  justify-content: center;
  margin: 0 0 var(--space-6);
}

.card-page__logo img {
  display: block;
  height: var(--logo-height-auth);
  width: auto;
}

.card-box {
  background: var(--paper);
  border: var(--border-width) solid var(--line);
  border-radius: var(--radius);
  padding: 32px;
}

.card-box h1 {
  font-size: 24px;
  letter-spacing: -0.02em;
  margin: 0 0 8px;
}

.card-box__lede {
  margin: 0 0 24px;
  color: var(--charcoal-soft);
  font-size: 15px;
}

.card-links {
  margin: 24px 0 0;
  padding: 20px 0 0;
  border-top: 1px solid var(--line);
  font-size: 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.card-page__back {
  text-align: center;
  margin-top: 24px;
  font-size: 14px;
}

/* Password field with the compact reveal control (owner review, steps 29-33).

   The control sits INSIDE the field, as an eye / eye-slash icon. It replaced a
   large textual HIDE button that appeared on some screens and not others. The
   touch target is 40px so it is usable on a phone, which is where the owner
   most wants someone to be able to check what they typed. */

.pw-field { position: relative; display: block; }

.pw-field input {
  /* Room for the button, so a long password never runs underneath it. */
  padding-right: 48px;
  width: 100%;
}

/* The reveal control IS the design system's icon button; it only needs
   positioning inside the field. Its colour, hover, focus ring and touch target
   come from `.icon-btn`, so it cannot drift from the other icon controls.
   (--grey-mid clears the 3:1 a non-text control needs; --grey does not.) */
.pw-field__eye {
  position: absolute;
  top: 50%;
  right: 4px;
  transform: translateY(-50%);
}

.pw-field__icon { display: block; }
.pw-field__icon[hidden] { display: none; }

/* The rules a password must satisfy, shown before it is typed (step 28). */
.requirements {
  margin: 0 0 16px;
  padding: 12px 16px;
  background: var(--neutral);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-size: 14px;
}

.requirements__lede { margin: 0 0 6px; font-weight: 600; }
.requirements ul { margin: 0; padding-left: 20px; }
.requirements li { margin: 2px 0; }

/* The shared logo container (steps 8-10). Every visual property is set inline
   from the validated branding settings; this rule only makes the wrapper behave
   like the image it replaces. */
.brandmark { line-height: 0; }
.brandmark img { display: block; }

/* Settings sub-navigation. */
/* --------------------------------------------------------------------------
   TABS (design system component).

   One tab bar, used by Settings and by anything that needs one later. The four
   states are defined here and nowhere else: a page renders `.tabs` and marks
   the current item with aria-current, which is both the accessible signal and
   the styling hook.
   -------------------------------------------------------------------------- */

.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 0 0 24px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--line);
}

.tabs a {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch-target);
  padding: var(--tab-padding-y) var(--tab-padding-x);
  border: var(--border-width) solid transparent;
  border-radius: var(--control-radius);
  font-size: var(--font-size-label);
  font-weight: var(--weight-label);
  line-height: var(--line-height-body);
  color: var(--charcoal);
  text-decoration: none;
}

.tabs a:hover {
  background: var(--neutral);
  border-color: var(--line);
  text-decoration: none;
}

.tabs a[aria-current="page"] {
  background: var(--charcoal);
  border-color: var(--charcoal);
  color: var(--paper);
}

.tabs a[aria-current="page"]:hover {
  background: var(--charcoal);
  border-color: var(--charcoal);
  color: var(--paper);
}

.tabs a:focus-visible {
  outline: var(--focus-width) solid var(--focus-colour);
  outline-offset: var(--focus-offset);
}

/* --------------------------------------------------------------------------
   SECTION HEADER (design system role).

   A heading that introduces a group of panels WITHIN a page, as distinct from
   the page header itself. Both are shared components; the difference is scale
   and which one a page is allowed to have (one page header, any number of
   section headers).
   -------------------------------------------------------------------------- */

.section-head {
  margin: var(--space-8) 0 var(--space-4);
}

.section-head .eyebrow { margin-bottom: var(--space-1); }

.section-head h2 { margin: 0 0 var(--space-1); }

.section-head p {
  margin: 0;
  max-width: 70ch;
  font-size: var(--font-size-small);
  color: var(--charcoal-soft);
}

/* The PAGE HEADER is `.app__head`, defined once near the top of this file:
   an eyebrow naming the section, a title naming the page, and an optional
   description. The Settings shell uses that same component rather than a
   second one that merely looks like it. */

/* --------------------------------------------------------------------------
   ICON BUTTON (design system component).

   A square control whose whole content is an icon. It carries an accessible
   name rather than visible text, and it takes its focus ring from the same
   token as every other control. The password reveal control is one of these.
   -------------------------------------------------------------------------- */

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--icon-btn-size);
  height: var(--icon-btn-size);
  padding: 0;
  border: 0;
  border-radius: var(--control-radius);
  background: transparent;
  color: var(--grey-mid);
  cursor: pointer;
}

.icon-btn svg { width: var(--icon-size); height: var(--icon-size); }

/* Not every icon is an SVG: the row menu is a midline ellipsis and the drawer
   closes with a multiplication sign. A text glyph inherited the body size and
   came out noticeably smaller than the drawn ones, which the Component Gallery
   made obvious by showing them side by side. */
.icon-btn { font-size: var(--icon-size); line-height: 1; }

.icon-btn:hover { color: var(--charcoal); background: var(--neutral); }

.icon-btn:focus-visible {
  outline: var(--focus-width) solid var(--focus-colour);
  outline-offset: -1px;
  color: var(--charcoal);
}

.icon-btn[disabled] { opacity: var(--disabled-opacity); pointer-events: none; }

/* --------------------------------------------------------------------------
   MODAL / DIALOG (design system component).

   Built on the native <dialog> element, so the browser supplies modal
   semantics, the focus trap, the inert background and Escape. That is why
   there is no hand-rolled overlay with click handlers on a div here: the
   accessible behaviour is the platform's, not an approximation of it.
   -------------------------------------------------------------------------- */

.modal {
  width: min(30rem, calc(100vw - 32px));
  max-width: 30rem;
  padding: 0;
  border: var(--border-width) solid var(--line);
  border-radius: var(--modal-radius);
  background: var(--paper);
  color: var(--charcoal);
  box-shadow: var(--shadow-3);
}

.modal::backdrop { background: rgba(38, 36, 37, .55); }

.modal__inner { padding: 24px; }

.modal__title {
  margin: 0 0 10px;
  font-size: 20px;
  line-height: 1.25;
}

.modal__body {
  margin: 0 0 22px;
  font-size: 15px;
  line-height: 1.6;
  color: var(--charcoal-soft);
}

.modal__body p { margin: 0 0 10px; }
.modal__body p:last-child { margin-bottom: 0; }

/* Layout comes from the shared action group; the dialog only decides that its
   actions sit at the end of the row. */
.modal__actions { justify-content: flex-end; }

/* A destructive dialog says so before it is read: the rule at the top is the
   same colour as the action at the bottom. */
.modal--danger { border-top: 4px solid var(--danger); }

@media (max-width: 480px) {
  .modal__inner { padding: var(--space-5); }
  /* The safe action ends up on top when the row becomes a column. */
  .modal__actions { flex-direction: column-reverse; }
}

/* --------------------------------------------------------------------------
   TOAST / NOTIFICATION (design system component).

   For feedback that does not need to block: a short confirmation of something
   that has already happened. Anything the user must acknowledge before
   continuing is a modal, and anything that survives a redirect is a flash
   notice; this is the third case and only the third case.
   -------------------------------------------------------------------------- */

.toasts {
  position: fixed;
  z-index: 100;
  right: 16px;
  bottom: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: min(24rem, calc(100vw - 32px));
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 14px;
  border: var(--border-width) solid var(--line);
  border-left-width: 4px;
  border-radius: var(--card-radius);
  background: var(--paper);
  box-shadow: var(--shadow-2);
  font-size: var(--font-size-small);
  line-height: var(--line-height-body);
  pointer-events: auto;
}

.toast p { margin: 0; }
.toast__glyph { font-weight: 800; }

.toast--success { border-left-color: var(--success); }
.toast--info    { border-left-color: var(--info); }
.toast--warning { border-left-color: var(--warning); }
.toast--danger  { border-left-color: var(--danger); }

@media (prefers-reduced-motion: no-preference) {
  .toast { animation: toast-in .18s ease-out; }
}

@keyframes toast-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------------------
   DESIGN SYSTEM component preview (Settings -> Design System).

   The preview renders the REAL components inside a scoped token override, so
   what is shown is what the published design would produce rather than a
   drawing of it.
   -------------------------------------------------------------------------- */

.preview {
  display: grid;
  gap: 20px;
}

.preview__group {
  padding: 20px;
  border: 1px solid var(--line);
  border-radius: var(--card-radius);
  background: var(--paper);
}

.preview__group > h3 {
  margin: 0 0 14px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--grey-mid);
}

.preview__row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
}

/* Declares itself a dark surface, so the controls inside it are styled for
   one. It is not simply a box with a dark background. */
.preview__dark {
  padding: var(--space-5);
  border-radius: var(--card-radius);
}

.swatch {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
}

.swatch__chip {
  width: 34px;
  height: 34px;
  border: 1px solid var(--line);
  border-radius: var(--control-radius);
}

/* The spacing scale, drawn from the tokens themselves. */
.scale { display: grid; gap: var(--space-2); }

.scale__row {
  display: grid;
  grid-template-columns: 90px 1fr 60px;
  gap: var(--space-3);
  align-items: center;
}

.scale__label { font-size: var(--font-size-helper); color: var(--grey-mid); }

.scale__bar {
  height: 14px;
  background: var(--blue);
  border-radius: var(--control-radius);
}

.swatches {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 12px;
}

/* A colour input should look like the other controls around it. */
.field input[type="color"] {
  width: 100%;
  height: 44px;
  padding: 4px;
  border: 1px solid var(--line);
  border-radius: var(--control-radius);
  background: var(--paper);
  cursor: pointer;
}

.field--colour {
  display: grid;
  grid-template-columns: 1fr 92px;
  gap: 10px;
  align-items: end;
}

.field--colour .field { margin-bottom: 0; }

/* A ghost button is styled for a DARK surface: its label is near-white. Inside
   a panel that made the Clear control on the customers list read as an
   unlabelled rectangle. The markup no longer does this, and this rule means the
   same mistake cannot be made again without being visible. */
.panel .btn--ghost,
.app__head .btn--ghost {
  border-color: var(--line);
  color: var(--charcoal);
}

/* ...but a DELIBERATELY dark box inside a panel is not that mistake, and the
   guard above was reaching into one. It painted the component preview's ghost
   button charcoal on charcoal-deep — a measured 1.08:1, which is why the owner
   could not read it. A declared dark surface wins here, as it should. */
.panel .surface--dark .btn--ghost,
.app__head .surface--dark .btn--ghost {
  border-color: var(--grey-light);
  color: var(--paper);
}

.panel .surface--dark .btn--ghost:hover,
.app__head .surface--dark .btn--ghost:hover {
  border-color: var(--paper);
  color: var(--paper);
  background: rgba(255, 255, 255, .10);
}

/* Ship-via lifecycle controls, side by side under the edit form. Geometry
   comes from the shared action group in styles.css. */
.row-actions form { margin: 0; }

/* --------------------------------------------------------------------------
   FILTER BAR (layout pattern).

   A search or filter form above a table: the fields on one row, the actions on
   the next, and on a narrow screen everything stacks. The Customers page used
   to lay this out for itself, which is how APPLY and CLEAR came to be
   different sizes.
   -------------------------------------------------------------------------- */

/* The rules themselves are further down the file, with the components added in
   the 2026-09-06 correction pass, so the whole filter bar is described in one
   place rather than half here and half there. */

/* --------------------------------------------------------------------------
   EMPTY STATE (layout pattern).
   -------------------------------------------------------------------------- */

.empty-state {
  padding: var(--space-8) var(--space-5);
  text-align: center;
  color: var(--grey-mid);
  font-size: var(--font-size-small);
}

.empty-state h3 {
  margin: 0 0 var(--space-2);
  font-size: var(--font-size-subsection);
  color: var(--charcoal);
}

/* Logo preview, against both surfaces the shared component serves. */
.logo-preview {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 12px;
  margin-bottom: 20px;
}

.logo-preview > div {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  padding: 20px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.logo-preview__light { background: var(--paper); }
.logo-preview__dark { background: var(--charcoal-deep); }

.logo-preview__label {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.logo-preview__light .logo-preview__label { color: var(--grey); }
.logo-preview__dark .logo-preview__label { color: rgba(255, 255, 255, .7); }

.logo-preview img { height: 46px; width: auto; }

/* --------------------------------------------------------------------------
   Portal shell (H.2)
   -------------------------------------------------------------------------- */

.portal-bar {
  background: var(--charcoal-deep);
  color: var(--paper);
  border-bottom: 3px solid var(--blue);
}

.portal-bar__inner {
  display: flex;
  align-items: center;
  gap: 24px;
  flex-wrap: wrap;
  padding: 12px 0;
}

.portal-bar__logo { flex: 0 0 auto; width: 150px; }

.portal-bar__logo img { display: block; width: 100%; height: auto; }

.portal-nav {
  display: flex;
  gap: 20px;
  flex-wrap: wrap;
  margin-left: auto;
  align-items: center;
}

.portal-nav a {
  color: var(--grey-light);
  text-decoration: none;
  font-size: 15px;
  font-weight: 600;
}

.portal-nav a:hover,
.portal-nav a[aria-current="page"] {
  color: var(--paper);
  text-decoration: underline;
  text-underline-offset: 4px;
}

.portal-nav a:focus-visible { outline: 3px solid var(--blue-light); outline-offset: 2px; }

.portal-bar__who {
  font-size: 13px;
  color: var(--grey-light);
  line-height: 1.35;
}

.portal-bar__who strong { display: block; color: var(--paper); font-size: 15px; }

.portal-bar form { margin: 0; }

/* --------------------------------------------------------------------------
   Admin shell (H.3)
   -------------------------------------------------------------------------- */

.admin {
  display: grid;
  grid-template-columns: var(--sidebar) minmax(0, 1fr);
  min-height: 100vh;
  background: var(--neutral);
}

.admin__side {
  background: var(--charcoal-deep);
  color: var(--grey-light);
  padding: 20px 0 40px;
}

.admin__brand {
  display: block;
  padding: 0 20px 20px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
  margin-bottom: 16px;
}

.admin__brand img { width: 140px; height: auto; display: block; }

.admin__badge {
  display: inline-block;
  margin-top: 10px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--blue-light);
}

.admin__nav a {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  padding: 10px 20px;
  color: var(--grey-light);
  text-decoration: none;
  font-size: 15px;
  border-left: 3px solid transparent;
}

.admin__nav a:hover { background: rgba(255, 255, 255, 0.06); color: var(--paper); }

.admin__nav a[aria-current="page"] {
  background: rgba(0, 154, 218, 0.14);
  border-left-color: var(--blue);
  color: var(--paper);
  font-weight: 700;
}

.admin__nav a:focus-visible { outline: 3px solid var(--blue-light); outline-offset: -3px; }

.admin__count {
  background: var(--blue);
  color: var(--charcoal-ink);
  border-radius: 10px;
  padding: 0 8px;
  font-size: 12px;
  font-weight: 800;
  line-height: 20px;
}

.admin__nav-note {
  padding: 16px 20px 0;
  margin-top: 12px;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  font-size: 12px;
  line-height: 1.5;
  color: var(--grey);
}

.admin__main { min-width: 0; }

.admin__top {
  background: var(--paper);
  border-bottom: 1px solid var(--line);
  padding: 14px 32px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.admin__who { font-size: 14px; color: var(--charcoal-soft); }

.admin__who strong { color: var(--charcoal); }

.admin__body {
  padding: 32px;
  max-width: var(--app-shell);
}

.admin__top form { margin: 0; display: inline; }

/* --------------------------------------------------------------------------
   Small utilities
   -------------------------------------------------------------------------- */

.muted { color: var(--charcoal-soft); }
.small { font-size: 14px; }
.micro {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--grey-mid);
}
.stack > * + * { margin-top: 16px; }
.spaced-top { margin-top: 20px; }
.spaced-bottom { margin-bottom: 20px; }
.inline-form { display: inline; margin: 0; }
.nowrap { white-space: nowrap; }
.mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 14px;
  word-break: break-all;
}

.codes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 8px;
  list-style: none;
  margin: 16px 0 0;
  padding: 0;
}

.codes li {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 16px;
  letter-spacing: 0.08em;
  background: var(--neutral);
  border: 1px solid var(--line);
  padding: 10px 12px;
  text-align: center;
}

.pre {
  background: var(--neutral);
  border: 1px solid var(--line);
  padding: 16px;
  overflow-x: auto;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
  line-height: 1.55;
  white-space: pre-wrap;
  word-break: break-word;
  margin: 0;
}

/* The shared pager (AI.14). One implementation, rendered by
   templates/partials/pager.php; there used to be two, written out by hand in
   the Audit Log and the Email Log. Navigation rather than an action group, so
   Newer and Older stay links: a pager of buttons would be a redesign of an
   approved screen, not a correction. */
.pager {
  display: flex;
  gap: var(--group-gap);
  align-items: center;
  margin-top: var(--space-4);
  font-size: var(--font-size-small);
}

/* The end of the list. Shown rather than removed, so the control does not move
   under the pointer on the last page. */
.pager__disabled {
  color: var(--grey-mid);
  opacity: var(--disabled-opacity);
}

/* --------------------------------------------------------------------------
   Responsive (K.13). Touch targets stay at least 44 px on tablet and mobile.
   -------------------------------------------------------------------------- */

@media (max-width: 1000px) {
  .admin { grid-template-columns: 1fr; }

  .admin__side { padding-bottom: 16px; }

  .admin__nav {
    display: flex;
    flex-wrap: wrap;
  }

  .admin__nav a { border-left: 0; border-bottom: 3px solid transparent; }

  .admin__nav a[aria-current="page"] { border-left: 0; border-bottom-color: var(--blue); }

  .admin__nav-note { display: none; }

  .grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 860px) {
  .admin__body { padding: 24px 20px; }
  .admin__top { padding: 12px 20px; }
  .portal-nav { margin-left: 0; width: 100%; }
}

@media (max-width: 620px) {
  .grid-2,
  .grid-3,
  .form-row { grid-template-columns: 1fr; }

  .panel { padding: 20px; }

  .card-box { padding: 24px 20px; }

  .form-actions .btn,
  .app__actions .btn { width: 100%; }

  .table th, .table td { padding: 10px 8px; }

  .segmented { width: 100%; }
  .segmented label { flex: 1 1 0; text-align: center; min-height: 44px; }

  /* The portal header gained a Quotes & Orders link, which is one item more
     than a 390 px row holds at the desktop gap. It wraps rather than being
     cut off at the edge. */
  .portal-bar__inner { gap: var(--space-3); }
  .portal-nav { gap: var(--space-2) var(--space-4); }
}

@media (prefers-reduced-motion: reduce) {
  .tile { transition: none; }
  .tile:hover { transform: none; }
}

/* --------------------------------------------------------------------------
   Two-factor enrolment: the QR code and the risk note beside "Keep me signed
   in". The QR is inline SVG drawn by the application, never an external image.
   -------------------------------------------------------------------------- */

.qr-box {
  display: inline-block;
  margin: 12px 0;
  padding: 8px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  line-height: 0;
}

.qr-box .qr {
  display: block;
  width: 100%;
  max-width: 260px;
  height: auto;
}

/* The remembered-login warning is a security disclosure, not incidental help
   text, so it is set apart rather than reading as one more grey hint. */
.field__help--warn {
  border-left: 3px solid var(--warning);
  background: var(--warning-wash);
  padding: 10px 12px;
  margin-top: -12px;
  margin-bottom: 20px;
  color: var(--charcoal);
}

/* --------------------------------------------------------------------------
   THE FAST-ENTRY EDITOR (specification C-7, C-8, C-13, O.1-O.3).

   A NEW SHARED COMPONENT, added deliberately and documented here as the design
   system requires (AI.1: reuse an existing component, else extend one, else add
   a documented shared one - never a page-specific variant because it is
   convenient).

   Why a new one was necessary. The shared `.table` presents records for
   reading: generous cell padding, a hover row, actions at the end. An entry
   grid is a different job. Its cells hold inputs rather than text, its rows are
   dense because forty of them have to be on screen at once, and the first three
   columns have to stay put while the rest scrolls sideways. Restyling `.table`
   to do both would have made every list in the application denser.

   What it does NOT do is invent presentation. Every colour, space, radius,
   border, focus ring and type size below is a token, and the controls inside
   the cells are the design system's own form controls with their padding
   reduced by one shared step - the same family, sized for the job (AI.12).

   The mobile treatment is a card per row rather than a squeezed table, which is
   what C-7 specifies for widths under 860 px.
   -------------------------------------------------------------------------- */

.editor__toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--group-gap);
  align-items: center;
  margin-bottom: var(--space-3);
}

.editor__toolbar .action-group { margin: 0; }

.editor__count {
  font-size: var(--font-size-helper);
  color: var(--grey-mid);
  margin-left: auto;
}

/* The save indicator: SAVING / SAVED / UNSAVED CHANGES / UNABLE TO SAVE.
   It is a status, so it reads as one; colour is never the only signal. */
.save-state {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--font-size-helper);
  font-weight: var(--weight-label);
  color: var(--grey-mid);
}

.save-state::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
}

.save-state[data-state="saved"]   { color: var(--success); }
.save-state[data-state="saving"]  { color: var(--info); }
.save-state[data-state="unsaved"] { color: var(--warning); }
.save-state[data-state="failed"]  { color: var(--danger); }

/* THE GRID, AS A LIST OF MULTI-ROW ITEMS.

   The first version put every field of an order line on ONE table row. With
   thirteen columns that came to well over 1600 pixels, so entering a single
   door meant scrolling sideways to reach its notes and back again to check its
   width. The owner rejected it, and the fault was not the widths — it was
   asking one screen line to hold one business object.

   One quote item is still ONE business object: one id, one selection, one undo
   entry, one autosave queue entry. It is now drawn as TWO table rows inside one
   <tbody>. The upper row carries what identifies and sizes the panel; the lower
   row carries machining, files and the notes, which get a whole row to
   themselves rather than the last narrow column.

   The result fits a laptop screen without panning, and the keyboard model is
   untouched: the editable cells are still in row-major order, so Enter and the
   arrow keys move exactly as they did.

   `overflow-x` stays as a safety net for a very narrow desktop window. It is no
   longer how the grid is meant to be read. */
.grid-wrap {
  overflow-x: auto;
  border: var(--border-width) solid var(--line);
  border-radius: var(--card-radius);
  background: var(--paper);
}

.grid {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--font-size-small);
}

.grid th,
.grid td {
  border-bottom: var(--border-width) solid var(--line);
  padding: 0;
  vertical-align: middle;
  background: var(--paper);
}

.grid thead th {
  position: sticky;
  top: 0;
  z-index: 3;
  padding: var(--space-2) var(--space-2);
  background: var(--neutral);
  border-bottom: var(--border-strong) solid var(--line);
  font-size: var(--font-size-table-head);
  font-weight: var(--weight-label);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--charcoal-soft);
  text-align: left;
  white-space: nowrap;
}

/* The selection box and the line number span both rows of an item, because
   they belong to the item and not to either half of it.

   They are no longer sticky. Sticky columns were there to survive horizontal
   panning; the item layout removed the panning, and a sticky column that never
   scrolls is a stacking context maintained for nothing. */
.grid__pin--select { width: 40px; text-align: center; vertical-align: middle; }
.grid__pin--no     { width: 52px; text-align: center; vertical-align: middle; }

.grid__pin--no {
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-label);
  color: var(--charcoal-soft);
  border-right: var(--border-width) solid var(--line);
}

.grid__pin--select input { margin: 0; }

.grid tbody tr.is-selected td,
.grid tbody tr.is-selected th { background: var(--blue-wash); }

/* ONE PANEL, TWO ROWS. The rule goes UNDER the pair, so the two rows read as
   one block and the eye separates panels rather than lines. */
.grid tbody tr.grid__main > td,
.grid tbody tr.grid__main > th { border-bottom: 0; }

.grid tbody tr.grid__detail > td {
  border-bottom: var(--border-width) solid var(--line);
  padding-bottom: var(--cell-padding-y);
}

.grid tbody tr:last-child > td { border-bottom: 0; }

/* The number and the checkbox span the pair, so their own rule is the pair's. */
.grid tbody tr.grid__main > .grid__pin,
.grid tbody tr.grid__main > .grid__menu {
  border-bottom: var(--border-width) solid var(--line);
}

.grid__centre { text-align: center; }
.grid__centre input { text-align: center; }

/* The detail row is quieter than the row above it, so the eye lands on the
   specification first and the notes second. */
.grid tbody tr.grid__detail { background: var(--table-stripe); }
.grid tbody tr.is-selected.grid__detail { background: var(--blue-wash); }

/* Each detail cell says what it is, because the table head above labels the
   upper row. A small label costs one line and removes all the guessing. */
.grid__field {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0 var(--cell-padding-x);
  min-height: var(--control-height-sm);
}

.grid__field-label {
  flex: 0 0 auto;
  font-size: var(--font-size-helper);
  font-weight: var(--weight-label);
  color: var(--grey-mid);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.grid__field > input { flex: 1 1 auto; }

/* NOTES gets the width it needs. It is the field people actually write in. */
.grid__detail td[data-col="notes"],
.grid__detail td[data-col="internal_note"] { width: auto; }

/* A cell input IS the shared form control, one step tighter, and with
   DELIBERATE padding on both sides. Without it a left-aligned code sat hard
   against the right-aligned number in the cell beside it and the two read as
   one value. */
/*
 * NOT the checkbox. A checkbox in this grid is the SELECTION control, and it is
 * the browser's own: `appearance: none` on it removes the tick, the box and the
 * whole control, leaving an invisible thing that still takes the click. That is
 * precisely what happened - the select-all and the per-panel boxes disappeared
 * from the editor while remaining clickable - so the reset is scoped to the
 * cells that hold values.
 */
.grid input:not([type="checkbox"]),
.grid select {
  width: 100%;
  min-width: 0;
  height: var(--control-height-sm);
  border: 0;
  border-radius: 0;
  padding: 0 var(--cell-padding-x);
  font: inherit;
  font-size: var(--font-size-small);
  color: var(--charcoal);
  background: transparent;
  appearance: none;
  -webkit-appearance: none;
}

/* The selection box keeps its own size and its own appearance, and is big
   enough to hit. */
.grid input[type="checkbox"] {
  width: 16px;
  height: 16px;
  margin: 0;
  cursor: pointer;
  accent-color: var(--blue);
}

/* Centred where centring is what the column means: the line number and the
   quantity are counts, not measurements. */
.grid td[data-col="quantity"] input { text-align: center; }

.grid input:focus-visible,
.grid select:focus-visible {
  outline: var(--focus-width) solid var(--focus-colour);
  outline-offset: calc(var(--focus-offset) * -1);
}

.grid input.is-invalid {
  background: var(--danger-wash);
  box-shadow: inset 0 0 0 1px var(--danger);
}

/* Column widths live here rather than on the cells, so a column is sized in
   one place and a template cannot disagree with the script that re-renders it.
   They are minimums: a table still distributes what is left over. */
/* Proportions, not minimums. A percentage width lets the eight specification
   columns share the table instead of each demanding a floor and pushing the
   total past the window, which is what forced the panning.

   The three fixed columns are fixed because they hold a checkbox, a number and
   a menu button: they do not become more useful with more room. */
.grid col.grid__col--select   { width: 40px; }
.grid col.grid__col--no       { width: 52px; }
.grid col.grid__col--menu     { width: 44px; }
.grid col.grid__col--ref      { width: 13%; }
.grid col.grid__col--type     { width: 15%; }
.grid col.grid__col--panel,
.grid col.grid__col--design,
.grid col.grid__col--material { width: 11%; }
.grid col.grid__col--size     { width: 11%; }
.grid col.grid__col--qty      { width: 7%; }

/* A hinge summary is one fact and reads better on one line than wrapped. */
.grid td[data-col="hinges"] .grid__link,
.grid td[data-col="handle"] .grid__link { white-space: nowrap; }

.grid__num  { text-align: right; font-variant-numeric: tabular-nums; }
.grid__code { text-transform: uppercase; }

/* A cell that opens the row rather than holding a value. */
.grid__link {
  display: block;
  width: 100%;
  padding: var(--control-padding-y) var(--space-2);
  border: 0;
  background: transparent;
  font: inherit;
  font-size: var(--font-size-small);
  color: var(--blue-dark);
  text-align: left;
  cursor: pointer;
}

.grid__link:hover { text-decoration: underline; }

.grid__link:focus-visible {
  outline: var(--focus-width) solid var(--focus-colour);
  outline-offset: calc(var(--focus-offset) * -1);
}

.grid__warn {
  margin-left: var(--space-1);
  color: var(--warning);
  font-size: var(--font-size-helper);
}

.grid__menu { width: 44px; text-align: center; }

.grid__menu .icon-btn {
  width: var(--touch-target);
  height: var(--touch-target);
  min-width: var(--touch-target);
}

/* The row drawer (C-8). A panel anchored to the right on a desktop and a full
   screen on a phone; it uses the shared panel, fields and action group. */
.row-drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(480px, 100%);
  z-index: 60;
  overflow-y: auto;
  padding: var(--card-padding);
  background: var(--paper);
  border-left: var(--border-strong) solid var(--line);
  box-shadow: var(--shadow-3);
}

.row-drawer__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--group-gap);
  margin-bottom: var(--space-4);
}

.row-drawer h2 {
  margin: 0;
  font-size: var(--font-size-section);
}

/* The mobile row card (C-7 under 860 px). */
.row-cards { display: none; }

.row-card {
  border: var(--border-width) solid var(--line);
  border-radius: var(--card-radius);
  background: var(--paper);
  padding: var(--space-3);
  margin-bottom: var(--space-3);
}

.row-card__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--group-gap);
}

.row-card__title {
  margin: 0;
  font-weight: var(--weight-label);
  color: var(--charcoal);
}

.row-card__ref,
.row-card__size,
.row-card__detail {
  margin: var(--space-1) 0 0;
  font-size: var(--font-size-small);
  color: var(--charcoal-soft);
}

.row-card__size { font-variant-numeric: tabular-nums; }

/* A card is a SUMMARY: four short lines, close together. Body paragraph
   margins between them turned it into something the length of a page. */
.row-card > p { margin: var(--space-1) 0 0; }
.row-card__head { margin-bottom: var(--space-2); }

.row-card__actions { margin-top: var(--space-3); }

/* The attachments panel (C-13). */
.dropzone {
  border: var(--border-strong) dashed var(--line);
  border-radius: var(--card-radius);
  padding: var(--space-6);
  text-align: center;
  color: var(--charcoal-soft);
  background: var(--neutral);
}

.dropzone.is-over {
  border-color: var(--blue);
  background: var(--blue-wash);
}

.attachments { list-style: none; margin: var(--space-4) 0 0; padding: 0; }

.attachment {
  display: flex;
  align-items: center;
  gap: var(--group-gap);
  padding: var(--space-3) 0;
  border-bottom: var(--border-width) solid var(--line);
}

.attachment > div { flex: 1 1 auto; min-width: 0; }

.attachment p { margin: var(--space-1) 0 0; }

.attachment__thumb {
  width: 56px;
  height: 56px;
  object-fit: cover;
  border: var(--border-width) solid var(--line);
  border-radius: var(--radius);
}

.attachment__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border: var(--border-width) solid var(--line);
  border-radius: var(--radius);
  background: var(--neutral);
  font-size: var(--font-size-helper);
  font-weight: var(--weight-heading);
  color: var(--charcoal-soft);
}

/* The paste mapping preview (C-14) and the bulk apply panel (C-15), inside the
   shared dialog. */
.modal--wide { width: min(760px, calc(100vw - 32px)); }

.paste-preview { font-size: var(--font-size-helper); }

.paste-preview td { white-space: nowrap; }

.paste-preview td.is-invalid { background: var(--danger-wash); color: var(--danger); }

.paste-preview select { width: 100%; }

.bulk-field { display: grid; gap: var(--space-1); margin-bottom: var(--space-3); }

.bulk-field__head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.bulk-field__head label { margin: 0; }

.bulk-field input[disabled],
.bulk-field select[disabled] { opacity: var(--disabled-opacity); }

/* Responsive: the table gives way to cards at the 860 px switch C-7 names. */
@media (max-width: 860px) {
  .grid-wrap { display: none; }
  .row-cards { display: block; }

  .editor__toolbar { gap: var(--space-2); }

  .row-drawer {
    width: 100%;
    border-left: 0;
  }
}

/* The toolbar on a narrow screen.

   A DOCUMENTED EXCEPTION to the stacking rule, and worth stating plainly. The
   design system stacks an ACTION GROUP into full-width buttons below 480 px,
   and that rule is right for what an action group is: two or three sibling
   actions presented together, APPLY and CLEAR, SAVE and CANCEL. The row
   editor's toolbar is not that. It is a palette of eleven, and stacking it put
   eleven full-width buttons between the heading and the first row - about two
   phone screens of scrolling before any of the customer's work was visible.

   So inside the toolbar, and only there, the groups keep their row and wrap.
   Every button still meets the touch-target minimum, which is the part of the
   rule that is governed rather than stylistic (AI.16). */
@media (max-width: 480px) {
  .editor__toolbar { gap: var(--space-2); }

  .editor__toolbar .action-group {
    flex-direction: row;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2);
  }

  .editor__toolbar .action-group > .btn {
    width: auto;
    min-height: var(--touch-target);
  }

  .editor__count {
    margin-left: 0;
    width: 100%;
    text-align: right;
  }
}


/* ==========================================================================
   SHARED COMPONENTS ADDED IN THE 2026-09-06 OWNER-REVIEW CORRECTION PASS.

   Each of these exists because a Phase 2 page needed it and the honest answer
   was a shared component rather than page CSS (AI.1). Every one of them appears
   in the Component Gallery, and no page below defines geometry of its own.
   ========================================================================== */

/* --------------------------------------------------------------------------
   PAGE COMPOSITION.

   A page is not "the fields, in order, down a very wide sheet of paper". It has
   a maximum readable measure, a rhythm between its sections, and a header whose
   actions sit beside the title rather than under it.
   -------------------------------------------------------------------------- */

.page {
  /* Wide enough for the entry grid, narrow enough that a line of prose is not
     180 characters long — and CENTRED, so a narrow page does not sit against
     the left edge of a wide screen with half the window empty beside it. */
  max-width: 1180px;
  margin-inline: auto;
}

.page--narrow { max-width: 820px; }

.page__head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-4) var(--space-6);
  margin-bottom: var(--section-gap);
  padding-bottom: var(--space-4);
  border-bottom: var(--border-width) solid var(--line);
}

.page__heading { min-width: 0; }

.page__heading .eyebrow { margin-bottom: var(--space-1); color: var(--grey-mid); }

.page__heading h1 {
  font-size: var(--font-size-page-title);
  line-height: var(--line-height-heading);
  letter-spacing: var(--tracking-heading);
  margin: 0;
}

.page__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-2) 0 0;
  font-size: var(--font-size-small);
  color: var(--grey-mid);
}

.page__head .action-group { margin: 0; flex: 0 0 auto; }

@media (max-width: 720px) {
  .page__head { align-items: stretch; }
  .page__head .action-group { width: 100%; }
}

/* A card that groups fields, with its own quiet heading. Lighter than .panel:
   used inside a page that already has several of them. */
.card {
  background: var(--paper);
  border: var(--border-width) solid var(--line);
  border-radius: var(--card-radius);
  padding: var(--card-padding);
  margin-bottom: var(--section-gap);
}

.card__head { margin-bottom: var(--space-5); }

.card__head h2 {
  font-size: var(--font-size-subsection);
  line-height: var(--line-height-heading);
  margin: 0;
}

.card__head p {
  margin: var(--space-1) 0 0;
  font-size: var(--font-size-helper);
  color: var(--grey-mid);
  max-width: 70ch;
}

/* --------------------------------------------------------------------------
   FORM GRID.

   Fields laid out on a grid rather than stacked one per line. A field may take
   one column, two, or the whole row, and the columns line up down the page —
   which is what stops a form looking like a list of boxes of different widths.
   -------------------------------------------------------------------------- */

.form-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 0 var(--field-gap);
}

.form-grid > .field { grid-column: span 12; }

.form-grid > .field--half    { grid-column: span 6; }
.form-grid > .field--third   { grid-column: span 4; }
.form-grid > .field--quarter { grid-column: span 3; }

@media (max-width: 720px) {
  .form-grid > .field,
  .form-grid > .field--half,
  .form-grid > .field--third,
  .form-grid > .field--quarter { grid-column: span 12; }
}

/* --------------------------------------------------------------------------
   THE DATE FIELD (AI.20).

   The customer's Wanted By control was a bare `<input type="date">`: a browser
   widget, a fraction of the height of the controls around it, showing a
   "yyyy-mm-dd" hint that contradicts the site's configured Australian date
   format, with an unrelated little calendar button hanging off one end.

   This is the shared replacement. The visible control is the design system's
   own single-line control, at the same height, border, radius and focus ring as
   every other one. The calendar button is part of the control rather than a
   separate thing beside it.

   PROGRESSIVE ENHANCEMENT. The element underneath is still a real date input,
   so a form works, validates and submits with no JavaScript at all, and a phone
   still gets its own excellent native picker. On a desktop the shared calendar
   popover takes over the presentation. Nothing here is a framework.
   -------------------------------------------------------------------------- */

.date-field {
  position: relative;
  display: block;
}

/*
 * THE SELECTORS ARE DELIBERATELY THIS SPECIFIC.
 *
 * The generic form rule matches `.field input[type="text"]` at two classes and
 * an attribute, and the enhanced date control IS a text input inside a field.
 * A plainer `.date-field input` therefore lost to it, and the field silently
 * took the generic padding - closing the space reserved for the calendar
 * button, so the button sat on top of the text - and the generic border, so a
 * field the server had refused showed a perfectly ordinary grey outline under
 * its red error message.
 *
 * Matching the two types the control can be keeps the weight equal and lets
 * source order decide, which is what the rest of this stylesheet relies on.
 */
.date-field input[type="text"],
.date-field input[type="date"] {
  width: 100%;
  height: var(--control-height);
  padding: 0 calc(var(--control-padding-x) + var(--control-height)) 0 var(--control-padding-x);
  font: inherit;
  font-size: var(--control-font);
  line-height: 1.2;
  color: var(--charcoal);
  background: var(--paper);
  border: var(--border-width) solid var(--line);
  border-radius: var(--control-radius);
  appearance: none;
  -webkit-appearance: none;
}

.date-field input[type="text"]:hover,
.date-field input[type="date"]:hover { border-color: var(--grey-light); }

.date-field input[type="text"]:focus-visible,
.date-field input[type="date"]:focus-visible {
  outline: var(--focus-width) solid var(--blue);
  outline-offset: 1px;
  border-color: var(--blue-dark);
}

/* The browser's own indicator is replaced, not decorated. */
.date-field input::-webkit-calendar-picker-indicator { display: none; }
.date-field input::-webkit-inner-spin-button { display: none; }

.date-field__button {
  position: absolute;
  top: var(--border-width);
  right: var(--border-width);
  width: calc(var(--control-height) - var(--border-width) * 2);
  height: calc(var(--control-height) - var(--border-width) * 2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: transparent;
  color: var(--grey-mid);
  border-radius: 0 var(--control-radius) var(--control-radius) 0;
  cursor: pointer;
}

.date-field__button:hover { color: var(--charcoal); background: var(--neutral); }

.date-field__button:focus-visible {
  outline: var(--focus-width) solid var(--blue);
  outline-offset: calc(-1 * var(--focus-width));
}

.date-field__button svg { width: 18px; height: 18px; display: block; }

.date-field--invalid input[type="text"],
.date-field--invalid input[type="date"] { border-color: var(--danger); }

.date-field input[type="text"][disabled],
.date-field input[type="date"][disabled] {
  background: var(--neutral);
  color: var(--grey-mid);
  cursor: not-allowed;
}

.date-field input[disabled] ~ .date-field__button {
  opacity: var(--disabled-opacity);
  pointer-events: none;
}

/* The calendar itself. */
.datepicker {
  position: absolute;
  z-index: 60;
  top: calc(100% + var(--space-2));
  left: 0;
  width: 292px;
  padding: var(--space-3);
  background: var(--paper);
  border: var(--border-width) solid var(--line);
  border-radius: var(--card-radius);
  box-shadow: var(--shadow-2);
}

.datepicker__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}

.datepicker__title {
  font-size: var(--font-size-small);
  font-weight: var(--weight-label);
  margin: 0;
}

.datepicker__nav {
  width: var(--control-height-sm);
  height: var(--control-height-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: var(--border-width) solid var(--line);
  border-radius: var(--control-radius);
  background: var(--paper);
  color: var(--charcoal);
  cursor: pointer;
  font-size: var(--font-size-small);
}

.datepicker__nav:hover { background: var(--neutral); }

.datepicker__nav:focus-visible {
  outline: var(--focus-width) solid var(--blue);
  outline-offset: 1px;
}

.datepicker__grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
}

.datepicker__dow {
  text-align: center;
  font-size: var(--font-size-eyebrow);
  font-weight: var(--weight-label);
  color: var(--grey-mid);
  padding-bottom: var(--space-1);
}

.datepicker__day {
  aspect-ratio: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: transparent;
  border-radius: var(--control-radius);
  font: inherit;
  font-size: var(--font-size-small);
  font-variant-numeric: tabular-nums;
  color: var(--charcoal);
  cursor: pointer;
}

.datepicker__day:hover { background: var(--neutral); }

.datepicker__day[disabled] {
  color: var(--grey-light);
  cursor: not-allowed;
  background: transparent;
}

.datepicker__day--today { box-shadow: inset 0 0 0 1px var(--grey-mid); }

.datepicker__day--selected,
.datepicker__day--selected:hover {
  background: var(--blue);
  color: var(--charcoal-ink);
  font-weight: var(--weight-label);
}

.datepicker__day:focus-visible {
  outline: var(--focus-width) solid var(--blue);
  outline-offset: -1px;
}

.datepicker__foot {
  display: flex;
  justify-content: space-between;
  gap: var(--space-2);
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: var(--border-width) solid var(--line);
}

/* --------------------------------------------------------------------------
   FILTER BAR.

   A single row of equal-height controls with the actions on the end of it. The
   version this replaces used the page form grid, so the search box and the
   status select were different heights and Apply and Clear were full-size
   buttons with more padding than label.
   -------------------------------------------------------------------------- */

.filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--group-gap) var(--field-gap);
  margin-bottom: var(--section-gap);
  padding: var(--space-4);
  background: var(--paper);
  border: var(--border-width) solid var(--line);
  border-radius: var(--card-radius);
}

/* The fields keep their own wrapper, so no list template has to be re-marked
   up for this; the wrapper is simply a row now rather than a grid of columns
   whose heights were free to differ. */
.filter-bar__fields {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--group-gap) var(--field-gap);
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
}

.filter-bar .field { margin-bottom: 0; flex: 1 1 180px; min-width: 150px; }

.filter-bar .field--auto { flex: 0 0 auto; min-width: 0; }

.filter-bar__actions {
  display: flex;
  gap: var(--group-gap);
  margin: 0;
  flex: 0 0 auto;
}

@media (max-width: 620px) {
  .filter-bar { flex-direction: column; align-items: stretch; }
  .filter-bar__actions > .btn { flex: 1 1 0; }
}

/* --------------------------------------------------------------------------
   A FACT INSIDE A FORM.

   Some values in an editable card are not editable — a date set when the job
   was created, a number the server issues. They are shown as a label and a
   value at the same size and on the same baseline as the fields around them, so
   the card still reads as one thing, and there is deliberately no box: a box
   invites somebody to click into it and find they cannot type.
   -------------------------------------------------------------------------- */

.field__label-static {
  display: block;
  font-size: var(--font-size-label);
  font-weight: var(--weight-label);
  line-height: 1.35;
  margin-bottom: 6px;
}

.field__static {
  display: flex;
  align-items: center;
  min-height: var(--control-height);
  margin: 0;
  padding: 0;
  font-size: var(--control-font);
  color: var(--charcoal);
}

/* --------------------------------------------------------------------------
   INSTRUCTION CARD.

   Keyboard shortcuts as a scannable pair list, not a paragraph. The rows the
   editor shows are the shortcuts it actually implements; a card that promised
   one the editor did not have would be worse than no card.
   -------------------------------------------------------------------------- */

.instructions {
  background: var(--neutral);
  border: var(--border-width) solid var(--line);
  border-radius: var(--card-radius);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
}

.instructions__title {
  font-size: var(--font-size-eyebrow);
  font-weight: var(--weight-heading);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--grey-mid);
  margin: 0 0 var(--space-3);
}

.instructions__list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: var(--space-2) var(--space-5);
  margin: 0;
}

.instructions__row {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  min-width: 0;
}

.instructions__row dt { flex: 0 0 auto; }

.instructions__row dd {
  margin: 0;
  font-size: var(--font-size-helper);
  color: var(--charcoal-soft);
  min-width: 0;
}

kbd {
  display: inline-block;
  padding: 2px 6px;
  font-family: var(--mono, ui-monospace, "Cascadia Mono", Consolas, monospace);
  font-size: var(--font-size-eyebrow);
  line-height: 1.5;
  color: var(--charcoal);
  background: var(--paper);
  border: var(--border-width) solid var(--grey-light);
  border-bottom-width: 2px;
  border-radius: 4px;
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   TOOLBAR.

   Eleven controls in a row is a wall unless it is grouped. The groups are the
   ones a person actually thinks in — make a row, move a row, change many rows,
   bring rows in, undo — separated by space and a hairline rather than by five
   separate cards.
   -------------------------------------------------------------------------- */

.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) 0;
  margin-bottom: var(--space-4);
  padding: var(--space-2);
  background: var(--paper);
  border: var(--border-width) solid var(--line);
  border-radius: var(--card-radius);
}

.toolbar__group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  padding: 0 var(--space-3);
  border-right: var(--border-width) solid var(--line);
}

.toolbar__group:first-child { padding-left: var(--space-2); }
.toolbar__group:last-of-type { border-right: 0; }

/* The destructive group sits at the far end, away from the controls somebody
   presses forty times an hour. */
.toolbar__group--end { margin-left: auto; border-right: 0; }

.toolbar__group .btn { min-width: 0; }

.toolbar__count {
  margin: 0 0 0 auto;
  padding: 0 var(--space-3);
  font-size: var(--font-size-helper);
  color: var(--grey-mid);
  white-space: nowrap;
}

@media (max-width: 860px) {
  .toolbar__group {
    border-right: 0;
    padding: 0 var(--space-2);
  }
  .toolbar__group--end { margin-left: 0; }
  .toolbar__count { margin-left: var(--space-2); }
}

/* --------------------------------------------------------------------------
   SUMMARY CARDS AND THE LABEL / VALUE GRID.

   The read-only Job Details used a definition strip that put every label above
   its value in its own column, so four short facts filled a whole screen and
   long ones wrapped for no reason. This groups them into cards by what they
   ARE, and inside a card a label and its value are on ONE line: label on the
   left, right-aligned; value on the right, left-aligned; a deliberate gap
   between them.
   -------------------------------------------------------------------------- */

.summary-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--group-gap);
  margin-bottom: var(--section-gap);
}

.summary-card {
  background: var(--paper);
  border: var(--border-width) solid var(--line);
  border-radius: var(--card-radius);
  padding: var(--space-4) var(--space-5);
  min-width: 0;
}

.summary-card__title {
  font-size: var(--font-size-eyebrow);
  font-weight: var(--weight-heading);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--grey-mid);
  margin: 0 0 var(--space-3);
}

.spec {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  gap: var(--space-2) var(--space-4);
  margin: 0;
  align-items: baseline;
}

.spec dt {
  text-align: right;
  font-size: var(--font-size-helper);
  color: var(--grey-mid);
  white-space: nowrap;
}

.spec dd {
  margin: 0;
  text-align: left;
  font-size: var(--font-size-small);
  font-weight: var(--weight-label);
  color: var(--charcoal);
  min-width: 0;
  overflow-wrap: break-word;
}

.spec dd.spec__value--long { font-weight: 400; }

/* A note is prose and needs the width of the card, not one column of it. */
.summary-card--wide { grid-column: 1 / -1; }

.summary-card__note {
  margin: 0;
  font-size: var(--font-size-small);
  line-height: var(--line-height-body);
  color: var(--charcoal);
  white-space: pre-wrap;
  overflow-wrap: break-word;
}

@media (max-width: 480px) {
  .spec { grid-template-columns: minmax(0, 1fr); gap: var(--space-1); }
  .spec dt { text-align: left; }
  .spec dd { margin-bottom: var(--space-2); }
}

/* --------------------------------------------------------------------------
   CHOICE CARDS.

   A choice somebody presses, rather than a dot they aim at. The whole card is
   the <label>, so the target is the card while the radio underneath still does
   the work — which means the keyboard behaviour, the grouping and the
   screen-reader announcement are exactly what a radio group has always been.
   The input is not hidden from assistive technology, only from sight.
   -------------------------------------------------------------------------- */

.choice-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--group-gap);
}

.choice {
  position: relative;
  display: block;
  padding: var(--space-4) var(--space-5);
  background: var(--paper);
  border: var(--border-width) solid var(--line);
  border-radius: var(--card-radius);
  cursor: pointer;
  transition: border-color .15s ease, box-shadow .15s ease, background-color .15s ease;
}

.choice:hover { border-color: var(--grey-light); }

.choice input {
  position: absolute;
  top: var(--space-4);
  left: var(--space-5);
  margin: 0;
  /* Present for a keyboard and a screen reader; the card is what is seen. */
  opacity: 0;
  width: 1px;
  height: 1px;
}

.choice__body { display: block; }

.choice__title {
  display: block;
  font-size: var(--font-size-subsection);
  font-weight: var(--weight-heading);
  line-height: var(--line-height-heading);
  color: var(--charcoal);
}

.choice__note {
  display: block;
  margin-top: var(--space-2);
  font-size: var(--font-size-helper);
  line-height: var(--line-height-body);
  color: var(--grey-mid);
}

/* Selected: a ring, not just a tint, so the state survives a monochrome screen
   and does not rely on colour alone. */
.choice:has(input:checked) {
  border-color: var(--blue);
  background: var(--blue-wash);
  box-shadow: inset 0 0 0 1px var(--blue);
}

.choice:has(input:focus-visible) {
  outline: var(--focus-width) solid var(--blue);
  outline-offset: 2px;
}

/* A tick, so "which one is chosen" is legible at a glance from across a desk. */
.choice:has(input:checked) .choice__title::after {
  content: " ✓";
  color: var(--blue-dark);
}
