/* ui2.css — the 11-component STYLES (ai/UI2_COMPONENT_SPEC.md). Styles only: every
 * token consumed here is declared in ui2.tokens.css, including the dark-mode swap
 * (.ui2[data-theme="dark"] lives there, not here).
 * Fonts are self-hosted (apps/fonts/, cached by sw.js) — no CDN. */

@font-face { font-family: 'Archivo'; font-weight: 600; font-display: swap; src: url('/fonts/archivo-latin-600-normal.woff2') format('woff2'); }
@font-face { font-family: 'Archivo'; font-weight: 700; font-display: swap; src: url('/fonts/archivo-latin-700-normal.woff2') format('woff2'); }
@font-face { font-family: 'Archivo'; font-weight: 800; font-display: swap; src: url('/fonts/archivo-latin-800-normal.woff2') format('woff2'); }
@font-face { font-family: 'Inter2'; font-weight: 400; font-display: swap; src: url('/fonts/inter-latin-400-normal.woff2') format('woff2'); }
@font-face { font-family: 'Inter2'; font-weight: 500; font-display: swap; src: url('/fonts/inter-latin-500-normal.woff2') format('woff2'); }
@font-face { font-family: 'Inter2'; font-weight: 600; font-display: swap; src: url('/fonts/inter-latin-600-normal.woff2') format('woff2'); }
@font-face { font-family: 'PlexMono'; font-weight: 500; font-display: swap; src: url('/fonts/ibm-plex-mono-latin-500-normal.woff2') format('woff2'); }
@font-face { font-family: 'PlexMono'; font-weight: 600; font-display: swap; src: url('/fonts/ibm-plex-mono-latin-600-normal.woff2') format('woff2'); }

/* Tokens are NOT declared here. They live in ui2.tokens.css, copied from the
 * DEC-023 authority at ai/design/ui2.tokens.css.
 *
 * Why this is governance, not tidying (TASK-053(a)): this file used to re-declare
 * 40 tokens — 25 in .ui2 and 15 more in a .ui2[data-theme="dark"] block.
 * ui2.tokens.css scopes `.ui2, .ui2-sheet`; this file scoped `.ui2` alone. Equal
 * specificity, and index.html loads this file SECOND (lines 19-20) — so for those
 * 40 tokens ui2.css won the cascade and the app never read the authority at all.
 * DEC-023 declares ai/design/ui2.tokens.css binding; for those 40 it was not.
 * Deleting them is what makes DEC-023 actually bind.
 *
 * token-parity.test.js now fails if any --u2-* declaration reappears here. */
.ui2 {
  font-family: var(--u2-body); color: var(--u2-ink); background: var(--u2-paper);
  min-height: 100%;
}
@media (prefers-reduced-motion: reduce) { .ui2 * { animation: none !important; transition: none !important; } }

/* TopBar — dispatch header band */
.ui2 .u2-topbar { background: var(--u2-indigo); color: var(--u2-on-indigo); padding: 14px 16px 12px; border-bottom: 3px solid var(--u2-now); }
/* iOS safe area (TASK-070). index.html carries viewport-fit=cover and
   apple-mobile-web-app-status-bar-style=black-translucent, so the status bar is drawn
   OVER the page: with a plain 14px top the salesman's name rendered under the clock
   and the ON FIELD pill under the signal/battery cluster (owner evidence, 22 Aug).
   26px, not 14px, because #sync-dot occupies the strip immediately below the inset —
   that is the third collision in the same screenshot. constant() first, env() second:
   iOS 11.0-11.2 only understood constant(), and the later valid rule wins elsewhere. */
.ui2 .u2-topbar { padding-top: calc(26px + constant(safe-area-inset-top)); }
.ui2 .u2-topbar { padding-top: calc(26px + env(safe-area-inset-top, 0px)); }
/* The second, independent bug: at ~390px the right-hand controls overlapped each
   other even below the status bar. A flex item defaults to min-width:auto, so the
   name/sub block refused to shrink and shoved the pill out of the row instead of
   truncating. min-width:0 + ellipsis is the fix; wrap is the safety net for a long
   status word at the 320px floor. */
.ui2 .u2-topbar .u2-id { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; row-gap: 8px; }
/* flex-basis 0, not auto: with wrap enabled the browser places items by their BASE
   size before shrinking any of them, so `1 1 auto` made a long name keep its full
   308px and pushed the whole pill onto a second row. Measured at 390px with the
   name "Muhammed Musthafa Abdul Rahman", `auto` also let the pre-fix layout run to
   r=521 on a 390px header — the gear and switch were off-screen, not merely
   overlapping. Basis 0 makes the name block size from the free space that is left,
   so it ellipsises and the controls stay where the salesman expects them; the wrap
   above stays as the last resort if even the pill cannot fit. */
.ui2 .u2-topbar .u2-who { min-width: 0; flex: 1 1 0; }
.ui2 .u2-topbar .u2-name, .ui2 .u2-topbar .u2-sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui2 .u2-topbar .u2-badge { width: 40px; height: 40px; border-radius: 10px; background: var(--u2-indigo-2); display: flex; align-items: center; justify-content: center; font: 700 15px var(--u2-display); }
.ui2 .u2-topbar .u2-name { font: 700 var(--u2-fs-title) var(--u2-display); }
.ui2 .u2-topbar .u2-sub { font: 500 var(--u2-fs-label) var(--u2-mono); letter-spacing: .08em; text-transform: uppercase; color: var(--u2-on-indigo-dim); }
.ui2 .u2-topbar .u2-pill { flex: 0 0 auto; margin-left: auto; font: 600 var(--u2-fs-caption-mono) var(--u2-mono); letter-spacing: .07em; text-transform: uppercase; display: flex; align-items: center; gap: 6px; }
.ui2 .u2-topbar .u2-dot { width: 7px; height: 7px; border-radius: 50%; }
.ui2 .u2-instruments { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 1px; background: var(--u2-indigo-cell-line); border-radius: 12px; margin-top: 12px; overflow: hidden; }

/* KpiTile */
.ui2 .u2-kpi { padding: 10px 12px; }
.ui2 .u2-kpi .u2-kpi-l { font: 600 var(--u2-fs-micro) var(--u2-display); letter-spacing: .09em; text-transform: uppercase; opacity: .75; }
.ui2 .u2-kpi .u2-kpi-v { font: 600 var(--u2-fs-metric-lg) var(--u2-mono); font-variant-numeric: tabular-nums; margin-top: 2px; }
.ui2 .u2-kpi .u2-kpi-u { font: 500 var(--u2-fs-tag) var(--u2-mono); opacity: .7; }
.ui2 .u2-kpi.u2-instrument { background: var(--u2-indigo-cell); color: var(--u2-on-indigo); }
.ui2 .u2-kpi.u2-card-kpi { background: var(--u2-card); border: 1px solid var(--u2-line); border-radius: 12px; }
.ui2 .u2-kpi.u2-pos .u2-kpi-v { color: var(--u2-kpi-pos); }
.ui2 .u2-kpi.u2-neg .u2-kpi-v { color: var(--u2-kpi-neg); }

/* Section label */
.ui2 .u2-sect { display: flex; align-items: center; gap: 10px; margin: 18px 16px 8px; font: 600 var(--u2-fs-caption) var(--u2-display); letter-spacing: .12em; text-transform: uppercase; color: var(--u2-muted); }
.ui2 .u2-sect::after { content: ''; flex: 1; height: 1px; background: var(--u2-line); }

/* NowCard — marigold hero, exactly one per screen */
.ui2 .u2-now-card { background: var(--u2-now); color: var(--u2-on-now); border-radius: 16px; margin: 0 16px; padding: 16px; }
.ui2 .u2-now-card .u2-eyebrow { font: 600 var(--u2-fs-tag) var(--u2-mono); letter-spacing: .1em; text-transform: uppercase; opacity: .8; }
.ui2 .u2-now-card .u2-now-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.ui2 .u2-now-card .u2-now-title { font: 800 var(--u2-fs-display) var(--u2-display); margin-top: 2px; }
.ui2 .u2-now-card .u2-now-metric { text-align: right; }
.ui2 .u2-now-card .u2-now-metric .v { font: 600 var(--u2-fs-metric) var(--u2-mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
.ui2 .u2-now-card .u2-now-metric .l { font: 600 var(--u2-fs-micro) var(--u2-mono); letter-spacing: .08em; text-transform: uppercase; opacity: .75; }
.ui2 .u2-now-card .u2-now-body { font: 400 var(--u2-fs-body) var(--u2-body); margin-top: 6px; }

/* ActionButton */
.ui2 .u2-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; border: 0; border-radius: 12px; padding: 12px 16px; min-height: 44px; font: 600 var(--u2-fs-button) var(--u2-body); cursor: pointer; }
.ui2 .u2-btn:focus-visible { outline: var(--u2-focus-w, 3px) solid var(--u2-focus); outline-offset: var(--u2-focus-offset, 2px); }
.ui2 .u2-btn.u2-primary { background: var(--u2-ink); color: var(--u2-paper); }
.ui2 .u2-btn.u2-onNow { background: var(--u2-now-action-bg); color: var(--u2-on-now); margin-top: 12px; }
.ui2 .u2-btn.u2-ghost { background: transparent; color: var(--u2-ink); border: 1px solid var(--u2-line); }
.ui2 .u2-btn.u2-danger { background: var(--u2-stop); color: #fff; }
.ui2 .u2-btn.u2-full { width: 100%; }

/* ListRow — the manifest row */
.ui2 .u2-list { margin: 0 16px; display: flex; flex-direction: column; gap: 10px; }
.ui2 .u2-row { position: relative; background: var(--u2-card); border: 1px solid var(--u2-line); border-radius: 14px; padding: 12px 14px 12px 18px; display: flex; align-items: center; gap: 12px; overflow: hidden; }
.ui2 .u2-row .u2-stripe { position: absolute; left: 0; top: 0; bottom: 0; width: 4px; }
/* TASK-054(a): an actionable row is now focusable, so it needs a visible focus ring —
 * focusable-but-invisible is worse than not focusable, because the keyboard user has
 * no idea where they are. Same treatment as .u2-btn:focus-visible. */
.ui2 .u2-row[role="button"] { cursor: pointer; }
.ui2 .u2-row[role="button"]:focus-visible { outline: var(--u2-focus-w, 3px) solid var(--u2-focus); outline-offset: var(--u2-focus-offset, 2px); }
.ui2 .u2-row .u2-m { min-width: 0; flex: 1; }
.ui2 .u2-row .u2-rid { font: 500 var(--u2-fs-caption-mono) var(--u2-mono); letter-spacing: .05em; color: var(--u2-muted); }
.ui2 .u2-row .u2-rtitle { font: 700 var(--u2-fs-row-title) var(--u2-display); margin: 1px 0; }
.ui2 .u2-row .u2-rsub { font: 400 var(--u2-fs-body-sm) var(--u2-body); color: var(--u2-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ui2 .u2-row .u2-rend { text-align: right; }
.ui2 .u2-row .u2-ramount { font: 600 var(--u2-fs-metric-sm) var(--u2-mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
.ui2 .u2-row .u2-rnote { font: 500 var(--u2-fs-caption-mono) var(--u2-mono); color: var(--u2-muted); }

/* StatusTag */
.ui2 .u2-tag { display: inline-block; margin-top: 5px; padding: 3px 8px; border-radius: 7px; font: 600 var(--u2-fs-tag) var(--u2-mono); letter-spacing: .07em; text-transform: uppercase; }
.ui2 .u2-tag.u2-t-go { background: var(--u2-go-bg); color: var(--u2-go-text); }
.ui2 .u2-tag.u2-t-stop { background: var(--u2-stop-bg); color: var(--u2-stop-text); }
.ui2 .u2-tag.u2-t-now { background: var(--u2-now-bg); color: var(--u2-now-text); }
.ui2 .u2-tag.u2-t-brand { background: var(--u2-brand-bg); color: var(--u2-brand); }
.ui2 .u2-tag.u2-t-muted { background: var(--u2-muted-bg); color: var(--u2-muted); }

/* EmptyState — load-bearing (states period + what's actionable) */
.ui2 .u2-empty { margin: 0 16px; background: var(--u2-card); border: 1px dashed var(--u2-line); border-radius: 14px; padding: 26px 18px; text-align: center; }
.ui2 .u2-empty .u2-e-icon { font-size: 26px; }
.ui2 .u2-empty .u2-e-title { font: 700 var(--u2-fs-row-title) var(--u2-display); margin-top: 6px; }
.ui2 .u2-empty .u2-e-hint { font: 400 var(--u2-fs-body-sm) var(--u2-body); color: var(--u2-muted); margin-top: 3px; }

.ui2 .u2-body-pad { padding-bottom: 18px; }

/* row action buttons (ListRow actions extension) + shell buttons in the TopBar */
/* flex-wrap added for v3 slot 1: a row that carries a QtyStepper AND a StatusTag —
 * which the ListRow.actions contract permits — overflows at the 320px floor without
 * it. (Slot 1's README finding F3 also reported a missing align-items here; that
 * part was already fixed in the shipped file before this branch, so only the wrap
 * was still outstanding.) */
.ui2 .u2-ractions { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.ui2 .u2-ractions .u2-btn { padding: 8px 12px; font-size: var(--u2-fs-button-sm); border-radius: 10px; }
.ui2 .u2-ractions .u2-btn.u2-ghost { color: var(--u2-ink); }
.ui2 .u2-shellbtn { background: var(--u2-shell-btn-bg); color: var(--u2-on-indigo); border: 0; border-radius: 9px; padding: 7px 10px; font-size: 14px; cursor: pointer; margin-left: 6px; min-width: 44px; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; }
.ui2 .u2-shellbtn:focus-visible { outline: var(--u2-focus-w, 3px) solid var(--u2-focus-on-indigo); outline-offset: var(--u2-focus-offset, 2px); }

/* slot 2: NowCard inline link (Navigate) + viewport-fill fix (owner live-check finding) */
.ui2 .u2-now-link { background: none; border: 0; padding: 0; font: 600 var(--u2-fs-body) var(--u2-body); color: var(--u2-on-now); text-decoration: underline; cursor: pointer; }
.ui2 .u2-now-link:focus-visible { outline: var(--u2-focus-w, 3px) solid var(--u2-focus-on-now); outline-offset: var(--u2-focus-offset, 2px); }
.ui2 { min-height: 100vh; min-height: 100dvh; }
.ui2.u2-body-pad { padding-bottom: 92px; }

/* slot 3: segmented data-view control (DESIGN_SYSTEM §6.4 — Live · Historical · Combined) */
.ui2 .u2-seg { display: grid; grid-template-columns: repeat(3, 1fr); background: var(--u2-muted-bg); border-radius: 12px; padding: 2px; min-height: 48px; }
.ui2 .u2-seg button { border: 0; background: transparent; color: var(--u2-muted); border-radius: 10px; font: 600 var(--u2-fs-label) var(--u2-mono); letter-spacing: .07em; text-transform: uppercase; cursor: pointer; min-height: 44px; }
.ui2 .u2-seg button.u2-on { background: var(--u2-card); color: var(--u2-ink); border: 1px solid var(--u2-line); }
.ui2 .u2-seg button:focus-visible { outline: var(--u2-focus-w, 3px) solid var(--u2-focus); outline-offset: var(--u2-focus-offset, 2px); }

/* FLAG 1 (DESIGN_SYSTEM §4.4, resolved): 44px effective hit areas via invisible
 * extension — visual density unchanged; the ::before extends the tap target. */
.ui2 .u2-ractions .u2-btn { position: relative; }
.ui2 .u2-ractions .u2-btn::before { content: ''; position: absolute; inset: -6px 0; }
/* .u2-shellbtn no longer needs the invisible-extension trick (RISK/audit
 * 2026-08-16): it now has a real 44x44 box (see the rule above). An expanded
 * invisible hit zone here would overlap the adjacent shellbtn's own hit zone —
 * they sit only 6px apart (margin-left) — and cause ambiguous mis-taps. */
.ui2 .u2-now-link { position: relative; }
.ui2 .u2-now-link::before { content: ''; position: absolute; inset: -14px -6px; }

/* search input (stock-category browse, RISK/audit 2026-08-16 — ported from
 * app.js's stockCatView; 16px font avoids Android/iOS focus-zoom, 44px floor). */
.ui2 .u2-input { width: 100%; background: var(--u2-card); border: 1px solid var(--u2-line); border-radius: 12px; padding: 12px 14px; font-size: 16px; min-height: 44px; color: var(--u2-ink); font-family: var(--u2-body); }
.ui2 .u2-input:focus-visible { border-color: var(--u2-brand); outline: var(--u2-focus-w, 3px) solid var(--u2-focus); outline-offset: var(--u2-focus-offset, 2px); }
.ui2 .u2-field-label { font: 600 var(--u2-fs-label) var(--u2-body); color: var(--u2-ink); }
.ui2 .u2-availability-list .u2-rsub,
.ui2 .u2-availability-banner .u2-rsub { white-space: normal; overflow: visible; text-overflow: clip; overflow-wrap: anywhere; }

/* slot 4: pick→pack→ready stage chips (U2StageChips) */
.ui2 .u2-stages { display: inline-flex; gap: 4px; align-items: center; }
.ui2 .u2-st { font: 600 var(--u2-fs-micro) var(--u2-mono); letter-spacing: .07em; text-transform: uppercase; padding: 3px 7px; border-radius: 7px; background: var(--u2-muted-bg); color: var(--u2-muted); border: 1px solid var(--u2-line); }
.ui2 .u2-st.u2-st-done { background: var(--u2-go-bg); color: var(--u2-go-text); border-color: transparent; }
.ui2 .u2-st.u2-st-on { background: var(--u2-now-bg); color: var(--u2-now-text); border-color: transparent; }

/* design polish slot A — §9.1 skeleton loader, §9.5 offline banner, §8 motion */
.ui2 .u2-skel-row { background: var(--u2-card); border: 1px solid var(--u2-line); border-radius: 14px; padding: 14px; display: flex; flex-direction: column; gap: 7px; }
.ui2 .u2-skel-bar { height: 10px; border-radius: 5px; background: linear-gradient(90deg, var(--u2-muted-bg) 25%, var(--u2-line) 50%, var(--u2-muted-bg) 75%); background-size: 200px 100%; animation: u2shimmer 1200ms linear infinite; }
.ui2 .u2-sk1 { width: 40%; height: 8px; }
.ui2 .u2-sk2 { width: 70%; height: 13px; }
.ui2 .u2-sk3 { width: 30%; height: 8px; }
@keyframes u2shimmer { from { background-position: -200px 0; } to { background-position: 200px 0; } }

.ui2 .u2-offline { background: var(--u2-now-bg); color: var(--u2-now-text); font: 500 var(--u2-fs-caption-mono) var(--u2-mono); letter-spacing: .07em; text-transform: uppercase; text-align: center; padding: 7px 16px; }

.ui2 .u2-btn { transition: transform var(--u2-t-instant, 80ms) var(--u2-ease-standard, ease); }
.ui2 .u2-btn:active { transform: scale(.98); }
.ui2 .u2-seg button { transition: background var(--u2-t-quick, 160ms), color var(--u2-t-quick, 160ms); }
.ui2 .u2-topbar .u2-dot { animation: u2pulse 2s ease-in-out infinite; }
@keyframes u2pulse { 0%, 100% { opacity: 1; } 50% { opacity: .4; } }

/* design polish slot B — §6.2/6.3 form chrome inside sheets, applied only while
 * the active role runs v2 (.ui2-sheet toggled by openSheet; flag OFF = v1 sheet).
 * Tokens come from ui2.tokens.css (scope includes .ui2-sheet). 16px input font
 * prevents Android focus-zoom; 48px min-height = touch floor. */
.ui2-sheet { font-family: var(--u2-body); }
.ui2-sheet .sh-head h3 { font: 700 var(--u2-fs-title) var(--u2-display); }
.ui2-sheet .fld .lab { font: 600 var(--u2-fs-caption) var(--u2-display); letter-spacing: .12em; text-transform: uppercase; color: var(--u2-muted); }
.ui2-sheet .fld input, .ui2-sheet .fld select, .ui2-sheet .fld textarea {
  width: 100%; background: var(--u2-card); border: 1px solid var(--u2-line); border-radius: var(--u2-r-lg, 12px);
  padding: 12px 14px; font-size: 16px; min-height: 48px; color: var(--u2-ink);
}
.ui2-sheet .fld input:focus-visible, .ui2-sheet .fld select:focus-visible, .ui2-sheet .fld textarea:focus-visible {
  border-color: var(--u2-brand); outline: var(--u2-focus-w, 3px) solid var(--u2-focus, var(--u2-brand)); outline-offset: var(--u2-focus-offset, 2px);
}
.ui2-sheet .fld input[inputmode="decimal"], .ui2-sheet .fld input[type="number"] {
  font-family: var(--u2-mono); font-variant-numeric: tabular-nums; text-align: right;
}
.ui2-sheet .btn { border-radius: var(--u2-r-lg, 12px); min-height: 44px; font: 600 var(--u2-fs-button) var(--u2-body); }
.ui2-sheet .btn.primary { background: var(--u2-ink); }

/* TASK-14 — the route map panel (routemap.js). rm-mount is what Leaflet takes
 * over; its height is set here (Leaflet reads the element's box at construction
 * time), never inline, so it stays themeable and consistent with the rest of
 * the shell. rm-offline sits in the SAME slot and is only ever shown by
 * toggling `hidden` — never rendered in place of rm-mount, so there is no
 * layout jump between "map" and "map needs internet". */
.ui2 .rm-panel { margin: 0 16px 14px; }
.ui2 .rm-mount { height: 220px; border-radius: 16px; overflow: hidden; border: 1px solid var(--u2-line); background: var(--u2-muted-bg); }
.ui2 .rm-offline {
  height: 220px; border-radius: 16px; border: 1px solid var(--u2-line); background: var(--u2-muted-bg);
  display: flex; align-items: center; justify-content: center; text-align: center; padding: 20px;
  color: var(--u2-muted); font: 600 var(--u2-fs-caption) var(--u2-display);
}
.ui2 .rm-empty {
  margin: 0 16px 14px; padding: 14px 16px; border-radius: 16px; border: 1px solid var(--u2-line);
  background: var(--u2-muted-bg); color: var(--u2-muted); font: 500 var(--u2-fs-caption) var(--u2-body);
}
.ui2 .rm-nogps { margin-top: 8px; font: 500 var(--u2-fs-tag) var(--u2-body); color: var(--u2-muted); }
/* Leaflet's own attribution control is left ON (never override it off) — this
 * only restyles it to sit comfortably on the app's own card chrome. */
.ui2 .rm-mount .leaflet-control-attribution { font-size: 10px; background: rgba(255,255,255,.85); }
/* Numbered stop pins + the depot marker — plain divIcon HTML, styled here rather
 * than inline so dark mode (if the map panel is ever shown on a dark surface)
 * only needs one place to adjust. */
.rm-pin {
  width: 26px; height: 26px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  background: var(--u2-brand); color: #fff; font: 700 12px var(--u2-mono, monospace);
  box-shadow: 0 1px 4px rgba(0,0,0,.35); border: 2px solid #fff;
}
.rm-pin.rm-depot { background: var(--u2-now); color: var(--u2-on-now); }
.ui2-sheet .x { min-width: 44px; min-height: 44px; }
