/* =============================================================================
   printmgmt — interim styling.
   Deliberately plain and neutral: no legacy report samples were available, so
   this is a sensible default to be revised once the old system's layouts turn
   up. Nothing here encodes brand decisions.
   ============================================================================= */

:root {
  --bg: #f6f7f9;
  --surface: #ffffff;
  --surface-2: #fafbfc;
  --border: #dfe3e8;
  --border-strong: #c3cad3;
  --text: #1c2430;
  --text-muted: #5c6b7a;
  /* Darkened from #8b98a6, which measured 2.94 on a card and 2.74 on the page —
     below even the relaxed threshold for large text, while carrying real
     content in thirty rules: table headers, hints, empty states, the statement
     document. This clears 4.5 on both grounds and stays visibly quieter than
     --text-muted, which is the job it exists to do. */
  --text-faint: #667381;
  --accent: #1f6feb;
  --accent-soft: #e8f0fe;
  --accent-hover: #1a5fd0;
  --ok: #1a7f4b;
  --ok-soft: #e6f4ec;
  --warn: #9a6400;
  --warn-soft: #fdf3e2;
  --danger: #b42318;
  --danger-soft: #fdecea;
  --danger-hover: #98271e;
  --radius: 6px;
  --shadow: 0 1px 2px rgba(16, 24, 40, 0.06), 0 1px 3px rgba(16, 24, 40, 0.08);
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  /* --- Design system, added in Phase 11 -----------------------------------
     A scale, so spacing and type are chosen from a set rather than invented
     per component. Everything added from here on uses these; the older rules
     keep their literals until their screens are ported.                    */

  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px;
  --sp-4: 16px; --sp-5: 24px; --sp-6: 32px; --sp-7: 48px;

  --radius-sm: 4px;
  --radius-lg: 10px;
  --radius-pill: 999px;

  --fs-xs: 11px;  --fs-sm: 12px;  --fs-base: 14px;
  --fs-md: 16px;  --fs-lg: 20px;  --fs-xl: 26px;  --fs-2xl: 34px;

  /* Type. A back-office tool is read at a desk all day, so the stack is the
     platform's own UI face — already hinted for the screen it is on, and no
     webfont round-trip before the first paint. */
  --font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
             "Helvetica Neue", Arial, sans-serif;

  --fw-normal: 400; --fw-medium: 500; --fw-semibold: 600;

  --lh-tight: 1.25;   /* headings and figures */
  --lh-normal: 1.5;   /* interface text */
  --lh-loose: 1.6;    /* paragraphs of prose */

  --tracking-wide: 0.05em;  /* uppercase eyebrows only — never body text */

  /* Icons. One size per role, so an icon is never sized by eye. */
  --icon-sm: 14px; --icon-md: 16px; --icon-lg: 18px;

  /* Focus. One ring everywhere, defined once. */
  --focus-ring: 2px solid var(--accent);
  --focus-offset: 2px;

  /* Breakpoints, for reference — CSS cannot use a variable in a media query,
     so these are documented here and written literally below. Anything else is
     a component's own wrapping point, not a layout breakpoint.
       --bp-sm: 560px    stack forms, drop to one column
       --bp-md: 720px    sidebar becomes a drawer
       --bp-lg: 900px    quarter-width fields become halves          */

  --elev-1: 0 1px 2px rgba(16, 24, 40, 0.06), 0 1px 3px rgba(16, 24, 40, 0.08);
  --elev-2: 0 2px 4px rgba(16, 24, 40, 0.06), 0 4px 12px rgba(16, 24, 40, 0.08);
  --elev-3: 0 8px 28px rgba(16, 24, 40, 0.14);

  /* Chart ink. Ordered so adjacent series stay distinguishable, and chosen to
     survive greyscale printing — these end up on reports. */
  --c1: #1f6feb; --c2: #1a7f4b; --c3: #9a6400;
  --c4: #7b3fb8; --c5: #b42318; --c6: #0d7c8c;

  --sidebar-w: 210px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font: var(--fs-base) / var(--lh-normal) var(--font-ui);
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { margin: 0; font-weight: 600; line-height: 1.25; }
h1 { font-size: 20px; }
h2 { font-size: 16px; }
h3 { font-size: 14px; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* --- Layout ---------------------------------------------------------------- */

/*
 * The shell owns the viewport; the CONTENT scrolls, not the page.
 *
 * Previously everything used min-height:100vh and the document itself scrolled,
 * so the sidebar and the search bar slid away with the content. Fixing the
 * shell to the viewport height and giving the content pane its own overflow
 * pins the navigation and the app bar in place.
 */
#app { height: 100vh; }

.shell { display: flex; height: 100vh; overflow: hidden; }

.sidebar {
  width: 210px;
  flex: 0 0 210px;
  height: 100%;
  background: var(--surface);
  border-right: 1px solid var(--border);
  padding: 16px 0;
  display: flex;
  flex-direction: column;
}

.brand {
  padding: 0 16px 16px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 8px;
}
.brand-name { font-weight: 600; font-size: 15px; }
.brand-org { font-size: 12px; color: var(--text-muted); margin-top: 2px; }

/* Seven items rather than twenty-three, so each gets the room to be read at a
   glance instead of scanned. */
.nav { flex: 1; overflow-y: auto; padding: var(--sp-2) 0; }
.nav a {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 9px 16px;
  color: var(--text-muted);
  font-size: var(--fs-base);
  border-left: 2px solid transparent;
}
/* The icon is quieter than the label until the row is active or hovered. An
   icon at full contrast in a list of twenty reads as twenty things shouting. */
.nav .icon { color: var(--text-faint); transition: color 100ms ease; }
.nav a:hover .icon { color: var(--text-muted); }
.nav a.active .icon { color: var(--accent); }
@media (prefers-reduced-motion: reduce) {
  .nav .icon { transition: none; }
}
.nav a:hover { background: var(--surface-2); text-decoration: none; color: var(--text); }
.nav a.active {
  color: var(--accent);
  background: var(--accent-soft);
  border-left-color: var(--accent);
  font-weight: 500;
}
/* How much is waiting on this person. Warn, because waiting work is attention,
   not failure; pushed to the row's end so the labels still align. */
.nav-count {
  margin-left: auto;
  padding: 0 var(--sp-2);
  border-radius: var(--radius-pill);
  background: var(--warn-soft);
  color: var(--text);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums;
  line-height: var(--lh-normal);
}

.sidebar-foot {
  padding: 12px 16px 0;
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--text-muted);
}
.sidebar-signout {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  margin-top: var(--sp-2);
}

/* --- Section tabs ----------------------------------------------------------
   A section's screens, across the top of the content area. This is a view of
   the route table rather than a new level of routing: every href here is the
   URL that screen already had.

   Shown only when a section has more than one screen the user can open — a
   strip with a single tab in it is chrome that says nothing.
   -------------------------------------------------------------------------- */

.tabs {
  display: flex;
  gap: var(--sp-1);
  align-items: stretch;
  padding: 0 var(--sp-5);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  overflow-x: auto;
  scrollbar-width: none;
  flex: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  padding: 11px var(--sp-3) 9px;
  font-size: var(--fs-base);
  color: var(--text-muted);
  white-space: nowrap;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.tab:hover { color: var(--text); text-decoration: none; border-bottom-color: var(--border-strong); }
.tab.active {
  color: var(--accent);
  border-bottom-color: var(--accent);
  font-weight: var(--fw-medium);
}
.tab:focus-visible { outline-offset: -3px; }

/* The app bar stays put and the pane below it scrolls. */
.main { flex: 1; min-width: 0; display: flex; flex-direction: column; overflow: hidden; }

.topbar {
  /* The negative margin every page used to carry inline, thirty-nine times, to
     bleed the header across the content padding. */
  margin: -20px -24px 20px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  padding: 14px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.topbar-actions { display: flex; gap: 8px; align-items: center; }

.content { padding: 20px 24px 48px; flex: 1; overflow-y: auto; overflow-x: hidden; }

/* --- Cards and panels ------------------------------------------------------ */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  margin-bottom: 16px;
}
.card-head {
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.card-body { padding: 16px; }
.card-body.tight { padding: 0; }

.grid, .grid-2, .grid-4 { display: grid; gap: var(--sp-4); }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }

.stat { padding: 14px 16px; }
.stat-label { font-size: 12px; color: var(--text-muted); }
.stat-value { font-size: 22px; font-weight: 600; margin-top: 4px; font-variant-numeric: tabular-nums; }
.stat-note { font-size: 12px; color: var(--text-faint); margin-top: 2px; }

/* --- Tables ---------------------------------------------------------------- */

table { width: 100%; border-collapse: collapse; font-size: 13px; }
th {
  text-align: left;
  font-weight: 600;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  padding: 9px 12px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
  white-space: nowrap;
}
td { padding: 9px 12px; border-bottom: 1px solid var(--border); vertical-align: top; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: var(--surface-2); }
.num { text-align: right; font-variant-numeric: tabular-nums; }
.mono { font-family: var(--mono); font-size: 12px; }
.table-wrap { overflow-x: auto; }

.empty { padding: 28px 16px; text-align: center; color: var(--text-faint); font-size: 13px; }

/* --- Loading ---------------------------------------------------------------
   A skeleton in the shape of what is coming, rather than the word "Loading".
   The shape is the point: the page does not jump when the content lands, and
   the wait reads as progress instead of as a stall.
   -------------------------------------------------------------------------- */

/* Announced, not shown. The skeleton is aria-hidden, so a screen reader needs
   something to hear while it waits. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skeleton { display: grid; gap: var(--sp-3); }
.skeleton-bar {
  height: 12px;
  border-radius: var(--radius-sm);
  background: linear-gradient(
    90deg,
    var(--border) 25%,
    var(--surface-2) 37%,
    var(--border) 63%
  );
  background-size: 400% 100%;
  animation: skeleton-sweep 1.4s ease infinite;
}
.skeleton-bar.tall { height: 64px; }
.skeleton-bar.w-40 { width: 40%; }
.skeleton-bar.w-60 { width: 60%; }
.skeleton-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-4); }

@keyframes skeleton-sweep {
  from { background-position: 100% 50%; }
  to   { background-position: 0 50%; }
}
@media (prefers-reduced-motion: reduce) {
  .skeleton-bar { animation: none; background: var(--border); }
}

/* --- Badges ---------------------------------------------------------------- */

.badge {
  display: inline-block;
  padding: 2px 7px;
  border-radius: 10px;
  font-size: 11px;
  font-weight: 500;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-muted);
  white-space: nowrap;
}
.badge.ok { background: var(--ok-soft); color: var(--ok); border-color: transparent; }
.badge.warn { background: var(--warn-soft); color: var(--warn); border-color: transparent; }
.badge.danger { background: var(--danger-soft); color: var(--danger); border-color: transparent; }
.badge.accent { background: var(--accent-soft); color: var(--accent); border-color: transparent; }

.chip {
  display: inline-block;
  width: 10px; height: 10px;
  border-radius: 2px;
  border: 1px solid rgba(0,0,0,0.15);
  vertical-align: -1px;
  margin-right: 6px;
}

/* --- Forms and buttons ----------------------------------------------------- */

label, .field-label { display: block; font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
input, select, textarea {
  width: 100%;
  padding: 7px 9px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  font: inherit;
  font-size: 13px;
  background: var(--surface);
  color: var(--text);
}
input:focus, select:focus, textarea:focus {
  outline: 2px solid var(--accent-soft);
  border-color: var(--accent);
}
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: var(--focus-ring);
}
.field { margin-bottom: var(--sp-3); }

/* --- Form layout -----------------------------------------------------------
   One system for every form in the app, modal dialogs and full-page forms
   alike. Fields sit on a twelve-column grid and claim a span; anything that
   does not ask for one takes the full width.

   Two deliberate proportions carry most of the effect. The row gap inside a
   group is smaller than the gap between groups, so the eye reads groups before
   it reads fields. And small values get small controls: a date is ten
   characters wide however much room its column has, and stretching it to the
   full width of a dialog is what made these forms read as unfinished.
   -------------------------------------------------------------------------- */

/* `hidden` must win over any display a class sets: .form-grid's `display: grid`
   beat the browser's own [hidden] rule, so a prospect's fields showed on the
   quotation form with a customer chosen. */
[hidden] {
  display: none !important;
}

.form-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--sp-3) var(--sp-4);
  align-items: start;
}
/* The span is set on the END line only, so .row-start can move the start
   without collapsing the field to one column ("span 6" alone is a start). */
.form-grid > * { grid-column: auto / span 12; }
.form-grid > .field { margin-bottom: 0; }
.form-grid > .col-3 { grid-column: auto / span 3; }
.form-grid > .col-4 { grid-column: auto / span 4; }
.form-grid > .col-6 { grid-column: auto / span 6; }
.form-grid > .col-8 { grid-column: auto / span 8; }
/* Force a new row. For where a half-width field is followed by a set that
   belongs together — dimensions, say — and must not be split across the gap. */
.form-grid > .row-start { grid-column-start: 1; }

/* Controls whose content has a known, small width. `.wide` opts back out for
   the odd case that needs the room — a search box, a long code. */
.field input[type="date"] { max-width: 175px; }
.field input[type="number"] { max-width: 190px; }
.field input[type="time"] { max-width: 130px; }
.field input.wide, .field select.wide, .field input.right { max-width: none; }

/* A row of controls that belong to one value: an amount and its currency, a
   quantity and its unit. Tighter than the grid gap, because they read as one
   field rather than two. */
.field-row { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }

/* Groups within a single form. The rule is the separator, not empty space, so
   the form can stay dense without the sections running together. */
.form-section { border: 0; padding: 0; margin: 0; min-width: 0; }
.form-section + .form-section {
  margin-top: var(--sp-5);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--border);
}
.form-section-title {
  display: block;
  padding: 0;
  margin-bottom: var(--sp-3);
  font-size: var(--fs-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-faint);
}

/* Sections that open on demand. `<details>` wearing the card's own chrome, so a
   collapsed section is recognisably the same object as an expanded one. */
details.card > summary.card-head {
  cursor: pointer;
  list-style: none;
  user-select: none;
}
details.card > summary.card-head::-webkit-details-marker { display: none; }
details.card:not([open]) > summary.card-head { border-bottom: 0; }
.card-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  color: var(--text-muted);
  font-size: var(--fs-sm);
  font-weight: 400;
}
.card-toggle::after {
  content: "";
  width: 7px;
  height: 7px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform 120ms ease;
}
details[open] > summary .card-toggle::after {
  transform: rotate(225deg) translate(-2px, -2px);
}
/* The disclosure marker is drawn in CSS rather than inlined per <summary>: it
   animates between states, and an icon swapped on toggle would not. */
@media (prefers-reduced-motion: reduce) {
  .card-toggle::after { transition: none; }
}
summary.card-head:hover .card-toggle { color: var(--text); }
summary.card-head:focus-visible {
  outline-offset: -2px;
  border-radius: var(--radius) var(--radius) 0 0;
}

/* A collapsed group inside a dialog. Lighter than the card treatment — a
   dialog is already a panel, so a second border inside one reads as clutter. */
.form-section-block { margin-top: var(--sp-4); border-top: 1px solid var(--border); }
.form-section-summary {
  padding: var(--sp-3) 0 0;
  cursor: pointer;
  list-style: none;
  user-select: none;
  font-size: var(--fs-sm);
  font-weight: 600;
}
.form-section-summary::-webkit-details-marker { display: none; }
.form-section-summary:hover .card-toggle { color: var(--text); }
.form-section-summary:focus-visible { outline-offset: 2px; }
.form-section-body { padding-top: var(--sp-3); }

@media (max-width: 900px) {
  /* Quarters become halves before anything becomes full width — four number
     boxes in a row is the first thing to break, and pairs still read well. */
  .form-grid > .col-3, .form-grid > .col-4 { grid-column: span 6; }
}
@media (max-width: 560px) {
  .form-grid { gap: var(--sp-3); }
  .form-grid > .col-3,
  .form-grid > .col-4,
  .form-grid > .col-6,
  .form-grid > .col-8 { grid-column: span 12; }
  .field input[type="date"], .field input[type="number"] { max-width: none; }
}
.row { display: flex; gap: 12px; flex-wrap: wrap; }
.row > * { flex: 1; min-width: 140px; }

/* --- Icons -----------------------------------------------------------------
   Lucide, inlined by lib/icons.js. They inherit `currentColor` and sit on the
   text baseline, so an icon beside a label is the label's colour without
   anything at the call site saying so.
   -------------------------------------------------------------------------- */

.icon {
  flex: none;
  vertical-align: -0.125em;
  stroke-width: 2;
}
/* Optical correction: at 14px and below Lucide's 2px stroke reads heavy
   against 12–13px text. */
.icon[width="14"], .icon[width="15"] { stroke-width: 1.75; }

/* Anything that puts an icon next to a word. */
.with-icon { display: inline-flex; align-items: center; gap: var(--sp-2); }

/* --- Focus -----------------------------------------------------------------
   One ring, on :focus-visible only — a mouse click on a button should not
   leave it ringed, but every keyboard path must be visible. Declared here so
   no component has to remember it.
   -------------------------------------------------------------------------- */

:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
  border-radius: var(--radius-sm);
}
/* Inputs draw the ring tight to their own border rather than outside it. */
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline-offset: 0;
}

button, .btn {
  padding: 7px 13px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  white-space: nowrap;
}
button:hover, .btn:hover { background: var(--surface-2); text-decoration: none; }
button:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.btn-primary:hover { background: var(--accent-hover); }

/* The full set. A button is one of these four; there is no fifth, and no
   one-off colour on a call site. */
.btn-danger {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
}
.btn-danger:hover { background: var(--danger-hover); }
.btn-ghost {
  background: transparent;
  border-color: transparent;
  color: var(--text-muted);
}
.btn-ghost:hover { background: var(--surface-2); color: var(--text); }

.btn-sm { padding: 4px 9px; font-size: var(--fs-sm); }

/* Buttons that carry an icon. */
button .icon, .btn .icon { vertical-align: -0.15em; }
button:has(> .icon + span), .btn:has(> .icon + span) {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* Disabled is a state, not a variant — it applies to all four. */
button:disabled, .btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}

.filters {
  display: flex;
  gap: 10px;
  align-items: flex-end;
  flex-wrap: wrap;
  margin-bottom: 14px;
}
.filters .field { margin-bottom: 0; min-width: 150px; }

/* --- Login ----------------------------------------------------------------- */

.login-wrap {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 24px;
}
.login-card { width: 100%; max-width: 360px; }
.login-title { text-align: center; margin-bottom: 4px; }
.login-sub { text-align: center; color: var(--text-muted); font-size: 13px; margin-bottom: 20px; }

/* --- Customer tracking page -------------------------------------------------
   The only page a customer sees. It carries no navigation and no controls, so
   it is wider and quieter than the login card it shares a wrapper with.
   -------------------------------------------------------------------------- */

.track-card {
  width: 100%;
  max-width: 480px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 28px 26px 22px;
}
.track-head { text-align: center; margin-bottom: 20px; }
.track-ref {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--text-faint);
}
.track-title { font-size: 21px; margin: 6px 0 2px; }
.track-supplier { color: var(--text-muted); font-size: 13px; margin: 0; }

.track-now {
  text-align: center;
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
  padding: 11px;
  border-radius: var(--radius);
  margin-bottom: 22px;
}
.track-now.complete { background: var(--ok-soft); color: var(--ok); }

.track-steps { list-style: none; margin: 0 0 22px; padding: 0; }
.track-step {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 7px 0;
  color: var(--text-faint);
  position: relative;
}
/* The connector is drawn from each step to the next, so the last one has none. */
.track-step:not(:last-child)::before {
  content: "";
  position: absolute;
  left: 13px;
  top: 34px;
  bottom: -7px;
  width: 2px;
  background: var(--border);
}
.track-step.done:not(:last-child)::before { background: var(--ok); }
.track-dot {
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 12px;
  font-weight: 600;
  background: var(--surface-2);
  border: 2px solid var(--border);
  color: var(--text-faint);
}
.track-step.done { color: var(--text); }
.track-step.done .track-dot {
  background: var(--ok);
  border-color: var(--ok);
  color: #fff;
}
.track-step.current .track-label { font-weight: 600; }
/* With a dozen stages the one under way needs more than a weight change. */
.track-step.current:not(.done) { color: var(--text); }
.track-step.current:not(.done) .track-dot { border-color: var(--accent); color: var(--accent); }
.track-label { display: flex; flex-direction: column; }
.track-date { font-size: var(--fs-xs); font-weight: var(--fw-normal); color: var(--text-muted); }

.track-dates { margin-bottom: 18px; }
.track-foot {
  border-top: 1px solid var(--border);
  padding-top: 14px;
  margin: 0;
  font-size: 12px;
  color: var(--text-faint);
  text-align: center;
}

/* --- Messages -------------------------------------------------------------- */

.alert {
  padding: 9px 12px;
  border-radius: var(--radius);
  font-size: 13px;
  margin-bottom: 12px;
}
.alert-error { background: var(--danger-soft); color: var(--danger); }
.alert-ok { background: var(--ok-soft); color: var(--ok); }
.alert-warn { background: var(--warn-soft); color: var(--warn); }

#toasts {
  position: fixed;
  right: 16px;
  bottom: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 100;
}
.toast {
  background: var(--text);
  color: #fff;
  padding: 9px 14px;
  border-radius: var(--radius);
  font-size: 13px;
  box-shadow: var(--shadow);
  max-width: 380px;
}
.toast.error { background: var(--danger); }

/* --- Modal ----------------------------------------------------------------- */

.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(16, 24, 40, 0.45);
  display: grid;
  place-items: center;
  padding: 24px;
  z-index: 90;
}
.modal {
  background: var(--surface);
  border-radius: 8px;
  width: 100%;
  max-width: 580px;
  max-height: 85vh;
  overflow-y: auto;
  box-shadow: 0 12px 32px rgba(16, 24, 40, 0.2);
}
.modal-head {
  padding: var(--sp-3) var(--sp-5);
  border-bottom: 1px solid var(--border);
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.modal-body { padding: var(--sp-4) var(--sp-5); }
.modal-foot {
  padding: var(--sp-3) var(--sp-5);
  border-top: 1px solid var(--border);
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

/* --- Detail lists ---------------------------------------------------------- */

.dl { display: grid; grid-template-columns: max-content 1fr; gap: 6px 16px; font-size: 13px; }
.dl dt { color: var(--text-muted); }
.dl dd { margin: 0; }

.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li {
  position: relative;
  padding: 0 0 14px 18px;
  border-left: 2px solid var(--border);
  font-size: 13px;
}
.timeline li:last-child { border-left-color: transparent; padding-bottom: 0; }
.timeline li::before {
  content: "";
  position: absolute;
  left: -5px; top: 4px;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--border-strong);
}
.timeline .when { color: var(--text-faint); font-size: 12px; }

.slot-row { display: flex; align-items: center; gap: 8px; }

/* --- Invoice document ------------------------------------------------------
   Plain business-document layout. Deliberately conservative until the legacy
   samples arrive and dictate the real format.                                */

.doc {
  background: #fff;
  padding: 36px 40px;
  max-width: 820px;
  margin: 0 auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.doc-head {
  display: flex;
  justify-content: space-between;
  gap: 24px;
  padding-bottom: 20px;
  border-bottom: 2px solid var(--text);
  margin-bottom: 20px;
}
.doc-title { font-size: 24px; font-weight: 600; letter-spacing: 0.02em; }
.doc-meta { text-align: right; font-size: 13px; }
.doc-meta div { margin-bottom: 2px; }
.doc-parties { display: flex; gap: 40px; margin-bottom: 24px; }
.doc-party-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  margin-bottom: 4px;
}
.doc table { font-size: 13px; }
.doc th { background: transparent; border-bottom: 1px solid var(--text); }
.doc-totals { margin-top: 18px; display: flex; justify-content: flex-end; }
.doc-totals table { width: auto; min-width: 260px; }
.doc-totals td { border: 0; padding: 4px 0 4px 24px; }
.doc-totals tr.grand td {
  border-top: 2px solid var(--text);
  font-weight: 600;
  font-size: 15px;
  padding-top: 8px;
}
.doc-foot {
  margin-top: 36px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--text-muted);
}
.doc-void {
  border: 2px solid var(--danger);
  color: var(--danger);
  padding: 4px 12px;
  font-weight: 600;
  letter-spacing: 0.1em;
  display: inline-block;
  transform: rotate(-3deg);
}

@media print {
  .sidebar, .topbar, .no-print { display: none !important; }
  body { background: #fff; }
  .content { padding: 0; }
  .doc { border: 0; max-width: none; padding: 0; }
  a { color: inherit; text-decoration: none; }
  table { page-break-inside: auto; }
  tr { page-break-inside: avoid; }
}

@media (max-width: 720px) {
  .tabs { padding: 0 var(--sp-3); }
  .tab { padding: 10px var(--sp-2) 8px; font-size: var(--fs-sm); }
  /* The sidebar becomes a drawer — see the design-system block at the end of
     this file. Seven items would very nearly fit as a horizontal strip, but the
     drawer is already built and keeps the phone and desktop menus identical. */
  .content { padding: 16px 12px 40px; }
}

/* --- Added after the usability audit --------------------------------------- */

.field-hint {
  font-size: 12px;
  color: var(--text-faint);
  margin-top: 4px;
  line-height: 1.4;
}

.modal-intro {
  margin: 0 0 14px;
  color: var(--text-muted);
  font-size: 13px;
  line-height: 1.5;
}

.modal-wide { max-width: 780px; }

label.check {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--text);
  font-size: 13px;
  cursor: pointer;
}
label.check input { width: auto; margin: 0; }

.pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 4px 0 8px;
  font-size: 13px;
  color: var(--text-muted);
}

/* Slot builder on the new-design page. Numbered rows, because the order is the
   print-station order and getting it wrong matters. */
.slot-editor {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin-bottom: 8px;
  background: var(--surface-2);
}
.slot-editor .field { margin-bottom: 0; flex: 1; }
.slot-no {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-muted);
  min-width: 52px;
  padding-bottom: 8px;
  white-space: nowrap;
}
.slot-editor button[disabled] { opacity: 0.35; }

.empty .btn, .empty button { margin-top: 4px; }

@media (max-width: 720px) {
  .slot-editor { flex-wrap: wrap; align-items: stretch; }
  .slot-no { min-width: 100%; padding-bottom: 0; }
}

/* --- Artwork previews ------------------------------------------------------ */

.preview {
  display: block;
  flex: none;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
  overflow: hidden;
  position: relative;
}
a.preview:hover { border-color: var(--accent); text-decoration: none; }

.preview-xs { width: 34px;  height: 34px; }
.preview-sm { width: 52px;  height: 52px; }
.preview-md { width: 132px; height: 132px; }
.preview-lg { width: 190px; height: 190px; }

.preview-img {
  width: 100%;
  height: 100%;
  background-size: cover;
  background-position: center;
  display: grid;
  place-items: center;
}

.preview-empty {
  display: grid;
  place-items: center;
  color: var(--text-faint);
  font-size: 10px;
  text-align: center;
  line-height: 1.2;
  padding: 4px;
  border-style: dashed;
}
.preview-xs.preview-empty span,
.preview-sm.preview-empty span { display: none; }

/* PDFs are not rasterised, so they get an unmistakable tile rather than a
   broken image. */
.preview-pdf {
  width: 100%; height: 100%;
  display: grid;
  place-items: center;
  background: repeating-linear-gradient(45deg, #fbfcfd, #fbfcfd 6px, #f3f5f7 6px, #f3f5f7 12px);
}
.preview-pdf-mark {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--danger);
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 2px 5px;
}
.preview-xs .preview-pdf-mark { font-size: 8px; padding: 1px 3px; }

.preview-spin {
  width: 14px; height: 14px;
  border: 2px solid var(--border);
  border-top-color: var(--text-faint);
  border-radius: 50%;
  animation: preview-spin 0.7s linear infinite;
}
@keyframes preview-spin { to { transform: rotate(360deg); } }
.preview-failed { font-size: 9px; color: var(--text-faint); text-align: center; }

/* --- File grid on the design page ------------------------------------------ */

.file-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 14px;
}
.file-card {
  display: flex;
  gap: 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px;
  background: var(--surface);
}
.file-meta { min-width: 0; flex: 1; }
.file-name {
  font-weight: 500;
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.file-sub { font-size: 12px; color: var(--text-muted); margin-top: 4px; }
.file-actions { margin-top: 8px; display: flex; gap: 6px; flex-wrap: wrap; }

/* --- Upload dialog ---------------------------------------------------------- */

.upload-preview {
  display: flex;
  gap: 12px;
  align-items: center;
  padding: 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
  margin-bottom: 12px;
}
.upload-thumb {
  width: 64px; height: 64px;
  object-fit: cover;
  border-radius: 4px;
  border: 1px solid var(--border);
}
.upload-meta { font-size: 12px; color: var(--text-muted); line-height: 1.5; }

/* --- Artwork on the reprint sheet ------------------------------------------- */

.doc-artwork {
  display: flex;
  gap: 16px;
  align-items: center;
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
  margin-bottom: 18px;
}
.doc-artwork-note { font-size: 13px; line-height: 1.5; }
.doc-artwork-note div { color: var(--text-muted); margin-top: 2px; }

@media print {
  /* The artwork is the point of printing this sheet — keep it, and keep it
     with its caption. */
  .doc-artwork { background: transparent; page-break-inside: avoid; }
  .preview { border-color: #999; }
  .preview-spin { display: none; }
}

/* --- Blocking alert dialog -------------------------------------------------- */

.modal-alert { max-width: 420px; }
.alert-message {
  margin: 0;
  font-size: 14px;
  line-height: 1.55;
}
.alert-detail {
  margin-top: 12px;
  padding: 10px 12px;
  background: var(--warn-soft);
  color: var(--warn);
  border-radius: var(--radius);
  font-size: 13px;
  line-height: 1.5;
}

/* --- Sales chain: quotes, contracts, purchase orders ------------------------
   These build on the existing .doc printable-document styles rather than
   introducing a second document layout. Only the pieces the sales documents
   need that invoices did not are defined here.                               */

.doc h2 { font-size: 24px; font-weight: 600; letter-spacing: 0.02em; margin: 0; }
.doc-no { font-family: var(--mono); font-size: 13px; color: var(--text-muted); margin: 4px 0 0; }
.doc-meta span {
  display: inline-block;
  min-width: 84px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
}
.doc-parties > div { flex: 1; }
.doc-parties h3 {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  margin: 0 0 6px;
}
.doc-parties p { margin: 0 0 2px; font-size: 13px; }
.doc-parties p.strong { font-weight: 600; font-size: 14px; }
.doc-h {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  margin: 24px 0 8px;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--border);
}
.doc-note { font-size: 13px; line-height: 1.6; white-space: pre-wrap; margin: 0 0 16px; }

/* Signature blocks. The contract is signed on paper — printed, signed by both
   parties, scanned back in — so this has to survive printing intact. */
.doc-signatures {
  display: flex;
  gap: 48px;
  margin-top: 56px;
  page-break-inside: avoid;
}
.doc-signatures > div { flex: 1; }
.doc-signatures p { font-size: 12px; color: var(--text-muted); margin: 6px 0 0; line-height: 1.5; }
.sig-line { border-bottom: 1px solid var(--text); height: 42px; }

/* --- Line-item editor ------------------------------------------------------ */

table.lines { width: 100%; font-size: 13px; }
table.lines th { font-weight: 500; }
table.lines td { padding: 4px 6px; vertical-align: middle; }
table.lines input { width: 100%; padding: 5px 7px; font-size: 13px; }
table.lines .right, table.lines th.right { text-align: right; }
table.lines input.right { text-align: right; }
.line-amount { font-variant-numeric: tabular-nums; color: var(--text-muted); }
.btn-icon {
  padding: 2px 8px;
  line-height: 1.4;
  font-size: 15px;
  color: var(--text-faint);
}
.btn-icon:hover { color: var(--danger); }
#add-line { margin-top: 8px; }

/* --- Small shared helpers used by the sales screens ------------------------ */

.alert-info { background: var(--accent-soft); color: var(--accent); }
.right { text-align: right; }
.strong { font-weight: 600; }
.muted { color: var(--text-faint); }
.hint { font-size: 12px; color: var(--text-muted); margin: 4px 0 0; }
.form-actions { display: flex; gap: 10px; margin: 20px 0 40px; }
.form-actions .btn-primary { min-width: 150px; }

/* --- App bar and universal search -------------------------------------------
   The bar sits above the view and outside it, so it survives navigation with
   whatever the user typed still in place.                                    */

.appbar {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 24px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.search { position: relative; flex: 1; max-width: 620px; }
.search-icon {
  position: absolute;
  left: 11px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-faint);
  pointer-events: none;
}
.search input {
  width: 100%;
  padding: 8px 34px 8px 32px;
  font-size: 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
}
.search input:focus {
  outline: 2px solid var(--accent);
  outline-offset: -1px;
  background: var(--surface);
}
.search input::-webkit-search-cancel-button { cursor: pointer; }

.search-key {
  position: absolute;
  right: 9px;
  top: 50%;
  transform: translateY(-50%);
  font-family: var(--mono);
  font-size: 11px;
  color: var(--text-faint);
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 1px 5px;
  pointer-events: none;
  background: var(--surface);
}
.search input:focus ~ .search-key { display: none; }

.search-results {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  max-height: 65vh;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 8px 28px rgba(16, 24, 40, 0.14);
  z-index: 50;
}
.search-empty { padding: 18px; text-align: center; color: var(--text-faint); font-size: 13px; }

.search-group + .search-group { border-top: 1px solid var(--border); }
.search-group-label {
  display: flex;
  justify-content: space-between;
  padding: 7px 12px 5px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-faint);
  background: var(--surface-2);
}

.search-hit {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 7px 12px;
  color: var(--text);
  text-decoration: none;
  border-left: 2px solid transparent;
}
.search-hit:hover, .search-hit.active {
  background: var(--accent-soft);
  border-left-color: var(--accent);
  text-decoration: none;
}
.search-hit-label { font-family: var(--mono); font-size: 13px; flex: none; }
.search-hit-sub { font-size: 13px; color: var(--text-muted); flex: 1; }
.search-hit-extra { font-family: var(--mono); font-size: 11px; color: var(--text-faint); flex: none; }

@media (max-width: 720px) {
  .appbar { padding: 8px 12px; }
  .search-hit { flex-wrap: wrap; gap: 2px 10px; }
}

@media print { .appbar { display: none !important; } }

/* --- Cylinder identity ------------------------------------------------------
   A cylinder carries two numbers: ours, which staff quote to each other, and
   the maker's, stamped on the metal. Ours leads.                             */

.cyl-id { display: inline-flex; align-items: baseline; gap: 7px; white-space: nowrap; }
.cyl-ref { font-family: var(--mono); font-size: 12px; }
.cyl-serial {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--text-faint);
}
.cyl-serial::before { content: "·"; margin-right: 6px; }

@media print { .cyl-serial { color: var(--text-muted); } }

/* ===========================================================================
   DESIGN SYSTEM — Phase 11

   Components added here are the vocabulary new screens are built from. Older
   rules above still style the screens not yet ported; nothing here overrides
   them, so the two coexist while pages move across one at a time.
   ========================================================================= */

/* --- Sub-headings under a page title --------------------------------------- */

.topbar-sub {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin-top: 2px;
}

/* --- Metric cards ----------------------------------------------------------
   The summary a screen opens with. State is encoded in the accent stripe as
   well as the number, so what needs attention reads at a glance rather than
   having to be compared.                                                    */

.metrics {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}

.metric {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-1);
  padding: var(--sp-4) var(--sp-4) var(--sp-4) calc(var(--sp-4) + 3px);
  overflow: hidden;
}
.metric::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 3px;
  background: var(--border-strong);
}
.metric.is-accent::before { background: var(--accent); }
.metric.is-ok::before     { background: var(--ok); }
.metric.is-warn::before   { background: var(--warn); }
.metric.is-danger::before { background: var(--danger); }

.metric-label {
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}
.metric-value {
  font-size: var(--fs-xl);
  font-weight: 600;
  line-height: 1.15;
  margin-top: var(--sp-1);
  font-variant-numeric: tabular-nums;
}
.metric-note {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin-top: var(--sp-1);
}
.metric-note a { color: inherit; text-decoration: underline; }

/* --- Section heading -------------------------------------------------------- */

.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  margin: var(--sp-6) 0 var(--sp-3);
}
.section-head h2 { font-size: var(--fs-md); }
.section-head .section-link { font-size: var(--fs-sm); }

/* --- Charts ----------------------------------------------------------------
   Hand-rolled SVG rather than a charting library: three chart types against a
   40 KB dependency and a CSP exception, and these inherit the theme tokens.  */

.chart { width: 100%; display: block; overflow: visible; }
.chart-wrap { padding: var(--sp-4); }
.chart .grid-line { stroke: var(--border); stroke-width: 1; }
.chart .axis-label {
  font-size: 10px;
  fill: var(--text-faint);
  font-family: inherit;
}
.chart .bar { fill: var(--accent); }
.chart .bar:hover { fill: #1a5fd0; }
.chart .bar-track { fill: var(--surface-2); }
.chart .line-path { fill: none; stroke: var(--accent); stroke-width: 2; stroke-linejoin: round; }
.chart .line-area { fill: var(--accent-soft); opacity: 0.7; }
.chart .line-dot { fill: var(--surface); stroke: var(--accent); stroke-width: 2; }
.chart .value-label {
  font-size: 10px;
  fill: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.chart-empty {
  padding: var(--sp-6);
  text-align: center;
  color: var(--text-faint);
  font-size: var(--fs-sm);
}

/* Donut plus its key, side by side until there is no room. */
.donut-wrap { display: flex; gap: var(--sp-5); align-items: center; flex-wrap: wrap; padding: var(--sp-4); }
.donut-key { display: flex; flex-direction: column; gap: var(--sp-2); min-width: 160px; flex: 1; }
.donut-key-row { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-sm); }
.donut-swatch { width: 10px; height: 10px; border-radius: 2px; flex: none; }
.donut-key-label { flex: 1; color: var(--text-muted); }
.donut-key-value { font-variant-numeric: tabular-nums; font-weight: 600; }
.donut-hole-value { font-size: var(--fs-lg); font-weight: 600; fill: var(--text); }
.donut-hole-label { font-size: 10px; fill: var(--text-faint); }

/* --- Record cards ----------------------------------------------------------
   The card alternative to a dense table row. Used where a record has more to
   say than a row can hold, and on narrow screens where tables stop working.  */

.record-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: var(--sp-3);
}

.record {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--sp-4);
  box-shadow: var(--elev-1);
  transition: box-shadow 0.12s ease, border-color 0.12s ease;
}
.record:hover { box-shadow: var(--elev-2); border-color: var(--border-strong); }
.record-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-2); }
.record-title { font-weight: 600; font-size: var(--fs-base); }
.record-title a { color: var(--text); }
.record-sub { font-size: var(--fs-sm); color: var(--text-muted); }
.record-body { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-4); font-size: var(--fs-sm); }
.record-field { display: flex; flex-direction: column; }
.record-field dt { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-faint); }
.record-field dd { margin: 0; font-variant-numeric: tabular-nums; }
.record-foot {
  display: flex;
  gap: var(--sp-2);
  align-items: center;
  padding-top: var(--sp-2);
  border-top: 1px solid var(--border);
}

/* --- Mobile: the sidebar becomes a drawer ----------------------------------- */

.nav-toggle { display: none; }

@media (max-width: 860px) {
  .nav-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    padding: 0;
    font-size: 17px;
    line-height: 1;
    flex: none;
  }

  .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    width: var(--sidebar-w);
    z-index: 60;
    transform: translateX(-100%);
    transition: transform 0.18s ease;
    box-shadow: var(--elev-3);
  }
  .shell.nav-open .sidebar { transform: translateX(0); }

  .shell.nav-open::after {
    content: "";
    position: fixed;
    inset: 0;
    background: rgba(16, 24, 40, 0.4);
    z-index: 55;
  }

  .metrics { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
  .record-grid { grid-template-columns: 1fr; }
  .donut-wrap { justify-content: center; }
}

@media (prefers-reduced-motion: reduce) {
  .sidebar, .record { transition: none; }
}

@media print {
  .nav-toggle { display: none !important; }
  .metric, .record { box-shadow: none; border-color: #ccc; break-inside: avoid; }
  .chart { break-inside: avoid; }
}

/* --- Document letterhead ----------------------------------------------------
   One header for every printable, so branding and the barcode reach all of
   them at once. The deferred styling pass has a single place to change.     */

.doc-letterhead {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--sp-5);
  padding-bottom: var(--sp-4);
  border-bottom: 2px solid var(--text);
  margin-bottom: var(--sp-5);
}

.doc-org { display: flex; gap: var(--sp-4); align-items: flex-start; min-width: 0; }
.doc-logo { flex: none; width: 76px; }
.doc-logo img { max-width: 76px; max-height: 76px; display: block; }
.doc-logo-fallback { display: block; width: 76px; height: 1px; }

.doc-org-name { font-size: var(--fs-lg); font-weight: 600; line-height: 1.2; }
.doc-org-tagline { font-size: var(--fs-sm); color: var(--text-muted); margin-top: 2px; }
.doc-org-contact {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin-top: var(--sp-2);
  line-height: 1.45;
}

.doc-ident { text-align: right; flex: none; }
.doc-kind {
  font-size: var(--fs-md);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.doc-number { font-family: var(--mono); font-size: var(--fs-sm); color: var(--text-muted); }
.doc-barcode { margin: var(--sp-2) 0; display: flex; justify-content: flex-end; }
.doc-barcode .barcode { max-width: 210px; height: auto; color: var(--text); }

.doc-ident-row { font-size: var(--fs-sm); margin-top: 2px; }
.doc-ident-row span {
  display: inline-block;
  min-width: 76px;
  color: var(--text-faint);
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.doc-foot-org { margin-top: var(--sp-2); white-space: pre-wrap; }

.logo-preview img { max-width: 180px; max-height: 90px; display: block; }

@media (max-width: 620px) {
  .doc-letterhead { flex-direction: column; }
  .doc-ident { text-align: left; }
  .doc-barcode { justify-content: flex-start; }
}

@media print {
  /* The barcode is the point of printing — it must stay pure black on white
     however the screen palette is set. */
  .doc-barcode .barcode { color: #000; }
  .doc-letterhead { break-inside: avoid; }
}

/* --- Treasury --------------------------------------------------------------- */

.recon-tick { width: 16px; height: 16px; cursor: pointer; }
.recon-tick:disabled { opacity: 0.4; cursor: wait; }

/* --- Mobile pass ------------------------------------------------------------
   Phase 15. The screens are dense by nature — a job list has eight useful
   columns — so tables scroll horizontally inside their own container rather
   than being reflowed into something unreadable. The page body never scrolls
   sideways.                                                                  */

@media (max-width: 860px) {
  .topbar {
    flex-direction: column;
    align-items: stretch;
    gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-4);
  }
  .topbar-actions { flex-wrap: wrap; }
  .topbar-actions .btn,
  .topbar-actions button { flex: 1 1 auto; justify-content: center; text-align: center; }

  /* Tables stack rather than scroll sideways — see the phone rules above. */

  .grid-2, .grid-4 { grid-template-columns: 1fr; }

  .doc { padding: var(--sp-4); }
  .doc-parties { flex-direction: column; gap: var(--sp-4); }
  .doc-signatures { flex-direction: column; gap: var(--sp-6); }

  .modal { width: calc(100vw - 24px); max-height: 88vh; }
  .search-results { max-height: 60vh; }

  /* Filter bars stack rather than squeezing each control to nothing. */
  .filters { flex-direction: column; align-items: stretch; }
  .filters .field { min-width: 0; width: 100%; }
  .filters .field[style] { flex: 1 1 auto !important; }
}

@media (max-width: 480px) {
  .metrics { grid-template-columns: 1fr 1fr; }
  .metric-value { font-size: var(--fs-lg); }
  .content { padding: var(--sp-3) var(--sp-2) var(--sp-7); }
}

/* Nothing may push the page sideways — a horizontally scrolling body makes
   every screen feel broken on a phone. */
html, body { max-width: 100%; overflow-x: hidden; }

/* --- The agreement form -----------------------------------------------------
   Matched to the legacy sample PDFs. Deliberately plainer than the rest of the
   interface: this is a form that gets printed, signed by hand and filed, so it
   uses hairline borders and tight rows rather than the app's card styling.   */

.ag-doc { font-size: 13px; }

.ag-letterhead {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-4);
  min-height: 34px;
  margin-bottom: var(--sp-2);
}
.ag-logo img { max-width: 130px; max-height: 56px; display: block; }
.ag-barcode .barcode { max-width: 190px; height: auto; color: var(--text); }

.ag-title-wrap { text-align: center; margin-bottom: var(--sp-3); }
.ag-title {
  display: inline-block;
  border: 1px solid var(--text);
  padding: 6px 16px;
  font-weight: 700;
  font-size: 16px;
}
.ag-variant {
  text-align: center;
  font-size: 24px;
  font-weight: 700;
  margin: 0 0 var(--sp-5);
}

.ag-parties {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-6);
  margin-bottom: var(--sp-4);
}
.ag-party > div { margin-bottom: 2px; line-height: 1.5; }
.ag-party.right { text-align: right; }

/* The dimension grid: label / value / label / value, hairline ruled. */
.ag-grid, .ag-table, .ag-price {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: var(--sp-3);
  font-size: 13px;
}
.ag-grid th, .ag-grid td,
.ag-table th, .ag-table td,
.ag-price td {
  border: 1px solid var(--text);
  padding: 5px 8px;
}
.ag-grid th { text-align: left; font-weight: 700; width: 20%; white-space: nowrap; }
.ag-grid td { width: 30%; font-variant-numeric: tabular-nums; }
.ag-table th { text-align: left; font-weight: 400; }
.ag-table td { font-variant-numeric: tabular-nums; }
.ag-table tfoot td { font-weight: 700; }

.ag-section-title {
  text-align: center;
  font-weight: 700;
  margin: var(--sp-4) 0 var(--sp-1);
}
.ag-section-label { font-weight: 700; font-size: 12px; }

.ag-colors { margin: var(--sp-3) 0; line-height: 2; }
.ag-check { display: inline-flex; align-items: center; gap: 4px; margin-right: 12px; }
.ag-box {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  border: 1px solid var(--text);
  font-size: 11px;
  line-height: 1;
}
.ag-box.ticked { background: var(--accent); color: #fff; border-color: var(--accent); }
.ag-spot-label { font-size: 12px; color: var(--text-muted); margin-right: 4px; }
.ag-spot {
  display: inline-block;
  border: 1px solid var(--text);
  padding: 2px 10px;
  margin-right: 6px;
  min-width: 70px;
  font-size: 12px;
}
.ag-spot-empty { min-width: 90px; height: 20px; }

.ag-price td { font-size: 13px; }

.ag-terms { margin: var(--sp-4) 0; }
.ag-terms-body { white-space: pre-wrap; font-size: 12px; line-height: 1.6; }

.ag-note {
  border: 1px solid var(--text);
  padding: var(--sp-2) var(--sp-3);
  margin: var(--sp-4) 0 var(--sp-6);
}
.ag-note legend { padding: 0 6px; font-size: 13px; }

.ag-signatures {
  display: flex;
  justify-content: space-around;
  gap: var(--sp-6);
  margin-top: var(--sp-7);
  text-align: center;
  page-break-inside: avoid;
}
.ag-signatures > div { flex: 1; max-width: 260px; font-size: 13px; line-height: 1.6; }
.ag-sig-line { border-top: 1px solid var(--text); margin-bottom: 6px; }

@media (max-width: 620px) {
  .ag-parties { flex-direction: column; gap: var(--sp-3); }
  .ag-party.right { text-align: left; }
  .ag-signatures { flex-direction: column; align-items: center; }
}

@media print {
  .ag-doc { font-size: 12px; }
  .ag-barcode .barcode { color: #000; }
  /* The tick boxes must survive a monochrome printer. */
  .ag-box.ticked { background: #000; border-color: #000; color: #fff; }
  .ag-grid th, .ag-grid td, .ag-table th, .ag-table td, .ag-price td,
  .ag-note, .ag-title { border-color: #000; }
  .ag-sig-line { border-top-color: #000; }
}

/* --- Customer statement -----------------------------------------------------
   Matched to the legacy sample: sixteen columns, hairline ruled, with the
   running balance carrying a DR/CR marker rather than a minus sign. The red and
   green are the sample's own (#c0392b / #27ae60) — a statement is read by
   people scanning for the side, not the sign.                                */

.st-doc { font-size: 12px; }

.st-letterhead {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-4);
  margin-bottom: var(--sp-3);
}
.st-logo img { max-width: 120px; max-height: 54px; display: block; }
.st-org-name { font-size: 16px; font-weight: 600; }
.st-org > div { line-height: 1.45; color: var(--text-muted); }
.st-org-name { color: var(--text); }

.st-title {
  text-align: center;
  font-size: 19px;
  font-weight: 700;
  margin: 0 0 var(--sp-4);
}

.st-party { border-collapse: collapse; border: 1px solid var(--text); margin-bottom: var(--sp-4); }
.st-party td { border-bottom: 1px solid var(--text); padding: 3px 8px; font-size: 12px; }
.st-party tr:last-child td { border-bottom: 0; }
.st-party td:first-child { width: 150px; }
.st-party td:nth-child(2) { width: 15px; text-align: center; }

.st-table { border-collapse: collapse; width: 100%; font-size: 11px; }
.st-table th, .st-table td { border: 1px solid var(--text); padding: 3px 5px; text-align: left; }
.st-table th { font-weight: 600; background: var(--surface-2); white-space: nowrap; }
.st-table td.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.st-table .st-opening td { font-style: italic; background: var(--surface-2); }
.st-table .st-totals td { border-top: 2px solid var(--text); }
.st-empty { text-align: center; padding: var(--sp-5); color: var(--text-faint); }

/* The sample's own debit/credit colours. */
.balance-dr { color: #c0392b; }
.balance-cr { color: #27ae60; }

@media print {
  .st-doc { font-size: 10px; }
  .st-table { font-size: 8.5px; }
  .st-table th, .st-table td, .st-party, .st-party td { border-color: #000; }
  .st-table th { background: transparent; }
  .st-table th, .st-table td { padding: 2px 3px; }
  .st-table tr { page-break-inside: avoid; }
  /* Repeat the column headings on every page of a long statement. */
  .st-table thead { display: table-header-group; }
}

/*
 * The statement prints on A4 PORTRAIT, like every other document.
 *
 * statementView() injects the page size with its own markup (a bare @page in
 * this stylesheet would apply to every printed document), so it exists only
 * while a statement is on screen.
 *
 * The columns below are sized for the 186 mm (≈ 703 px) that A4 portrait
 * leaves inside 12 mm margins, at the 8.5 px print size. Each fixed column
 * holds the widest thing it can ever contain. Widths measured from a real
 * print-to-PDF (Chromium, 2026-09-29), against the column's width:
 *
 *   Date       01-Jan-25                  42 px   of  70 (10%)
 *   Reference  PO-PRT-2026-0001 (mono)    82 px   of  98 (14%)
 *   Debit      999,999,999,999.99         76 px   of  98 (14%)
 *   Credit     999,999,999,999.99         76 px   of  98 (14%)
 *   Balance    999,999,999,999.99 DR      90 px   of 133 (19%)
 *
 * — the largest amount NUMERIC(14,2) can hold. Narration takes what is left
 * (29%) and wraps, because it is the only column whose length is unknown.
 * An earlier layout gave Balance 12% and printed "192,240.00 D".
 */
.st-table { table-layout: fixed; }
/* Through .st-doc on purpose: `.doc table` (13 px, above) outranks a bare
   `.st-table`, and silently did — the statement printed at 13 px, the columns
   below could not hold it, and figures ran across the rules. */
.st-doc .st-table { font-size: 11px; }
.st-table th:nth-child(1), .st-table td:nth-child(1) { width: 10%; white-space: nowrap; } /* Date */
.st-table th:nth-child(2), .st-table td:nth-child(2) { width: 14%; }  /* Reference */
.st-table th:nth-child(3), .st-table td:nth-child(3) { width: auto; } /* Narration */
.st-table th:nth-child(4), .st-table td:nth-child(4) { width: 14%; }  /* Debit */
.st-table th:nth-child(5), .st-table td:nth-child(5) { width: 14%; }  /* Credit */
.st-table th:nth-child(6), .st-table td:nth-child(6) { width: 19%; }  /* Balance */

/* Narration wraps, at a word where it can. Figures and references never do: a
   number broken across two lines is unreadable, and the widths above hold the
   widest. The money headings sit over their figures, right-aligned. */
.st-table td { overflow-wrap: anywhere; }
.st-table th { white-space: normal; }
.st-table th:nth-child(n + 4) { text-align: right; }
.st-table td.num, .st-table th.num { white-space: nowrap; }
/* At the table's own size: `.mono` pins 12 px, which on paper made
   "INV-2026-0001" wider than its column. */
.st-doc .st-table td.mono { font-family: var(--mono); font-size: inherit; white-space: nowrap; }

/* The app's tables drop the last row's rule (`tbody tr:last-child td`); a ruled
   statement needs it, or the table stops open at the bottom. */
.st-doc .st-table tbody tr:last-child td { border-bottom: 1px solid var(--text); }

/* The customer's name in the heading opens their record. */
.st-party a { color: inherit; }
.st-party a:hover { text-decoration: underline; }

@media print {
  /* On screen the table scrolls inside .table-wrap; on paper that box would
     clip whatever reached its edge. Nothing here should, but a clip would hide
     the proof. */
  .st-doc .table-wrap { overflow: visible; }
  .st-doc .st-table { font-size: 8.5px; }
  /* Hairline rules, as the legacy statement — a heavy grid reads as a form. */
  .st-table th, .st-table td, .st-party, .st-party td { border-width: 0.5pt; }
  .st-table .st-totals td { border-top-width: 1.5pt; }
  .st-doc .st-table tbody tr:last-child td { border-bottom: 0.5pt solid #000; }
  .st-table th { font-size: 8px; text-transform: uppercase; letter-spacing: 0.03em; }
  .st-title { font-size: 15px; margin-bottom: var(--sp-3); }
  .st-party { margin-bottom: var(--sp-3); }
  .st-party td { font-size: 9px; padding: 2px 6px; }
  .st-party td:first-child { width: 110px; }
}

/* --- Cylinder lifeline ------------------------------------------------------
   A single chronological spine. Distinct from `.timeline`, which the job status
   log uses and which draws its own dot via ::before — mixing the two would put
   two markers on every row.                                                   */

.lifeline { list-style: none; margin: 0; padding: 0; }

.tl-item {
  position: relative;
  display: flex;
  gap: var(--sp-3);
  padding: 0 0 var(--sp-4) var(--sp-5);
  border-left: 2px solid var(--border);
}
.tl-item:last-child { border-left-color: transparent; padding-bottom: 0; }

.tl-node {
  position: absolute;
  left: -11px;
  top: 0;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  line-height: 1;
  background: var(--surface);
  border: 2px solid var(--border-strong);
  color: var(--text-muted);
}
.tl-accent .tl-node { border-color: var(--accent); color: var(--accent); }
.tl-ok     .tl-node { border-color: var(--ok);     color: var(--ok); }
.tl-warn   .tl-node { border-color: var(--warn);   color: var(--warn); }
.tl-danger .tl-node { border-color: var(--danger); color: var(--danger); }

.tl-body { flex: 1; min-width: 0; }
.tl-head {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  flex-wrap: wrap;
  margin-bottom: 1px;
}
.tl-kind {
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 600;
  color: var(--text-muted);
}
.tl-accent .tl-kind { color: var(--accent); }
.tl-ok     .tl-kind { color: var(--ok); }
.tl-warn   .tl-kind { color: var(--warn); }
.tl-danger .tl-kind { color: var(--danger); }

.tl-date { font-size: var(--fs-sm); color: var(--text-faint); }
.tl-amount {
  margin-left: auto;
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}
.tl-title { font-size: var(--fs-base); }
.tl-context {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin-top: 1px;
  line-height: 1.5;
}
.tl-context .badge { vertical-align: baseline; }

@media (max-width: 620px) {
  .tl-amount { margin-left: 0; }
}

@media print {
  .lifeline { break-inside: auto; }
  .tl-item { break-inside: avoid; }
  .tl-node { border-color: #000; color: #000; }
}

/* --- Printing --------------------------------------------------------------
   The pinned-shell layout would otherwise print a single viewport and stop.  */

@media print {
  #app, .shell { height: auto; overflow: visible; }
  .sidebar { display: none !important; }
  .main { overflow: visible; }
  .content { overflow: visible; padding: 0; }
}

/* --- Printing a document ----------------------------------------------------
   A page with a document on it prints the document and nothing else.

   Hiding the app piece by piece — the sidebar, the top bar, anything marked
   no-print — missed whatever nobody marked: breadcrumbs, the section tabs, a
   record's tabs, the cards and notes above a document. So this is the other
   way round: on a page holding a .doc, every element that is not the document,
   not inside it, and not one of the containers it sits in is hidden. Nothing
   new added to a page can leak onto paper, whoever forgets to mark it.

   Pages without a document (a list, a report) keep the rules above.          */

@media print {
  body:has(.doc) *:not(.doc, .doc *, :has(.doc)) { display: none !important; }
  /* The containers it sits in keep no chrome of their own. */
  body:has(.doc) :has(.doc) {
    margin: 0 !important;
    padding: 0 !important;
    border: 0 !important;
    background: none !important;
    box-shadow: none !important;
  }
}

/* --- Quotation specification form -------------------------------------------
   The specification IS the pricing input, so the calculation is shown as it is
   entered rather than only after saving.                                     */

.colour-picker { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-3); margin-bottom: var(--sp-2); }
.colour-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px 5px 7px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  font-size: var(--fs-sm);
  cursor: pointer;
  user-select: none;
}
.colour-chip:hover { border-color: var(--border-strong); background: var(--surface-2); }
.colour-chip:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); }
.colour-chip input { margin: 0; }
.colour-swatch {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  flex: none;
}

.spot-list { display: flex; flex-direction: column; gap: var(--sp-1); margin: var(--sp-2) 0; }
.spot-row { display: flex; gap: var(--sp-2); align-items: center; max-width: 380px; }
.spot-row input { flex: 1; }

/* The live arithmetic, so the price is visible before saving. */
.calc-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  margin: var(--sp-3) 0;
  background: var(--surface-2);
  border-radius: var(--radius);
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
}
.calc-strip strong { font-size: var(--fs-md); color: var(--accent); }

.colour-price { width: 110px; }
input.right { text-align: right; }

details.more { margin-top: var(--sp-3); }
details.more summary {
  cursor: pointer;
  font-size: var(--fs-sm);
  color: var(--text-muted);
  user-select: none;
}
details.more summary:hover { color: var(--text); }

/* The running total and the primary action, pinned to the bottom of the form.
   Sticky rather than fixed so it stays inside the content column and never
   covers the sidebar; the form is its containing block, so it is pinned from
   the moment the page opens rather than appearing once you scroll. */
.form-bar {
  position: sticky;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-4);
  margin-top: var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-2);
  z-index: 10;
}
.form-bar-total { display: flex; align-items: baseline; gap: var(--sp-3); min-width: 0; }
.form-bar-label { font-size: var(--fs-sm); color: var(--text-muted); }
.form-bar-value {
  font-size: var(--fs-xl);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
/* The arithmetic behind the total, so the number is checkable without
   scrolling back up to the sections that produced it. */
.form-bar-breakdown {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1) var(--sp-3);
  font-size: var(--fs-sm);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.form-bar-breakdown b { font-weight: 600; color: var(--text); }
.form-bar-actions { display: flex; align-items: center; gap: var(--sp-2); margin-left: auto; }
.form-bar-actions .btn-primary { min-width: 150px; }

@media (max-width: 620px) {
  .colour-price { width: 90px; }
  .form-bar { padding: var(--sp-2) var(--sp-3); gap: var(--sp-2); }
  .form-bar-value { font-size: var(--fs-lg); }
  .form-bar-actions { width: 100%; }
  .form-bar-actions .btn-primary { flex: 1; }
}

/* The payment schedule's total restated in words, under its table. */
.ag-inwords { font-size: 12px; margin: 4px 0 var(--sp-4); }

/* --- Sortable headers ------------------------------------------------------
   The header is a button, not a link with an onclick: it is operated by the
   keyboard for free and announces its state through aria-sort.
   -------------------------------------------------------------------------- */

.th-sort { padding: 0; }
.th-sort button {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  width: 100%;
  padding: 0 12px 9px 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: var(--fs-xs);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-faint);
  cursor: pointer;
  text-align: inherit;
}
th.num.th-sort button { justify-content: flex-end; }
.th-sort button:hover { color: var(--text); }
.th-sort.is-sorted button { color: var(--accent); }
.th-arrow { display: inline-flex; align-items: center; }

/* --- Tables on a phone -----------------------------------------------------
   Below the layout breakpoint each row becomes a labelled block. Every cell
   already carries its column's label, put there by table(), so this needs
   nothing from the fifty-three call sites.

   Dragging a six-column table sideways was the alternative, and on an invoice
   list it put the total off-screen.

   SCREEN ONLY. The delivery challan is a printed document that goes through the
   same table(), and a challan printed from a phone would otherwise come out as
   stacked blocks rather than as a table. A printed page has a paper width, not
   a viewport, and it should not depend on which device pressed print.
   -------------------------------------------------------------------------- */

@media screen and (max-width: 720px) {
  .table-wrap { overflow-x: visible; }
  .table-wrap table { min-width: 0; }

  .table-wrap thead { display: none; }
  .table-wrap tr {
    display: block;
    padding: var(--sp-3) var(--sp-4);
    border-bottom: 1px solid var(--border);
  }
  .table-wrap tbody tr:last-child { border-bottom: 0; }

  .table-wrap td {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--sp-4);
    padding: 2px 0;
    border: 0;
    text-align: right;
  }
  /* The label, from the cell's own data-label. */
  .table-wrap td::before {
    content: attr(data-label);
    flex: none;
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-faint);
    text-align: left;
  }
  /* A cell whose label is empty is an actions cell: it needs no prefix and
     should sit on its own line. */
  .table-wrap td[data-label='']::before { content: none; }
  .table-wrap td[data-label=''] { justify-content: flex-start; padding-top: var(--sp-2); }

  /* The row's headline. Reads as a title rather than as another field. */
  .table-wrap td.col-title {
    display: block;
    font-size: var(--fs-base);
    font-weight: var(--fw-semibold);
    text-align: left;
    padding-bottom: var(--sp-2);
  }
  .table-wrap td.col-title::before { content: none; }

  /* Marked as worth dropping when there is no room. */
  .table-wrap td.col-secondary { display: none; }

  .table-wrap tfoot tr { display: flex; justify-content: space-between; }
  .table-wrap tfoot td { padding: var(--sp-1) 0; }
  .table-wrap tfoot td:empty { display: none; }
}

/* --- Skip link -------------------------------------------------------------
   Off-screen until focused, which is the only time it is wanted.
   -------------------------------------------------------------------------- */

.skip-link {
  position: absolute;
  top: var(--sp-2);
  left: var(--sp-2);
  z-index: 100;
  padding: var(--sp-2) var(--sp-4);
  background: var(--accent);
  border: 0;
  border-radius: var(--radius);
  color: #fff;
  font: inherit;
  font-size: var(--fs-sm);
  cursor: pointer;
  /* Not display:none — it must stay reachable by Tab. */
  transform: translateY(-200%);
}
.skip-link:focus { transform: none; }

/* The content pane takes focus from the skip link, but never draws a ring for
   it: a whole-page outline is noise, and the heading inside says where you
   landed. */
#view:focus { outline: none; }

/* --- Page header -----------------------------------------------------------
   One component for all thirty-nine pages. The negative margin that every one
   of them used to carry inline lives here now.
   -------------------------------------------------------------------------- */

.crumbs {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  flex-wrap: wrap;
  margin: -20px -24px 0;
  padding: var(--sp-3) var(--sp-5) 0;
  font-size: var(--fs-sm);
  color: var(--text-muted);
  /* The header's own surface, so the two read as one block. */
  background: var(--surface);
}
.crumbs a { color: var(--text-muted); }
.crumbs a:hover { color: var(--accent); }
.crumbs .icon { color: var(--text-faint); }
.crumbs span[aria-current] { color: var(--text); font-weight: var(--fw-medium); }

.topbar-title { display: flex; align-items: center; gap: var(--sp-4); min-width: 0; }
.topbar-title h1 { margin: 0; }
.topbar-meta { font-size: var(--fs-sm); color: var(--text-muted); margin-top: 2px; }

/* With a breadcrumb above it the header loses its own top margin, so the two
   read as one block rather than as two stacked bars. The MARGIN is the part
   that matters: the breadcrumb already bleeds up into the content padding, and
   a header keeping its own negative top margin slid up over it, hiding the
   trail behind the title. */
.topbar.has-crumbs { margin-top: 0; padding-top: var(--sp-2); }

@media (max-width: 720px) {
  .crumbs { margin: -16px -12px 0; padding: var(--sp-2) var(--sp-3) 0; }
  /* The header bleeds by the phone's content padding too. It kept the desktop
     24px, overshooting the 12px padding, so every title sat on the screen's
     left edge. (.topbar.has-crumbs still drops the top margin.) */
  .topbar { margin: -16px -12px 16px; }
  /* Only the last two levels fit on a handset, and the last is where you are. */
  .crumbs a:not(:last-of-type) { display: none; }
  .crumbs .icon:not(:last-of-type) { display: none; }
}

/* --- Queues ----------------------------------------------------------------
   The dashboard's first answer: what is waiting. A row per queue, its count on
   the right, the whole row a link into the list already filtered to it.

   Only non-empty queues are rendered — a list of zeroes is not reassurance, it
   is six rows of noise between someone and the one thing that needs them.
   -------------------------------------------------------------------------- */

.queues { display: grid; gap: var(--sp-2); margin-bottom: var(--sp-5); }
.queue {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--border-strong);
  border-radius: var(--radius);
  color: var(--text);
  box-shadow: var(--elev-1);
}
.queue:hover { text-decoration: none; border-color: var(--accent); border-left-color: var(--accent); }
.queue .icon { color: var(--text-faint); flex: none; }
.queue:hover .icon { color: var(--accent); }
.queue-text { display: flex; flex-direction: column; min-width: 0; }
.queue-text strong { font-weight: var(--fw-medium); }
.queue-text em {
  font-style: normal;
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin-top: 1px;
}
.queue-n {
  margin-left: auto;
  font-size: var(--fs-lg);
  font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums;
  padding-left: var(--sp-3);
}

/* Tone marks urgency on the rail, not on the whole row — a dashboard of
   coloured blocks tells you nothing about which one is worst. */
.queue-warn { border-left-color: var(--warn); }
.queue-warn .queue-n { color: var(--warn); }
.queue-warn .icon { color: var(--warn); }
.queue-danger { border-left-color: var(--danger); }
.queue-danger .queue-n { color: var(--danger); }
.queue-danger .icon { color: var(--danger); }

/* --- Record tabs -----------------------------------------------------------
   A record's own sections, inside its page — distinct from the section strip
   under the app bar, which belongs to the sidebar. Pills rather than a second
   underlined strip, so two levels of navigation never look like one.
   -------------------------------------------------------------------------- */

.record-tabs {
  display: flex;
  gap: var(--sp-1);
  flex-wrap: wrap;
  margin-bottom: var(--sp-4);
  padding: var(--sp-1);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.rtab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px var(--sp-3);
  border-radius: var(--radius);
  font-size: var(--fs-base);
  color: var(--text-muted);
  white-space: nowrap;
}
.rtab:hover { background: var(--surface); color: var(--text); text-decoration: none; }
.rtab.active {
  background: var(--surface);
  color: var(--text);
  font-weight: var(--fw-medium);
  box-shadow: var(--elev-1);
}
.rtab-n {
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  color: var(--text-faint);
  background: var(--bg);
  border-radius: var(--radius-pill);
  padding: 1px 6px;
}
.rtab.active .rtab-n { background: var(--accent-soft); color: var(--accent); }

@media (max-width: 560px) {
  .record-tabs { overflow-x: auto; flex-wrap: nowrap; scrollbar-width: none; }
  .record-tabs::-webkit-scrollbar { display: none; }
}

/* Figures in a definition list line up on the right, as money should. */
.dl dd.num { text-align: right; font-variant-numeric: tabular-nums; }

/* --- Reports index ---------------------------------------------------------
   Pointers to the views elsewhere in the app that answer a reporting question.
   Listed rather than duplicated: there is one Store Stock screen, and this
   links to it.
   -------------------------------------------------------------------------- */

.report-links { display: grid; gap: var(--sp-2); }
.report-link {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
}
.report-link:hover {
  text-decoration: none;
  border-color: var(--accent);
  background: var(--accent-soft);
}
.report-link .icon { color: var(--text-faint); margin-top: 2px; }
.report-link:hover .icon { color: var(--accent); }
.report-link strong { display: block; font-weight: var(--fw-semibold); }
.report-link em {
  display: block;
  font-style: normal;
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin-top: 2px;
}

/* --- Workflow stepper --------------------------------------------------------
   A record's stages in one strip, current one in view (ui.js stepper()). The
   strip scrolls sideways inside itself on a narrow screen; the page never does.
   State is carried by the icon and the word, colour only reinforces it.      */

.stepper {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin-bottom: var(--sp-4);
}
.stepper-list {
  display: flex;
  list-style: none;
  margin: 0;
  padding: var(--sp-2);
  gap: var(--sp-1);
  overflow-x: auto;
  scroll-behavior: smooth;
}
.step { flex: 1 0 auto; min-width: 0; }
.step-head {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  width: 100%;
  padding: var(--sp-2) var(--sp-3);
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
}
button.step-head { cursor: pointer; }
button.step-head:hover, a.step-head:hover { background: var(--surface-2); }
/* A stage that opens its record reads as a link: the label underlines. */
a.step-head { text-decoration: none; }
a.step-head:hover .step-label { text-decoration: underline; }
button.step-head[aria-expanded='true'] { background: var(--accent-soft); }
.step-head .icon { flex: none; margin-top: 1px; }
.step-text { display: flex; flex-direction: column; min-width: 0; }
.step-label { font-size: var(--fs-sm); font-weight: var(--fw-medium); white-space: nowrap; }
.step-sub { font-size: var(--fs-xs); color: var(--text-muted); white-space: nowrap; }

.step-done .icon { color: var(--ok); }
.step-current .icon { color: var(--accent); }
.step-current .step-label { font-weight: var(--fw-semibold); }
.step-current { box-shadow: inset 0 -2px 0 var(--accent); }
.step-blocked .icon { color: var(--warn); }
.step-blocked { box-shadow: inset 0 -2px 0 var(--warn); }
.step-upcoming .icon, .step-skipped .icon { color: var(--text-faint); }
.step-skipped .step-label { color: var(--text-faint); }

.stepper-panel:empty { display: none; }
.stepper-detail {
  border-top: 1px solid var(--border);
  padding: var(--sp-3) var(--sp-4);
  font-size: var(--fs-sm);
  line-height: var(--lh-normal);
}
.stepper-detail div { color: var(--text-muted); }

@media (prefers-reduced-motion: reduce) {
  .stepper-list { scroll-behavior: auto; }
}

/* The progress view: what is done and what is left, in plain lists. */
.progress-lists { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4); }
.progress-lists ul { list-style: none; margin: 0; padding: 0; }
.progress-lists li {
  display: flex;
  gap: var(--sp-2);
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--border);
}
.progress-lists li:last-child { border-bottom: 0; }
.progress-lists .icon { flex: none; margin-top: 2px; }
.progress-lists small { display: block; color: var(--text-muted); font-size: var(--fs-xs); }
@media (max-width: 720px) {
  .progress-lists { grid-template-columns: 1fr; }
}

/* A cylinder search hit's primary and current jobs: links beside the hit,
   indented under it, since a link cannot sit inside the hit's own. */
.search-hit-jobs {
  padding: 0 var(--sp-3) var(--sp-2) var(--sp-5);
  font-size: var(--fs-xs);
  color: var(--text-muted);
}
