/* stylelint-disable length-zero-no-unit -- env(safe-area-inset-*, 0px) needs the
   unit: a bare 0 fallback makes calc(0.65rem + env(...)) invalid. */

/* HESS Field — phone adaptations on top of HESS Core's own stylesheets (R99 / TR-099).
 *
 * Owner, 2026-09-30: "hess core current is the truth. we will leverage all it's
 * assets/ui/ux/looks/classes/etc directly from hess field." So this file holds
 * ONLY what Core's CSS lacks for an installed phone app: safe-area insets, a
 * phone column budget, the switch's few lines. It is loaded LAST and unlayered
 * (index.html), so it wins over shell/primitives/compositions/forms.css by
 * order and over the @layer'd tables.css / pages.css outright.
 *
 * HAZARD (lint-enforced, scripts/staticchecks/43_check-field-classes.sh):
 * never write a generic rule on tr, td, .badge*, .status-* here — it would
 * silently kill the jobs list's status tints from pages.css.
 *
 * TODO(R99d dark-mode-later): [data-theme="dark"] re-values --app-* for Core AND
 * Field; no rules until specced. Light only for now (TR-099: "stub todo enable a
 * new dark mode later for the unified proj ui styling as defined").
 *
 * Sections are owned by lanes during v1.17.0's build; the markers stay. */

/* == shell == (index.html, shell.js, sidebar.js: safe areas, drawer, brand switch) */

/* ---- Safe areas ---------------------------------------------------------
   index.html has viewport-fit=cover, so an installed phone app draws under the
   notch, the rounded corners and the home indicator; Core's CSS (built for a
   desktop browser) has no env(safe-area-inset-*). Padding only — Core's own
   values stay the floor (max()), so on a device with no inset nothing moves.
   Unlayered + loaded last, so this beats shell.css at equal specificity. */
.app-topbar {
  padding-top: calc(0.65rem + env(safe-area-inset-top, 0px));
  padding-left: max(var(--space-5), env(safe-area-inset-left, 0px));
  padding-right: max(var(--space-5), env(safe-area-inset-right, 0px));
  flex-wrap: wrap;
}

.app-sidebar {
  padding-top: env(safe-area-inset-top, 0px);
  padding-bottom: env(safe-area-inset-bottom, 0px);
  padding-left: env(safe-area-inset-left, 0px);
}

.container {
  padding-left: max(var(--space-4), env(safe-area-inset-left, 0px));
  padding-right: max(var(--space-4), env(safe-area-inset-right, 0px));
  padding-bottom: calc(var(--space-5) + env(safe-area-inset-bottom, 0px));
}

@media (width >= 1400px) {
  .container {
    padding-left: max(var(--space-5), env(safe-area-inset-left, 0px));
    padding-right: max(var(--space-5), env(safe-area-inset-right, 0px));
  }
}

@media (width <= 980px) {
  .app-topbar {
    padding-top: calc(var(--space-3) + env(safe-area-inset-top, 0px));
    padding-left: max(var(--space-4), env(safe-area-inset-left, 0px));
    padding-right: max(var(--space-4), env(safe-area-inset-right, 0px));
  }
}

/* Phones: Core's 1rem gutter is generous on a 360px screen. */
@media (width <= 480px) {
  .container {
    padding-left: max(var(--space-3), env(safe-area-inset-left, 0px));
    padding-right: max(var(--space-3), env(safe-area-inset-right, 0px));
  }
}

/* Signed out (login): no sidebar, so Core's 214px first grid column must not
   be reserved, and the form is a narrow centred card. */
.app-shell-guest,
body.sidebar-collapsed .app-shell.app-shell-guest {
  /* v1.19.1 (Mitch, 2026-10-06, desktop): a sidebar collapsed earlier
     (localStorage manifest.sidebar.collapsed) made shell.css's
     `body.sidebar-collapsed .app-shell` 68px column win over this one, and
     the sign-in card was squeezed into 68px. The guest page has no sidebar. */
  grid-template-columns: minmax(0, 1fr);
}

.app-shell-guest .container {
  max-width: 460px;
  margin: 0 auto;
}

/* ---- Top bar: queue line (O17), who (S9), signal ------------------------
   Core's top bar is hamburger + breadcrumbs only. The field app adds what the
   records pin. Core's .alert carries a bottom margin meant for page flow;
   inside a flex bar it must sit flush and let its text wrap at 390px. */
.topbar-right {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-left: auto;
  min-width: 0;
}

.topbar-alert {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  padding: 0.4rem 0.8rem;
  font-size: 0.85rem;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  min-width: 0;
}

.topbar-net {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 0.85rem;
  color: var(--app-warning-fg);
  white-space: nowrap;
}

/* S9 on a phone, where the sidebar that carries the name is closed; on a
   desktop the sidebar footer already shows it. Truncated, never wrapped. */
.topbar-who {
  display: none;
  max-width: 9rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--app-text-default);
}

@media (width <= 980px) {
  .topbar-who {
    display: block;
  }

  .topbar-alert-text {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
}

/* A long display name must not drop under the bar (v1.18.0 polish). The bar
   wraps so the queue alarm can take its own row, but with no alarm in it the
   right side shrinks instead: the name is ellipsised on the first row beside
   the hamburger and breadcrumbs. */
@media (width <= 980px) {
  .topbar-right:not(:has(.topbar-alert)) {
    flex: 1 1 3rem;
    justify-content: flex-end;
  }

  .topbar-right:not(:has(.topbar-alert)) .topbar-who {
    flex: 0 1 auto;
    min-width: 0;
  }
}

/* Signed-out top bar: there is no hamburger or breadcrumb, so it carries the
   name of the app. */
.topbar-brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.topbar-brand-name {
  font-weight: 700;
  color: var(--app-brand-700);
}

/* The real HESS letters on a white tile (v1.16.2 logo). Core has no logo class. */
.brand-mark {
  width: 30px;
  height: 26px;
  object-fit: contain;
  border-radius: var(--radius);
  background: var(--app-surface-panel);
  flex: 0 0 auto;
  display: block;
}

/* ---- Brand slot: the Field/Core switch (R99a; app/surface/render.js) -------
   Core's brand row is one link. The slot holds the CURRENT side (bold, never a
   link), and for an admin the OTHER side as Core's own .sidebar-brand-switch
   (smaller, plain, already styled in shell.css). Not an admin / Core not
   reachable: just the bold current side. */
.sidebar-brand-slot {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  min-width: 0;
  color: var(--app-text-inverse);
}

.sidebar-brand-current {
  font-size: 1.02rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  white-space: nowrap;
  color: inherit;
}

/* Before the first probe the current side is a button (the admin's first tap
   on "Field" is what runs the Core reachability probe) — reset to plain text
   so it reads exactly like the non-tappable version. */
.sidebar-brand-arm {
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.sidebar-brand-arm:focus-visible {
  outline: 2px solid var(--alpha-white-50);
  outline-offset: 2px;
}

/* Core collapses its brand text on a collapsed desktop rail; the slot follows.
   (Core drops "collapsed" on phones, where the drawer is always full width.) */
body.sidebar-collapsed .sidebar-brand-slot {
  display: none;
}

@media (width <= 980px) {
  body.sidebar-collapsed .sidebar-brand-slot {
    display: flex;
  }

  .sidebar-brand-arm {
    min-height: 44px;
  }
}

/* Sidebar footer: .sidebar-user-account lives in Core's shell.css since
   v1.20.0 (HESS Core's menu name opens My Account too). */

/* ---- Sign-out hold (S1 / ADR-001) ---------------------------------------- */
.signout-confirm {
  border-left: 4px solid var(--app-warning-accent);
  margin-bottom: var(--space-4);
}

.signout-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}

.signout-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-3);
}

/* ---- ui/parts.js pieces screens still use ---------------------------------
   Status dot, rebuild-line track, overview mix bar. They lived in app.css
   (retired) on its own palette; these read Core's tokens. Screens lanes may
   replace them with Core's own markup and delete the rule. Nothing here is
   a generic rule on table rows or cells, badges or status classes. */
.dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  display: inline-block;
  flex: none;
  background: var(--app-neutral-border);
}

.dot.s-ongoing { background: var(--app-success-accent); }
.dot.s-parts { background: var(--job-status-awaiting-parts-accent); }
.dot.s-customer { background: var(--job-status-decision-fg); }
.dot.s-arrival { background: var(--job-status-awaiting-arrival-accent); }
.dot.s-hold { background: var(--app-text-muted); }
.dot.s-ship { background: var(--job-status-completed-not-shipped-bg); }
.dot.s-done { background: var(--job-status-completed-bg); }
.dot.s-off { background: var(--app-border-strong); }

.track-wrap {
  display: block;
}

.track {
  display: flex;
  gap: 3px;
  width: 176px;
  max-width: 100%;
}

.track i {
  flex: 1;
  height: 6px;
  border-radius: 2px;
  background: var(--app-border-default);
}

.track i.done { background: var(--app-brand-300); }
.track i.now { background: var(--app-brand-600); }
.track.complete i { background: var(--app-success-accent); }

.track-label {
  display: block;
  font-size: 0.85rem;
  color: var(--app-text-muted);
  margin-top: 6px;
  white-space: nowrap;
}

.track-label b {
  font-weight: 600;
  color: var(--app-text-default);
}

.no-track {
  font-size: 0.85rem;
  color: var(--app-text-muted);
}

.mix-bar {
  height: 12px;
  border-radius: 6px;
  overflow: hidden;
  background: var(--app-neutral-bg);
}

.mix-svg {
  display: block;
  width: 100%;
  height: 100%;
}

.mix-svg .seg {
  stroke: var(--app-surface-panel);
  stroke-width: 2px;
  vector-effect: non-scaling-stroke;
}

.mix-svg .seg.s-ongoing { fill: var(--app-success-accent); }
.mix-svg .seg.s-parts { fill: var(--job-status-awaiting-parts-accent); }
.mix-svg .seg.s-customer { fill: var(--job-status-decision-fg); }
.mix-svg .seg.s-arrival { fill: var(--job-status-awaiting-arrival-accent); }
.mix-svg .seg.s-hold { fill: var(--app-text-muted); }
.mix-svg .seg.s-ship { fill: var(--job-status-completed-not-shipped-bg); }
.mix-svg .seg.s-done { fill: var(--job-status-completed-bg); }

/* == /shell == */

/* == rep == (jobs list, job detail page, new job, customers) */

/* Rep section — every rule says why Core's CSS did not already cover it. No
   colours are invented: everything reads Core's tokens. No generic rule on tr,
   td, .badge* or .status-* (it would kill the jobs status tints); phone-card
   cells are hidden through their `c-*` class, at a specificity that beats
   pages.css's `.jobs-table-wrapper .jobs-table .job-row td` without naming td. */

/* Visually hidden text (a header with no words, e.g. the row-action column). */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Core has no status-tab control (its list filters through .filter-bar
   selects). Reps pick a status, or My/All jobs, with one tap, so the tabs are
   Core's own .btn / .btn-sm / .btn-outline / .btn-primary in a wrapping row. */
.tab-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.tab-row .btn { white-space: nowrap; }

.tab-count {
  margin-left: 0.35rem;
  opacity: 0.8;
  font-variant-numeric: tabular-nums;
}
.filter-bar .filter-field-wide { flex: 1 1 260px; }
.filter-bar .filter-field-wide input { width: 100%; }

/* A sortable column head is a button; Core's sortable heads are links in its
   own table script, which is not mounted here. It must read as the header. */
.th-sort {
  padding: 0;
  background: transparent;
  border: 0;
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-align: left;
  cursor: pointer;
}

/* Core draws an empty list as a table row; the board hides its table when
   nothing matches and says so in one line. */
.empty-state {
  margin: var(--space-4) 0;
  color: var(--app-text-muted);
}

/* Rows open the job on tap (Core opens from the job number link only; a
   fingertip needs the whole row). */
.jobs-table .job-row { cursor: pointer; }

/* Equipment folded under the client name (phone cards and the tablet range);
   hidden where the Equipment column is on screen. */
.phone-only { display: none; }

/* The equipment line on a phone card carries its own label, like every other
   row of the card (the cells get theirs from data-label via pages.css). */
.phone-only .phone-only-label { display: none; }

/* Core's column widths (pages.css) are percentages cut for its 11-column
   table. The technician's short list carries Job #, Customer, Unit, Status and
   an action, so it is re-cut here for that. */
.jobs-table-compact .c-number { width: 8.5rem; }
.jobs-table-compact .c-client { width: 30%; }
.jobs-table-compact .c-equipment { width: auto; }
.jobs-table-compact .c-status { width: 11rem; }

.jobs-table-compact .c-go {
  width: 7.5rem;
  text-align: right;
}

/* Desktop table (above the card breakpoint): the job number is nowrap, so a
   column narrower than the number ("26-0541-SH" ~90px) painted it over the
   client name. Give the cell right padding and the column a floor wide enough
   for the longest number plus that gap. The 901-1250px range overrides the
   col width below (7.4rem) with a value that still clears the number. */
@media (width > 900px) {
  .jobs-table .job-number-cell { padding-right: var(--space-4); }
  .jobs-table col.c-number { width: 8.5rem; }
}

/* v1.17.0 Customers: Core's client table (Name · Short code · Status) in the
   compact jobs-table markup, so a phone gets the same cards. */
.customers-table .c-client { width: auto; }
.customers-table .c-code { width: 10rem; }

/* Phone column budget (<=4 fields per job). From 900px down Core's pages.css
   turns the jobs table into cards (one labelled line per cell). Core shows
   every cell; on a phone a rep scans four: priority, job #, client (unit
   underneath) and status. The rest is one tap away on the job page. */
@media (width <= 900px) {
  .jobs-table-wrapper .jobs-table .job-row .c-equipment,
  .jobs-table-wrapper .jobs-table .job-row .c-scope,
  .jobs-table-wrapper .jobs-table .job-row .c-service,
  .jobs-table-wrapper .jobs-table .job-row .c-rep,
  .jobs-table-wrapper .jobs-table .job-row .c-stage,
  .jobs-table-wrapper .jobs-table .job-row .c-go { display: none; }

  .phone-only { display: block; }

  /* In card mode the priority badge was a full-width bar (grid item
     stretch); a chip the size of its digit reads as a badge again. */
  .jobs-table .job-priority-cell .job-priority-badge { justify-self: start; }
  .filter-bar { padding: var(--space-3); }
}

/* Tablet / small laptop (901-1250px): the sidebar is docked so the table gets
   ~800px, and Core's nine percentage columns collapse into each other (job
   number painted over the client). Dropping scope and service type (both on
   the job page) and folding equipment under the client keeps every remaining
   cell legible. Not needed above 1250px. */
@media (900px < width <= 1250px) {
  .jobs-table .c-scope,
  .jobs-table .c-service,
  .jobs-table .c-equipment { display: none; }
  .phone-only { display: block; }

  /* Core's 8% number column is ~65px here; a job number needs ~7rem. The
     fixed-layout table needs explicit widths or the last column is crushed. */
  .jobs-table col.c-priority { width: 2.6rem; }
  .jobs-table col.c-number { width: 8.5rem; }
  .jobs-table col.c-client { width: 30%; }
  .jobs-table col.c-rep { width: 7rem; }

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

  .jobs-table col.c-stage {
    width: auto;
    min-width: 0;
  }
}

/* Phone cards: the equipment line shows its own label (after the tablet
   block so `.phone-only` above does not out-rank it). */
@media (width <= 900px) {
  .c-client .phone-only {
    display: grid;
    grid-template-columns: 6rem minmax(0, 1fr);
    gap: var(--space-3);
    align-items: baseline;
    grid-column: 1 / -1;
  }

  .c-client .phone-only .phone-only-label {
    display: block;
    color: color-mix(in srgb, var(--app-text-muted) 88%, var(--app-text-strong));
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    line-height: 1.2;
    text-transform: uppercase;
  }
}

/* Customers / New job: the pick list under a search box. Core has no
   autocomplete list; this is a plain bordered list in Core's panel tokens. */
.pick-list {
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
  border: 1px solid var(--app-border-default);
  border-radius: var(--radius);
  background: var(--app-surface-panel);
  overflow: hidden;
}
.pick-list li + li { border-top: 1px solid var(--app-border-default); }

.pick-list button,
.pick-list span {
  display: block;
  width: 100%;
  padding: 0.6rem 0.8rem;
  text-align: left;
  background: transparent;
  border: 0;
  font: inherit;
  color: var(--app-text-strong);
}
.pick-list button { cursor: pointer; }

.pick-list button:focus-visible { background: var(--app-surface-subtle); }

/* R126: a tap on an iPhone leaves :hover stuck on; hover tints only where
   there is a real pointer. */
@media (hover: hover) {
  .pick-list button:hover { background: var(--app-surface-subtle); }
}

.picked-client {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-2);
  padding: 0.55rem 0.8rem;
  border: 1px solid var(--app-border-default);
  border-radius: var(--radius);
  background: var(--app-surface-subtle);
}

/* Job detail cards sit in one column; Core's page-shell is a grid that only
   exists for admin-only cards this screen does not have. */
.rep-detail {
  display: grid;
  gap: var(--space-4);
}
.rep-detail .detail-body > .section-head { margin-bottom: var(--space-3); }
.detail-body .detail-field .value.scope-text { white-space: pre-wrap; }
.detail-field .value p { margin: 0 0 var(--space-1); }

/* A collapsible chart card: its header is one full-width button (aria-expanded),
   title and summary on the left, +/- on the right. Core's detail cards are
   static. */
.card-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  width: 100%;
  padding: 0;
  background: transparent;
  border: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.card-toggle-text {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.card-toggle-summary {
  font-size: 0.85rem;
  overflow-wrap: anywhere;
}

.card-toggle-mark {
  font-size: 1.3rem;
  line-height: 1;
  color: var(--app-text-muted);
}
.card-toggle[aria-expanded="true"] { margin-bottom: var(--space-3); }

/* Rebuild-line track (11 steps). Core has no stepper; this is a row of cells
   in Core's brand/neutral tokens. done = filled, now = outlined with a bold
   label, later = empty; a finished job is all steps done, in neutral. */
.stage-track {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(11, minmax(0, 1fr));
  gap: 4px;
}

.stage-track .stage-step {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}

.stage-track .bar {
  height: 10px;
  border-radius: 999px;
  background: var(--app-border-default);
}
.stage-track .stage-step.done .bar { background: var(--app-brand-700); }

.stage-track .stage-step.now .bar {
  background: var(--app-brand-50);
  outline: 2px solid var(--app-brand-700);
  outline-offset: -2px;
}
.stage-track.complete .stage-step.done .bar { background: var(--app-border-strong); }

.stage-track .name {
  font-size: 0.72rem;
  line-height: 1.2;
  color: var(--app-text-muted);
  overflow-wrap: anywhere;
}

.stage-track .stage-step.now .name {
  color: var(--app-text-strong);
  font-weight: 700;
}

.line-summary {
  margin: var(--space-3) 0 0;
  font-size: 0.9rem;
}

@media (width <= 720px) {
  .stage-track .name { display: none; }
}

/* == /rep == */

/* == tech == (clock, picker, confirm, clock-out, note, attention, login, account) */

/* Every rule says why Core's CSS did not cover it (from the W0 tech mock-up,
   core-design/tech/field.css, plus the sign-in look). */

/* Shift card: Core has no "big number" primitive; the elapsed time is the one
   thing a tech reads from arm's length. */
.shift-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-bottom: var(--space-2);
}

.shift-word {
  font-weight: 600;
  color: var(--app-text-strong);
}

.shift-elapsed {
  font-size: 2.6rem;
  font-weight: 700;
  line-height: 1.1;
  color: var(--app-brand-700);
  font-variant-numeric: tabular-nums;
}

.shift-elapsed small {
  font-size: 1rem;
  font-weight: 500;
  color: var(--app-text-muted);
  margin: 0 var(--space-2) 0 2px;
}

.shift-since {
  margin: 0 0 var(--space-4);
  color: var(--app-text-muted);
  font-size: 0.9rem;
}

.shift-title {
  margin: 0 0 var(--space-1);
  font-size: 1.3rem;
  font-weight: 600;
  color: var(--app-text-strong);
}

.shift-sub {
  margin: 0 0 var(--space-4);
  color: var(--app-text-muted);
}
.shift-queued { border-left: 4px solid var(--app-warning-accent); }

/* Big actions: Core's .btn is 44px min; a tech in gloves needs 56px. Sizes
   only, colours stay Core's. */
.btn-lg {
  min-height: 56px;
  font-size: 1rem;
  padding-inline: var(--space-5);
}

/* minmax(0, 1fr), not the implicit auto column: an auto column grows to its
   widest child's min-content, and two nowrap .btn-lg side by side ran the
   whole stack ~17-33px past the card at 320px (walk 05, 2026-10-09). The
   pair under "Switch job" shrinks its side padding and may wrap a word
   rather than overflow. */
.clock-actions {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-3);
  margin-top: var(--space-4);
}

.clock-actions .split {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
}

.clock-actions .split .btn {
  min-width: 0;
  padding-inline: var(--space-3);
  white-space: normal;
}

/* Core has solid .btn-danger only; a quiet red outline for "Clock out" beside
   the primary action keeps the destructive button from shouting. */
.btn-danger-outline {
  background: transparent;
  border-color: var(--app-danger-accent);
  color: var(--app-danger-fg);
}

.btn-ghost {
  background: transparent;
  color: var(--app-text-muted);
  border-color: transparent;
}

/* Banners with a leading icon (queued punch, needs attention): Core's .alert
   is block text only. */
.alert-with-icon {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
}

.alert-text {
  display: grid;
  gap: 2px;
  min-width: 0;
}

/* Two-column Clock on wide screens (shift left, recent jobs right). Core's
   .page-grid is for form/detail pairs, not this. */
.tech-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
  align-items: start;
}

@media (width >= 1100px) {
  .tech-grid {
    grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  }
}

.tech-col {
  display: grid;
  gap: var(--space-4);
  min-width: 0;
}

/* Recent jobs: Core's rows link only the number; here the job-number cell is a
   real button (keyboard + screen reader) and the whole row taps too. */

.row-open {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  min-height: 44px;
  font: inherit;
  font-weight: 700;
  color: var(--app-brand-700);
  text-align: left;
  cursor: pointer;
}

/* pages.css sizes .jobs-col-* for Core's 11-column table; on this 4-column
   table those leave the text columns too narrow (the job number overprints the
   customer). Same classes, widths re-cut. */
.tech-table .jobs-col-number { width: 8.5rem; }
.tech-table .jobs-col-client { width: 30%; }
.tech-table .jobs-col-equipment { width: auto; }

.tech-table .jobs-col-status {
  width: 8rem;
  min-width: 0;
}

/* Confirm / clock-out / note / picker cards. */
.job-summary {
  display: grid;
  gap: 2px;
  padding: var(--space-3) var(--space-4);
  background: var(--app-surface-subtle);
  border: 1px solid var(--app-border-default);
  border-radius: var(--radius);
  margin-bottom: var(--space-4);
}

.job-summary .num {
  font-weight: 700;
  color: var(--app-brand-700);
  font-size: 1.1rem;
}

.form-card.narrow {
  max-width: 40rem;
}

.form-card textarea {
  width: 100%;
}

.confirm-actions {
  display: grid;
  gap: var(--space-3);
  margin-top: var(--space-5);
}

.muted {
  color: var(--app-text-muted);
  font-size: 0.85rem;
}

/* Core's form inputs are 0.9rem (14.4px): iOS zooms the page on focus below
   16px, and these are the fields a tech types into on a phone. */
.form-card .form-group input,
.form-card .form-group textarea {
  font-size: 1rem;
}

.form-card .form-group input {
  min-height: 44px;
}

/* R126: a finger is about 44px (Apple's minimum). The phone menu button, the
   drawer's links, name, Sign out and collapse arrow, and the small buttons
   (the board's tabs, banner buttons) were 28–40px. Phone widths only. */
@media (width <= 980px) {
  .topbar-sidebar-toggle,
  .app-sidebar .sidebar-toggle-sidebar,
  .sidebar-user-logout {
    min-width: 44px;
    min-height: 44px;
  }

  .sidebar-link,
  .sidebar-user-account,
  .app-body .btn-sm,
  .app-body select {
    min-height: 44px;
  }

  /* The parent breadcrumb is the phone's "back" (R75): a 44px-tall target. */
  .breadcrumb-item a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}

/* R126: the Home Screen card (sign-in, Account) and the landing banner. */
.install-tip {
  display: grid;
  gap: var(--space-2);
  margin: 0 0 var(--space-4);
  padding: var(--space-4);
  border: 1px solid var(--app-border);
  border-radius: var(--radius);
  background: var(--app-surface-panel);
}

.detail-card .install-tip {
  margin: 0;
  border: 0;
  padding: 0;
}

.install-tip-title {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 1.3;
}

.install-tip-why {
  margin: 0;
  color: var(--app-text-muted);
}

.install-steps {
  margin: 0;
  padding-left: 1.25rem;
  display: grid;
  gap: var(--space-1);
}

.install-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

.install-banner-text {
  margin: 0;
  flex: 1 1 14rem;
}

.install-banner-actions {
  display: flex;
  gap: var(--space-2);
}

/* Toggle rows (Night shift): Core's .form-check is a bare checkbox; a two-line
   label with the checkbox at the right is the kiosk pattern. */
.toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) 0;
}

.toggle-row .t {
  display: block;
  font-weight: 600;
}

.toggle-row .sub {
  display: block;
  color: var(--app-text-muted);
  font-size: 0.82rem;
}

.form-card .toggle-row input {
  width: 1.4rem;
  height: 1.4rem;
}

/* Needs-attention rows (whole-row buttons) and account rows (key/value lines
   inside a .detail-card). */
.attn-row {
  display: grid;
  gap: var(--space-2);
  justify-items: start;
  width: 100%;
  padding: var(--space-4) var(--space-5);
  text-align: left;
  font: inherit;
  background: none;
  border: 0;
  border-top: 1px solid var(--app-border-default);
  color: inherit;
  cursor: pointer;
}
.attn-row:first-child { border-top: 0; }

.attn-row h3 {
  margin: 0;
  font-size: 1rem;
}
/* stylelint-disable-next-line no-descending-specificity -- never the same element as .detail-field .value p */
.attn-row p { margin: 0; }

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

/* stylelint-disable-next-line no-descending-specificity -- never the same element as .pick-list li + li */
.row-list li {
  display: flex;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-5);
  border-top: 1px solid var(--app-border-default);
  align-items: center;
}
.row-list li:first-child { border-top: 0; }

.row-list .what {
  display: grid;
  gap: 2px;
  min-width: 0;
}
.row-list .what b { font-weight: 600; }

.row-list .what span {
  color: var(--app-text-muted);
  font-size: 0.85rem;
}

.avatar-lg {
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  background: var(--app-brand-700);
  color: var(--app-text-inverse);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
}

.account-head {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-5);
}

.account-head h2 {
  margin: 0;
  font-size: 1.15rem;
}

/* Sign-in: Core's login look (static/auth/login.css is a page stylesheet the
   field origin does not mount): a centred card with the brand as a kicker pill.
   R126: a column — the Home Screen card stacks ABOVE the form (a row put
   them side by side and squeezed the form to one word a line). */
.login-stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: var(--space-5) 0;
}

.login-stage > .install-tip {
  width: 100%;
  max-width: 380px;
}

.login-card {
  width: 100%;
  max-width: 380px;
  display: grid;

  /* minmax(0, ...): a bare auto column grows to its widest child, and at 320px
     the sign-in (lead text, password row) ran 30px past the card and was cut. */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
  padding: 2rem;
  box-shadow: 0 18px 40px var(--alpha-black-12-card);
}

.login-card .login-title {
  text-align: center;
  font-size: 1.2rem;
  color: var(--app-brand-700);
  margin: 0;
}

.login-kicker {
  justify-self: center;
  padding: 0.35rem 0.7rem;
  border-radius: 999px;
  background: var(--alpha-brand-10);
  color: var(--app-brand-700);
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.login-card .alert { margin-bottom: 0; }

/* v1.17.3: one line saying which password, and Show beside it. */
.login-card .login-lead {
  margin: 0;
  text-align: center;
  font-size: 0.9rem;
}

/* v1.18.0: the one way back in, under the Sign in button. */
.login-card .login-help {
  margin: 0;
  text-align: center;
  font-size: 0.85rem;
}

.password-row {
  display: flex;
  gap: var(--space-2, 0.5rem);
  align-items: stretch;
}

.login-card .password-row input {
  flex: 1;
  min-width: 0;
}

.password-row .password-toggle {
  flex: none;
  min-width: 4.5rem;
}

/* == /tech == */

/* == back arrow (R75, 2026-09-24: "can be small back arrow") ==
   The header's parent link carries a small arrow glyph before the destination's
   name. Small, in the link's own colour, vertically centred with the label. */
.breadcrumb-item a[data-back] {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
}

.breadcrumb-item a[data-back] i {
  font-size: 0.9em;
  line-height: 1;
}

/* ---- v1.21 impersonation: the red bar ----------------------------------
   Mitch, 2026-10-09: "can we add 'impersonate' with truthful audit
   logging". While a tab acts as someone, a red bar sits above everything,
   on every screen, and cannot be dismissed: "You are Mitch, acting as Ken ·
   29 min left · Stop". It lives OUTSIDE #app (main.js: rebuilt once, not on
   every render), so the body becomes a column and the app shell takes the
   height that is left, instead of Core's full 100dvh pushing the page past
   the screen. Fits a 320px phone: the sentence wraps, Stop never shrinks. */
body[data-impersonating] {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  overflow: hidden;
}

body[data-impersonating] #app {
  flex: 1 1 auto;
  min-height: 0;
}

body[data-impersonating] .app-shell {
  height: 100%;
  min-height: 0;
}

.impersonation-bar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--space-2);

  /* Under a notch (index.html sets viewport-fit=cover) the safe areas win. */
  padding:
    max(var(--space-2), env(safe-area-inset-top, 0px))
    max(var(--space-3), env(safe-area-inset-right, 0px))
    var(--space-2)
    max(var(--space-3), env(safe-area-inset-left, 0px));
  background: var(--app-danger-strong);
  color: var(--color-white);
  z-index: 40;
}

.impersonation-bar-text {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  font-weight: 600;
  line-height: 1.3;
  overflow-wrap: anywhere;
}

.impersonation-bar-stop {
  flex: 0 0 auto;
  min-height: 44px;
  min-width: 4rem;
  background: var(--color-white);
  color: var(--app-danger-strong);
  border: 1px solid var(--color-white);
  font-weight: 700;
}

@media (hover: hover) {
  .impersonation-bar-stop:hover {
    background: var(--app-danger-50);
    color: var(--app-danger-strong);
  }
}

.impersonation-bar-stop:focus-visible {
  background: var(--app-danger-50);
  color: var(--app-danger-strong);
  outline: 3px solid var(--color-white);
  outline-offset: 2px;
}

/* R126 (v1.21.1): EVERY place a person types or picks, not just the form
   cards — the jobs and board search, the board's sort, the customers filter,
   the job's priority and part type, the new-job selects were 14–14.4px, and
   an iPhone zooms the page on focus below 16px and stays zoomed. LAST in the
   file on purpose: it is the floor under every earlier input rule. */
.app-body input:not([type="checkbox"], [type="radio"], [type="range"], [type="file"]),
.app-body select,
/* stylelint-disable-next-line no-descending-specificity -- a floor, not an override: 16px wins over nothing smaller */
.app-body textarea {
  font-size: max(1rem, 16px);
}

/* R133: stable feedback height; the sentence may wrap at phone zoom. */
.job-number-feedback {
  min-height: 1.5em;
  margin-block: 0.25rem 0;
}

/* R136: [year] – [number] – INITIALS on one line, phone first. */
.job-number-parts {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 0.5rem;
}

#f-job-number-year,
#f-job-number {
  min-height: 44px;
}

#f-job-number-year {
  flex: 0 0 5.5rem;
  width: 5.5rem;
}

#f-job-number {
  flex: 0 1 7.5rem;
  min-width: 5.5rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.05em;
}

.job-number-dash,
.job-number-suffix {
  font-weight: 600;
  white-space: nowrap;
}

.job-number-suggestions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.375rem;
  margin-top: 0.25rem;
}

.job-number-chip {
  min-height: 44px;
  min-width: 44px;
  font-variant-numeric: tabular-nums;
}
