/* Jobs list page-specific row accents and notes column. */

/* stylelint-disable no-descending-specificity --
   pages.css is layered: status-class rules → hover-state rules →
   inline-edit cascades → cross-table reuse (parts-arrivals,
   jobforms, job-shipping). Many "descending specificity" warnings
   are false positives across disjoint parent scopes
   (`.parts-arrivals-row` is never also `.job-row`, etc.); a few are
   legitimate cascade layering (`:focus-visible` after `:hover`).
   Disabled file-wide pending a structural refactor into per-section
   stylesheets (tracked in open_debt as Phase D CSS hygiene). */

.jobs-table-wrapper {
  overflow-x: clip;
}

.jobs-table {
  table-layout: fixed;
}

.jobs-table th,
.jobs-table td {
  overflow-wrap: anywhere;
  white-space: normal;
}

.jobs-table th {
  padding-left: var(--space-2);
  padding-right: var(--space-2);
}

.jobs-table td {
  /* F07a: tighter vertical rhythm — 0.4rem block padding (was the base
     0.52rem) for a denser scan-friendly row. Unlayered so it beats the base
     `td { padding }` shorthand (unlayered normal > layered normal). */
  padding-block: 0.4rem;
  padding-left: var(--space-2);
  padding-right: var(--space-2);
}

.jobs-col-drag { width: 3rem; }
.jobs-col-number { width: 8%; }
.jobs-col-client { width: 10%; }
.jobs-col-equipment { width: 10%; }
.jobs-col-notes { width: 18%; }
.jobs-col-plates { width: 6.5%; }
.jobs-col-service { width: 10%; }
.jobs-col-priority { width: 4%; }
.jobs-col-team { width: 8%; }
.jobs-col-assignee { width: 8%; }
.jobs-col-archived { width: 5%; }

.jobs-col-status {
  width: 9rem;
  min-width: 9rem;
}

.jobs-col-stage {
  width: 9rem;
  min-width: 9rem;
}

/* R08 reverts F04's `white-space: nowrap` here.
   F04 could pin the chip to one line because it abbreviated the longest
   status to "CNS". R08 retires that abbreviation — the acronym was the
   readability bug Mitch reported — so the chip is "Completed / Not
   Shipped" again, which measures ~165px against a 144px (1280vw) /
   160px (1600vw) column. With `nowrap` it did not wrap OR clip: it
   silently overflowed rightward and painted over the Stage column's
   content. Widening the column alone does not fix it (165px still
   exceeds the 1600px-viewport width), so wrapping is restored — the
   pre-F04 behaviour this comment already described.
   The short chips ("Await A+P", "Ongoing") are unaffected; they are far
   under the column width and never reach a wrap point. */
.jobs-table .badge-status {
  white-space: normal;
}

.jobs-table .job-number-cell {
  overflow-wrap: normal;
  white-space: nowrap;
}

/* R120 (machine finding, v1.20.2) — HESS Core's jobs list only (HESS Field's
   tables reuse these classes with their own widths and were walked clean).
   The job number measures ~118px at 0.95rem/600 ("26-0632-GG-2"); as 8% of
   the table it printed over the client name at 1024 and 1280, menu open or
   collapsed, and spilled 2px at 1920. A fixed width holds the usual number
   and its padding at every width; a longer one ("24-1126-B-KDREV1") breaks at
   its own hyphens, never inside a token. The priority badge's 4% was 20px at
   1024 with the menu open, narrower than the badge. */
.jobs-table-office .jobs-col-number { width: 8.75rem; }
.jobs-table-office .jobs-col-priority { width: 2.75rem; }

.jobs-table-office .job-number-cell {
  white-space: normal;
}

/* F07a type scale: number 0.95rem/600 (the strong inherits 600, not its UA
   700 bolder), client 0.9rem, notes 0.85rem — a gentle hierarchy that lets the
   job number lead without shouting. Unlayered (no layered competitor). */
.job-number-link {
  color: var(--app-brand-700);
  font-size: 0.95rem;
  font-weight: 600;
  text-decoration: none;
}

.job-number-link strong {
  font-weight: inherit;
}

.job-number-link:hover,
.job-number-link:focus {
  color: var(--app-brand-900);
  text-decoration: underline;
}

.job-client-link {
  color: var(--app-brand-700);
  font-size: 0.9rem;
  font-weight: 700;
  text-decoration: none;
}

.job-client-link:hover,
.job-client-link:focus {
  color: var(--app-brand-900);
  text-decoration: underline;
}

/* C04 + F06: row tints mirror the XLSX conditional-formatting palette in
   `/home/usar/Documents/HESS Shop _ Field Service Jobs in Progress.xlsx`.
   Workers pattern-match these colors from the spreadsheet, so we ship the
   tints XLSX-exact (CNS blue + completed vivid green; F01 reconciled their
   foregrounds to AA contrast).

   F06 retired the old `!important` arms race: these rules used to carry
   `!important` solely to beat the generic
   `tbody tr:hover { background: var(--app-surface-subtle) }`
   in tables.css. Both are now in cascade layers — the generic hover in
   `app.tables`, this block in `app.jobs` — so `app.jobs` wins by LAYER
   precedence and no `!important` is needed. Sub-layer order within app.jobs
   (low→high): tints < states < badges. That ordering lets the priority-badge
   color pins (badges) beat the inline-edit brand-50 hover wash (states) on
   the badge cell WITHOUT `!important`, and both beat the row tints.

   F03's left-border accents stay UNLAYERED (below this block) on purpose:
   they must outrank the unlayered `tbody tr { border-left: transparent }`
   base in tables.css, and unlayered normal > layered normal. The status
   *backgrounds* are layered (they fight the layered hover); the *border-left*
   is unlayered (it fights an unlayered base). Different competitors. */
@layer app.jobs {
  @layer tints, states, badges;

  @layer tints {
    .job-row[class~="status-on_hold"] {
      background: var(--job-status-on-hold-bg);
    }

    .job-row[class~="status-awaiting_arrival"],
    .job-row[class~="status-awaiting_parts"],
    .job-row[class~="status-awaiting_arrival_and_parts"] {
      background: var(--job-status-awaiting-bg);
    }

    .job-row[class~="status-awaiting_customer_decision"] {
      background: var(--job-status-decision-bg);
    }

    .job-row.status-ongoing {
      background: var(--job-status-ongoing-bg);
    }

    /* CNS (completed_not_shipped) — soft-wash pattern (G01, Mitch 2026-05-28).
       The XLSX-canonical blue read too intense as a full-row fill, and its old
       white text fought the dark link/muted children, so they rendered
       dark-on-blue and were unreadable. Now rendered as a soft wash of the
       canonical token at --job-row-tint-strength over the panel (an opaque
       wash, so no zebra/hover bleed-through), with `color: white` dropped so
       text falls back to the normal dark colors — readable, and matching the
       light tints (decision/awaiting/on-hold). Supersedes C04's solid-blue +
       white-text CNS treatment. The badge + kiosk flag keep full-strength blue
       as the punchy identity carrier. */
    .job-row[class~="status-completed_not_shipped"] {
      background: color-mix(in srgb, var(--job-status-completed-not-shipped-bg) var(--job-row-tint-strength), var(--app-surface-panel));
    }

    /* Completed (green) — same soft-wash pattern (G01, Mitch 2026-05-28:
       "extend this principle, the hard colors are too hard"). The vivid XLSX
       green is now a soft wash at the shared --job-row-tint-strength; normal
       dark text reads on it, so the F01 near-black foreground flip (below) is
       retired along with the vivid fill it compensated for. Supersedes the
       C04 vivid-green + F01 near-black-fg treatment. */
    .job-row.status-completed {
      background: color-mix(in srgb, var(--job-status-completed-bg) var(--job-row-tint-strength), var(--app-surface-panel));
    }

    /* A08: parallel completed tint (survives a status-class refactor) + its
       hover guard. Lower specificity than the status tints, so a dragged or
       hovered completed row still resolves to the same soft green wash. */
    .job-row-completed {
      background: color-mix(in srgb, var(--job-status-completed-bg) var(--job-row-tint-strength), var(--app-surface-panel));
    }

    .job-row-completed:hover {
      background: color-mix(in srgb, var(--job-status-completed-bg) var(--job-row-tint-strength), var(--app-surface-panel));
    }
  }

  @layer states {
    /* Row hover affordance. F07a dropped the C04 Step-4 brightness(0.95)
       filter — the 2px brand outline is a cleaner, less muddy hover cue on the
       tinted rows, and the filter created a faint stacking-context seam on the
       inline-edit popovers. Outline only now. */
    .job-row:hover {
      outline: 2px solid var(--app-brand-600);
      outline-offset: -2px;
    }
  }

  @layer badges {
    /* A12: row hover must not dim the priority badge — opacity/filter guard,
       plus the badge keeps its own token color over the inline-edit brand-50
       wash (states sub-layer). The badges sub-layer outranks states, so the
       badge opts out of the wash by LAYER precedence, no `!important`. */
    .job-row:hover .job-priority-badge,
    .job-row:hover .badge[class*="badge-p"] {
      opacity: 1;
      filter: none;
    }

    .job-priority-badge.badge-p1,
    .job-row:hover .job-priority-badge.badge-p1,
    .job-row .job-priority-badge.badge-p1:hover { background: var(--app-danger-accent); }

    .job-priority-badge.badge-p2,
    .job-row:hover .job-priority-badge.badge-p2,
    .job-row .job-priority-badge.badge-p2:hover { background: var(--app-warning-accent); }

    /* J02/J-BLUE (L-1): P3 neutral grey (was --app-info-accent blue) — matches
       the primitives .badge-p3 + kiosk-priority-low so priority != status. */
    .job-priority-badge.badge-p3,
    .job-row:hover .job-priority-badge.badge-p3,
    .job-row .job-priority-badge.badge-p3:hover { background: var(--app-neutral-fg); }

    /* v1.3.5 C02: P4 = completed_not_shipped (white on CNS blue, 5.15:1),
       P5 = completed (near-black on green per F01). The badge ladder mirrors
       the C04 row-tint ladder via the same status-* tokens. */
    .job-priority-badge.badge-p4,
    .job-row:hover .job-priority-badge.badge-p4,
    .job-row .job-priority-badge.badge-p4:hover {
      background: var(--job-status-completed-not-shipped-bg);
      color: white;
    }

    .job-priority-badge.badge-p5,
    .job-row:hover .job-priority-badge.badge-p5,
    .job-row .job-priority-badge.badge-p5:hover {
      background: var(--job-status-completed-bg);
      color: var(--app-text-strong);
    }

    /* v1.9.2 R09: P6 = Invoiced. Breaks out of the C04 status family on
       purpose — see the token comment. White on purple, 7.38:1. */
    .job-priority-badge.badge-p6,
    .job-row:hover .job-priority-badge.badge-p6,
    .job-row .job-priority-badge.badge-p6:hover {
      background: var(--job-priority-invoiced-bg);
      color: white;
    }
  }
}

/* F03: three operationally-distinct "waiting" statuses share the cream awaiting
   field tint (tints sub-layer above) but each gets a unique warm-family left-border
   accent so an admin can tell them apart at a glance. UNLAYERED so the accent
   outranks tables.css `tbody tr { border-left: transparent }` (unlayered).
   `[class~=]` matches whole tokens, so status-awaiting_arrival does NOT also
   match status-awaiting_arrival_and_parts. */
.job-row[class~="status-awaiting_arrival"] {
  border-left: 4px solid var(--job-status-awaiting-arrival-accent);
}

.job-row[class~="status-awaiting_parts"] {
  border-left: 4px solid var(--job-status-awaiting-parts-accent);
}

.job-row[class~="status-awaiting_arrival_and_parts"] {
  border-left: 4px solid var(--job-status-awaiting-both-accent);
}

/* Archived rows: unlayered muted treatment. Post-F06 the neutral bg now wins
   over the (layered) status tint for archived rows — the intended "this row is
   parked" look. Archived rows are hidden from the default list view, so this
   is invisible until an admin filters to Archived. */
.job-row.archived {
  border-left-color: var(--app-neutral-fg);
  background: var(--app-neutral-soft);
  color: var(--app-text-muted);
}

/* A11: inline-edit dropdowns must render above neighboring cells so the
   <select> popover isn't clipped by adjacent priority badges or status pills.
   Uses :has() — Chromium ≥105, Brave/Chromium is the target. */
.job-row td:has(.job-inline-edit-field.is-editing) {
  position: relative;
  z-index: 10;
}

.job-row:has(.job-inline-edit-field.is-editing) {
  position: relative;
  z-index: 5;
}

.job-inline-edit-field.is-editing .job-inline-edit-editor {
  position: relative;
  z-index: 20;
}

.assignee-chip {
  display: inline-block;
  padding: 0.15rem 0.55rem;
  border-radius: 12px;
  font-size: 0.75rem;
  font-weight: 600;
  background: var(--app-brand-50);
  color: var(--app-brand-900);
  border: 1px solid var(--app-brand-100);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: middle;
}

.kiosk-jobs-assignee {
  margin: var(--space-1) 0 0;
  font-size: 0.875rem;
  color: var(--app-brand-700);
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

.job-notes-cell {
  min-width: 0;
}

.job-notes-inline {
  display: grid;
  gap: var(--space-1);
  min-width: 0;
}

.job-notes-text {
  min-width: 0;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 5;

  /* F07a: notes are the smallest tier of the type scale (0.85rem). The clamp
     max-height uses `em` so it tracks this font-size automatically. */
  font-size: 0.85rem;
  max-height: calc(1.35em * 5);
  overflow: hidden;
  white-space: normal;
  overflow-wrap: anywhere;
  color: var(--app-text-strong);
  line-height: 1.35;
}

.job-notes-cell:hover .job-notes-text,
.job-notes-cell:focus-within .job-notes-text {
  -webkit-line-clamp: unset;
  max-height: none;
  overflow: visible;
}

.job-notes-meta {
  white-space: normal;
  overflow-wrap: anywhere;
  font-size: 0.78rem;
  color: var(--app-text-muted);
}

/* Washed strong-tint foreground (G01, 2026-05-28). Supersedes F01's
   completed-only near-black flip with a single rule covering BOTH washed
   terminal tints (completed + CNS). At a high --job-row-tint-strength the fill
   is saturated, so every text child — including the job#/client links (base
   --app-brand-700, otherwise dark-on-saturated) and the muted notes — flips to
   --job-row-tint-fg (+ optional --job-row-tint-halo). text-shadow inherits, so
   the row sets it once; color is set explicitly on the link/muted children that
   carry their own. Unlayered + .job-row descendant specificity beats the
   unlayered base link colors.

   Per-hue text (Mitch 2026-05-28): the default --job-row-tint-fg is WHITE,
   which reads on the blue CNS wash (3.98:1, AA-large). But white on the green
   completed wash is only 2.48:1 (fails — the same white-on-vivid-green problem
   F01 caught). So the green row alone re-points the --job-row-tint-fg *variable* to
   near-black (6:1, crisp); its color/children rules below resolve it via the
   cascade — no separate selector list. This override is unlayered so it beats
   the :root default by specificity (0,2,0 > 0,1,0). */
.job-row.status-completed {
  --job-row-tint-fg: var(--app-text-strong);
}

.job-row.status-completed,
.job-row[class~="status-completed_not_shipped"] {
  color: var(--job-row-tint-fg);
  text-shadow: var(--job-row-tint-halo);
}

.job-row.status-completed :is(.job-number-link, .job-client-link, .job-client-link:hover, .job-client-link:focus, .cell-subtle, .muted, .text-muted-sm, .job-notes-text, .job-notes-meta, .text-muted),
.job-row[class~="status-completed_not_shipped"] :is(.job-number-link, .job-client-link, .job-client-link:hover, .job-client-link:focus, .cell-subtle, .muted, .text-muted-sm, .job-notes-text, .job-notes-meta, .text-muted) {
  color: var(--job-row-tint-fg);
}

/* F02 (C04 gap #2): detail-page status pill. On the list, status reads as a
   tinted row (these --job-status-*-bg tokens). On /admin/jobs/{id} it was
   plain humanize text. This pill reuses the SAME vivid row-tint tokens (not
   the soft primitives.css .badge-status.{enum} palette the list pill uses) so
   the list↔detail color identity is continuous, with F01's near-black/white
   foregrounds. The `status-{enum}` class (vs the bare `{enum}` primitives
   targets) keeps these distinct from the soft pills, so both can coexist.
   `[class~=]` selectors (not `.status-foo`) match the codebase convention for
   underscore-bearing status tokens — same as the row tints above. */
.badge-status[class~="status-on_hold"] {
  background: var(--job-status-on-hold-bg);
  color: var(--app-text-strong);
}

.badge-status[class~="status-awaiting_arrival"],
.badge-status[class~="status-awaiting_parts"],
.badge-status[class~="status-awaiting_arrival_and_parts"] {
  background: var(--job-status-awaiting-bg);
  color: var(--app-text-strong);
}

.badge-status[class~="status-awaiting_customer_decision"] {
  background: var(--job-status-decision-bg);
  color: var(--app-text-strong);
}

.badge-status[class~="status-ongoing"] {
  background: var(--job-status-ongoing-bg);
  color: var(--app-text-strong);
}

.badge-status[class~="status-completed_not_shipped"] {
  background: var(--job-status-completed-not-shipped-bg);
  color: white;
}

.badge-status[class~="status-completed"] {
  background: var(--job-status-completed-bg);
  color: var(--app-text-strong);
}

.job-row.archived .job-number-link,
.job-row.archived .cell-subtle,
.job-row.archived .muted,
.job-row.archived .job-notes-text,
.job-row.archived .job-notes-meta {
  color: var(--app-text-muted);
}

@media (width <= 900px) {
  .jobs-table-wrapper {
    overflow: visible;
  }

  .jobs-table-wrapper .jobs-table,
  .jobs-table-wrapper .jobs-table colgroup,
  .jobs-table-wrapper .jobs-table tbody,
  .jobs-table-wrapper .jobs-table tr,
  .jobs-table-wrapper .jobs-table td {
    display: block;
    width: 100%;
    min-width: 0;
  }

  .jobs-table-wrapper .jobs-table {
    background: transparent;
  }

  .jobs-table-wrapper .jobs-table colgroup,
  .jobs-table-wrapper .jobs-table thead {
    display: none;
  }

  .jobs-table-wrapper .jobs-table tbody {
    display: grid;
    gap: var(--space-3);
  }

  .jobs-table .job-row {
    border: 1px solid var(--app-border-default);
    border-left-width: 4px;
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
  }

  .jobs-table .job-row:hover {
    outline-offset: 0;
  }

  .jobs-table-wrapper .jobs-table .job-row td {
    display: grid;
    grid-template-columns: 6rem minmax(0, 1fr);
    gap: var(--space-3);
    align-items: baseline;
    padding: 0.38rem var(--space-3);
    border: 0;
    font-size: 0.88rem;
  }

  .jobs-table-wrapper .jobs-table .job-row td:first-child {
    padding-top: 0.65rem;
  }

  .jobs-table-wrapper .jobs-table .job-row td:last-child {
    padding-bottom: 0.65rem;
  }

  .jobs-table-wrapper .jobs-table .job-row td::before {
    content: attr(data-label);
    color: var(--app-text-muted);
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    line-height: 1.2;
    text-transform: uppercase;
  }

  .jobs-table-wrapper .jobs-table .job-number-cell {
    font-size: 1rem;
  }

  .jobs-table-wrapper .jobs-table .job-notes-cell {
    grid-template-columns: 1fr;
    gap: var(--space-1);
  }

  .jobs-table-wrapper .jobs-table .job-notes-cell::before {
    display: block;
  }
}

.job-detail-actions {
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-4) var(--space-5);
}

.job-detail-action-group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
}

.job-inline-edit-head {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-2);
  margin-bottom: 0.15rem;
}

.job-inline-edit-head label {
  margin-bottom: 0;
}

.job-inline-edit-editor {
  display: grid;
  gap: var(--space-2);
}

.job-inline-edit-editor[hidden],
.job-inline-edit-status[hidden] {
  display: none;
}

.job-inline-edit-editor input,
.job-inline-edit-editor textarea {
  width: 100%;
  font-size: 0.925rem;
  font-weight: 500;
  color: var(--app-text-strong);
}

/* F05 (C04 gap #1): tint each status <option> with the row color the choice
   produces, previewing the tint at edit time. Reuses the same
   --job-status-*-bg tokens as the row tints so it can never drift, and the
   F01-settled foregrounds (near-black on green, white on CNS blue). Native
   <option> styling is engine-limited; on Brave/Chromium-Linux the popup list
   renders these backgrounds (the closed control does not — accepted). */
.job-inline-edit-editor option[class~="status-on_hold"] {
  background: var(--job-status-on-hold-bg);
}

.job-inline-edit-editor option[class~="status-awaiting_arrival"],
.job-inline-edit-editor option[class~="status-awaiting_parts"],
.job-inline-edit-editor option[class~="status-awaiting_arrival_and_parts"] {
  background: var(--job-status-awaiting-bg);
}

.job-inline-edit-editor option[class~="status-awaiting_customer_decision"] {
  background: var(--job-status-decision-bg);
}

.job-inline-edit-editor option[class~="status-ongoing"] {
  background: var(--job-status-ongoing-bg);
}

.job-inline-edit-editor option[class~="status-completed_not_shipped"] {
  background: var(--job-status-completed-not-shipped-bg);
  color: white;
}

.job-inline-edit-editor option[class~="status-completed"] {
  background: var(--job-status-completed-bg);
  color: var(--app-text-strong);
}

/* Inline-edit affordance: cells are double-click to edit (Enter / F2 from
   keyboard focus). The text cursor signals editability without adding any
   pencil chrome; the hover tint is the row's existing brand-50 hover bg
   scoped to the value node so the affordance is visible without extra
   layout. touch-action: manipulation tells Blink/WebKit to skip the
   double-tap-zoom delay so the dblclick still fires on touch. */
.job-inline-edit-field [data-inline-display] {
  cursor: text;
  touch-action: manipulation;
  border-radius: 4px;
  transition: background-color 120ms ease;
}

/* F06: the inline-edit brand-50 hover wash lives in app.jobs/states (a sub-
   layer BELOW app.jobs/badges) so the priority-badge color pins beat it on the
   badge cell without `!important` — the A12 fix, expressed via layer order
   instead of an `!important` flag. Layers merge by name, so this stays next to
   its sibling affordance rules while taking its precedence from the
   `@layer tints, states, badges;` declaration above. */
@layer app.jobs {
  @layer states {
    .job-inline-edit-field [data-inline-display]:hover,
    .job-row:hover .job-inline-edit-cell [data-inline-display] {
      background: var(--app-brand-50);
    }
  }
}

.job-inline-edit-field [data-inline-display]:focus-visible {
  outline: 2px solid var(--app-brand-600);
  outline-offset: 2px;
}

.job-inline-edit-status {
  color: var(--app-text-muted);
}

.job-inline-edit-status.is-error {
  color: var(--app-danger-strong);
}

:where(.job-inline-edit-field.is-saving) .job-inline-edit-editor input,
:where(.job-inline-edit-field.is-saving) .job-inline-edit-editor textarea {
  background: var(--app-surface-muted);
}

.job-inline-edit-cell {
  min-width: 0;
}

.job-inline-edit-cell [data-inline-display] {
  display: inline-block;
  min-width: 1ch;
}

.job-inline-edit-cell.is-editing [data-inline-display] {
  display: none;
}

.job-inline-edit-cell .job-inline-edit-editor select,
.job-inline-edit-cell .job-inline-edit-editor input,
.job-inline-edit-cell .job-inline-edit-editor textarea {
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
  font-size: 0.85rem;
  padding: 0.2rem 0.35rem;
}

.job-inline-edit-reasons {
  margin: 0;
  padding-left: 1.1rem;
  list-style: disc;
}

.job-cascade-notice {
  display: inline-block;
  margin-top: 0.3rem;
  padding: var(--space-1) var(--space-2);
  border-radius: 8px;
  background: var(--app-brand-50);
  color: var(--app-brand-900);
  font-size: 0.78rem;
}

/* section-head padding now defaults from the .detail-card primitive
   (compositions.css, A4). Only the body delta is kept. */
.job-equipment-card .detail-body {
  padding-top: 0.6rem;
}

.job-updates-section > .section-head,
.job-updates-section > .section-head-split {
  align-items: center;
  margin-bottom: 0.55rem;
}

.job-updates-section .section-actions {
  align-items: center;
}

.job-assignment-grid {
  /* Floors 160/280/150 (were 220/360/190, v1.20.3): at 1024px with the menu
     column open the page has about 810px, and the old floors made the New
     Job form 1114px wide, Create Job off the right edge. */
  grid-template-columns: minmax(160px, 1.05fr) minmax(280px, 2.3fr) minmax(150px, 0.9fr);
  align-items: start;
}

.job-assignment-equipment {
  min-width: 0;
}

.job-assignment-equipment select {
  width: 100%;
}

.job-assignment-number {
  min-width: 0;

  /* R136: year – number – initials (– sub) needs its own line. */
  grid-column: 1 / -1;
}

.job-number-parts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

.job-number-parts select,
.job-number-parts input {
  width: auto;
  font-variant-numeric: tabular-nums;
  text-align: center;
}

#job-number-year {
  min-width: 4.5rem;
}

#job-number-serial {
  width: 6.5rem;
  letter-spacing: 0.05em;
}

#job-number-suffix {
  width: 5rem;
  text-transform: uppercase;
}

#job-number-suffix[readonly] {
  background: var(--app-surface-muted);
  color: var(--app-text);
  cursor: default;
}

#job-number-sub {
  width: 4rem;
}

.job-number-dash {
  font-weight: 600;
}

.job-number-override {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.4rem;
  font-weight: 400;
}

.job-number-override:has(input:checked) {
  padding: 0.15rem 0.5rem;
  border: 1px solid var(--app-warning-border);
  border-radius: 4px;
  background: var(--app-warning-bg);
  color: var(--app-warning-fg);
  font-weight: 600;
}

/* Overridden initials must look different from the rep's own. */
#job-number-suffix:not([readonly]) {
  border-color: var(--app-warning-accent);
  background: var(--app-warning-soft);
}

.job-form-summary-card {
  margin-top: 0.15rem;
}

.job-form-summary-panel {
  margin-top: 0.15rem;
  padding: var(--space-4) 1.1rem;
  background: var(--app-brand-100);
  border-color: var(--app-brand-100);
}

.job-form-summary-panel .section-head {
  margin-bottom: 0.8rem;
}

.job-form-summary-panel .detail-grid {
  gap: var(--space-4) var(--space-5);
}

.job-form-summary-panel .detail-field .value {
  font-weight: 600;
}

.page-form-actions {
  margin-top: -0.1rem;
}

@media (width <= 900px) {
  /* minmax(0, 1fr), not 1fr (v1.20.3, Mitch 2026-10-09: "fix the known
     issues"): a bare 1fr track is at least as wide as its widest field, and a
     select is as wide as its longest option. On an iPhone the client list
     made the whole New Job form 500px wide and clipped every field. */
  .job-assignment-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* The same for the job form's other sections. Here, not in forms.css: that
   file is a kiosk asset and is never edited (TR-099). */
@media (width <= 768px) {
  .stack-page .form-grid,
  .stack-page .form-grid.form-grid-two {
    grid-template-columns: minmax(0, 1fr);
  }

  .stack-page .form-group {
    min-width: 0;
  }
}

.job-transition-banner {
  border-top: 1px solid var(--app-border-default);
  border-bottom: 1px solid var(--app-border-default);
  padding: 0.95rem var(--space-5);
}

.job-transition-banner.is-blocked {
  background: var(--app-warning-soft);
}

.job-transition-panel,
.job-transition-banner {
  border: 1px solid var(--app-border-default);
  border-radius: var(--radius);
  padding: 0.8rem 0.95rem;
  background: var(--app-surface-muted);
}

.job-transition-panel.is-blocked {
  border-color: var(--app-warning-border);
  background: var(--app-warning-soft);
}

.job-transition-panel.is-ready {
  border-color: var(--app-success-border);
  background: var(--app-success-soft);
}

.job-transition-head {
  display: grid;
  gap: var(--space-1);
}

.job-transition-head strong {
  font-size: 0.94rem;
  color: var(--app-text-strong);
}

.job-transition-head span,
.job-transition-copy,
.job-transition-list {
  font-size: 0.84rem;
  color: var(--app-text-muted);
}

.job-transition-copy {
  margin: var(--space-2) 0 0;
}

.job-transition-list {
  max-width: 52rem;
  margin: 0.55rem 0 0;
  padding-left: 1.1rem;
}

.job-transition-list li + li {
  margin-top: var(--space-1);
}

.job-detail-meta {
  margin-right: auto;
  margin-left: 0;
  text-align: left;
}

.job-timeline-section {
  padding-top: 0.1rem;
}

/* .job-section-card removed (UI-consistency pass, 2026-05-28): it duplicated
   the shared .surface/.panel canon byte-for-byte and had no template usage.
   New job sections use the shared .surface/.panel/.detail-card primitives. */

.job-updates-note {
  margin: 0 0 0.95rem;
  color: var(--app-text-muted);
  font-size: 0.94rem;
}

.jobforms-table td {
  padding-top: 0.8rem;
  padding-bottom: 0.8rem;
}

.jobforms-table td:where(:first-child) {
  min-width: 18rem;
}

.job-timeline {
  display: grid;
  gap: var(--space-1);
}

.timeline-item {
  position: relative;
  display: grid;
  grid-template-columns: 2.2rem minmax(0, 1fr);
  gap: var(--space-4);
  align-items: start;
  padding-bottom: 0.95rem;
}

.timeline-item::before {
  content: "";
  position: absolute;
  top: 2.15rem;
  bottom: -0.15rem;
  left: var(--space-4);
  width: 1px;
  background: var(--app-border-default);
}

.timeline-item:last-child::before {
  display: none;
}

.timeline-avatar {
  position: relative;
  z-index: 1;
  width: 2rem;
  height: 2rem;
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--app-neutral-fg);
  background: var(--app-surface-page);
  border: 1px solid var(--app-border-default);
}

.timeline-content {
  min-width: 0;
  padding: 0.95rem var(--space-4) var(--space-4);
  border: 1px solid var(--app-border-default);
  border-radius: var(--radius);
  background: var(--app-surface-panel);
}

.timeline-item-current-report .timeline-content {
  border-color: var(--app-brand-600);
  box-shadow: 0 0 0 1px var(--app-brand-100);
}

.timeline-item:last-child .timeline-content {
  padding-bottom: var(--space-4);
}

.timeline-item-current-actor .timeline-avatar {
  color: var(--app-brand-900);
  background: var(--app-brand-50);
}

.timeline-entry-card {
  box-shadow: 0 1px 0 var(--alpha-black-02);
}

.timeline-change-card {
  background: var(--app-surface-muted);
}

.timeline-item-head {
  display: flex;
  justify-content: space-between;
  gap: var(--space-4);
  align-items: flex-start;
  margin-bottom: 0.45rem;
}

.timeline-item-heading {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}

.timeline-item-actor {
  font-weight: 600;
  color: var(--app-text-strong);
}

.timeline-item-kind {
  display: inline-flex;
  align-items: center;
  width: fit-content;
  border-radius: 999px;
  padding: 0.14rem var(--space-2);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.timeline-item-kind.is-activity {
  background: var(--app-info-bg);
  color: var(--app-info-fg);
}

.timeline-item-kind.is-change {
  background: var(--app-neutral-soft);
  color: var(--app-neutral-fg);
}

.timeline-item-meta {
  color: var(--app-text-muted);
  font-size: 0.82rem;
  white-space: nowrap;
}

.timeline-item-body {
  margin: 0 0 var(--space-3);
}

.timeline-report-state {
  display: inline-flex;
  align-items: center;
  border-radius: 999px;
  padding: 0.14rem var(--space-2);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  color: var(--app-neutral-fg);
  background: var(--app-neutral-soft);
}

.timeline-fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: var(--space-2) var(--space-4);
  margin: 0;
}

.timeline-field dt {
  color: var(--app-text-muted);
  font-size: 0.78rem;
  margin-bottom: 0.2rem;
}

.timeline-field dd {
  margin: 0;
}

.timeline-documents {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: 0.7rem;
}

.timeline-report-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
  margin-top: 0.9rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--app-border-default);
}

.timeline-document {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  border: 1px solid var(--app-border-default);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--app-surface-panel);
  color: inherit;
  text-decoration: none;
  width: min(16rem, 100%);
}

.timeline-document img {
  display: block;
  width: 100%;
  height: 8rem;
  object-fit: cover;
}

.timeline-document-file {
  min-height: 6.25rem;
  padding: 0.8rem;
  gap: var(--space-3);
  justify-content: center;
}

.timeline-document-icon {
  font-size: 1.2rem;
  color: var(--app-text-muted);
}

.timeline-document-name {
  font-size: 0.82rem;
  overflow-wrap: anywhere;
}

.job-timeline-footer {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
  padding-left: 3.05rem;
  margin-top: 0.4rem;
}

.job-timeline-footer[hidden],
.job-timeline-footer [hidden] {
  display: none !important;
}

.job-timeline-sentinel {
  width: 100%;
  height: 1px;
}

.job-photo-field {
  display: grid;
  gap: var(--space-3);
}

.job-photo-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}

.job-photo-field.is-enhanced .job-photo-input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.job-photo-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
}

.job-photo-card {
  margin: 0;
  border: 1px solid var(--app-border-default);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--app-surface-panel);
  width: min(16rem, 100%);
}

.job-photo-preview {
  display: block;
  background: var(--app-surface-page);
}

.job-photo-preview img {
  display: block;
  width: 100%;
  height: 10rem;
  object-fit: cover;
}

.job-photo-card-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 0.65rem var(--space-3);
}

.job-photo-name {
  min-width: 0;
  font-size: 0.82rem;
  color: var(--app-text-muted);
  overflow-wrap: anywhere;
}

.jobform-form-context {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: var(--space-3) var(--space-4);
  margin-bottom: 0.95rem;
  padding: 0.85rem 0.95rem;
  border: 1px solid var(--app-border-default);
  border-radius: var(--radius);
  background: var(--app-surface-panel);
}

.jobform-form-context-item {
  display: grid;
  gap: var(--space-1);
}

.jobform-form-context-item span {
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--app-text-muted);
}

.jobform-form-context-item strong {
  font-size: 0.95rem;
  color: var(--app-text-strong);
}

.job-form-edit-banner strong {
  font-weight: 700;
}

.jobforms-table td strong {
  font-weight: 700;
}

.jobform-form-intro {
  margin-bottom: var(--space-4);
  font-size: 0.95rem;
  line-height: 1.5;
}

.jobform-groups {
  display: grid;
  gap: var(--space-4);
}

.jobform-group-card {
  overflow: hidden;
}

.jobform-readonly-value {
  min-height: 2.75rem;
  display: flex;
  align-items: center;
  padding: 0.72rem 0.85rem;
  border: 1px solid var(--app-border-default);
  border-radius: var(--radius);
  background: var(--app-surface-muted);
  color: var(--app-text-strong);
}

.job-form-edit-banner {
  display: grid;
  gap: var(--space-1);
  padding: 0.8rem 0.9rem;
  margin-bottom: var(--space-4);
  border: 1px solid var(--app-info-border);
  border-radius: var(--radius);
  background: var(--app-info-bg);
}

.job-form-edit-banner span {
  color: var(--app-text-muted);
  font-size: 0.88rem;
}

.job-form-existing-documents {
  margin-top: 1.1rem;
  padding-top: var(--space-4);
  border-top: 1px solid var(--app-border-default);
}

.job-form-existing-documents-head {
  margin-bottom: var(--space-3);
}

.job-form-existing-documents-head h3 {
  margin: 0 0 0.2rem;
}

.job-form-existing-documents-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
}

.job-form-existing-document {
  display: grid;
  gap: var(--space-2);
  width: min(15rem, 100%);
  padding: 0.8rem;
  border: 1px solid var(--app-border-default);
  border-radius: var(--radius);
  background: var(--app-surface-panel);
}

.job-form-existing-document-check {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 0.82rem;
  color: var(--app-text-muted);
}

.job-form-existing-document-preview {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 7rem;
  border-radius: 4px;
  overflow: hidden;
  background: var(--app-surface-page);
}

.job-form-existing-document-preview img {
  display: block;
  width: 100%;
  height: 7rem;
  object-fit: cover;
}

.job-form-existing-document-name {
  font-size: 0.82rem;
  overflow-wrap: anywhere;
}

.jobform-attachments-block {
  display: grid;
  gap: var(--space-3);
}

.jobform-attachments-block h3 {
  margin: 0;
  font-size: 0.95rem;
}

.jobform-detail-shell {
  display: grid;
  gap: var(--space-4);
  width: min(100%, 980px);
}

.jobform-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-4);
}

.jobform-toolbar-copy {
  display: grid;
  gap: var(--space-1);
}

.jobform-toolbar-kicker,
.jobform-toolbar-meta {
  margin: 0;
}

.jobform-toolbar-kicker {
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--app-text-muted);
}

.jobform-toolbar-meta {
  color: var(--app-text-muted);
  font-size: 0.92rem;
}

.jobform-section-body {
  display: grid;
  gap: var(--space-4);
  padding: var(--space-4) 1.1rem;
}

/* Parts arrivals table */

/* section-head padding defaults from the .detail-card primitive (A4). */

.parts-arrivals-table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: var(--space-4);
}

.parts-arrivals-table th,
.parts-arrivals-table td {
  padding: 0.55rem var(--space-3);
  border-bottom: 1px solid var(--app-border-default);
  text-align: left;
  vertical-align: middle;
}

.parts-arrivals-table th {
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--app-text-muted);
  background: var(--app-surface-muted);
}

.parts-arrivals-actions-col {
  width: 3rem;
}

.parts-arrivals-actions {
  text-align: right;
}

.parts-arrivals-row [data-inline-edit-field] {
  cursor: pointer;
}

.parts-arrivals-row [data-inline-edit-field].is-editing {
  background: var(--app-surface-muted);
}

.parts-arrivals-row.parts-arrival-overdue {
  background: var(--app-warning-soft);
}

.parts-arrivals-row.parts-arrival-overdue td {
  border-bottom-color: var(--app-warning-border);
  color: var(--app-warning-fg, var(--app-text-strong));
}

.parts-arrivals-add {
  padding: 0.85rem var(--space-4);
  border: 1px dashed var(--app-border-default);
  border-radius: var(--radius);
  background: var(--app-surface-muted);
}

.parts-arrivals-add-heading {
  margin: 0 0 0.6rem;
  font-size: 0.92rem;
  font-weight: 700;
}

.parts-arrivals-add-grid {
  display: grid;
  grid-template-columns: minmax(8rem, 1fr) minmax(8rem, 1fr) minmax(8rem, 1fr) minmax(10rem, 2fr) auto;
  gap: var(--space-3);
  align-items: end;
}

.parts-arrivals-add-grid label {
  display: grid;
  gap: var(--space-1);
  font-size: 0.82rem;
  color: var(--app-text-muted);
}

.parts-arrivals-add-grid input,
.parts-arrivals-add-grid select {
  width: 100%;
}

.parts-arrivals-status {
  margin: var(--space-2) 0 0;
}

.parts-arrivals-status.is-error {
  color: var(--app-danger-strong);
}

@media (width <= 900px) {
  .parts-arrivals-add-grid {
    grid-template-columns: 1fr;
  }
}

/* Boolean quick-edit checkboxes */

.job-quick-edit-bool {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 0.92rem;
}

.job-quick-edit-bool input[type="checkbox"] {
  width: 1rem;
  height: 1rem;
  margin: 0;
  cursor: pointer;
}

.job-quick-edit-bool .helper-text {
  margin: 0 0 0 0.4rem;
  font-size: 0.78rem;
}

.job-quick-edit-bool .helper-text.is-error {
  color: var(--app-danger-strong);
}

.job-bool-row {
  display: flex;
  flex-wrap: wrap;

  /* STATICCHECK:IGNORE-BEGIN ITP/SR checkbox row uses 1.75rem column gap to match field-label rhythm */
  gap: var(--space-4) 1.75rem;

  /* STATICCHECK:IGNORE-END */
  margin-top: 0.4rem;
}

/* Shipping panel — Bryton's at-a-glance shipping editor.
 * Three blocks (Destination / Carrier & tracking / Pre-ship checks) + a
 * status pill in the section head. The BS-03 warning is collapsed to a
 * single inline "Still needed" line under the head; the boxy banner is gone. */

/* section-head padding defaults from the .detail-card primitive (A4); only
   the vertical-centering delta is kept (the shipping head has an inline
   status pill that should align to the title baseline). */
.job-shipping-panel .section-head {
  align-items: center;
}

.job-shipping-status {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.32rem var(--space-3);
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  border-radius: 999px;
  background: var(--pa-row-hover-bg);
  color: var(--app-text-muted);
}

.job-shipping-status i {
  font-size: 0.9rem;
}

.job-shipping-status-ready {
  background: var(--pa-arrived-bg);
  color: var(--pa-arrived-fg);
}

.job-shipping-status-pending {
  background: var(--pa-pending-bg);
  color: var(--pa-pending-fg);
}

.job-shipping-status-shipped {
  background: var(--pa-info-bg);
  color: var(--pa-info-fg);
}

.job-shipping-needs {
  margin: 0 0 1.1rem;
  padding: 0.6rem 0.9rem;
  border-left: 3px solid var(--pa-pending-border);
  background: var(--pa-pending-bg-soft);
  border-radius: 0 4px 4px 0;
  font-size: 0.9rem;
  color: var(--app-text-strong);
  line-height: 1.5;
}

.job-shipping-needs-label {
  font-weight: 700;
  margin-right: 0.35rem;
}

.job-shipping-needs-list span[data-bs03-flag] {
  font-weight: 600;
  color: var(--pa-pending-fg);
}

.job-shipping-block + .job-shipping-block {
  margin-top: 1.4rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--app-border);
}

.job-shipping-block-title {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 0.85rem;
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--app-text-muted);
}

.job-shipping-block-title i {
  font-size: 1rem;
  color: var(--app-text-strong);
}

.job-shipping-block-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: var(--space-4) var(--space-5);
}

.job-shipping-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}

.job-shipping-field-wide {
  grid-column: 1 / -1;
}

.job-shipping-field label {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--app-text-strong);
}

.job-shipping-field input[type="text"],
.job-shipping-field input[type="url"],
.job-shipping-field textarea {
  width: 100%;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--app-border);
  border-radius: var(--radius);
  font: inherit;
  background: var(--app-surface-panel);
  color: var(--app-text-strong);
  transition: border-color 120ms ease, box-shadow 120ms ease;
}

.job-shipping-field textarea {
  min-height: 7rem;
  resize: vertical;
  font-family: inherit;
  line-height: 1.45;
}

.job-shipping-field input:focus,
.job-shipping-field textarea:focus {
  outline: none;
  border-color: var(--app-primary);
  box-shadow: 0 0 0 3px var(--pa-focus-ring);
}

.job-shipping-field input::placeholder,
.job-shipping-field textarea::placeholder {
  color: var(--app-text-muted);
  opacity: 0.7;
}

.job-shipping-helper {
  margin: 0;
  font-size: 0.8rem;
  color: var(--app-text-muted);
  line-height: 1.4;
}

.job-shipping-source-tag {
  display: inline-flex;
  align-items: center;
  margin-left: var(--space-2);
  padding: 0.1rem var(--space-2);
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--pa-info-fg);
  background: var(--pa-info-bg-soft);
  border-radius: 4px;
}

.job-shipping-track-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: 0.85rem;
  padding: var(--space-2) 0.95rem;
  background: var(--app-primary);
  color: white;
  border-radius: var(--radius);
  text-decoration: none;
  font-weight: 600;
  font-size: 0.88rem;
  transition: filter 120ms ease;
}

.job-shipping-track-link:hover,
.job-shipping-track-link:focus {
  filter: brightness(1.08);
  color: white;
  text-decoration: none;
}

.job-shipping-block-quality .job-bool-row {
  margin: 0;
}

.job-shipping-form-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: 1.4rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--app-border);
}

.job-shipping-meta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  color: var(--app-text-muted);
  font-size: 0.88rem;
}

.job-shipping-meta i {
  font-size: 0.95rem;
}

.job-shipping-form-actions .btn i {
  margin-right: 0.3rem;
}

/* Labor by day + by activity section headers (Gantt content lives in
   labor_gantt.css). Their section-head padding now defaults from the
   .detail-card primitive (compositions.css, A4) — this is the rule the flush
   "Labour by Activity" header was missing. It can no longer be omitted by any
   detail-card section, so the two siblings (and every future one) can't drift
   flush again. No per-section padding rule is needed here. */

@media (width <= 900px) {
  .job-detail-actions {
    flex-direction: column;
    align-items: flex-start;
  }

  .job-detail-meta {
    text-align: left;
    margin-left: 0;
  }
}

@media (width <= 900px) {
  .job-detail-actions {
    flex-direction: column;
    align-items: flex-start;
  }

  .job-detail-meta {
    text-align: left;
    margin-left: 0;
  }

  .timeline-item-head {
    flex-direction: column;
    align-items: flex-start;
  }

  .job-photo-card-footer,
  .jobform-toolbar,
  .timeline-report-actions {
    flex-direction: column;
    align-items: flex-start;
  }

  .timeline-item {
    grid-template-columns: 1.85rem minmax(0, 1fr);
    gap: var(--space-3);
  }

  .timeline-item::before {
    left: 0.9rem;
  }

  .timeline-avatar {
    width: 1.8rem;
    height: 1.8rem;
    font-size: 0.7rem;
  }

  .job-timeline-footer {
    padding-left: 2.55rem;
  }
}

/* ------------------------------------------------------------------------- */

/* Order-mode drag-and-drop reorder. /admin/jobs default landing view.       */

/* ------------------------------------------------------------------------- */

/* Order toggle: lives in the filter-actions row beside Clear/Filter. Inherits
   .btn .btn-outline .btn-sm sizing; only the active state and the leading
   grip icon need overrides. */
.jobs-order-button {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

.jobs-order-button-active {
  background: var(--app-brand-100);
  border-color: var(--app-brand-500);
  color: var(--app-brand-800);
  font-weight: 600;
}

.jobs-order-button-active:hover {
  background: var(--app-brand-200);
}

/* Drag handle column: hidden when not in Order mode (kept in markup so the
   colgroup stays stable across HTMX swaps). */
.jobs-table:not([data-order-mode="true"]) .drag-handle-cell,
.jobs-table:not([data-order-mode="true"]) col.jobs-col-drag {
  display: none;
}

.jobs-table[data-order-mode="true"] .drag-handle-cell {
  padding: var(--space-1) 0.4rem;
  text-align: center;
  vertical-align: middle;
}

.drag-handle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: 4px;
  color: var(--app-text-muted);
  cursor: grab;
  font-size: 1.1rem;
  line-height: 1;
  transition: background-color 120ms ease, color 120ms ease, transform 120ms ease;
  user-select: none;
}

.job-row:hover .drag-handle {
  /* Subtle hint on row hover so the handle is discoverable. */
  color: var(--app-text-strong);
}

/* Specificity bumped (`.job-row .drag-handle:*`) so these direct-handle
   pseudo-class rules come AFTER the `.job-row:hover .drag-handle` rule
   above without tripping no-descending-specificity. The .job-row prefix
   is harmless — the drag handle only lives inside .job-row. */
.job-row .drag-handle:hover {
  /* Distinct from .job-row:hover so the user knows the handle itself
     is targetable, not just the row. */
  background-color: var(--app-brand-50);
  color: var(--app-brand-700);
}

.job-row .drag-handle:active {
  cursor: grabbing;
  transform: scale(0.96);
  background-color: var(--app-brand-100);
  color: var(--app-brand-700);
}

/* SortableJS visual states. F06: in app.jobs/tints (no `!important`). The
   status tints in the same sub-layer out-specify `.jobs-drag-ghost` (0,1,0),
   so a dragged tinted row keeps its tint — exactly the pre-F06 outcome (the
   tint's old `!important` already out-specified the ghost's). Untinted rows
   get the brand-50 ghost wash. */
@layer app.jobs {
  @layer tints {
    .jobs-drag-ghost {
      opacity: 0.35;
      background: var(--app-brand-50);
    }

    .jobs-drag-chosen {
      background: var(--app-brand-50);
      box-shadow: 0 4px 14px -4px var(--pa-elevation-shadow);
    }
  }
}

/* Completed rows: no drag affordance, slightly muted in Order mode. */
.jobs-table[data-order-mode="true"] tr.job-row-completed {
  opacity: 0.7;
}

/* Inline error banner posted by sort.js on a failed drop or move. */
.jobs-order-error {
  margin: var(--space-2) 0;
  padding: var(--space-2) var(--space-3);
  background: var(--app-danger-50);
  border: 1px solid var(--app-danger-300);
  color: var(--app-danger-700);
  border-radius: 4px;
  font-size: 0.85rem;
}

/* B05: Reopen Job affordance on the detail page. Rendered in place of the
   inline status editor when the current status is `completed`. Sized to sit
   alongside the displayed status value rather than dominating the field. */
.job-reopen-wrapper {
  margin-top: 0.4rem;
}

.job-reopen-button {
  /* Secondary action — the action is recoverable (the user can complete the
     job again from the inline editor that reappears after reopen). Don't use
     a destructive-red tone. */
  font-size: 0.85rem;
}

.job-reopen-error {
  margin-top: 0.35rem;
  color: var(--app-danger-700);
}

/* D02: read-only sibling-jobs panel. Renders when the current job
   shares its po_number with one or more other open jobs. Tokens are
   the standard app-surface / app-border pair so the panel reads as a
   navigation aid, not a primary action. */
.sibling-jobs-panel {
  background: var(--app-surface-panel);
  border: 1px solid var(--app-border);
  border-radius: 4px;
  padding: var(--space-4);
}

.sibling-jobs-list {
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.sibling-jobs-row {
  border: 1px solid var(--app-border-soft);
  border-radius: 4px;
}

.sibling-jobs-link {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  text-decoration: none;
  color: inherit;
}

.sibling-jobs-link:hover,
.sibling-jobs-link:focus-visible {
  background: var(--app-surface-subtle);
}

.sibling-jobs-number {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-weight: 600;
}

.sibling-jobs-client {
  color: var(--app-text-muted);
  font-size: 0.9rem;
}

.sibling-jobs-status {
  margin-left: auto;
  font-size: 0.8rem;
}

/* v1.3.6 D05 — OT/rush-approved banner.
 *
 * Renders at the top of /admin/jobs/{id} when jobs.ot_approved=TRUE.
 * Uses the warning palette from tokens.css (matches the soft-yellow
 * accent C04 settled on for high-attention but non-error surfaces).
 * The banner ships with role=status + aria-live=polite so an assistive
 * tech reader announces it on initial page load without interrupting
 * existing flow. */

.job-ot-approved-banner {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  margin: 0 0 var(--space-4);
  padding: var(--space-3) 1.1rem;
  border: 1px solid var(--app-warning-border);
  border-left: 6px solid var(--app-warning-accent);
  border-radius: 8px;
  background: var(--app-warning-bg);
  color: var(--app-warning-fg);
  font-size: 0.95rem;
}

.job-ot-approved-banner strong {
  font-weight: 700;
  font-size: 1.02rem;
  letter-spacing: 0.04em;
}

/* v1.6.2 K02 — Invoiced stamp + form on the Labour-by-Activity section.
 *
 * Stamp panel (.labor-by-activity-invoiced-stamp): one inline row of
 * badge + invoice number + "by <name>" + "on <date>" + a small Clear
 * affordance. Sits below the table inside .detail-body.
 *
 * Form (.labor-by-activity-invoiced-form): the not-yet-invoiced state —
 * checkbox, text input, submit button on one line, with an inline error
 * span that only appears when the K02 handler set ErrorField =
 * "invoice_number".
 *
 * Status badge "Invoiced" reuses the existing .badge-status base and
 * adds a `status-invoiced` modifier rendered via the same [class~="..."]
 * convention F02 established for the other detail-page status pills. */

.badge-status[class~="status-invoiced"] {
  background: var(--app-success-soft);
  color: var(--app-success-fg);
  border: 1px solid var(--app-success-border);
}

.labor-by-activity-invoiced-stamp,
.labor-by-activity-invoiced-form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--app-border-soft, var(--app-border));
  font-size: 0.95rem;
}

.labor-by-activity-invoiced-stamp strong {
  font-weight: 600;
}

.labor-by-activity-invoiced-clear-form {
  margin: 0;
  margin-left: auto;
}

.labor-by-activity-invoiced-checkbox {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

.labor-by-activity-invoiced-number {
  flex: 0 1 240px;
  min-width: 0;
}

.labor-by-activity-invoiced-form .form-error {
  flex-basis: 100%;
  color: var(--app-danger-fg);
  font-size: 0.875rem;
}

/* L02 (v1.6.3): blocked-state hint rendered in place of the form when the
 * job is not yet in the completed bucket. Muted, no border, no button —
 * reads as a hint, not a control. */
.labor-by-activity-invoiced-blocked {
  margin: var(--space-4) 0 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--app-border-soft, var(--app-border));
  color: var(--app-text-muted);
  font-size: 0.9rem;
  font-style: italic;
}

/* L01 (v1.6.3): Hour-type clarity on the Labour-by-Activity table.
 *
 * The four numeric columns (Lead / Additional / Regular / OT) are two
 * orthogonal decompositions of the Hours total: Attribution + Rate
 * Class. The thead axis row spans each pair so the two cuts read at
 * a glance. The identity subtitle ("Hours = Lead + Additional =
 * Regular + OT") names the invariant in plain words.
 *
 * Visual treatment: minimal — small-caps, muted color, soft border
 * under each group header. The K01 row classes are inherited; only
 * the new axis row + Additional column + identity caption need rules.
 */

.labor-by-activity-table .labor-by-activity-axis-row > th {
  /* Soft-wash group header. Lighter weight than the column heads, so
   * the eye reads the column heads as the primary axis and the group
   * heads as the meta-label. */
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--app-text-muted);
  padding: var(--space-1) var(--space-2);
  text-align: center;
  border-bottom: 1px solid var(--app-border-soft, var(--app-border));
}

.labor-by-activity-table .labor-by-activity-axis-spacer {
  /* The cells covering ungrouped columns (Activity / Entries / Hours
   * on the left, Dates on the right) — no label, no border so the
   * axis row reads as two distinct caps. */
  border-bottom: 0;
}

.labor-by-activity-table .labor-by-activity-axis-attribution,
.labor-by-activity-table .labor-by-activity-axis-lead {
  /* Light divider on the right edge separates the two attribution
   * bands without a heavy border line. L01's "attribution" class is
   * kept for backwards-compat in CSS during the L05 transition; the
   * new L05 markup uses "lead" + "additional" band classes. */
  border-right: 1px solid var(--app-border-soft, var(--app-border));
}

.labor-by-activity-table .labor-by-activity-axis-rateclass,
.labor-by-activity-table .labor-by-activity-axis-additional {
  border-right: 1px solid var(--app-border-soft, var(--app-border));
}

/* L05 (v1.6.3): mirror the axis-group divider on body rows. Each row
 * carries the same two-band structure (Lead | Additional) — divider
 * sits after each band's last column (Lead-OT, Additional-OT). */
.labor-by-activity-table tbody td[data-labor-activity-lead-ot],
.labor-by-activity-table tbody td[data-labor-activity-additional-ot],
.labor-by-activity-table tfoot td[data-labor-activity-total-lead-ot],
.labor-by-activity-table tfoot td[data-labor-activity-total-additional-ot] {
  border-right: 1px solid var(--app-border-soft, var(--app-border));
}

/* L05: Job-totals 2×2 summary block — sits above the per-activity
 * table; gives the at-a-glance four-number cross-cut Mitch asked
 * for. Visual treatment matches the per-activity table chrome:
 * neutral, no color-coding (G01). */
.labor-by-activity-summary {
  margin-bottom: var(--space-4);
}

.labor-by-activity-summary-table {
  /* Narrow inner table — the four interior cells answer "of the
   * job's OT, which crew got it?" without scanning activity rows. */
  border-collapse: collapse;
  margin-inline-start: 0;
}

.labor-by-activity-summary-table caption.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.labor-by-activity-summary-table th,
.labor-by-activity-summary-table td {
  padding: var(--space-1) var(--space-3);
  border-bottom: 1px solid var(--app-border-soft, var(--app-border));
  text-align: right;
}

.labor-by-activity-summary-table thead th {
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--app-text-muted);
}

.labor-by-activity-summary-table th[scope="row"] {
  text-align: left;
  font-weight: 600;
  color: var(--app-text, inherit);
}

.labor-by-activity-summary-table .labor-by-activity-summary-total-row th,
.labor-by-activity-summary-table .labor-by-activity-summary-total-row td {
  /* Total row gets a slightly heavier top border so the grand totals
   * read as the column footer for the 2×2. */
  border-top: 1px solid var(--app-border);
}

/* The identity caption under the section title — sits between the
 * heading and the table, names the invariant once. */
[data-labor-activity-identity] strong {
  font-weight: 600;
  color: var(--app-text, inherit);
  white-space: nowrap;
}

@media (width <= 768px) {
  /* Mobile collapse: the axis row reads as decoration on narrow
   * viewports — hide it and let the column heads stand alone. The
   * identity subtitle still names the invariant in plain text. */
  .labor-by-activity-table .labor-by-activity-axis-row {
    display: none;
  }

  .labor-by-activity-table tbody td[data-labor-activity-lead-ot],
  .labor-by-activity-table tbody td[data-labor-activity-additional-ot],
  .labor-by-activity-table tfoot td[data-labor-activity-total-lead-ot],
  .labor-by-activity-table tfoot td[data-labor-activity-total-additional-ot] {
    border-right: 0;
  }
}

/* v1.9.4 R30 — the job-number rename control.
 *
 * Deliberately NOT styled like .job-inline-edit-field. Those read as
 * "click me, I'm editable" because they are; this one is a three-state,
 * confirm-before-commit action (R30 i2) and should read as quieter at rest
 * and more serious once armed. A job number is an identifier other people
 * quote, not an attribute to nudge. */
.job-number-edit {
  display: grid;
  gap: var(--space-2);
}

.job-number-resting[hidden],
.job-number-armed[hidden],
.job-number-confirm[hidden],
.job-number-rep-warning[hidden],
.job-number-status[hidden] {
  display: none;
}

.job-number-resting {
  display: flex;
  gap: var(--space-3);
  align-items: baseline;
  flex-wrap: wrap;
}

.job-number-value {
  font-weight: 600;
  color: var(--app-text-strong);
}

/* A link-weight affordance, not a button: at rest this must not compete with
   the real actions on the folder. It becomes prominent only once armed. */
.job-number-reveal {
  padding: 0;
  border: 0;
  background: none;
  font-size: 0.85rem;
  color: var(--app-brand-600);
  text-decoration: underline;
  cursor: pointer;
}

.job-number-reveal:focus-visible {
  outline: 2px solid var(--app-brand-600);
  outline-offset: 2px;
}

/* While armed or confirming, the field takes the whole grid row.
 *
 * .detail-grid is repeat(auto-fill, minmax(200px, 1fr)), so a panel left inside
 * one cell renders about 250px wide — and the story walk screenshot showed what
 * that costs: the confirm line wrapped a job number mid-identifier ("26-" /
 * "0002-GG") at the exact moment the admin is checking it character by
 * character, and the three buttons broke across two rows leaving Cancel
 * orphaned. Every assertion still passed; only looking at the render caught it.
 *
 * At rest the field is an ordinary cell, so the folder's layout is unchanged
 * for anyone who never opens the control. */
.detail-field:has(.job-number-armed:not([hidden])),
.detail-field:has(.job-number-confirm:not([hidden])) {
  grid-column: 1 / -1;
}

.job-number-armed,
.job-number-confirm {
  display: grid;
  gap: var(--space-2);

  /* Wide enough that a job number and the rep sentence never wrap awkwardly,
     capped so it does not stretch to a 1400px ribbon on a wide screen. */
  max-width: 46rem;
  padding: 0.8rem 0.95rem;
  border: 1px solid var(--app-border-default);
  border-radius: var(--radius);
  background: var(--app-surface-muted);
}

/* Keep an identifier on one line — it is the thing being proof-read. */
.job-number-confirm-line strong,
.job-number-value {
  white-space: nowrap;
}

.job-number-input {
  width: 100%;
  font-size: 0.925rem;
  font-weight: 500;
  color: var(--app-text-strong);
}

.job-number-actions {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.job-number-confirm-line {
  margin: 0;
}

/* The rep-routing warning is the one thing on this control that reports a
   consequence the admin did not ask for — changing the initials moves which
   sales rep receives this job's reports. It gets the warning treatment so it
   cannot be skimmed past on the way to the confirm button. */
.job-number-rep-warning {
  margin: 0;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--app-warning-border);
  border-radius: var(--radius);
  background: var(--app-warning-soft);
  font-size: 0.85rem;
}

.job-number-status {
  color: var(--app-text-muted);
}

.job-number-status.is-error {
  color: var(--app-danger-strong);
}
