/* =============================================================================
   Reliability360 CMMS — Stylesheet
   Single source of truth for the Blazor Server build (plain CSS, no framework).
   Extracted verbatim from the prototypes + Component Spec.dc.html.

   Sections
     1. Fonts
     2. Tokens (CSS custom properties)
     3. Reset & base
     4. Layout shells (sidebar / mobile bottom-tab)
     5. Typography helpers (eyebrow, display, mono)
     6. Components
          .pill  .card  .statcard  .btn  .datarow  .field
          .drawer  .toast  .hero  .tabbar  .help-chip
     7. States (hover / focus / press)
     8. Animations
     9. Utilities & placeholders
    10. Responsive
============================================================================= */

/* 1. FONTS ------------------------------------------------------------------ */
@import url("https://fonts.googleapis.com/css?family=JetBrains+Mono:wght@400;500;600&family=Arimo:wght@400;500;600;700&display=swap");

@font-face {
  font-family: "Helvetica LT";
  src: url("../Helvetica_LT_Bold.ttf") format("truetype");
  font-weight: 500 900;
  font-style: normal;
  font-display: swap;
}

/* 2. TOKENS ----------------------------------------------------------------- */
:root {
  /* Brand */
  /* Accessibility audit [A1]: --brand at #0599E1 is only 3.16:1 as white-on-blue button/link text
     — below the 4.5:1 AA minimum for normal-weight text. --brand is now the AA-safe deep blue
     (4.77:1) and drives all TEXT/button/link uses; --brand-bright keeps the original vivid blue
     for non-text uses (icons, active-nav fills, decorative accents) where contrast rules don't apply.
     Component CSS must reference these variables, never the hex values: App.razor overrides them per
     customer (Branding:BrandColor), and a literal silently opts that component out. Rule of thumb:
     text, button fills, links, focus rings -> --brand; mono record IDs and text on a pale-blue tint
     -> --brand-tile (keeps 4.5:1 on the tint); hover outlines of cards/tiles, progress fills, drag
     highlights and icon tiles -> --brand-bright. */
  --brand: #0479B4; /* AA-safe blue (4.77:1 on white) — buttons, links, focus */
  --brand-bright: #0599E1; /* original vivid blue — icons/decorative/non-text UI only */
  --brand-deep: #0479B4; /* primary button hover (now same as --brand; kept for callers that reference it explicitly) */
  --brand-ink: #0A2540; /* navy — sidebar, hero, weight */
  --brand-tile: #036FA8; /* deep blue — mono IDs, ghost-button text */
  --splash-lime: #E6F47A; /* the one pop — dark surfaces only */
  --splash-cobalt: #0E3A66; /* resume-gradient + scanner stripe partner */
  /* Ink ramp (text) */
  --ink: #0E1726; /* body */
  --ink-2: #2C3A4F; /* labels, secondary body */
  --ink-3: #5A6878; /* help & meta */
  /* Accessibility audit [A3]: was #8B97A8 (2.96:1 on white, 2.76:1 on --paper-2) but used for real
     content (.meta, user emails, report subtext), not just decoration — needs 4.5:1. */
  --ink-4: #647285; /* real-content meta text (4.57:1 on --paper-2, 4.9:1 on white) */
  --ink-5: #B9C3D1; /* disabled */
  /* Surfaces */
  --paper: #FFFFFF;
  --paper-2: #F5F7FB; /* app background, inset fills */
  --paper-3: #EAEEF6; /* chips, tracks */
  --paper-mobile: #F1F3F8; /* mobile screen background */
  --app-bg: #E7EAF0; /* desktop canvas behind cards */
  --icon-tile: #EAF2FB; /* asset/doc icon square */
  /* Lines */
  --line: #DCE2ED;
  --line-soft: #EBEFF5;
  --line-strong: #B6C0D0;
  /* Status pairs (bg / fg) */
  /* Accessibility audit [A2]: pill text color must hit 4.5:1 against its own -bg. success/warning/
     danger all failed (3.64/3.42/4.21:1); info passed but barely (4.59:1, left as-is). Deepened
     fg only — bg tints unchanged so pills keep their current look at a glance. */
  --success: #1B7950; /* was #1F8A5B (3.64:1) — now 4.52:1 on --success-bg */
  --success-bg: #D8F1E3;
  --warning: #966100; /* was #B27300 (3.42:1) — now 4.57:1 on --warning-bg */
  --warning-bg: #FFEEC7;
  --danger: #BB3320; /* was #C43521 (4.21:1) — now 4.52:1 on --danger-bg */
  --danger-bg: #FBDCD7;
  --info: #036FA8; /* unchanged — already 4.59:1 on --info-bg */
  --info-bg: #D6EFFF;
  --neutral: #2C3A4F;
  --neutral-bg: #EAEEF6;
  --toast-dot: #4AD07E;
  /* Priority bar colors (work-order left rail) */
  --pri-low: #8B97A8;
  --pri-medium: #0599E1;
  --pri-high: #B27300;
  --pri-critical: #C43521;
  /* Semantic aliases (design system, Sep 28, 2026). Name a surface/text/border by its ROLE so a
     rule reads as intent ("the card surface") rather than a palette step. They resolve to the
     palette above, so per-customer brand overrides still flow through. */
  --surface-canvas: var(--paper-2); /* app canvas behind the content column */
  --surface-card: var(--paper); /* card / stat card / r360-card fill */
  --surface-dark: var(--brand-ink); /* sidebar, dark cards */
  --surface-inset: var(--paper-2); /* inset fills, table header row */
  --text-body: var(--ink);
  --text-label: var(--ink-2);
  --text-help: var(--ink-3);
  --text-meta: var(--ink-4);
  --text-disabled: var(--ink-5);
  --text-link: var(--brand);
  --text-id: var(--brand-tile);
  --text-on-dark: #FFFFFF;
  --text-on-dark-muted: rgba(255, 255, 255, 0.7);
  --border-card: var(--line);
  --border-input: var(--line);
  --focus-ring: var(--brand);
  --focus-halo: color-mix(in srgb, var(--brand-bright) 15%, transparent); /* 4px glow around a focused input */
  --scrim: color-mix(in srgb, var(--brand-ink) 50%, transparent); /* backdrop behind drawers and sheets */
  --gradient-resume: linear-gradient(95deg, var(--brand-ink) 0%, var(--splash-cobalt) 100%);
  /* Typography */
  --f-display: "Helvetica LT", "Helvetica Neue", Helvetica, Arial, sans-serif;
  /* Arimo (Google Fonts, metric-compatible with Arial) sits ahead of Arial so machines without
     Helvetica -- Windows, Linux, most Android -- get one consistent sans (design system, Sep 29 2026). */
  --f-body: "Helvetica Neue", Helvetica, Arimo, Arial, sans-serif;
  --f-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  /* Type scale (fs = font-size, ls = letter-spacing, lh = line-height) */
  --fs-body: 15px;
  --lh-body: 1.55;
  --fs-display-1: 44px;
  --ls-display-1: -0.035em;
  --fs-report-hero: 48px;
  --fs-h1: 30px;
  --ls-h1: -0.03em;
  --fs-ab-title: 24px; /* ActionBar page title */
  --fs-h2: 22px;
  --ls-h2: -0.03em;
  --fs-card-head: 17px;
  --ls-card-head: -0.02em;
  --fs-stat: 38px; /* StatCard value */
  --fs-button: 14px;
  --fs-button-block: 16px;
  --fs-input: 14px;
  --fs-label: 13px; /* field and checkbox labels */
  --fs-sub: 13px; /* stat card sub line, ActionBar subtitle, pager text */
  --fs-cell: 12.5px; /* table cells */
  /* Design system (Sep 29, 2026): headers and eyebrows are sentence-case body text, no longer
     mono uppercase with wide tracking, so the --ls-* tracking tokens were retired. */
  --fs-th: 13px;
  --fs-eyebrow: 12px;
  --fs-id: 12px;
  --fs-id-cell: 11.5px; /* mono record ID inside a table cell */
  --fs-meta: 12px;
  --fs-pill: 12px;
  --fs-tab: 11px; /* mobile tab bar label */
  /* Radii */
  --r-xs: 3px;
  --r-sm: 6px;
  --r-md: 6px;
  --r-lg: 8px;
  --r-xl: 10px;
  --r-2xl: 10px;
  --r-pill: 999px;
  --r-sheet: 16px;
  --r-tag: 3px; /* status pills, type badges, filter chips, nav counts */
  /* Spacing (8-pt base) */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 14px;
  --s-5: 16px;
  --s-6: 18px;
  --s-7: 22px;
  --s-8: 24px;
  --s-9: 28px;
  /* Shadows — very restrained; cards default to none */
  --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.03); /* barely-there lift */
  --shadow-2: 0 4px 18px rgba(14, 23, 38, 0.06); /* handoff pages, hover */
  --shadow-cover: 0 8px 30px color-mix(in srgb, var(--brand-ink) 18%, transparent); /* dark cover plates */
  --shadow-drawer: -8px 0 24px color-mix(in srgb, var(--brand-ink) 12%, transparent); /* side sheet */
  --shadow-toast: 0 12px 30px rgba(0, 0, 0, 0.25); /* floating toast */
  --shadow-menu: 0 8px 24px rgba(14, 23, 38, 0.14); /* dropdown / More-actions panel */
  /* Motion */
  --ease-house: cubic-bezier(0.2, 0.6, 0.2, 1);
  --ease-progress: cubic-bezier(0.5, 0.1, 0.2, 1);
  --tr-fast: 0.15s;
  --tr-enter: 0.25s; /* drawer / sheet enter */
  /* Layout */
  --hit-min: 44px; /* minimum touch target */
  --btn-h: 36px; /* visual height of a Btn (the ::after hit area still reaches 44px) */
  --sidenav-w: 268px;
  --pageheader-h: 56px;
  --drawer-w: 520px;
  --content-pad: 30px 44px 96px; /* .main-body padding */
  /* Breakpoints for reference only: custom properties can't be used inside @media queries, so
     the media rules below still spell out 1200px (tablet) and 900px (phone). */
  --bp-tablet: 1200px;
  --bp-phone: 900px;
}

/* 3. RESET & BASE ----------------------------------------------------------- */
* {
  box-sizing: border-box;
}

html, body {
  margin: 0;
  padding: 0;
  background: var(--surface-canvas);
  color: var(--ink);
  font-family: var(--f-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}

a {
  color: var(--brand);
  text-decoration: none;
}

  a:hover {
    text-decoration: underline;
  }

/* 4. LAYOUT SHELLS ---------------------------------------------------------- */
.app {
  display: flex;
  min-height: 100vh;
}

/* Accessibility audit [A6]: visually hidden until keyboard-focused, so it doesn't disturb the
   normal visual layout but is the very first thing a keyboard user can Tab to on any page.
   P8-12 fix: this used to hide via a fixed `top: -40px`, but the link's actual rendered height
   (10px+10px padding plus a 14px/600 line box, ~41.7px) was taller than that 40px offset, so a
   ~1-2px sliver of its navy background was always visible at the very top of the viewport, on
   every page, at every width. It was invisible on desktop purely by coincidence: that sliver sits
   at x 8-144px, which on desktop falls behind the 268px-wide `.sidenav` (identical navy color), so
   the two navy pixels blended together. At phone width the sidenav goes off-canvas (see the
   `.sidenav { left: -280px }` mobile rule below) and nothing else occupies that space, so the
   sliver became a visible "stray dark bar" above the mobile page header. Fixing with
   `transform: translateY(-120%)` (relative to the element's own rendered height, with a safety
   margin) instead of a magic pixel offset means it stays fully off-screen even if padding/font
   ever change its height again. */
.skip-link {
  position: absolute;
  top: 0;
  left: 8px;
  z-index: 1000;
  background: var(--brand-ink);
  color: #fff;
  padding: 10px 16px;
  border-radius: var(--r-sm);
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  transform: translateY(-120%);
}

.skip-link:focus {
  transform: translateY(8px);
}

/* Desktop: fixed 268px navy sidebar */
.sidenav {
  width: var(--sidenav-w);
  flex-shrink: 0;
  position: sticky;
  top: 0;
  height: 100vh;
  background: var(--surface-dark);
  color: #fff;
  padding: 22px 18px 24px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* Only the link list scrolls; the Support group and account strip stay pinned (UX 2026-09-23
   P1-01). The scrollbar is hidden (design system, Sep 28, 2026); the list still scrolls by wheel,
   touch and keyboard, and SideNav scrolls the active link into view after each navigation. */
.sidenav-scroll { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 4px; scrollbar-width: none; }
.sidenav-scroll::-webkit-scrollbar { display: none; }

/* One always-open nav group: h2 heading + its links, 4px apart like the rest of the list. */
.sidenav-grp { display: flex; flex-direction: column; gap: 4px; }

/* Pinned Support group under the scrolling list. The negative side margins let its top hairline
   run the full sidebar width; the matching padding puts the links back on the 18px gutter. */
.sidenav-pinned {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 8px -18px 0;
  padding: 0 18px;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}

/* Account strip: runs edge to edge and sits flush with the sidebar's bottom (the negative margins
   cancel .sidenav's 18px side and 24px bottom padding). 14 + 44 + 16 = 74px tall. No avatar. */
.sidenav-foot {
  flex-shrink: 0;
  margin: 16px -18px -24px;
  padding: 14px 18px 16px;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  display: flex;
  align-items: center;
  gap: 12px;
}
.sidenav-foot__name { font-size: 13.5px; font-weight: 600; color: var(--text-on-dark); }
.sidenav-foot__role {
  font-size: 12px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.65);
  margin-top: 2px;
}
/* 44x44 sign-out target (the icon stays 18px). */
.sidenav-signout {
  width: var(--hit-min);
  height: var(--hit-min);
  padding: 0;
  display: grid;
  place-items: center;
  background: transparent;
  border: none;
  border-radius: var(--r-sm);
  color: rgba(255, 255, 255, 0.55);
  cursor: pointer;
}
.sidenav-signout:hover { color: #fff; background: rgba(255, 255, 255, 0.08); }
.sidenav-signout:focus-visible { outline: 2px solid #fff; outline-offset: -2px; }

.main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.main-body {
  flex: 1;
  min-width: 0;
  overflow: auto;
  padding: var(--content-pad);
  /* Design system (Sep 28, 2026): the content column is a flex stack with one 18px gap between
     page blocks (ActionBar, stat strip, cards), so pages no longer space themselves with ad-hoc
     margins. max-width keeps list pages readable on very wide monitors. */
  display: flex;
  flex-direction: column;
  gap: 18px;
  max-width: 1400px;
  width: 100%;
  box-sizing: border-box;
}

/* 5. TYPOGRAPHY HELPERS ----------------------------------------------------- */
.eyebrow {
  font-family: var(--f-body);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  color: var(--ink-3);
}

.eyebrow--brand {
  color: var(--brand);
}

.display-1 {
  font-family: var(--f-display);
  font-weight: 700;
  font-size: var(--fs-display-1);
  letter-spacing: var(--ls-display-1);
  line-height: 1.05;
}

.display-2 {
  font-family: var(--f-display);
  font-weight: 700;
  font-size: var(--fs-h1);
  letter-spacing: var(--ls-h1);
  line-height: 1.1;
}

.display-3 {
  font-family: var(--f-display);
  font-weight: 700;
  font-size: var(--fs-h2);
  letter-spacing: var(--ls-h2);
  line-height: 1.15;
}

.display-4 {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: var(--fs-card-head);
  letter-spacing: var(--ls-card-head);
}

.mono {
  font-family: var(--f-mono);
  letter-spacing: 0.02em;
}

.id-mono {
  font-family: var(--f-mono);
  font-size: var(--fs-id);
  font-weight: 600;
  color: var(--brand-tile);
}

/* Monospace record id in a table cell or row link (WO-1042, AST-017, PO/part/PR numbers, PO
   line #). Replaces the inline style="font-family:'JetBrains Mono',monospace;font-size:11.5px;
   color:#0599e1" that was copied across ~20 pages and ignored the customer's brand colour.
   --brand-tile keeps 4.5:1 on white. Smaller and regular-weight next to .id-mono (12px/600).
   The a.-qualified selector (0,1,1) outranks .r360-row-link's `color: inherit` (0,1,0) whatever
   the source order, while .r360-row-link:hover (0,2,0) still adds its underline. The td form
   (0,2,1) outranks `.r360-table td` (0,1,1), which otherwise resets the cell to body font and
   --ink -- the inline style it replaced beat that rule by being inline. */
.r360-rec-id,
a.r360-rec-id,
.r360-table td.r360-rec-id {
  font-family: var(--f-mono);
  font-size: var(--fs-id-cell);
  color: var(--brand-tile);
}
a.r360-rec-id { text-decoration: none; }

.meta {
  font-family: var(--f-mono);
  font-size: var(--fs-meta);
  color: var(--ink-4);
}

/* UX 2026-09-23 P5-06: AutoIdField's read-only id display (server-minted, shown as text rather
   than a disabled input). Reuses the project's existing --f-mono token rather than hardcoding
   "JetBrains Mono" so it matches every other monospace id/code label already in the app. */
.r360-auto-id {
  font-family: var(--f-mono);
  font-size: 13px;
  font-weight: 600;
}

.r360-auto-id__note {
  font-size: 12.5px;
  color: var(--ink-4);
}

h1 {
  font-family: var(--f-display);
  font-weight: 700;
  font-size: var(--fs-h1);
  letter-spacing: var(--ls-h1);
  margin: 0;
}

h2 {
  font-family: var(--f-display);
  font-weight: 700;
  font-size: var(--fs-h2);
  letter-spacing: var(--ls-h2);
  margin: 0;
}

/* Design system base rules (Sep 28, 2026). Every text field gets the same focus treatment: brand
   border plus a 4px halo, so a control that forgot its own :focus rule still shows focus. Every
   focusable element gets a visible 2px ring for keyboard users; component-level copies of this
   rule stay because some tune the offset (e.g. inset rings inside table rows). MudBlazor inputs
   (class mud-*) are excluded: their inner <input> sits inside Mud's own outline, and a halo there
   would draw a second box inside the field. */
:where(input, select, textarea):not([class*="mud-"]):focus {
  outline: 0;
  border-color: var(--brand);
  box-shadow: 0 0 0 4px var(--focus-halo);
}

:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

/* Live dots and the scanner sweep loop forever; stop them for users who ask for less motion. */
@media (prefers-reduced-motion: reduce) {
  .r360-live, .r360-scanline { animation: none !important; }
}

/* 6. COMPONENTS ------------------------------------------------------------- */

/* --- Pill / status badge -------------------------------------------------- */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  border-radius: var(--r-tag);
  font-size: var(--fs-pill);
  line-height: 1.3;
  font-weight: 600;
  white-space: nowrap;
}

  .pill .pill__dot {
    width: 6px;
    height: 6px;
    border-radius: 1px; /* square marker, per the design system (reference Pill.jsx) */
    background: currentColor;
    flex-shrink: 0;
  }

.pill--success {
  background: var(--success-bg);
  color: var(--success);
}

.pill--warning {
  background: var(--warning-bg);
  color: var(--warning);
}

.pill--danger {
  background: var(--danger-bg);
  color: var(--danger);
}

.pill--info {
  background: var(--info-bg);
  color: var(--info);
}

.pill--neutral {
  background: var(--neutral-bg);
  color: var(--neutral);
}

/* --- Mark (square + word) ------------------------------------------------ */
/* Priority / criticality / work-order type as an 8px square plus the plain word (design system
   CriticalityMark). Global rather than component-scoped because CriticalityMark, TypeBadge's
   Mark form and the mobile WASM pages all render it. The word inherits colour, so it reads in
   body ink on white and white on the navy mobile hero. */
.critmark {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  /* Fixed at the table-cell size so a mark reads the same beside a pill on a card or detail
     header as it does in a table row (it would otherwise inherit the 15px body text there). */
  font-size: var(--fs-cell);
  white-space: nowrap;
}

.critmark__sq {
  width: 8px;
  height: 8px;
  border-radius: 1px; /* same squared corner as the pill dot */
  flex-shrink: 0;
}

/* --- Card ----------------------------------------------------------------- */
.card {
  background: var(--surface-card);
  border: 1px solid var(--line);
  border-radius: var(--r-2xl);
  padding: var(--s-8);
}

  .card.flush {
    padding: 0;
    overflow: hidden;
  }
  /* tables / row lists */
  .card.dark {
    background: var(--surface-dark);
    color: #fff;
    border-color: transparent;
  }

  .card.brand {
    background: var(--brand);
    color: #fff;
    border-color: transparent;
  }

/* --- Stat card ------------------------------------------------------------ */
.statcard {
  background: var(--surface-card);
  border: 1px solid var(--line);
  border-radius: var(--r-2xl);
  padding: 22px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-height: 118px;
}

.statcard__eyebrow {
  font-family: var(--f-body);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  color: var(--ink-3);
}

.statcard__value {
  font-family: var(--f-display);
  font-weight: 700;
  font-size: var(--fs-stat);
  line-height: 1;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}

.statcard__unit {
  font-family: var(--f-mono);
  font-size: 13px;
  color: var(--ink-4);
}

.statcard__sub {
  font-size: var(--fs-sub);
  line-height: 18px;
  color: var(--ink-3);
}

/* --- Buttons -------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: inherit;
  font-size: var(--fs-button);
  font-weight: 600;
  padding: 9px 16px;
  border: 0;
  border-radius: var(--r-md);
  cursor: pointer;
  transition: background var(--tr-fast) ease, transform var(--tr-fast) ease;
}

.btn--primary {
  background: var(--brand);
  color: #fff;
}

.btn--ghost {
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--line);
}

.btn--dark {
  background: var(--brand-ink);
  color: #fff;
}

.btn--success {
  background: var(--success);
  color: #fff;
}

.btn--danger {
  background: var(--danger);
  color: #fff;
}

.btn--block {
  width: 100%;
  padding: 15px;
  font-size: var(--fs-button-block);
  border-radius: var(--r-lg);
}

.btn--dashed {
  background: var(--paper-2);
  color: var(--brand-tile);
  border: 1px dashed var(--line-strong);
  font-size: 13px;
}

/* --- Inline-edit list cards (Settings) --------------------------------------
   Shared by FailureCodeCard, LookupTableCard, WorkOrderStatusCard, WorkOrderTypeCard, KitCard and
   WOTemplateCard. Each card used to carry its own copy of these rules (with hex literals); they
   live here once so a fix lands on every card.

   .r360-iconbtn: the row's Edit / Delete / Save / Cancel glyph buttons. Visible AT REST, never
   hover-revealed: a hover-only control is invisible on plant-floor tablets (no hover) and to
   low-vision admins (one reported the rename feature "didn't exist"), and still takes a tab stop
   while invisible (WCAG 2.4.11). Do not restore an opacity: 0 resting state. 44x44 minimum hit
   area from the A1-A10 accessibility pass; the glyph stays small inside it. The resting glyph is
   --ink-4 (not the disabled-only --ink-5) so it reads as a live control. Every icon-only use needs
   an aria-label. */
.r360-iconbtn {
  border: none;
  background: transparent;
  cursor: pointer;
  color: var(--ink-4);
  font-size: 13px;
  line-height: 1;
  min-width: var(--hit-min);
  min-height: var(--hit-min);
  display: grid;
  place-items: center;
  border-radius: var(--r-sm);
  transition: color .12s, background .12s;
  flex-shrink: 0;
}

.r360-iconbtn:disabled {
  opacity: .3;
  cursor: not-allowed;
}

.r360-iconbtn:hover:not(:disabled) {
  background: var(--paper-3);
  color: var(--ink-3);
}

/* Keyboard users get the same visible affordance as hover. */
.r360-iconbtn:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

.r360-iconbtn--del:hover:not(:disabled) {
  color: var(--danger);
  background: var(--danger-bg);
}

.r360-iconbtn--save {
  color: var(--success);
}

.r360-iconbtn--save:hover:not(:disabled) {
  color: var(--success);
  background: var(--success-bg);
}

/* Glyph plus a word ("✓ Save", "✕ Cancel") in the multi-line Kit / WO template editors. */
.r360-iconbtn--text {
  font-weight: 600;
  padding: 6px 12px;
}

/* Full-width dashed "+ Add …" button at the foot of a card. The 5% brand tint on hover follows
   the customer's hue (was the fixed literal #f3f8fd). */
.r360-dashed-add {
  margin-top: 12px;
  width: 100%;
  border: 1px dashed var(--line-strong);
  background: transparent;
  color: var(--brand);
  font-weight: 600;
  font-size: 14px;
  padding: 11px;
  border-radius: var(--r-lg);
  cursor: pointer;
  transition: background .12s, border-color .12s;
}

.r360-dashed-add:hover {
  background: color-mix(in srgb, var(--brand-bright) 5%, var(--paper));
  border-color: var(--brand-bright);
}

/* Compact inline variant ("+ Add line" inside an editor). */
.r360-dashed-add--sm {
  width: auto;
  margin-top: 0;
  margin-bottom: 10px;
  font-size: 12px;
  padding: 6px 10px;
  border-radius: var(--r-sm);
}

/* --- Kit / WO template cards (.kc-*) ----------------------------------------
   KitCard and WOTemplateCard are the same card shape (name, description, line-by-line part/qty
   editor) and used byte-identical scoped stylesheets. Global here so both render from one copy. */
.kc-card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  padding: 20px 22px 22px;
}

.kc-head { margin-bottom: 12px; }

.kc-title { font-weight: 700; font-size: 16px; color: var(--ink); }

.kc-sub {
  font-family: var(--f-mono);
  font-size: 11px;
  color: var(--ink-4);
  margin-top: 3px;
}

.kc-rowgroup { border-bottom: 1px solid var(--line-soft); }

.kc-rowgroup .kc-row { border-bottom: none; }

.kc-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 2px;
  border-bottom: 1px solid var(--line-soft);
}

.kc-name {
  flex: 0 0 160px;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}

.kc-desc {
  flex: 1;
  font-size: 13px;
  color: var(--ink-4);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.kc-count {
  flex: 0 0 auto;
  font-family: var(--f-mono);
  font-size: 11px;
  color: var(--ink-4);
}

.kc-input {
  font-size: 14px;
  font-family: inherit;
  color: var(--ink);
  border: 1px solid var(--brand);
  border-radius: var(--r-sm);
  padding: 6px 10px;
  outline: none;
}

.kc-input-name { flex: 0 0 200px; }
.kc-input-desc { flex: 1; }
.kc-input-qty { flex: 0 0 70px; }

/* Inline editor (name/description + line-by-line part/qty rows) shown while adding or editing. */
.kc-editor {
  padding: 12px 2px 16px;
  border-bottom: 1px solid var(--line-soft);
}

.kc-editor--new {
  margin-top: 12px;
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-lg);
  padding: 14px;
}

.kc-editrow {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}

.kc-linerow {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

.kc-editactions {
  display: flex;
  gap: 8px;
  margin-top: 4px;
}

/* No-site-assigned banner (NoSiteAssignedBanner.razor). Global rather than scoped because the
   banner is rendered inside MudBlazor child-content wrappers on a dozen pages, where a scoped
   stylesheet would not reach it. Warning status pair: --warning edge on --warning-bg. */
.no-site-banner {
  display: flex;
  gap: 8px;
  align-items: baseline;
  padding: 14px 16px;
  margin: 12px 0;
  border: 1px solid var(--warning);
  border-left-width: 4px;
  border-radius: var(--r-lg);
  background: var(--warning-bg);
  color: var(--ink);
  font-size: 14px;
}

/* --- List / table row ----------------------------------------------------- */
.datarow {
    display: grid;
    grid-template-columns: 96px 1fr auto;
    gap: 14px;
    align-items: center;
    padding: 13px 22px;
    border-bottom: 1px solid var(--line-soft);
    cursor: pointer;
    background: var(--paper);
    text-align: left;
    width: 100%;
    border-left: 0;
    border-right: 0;
    border-top: 0;
    font-family: inherit;
}

  .datarow:last-child {
    border-bottom: 0;
  }

.datarow__id {
  font-family: var(--f-mono);
  font-size: 13px;
  font-weight: 600;
  color: var(--brand-tile);
}

.datarow__title {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  /* Must be block for overflow/ellipsis to apply — an inline span ignores them,
     so a long title would spill out of its grid track and over the pills column. */
  display: block;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --- Tables (dashboard / list pages) ------------------------------- */
.r360-table {
  width: 100%;
  border-collapse: collapse;
}

/* Compact by default. The client compared these rows against Fiix's ~26px and asked for a list
   view; the table was already a table, the rows were just twice as tall as they needed to be.
   Changing the shared rule lifts density on every r360-table in the app at once, which is the
   point -- Assets and Inventory are not the only dense lists here. */
.r360-table th,
.r360-table td {
  font-family: var(--f-body);
  font-size: var(--fs-cell);
  padding: 7px 12px;
  border-bottom: 1px solid var(--line-soft);
  text-align: left;
  vertical-align: middle;
  color: var(--ink);
}

.r360-table th {
  font-family: var(--f-body);
  font-size: var(--fs-th);
  font-weight: 600;
  color: var(--ink-2);
  padding: 7px 12px;
  /* Design system (Sep 28, 2026): the header row sits on an inset fill, so it reads as a band
     distinct from the white body rows. Sep 29, 2026: sentence-case labels, with a 2px brand
     underline in place of the grey rule. */
  background: var(--surface-inset);
  border-bottom: 2px solid var(--brand-tile);
  white-space: nowrap;
}

.r360-table th {
    position: relative;
}

.r360-col-resize-handle {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 6px;
    cursor: col-resize;
    user-select: none;
}

.r360-col-resize-handle:hover,
.r360-col-resize-handle:active {
    background: color-mix(in srgb, var(--brand-bright) 30%, transparent);
}

/* SortHeader button (UX 2026-09-23 P1-17): looks like the header text it replaces (a column
   header, not an action button), full-cell hit area, 44px tall, visible keyboard focus. Defined
   globally since SortHeader.razor is shared across every list page's table. */
.r360-sort-btn { all: unset; box-sizing: border-box; display: inline-flex; align-items: center; gap: 4px;
                 min-height: var(--hit-min); width: 100%; cursor: pointer; font: inherit; color: inherit;
                 text-transform: inherit; letter-spacing: inherit; }
.r360-sort-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: var(--r-sm); }
/* The sorted column's label darkens to full ink so the active sort reads without the arrow. */
.r360-sort-btn.is-sorted { color: var(--ink); }
.r360-sort-icon { opacity: .45; }
.r360-sort-icon.is-active { opacity: 1; color: var(--brand); }

/* --- List page pattern (design system, Sep 28, 2026) ----------------------------------------
   Every list page is ONE flush card: filter bar on top, table in the middle, count/pager footer
   at the bottom, all edge to edge. These are global (not page-scoped) so every list page shares
   one set of values; pages only add their own column widths. */

/* Flush card: no inner padding, and the filter bar, table and footer reach the card's edges.
   overflow:hidden clips their square corners to the card's rounded ones. */
.r360-card.r360-card--flush {
  padding: 0;
  overflow: hidden;
}

/* Filter bar: first row of the flush card. 10px top/bottom + the 44px checkbox label = 64px,
   plus the 1px rule = 65px. Wraps onto a second row on narrow screens. */
.r360-filterbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--line);
}

/* Filter select and search box: 38px, 13.5px text. The select draws its own chevron (appearance
   none removes the browser's), so every browser shows the same control. */
.r360-filter {
  width: 180px;
  height: 38px;
  padding: 0 32px 0 12px;
  border: 1px solid var(--border-input);
  border-radius: var(--r-sm);
  font-family: var(--f-body);
  font-size: 13.5px;
  color: var(--ink);
  background: #fff url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%235a6878' stroke-width='2'><path d='M6 9l6 6 6-6'/></svg>") no-repeat right 10px center;
  cursor: pointer;
  appearance: none;
}
/* Search box: wider, no chevron, text cursor. */
.r360-filter--search {
  width: 240px;
  padding: 0 12px;
  background-image: none;
  cursor: text;
}
/* A filter whose option text is long (a person's name, a status list) may ask for more room. */
.r360-filter--wide {
  width: 220px;
}
/* Date filter: the browser draws its own calendar icon, so no chevron and natural width. */
.r360-filter--date {
  width: auto;
  padding: 0 10px;
  background-image: none;
  cursor: text;
}
/* Text label that sits beside a filter control ("Created from [date]"). */
.r360-filter-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--f-body);
  font-size: var(--fs-label);
  color: var(--text-help);
}
.r360-filter:focus {
  outline: 0;
  border-color: var(--brand);
  box-shadow: 0 0 0 4px var(--focus-halo);
}

/* Filter checkbox with its label: the label is the 44px-tall hit area, the box itself is 16px. */
.r360-check {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: var(--hit-min);
  font-family: var(--f-body);
  font-size: var(--fs-label);
  color: var(--text-help);
  cursor: pointer;
}
.r360-check input[type=checkbox],
.r360-rowcheck input[type=checkbox] {
  width: 16px;
  height: 16px;
  margin: 0;
  flex-shrink: 0;
  accent-color: var(--brand);
  cursor: pointer;
}

/* Row-select checkbox: 16px box centred in a 32x32 hit box, in a 32px-wide first column. The 32px
   box is what sets a selectable row's height: 32 + 14 padding + 1 border = 47px. */
.r360-rowcheck {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  min-height: 32px;
  cursor: pointer;
}
/* Stable column widths for list tables (Sep 29, 2026). A list opts in with r360-table--fixed and a
   <colgroup> of widths (the one col with no width takes what's left), plus an optional
   --r360-table-min below which its scroll wrap scrolls sideways instead of squeezing columns.
   Without it the browser sizes every column from the rows on screen, so a filter, sort or page
   change made the whole table jump. Desktop only: at 1200px and below the table stacks into
   labelled cards and column widths don't apply. */
@media (min-width: 1201px) {
  .r360-table--fixed { table-layout: fixed; min-width: var(--r360-table-min, 0); }
  .r360-table--fixed td { overflow-wrap: anywhere; }

  /* Pinned actions column (Sep 30, 2026). On a laptop the content column can be narrower than a
     list's --r360-table-min, so the table scrolls sideways inside its wrap -- and the wrap's
     scrollbar sits under the last row, below the fold. The right-most column (Edit/Delete,
     Action) then looked cut off with no visible way to reach it. Marking that column's th and
     td r360-pin-end sticks it to the wrap's right edge so it stays on screen while the other
     columns scroll under it. The td needs an opaque fill for that; the hover and selected row
     rules above out-rank this one, so a pinned cell still follows its row's state. The th keeps
     its own header fill from the .r360-table th rule. */
  .r360-table th.r360-pin-end,
  .r360-table td.r360-pin-end {
    position: sticky;
    right: 0;
    z-index: 1;
    box-shadow: -1px 0 0 var(--line-soft);
  }
  .r360-table td.r360-pin-end { background: var(--paper); }
}

.r360-table th.r360-selcol,
.r360-table td.r360-selcol {
  width: 32px;
  padding-right: 0;
}

/* Row states. Only rows that open something (class is-click) get the pointer and hover fill, so
   a read-only table doesn't pretend to be clickable. Selected rows get a faint brand wash --
   written as a solid colour-mix rather than a translucent rgba so pinned (sticky) cells stay
   opaque while the rest of the row scrolls under them. */
.r360-table tbody tr.is-click { cursor: pointer; }
.r360-table tbody tr.is-click:hover td { background: var(--paper-2); }
.r360-table tbody tr.is-selected td { background: color-mix(in srgb, var(--brand-bright) 6%, var(--paper)); }
.r360-table tbody tr:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }

/* Empty-table message cell, and the same message when a list renders no table at all. */
.r360-table td.r360-empty,
.r360-empty-note {
  text-align: center;
  color: var(--ink-4);
  padding: 24px;
  margin: 0;
}

/* Tablet/phone: the table stacks each row into its own bordered card (see the <=1200px block
   below). Inside a flush list card those row cards would sit on the card's own edges and draw a
   double border, so give the stacked table some inner padding. */
@media (max-width: 1200px) {
  .r360-card.r360-card--flush table.r360-table {
    padding: 12px 16px;
  }
}

/* List footer: "Showing 1-25 of 60" plus the pager, 44px tall, under a soft rule. Replaces the
   record count that used to sit in the page subtitle. */
.r360-list-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  padding: 0 16px;
  border-top: 1px solid var(--line-soft);
  font-family: var(--f-body);
  font-size: var(--fs-sub);
  color: var(--ink-3);
}
.r360-list-footer__pager {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-left: auto;
}
.r360-list-footer__size {
  display: flex;
  align-items: center;
  gap: 6px;
}
/* 28px pager controls: compact on purpose, inside a 44px-tall footer row. */
.r360-pager-size {
  height: 28px;
  padding: 0 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  font-size: 13px;
  color: var(--ink);
  background: #fff;
  cursor: pointer;
}
.r360-pager-btn {
  height: 28px;
  min-width: 28px;
  padding: 0 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: #fff;
  cursor: pointer;
  font-size: 14px;
  color: var(--ink);
  line-height: 1;
}
.r360-pager-btn:disabled { opacity: 0.35; cursor: default; }
.r360-pager-btn:not(:disabled):hover { border-color: var(--brand); color: var(--brand); }

/* Mobile work-order card (Today list) with priority bar */
.wo-card {
  display: flex;
  align-items: stretch;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  overflow: hidden;
  cursor: pointer;
  font-family: inherit;
  text-align: left;
  padding: 0;
}

.wo-card__bar {
  width: 5px;
  flex-shrink: 0;
}

.wo-card__body {
  flex: 1;
  padding: 13px 14px;
  min-width: 0;
}

.bar--low {
  background: var(--pri-low);
}

.bar--medium {
  background: var(--pri-medium);
}

.bar--high {
  background: var(--pri-high);
}

.bar--critical {
  background: var(--pri-critical);
}

/* --- Form field ----------------------------------------------------------- */
.field__label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-2);
  margin-bottom: 6px;
}

.field__input,
.input,
select.input,
textarea.input {
  width: 100%;
  padding: 11px 13px;
  font-family: inherit;
  font-size: 14px;
  color: var(--ink);
  background: var(--paper);
  border: 1.5px solid var(--line);
  border-radius: var(--r-sm);
  transition: border-color var(--tr-fast) ease, box-shadow var(--tr-fast) ease;
}

textarea.input {
  min-height: 62px;
  resize: none;
}

/* --- req-* form primitives (GLOBAL) --------------------------------------- */
/* Shared form styles used by many pages (Requests, Checklist templates, Requisitions,
   RCA, Notifications, Meter readings, …). These were originally only in Requests.razor.css,
   which is component-SCOPED — so every other page that reused the class names rendered
   unstyled inputs. Declaring them here, globally, styles all pages from one place. */
.req-title {
  font-family: 'Helvetica LT', 'Helvetica Neue', sans-serif;
  font-weight: 700;
  font-size: 34px;
  letter-spacing: -0.03em;
  color: var(--ink);
  margin: 0;
}
.req-subtitle { font-size: 15px; color: var(--ink-3); margin: 6px 0 22px; }
.req-kicker {
  font-family: var(--f-body);
  font-size: 12px;
  font-weight: 600;
  color: var(--text-meta);
  margin-bottom: 14px;
}
.req-label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  margin: 12px 0 6px;
}
/* UX 2026-09-23 P6-18: the requisition ACTIONS card's explanatory sentence (RequisitionActionHint),
   shown instead of controls when there's nothing to press. Muted so it reads as guidance, not a
   heading, matching the style already used for the "All lines converted"/over-limit messages
   elsewhere in the same card. */
.req-hint {
  font-size: 14px;
  color: var(--ink-3);
  margin: 0 0 10px;
}
/* Red "*" marking a required field (used by Field.razor's <span class="r360-required">
   and by raw-label forms). Kept small and non-bold so it reads as a marker, not shouting.
   Uses --danger (5.8:1 on white) rather than the old #e5484d (3.9:1, failed AA) so validation,
   Danger buttons and danger pills are one red. */
.r360-required {
  color: var(--danger);
  margin-left: 2px;
  font-weight: 700;
}
/* One-line "what the asterisk means" legend shown atop create forms. */
.r360-required-legend {
  font-size: 12px;
  color: #5a6a7e;
  margin: 0 0 12px;
}
.r360-required-legend .r360-required {
  margin-left: 0;
  margin-right: 3px;
}
/* Inline field-level validation (Field.razor Error param). The control is outlined red and a
   small message is shown below it; all errors stay visible until the next validate. The
   descendant selectors reach the inner control (.r360-input is global, incl. the Combo input). */
.r360-field--error .r360-input,
.r360-field--error .r360-field__input,
.r360-field--error .r360-combo__input {
  border-color: var(--danger);
  box-shadow: 0 0 0 1px rgba(229, 72, 77, 0.25);
}
.r360-field__error {
  display: block;
  font-size: 12px;
  color: var(--danger);
  margin-top: 4px;
}
/* Same inline-error message for raw-label forms (req-/pm-/ven-) that don't use Field. */
.r360-input-error {
  display: block;
  font-size: 12px;
  color: var(--danger);
  margin-top: 4px;
}
.req-input.input-error,
.pm-input.input-error,
.r360-input.input-error {
  border-color: var(--danger);
  box-shadow: 0 0 0 1px rgba(229, 72, 77, 0.25);
}
.req-input {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 10px 12px;
  font-size: 14px;
  color: var(--ink);
  background: #fff;
  box-sizing: border-box;
  font-family: inherit;
}
.req-input:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 4px var(--focus-halo);
}
.req-textarea { resize: vertical; font-family: inherit; }
/* Native <select> chevron so dropdowns match the text inputs. */
select.req-input {
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  padding-right: 30px;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238b97a8' stroke-width='2'><path d='M6 9l6 6 6-6'/></svg>");
  background-repeat: no-repeat;
  background-position: right 10px center;
}
.req-submit {
  margin-top: 18px;
  width: 100%;
  background: var(--brand);
  color: #fff;
  border: none;
  border-radius: var(--r-lg);
  padding: 11px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: background .15s;
}
/* Hover darkens the customer's brand by 15% (the base fill is already --brand, so the old
   literal #047ab5 hover would have been the same colour). color-mix keeps it per-customer. */
.req-submit:hover:not(:disabled) { background: color-mix(in srgb, var(--brand-deep) 85%, #000); }
.req-submit:disabled { opacity: .5; cursor: not-allowed; }

/* Save Guard (docs/superpowers/specs/2026-08-29-save-guard-connectivity-resilience-design.md):
   visual states for a disconnect-safe Save/Create control. Pending = clicked, waiting on the
   server; Failed = no acknowledgment arrived within the timeout window. The message span reuses
   .r360-field__error's typography (display:block; font-size:12px; color:var(--danger)) via its own id
   rather than duplicating the rule. */
.r360-save-pending {
  opacity: 0.65;
  cursor: wait;
}
.r360-save-failed {
  outline: 2px solid var(--danger);
  outline-offset: 1px;
}

/* --- Side drawer / sheet -------------------------------------------------- */
.scrim {
  position: fixed;
  inset: 0;
  background: var(--scrim);
  z-index: 80;
  display: flex;
}

.drawer {
  width: min(var(--drawer-w), 100%);
  height: 100%;
  margin-left: auto;
  background: var(--paper);
  display: flex;
  flex-direction: column;
  box-shadow: var(--shadow-drawer);
  animation: r360slideX 0.25s var(--ease-house);
}

.drawer__header {
  position: sticky;
  top: 0;
  background: var(--brand-ink);
  color: #fff;
  padding: 18px 20px;
}

.drawer__body {
  padding: 20px;
  overflow: auto;
  flex: 1;
}

/* Mobile bottom sheet variant */
.sheet {
  width: 100%;
  margin-top: auto;
  background: var(--paper);
  border-radius: var(--r-sheet) var(--r-sheet) 0 0;
  padding: 20px 18px 30px;
  animation: r360sheet 0.28s var(--ease-house);
}

.sheet__grabber {
  width: 38px;
  height: 4px;
  border-radius: var(--r-pill);
  background: var(--line);
  margin: 0 auto 16px;
}

/* --- Toast ---------------------------------------------------------------- */
.toast {
  position: fixed;
  bottom: 26px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 90;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  background: var(--ink);
  color: #fff;
  padding: 13px 20px;
  border-radius: var(--r-lg);
  font-size: 13px;
  font-weight: 500;
  box-shadow: var(--shadow-toast);
  animation: r360fade 0.25s ease;
}

.toast__dot {
  width: 8px;
  height: 8px;
  border-radius: var(--r-pill);
  background: var(--toast-dot);
  flex-shrink: 0;
}

/* --- Hero (navy stripe with radial glow) ---------------------------------- */
.hero {
  background: var(--brand-ink);
  color: #fff;
  position: relative;
  overflow: hidden;
  padding: 22px 20px;
}

.hero__glow {
  position: absolute;
  right: -70px;
  top: -70px;
  width: 220px;
  height: 220px;
  border-radius: var(--r-pill);
  background: radial-gradient(circle at 30% 30%, color-mix(in srgb, var(--brand-bright) 50%, transparent), transparent 60%);
  pointer-events: none;
  z-index: 0; /* sits behind the content */
}

/* Lift content above the glow. Exclude .hero__glow itself: it's also a direct child,
   and this rule (defined after .hero__glow) would otherwise override its
   position:absolute with position:relative, dropping the glow into normal flow on top. */
.hero > *:not(.hero__glow) {
  position: relative;
  z-index: 1;
}

/* The ONE allowed gradient — resume banner */
.resume-banner {
  background: var(--gradient-resume);
  color: #fff;
  border-radius: var(--r-2xl);
}

/* Inset card on a dark surface (translucent fill) */
.dark-inset {
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: var(--r-md);
  padding: 10px 12px;
}

/* --- Icon tile (asset / document glyph square) ---------------------------- */
.icon-tile {
  width: 40px;
  height: 40px;
  border-radius: var(--r-sm);
  background: var(--icon-tile);
  display: grid;
  place-items: center;
  flex-shrink: 0;
  color: var(--brand-tile);
}

.icon-svg {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --- Mobile bottom tab bar ------------------------------------------------ */
.tabbar {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  background: var(--brand-ink);
  display: flex;
  padding: 8px 8px 26px;
  z-index: 30;
}

.tabbar__btn {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  background: transparent;
  border: 0;
  padding: 7px 0;
  cursor: pointer;
  color: rgba(255, 255, 255, 0.55);
  font-family: inherit;
}

  .tabbar__btn span {
    font-size: var(--fs-tab);
    font-weight: 600;
    font-family: var(--f-mono);
  }

  .tabbar__btn.is-active {
    color: var(--splash-lime);
  }

/* --- Help chip (fixed, live indicator) ------------------------------------ */
.help-chip {
  position: fixed;
  bottom: 22px;
  right: 22px;
  background: var(--brand-ink);
  color: #fff;
  border-radius: var(--r-sm);
  padding: 11px 16px;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  box-shadow: var(--shadow-toast);
  cursor: pointer;
  transition: background var(--tr-fast) ease;
}

.help-chip__live {
  width: 8px;
  height: 8px;
  border-radius: var(--r-pill);
  background: var(--splash-lime);
  animation: r360pulse 2s ease-in-out infinite;
}

/* --- Auth pages (Login, MFA, Forgot password, Set password) ---------------- */
/* UX 2026-09-23 P3-04/P3-10: one style set instead of per-page inline styles. Colours use the
   AA-safe brand tokens; raw #0599e1 is 3.16:1 on white and fails WCAG AA for text. */
.r360-auth-card { width: 100%; max-width: 400px; background: #fff; border: 1px solid var(--line); border-radius: var(--r-xl); padding: 28px; }
.r360-auth-logo { max-height: 96px; max-width: 100%; display: block; margin-bottom: 16px; }
.r360-auth-title { font-family: var(--f-display); font-size: 22px; font-weight: 700; letter-spacing: -0.02em; color: var(--ink); margin: 0 0 16px; }
.r360-auth-lead { font-size: 14px; color: var(--ink); margin: 0 0 14px; }
.r360-auth-form { display: flex; flex-direction: column; gap: 12px; }
.r360-auth-label { font-size: 13px; font-weight: 600; color: var(--ink); margin-bottom: -6px; }
.r360-auth-input { border: 1.5px solid var(--line); border-radius: var(--r-sm); padding: 12px; font-size: 14px; color: var(--ink); background: #fff; width: 100%; box-sizing: border-box; }
.r360-auth-input:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 4px var(--focus-halo); }
.r360-auth-btn { min-height: var(--hit-min); background: var(--brand); color: #fff; border: none; border-radius: var(--r-sm); padding: 12px; font-size: 14px; font-weight: 600; cursor: pointer; }
.r360-auth-btn:hover { filter: brightness(0.92); }
.r360-auth-btn[disabled] { opacity: 0.7; cursor: progress; }
.r360-auth-alert { background: #fdecea; color: #b3261e; font-size: 13px; border-radius: var(--r-lg); padding: 10px; margin-bottom: 14px; }
.r360-auth-alert--ok { background: #eafaf0; color: #1a7f37; }
.r360-auth-link { color: var(--brand); display: inline-block; padding: 12px 0; font-size: 13px; }
.r360-auth-hint { font-size: 12.5px; color: var(--ink-4); }

/* 7. STATES ----------------------------------------------------------------- */
.btn--primary:hover {
  background: var(--brand-deep);
}

.btn--ghost:hover {
  background: var(--paper-2);
}

.btn:active {
  transform: translateY(1px);
}

.help-chip:hover {
  background: #06182D;
}

/* Tiles lift on hover */
.tile, .wo-card, .datarow {
  transition: transform var(--tr-fast) ease, box-shadow var(--tr-fast) ease, border-color var(--tr-fast) ease;
}

  .tile:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-2);
    border-color: var(--brand);
  }

  .datarow:hover {
    background: var(--paper-2);
  }

/* Universal focus treatment — 1.5px brand border + 4px halo */
.field__input:focus,
.input:focus,
input:focus,
select:focus,
textarea:focus {
  outline: 0;
  border-color: var(--brand);
  box-shadow: 0 0 0 4px var(--focus-halo);
}

/* 8. ANIMATIONS ------------------------------------------------------------- */
@keyframes r360fade {
  from {
    opacity: 0;
    transform: translateY(8px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes r360sheet {
  from {
    transform: translateY(100%);
  }

  to {
    transform: translateY(0);
  }
}

@keyframes r360slideX {
  from {
    transform: translateX(100%);
  }

  to {
    transform: translateX(0);
  }
}

@keyframes r360scan {
  0% {
    top: 8%;
  }

  50% {
    top: 88%;
  }

  100% {
    top: 8%;
  }
}

@keyframes r360pulse {
  0%, 100% {
    box-shadow: 0 0 0 3px rgba(230, 244, 122, 0.4);
  }

  50% {
    box-shadow: 0 0 0 6px rgba(230, 244, 122, 0);
  }
}

/* Accessibility audit [A9]: .help-chip__live and .scan-line run these two animations forever with
   no prefers-reduced-motion guard — a problem for vestibular-sensitive users and for anything
   glanced at on a wall-mounted dashboard. ReportCard.razor.css already respects this media query
   for its own pending-state animation; apply the same pattern here. */
@media (prefers-reduced-motion: reduce) {
  .help-chip__live {
    animation: none;
  }

  .scan-line {
    animation: none;
  }
}

/* Progress fills — slightly snappy */
.progress__fill {
  transition: width 0.5s var(--ease-progress);
}

/* 9. UTILITIES & PLACEHOLDERS ---------------------------------------------- */
.fade-up {
  animation: r360fade 0.25s var(--ease-house) both;
}

/* Diagonal-stripe placeholders for missing imagery (crop 4:3) */
.img-placeholder {
  background: repeating-linear-gradient(135deg, var(--paper-2) 0 14px, var(--paper-3) 14px 28px);
}

.img-placeholder--dark {
  background: repeating-linear-gradient(135deg, var(--brand-ink) 0 14px, #0E3A66 14px 28px);
}

.img-placeholder--sky {
  background: repeating-linear-gradient(135deg, #EAF6FE 0 14px, #D6EFFF 14px 28px);
}

/* QR scanner corner brackets (splash-lime on dark) */
.scan-corner {
  position: absolute;
  width: 34px;
  height: 34px;
}

.scan-corner--tl {
  top: 0;
  left: 0;
  border-top: 3px solid var(--splash-lime);
  border-left: 3px solid var(--splash-lime);
  border-radius: var(--r-lg) 0 0 0;
}

.scan-corner--tr {
  top: 0;
  right: 0;
  border-top: 3px solid var(--splash-lime);
  border-right: 3px solid var(--splash-lime);
  border-radius: 0 var(--r-lg) 0 0;
}

.scan-corner--bl {
  bottom: 0;
  left: 0;
  border-bottom: 3px solid var(--splash-lime);
  border-left: 3px solid var(--splash-lime);
  border-radius: 0 0 0 var(--r-lg);
}

.scan-corner--br {
  bottom: 0;
  right: 0;
  border-bottom: 3px solid var(--splash-lime);
  border-right: 3px solid var(--splash-lime);
  border-radius: 0 0 var(--r-lg) 0;
}

.scan-line {
  position: absolute;
  left: 8%;
  right: 8%;
  height: 2px;
  background: var(--splash-lime);
  box-shadow: 0 0 12px 2px rgba(230, 244, 122, 0.7);
  animation: r360scan 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}

/* Hidden scrollbar (scroll regions inside the mobile frame) */
.r360-scroll::-webkit-scrollbar {
  width: 0;
}

/* Reserve space at the bottom of the mobile content so the last element never
   sits under the fixed tab bar. Height = tabbar padding (8 + 26 = 34px) + icon
   row (~40px) + breathing room → 80px base + iOS safe area. */
.r360-mobile-shell {
    padding-bottom: calc(80px + env(safe-area-inset-bottom, 0px));
}

/* --- Mobile tab bar ------------------------------------------------------- */
.r360-tabbar {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    background: var(--brand-ink) !important;
    display: flex !important;
    padding: 8px 8px 26px !important;
    z-index: 30 !important;
    border: none !important;
    box-shadow: none !important;
}

/* ── Viewport tiers ────────────────────────────────────────────────────────
   PHONE   (<= 900px)  purpose-built layouts + the bottom tab bar for technicians. The only
                       rule in this file that actually enforces this boundary is the tab bar's
                       own @media (min-width: 901px) hide, immediately below -- there is no
                       separate 768px gate anywhere in app.css or the component .razor.css
                       files (confirmed by grep); don't assume one exists just because 768px
                       is a common device-width convention elsewhere.
   TABLET  (<= 1200px) desktop chassis, but the sidebar becomes a drawer and dense
                       tables become stacked cards. Raised from 900px: Task 2's baseline
                       measurement (_qa/responsive/before/results.json) showed /work-orders
                       at 1024px with NO document-level horizontal overflow, but a crushed
                       177px first-row height (vs. 61-122px on other routes) because the
                       card-stacking layout and the MudBlazor table's overflow-x:auto safety
                       net were both gated behind max-width:900px, which never fires at a
                       1024px tablet viewport -- the sidebar stayed pinned (267px) and the
                       desktop table's cells wrapped their content into tall rows instead of
                       either stacking into cards or scrolling horizontally.
   DESKTOP (>  1200px) pinned sidebar, full tables.
   The drawer, the hamburger and the card layout MUST share one breakpoint: cards beside
   a pinned sidebar is worse than the desktop table.
   ──────────────────────────────────────────────────────────────────────── */

/* The bottom tab bar stays phone-only and deliberately keeps its own 901px gate below instead
   of following the sidebar/table tier up to 1200px: a technician on a 1024px tablet should get
   the drawer chassis, not a phone shell. This is now the ONLY nav rule still gated at 900px --
   .sidenav's own hide (see the RESPONSIVE section further down) moved to 1200px alongside it,
   so the two navs no longer mirror the same breakpoint the way they used to. */
@media (min-width: 901px) {
  .r360-tabbar {
    display: none !important;
  }
}

/* --- Mobile hamburger (non-Technician drawer trigger) --------------------- */
/* Accessibility audit [A8]: 36x36 is below the 44x44 minimum touch target for gloved hands /
   reduced motor control. Bumped the hit area via width/height; icon itself is unchanged since the
   SVG inside is sized independently and stays centered by the existing flex alignment. */
.ph-menu-btn {
    display: none;
    align-items: center;
    justify-content: center;
    width: var(--hit-min);
    height: var(--hit-min);
    border: none;
    background: transparent;
    color: var(--ink);
    cursor: pointer;
    padding: 0;
    margin-right: 8px;
    flex-shrink: 0;
}

@media (max-width: 1200px) {
  .ph-menu-btn {
    display: inline-flex;
  }
}

.r360-tab-item {
    flex: 1 !important;
    display: flex !important;
    flex-direction: column !important;
    align-items: center !important;
    gap: 4px !important;
    background: transparent !important;
    border: none !important;
    padding: 7px 0 !important;
    cursor: pointer !important;
    color: rgba(255, 255, 255, 0.55) !important;
    font-family: inherit !important;
    text-decoration: none !important;
    min-height: auto !important;
    min-width: 0 !important;
}

.r360-tab-item span {
    font-size: 11px !important;
    font-weight: 600 !important;
    font-family: var(--f-mono) !important;
    white-space: nowrap !important;
}

.r360-tab-item.is-active {
    color: var(--splash-lime) !important;
}

/* --- Form inputs (standalone) ----------------------------------------------- */
.r360-input {
    width: 100%;
    padding: 9px 11px;
    border: 1.5px solid var(--line);
    border-radius: var(--r-sm);
    font-size: var(--fs-input);
    background: var(--paper);
    color: var(--ink);
    font-family: inherit;
}

.r360-input:focus {
    outline: none;
    border-color: var(--brand);
    box-shadow: 0 0 0 4px var(--focus-halo);
}

/* --- Combo (typeable/filterable dropdown) --------------------------------- */
/* Wrapper is positioned so the floating menu anchors to the input below it. */
.r360-combo {
    position: relative;
    width: 100%;
}

/* Floating list of options shown while the combo is open. */
.r360-combo__menu {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    z-index: 30;
    max-height: 260px;
    overflow-y: auto;
    background: var(--paper);
    border: 1.5px solid var(--line);
    border-radius: var(--r-sm);
    box-shadow: 0 8px 24px rgba(14, 23, 38, 0.12);
    padding: 4px;
}

/* One selectable option row. */
.r360-combo__opt {
    padding: 8px 10px;
    border-radius: var(--r-sm);
    font-size: 14px;
    color: var(--ink);
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.r360-combo__opt:hover {
    background: var(--wash, rgba(14, 23, 38, 0.05));
}

/* Keyboard-highlighted option (ArrowUp/ArrowDown on the combobox input) -- the visual equivalent
   of :hover but driven by aria-activedescendant/tabindex="-1" navigation rather than the mouse,
   since the options themselves are never individually focused (see Combo.razor's OnKeyDown). */
.r360-combo__opt--active {
    background: var(--wash, rgba(14, 23, 38, 0.05));
}

/* "No matches" placeholder row. */
.r360-combo__empty {
    padding: 8px 10px;
    font-size: 13px;
    color: var(--muted, #64748b);
}

/* --- Card with flush styling ----------------------------------------------- */
.r360-card {
    background: var(--surface-card);
    border: 1px solid var(--line);
    border-radius: var(--r-2xl);
    padding: 20px;
}

/* --- Sidebar navigation --------------------------------------------------- */
/* Nav group heading. Design system (Sep 28, 2026): groups no longer collapse, so this is a plain
   <h2> (not a button) and no longer needs a 44px touch height. Sep 29, 2026: sentence-case body
   text at 12/600 in place of the faint mono caps, so 18 + 17 (12px x 1.4) + 6 = 41px.
   margin/font-weight reset the browser's h2 defaults. */
.grp {
    display: flex;
    align-items: center;
    width: 100%;
    margin: 0;
    font-family: var(--f-body);
    font-size: 12px;
    font-weight: 600;
    line-height: 1.4;
    color: rgba(255, 255, 255, 0.75);
    padding: 18px 12px 6px;
}

/* Screen-reader-only text: visually removed but still read aloud (UX 2026-09-23 P1-05 -- the
   Reports "!" badge used to carry a hover-only title with no equivalent for screen readers). */
.visually-hidden { position:absolute !important; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0; }

.badge {
    background: rgba(255, 255, 255, 0.16);
    border-radius: var(--r-tag);
    padding: 2px 8px;
    font-size: 11px;
    font-weight: 700;
    font-family: var(--f-mono);
}

.badge.alert {
    background: var(--danger);
}

.nav {
    display: flex !important;
    align-items: center !important;
    gap: 12px !important;
    padding: 9px 12px !important;
    border-radius: var(--r-sm) !important;
    width: 100% !important;
    text-align: left !important;
    font-size: 14.5px !important;
    font-weight: 500 !important;
    color: rgba(255, 255, 255, 0.82) !important;
    text-decoration: none !important;
    cursor: pointer !important;
    transition: background var(--tr-fast) ease, color var(--tr-fast) ease !important;
    background: transparent !important;
    border: none !important;
    min-height: 40px !important;
}

/* Inset white ring: the navy sidebar makes the default brand-blue ring hard to see, and an
   outward offset would be clipped by the scrolling list. */
.nav:focus-visible {
    outline: 2px solid #fff;
    outline-offset: -2px;
}

.nav.active {
    background: var(--brand) !important;
    color: #fff !important;
    font-weight: 600 !important;
}

.nav:hover {
    background: rgba(255, 255, 255, 0.08) !important;
    color: #fff !important;
}

.nav .icon-svg {
    flex-shrink: 0;
    stroke: currentColor;
    fill: none;
    stroke-width: 1.9;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.va-wrap{display:flex;align-items:center;gap:7px}
.va-label{font-family:var(--f-body);font-size:12px;font-weight:600;color:var(--text-meta)}
.va-select{border:1px solid var(--line);border-radius:var(--r-sm);padding:5px 9px;font-size:12.5px;
  font-weight:600;color:var(--ink);background:#fff;cursor:pointer}
/* Notification bell: a 44x44 target with a transparent border that fills on hover, so the button
   reads as a button without adding chrome to the top bar at rest. The unread counter sits inside
   the corner of that square and uses the shared --danger red. */
.nf-bell{position:relative;background:transparent;border:1px solid transparent;border-radius:var(--r-sm);
  cursor:pointer;color:var(--ink-3);width:var(--hit-min);height:var(--hit-min);padding:0;display:grid;place-items:center}
.nf-bell:hover{background:var(--paper-2);border-color:var(--line)}
.nf-badge{position:absolute;top:6px;right:5px;background:var(--danger);color:#fff;font-size:9.5px;
  font-weight:700;border-radius:999px;min-width:15px;height:15px;display:grid;place-items:center;padding:0 3px}
.nf-kicker{font-family:var(--f-body);font-size:12px;font-weight:600;color:var(--text-meta)}
.nf-title{font-size:20px;font-weight:700;color:var(--ink);margin-top:4px}
.nf-list{display:flex;flex-direction:column;gap:10px;margin-top:14px}
.nf-item{display:flex;align-items:center;gap:10px;border:1px solid var(--line);border-radius:var(--r-lg);padding:11px}
/* Permits/Requisitions/RFQs list rows navigate to a detail page and were <div @onclick> with no
   href -- unreadable as a link by a screen reader, unreachable by keyboard, and un-openable in a
   new tab. Rendering them as <a> fixes all three; this reset keeps the anchor visually identical
   to the old div (no blue text, no underline) since .nf-item already supplies all the row chrome. */
a.nf-item{color:inherit;text-decoration:none}
.nf-tag{font-family:'JetBrains Mono',monospace;font-size:10px;border-radius:var(--r-tag);padding:3px 7px;white-space:nowrap}
.nf-tag.alert{background:#fdeaea;color:#e05353}
.nf-tag.info{background:#eaf6fd;color:var(--brand-tile)}
.nf-tag.success{background:#eafaf0;color:#0f9d58}
.nf-text{font-size:13px;color:var(--ink)}

/* 10. RESPONSIVE ------------------------------------------------------------ */
@media (max-width: 1200px) {
  /* Accessibility audit [A5]: transform alone moves the nav off-screen but its links stay in the
     tab order (tabindex 0, no aria-hidden) -- a keyboard user tabbing from page top walks through
     ~20 invisible links before reaching visible content, with no visible focus indicator since the
     element is off-screen. visibility:hidden removes the whole subtree from the accessibility tree
     and tab order without any Blazor/JS state to keep in sync with this same breakpoint; the
     transition delay keeps the slide-in/out animation working (hide only takes effect after the
     close transition finishes, so it doesn't cut the animation short). */
  .sidenav {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: 280px;
    max-width: 82vw;
    transform: translateX(-100%);
    visibility: hidden;
    transition: transform 0.25s ease, visibility 0s linear 0.25s;
    z-index: 50;
    overflow-y: auto;
  }
  .sidenav.is-open {
    transform: translateX(0);
    visibility: visible;
    transition: transform 0.25s ease;
  }
  /* Tablet/phone gutters live on .main-body alone. .main used to add 24px 18px 96px here on top
     of .main-body's desktop 30px 44px 96px, so a 390px phone lost 124px to side padding and
     reading pages (help topics especially) became a narrow strip down the middle. .main now
     carries no padding at any width, so the page header also runs edge to edge as on desktop;
     the 96px bottom still clears the fixed mobile tab bar. */
  .main-body {
    padding: 20px 16px 96px;
  }

  .card {
    padding: var(--s-6);
  }

  .display-1 {
    font-size: 32px;
  }

  .display-2 {
    font-size: 24px;
  }

  .help-chip {
    bottom: 84px;
    right: 12px;
  }
  /* clear the bottom bar */

  /* Safety net so MudTable-based pages (most desktop pages) scroll horizontally instead of
     blowing out the viewport at narrow widths -- not a redesign, just prevents breakage. */
  .mud-table-container {
    overflow-x: auto;
  }

  /* --- Mobile tables: stack each row into a labelled card --------------- */
  .r360-table,
  .r360-table tbody,
  .r360-table tr,
  .r360-table td {
    display: block;
    width: 100%;
  }
  .r360-table thead {
    display: none;
  }
  .r360-table tr {
    background: var(--paper);
    border: 1px solid var(--line);
    border-radius: var(--r-lg, 14px);
    margin-bottom: 10px;
    overflow: hidden;
  }
  .r360-table td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    text-align: right;
    padding: 10px 14px;
    border-bottom: 1px solid var(--line-soft);
  }
  .r360-table tr td:last-child {
    border-bottom: none;
  }
  /* Labelled cells: show the column name on the left via data-label */
  .r360-table td[data-label]::before {
    content: attr(data-label);
    font-family: var(--f-body);
    font-size: 12px;
    font-weight: 600;
    color: var(--ink-3);
    text-align: left;
    flex: 0 0 auto;
    white-space: nowrap;
  }
  /* Cells with no data-label: value spans the row, left-aligned */
  .r360-table td:not([data-label]) {
    justify-content: flex-start;
    text-align: left;
  }
}

@media print {
  body {
    background: #fff;
  }

  .card, .statcard {
    box-shadow: none !important;
  }

  .help-chip, .toast {
    display: none !important;
  }
}

/* ── Shared user / role-pill styles (used by Settings + SettingsUsers) ── */
.set-avatar {
    width: 38px;
    height: 38px;
    border-radius: 999px;
    background: var(--brand-ink);
    color: #fff;
    font-size: 12.5px;
    font-weight: 700;
    display: grid;
    place-items: center;
    flex-shrink: 0;
}
.set-username { font-weight: 700; font-size: 14.5px; color: var(--ink); }
.set-useremail {
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 12px;
    /* Accessibility audit [A3]: was hardcoded #8b97a8 (2.96:1) instead of referencing the token --
       real content (actual user emails), needs 4.5:1. */
    color: var(--ink-4);
    margin-top: 1px;
}
.set-roleselect { padding: 5px 8px; }
.set-rolepill {
    border: none;
    cursor: pointer;
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 11.5px;
    font-weight: 600;
    padding: 5px 12px;
    border-radius: var(--r-tag);
    flex-shrink: 0;
}
.role-admin     { background: #fbdcd7; color: var(--danger); }
.role-planner   { background: #d6efff; color: var(--info); }
.role-manager   { background: #e9e3fb; color: #6b46c1; }
.role-tech      { background: #d8f1e3; color: var(--success); }
.role-requester { background: #eaeef6; color: #2c3a4f; }

/* ── Shared settings form styles (Settings + SettingsUsers) ── */
.set-title {
    font-size: 34px;
    font-weight: 800;
    color: var(--ink);
    margin: 0 0 4px;
}

.set-card-sub {
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 11px;
    /* Accessibility audit [A3]: same hardcoded ink-4 value as .set-useremail immediately above in
       this Settings section -- deepened for the same reason (real content, not decoration). */
    color: var(--ink-4);
    margin-top: 3px;
}

/* Buttons */
.set-btn {
    border: none;
    border-radius: var(--r-sm);
    font-weight: 600;
    font-size: 13.5px;
    padding: 9px 15px;
    cursor: pointer;
    white-space: nowrap;
}

.set-btn--primary { background: var(--brand); color: #fff; }
/* Hover: --brand darkened 15% so it tracks the customer's colour (see .req-submit:hover). */
.set-btn--primary:hover { background: color-mix(in srgb, var(--brand-deep) 85%, #000); }
.set-btn--ghost { background: #fff; color: var(--ink); border: 1px solid var(--line); }
.set-btn--ghost:hover { background: #f5f7fb; }

/* Inputs */
.set-inp {
    font-family: inherit;
    font-size: 14px;
    color: var(--ink);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    padding: 8px 11px;
    outline: none;
    background: #fff;
}
.set-inp:focus { border-color: var(--brand); box-shadow: 0 0 0 4px var(--focus-halo); }

/* UX 2026-09-23 P3-13: a section heading inside a page (e.g. "Two-factor authentication" on
   /account/password) must read smaller than the 24px ActionBar page title above it, not larger
   like the old 34px .set-title it used to borrow. */
.acct-section-title {
    font-size: 18px;
    font-weight: 700;
    color: var(--ink);
    margin: 28px 0 8px;
}

/* UX 2026-09-23 P3-R7: real <form> wrappers so Enter submits (instead of type="button" @onclick).
   Reuses the same vertical-stack layout the cards already had inline. */
.acct-form {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.set-invite {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 14px;
    padding: 14px;
    background: #f5f7fb;
    border-radius: var(--r-lg);
}

/* =============================================================================
   11. RECONNECTION UI (Task 26 — mobile field-tech resilience to dropped connection)
   Styles the #components-reconnect-modal div that blazor.web.js injects/toggles when the
   SignalR circuit to a Blazor Server page drops. Base state is hidden; blazor.web.js adds
   exactly one of these classes to the modal root as the circuit blips, retries, or gives up:
     .components-reconnect-show     — circuit lost, actively retrying (transient — the common case
                                       for a tech losing a beat of cell signal in a mechanical room)
     .components-reconnect-hide     — connected/reconnected — modal stays hidden
     .components-reconnect-failed   — retries exhausted, one more manual attempt is possible
     .components-reconnect-rejected — server rejected resume (e.g. app restarted) — hard reload needed
   Only one sub-message (.r360-reconnect-sub--*) is shown at a time via the state-scoped selectors
   below; the retry button is hidden unless we're in a failed/rejected state (in "show" state
   blazor.web.js is already retrying automatically, so a manual button would be redundant noise).
============================================================================= */
#components-reconnect-modal {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 9999;
    align-items: center;
    justify-content: center;
    background: color-mix(in srgb, var(--brand-ink) 55%, transparent); /* --brand-ink wash, dims the app behind the overlay */
    font-family: var(--f-body);
}

/* blazor.web.js toggles these on the root — only "show" and "failed"/"rejected" render anything. */
#components-reconnect-modal.components-reconnect-show,
#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-rejected {
    display: flex;
}
#components-reconnect-modal.components-reconnect-hide {
    display: none;
}

.r360-reconnect-box {
    background: var(--paper);
    border-radius: var(--r-2xl);
    padding: 28px 24px;
    max-width: 320px;
    width: calc(100% - 48px);
    text-align: center;
    box-shadow: 0 20px 48px rgba(14, 23, 38, 0.28);
}

.r360-reconnect-spinner {
    width: 34px;
    height: 34px;
    margin: 0 auto 14px;
    border-radius: 50%;
    border: 3px solid var(--paper-3);
    border-top-color: var(--brand);
    animation: r360-spin 0.8s linear infinite;
}
/* Failed/rejected are terminal, non-retrying states — a static warning dot reads calmer than a
   spinner that implies "still working" when it isn't. */
.components-reconnect-failed .r360-reconnect-spinner,
.components-reconnect-rejected .r360-reconnect-spinner {
    animation: none;
    border-top-color: var(--warning);
}

@keyframes r360-spin {
    to { transform: rotate(360deg); }
}

.r360-reconnect-title { font-size: 16px; font-weight: 700; color: var(--ink); }

.r360-reconnect-sub {
    display: none; /* each sub-message only appears in its matching modal state, via the rules below */
    font-size: 13px;
    color: var(--ink-3);
    margin-top: 8px;
    line-height: 1.4;
}
.components-reconnect-show .r360-reconnect-sub--show,
.components-reconnect-failed .r360-reconnect-sub--failed,
.components-reconnect-rejected .r360-reconnect-sub--rejected {
    display: block;
}

/* NOTE: MudBlazor's own stylesheet ships a rule targeting this exact modal
   (#components-reconnect-modal button { color: var(--mud-palette-text-primary) }); a plain class
   selector loses that cascade fight (ID+element beats a single class), leaving white text
   invisible against the dark navy button. Prefixing with the modal's #id here matches that
   specificity and wins via source order (this stylesheet loads after MudBlazor's in App.razor). */
#components-reconnect-modal .r360-reconnect-retry {
    display: none; /* only meaningful once auto-retry has given up — overridden below per state */
    margin-top: 16px;
    background: var(--brand-ink);
    color: #fff;
    border: none;
    border-radius: var(--r-sm);
    padding: 10px 20px;
    font-size: 14px;
    font-weight: 600;
    text-transform: none; /* override MudBlazor's default button uppercase transform */
    cursor: pointer;
}
/* Same #id-scoped specificity as the base rule above (an unscoped ".foo .bar" pair would lose to
   it and stay display:none forever) — shows the button only once auto-retry has given up. */
#components-reconnect-modal.components-reconnect-failed .r360-reconnect-retry,
#components-reconnect-modal.components-reconnect-rejected .r360-reconnect-retry {
    display: inline-block;
}
.set-invite .set-inp { flex: 1; min-width: 120px; }

/* --- Mobile drawer: close button + backdrop ------------------------------- */
.sidenav-close {
    display: none;
}

@media (max-width: 1200px) {
  /* Accessibility audit [A8]: 30x30 is below the 44x44 minimum touch target. */
  .sidenav-close {
    display: flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    top: 12px;
    right: 12px;
    width: var(--hit-min);
    height: var(--hit-min);
    border: none;
    background: rgba(255, 255, 255, 0.08);
    border-radius: var(--r-sm);
    color: rgba(255, 255, 255, 0.82);
    cursor: pointer;
    padding: 0;
    /* Above the logo plate, which used to paint over it and hide the X. */
    z-index: 1;
  }
  /* Keep the logo clear of the close button: 12px inset + 44px button + 8px breathing room. */
  .sidenav-logo {
    padding-right: 64px !important;
  }
}

.r360-drawer-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(11, 18, 32, 0.45);
    z-index: 45;
}

/* ---- In-app help ------------------------------------------------------------------------------
   Styling for the /help, /help/{role} and /help/{role}/{slug} pages (HelpRole.razor and
   HelpTopicPage.razor) plus the shared block renderer (HelpBlocks.razor / HelpSpans.razor).

   These rules are GLOBAL rather than Blazor scoped CSS for two reasons: (1) there is no single
   "Help.razor" component to own a scoped stylesheet -- the section is two page components
   (HelpRole, HelpTopicPage) plus a shared child (HelpBlocks) -- and (2) even a scoped stylesheet
   on one of those pages would not reach the markup HelpBlocks renders, because Blazor's scoped
   CSS does not propagate a parent's scope attribute into a child component's own output (the same
   gotcha this codebase already hit with MudBlazor children). Every class below is help-prefixed,
   which is what keeps this safe to declare globally instead.

   Uses the app's own design tokens (--paper, --line, --ink, --brand) rather than hard-coded hex,
   so it follows the branding options like every other page. */

.help-switcher {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin: 12px 0 20px;
}

.help-role {
    display: inline-flex;
    align-items: center;
    min-height: var(--hit-min);              /* 44px hit area -- accessibility remediation A4 */
    padding: 0 14px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    text-decoration: none;
    color: var(--ink);
}

.help-role.is-current {
    background: var(--brand);
    border-color: var(--brand);
    color: #fff;
}

.help-index {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 10px;
}

.help-card {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-height: var(--hit-min);
    padding: 14px 16px;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    background: var(--paper);
    text-decoration: none;
    color: var(--ink);
}

.help-card:hover,
.help-card:focus-visible {
    border-color: var(--brand);
}

.help-card-title {
    font-weight: 600;
}

.help-card-summary {
    font-size: 0.9rem;
    opacity: 0.8;
}

.help-article {
    max-width: 68ch;               /* prose measure -- long lines are the main readability failure */
    color: var(--text-body);
    line-height: 1.65;
}

/* The goto link that ends a topic ("Open requisitions"): a filled brand tile rather than a bare
   link, because it is the reader's natural next step. It navigates, so it stays an <a>. */
.help-goto {
    margin: 32px 0 0;
    padding-top: 20px;
    border-top: 1px solid var(--line);
}

.help-goto-link {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    min-height: var(--hit-min);
    padding: 0 18px;
    border-radius: var(--r-lg);
    background: var(--brand);
    color: var(--text-on-dark);
    font-weight: 600;
    text-decoration: none;
}

.help-goto-link:hover,
.help-goto-link:focus-visible {
    background: var(--brand-ink);
    color: var(--text-on-dark);
}

@media (prefers-reduced-motion: no-preference) {
    .help-goto-arrow { transition: transform 0.15s ease; }
    .help-goto-link:hover .help-goto-arrow { transform: translateX(3px); }
}

.help-pdf-link,
.help-back {
    display: inline-flex;
    align-items: center;
    min-height: var(--hit-min);
    padding: 0 12px;
}

.help-empty {
    opacity: 0.8;
}

/* ---- Block styles rendered by the HelpBlocks child component ----------------------------------
   See the file-level "why" comment above for why these live here instead of a scoped stylesheet
   on a page component. The global reset zeroes <p> margins, so paragraph spacing is set here
   explicitly -- without it consecutive paragraphs ran together into one wall of text. */

.help-p { margin: 0 0 14px; }

/* The opening summary line ({.lead}): larger, softer, and ruled off from the body so it reads as
   an introduction rather than a slightly bold first paragraph. */
.help-p.lead {
    margin-bottom: 22px;
    padding-bottom: 18px;
    border-bottom: 1px solid var(--line);
    font-size: 17px;
    line-height: 1.55;
    color: var(--text-label);
}

/* Section headings ("## " / "### "). The topic title in ActionBar is the h1. */
.help-h2,
.help-h3 {
    font-family: var(--f-display);
    color: var(--text-body);
    letter-spacing: -0.01em;
}

.help-h2 {
    margin: 32px 0 10px;
    font-size: 19px;
    font-weight: 700;
    line-height: 1.3;
}

.help-h3 {
    margin: 24px 0 8px;
    font-size: 16px;
    font-weight: 600;
    line-height: 1.35;
}

/* A heading straight after the lead (or at the very top) doesn't need the full gap above it. */
.help-article > .help-h2:first-child,
.help-article > .help-h3:first-child,
.help-p.lead + .help-h2,
.help-p.lead + .help-h3 {
    margin-top: 0;
}

/* Inline formatting inside any help block. <code> otherwise falls back to the browser's default
   monospace at full size, which looks like a rendering fault. */
.help-article strong {
    font-weight: 600;
    color: var(--text-body);
}

.help-article code {
    padding: 1px 6px;
    border-radius: var(--r-xs);
    background: var(--paper-3);
    color: var(--text-label);
    font-family: var(--f-mono);
    font-size: 0.86em;
}

/* Bulleted lists: small brand dots instead of the browser's default discs. */
.help-list {
    margin: 0 0 18px;
    padding: 0;
    list-style: none;
}

ul.help-list li {
    position: relative;
    margin-bottom: 8px;
    padding-left: 22px;
}

ul.help-list li::before {
    content: "";
    position: absolute;
    left: 6px;
    top: 0.68em;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--brand-bright);
}

/* Numbered lists are how-to steps: each number sits in a filled circle, and a thin rail joins
   one step to the next. The <ol> keeps its list semantics -- only the visual numbering changes,
   via a CSS counter. */
ol.help-list {
    counter-reset: help-step;
}

ol.help-list li {
    position: relative;
    counter-increment: help-step;
    min-height: 28px;
    padding: 2px 0 16px 44px;
}

ol.help-list li::before {
    content: counter(help-step);
    position: absolute;
    left: 0;
    top: 0;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: var(--brand);
    color: var(--text-on-dark);
    font-family: var(--f-display);
    font-size: 14px;
    font-weight: 700;
    line-height: 28px;
    text-align: center;
}

/* The rail between step circles; the last step has nothing below it to join. */
ol.help-list li:not(:last-child)::after {
    content: "";
    position: absolute;
    left: 13px;
    top: 32px;
    bottom: 2px;
    width: 2px;
    border-radius: 1px;
    background: var(--line);
}

/* Call-outs: a tinted box with a labelled icon ("Note" / "Important") so the kind of aside is
   stated in words, not only by colour. Tints are mixed from the tokens so branding still applies. */
.help-callout {
    margin: 22px 0;
    padding: 12px 16px 14px;
    border: 1px solid color-mix(in srgb, var(--brand) 22%, var(--paper));
    border-left: 4px solid var(--brand);
    border-radius: var(--r-lg);
    background: color-mix(in srgb, var(--brand) 6%, var(--paper));
}

.help-callout.warn {
    border-color: color-mix(in srgb, var(--danger) 25%, var(--paper));
    border-left-color: var(--danger);
    background: color-mix(in srgb, var(--danger) 6%, var(--paper));
}

.help-callout-label {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0 0 4px;
    color: var(--brand);
    font-size: var(--fs-label);
    font-weight: 700;
    letter-spacing: 0.02em;
}

.help-callout.warn .help-callout-label {
    color: var(--danger);
}

.help-callout-body {
    margin: 0;
}

/* Help screenshots. Desktop captures are 1280px wide and phone captures 780px (2x), so both are
   capped to the text column; the height cap stops a tall phone capture filling a whole laptop
   screen. Each sits in a white mat with a soft shadow so it reads as a framed picture of the app,
   not part of the page around it. */
.help-figure {
    margin: 24px 0;
    padding: 8px;
    border: 1px solid var(--line);
    border-radius: var(--r-xl);
    background: var(--surface-card);
    box-shadow: var(--shadow-2);
}

.help-figure img {
    display: block;
    max-width: 100%;
    max-height: 560px;
    width: auto;
    height: auto;
    margin: 0 auto;
    border: 1px solid var(--line-soft);
    border-radius: var(--r-sm);
}

.help-figure figcaption {
    margin-top: 8px;
    font-size: 13px;
    color: var(--text-help);
    text-align: center;
}

/* Phones: the fixed 560px cap is nearly the whole space between the page header and the bottom
   tab bar, so a phone capture filled the screen with the caption and the next step hidden below
   it -- it looked like the end of the page and readers didn't know to scroll. Capping to 40% of
   the viewport height keeps the caption and the start of the next step in view. */
@media (max-width: 900px) {
    .help-figure img {
        max-height: 40vh;
    }
}

/* Spare-part create/edit form layout, shared by /inventory/new and the part detail page's
   editable sections. Mirrors the asset form's responsive auto-fit grid. */
.inv-form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 16px;
}

.inv-form-check {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 10px;
  font-size: 13px;
  color: #4a5568;
}

.inv-form-hint {
  margin: 4px 0 10px;
  font-size: 12.5px;
  color: var(--ink-4);
}

/* Critical-spare marker on the parts list. A dot rather than a column: critical spares are a
   minority, and a column of "No" would cost more width than it conveys. */
.inv-critical-dot {
  margin-left: 6px;
  color: var(--danger);
  font-size: 11px;
}

/* Record-page tab bars wrap onto a second row instead of scrolling (UX 2026-09-23 P1-10).
   MudBlazor 9.5 has no wrap option: its wrapper is width:max-content inside an overflow:hidden
   content box, and it scrolls with a transform. All three are overridden here. */
.r360-tabs-wrap .mud-tabs-tabbar-content { overflow: visible; white-space: normal; }
.r360-tabs-wrap .mud-tabs-tabbar-wrapper { width: 100%; flex-wrap: wrap; transform: none !important; }
.r360-tabs-wrap .mud-tabs-scroll-button { display: none; }
/* Tabs are sized to their labels: every page-level MudTabs sets MinimumTabWidth="0px"
   (TabsWrapConventionTests), replacing MudBlazor's 160px default -- an inline style no rule here
   could override -- under which the eight work order tabs wrapped to a second row at 1440px and
   stacked eight rows deep on a phone. This padding gives each label room either side. */
.r360-tabs-wrap .mud-tab { padding-inline: 16px; }
/* Active-tab marker replacing the hidden slider. */
/* MudTabs uppercase their labels by default (MudBlazor .mud-tab); the design system's labels are
   sentence case, so tabs read as written ("Labour", not "LABOUR"). */
.mud-tab { text-transform: none; letter-spacing: normal; }
.r360-tabs-wrap .mud-tab.mud-tab-active { box-shadow: inset 0 -2px 0 var(--mud-palette-primary); }

/* FocusOnNavigate (Routes.razor) moves focus to the page's h1 after navigation so screen readers
   announce the new page. That focus is programmatic; a heading is not an interactive control
   and is never in the tab order, so no visible ring is needed and the default one looked like
   a rendering fault (UX 2026-09-23 P1-12). Keyboard focus on real controls is unaffected. */
h1:focus { outline: none; }

/* Not found / access denied / error message panels (UX 2026-09-23 P1-13, P1-15). */
.r360-msg-panel { max-width: 640px; padding: 20px 24px; }
.r360-msg-panel p { margin: 0 0 12px; font-size: 15px; color: var(--ink-2); }
.r360-msg-link { display: inline-flex; align-items: center; min-height: var(--hit-min); padding: 0 16px; border-radius: var(--r-sm);
                 background: var(--brand); color: #fff; font-weight: 600; text-decoration: none; }
.r360-msg-link:hover { filter: brightness(0.95); }
.r360-msg-ref code { font-size: 12px; color: var(--ink-3); }

/* UX 2026-09-23 P5-07: "Create work order" on AssetDetail navigates (query-string prefill), so it
   is an <a>, but it sits in the ActionBar's Primary slot next to other buttons and must look like
   one -- solid brand fill, white text, no underline, same min hit area as Btn's Primary variant. */
.r360-btn-link--primary { display: inline-flex; align-items: center; min-height: var(--hit-min); padding: 0 16px;
                           border-radius: var(--r-sm); background: var(--brand); color: #fff; font-weight: 600;
                           text-decoration: none; }
.r360-btn-link--primary:hover { filter: brightness(0.95); }

/* Links inside clickable rows keep the cell's look but show keyboard focus (P1-18). */
.r360-row-link { color: inherit; text-decoration: none; }
.r360-row-link:hover { text-decoration: underline; }
.r360-row-link:focus-visible, .dash-reorder-row:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: var(--r-sm); }

/* PasswordInput (P3-03): the toggle sits inside the box's right edge with a 44px hit area.
   `width: 100%` / `box-sizing: border-box` / `min-height: var(--hit-min)` are scoped to `.r360-pw input`
   (not the InputClass the caller passes) on purpose: Login and SetPassword pass
   InputClass="r360-auth-input" (which already sets width:100%), but AccountPassword.razor passes
   InputClass="set-inp", which sets neither a width nor a height tall enough for the 44px toggle
   button -- without this rule those three password boxes (plus the MFA-disable password box) would
   render at the browser's default input width, and the toggle button (absolutely positioned
   `right: 4px` against this wrapper) would sit detached from the input's actual right edge instead
   of flush against it. Scoping to `.r360-pw input` rather than editing `.set-inp` directly avoids
   changing the several OTHER inputs on the same page (the 6-digit MFA code boxes) that use
   `set-inp` directly and are not wrapped in a PasswordInput / `.r360-pw` div. */
.r360-pw { position: relative; }
.r360-pw input { width: 100%; box-sizing: border-box; padding-right: 76px; min-height: var(--hit-min); }
.r360-pw__toggle { position: absolute; right: 4px; top: 50%; transform: translateY(-50%); min-width: 64px; min-height: var(--hit-min); border: 1px solid var(--line); border-radius: var(--r-sm); background: #fff; color: var(--ink); font-size: 13px; font-weight: 600; cursor: pointer; }
.r360-pw__toggle:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/* PasswordRuleList (P3-08): rules ticked green as they're met; sits under the New-password box and
   is linked to it via aria-describedby. */
.r360-pw-rules { list-style: none; margin: -4px 0 0; padding: 0; font-size: 13px; color: var(--ink-4); }
.r360-pw-rules li[data-met="true"] { color: #1a7f37; }

/* MudBlazor corner radii (Sep 28, 2026 radius update). The theme's DefaultBorderRadius (6px,
   Reliability360Theme.cs) already squares most Mud components; these per-component overrides map
   the rest onto the app's radius tokens so Mud surfaces match the hand-rolled ones. app.css loads
   after MudBlazor.min.css, so equal-specificity rules here win. Papers and popovers exclude
   .mud-paper-square so components that ask for square corners keep them; the popover rule comes
   last so it beats the paper rule (a popover is also a .mud-paper). Buttons target .mud-button, not
   .mud-button-root: tabs, list items and nav links share .mud-button-root and are square by design.
   The chip selector repeats its class to outrank MudBlazor's .mud-chip.mud-chip-size-* radii. */
.mud-button { border-radius: var(--r-sm); }
.mud-input-outlined-border { border-radius: var(--r-sm); }
.mud-chip.mud-chip { border-radius: var(--r-tag); }
.mud-paper:not(.mud-paper-square) { border-radius: var(--r-xl); }
.mud-dialog { border-radius: var(--r-xl); }
.mud-popover:not(.mud-paper-square) { border-radius: var(--r-lg); }

/* Settings sub-page card layout (/settings/asset-lists, /settings/part-lists,
   /settings/work-order-setup). Global, not in Settings.razor.css: scoped CSS only reaches the
   component that owns it, so when these lists moved off the hub onto their own pages the grid
   silently stopped applying. The cards then stacked as plain blocks and LookupTableCard, which
   used to stretch to 100% of its parent, grew to the height of the entire stack (a 4-row
   priorities list rendered ~7,000px tall). */
.set-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
    align-items: start;
}

@media (max-width: 1100px) {
    .set-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 720px) {
    .set-grid { grid-template-columns: 1fr; }
}

/* Single-column variant for pages whose cards hold wide tables (work order types, statuses,
   failure codes) that would be unreadable squeezed into a third of the width. */
.set-grid.set-grid-stack { grid-template-columns: 1fr; }

/* Unhandled-exception fallback (App.razor #blazor-error-ui). Hidden until Blazor sets
   display:block on a component failure; pinned above the mobile tab bar so it is never covered. */
#blazor-error-ui {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  bottom: calc(64px + env(safe-area-inset-bottom, 0px));
  z-index: 1000;
  padding: 12px 16px;
  background: var(--danger);
  color: var(--paper);
  font-size: 15px;
}
#blazor-error-ui .reload {
  color: var(--paper);
  font-weight: 700;
  text-decoration: underline;
  margin-left: 8px;
}
#blazor-error-ui .dismiss {
  float: right;
  min-width: 44px;
  min-height: 44px;
  background: transparent;
  color: var(--paper);
  border: 1px solid var(--paper);
  border-radius: 6px;
  cursor: pointer;
}

/* Field-tech (WASM) pages: every text box, note box and picker at 16px. iOS Safari zooms the whole
   page into any focused field whose text is smaller than 16px and leaves it zoomed, which on a
   phone reads as "the screen jumped". The desktop keeps --fs-input (14px). */
.r360-mobile-shell .r360-input {
  font-size: 16px;
}

/* Work order detail: Labour and Parts tab bodies (WorkOrderLabourTab, WorkOrderPartsTab).
   These rules lived in WorkOrdersDetail.razor.css until the two tabs became their own components;
   scoped CSS only reaches the component that owns the stylesheet, so rules both tab components
   use (empty state, sub-headings, the add-form row) live here instead of being copied into each. */
/* P4-15/P4-16: Parts tab "Add used part" / "Reserve for later" headings, replacing the mono
   uppercase MudText caption those two blocks used to share with every other Parts-tab section --
   a plain sentence-case heading plus a one-line hint reads as an explanation, not a label, so a
   tech can tell the two forms apart without already knowing which one moves inventory. */
.wod-subhead { font-size: 14px; font-weight: 700; margin: 16px 0 2px; }
.wod-subhead-hint { font-size: 12.5px; color: var(--ink-4); margin: 0 0 8px; }

/* Task 28 — clock in/out timer card on the Labor tab. Sits above the read-only Labor Entries
   table so a tech (or anyone whose signed-in name resolves to a Technician row) can start/stop
   their own timer without leaving the desktop WO detail page. */
.wod-timer-card {
    display: flex;
    align-items: center;
    gap: 12px;
    background: #fbfdff;
    border: 1px solid #e3eaf2;
    border-radius: var(--r-lg);
    padding: 10px 14px;
    margin-bottom: 12px;
}
.wod-timer-running { color: var(--success); font-weight: 600; font-size: 13px; }

/* Empty state for the Labour and Parts tables: one muted sentence in place of a header-only table
   and a zero total, which read as a table that failed to load. */
.wod-empty { font-size: 14px; color: var(--ink-4); margin: 0 0 12px; }

/* One add-form row (Labour, Add used part, Reserve for later, Pull kit): labelled boxes side by
   side, wrapping on narrow screens, with the action button level with the boxes.
   Items align to the TOP, not the bottom: a box with an inline error (Field's Error message below
   it) is taller than its neighbours, and bottom alignment pushed every other box and the button
   down to the error text's baseline. Top alignment keeps all labels on one line and all boxes on
   another; the button cell starts with a label-height spacer so it sits on the boxes' line too.
   Field gives each box a 12px bottom margin, so the button cell carries the same margin. */
.wod-add-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 0 16px;
}
.wod-add-row__field { flex: 1 1 200px; min-width: 0; max-width: 320px; }
/* Quantity boxes only ever hold a few digits. */
.wod-add-row__field--narrow { flex: 0 1 120px; }
/* The kit picker shows "Kit name (N parts)", so it gets more room. */
.wod-add-row__field--wide { max-width: 480px; }
.wod-add-row__action { margin-bottom: 12px; }
/* Invisible line the height of a Field label (Field.razor.css .r360-field__label: same font,
   size, weight and 4px gap), so the button below it starts where the boxes start. Keep these
   values in step with that rule if the label typography changes. */
.wod-add-row__label-spacer {
    display: block;
    visibility: hidden;
    font-family: 'Helvetica LT', 'Helvetica Neue', Helvetica, sans-serif;
    font-size: 13px;
    font-weight: 600;
    margin-bottom: 4px;
}
