/* =========================
   Design Tokens (shared)
   ========================= */
/* Alpine.js: hide x-cloak elements until Alpine has initialised (and keep them
   hidden if the CDN never answers) — the one global rule Alpine documents. */
[x-cloak] {
    display: none !important;
}


:root {
  /* Core */
  --c-text: #333;
  --c-bg: #fff;

  /* Neutrals */
  --c-border: #777;
  --c-border-soft: #ddd;
  --c-muted: #666;
  /* Was #9a9a9a (~2.8:1 on white — below WCAG AA 4.5:1). Darkened to ~4.5:1
     for secondary text (timestamps/metadata). */
  --c-muted-2: #767676;

  /* Links */
  --c-link: #0066cc;

  /* Accent (neutral default; set per-view) */
  --c-accent: #111;

  /* ── Surfaces & semantic aliases ──
     These were referenced via var(--…) across templates but never defined,
     so their declarations were invalid (transparent backgrounds, non-mono
     text). Values match the inline fallbacks already used in those templates
     so the intended appearance is now applied everywhere, not just where a
     fallback happened to be typed. */
  --c-surface: #f5f5f5;
  --c-bg-alt: #f8f9fa;
  --c-bg-soft: #f9fafb;
  --c-primary: #2563eb;
  --c-danger: #c62828;
  --c-text-secondary: #555;
  --c-warning-bg: #fff8e1;
  --c-warning-border: #ffe082;
  --c-warning-text: #e65100;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;

  /* Radii */
  --r-sm: 0.25rem;
  --r-md: 0.5rem;
  --r-lg: 0.75rem;
  --r-xl: 1rem;

  /* Layout widths - responsive */
  --page-narrow: 32rem;      /* Mobile/forms: 512px */
  --page-wide: 64rem;        /* Tables/lists: 1024px - original */
  --page-max: 80rem;         /* Full-width views: 1280px */

  /* ── Subway Line Colors ──
     Task 3b (axe AA-contrast fixup, 2026-07-21): these vars back both the
     per-page <h1> accent (needs >=3:1 on white) AND, on any page whose
     .button--primary uses --c-accent, white button text (needs >=4.5:1 on
     white). Six shades were darkened (uniform RGB scaling — preserves hue
     and saturation exactly, only lightness drops) to clear 4.5:1 wherever a
     real .button--primary consumes that accent; the rest already cleared
     4.5:1 and are untouched. subway-red stays at its original shade: no
     .button--primary currently uses it (kickback_queue's white-text button
     is --btn-success, fixed separately below) and its original 4.05:1
     already clears the 3:1 h1 floor. No spec references these hex values by
     name; the full old/new ratio table lives in the commit message that
     introduced this block (git log -S subway-orange -- static/css/main.css). */
  --subway-orange:   #CA4F14;   /* Receiving — was #FF6319 (2.98:1 h1 / N/A btn), now 4.53:1 (button used in receive_batch.html) */
  --subway-blue:     #0139A6;   /* Delivery tracking — 9.83:1, unchanged */
  --subway-yellow:   #8F7306;   /* Requesting/Warehouses — was #FCCC0B (1.52:1), now 4.54:1 (button used in warehouse_management.html) */
  --subway-magenta:  #B934AD;   /* Outgoing/BOL — 5.05:1, unchanged */
  --subway-green:    #018938;   /* Completing — was #01933C (4.01:1), now 4.53:1 (button used in complete_delivery.html) */
  --subway-gray:     #757679;   /* Inventory views — was #A7A9AC (2.36:1), now 4.54:1 (button used in batch_detail.html) */
  --subway-brown:    #996634;   /* User management — 4.88:1, unchanged */
  --subway-purple:   #6E3667;   /* Invoicing — 8.80:1, unchanged */
  --subway-teal:     #2A7B9B;   /* Site management — 4.77:1, unchanged */
  --subway-red:      #EE352E;   /* Review/Approval — 4.05:1, unchanged (no white-text button consumes it; see note above) */
  --subway-seagreen: #2D8654;   /* Contractor portal — was #2E8B57 (4.25:1), now 4.52:1 (button used in contractor_portal.html) */
  --subway-lime:     #4B8430;   /* Approval queue — was #6CBE45 (2.31:1, axe-flagged h1 + .button--primary), now 4.52:1 */

  /* ── Header Icon System ── */
  --icon-size:       2rem;      /* 32px desktop */
  --icon-size-sm:    1.75rem;   /* 28px mobile */
  --icon-color:      #4A5568;   /* Slate */
  --icon-hover:      #2D3748;   /* Dark slate */
  --icon-touch-min:  2.75rem;   /* 44px WCAG minimum */

  /* ── Message / Alert Colors ── */
  --msg-success-bg:     #d4edda;
  --msg-success-border: #28a745;
  --msg-success-text:   #155724;
  --msg-error-bg:       #f8d7da;
  --msg-error-border:   #dc3545;
  --msg-error-text:     #721c24;
  --msg-warning-bg:     #fff3cd;
  --msg-warning-border: #ffc107;
  --msg-warning-text:   #856404;
  --msg-info-bg:        #d1ecf1;
  --msg-info-border:    #17a2b8;
  --msg-info-text:      #0c5460;

  /* ── Badge Colors ── */
  --badge-danger:  #e41e3f;    /* Notification count */
  --badge-bg:      #e9ecef;    /* Role badge background */
  --badge-text:    #495057;    /* Role badge text */

  /* ── Action Button Semantic Colors ──
     Task 3b (axe AA-contrast fixup, 2026-07-21): white-text button
     backgrounds must reach >=4.5:1 on white.
     - --btn-success was #28a745 (3.13:1, axe-flagged on kickback_queue's
       "Edit & Resubmit"), darkened to 4.52:1. --btn-success-hover darkened
       to match (kept darker than the new base so hover still reads as a
       press-down state, not a lighten) — was #218838 (4.52:1, incidentally
       equal to the new base), now #1B702E (6.17:1).
     - --btn-warning/-hover keep their YELLOW background (#ffc107) because
       the button text on this variant is dark (#212529, see
       .button--warning), not white — out of scope for the white-text
       requirement, left untouched.
     - --btn-danger (4.53:1) and --btn-danger-hover (5.61:1) already clear
       4.5:1 — left untouched, ratios recorded here as proof.
     - --btn-disabled (#A7A9AC, 2.36:1) intentionally untouched: disabled
       controls are exempt from WCAG 1.4.11 (inactive UI components), and
       axe does not flag :disabled elements for color-contrast. */
  --btn-success:         #218838;
  --btn-success-hover:   #1B702E;
  --btn-success-disabled:#94d3a2;
  --btn-warning:         #ffc107;
  --btn-warning-hover:   #e0a800;
  --btn-danger:          #dc3545;
  --btn-danger-hover:    #c82333;
  --btn-disabled:        #A7A9AC;
}

/* =========================
   Messages / Alerts
   ========================= */

.messages {
  text-align: center;
  margin-bottom: 1rem;
}

.message {
  display: inline-block;
  padding: 0.75rem 1.5rem;
  border-radius: var(--r-md);
  font-weight: 500;
  margin-bottom: 0.5rem;
}

.message--success {
  background-color: var(--msg-success-bg);
  border: 1px solid var(--msg-success-border);
  color: var(--msg-success-text);
}

.message--error {
  background-color: var(--msg-error-bg);
  border: 1px solid var(--msg-error-border);
  color: var(--msg-error-text);
}

.message--warning {
  background-color: var(--msg-warning-bg);
  border: 1px solid var(--msg-warning-border);
  color: var(--msg-warning-text);
}

.message--info {
  background-color: var(--msg-info-bg);
  border: 1px solid var(--msg-info-border);
  color: var(--msg-info-text);
}

/* Promo banner — the ONE component for site promos (issue #306).

   Layout template (the rule, so future banners don't improvise):
     [ text ······················· ] [action] [×]
     [ detail (optional second row, full width)  ]
   - Text: left-aligned, semibold, flex-1 — the message IS the banner.
     No icons: emoji render inconsistently across platforms and read
     informal; the category is conveyed by the thin left accent rule.
   - Action: one compact solid button in the brand accent, immediately
     left of the dismiss. One action max — more belongs on a page.
   - Dismiss: ALWAYS the last element, far right, muted ×, ≥32px hit
     target (matches the trailing-dismiss convention users expect).
   - Detail: revealed-on-demand second row (regular weight, muted);
     never shown by default.
   - Surface: neutral white + standard border. Color appears ONLY in the
     3px left rule (category) and the action button (brand) — no tinted
     washes.
   At most one promo shows at a time (push outranks passkey — see the
   apex-promo-resolved handshake in the partials). */
.promo-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  padding: 0.55rem 0.6rem 0.55rem 0.85rem;
  background: var(--c-surface, #fff);
  border: 1px solid var(--c-border);
  border-left: 3px solid var(--c-border);
  border-radius: var(--r-sm);
  margin-bottom: 0.75rem;
  font-size: 0.88rem;
  color: var(--c-text, #1f2937);
}

/* display:flex would otherwise beat the UA [hidden] rule (equal specificity,
   later source order), leaving a hidden banner stuck visible. */
.promo-banner[hidden] {
  display: none;
}

.promo-banner__text {
  /* Basis 0 keeps long copy from claiming a full row of its own. */
  flex: 1 1 0;
  min-width: 0;
  font-weight: 600;
}

.promo-banner__action {
  border: none;
  cursor: pointer;
  border-radius: var(--r-sm);
  padding: 0.35rem 0.75rem;
  font-weight: 600;
  font-size: 0.8rem;
  white-space: nowrap;
  color: #fff;
  background: var(--c-accent);
}

.promo-banner__dismiss {
  background: none;
  border: none;
  cursor: pointer;
  font-size: 1.05rem;
  line-height: 1;
  min-width: 32px;
  min-height: 32px;
  padding: 0;
  color: var(--c-muted);
}

.promo-banner__dismiss:hover {
  color: var(--c-text, #1f2937);
}

.promo-banner__detail {
  flex-basis: 100%;
  font-size: 0.82rem;
  font-weight: 400;
  line-height: 1.4;
  color: var(--c-muted);
}

.promo-banner__detail--error {
  color: #991b1b;
}

/* Category = left rule only. */
.promo-banner--push {
  border-left-color: var(--c-accent);
}

.promo-banner--passkey {
  border-left-color: #16a34a;
}

.warning-banner {
  padding: 0.75rem 1rem;
  border-radius: var(--r-md);
  background-color: var(--msg-warning-bg);
  border: 1px solid var(--msg-warning-border);
  color: var(--msg-warning-text);
  font-size: 0.9rem;
  line-height: 1.5;
}

/* =========================
   View Accent Overrides
   (NYC subway palette idea)
   ========================= */

/* Default (neutral) */
body {
  --c-accent: #111;
}

/* Receive Batch */
body#view-receive-batch {
  --c-accent: var(--subway-orange);
}

/* Upcoming Deliveries */
body#view-upcoming-deliveries {
  --c-accent: var(--subway-blue);
}

/* Batch Status */
body#view-batch-status {
  --c-accent: var(--subway-blue);
}

/* Request Delivery */
body#view-request-delivery {
  --c-accent: var(--subway-yellow);
}

/* Record Outgoing BOL */
body#view-create-outgoing-bol {
  --c-accent: var(--subway-magenta);
}

body#view-create-outgoing-bol .truck-contents h3,
body#view-create-outgoing-bol .truck-contents-header,
body#view-create-outgoing-bol .form-section-title {
  border-top: none;
  padding-top: 0;
}

body#view-create-outgoing-bol .print-button {
  margin: 1.5rem 0 0;
  background-color: var(--c-accent);
  color: #fff;
  border: none;
  cursor: pointer;
  padding: 0.8rem 1.5rem;
  font-weight: 600;
  border-radius: var(--r-md);
}

body#view-create-outgoing-bol .print-button:disabled {
  background-color: #A7A9AC;
  cursor: not-allowed;
  opacity: 0.7;
}

body#view-create-outgoing-bol .col-requested,
body#view-create-outgoing-bol .col-shipped,
body#view-create-outgoing-bol .col-remaining {
  text-align: center;
}

/* Complete Delivery */
body#view-complete-delivery {
  --c-accent: var(--subway-green);
}

/* View Batches / Inventory (neutral views) */
body#view-batches,
body#view-batch-detail {
  --c-accent: var(--subway-gray);
}

/* View Users / User Detail */
body#view-users,
body#view-user-detail {
  --c-accent: var(--subway-brown);
}

body#view-users .table-card {
  border: none;
  border-radius: 0;
}

body#view-users .email-link {
  text-decoration: underline;
  color: var(--c-accent);
}

body#view-users .users-table {
  margin-top: 0;
}

/* Create Invoice */
body#view-create-invoice {
  --c-accent: var(--subway-purple);
}

body#view-create-invoice .button.button--primary,
body#view-create-invoice .generate-btn {
  background-color: var(--c-accent);
  color: #fff;
  border: none;
  cursor: pointer;
  padding: 0.8rem 1.5rem;
  font-weight: 600;
  border-radius: var(--r-md);
}

body#view-create-invoice .context-info h3 {
  padding-top: 0;
}

/* #315 PR5 Task 8: was #A7A9AC (2.36:1 on white); var(--c-muted) is the
   same token .step's inactive-label color already uses, 5.74:1. */
body#view-create-invoice .step.completed .step-number {
  background-color: var(--c-muted);
  color: #fff;
}

body#view-create-invoice .line-items-table .text-center,
body#view-create-invoice .line-items-table .qty-cell,
body#view-create-invoice .line-items-table .days-cell {
  text-align: center;
}

body#view-create-invoice .line-items-table input[type="number"] {
  padding: 0.2rem;
}

body#view-create-invoice .line-note-display {
  white-space: nowrap;
}

body#view-create-invoice .details-panel h2 {
  padding-top: 0;
}

body#view-create-invoice .details-panel textarea {
  width: 100%;
}

/* Approval Queue (review workflow) */
body#view-approval-queue {
  --c-accent: var(--subway-lime);
}

/* Approval Queue mobile overrides removed 2026-07-20: the squeezed-table
   rules (nowrap identity, 120px ellipsis description) predate card mode and
   were truncating card content; the stats-bar rules died with the stats
   tiles (issue #315 walk feedback). */

/* Kickback Queue (review workflow) */
body#view-kickback-queue {
  --c-accent: var(--subway-red);
}

/* Contractor Portal */
body#view-contractor-portal {
  --c-accent: var(--subway-seagreen);
}

/* Site Management */
body#view-site-management {
  --c-accent: var(--subway-teal);
}

/* Warehouse Management */
body#view-warehouse-management {
  --c-accent: var(--subway-yellow);
}

body#view-site-management .contractor-meta {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.25rem 0.75rem;
  align-items: baseline;
}

body#view-site-management .billing-info {
  border-top: none;
}

body#view-site-management .assign-dropdown {
  padding: 0.3rem 1.8rem 0.3rem 0.5rem;
}

body#view-site-management .edit-link {
  padding: 0 0.6rem;
  /* --subway-teal (#2A7B9B) is only 4.37:1 on the contractor card's
     --c-surface (#f5f5f5) background -- its documented 4.77:1 assumes a
     white page background, which this card isn't. Pre-existing gap
     surfaced by the PR8 admin-screens smoke test's axe scan (issue #315
     follow-on); darkened just for this placement (5.3:1 on #f5f5f5)
     rather than touching the shared --subway-teal token, which stays
     correct everywhere else it's used (add-btn background, dashboard
     tile, both white-on-teal). */
  color: #1f6d8c;
}

body#view-site-management .add-btn {
  background-color: var(--c-accent);
  color: #fff;
  border: none;
  padding: 0.8rem 1.5rem;
  font-weight: 600;
  border-radius: var(--r-md);
  cursor: pointer;
}

/* View Invoices / Invoice Preview */
body#view-invoices,
body#view-invoice-preview {
  --c-accent: #000;
}

/* Invoices List */
body#view-invoices .filter-search {
  width: 100%;
  max-width: none;
}

body#view-invoices .invoices-table-wrap {
  overflow-x: auto;
}

body#view-invoices .invoices-table {
  width: 100%;
  table-layout: auto;
}

body#view-invoices .invoice-link {
  text-decoration: underline;
  color: var(--c-accent);
}

/* Invoice Preview */
body#view-invoice-preview .invoice-actions {
  display: flex;
  justify-content: center;
  gap: 1rem;
  flex-wrap: wrap;
}

body#view-invoice-preview .invoice-actions .button,
body#view-invoice-preview .invoice-actions a,
body#view-invoice-preview .invoice-actions button {
  background-color: var(--c-accent);
  color: #fff;
  border: none;
  padding: 0.8rem 1.5rem;
  font-weight: 600;
  border-radius: var(--r-md);
  cursor: pointer;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
}

/* Profile */
body#user-profile {
  --c-accent: #000;
}

/* Edit User / Add User */
body#view-create-user {
  --c-accent: var(--subway-brown);
}



/* =========================
   Base
   ========================= */

body {
  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    Helvetica, Arial, sans-serif;
  color: var(--c-text);
  background-color: var(--c-bg);
}

/* =========================
   Layout & Page Structure
   ========================= */

.page {
  max-width: var(--page-narrow);
  margin: 0 auto;
  padding: 1.5rem 1rem 2rem;
}

.page--wide {
  max-width: var(--page-wide);
}

.page--max {
  max-width: var(--page-max);
}

/* Tablet+ : more breathing room */
@media (min-width: 768px) {
  .page {
    padding: 2rem 2.5rem 3rem;
  }

  /* Forms can be wider on tablet/desktop */
  .page:not(.page--wide):not(.page--max) {
    max-width: 40rem; /* 640px - better for forms */
  }
}

/* Desktop: generous padding */
@media (min-width: 1024px) {
  .page {
    padding: 2.5rem 3rem 4rem;
  }
}

/* Large desktop: even more space for wide pages */
@media (min-width: 1440px) {
  .page--wide,
  .page--max {
    padding-left: 4rem;
    padding-right: 4rem;
  }
}

/* Small screens: constrain wide pages */
@media (max-width: 560px) {
  .page--wide {
    max-width: 100%;
  }
}

/* =========================
   Header
   ========================= */

.header {
  position: relative;
  text-align: center;
  margin-bottom: 1.25rem;
}

.logo {
  max-width: 8rem;
  margin: 0 auto;
  display: block;
}

.logo-link {
  display: block;
  margin: 0 auto;
  width: fit-content;
}

/* Header actions row (bell + sign out) */
.header-actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 0.5rem;
}

/* Notification bell */
.notification-bell {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--icon-color);
  text-decoration: none;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}

.notification-bell:hover {
  color: var(--icon-hover);
}

.notification-bell svg {
  width: var(--icon-size);
  height: var(--icon-size);
}

/* Badge count */
.notification-badge {
  position: absolute;
  bottom: 2px;
  right: -6px;
  min-width: 1.25rem;
  height: 1.25rem;
  padding: 0 0.35rem;
  border-radius: 999px;
  background: var(--badge-danger);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 700;
  line-height: 1.25rem;
  text-align: center;
  text-decoration: none;
  pointer-events: none;
}

.notification-badge:empty {
  display: none;
}

/* Notification Dropdown */
.notification-bell-wrap {
  position: relative;
}

.notification-dropdown {
  position: absolute;
  top: 100%;
  right: 0;
  width: 320px;
  max-height: 400px;
  overflow-y: auto;
  background: #fff;
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
  z-index: 100;
}

.notification-dropdown-item {
  display: block;
  padding: 0.75rem 1rem;
  text-decoration: none;
  color: inherit;
  border-bottom: 1px solid var(--c-border);
}

.notification-dropdown-item:hover {
  background: #f5f5f5;
}

.notification-dropdown-item--unread {
  background: #e8f4fd;
}

.notification-dropdown-title {
  font-size: 0.85rem;
  font-weight: 600;
  margin: 0 0 0.15rem;
}

.notification-dropdown-message {
  font-size: 0.8rem;
  color: var(--c-muted);
  margin: 0 0 0.15rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.notification-dropdown-time {
  font-size: 0.75rem;
  color: #999;
  margin: 0;
}

.notification-dropdown-more {
  padding: 0.5rem 1rem;
  font-size: 0.8rem;
  color: var(--c-muted);
  border-bottom: 1px solid var(--c-border);
}

.notification-dropdown-viewall {
  display: block;
  padding: 0.75rem 1rem;
  text-align: center;
  font-size: 0.85rem;
  color: var(--c-accent);
  text-decoration: none;
  font-weight: 600;
}

.notification-dropdown-viewall:hover {
  background: #f5f5f5;
}

.notification-dropdown-empty {
  padding: 2rem 1rem;
  text-align: center;
  color: var(--c-muted);
  font-size: 0.85rem;
}

@media (max-width: 640px) {
  .notification-dropdown {
    position: fixed;
    top: auto;
    right: 0.5rem;
    left: 0.5rem;
    width: auto;
  }
}

.sign-out {
  font-size: 0.9rem;
  text-decoration: underline;
  color: var(--c-link);
}

.user-menu {
  position: relative;
}

.user-menu-toggle {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--icon-color);
  font-size: 0.9rem;
  padding: 0.5rem;
  border-radius: var(--r-md);
  min-height: var(--icon-touch-min);
}

.user-menu-toggle:hover {
  color: var(--icon-hover);
  background: rgba(0, 0, 0, 0.05);
}

.user-menu-toggle svg {
  width: var(--icon-size);
  height: var(--icon-size);
}

@media (max-width: 640px) {
  .notification-bell svg {
    width: var(--icon-size-sm);
    height: var(--icon-size-sm);
  }
  .user-menu-toggle svg {
    width: var(--icon-size-sm);
    height: var(--icon-size-sm);
  }
}

.user-menu-name {
  max-width: 120px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.user-menu-dropdown {
  position: absolute;
  top: 100%;
  /* left-anchored: the user menu sits at the LEFT of .header-actions, so a
     right: 0 anchor hung the dropdown off the viewport edge (pre-#315 rule
     from #275; Matt caught the clip 2026-07-21). */
  left: 0;
  background: #fff;
  border: 1px solid var(--c-border);
  border-radius: 6px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  min-width: 140px;
  z-index: 100;
  overflow: hidden;
}

.user-menu-dropdown a,
.user-menu-dropdown button {
  display: block;
  width: 100%;
  padding: 0.6rem 1rem;
  font-size: 0.9rem;
  color: var(--c-text);
  text-decoration: none;
  text-align: left;
  background: none;
  border: none;
  font-family: inherit;
  cursor: pointer;
}

.user-menu-dropdown a:hover,
.user-menu-dropdown button:hover {
  background: #f5f5f5;
}

.user-menu-dropdown form {
  margin: 0;
}

.header h1 {
  font-size: 1.5rem;
  font-weight: 600;
  color: var(--c-accent);
  margin: 0;
}

.divider {
  height: 1px;
  background-color: var(--c-accent);
  margin: 1.25rem 0;
}

/* Desktop header improvements */
@media (min-width: 768px) {
  .header {
    margin-bottom: 1.5rem;
  }

  .logo {
    max-width: 10rem;
  }

  .header h1 {
    font-size: 1.75rem;
  }

  .divider {
    height: 2px;
    margin: 1.5rem 0;
  }
}

@media (min-width: 1024px) {
  .header {
    margin-bottom: 2rem;
  }

  .header h1 {
    font-size: 2rem;
  }

  .divider {
    margin: 1.75rem 0;
  }
}

/* =========================
   Form
   ========================= */

.form {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

.form-group {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.form-group-row {
  display: flex;
  flex-direction: row;
  gap: 1rem;
  width: 100%;
}

.form-group-row > .form-group {
  flex: 1 1 0;
  min-width: 0;
}

label {
  font-size: 0.95rem;
  font-weight: 600;
}

input,
select,
textarea {
  width: 100%;
  max-width: 100%;
  padding: 0.75rem;
  font-size: 1rem;
  border: 1px solid var(--c-border);
  border-radius: var(--r-sm);
  box-sizing: border-box;
}

/* Strip native Safari/iOS form control styling */
input,
select,
textarea,
button,
input[type="submit"],
input[type="button"],
input[type="reset"] {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
}

/* Restore native appearance for checkboxes and radios */
input[type="checkbox"],
input[type="radio"] {
  appearance: auto;
  -webkit-appearance: auto;
  -moz-appearance: auto;
  width: auto;
}

select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%23333' d='M1.41 0L6 4.58 10.59 0 12 1.41l-6 6-6-6z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.75rem center;
  background-size: 12px 8px;
  padding-right: 2.25rem;
  background-color: #fff;
}

/* Flatpickr inputs */
.flatpickr-input,
.flatpickr-alt-input,
input.flatpickr-input {
  width: 100% !important;
  max-width: 100% !important;
  box-sizing: border-box !important;
}

textarea {
  min-height: 6rem;
  resize: vertical;
}

input::placeholder,
textarea::placeholder {
  color: #aaa;
}

/* Desktop form improvements */
@media (min-width: 768px) {
  .form {
    gap: 1.5rem;
  }

  .form-group-row {
    gap: 1.5rem;
  }

  label {
    font-size: 1rem;
  }

  input,
  select,
  textarea {
    padding: 0.85rem 1rem;
  }

  /* The shorthand above also reset a select's right padding to 1rem, so long
     option text ran under the drawn arrow on every page at tablet width and up.
     Keep the room the base select rule leaves for it. */
  select {
    padding-right: 2.25rem;
  }
}

@media (min-width: 1024px) {
  .form {
    gap: 1.75rem;
  }

  .form-group-row {
    gap: 2rem;
  }
}

/* =========================
   Toolbar / Buttons
   ========================= */

/* ── Toolbar ──
   Filter/search/export bars. No primary concept, no reordering —
   .actions is for priority-ordered record actions, .toolbar is for
   controls. Stacks full-width below 768px. */
.toolbar {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.5rem;
  margin: 0.75rem 0 1rem;
}

@media (min-width: 768px) {
  .toolbar {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
  }

  .toolbar--end {
    justify-content: flex-end;
  }
}

/* ── Base Button ── */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0.6rem 1.1rem;
  font-size: 0.95rem;
  font-weight: 600;
  line-height: 1.3;
  white-space: normal;
  text-align: center;
  text-decoration: none;
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  color: var(--c-text);
  background: transparent;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease, opacity 0.15s ease;
}

/* ── Variants ── */
.button--primary {
  background-color: var(--c-accent);
  border-color: var(--c-accent);
  color: #fff;
}

.button--primary:hover {
  background-image: linear-gradient(rgba(0,0,0,0.12), rgba(0,0,0,0.12));
}

.button--success {
  background-color: var(--btn-success);
  border-color: var(--btn-success);
  color: #fff;
}

.button--success:hover {
  background-color: var(--btn-success-hover);
  border-color: var(--btn-success-hover);
}

.button--warning {
  background-color: var(--btn-warning);
  border-color: var(--btn-warning);
  color: #212529;
}

.button--warning:hover {
  background-color: var(--btn-warning-hover);
  border-color: var(--btn-warning-hover);
}

.button--danger {
  background-color: var(--btn-danger);
  border-color: var(--btn-danger);
  color: #fff;
}

.button--danger:hover {
  background-color: var(--btn-danger-hover);
  border-color: var(--btn-danger-hover);
}

.button--secondary {
  background: transparent;
  border: 1px solid var(--c-border);
  color: var(--c-text);
}

.button--secondary:hover {
  background-color: #f5f5f5;
}

/* ── Focus (keyboard navigation) ── */
.button:focus-visible {
  outline: 2px solid var(--c-accent);
  outline-offset: 2px;
}

/* ── Disabled States ── */
.button--primary:disabled,
.button--success:disabled,
.button--warning:disabled,
.button--danger:disabled {
  background-color: var(--btn-disabled);
  border-color: var(--btn-disabled);
  color: #fff;
  cursor: not-allowed;
  opacity: 0.7;
}

.button--secondary:disabled {
  color: var(--btn-disabled);
  border-color: var(--c-border-soft);
  cursor: not-allowed;
  opacity: 0.7;
}

/* ── Sizes ── */
/* 36px row-action size — the one exception to the 44px touch-target
   floor (table/row actions only; issue #315). */
.button--sm {
  min-height: 36px;
  padding: 0.35rem 0.7rem;
  font-size: 0.85rem;
}

/* ── Full-width variant ── */
.button--full {
  width: 100%;
}

/* ── Link-button ──
   Inline text action (toggles, expanders). Constant --c-link, NOT the
   per-section --c-accent — the same control must not change colour
   between pages. The hit area is enlarged by the ::after overlay below
   rather than by padding, so the text metrics stay put. Usable with or
   without the .button base class, so it carries its own focus ring.

   AMENDED 2026-07-20 (PR 2, the freeze gate). This used to add "so inline
   flow is undisturbed" — no longer true, and the correction is the point:
   the overlay now reserves matching vertical margin, because costing zero
   layout space is exactly what let it steal taps from the control above.
   `line-height` and `margin` below are load-bearing for that overlay — read
   the arithmetic at ::after before changing either. */
.button--link {
  position: relative;
  min-height: 0;
  /* 0.8rem × 1.25 = 16px line box. Pinned rather than inherited so the
     overlay arithmetic below is deterministic at every call site. */
  line-height: 1.25;
  /* (44 − 16) / 2 = 14px. Reserves exactly the space the 44px overlay
     occupies, so the overlay never escapes this element's margin box. */
  margin: 14px 0;
  padding: 0;
  border: none;
  background: none;
  font-size: 0.8rem;
  color: var(--c-link);
  text-decoration: underline;
  cursor: pointer;
}

/* The visible control is one line of 0.8rem text (16px line box) — far under
   the 44px touch floor, closed with an overlay rather than padding so the
   text metrics stay put. Vertical only: these sit inline among other text,
   and a horizontal expansion would swallow taps meant for their neighbours.

   AMENDED 2026-07-20 (PR 2, the freeze gate). The overlay used to cost no
   layout space at all, and that was the bug: `.button--link` is
   `position: relative`, so the overlay paints in the positioned layer above
   in-flow siblings and captured their taps. It overhangs the 16px line box
   by (44 − 16) / 2 = 14px in each direction, while three of the four
   batch_detail call sites sit only 8px (`margin-top: 0.5rem`) below a
   control — a button row at :1820, a <select> at :2407 and :2459 — leaving
   the bottom ~6px of each of those dead to touch.

   THE TRADE CHOSEN: pay the layout space rather than shrink the target. The
   14px vertical margin on `.button--link` above makes the element reserve
   exactly the 44px it claims, so the overlay is flush with its own margin
   box and cannot reach a sibling. Shrinking the overlay to the ~24px that
   fit in the old zero-margin box was rejected — the 44px floor is the whole
   reason the overlay exists, and a 24px target fails it silently.

   ARITHMETIC (all four call sites, margins do not collapse — the element is
   inline-level, and at :2407/:2459 it is a flex item):
     border box       = 16px (line box; padding 0, border none)
     overlay          = 44px, `top: 50%` + `translateY(-50%)`
     overlay top      = border-box top + 8 − 22 = border-box top − 14px
     margin box top   = border-box top − 14px   → flush, no overhang
   Same by symmetry at the bottom. Changing `line-height`, `font-size` or
   `margin` above without redoing this breaks containment. */
.button--link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 44px;
  transform: translateY(-50%);
}

/* Matches .button:focus-visible. Declared separately because .button--link is
   valid standalone (it resets min-height/padding, so it does not require the
   .button base) — without this, a keyboard user gets no focus indicator on
   those call sites. The ring tracks the visible text, not the 44px overlay. */
.button--link:focus-visible {
  outline: 2px solid var(--c-accent);
  outline-offset: 2px;
}

/* ── Modal close ── */
.modal-close {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: none;
  background: none;
  font-size: 1.25rem;
  color: var(--c-muted);
  cursor: pointer;
  /* Modal headers are flex rows (title + close); without this a
     two-line title on mobile can shrink the close button below its
     44px target. */
  flex-shrink: 0;
}

.modal-close:hover {
  border-radius: var(--r-sm);
  background: #f0f0f0;
  color: #333;
}

/* Call sites use .modal-close without the .button base, so it carries its
   own ring (same reasoning as .button--link). */
.modal-close:focus-visible {
  outline: 2px solid var(--c-accent);
  outline-offset: 2px;
}

/* ── Action cluster ──
   Priority-ordered actions on a record. DOM order is ALWAYS priority
   order, so THE FIRST CHILD IS THE PRIMARY — no marker class. Mobile:
   the primary spans the full width and secondaries pair in a 2-column
   grid; an unpaired last secondary lands on an EVEN child index and
   spans. Desktop: row-reverse renders the primary rightmost while
   screen-reader order stays priority-first.

   AMENDED 2026-07-20 (PR 2, the freeze gate). This previously required an
   explicit .actions__primary marker. That broke on role-gated clusters:
   batch_detail's toolbar has three permission flags, so which button is
   primary varies at runtime, and expressing that needed a chain of
   {% if %}-gated marker classes — which a static guard reads as three
   primaries. Deriving the primary positionally makes every flag
   combination correct by construction and deletes that whole class of
   bug.

   The trade accepted, stated honestly: what is true by definition is that
   the FIRST CHILD IS STYLED AS THE PRIMARY. It is NOT true that the first
   child IS the highest-priority action — that is now an authoring
   convention with no marker to check it against. Under the retired marker,
   putting Cancel first failed a guard; now it silently renders Cancel
   full-width, desktop-rightmost, in the primary's suite. The residual
   static coverage is ActionsPrimaryGuardTest, which asserts no cluster
   leads with a .button--secondary — a secondary as the cluster primary is
   always wrong and is statically checkable. Ordering between two
   non-secondary actions is not, and rests on review. */
.actions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5rem;
  margin-top: 1rem;
}

.actions .button {
  width: 100%;
}

.actions > :first-child {
  grid-column: 1 / -1;
}

/* A form-wrapped primary (submit actions need their own form) needs no rule
   of its own: `.actions .button` above is a descendant selector and already
   matches it. A `.actions > :first-child > .button` child rule was removed
   2026-07-20 — it added nothing on mobile and, sitting outside the media
   query at specificity (0,3,0), it outranked the (0,2,0)
   `.actions .button { width: auto }` below, pinning form-wrapped primaries
   to 100% on desktop. */

.actions > :not(:first-child):last-child:nth-child(even) {
  grid-column: 1 / -1;
}

@media (min-width: 768px) {
  .actions {
    display: flex;
    flex-direction: row-reverse;
    justify-content: flex-start;
    gap: 0.75rem;
  }

  .actions .button {
    width: auto;
  }
}

/* A cluster used as a modal footer restores the padding and separator the
   deleted `.modal-footer` used to supply. `.modal-overlay .modal` is
   deliberately `padding: 0` (sticky-header fix, see that rule), so each
   region — header, body, footer — pads itself; `.actions` alone supplies
   only `margin-top`, which left these buttons flush to the card edge with
   no rule above them.

   Scoped to `footer.actions`, not a bare descendant selector, for two
   reasons found by reading the markup: two of the four footers sit inside
   the modal's <form> rather than directly under `.modal`, so a child
   combinator misses them; and the request-delivery modal nests a policy
   popup whose own `<div class="actions">` a bare descendant selector would
   wrongly pad. `<footer class="actions">` matches exactly the four real
   footers.

   `.modal-backdrop` modals (the _confirm_submit_modal.html include, the
   photo-delete confirm) are NOT covered and need no rule: they keep the
   global `.modal { padding: 1.5rem }`, so their clusters are already
   inset, and they carry no header to separate from. */
.modal-overlay .modal footer.actions {
  padding: 1rem 1.25rem;
  border-top: 1px solid var(--c-border);
  margin-top: 0;
}

/* ── Tabs ──
   Switches which set of records is listed. Underline, no fill — tabs
   must read lighter than actions. Scrolls horizontally on mobile (no
   overflow affordance — a right-edge mask fade was tried and dropped;
   mask-image is positioned against the element box, not the scroll
   content, so it never cleared after scrolling fully right). */
.tabs {
  display: flex;
  gap: 1.25rem;
  margin-bottom: 1.25rem;
  border-bottom: 2px solid var(--c-border-soft);
  overflow-x: auto;
  scrollbar-width: none;
}

/* Overflow affordance (issue #315, amended at the PR 2 freeze gate). The
   scrollbar is hidden, so a clean cut between tabs reads as "no more tabs" —
   these fades signal hidden content per side. The classes are toggled by the
   tabsScroll Alpine component in base.html on scroll; gating by class is what
   the earlier CSS-only mask lacked (it stayed faded at scroll-end, which is
   why PR 2's review removed it). mask-image is pinned to the element box, so
   the fade sits at the visible edge regardless of scroll position — exactly
   right once something else decides WHEN it shows. */
.tabs--more-right {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent);
}

.tabs--more-left {
  -webkit-mask-image: linear-gradient(to left, #000 calc(100% - 24px), transparent);
  mask-image: linear-gradient(to left, #000 calc(100% - 24px), transparent);
}

.tabs--more-left.tabs--more-right {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 24px, #000 calc(100% - 24px), transparent);
  mask-image: linear-gradient(to right, transparent, #000 24px, #000 calc(100% - 24px), transparent);
}

.tabs::-webkit-scrollbar {
  display: none;
}

.tabs__tab {
  /* Buttons are first-class consumers of this primitive, not just <a>s.
     The global `appearance: none` reset above does not strip Chrome's UA
     button background-color/border, so reset them explicitly here.
     background/border must precede border-bottom or the shorthand reset
     wipes out the underline; font: inherit must precede font-size/
     font-weight for the same reason. (Discovered #315 PR 8 preview walk;
     billing_setup shipped with this leak since PR 5.) */
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  min-height: 44px;
  padding: 0.5rem 0.125rem;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--c-muted);
  font: inherit;
  font-size: 0.9rem;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}

.tabs__tab:hover {
  color: var(--c-text);
}

.tabs__tab.active {
  border-bottom-color: var(--c-accent);
  color: var(--c-text);
  font-weight: 600;
}

.tabs__count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.5rem;
  height: 1.25rem;
  padding: 0 0.35rem;
  border-radius: 999px;
  background: var(--badge-bg);
  color: var(--badge-text);
  font-size: 0.7rem;
  font-weight: 700;
}

/* ── Segmented filter ──
   Narrows the set the tabs already chose (Active | Inactive | All).
   ~32px and lighter than .tabs — the nesting order must stay visible. */
.segmented {
  display: inline-flex;
  overflow: hidden;
  border: 1px solid var(--c-border-soft);
  border-radius: var(--r-md);
}

.segmented__btn {
  display: inline-flex;
  flex: 1 1 auto;
  align-items: center;
  justify-content: center;
  min-height: 32px;
  padding: 0.3rem 0.75rem;
  border-right: 1px solid var(--c-border-soft);
  background: var(--c-bg);
  color: var(--c-text);
  font-size: 0.8rem;
  text-decoration: none;
  cursor: pointer;
}

.segmented__btn:last-child {
  border-right: none;
}

.segmented__btn:hover {
  background: #f5f5f5;
}

.segmented__btn.active {
  background: var(--badge-bg);
  color: var(--badge-text);
  font-weight: 600;
}

/* =========================
   Filter-toolbar component (issue #315, PR 3)
   =========================
   The five Tier 1 list pages — plus contractor_portal, a SIXTH carrier
   found in review — held this block as byte-identical page <style> copies
   (verified by hash). Eight of the nine rules are hoisted here once
   (.filter-actions is not: the shared shell uses the .toolbar primitive for
   that row); each page's adoption commit deletes its local copy —
   contractor_portal's dies with PR 4. Two rules need care:
   - A bare .filter-row global also exists (align-items: stretch; currently
     below this block — specificity decides, not order). site_management
     uses it un-nested and is unaffected by the scoped rule.
     contractor_portal DOES nest .filter-row inside .filter-section, so the
     scoped center rule matches it — harmlessly, because its local copy
     already sets center; PR 4 can delete that copy without a visual change.
   - .filter-toggle also exists globally (cursor only); the page copy adds
     white-space, merged below under the same scoping. */
.filter-section {
  margin-bottom: 1.25rem;
}

.filter-section .filter-row {
  display: flex;
  gap: 0.75rem;
  align-items: center;
}

/* Below 768px the nowrap row crushed the search input to ~31px beside the
   buttons — the audit's original View Batches complaint, faithfully
   preserved by the byte-identical page copies this component replaced.
   Search takes its own full-width row; the buttons wrap beneath at 44px.
   (Found on the PR 3 preview walk.) */
@media (max-width: 767px) {
  .filter-section .filter-row {
    flex-wrap: wrap;
  }

  .filter-section .filter-search {
    flex: 1 1 100%;
  }
}

.filter-search {
  flex: 1;
  padding: 0.85rem 0.9rem;
  border: 1px solid var(--c-border);
  border-radius: 6px;
}

.filter-section .filter-toggle {
  white-space: nowrap;
}

.filter-panel {
  margin-top: 1rem;
  padding: 1rem;
  background: #f9f9f9;
  border-radius: 6px;
  border: 1px solid var(--c-border);
}

.filter-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1rem;
}

.filter-item label {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
  margin-bottom: 0.35rem;
}

.filter-item select {
  width: 100%;
  padding: 0.5rem;
  border: 1px solid var(--c-border);
  border-radius: 4px;
}

/* =========================
   Card-mode list tables (issue #315, PR 3)
   =========================
   Tier 1 list pages render ONE markup tree: a real <table> that desktop
   shows as-is and mobile restructures into cards purely with CSS. Opt in
   with .list-table--cards on the <table>. Every <td> must carry
   data-label="..." (guarded by CardTableLabelGuardTest) except cells
   opting out via .card-cell--bare (identity cells whose content needs no
   caption). Card-only content (footers, action rows) lives INSIDE an
   existing td, marked .card-only; desktop-only cells are hidden in card
   mode with .desktop-only. Column sorting lives in the sr-only thead (PR4
   Task 3: sr-only clip, not display:none, so headers stay in the
   accessibility tree) and is deliberately unavailable to sighted mobile
   users (design: mobile is lookup, not triage) — see the thead rule below
   for how a focused sort button un-clips instead of trapping keyboard
   focus on an invisible control. */

.card-only {
  display: none;
}

/* Shared filter extras (#315): the date-range filter hides rows by class so
   it composes with each page's own inline display toggling — !important so
   an out-of-range row stays hidden even when the page JS shows it.
   [data-filter-extras-table] mirrors the JS opt-in (PR 4): contractor_portal
   keeps its own bespoke card CSS rather than adopting .list-table--cards, so
   the visibility rule needs the same explicit second selector or the
   date-range control would render but silently do nothing on that table. */
.list-table--cards tr.date-filtered,
[data-filter-extras-table] tr.date-filtered {
  display: none !important;
}

/* The sort select is mobile-only — desktop keeps its column-header sort. */
@media (min-width: 768px) {
  .filter-item--sort {
    display: none;
  }
}

@media (max-width: 767px) {
  .list-table--cards {
    border-collapse: separate;
  }

  /* Visually hide the header row but keep it in the accessibility tree —
     ported from the contractor_portal.html card dialect (issue #315 PR4
     Task 2): the data-label ::before captions above are CSS-generated
     content, which screen readers don't announce reliably, so the real
     <th> text has to stay reachable instead of being display:none'd away. */
  .list-table--cards thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  /* The column-sort controls are real <button>s inside those <th>s
     (th[data-sort] .sort-header-btn, issue #258) — CSS has no way to pull
     an element out of the tab order (that's tabindex, an HTML/JS concern),
     so clipping the thead alone would leave sighted keyboard users tabbing
     onto an invisible 1px control with no visible focus target. Rather than
     that trap, :focus-within un-clips the whole header row the moment a
     sort button receives focus, so it becomes visible right when a keyboard
     user lands on it (and re-clips on blur). Screen-reader users already
     get the header text regardless, since it was never removed from the
     accessibility tree. */
  .list-table--cards thead:focus-within {
    position: static;
    width: auto;
    height: auto;
    overflow: visible;
    clip: auto;
    white-space: normal;
  }

  .list-table--cards,
  .list-table--cards tbody {
    display: block;
    width: 100%;
  }

  .list-table--cards tbody tr {
    /* position: relative anchors the absolutely-placed status badge below. */
    display: block;
    position: relative;
    margin-top: 0.75rem;
    margin-bottom: 0.75rem;
    padding: 0.75rem 1rem;
    border: 1px solid var(--c-border-soft);
    border-radius: var(--r-md);
    background: var(--c-bg);
  }

  .list-table--cards tbody td {
    display: block;
    padding: 0.15rem 0;
    border: none;
    width: auto;
  }

  /* Field caption from the markup's own data-label, inline with the value
     (2026-07-20 density pass: a block caption cost a full line per field,
     capping scan density below 2 cards per screen). */
  .list-table--cards tbody td[data-label]::before {
    content: attr(data-label) ": ";
    display: inline;
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--c-muted);
    margin-right: 0.25rem;
  }

  /* Identity/status cells render without a caption. */
  .list-table--cards tbody td.card-cell--bare::before {
    content: none;
  }

  /* Status badge joins the identity line, top-right — it is the card's
     second fact and was buried below the description. The identity cell
     reserves the corridor so long batch/PO identities wrap under, not
     behind, the badge. */
  .list-table--cards tbody td.card-cell--bare > .status-badge {
    position: absolute;
    top: 0.75rem;
    right: 1rem;
  }

  /* The identity cell is the first *rendered* cell: first-child on most
     pages, but aws_portal leads with a desktop-only Batch column, so the
     adjacent-sibling form covers identity cells behind hidden leaders. */
  .list-table--cards tbody td.card-cell--bare:first-child,
  .list-table--cards tbody td.desktop-only:first-child + td.card-cell--bare {
    padding-right: 9rem;
  }

  .list-table--cards .desktop-only {
    display: none;
  }

  /* revert, not block: span.card-only (inline prefixes/suffixes) must flow
     with their line; div.card-only (footers, action rows) stays block. */
  .list-table--cards .card-only {
    display: revert;
  }

  /* PO/batch identity segments wrap between, never inside, each other. */
  .list-table--cards .po-pair {
    white-space: nowrap;
  }

  .list-table--cards .card-footer {
    margin-top: 0.5rem;
    padding-top: 0.5rem;
    border-top: 1px solid var(--c-border-soft);
    font-size: 0.8rem;
    color: var(--c-muted);
  }
}

.filter-toggle,
button[type="button"] {
  cursor: pointer;
}

.filter-row {
  display: flex;
  gap: 0.75rem;
  align-items: stretch;
}

.remove-photo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.required-note {
  font-size: 0.85rem;
  margin: 0;
}

/* Upload */
.upload-button {
  display: block;
  width: 100%;
  padding: 0.75rem;
  font-size: 0.95rem;
  font-weight: 600;
  text-align: center;
  background-color: #fff;
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  cursor: pointer;
}

/* Form Help Text */
.form-help {
  font-size: 0.85rem;
  color: var(--c-muted);
  margin-top: 0.25rem;
}

.approval-required-hint {
  /* #b45309 (amber-700), not #d97706 (amber-600): the lighter shade only
     hits 3.18:1 on white, below WCAG's 4.5:1 floor for this small
     (0.85rem/13.6px) text -- axe caught it on the Receive Batch page e2e
     smoke (#315 PR6 Task 9). #b45309 matches this template's own
     .label-incomplete-hint warning color and clears 5.02:1. */
  color: #b45309;
  font-weight: bold;
}

.form-help--approval {
  color: #b45309;
  font-style: italic;
}

/* Form Section Title */
.form-section-title {
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--c-accent);
  margin: 0 0 0.75rem;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--c-border-soft);
}

/* (Button sizes now defined in the button system above) */

/* =========================
   Table (Users)
   ========================= */

.table-card {
  border-top: 1px solid var(--c-border);
}

.users-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 0.75rem;
}

.users-table th,
.users-table td {
  text-align: left;
  padding: 0.85rem 0.5rem;
  border-bottom: 1px solid var(--c-border);
  font-size: 0.95rem;
  vertical-align: middle;
}

.users-table th {
  font-weight: 700;
}

.email-link {
  color: var(--c-link);
  text-decoration: underline;
}

.col-last-active {
  white-space: nowrap;
}

/* Desktop table improvements */
@media (min-width: 768px) {
  .users-table th,
  .users-table td {
    padding: 1rem 0.75rem;
    font-size: 1rem;
  }
}

@media (min-width: 1024px) {
  .users-table th,
  .users-table td {
    padding: 1.1rem 1rem;
  }
}

/* Responsive column hiding */
@media (max-width: 900px) {
  .col-last-active {
    display: none;
  }
}
@media (max-width: 720px) {
  .col-active {
    display: none;
  }
}
@media (max-width: 560px) {
  .col-admin {
    display: none;
  }
}

/* =========================
   Modal
   ========================= */

.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 1000;
}

.modal-backdrop.active {
  display: flex;
}

.modal {
  background: #fff;
  width: 100%;
  max-width: 28rem;
  padding: 1.5rem;
  border-radius: var(--r-xl);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25);
}

.modal--wide {
  max-width: 40rem;
}

.modal h2 {
  margin-top: 0;
  margin-bottom: 0.5rem;
}

.modal-blurb {
  font-size: 0.95rem;
  margin-bottom: 1.25rem;
  color: var(--c-text);
}

.modal label {
  display: block;
  font-weight: 600;
  margin-bottom: 0.25rem;
}

.modal input[type="email"] {
  width: 100%;
  padding: 0.6rem 0.7rem;
  font-size: 0.95rem;
  border-radius: var(--r-md);
  border: 1px solid var(--c-border);
}

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.75rem;
  margin-top: 1.5rem;
}

/* =========================
   Cards / Layout Helpers
   ========================= */

.card {
  border-top: 1px solid var(--c-border);
  padding: 1rem 0;
}

.card-title {
  margin: 0 0 0.75rem;
  font-size: 1.05rem;
}

.card-row {
  display: flex;
  gap: 2rem;
  flex-wrap: wrap;
  margin-bottom: 0.75rem;
}

.card-field .label {
  font-weight: 700;
  font-size: 0.85rem;
  margin-bottom: 0.25rem;
}

.card-field .value {
  font-size: 0.95rem;
}

.grid-two {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2rem;
}

@media (max-width: 720px) {
  .grid-two {
    grid-template-columns: 1fr;
    gap: 1rem;
  }
}

/* =========================
   Chips
   ========================= */

.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.chip {
  display: inline-block;
  padding: 0.35rem 0.6rem;
  border-radius: 999px;
  border: 1px solid var(--c-border);
  font-size: 0.9rem;
}

.chip--subtle {
  background: #f5f5f5;
}

/* =========================
   Accordion
   ========================= */

.accordion {
  border-top: 1px solid var(--c-border);
}

.accordion:last-child {
  border-bottom: 1px solid var(--c-border);
}

.accordion-header {
  width: 100%;
  background: transparent;
  border: none;
  padding: 0.9rem 0;
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  text-align: left;
  cursor: pointer;
  font-size: 0.95rem;
}

.accordion-icon {
  font-weight: 800;
  width: 1.5rem;
  text-align: center;
}

.accordion-body {
  padding: 0 0 1rem 0;
}

.muted {
  color: #444;
  font-size: 0.92rem;
}

.activity-list {
  margin: 0.6rem 0 0 1.2rem;
}

/* =========================
   Form Grid / Toggles (modal layouts)
   ========================= */

.form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem 2rem;
}

@media (max-width: 720px) {
  .form-grid {
    grid-template-columns: 1fr;
  }
}

.toggle-row {
  display: flex;
  gap: 1.5rem;
  align-items: flex-end;
  flex-wrap: wrap;
}

.modal-section {
  margin-top: 1.25rem;
}

.section-title {
  font-weight: 700;
  margin-bottom: 0.5rem;
}

.checkbox-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.6rem 1rem;
}

.checkbox-grid--small {
  grid-template-columns: repeat(5, minmax(2rem, 1fr));
}

@media (max-width: 560px) {
  .checkbox-grid {
    grid-template-columns: 1fr;
  }
  .checkbox-grid--small {
    grid-template-columns: repeat(3, minmax(2rem, 1fr));
  }
}

.checkbox {
  display: flex;
  gap: 0.5rem;
  align-items: center;
}

/* =========================
   Edit User modal spacing
   ========================= */

.form-basics {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 2rem;
}

.basic-block {
  min-width: 16rem;
}

.basic-block--right {
  min-width: auto;
}

.checkbox--inline {
  margin-top: 0.25rem;
}

.modal-section--spaced {
  padding-top: 1rem;
  margin-top: 1rem;
  border-top: 1px solid #e0e0e0;
}

@media (max-width: 720px) {
  .form-basics {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.75rem;
  }

  .basic-block {
    min-width: 100%;
  }
}

/* =========================
   Recent Activity (muted)
   ========================= */

.activity-muted {
  margin-top: 1.25rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--c-border-soft);
  color: var(--c-muted);
}

.activity-toggle {
  background: transparent;
  border: none;
  padding: 0;
  color: #555;
  text-decoration: underline;
  cursor: pointer;
  font-size: 0.95rem;
}

.activity-panel {
  margin-top: 0.6rem;
  background: #f6f6f6;
  border: 1px solid var(--c-border-soft);
  border-radius: 0.6rem;
  padding: 0.5rem 0.75rem;
}

.activity-list-simple {
  font-size: 0.82rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.activity-list-simple li {
  display: grid;
  grid-template-columns: 13.5rem 1fr;
  column-gap: 0.4rem;
  padding: 0.35rem 0;
  border-bottom: 1px solid var(--c-border-soft);
}

.activity-list-simple li:last-child {
  border-bottom: none;
}

.activity-ts {
  font-weight: 500;
  color: #555;
  white-space: nowrap;
}

.activity-text {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
    "Liberation Mono", "Courier New", monospace;
  font-size: 0.8rem;
  color: var(--c-text);
}

@media (max-width: 720px) {
  .activity-list-simple li {
    grid-template-columns: 1fr;
  }

  .activity-ts {
    margin-bottom: 0.15rem;
    font-size: 0.75rem;
  }
}

/* =========================
   User Summary
   ========================= */

.user-summary {
  border-top: 1px solid var(--c-border);
  padding: 1rem 0 1.25rem;
}

.user-identity {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 2rem;
  flex-wrap: wrap;
}

.user-primary {
  min-width: 16rem;
}

.user-name {
  font-size: 1.15rem;
  font-weight: 700;
  margin-bottom: 0.15rem;
}

.user-email {
  font-size: 0.95rem;
}

.user-meta {
  display: flex;
  gap: 2rem;
  flex-wrap: wrap;
}

.meta-item {
  font-size: 0.85rem;
}

.meta-label {
  display: block;
  font-weight: 700;
  margin-bottom: 0.15rem;
}

.meta-value {
  white-space: nowrap;
}

@media (max-width: 720px) {
  .user-identity {
    flex-direction: column;
    gap: 0.75rem;
  }

  .user-meta {
    gap: 1.25rem;
  }
}

/* =========================
   View: Receive Batch
   ========================= */

body#view-receive-batch .form-group-row {
    display: flex;
    gap: 1rem;
    width: 100%;
}

body#view-receive-batch .form-group-row > .form-group {
    flex: 1 1 0;
    min-width: 0;
}

/* Campus ID's placeholder ("Select campus ID") is the widest text in its row;
   give it and Phase a base width each so neither is cut on a 375px phone. */
body#view-receive-batch .form-group-row > .form-group:has(#id_campus_id) {
    flex: 1 1 11.25rem;
}

body#view-receive-batch .form-group-row > .form-group:has(#id_phase_number) {
    flex: 1 1 9rem;
}

@media (max-width: 374px) {
    body#view-receive-batch .form-group-row > .form-group:is(:has(#id_campus_id), :has(#id_phase_number)) select {
        padding-left: 0.5rem;
        padding-right: 1.75rem;
        background-position: right 0.5rem center;
    }
}

body#view-receive-batch .form-group input,
body#view-receive-batch .form-group select,
body#view-receive-batch .form-group textarea {
    width: 100%;
}

body#view-receive-batch .add-item-button,
body#view-receive-batch [x-show] .button {
  border: 1px solid var(--c-border);
  background: #F8F9FA;
  cursor: pointer;
}

/* The [x-show] .button ghosting above predates the #315 primitives and its
   id specificity beats .button--primary — the stale-draft banner's Continue
   rendered white-on-grey (invisible). Restore the primitive for primaries
   only; everything else the legacy rule touches stays as-is (receive-form
   cleanup is parked for the vendor slice). */
body#view-receive-batch [x-show] .button--primary {
  border-color: var(--c-accent);
  background-color: var(--c-accent);
}

body#view-receive-batch .form-group-row {
  padding-bottom: 1rem;
}

body#view-receive-batch .form-group-row > * {
  padding-bottom: 0;
}

body#view-receive-batch .form-section:first-of-type {
  margin-top: 0;
}

/* 3-up row behavior: stack only on truly tiny screens */
@media (max-width: 359px) {
    body#view-receive-batch .form-group-row--three {
        flex-direction: column;
    }
}


/* =========================
   View: Batch Status
   (scoped + clean)
   ========================= */

body#view-batch-status .batch-title {
  margin-top: 0.5rem;
}

body#view-batch-status .batch-id {
  font-weight: 700;
}

body#view-batch-status .status-timeline {
  max-width: 50rem;
  margin: 0 auto;
}

body#view-batch-status .status-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

body#view-batch-status .status-item {
  display: grid;
  grid-template-columns: 3.25rem 1fr auto;
  column-gap: 0.9rem;
  align-items: start;
  padding: 0.85rem 0;
}

body#view-batch-status .status-marker {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
}

body#view-batch-status .status-dot {
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 999px;
  display: grid;
  place-items: center;
  background: var(--c-accent);
  color: #fff;
  position: relative;
  z-index: 2;
}

body#view-batch-status .status-line {
  position: absolute;
  top: 2rem;
  bottom: -2rem;
  width: 0.22rem;
  background: var(--c-accent);
  border-radius: 999px;
}

body#view-batch-status .status-item:last-child .status-line {
  display: none;
}

body#view-batch-status .status-check {
  font-size: 1.05rem;
  font-weight: 800;
  line-height: 1;
}

body#view-batch-status .status-name {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--c-accent);
  margin-bottom: 0.15rem;
}

body#view-batch-status .status-ts {
  color: var(--c-muted-2);
  font-size: 0.82rem;
  line-height: 1.25;
}

body#view-batch-status .status-detail-link {
  color: var(--c-accent);
  text-decoration: underline;
  font-weight: 700;
  font-size: 0.92rem;
  white-space: nowrap;
}

body#view-batch-status .status-action-spacer {
  display: inline-block;
  width: 6.2rem;
  height: 1rem;
}

/* states */
body#view-batch-status .status-item--pending .status-dot,
body#view-batch-status .status-item--pending .status-line {
  background: #bdbdbd;
}

body#view-batch-status .status-name--muted {
  color: #b0b0b0;
  font-weight: 700;
}

body#view-batch-status .status-item--active .status-check {
  display: none;
}

body#view-batch-status .status-active-dot {
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 999px;
  background: #fff;
}

/* active transitions to gray line afterward */
body#view-batch-status .status-item--active .status-line {
  background: #bdbdbd;
}

body#view-batch-status .status-modal-body {
  border-top: 1px solid #e6e6e6;
  padding-top: 0.9rem;
  margin-top: 0.75rem;
  line-height: 1.45;
}

@media (max-width: 720px) {
  body#view-batch-status .status-item {
    grid-template-columns: 3rem 1fr auto;
    column-gap: 0.8rem;
  }

  body#view-batch-status .status-dot {
    width: 2.25rem;
    height: 2.25rem;
  }

  body#view-batch-status .status-name {
    font-size: 1.02rem;
  }

  body#view-batch-status .status-detail-link {
    font-size: 0.9rem;
  }
}

/* =========================
   Filters
   ========================= */

body#view-upcoming-deliveries .deliveries-filters {
  margin: 0 0 1rem;
}



body#view-upcoming-deliveries .filter-item,
body#view-upcoming-deliveries .filter-item--search {
  width: 100%;
  min-width: 0;
  margin: 0 0 0.85rem 0;
}

body#view-upcoming-deliveries .filter-item select,
body#view-upcoming-deliveries .filter-item input {
  width: 100%;
}

/* =========================
   Table
   ========================= */

body#view-upcoming-deliveries .deliveries-card {
  margin-top: 0.5rem;
}

body#view-upcoming-deliveries .deliveries-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 0;
}

body#view-upcoming-deliveries .deliveries-table th,
body#view-upcoming-deliveries .deliveries-table td {
  text-align: left;
  vertical-align: middle;
  border-bottom: 1px solid var(--c-border);
  padding: 0.85rem 0.35rem;
  font-size: 0.9rem;
}

/* Column behavior */
body#view-upcoming-deliveries .col-date {
  width: 1%;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  text-align: center;
}

body#view-upcoming-deliveries .col-action {
  width: 1%;
  white-space: nowrap;
}

body#view-upcoming-deliveries .col-status {
  color: var(--c-muted);
  white-space: normal;
  text-align: center;
}

/* =========================
   Batch link + phase
   ========================= */

body#view-upcoming-deliveries .batch-link {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  white-space: nowrap;
}

body#view-upcoming-deliveries .batch-link:hover {
  color: var(--c-accent);
}

body#view-upcoming-deliveries .batch-phase {
  color: var(--c-muted);
  margin-left: 0.35rem;
}

/* =========================
   Items (single type)
   ========================= */

body#view-upcoming-deliveries .items-single {
  color: var(--c-muted);
  font-weight: 600;
  white-space: nowrap;
}

body#view-upcoming-deliveries .items-single strong {
  font-weight: 800;
}

/* =========================
   Buttons
   ========================= */

body#view-upcoming-deliveries .button--compact {
  padding: 0.42rem 0.8rem;
  font-size: 0.85rem;
  border-radius: 999px;
}

/* =========================
   Responsive Adjustments
   ========================= */

/* Hide Action column on small screens */
@media (max-width: 520px) {
  body#view-upcoming-deliveries th.col-action,
  body#view-upcoming-deliveries td.col-action {
    display: none;
  }

  body#view-upcoming-deliveries .deliveries-table th,
  body#view-upcoming-deliveries .deliveries-table td {
    padding: 0.85rem 0.3rem;
    font-size: 0.88rem;
  }
}

/* =========================
   View: Complete Delivery
   ========================= */

/* If you want a slightly larger notes box ONLY on this view */
body#view-complete-delivery textarea {
  min-height: 8rem;
}

/* =========================
   View: Request Delivery
   ========================= */

/* Delivery Address should be a larger box (desktop mockup style) */
body#view-request-delivery textarea {
  min-height: 8rem;
}

/* =========================
   View: Create Outgoing BOL
   ========================= */

/* =========================
   View: Upcoming Deliveries
   ========================= */

/* Basic layout hooks (only if you don't already have equivalents) */
body#view-upcoming-deliveries .page {
  max-width: 980px;
  margin: 0 auto;
  padding: 1rem;
}

body#view-upcoming-deliveries .header {
  position: relative;
  text-align: center;
  padding-top: 0.25rem;
}

body#view-upcoming-deliveries .sign-out {
  position: absolute;
  top: 0;
  right: 0;
  text-decoration: underline;
}


body#view-upcoming-deliveries .page-title {
  margin: 0.25rem 0 0.75rem;
  color: var(--c-accent);
}

body#view-upcoming-deliveries .page-rule {
  border: 0;
  border-top: 2px solid var(--c-accent);
  margin: 0.75rem 0 1.25rem;
}

/* Filters */
body#view-upcoming-deliveries .deliveries-filters {
  margin-bottom: 1.25rem;
}

body#view-upcoming-deliveries .filter-label {
  display: block;
  margin-bottom: 0.5rem;
  font-weight: 700;
}

body#view-upcoming-deliveries .filter-search {
  width: 100%;
  padding: 0.85rem 0.9rem;
  border: 1px solid var(--c-border);
  border-radius: 6px;
}

/* Table container */
body#view-upcoming-deliveries .deliveries-table-wrap {
  border-top: 1px solid var(--c-border);
}

/* Table */
body#view-upcoming-deliveries .deliveries-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed; /* enables predictable truncation */
  margin-top: 0;
}

body#view-upcoming-deliveries .deliveries-table th,
body#view-upcoming-deliveries .deliveries-table td {
  text-align: left;
  vertical-align: middle;
  border-bottom: 1px solid var(--c-border);
  padding: 0.85rem 0.55rem; /* column spacing */
  font-size: clamp(0.78rem, 2.2vw, 0.92rem);
  line-height: 1.25;

  /* allow our intentional 2-line stack */
  white-space: normal;
  overflow: hidden;
}

/* Column widths (stacked mobile-friendly) */
body#view-upcoming-deliveries th.col-batch,
body#view-upcoming-deliveries td.col-batch { width: 30%; }

body#view-upcoming-deliveries th.col-location,
body#view-upcoming-deliveries td.col-location { width: 34%; }

body#view-upcoming-deliveries th.col-status,
body#view-upcoming-deliveries td.col-status { width: 20%; }

body#view-upcoming-deliveries th.col-action,
body#view-upcoming-deliveries td.col-action {
  width: 16%;
  text-align: right;
}

/* Headings */
/* Status heading must NOT be muted */
body#view-upcoming-deliveries th.col-status {
  color: inherit;
}

/* Stacked cell lines: never wrap, always ellipsis */
body#view-upcoming-deliveries .cell-primary,
body#view-upcoming-deliveries .cell-sub {
  display: block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

body#view-upcoming-deliveries .cell-sub {
  color: var(--c-muted);
  font-size: 0.86em;
  margin-top: 0.12rem;
}

/* Batch link */
body#view-upcoming-deliveries .batch-link {
  color: inherit;
  font-weight: 600;
}

body#view-upcoming-deliveries .batch-link:hover,
body#view-upcoming-deliveries .batch-link:focus-visible {
  text-decoration: underline;
}

/* Muted rows (delivered/etc) */
body#view-upcoming-deliveries tr.row--muted td {
  color: #b0b0b0;
}

body#view-upcoming-deliveries tr.row--muted td .cell-sub {
  color: #b0b0b0;
}

/* Compact buttons for this view */
body#view-upcoming-deliveries .button--compact {
  padding: 0.42rem 0.8rem;
  font-size: 0.82rem;
  border-radius: 999px;
  white-space: nowrap;
  min-width: 6rem;
  text-align: center;
}

/* Filter toggle fixed width */
body#view-upcoming-deliveries .filter-toggle {
  min-width: 8rem;
  text-align: center;
  cursor: pointer;
}

/* Reduce expand-cell and child-indent */
body#view-upcoming-deliveries .expand-cell {
  width: 2rem;
  padding: 0.25rem;
}

body#view-upcoming-deliveries .child-indent {
  width: 1.5rem;
}

/* Multi-item batches each on own line */
body#view-upcoming-deliveries .item-list .item {
  display: block;
}

/* If Action is too tight, drop it */
@media (max-width: 520px) {
  body#view-upcoming-deliveries th.col-action,
  body#view-upcoming-deliveries td.col-action {
    display: none;
  }

  body#view-upcoming-deliveries th.col-batch,
  body#view-upcoming-deliveries td.col-batch { width: 34%; }

  body#view-upcoming-deliveries th.col-location,
  body#view-upcoming-deliveries td.col-location { width: 40%; }

  body#view-upcoming-deliveries th.col-status,
  body#view-upcoming-deliveries td.col-status { width: 26%; }
}

/* Mobile overrides for upcoming deliveries */
@media (max-width: 768px) {
  body#view-upcoming-deliveries .deliveries-table {
    min-width: unset;
  }
  /* NOTE: .expand-cell must stay visible — it is the first column of the
     grid card layout. Hiding it (old table-era rule) shifted every row one
     grid column left, so the status chip rendered on top of the batch
     number (2026-07-18 mobile inspection finding). */
  body#view-upcoming-deliveries .deliveries-table th:first-child,
  body#view-upcoming-deliveries .child-indent {
    display: none;
  }
  body#view-upcoming-deliveries .action-buttons {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
  }
}

/* ==========================================================================
   Item quantity tables (2026-07-18 redesign)
   Description-anchored rows: each item leads with its equipment description
   (unit type as a tag beneath) so same-type lines are distinguishable, and
   every table closes with a Total footer. Used by batch detail Contents,
   delivery-request item tables, the request-delivery modal and the batch
   status page. Display-only component — it renders existing quantities.
   ========================================================================== */

.iqt-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.iqt-table th,
.iqt-table td {
  padding: 0.45rem 0.5rem;
  border-bottom: 1px solid var(--c-border-soft);
  text-align: right;
  vertical-align: top;
}

.iqt-table th:first-child,
.iqt-table td:first-child {
  text-align: left;
  width: 55%;
}

.iqt-table thead tr {
  border-bottom: 2px solid var(--c-border);
}

.iqt-table thead th {
  font-weight: 600;
  color: var(--c-muted);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  border-bottom: none;
}

.iqt-desc {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-weight: 600;
  line-height: 1.35;
}

.iqt-type {
  display: block;
  font-size: 0.78rem;
  color: var(--c-muted);
  margin-top: 0.1rem;
}

/* Name-leading item lines: "NAME — description" (issue #315 PR 6 Task 6,
   apps/inventory/templatetags/item_line.py). .item-line-desc overrides
   .iqt-desc's inherited font-weight:600 where the two nest, so only the
   name reads bold. */
.item-line-name {
  font-weight: 600;
}
.item-line-desc {
  color: var(--c-muted);
  font-weight: 400;
}

/* Header label pair: desktop shows the full word. */
.iqt-table .th-full {
  display: inline;
}
.iqt-table .th-short {
  display: none;
}

.iqt-table tfoot .iqt-total td,
.iqt-table tfoot .iqt-total th {
  font-weight: 700;
  border-bottom: none;
  border-top: 2px solid var(--c-border);
}

@media (max-width: 640px) {
  .iqt-table {
    /* Slightly smaller than body copy: descriptions run long and the
       numbers matter more than font size here (Matt, 2026-07-19). */
    font-size: 0.82rem;
    /* Fixed layout: auto tables grow past width:100% when the unbreakable
       DELIVERED/REMAINING headers exceed their share, bleeding out of
       padded cards (batch-status finding, 2026-07-19). */
    table-layout: fixed;
  }
  .iqt-table th,
  .iqt-table td {
    padding: 0.35rem 0.3rem;
  }
  .iqt-table th:first-child,
  .iqt-table td:first-child {
    width: 46%;
  }
  .iqt-table thead th {
    font-size: 0.72rem;
    white-space: normal;
  }
  /* Dual header labels: full words desktop, short forms on phones —
     mid-word breaking ("AVAILA-BLE") read as smooshed (Matt, 2026-07-19). */
  .iqt-table .th-short {
    display: inline;
  }
  .iqt-table .th-full {
    display: none;
  }
  /* Narrow columns push the distinguishing suffix ("(4) 32' UNITS") onto a
     third line — two-line clamping made same-prefix items identical again. */
  .iqt-desc {
    -webkit-line-clamp: 3;
  }
}

/* =========================
   View: View Batches
   ========================= */

/* Layout hooks (only on this page) */
body#view-batches .page {
  margin: 0 auto;
  padding: 1rem;
}

body#view-batches .header {
  position: relative;
  text-align: center;
  padding-top: 0.25rem;
}

body#view-batches .sign-out {
  position: absolute;
  top: 0;
  right: 0;
  text-decoration: underline;
}


body#view-batches .page-title {
  margin: 0.25rem 0 0.75rem;
  color: var(--c-accent);
}

body#view-batches .page-rule {
  border: 0;
  border-top: 2px solid var(--c-accent);
  margin: 0.75rem 0 1.25rem;
}

/* Filters */
body#view-batches .batches-filters {
  margin-bottom: 1.25rem;
}

body#view-batches .filter-label {
  display: block;
  margin-bottom: 0.5rem;
  font-weight: 700;
}

body#view-batches .filter-search {
  width: 100%;
  padding: 0.85rem 0.9rem;
  border: 1px solid var(--c-border);
  border-radius: 6px;
}

/* Table wrap */
body#view-batches .batches-table-wrap {
  border-top: 1px solid var(--c-border);
}

/* Table (unstacked cells) */
body#view-batches .batches-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed; /* predictable truncation */
}

body#view-batches .batches-table th,
body#view-batches .batches-table td {
  text-align: left;
  vertical-align: middle;
  border-bottom: 1px solid var(--c-border);
  padding: 0.85rem 0.55rem; /* spacing between columns */
  font-size: clamp(0.78rem, 2.1vw, 0.92rem);
  line-height: 1.25;
}

/* Hard no-wrap/ellipsis is the DESKTOP truncation contract (with
   table-layout: fixed). Desktop-only since 2026-07-20: the id selector
   outguns the card block at any width, and it was clipping the paired
   count—description lines on mobile cards. Cards wrap instead. */
@media (min-width: 768px) {
  body#view-batches .batches-table th,
  body#view-batches .batches-table td {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}

body#view-batches .batches-table th {
  font-weight: 700;
}

/* Column widths (desktop/tablet baseline) */
body#view-batches th.col-batch,
body#view-batches td.col-batch { width: 22%; }

body#view-batches th.col-po,
body#view-batches td.col-po { width: 22%; }

body#view-batches th.col-status,
body#view-batches td.col-status { width: 22%; }

body#view-batches th.col-received,
body#view-batches td.col-received { width: 17%; }

body#view-batches th.col-delivered,
body#view-batches td.col-delivered { width: 17%; }

/* Batch link */
body#view-batches .batch-link {
  color: inherit;
  font-weight: 800;
  text-decoration: none;
}

body#view-batches .batch-link:hover,
body#view-batches .batch-link:focus-visible {
  text-decoration: underline;
}

/* Optional muted row */
body#view-batches tr.row--muted td {
  color: #b0b0b0;
}

/* At small resolutions: both dates fall off */
@media (max-width: 560px) {
  body#view-batches th.col-received,
  body#view-batches td.col-received,
  body#view-batches th.col-delivered,
  body#view-batches td.col-delivered {
    display: none;
  }

  /* Rebalance remaining columns */
  body#view-batches th.col-batch,
  body#view-batches td.col-batch { width: 34%; }

  body#view-batches th.col-po,
  body#view-batches td.col-po { width: 33%; }

  body#view-batches th.col-status,
  body#view-batches td.col-status { width: 33%; }
}


body#view-batches .batch-link {
  text-decoration: underline;
}

/* =========================
   View: Batch Detail (Single Batch)
   ========================= */

/* Title formatting */
body#view-batch-detail .batch-page-title {
  font-size: 1.6rem;
  font-weight: 700;
  color: var(--c-accent);
  margin: 0;
}

body#view-batch-detail .batch-page-id {
  font-weight: 800;
}

body#view-batch-detail .batch-page-status {
  font-weight: 700;
}

/* Sections */
body#view-batch-detail .batch-section {
  border-top: 1px solid var(--c-border);
  padding: 1rem 0 1.25rem;
}

body#view-batch-detail .batch-section:first-of-type {
  margin-top: 0.25rem;
}

body#view-batch-detail .batch-section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding-bottom: 0.65rem;
}

body#view-batch-detail .batch-section-title {
  margin: 0;
  font-size: 1.15rem;
  font-weight: 700;
}

body#view-batch-detail .batch-section-edit {
  color: var(--c-text);
  text-decoration: underline;
  text-underline-offset: 2px;
  font-weight: 600;
}

body#view-batch-detail .batch-section-spacer {
  width: 2rem;
}

/* Key/Value grid */
body#view-batch-detail .batch-kv-grid {
  display: grid;
  gap: 2rem;
}

body#view-batch-detail .batch-kv-grid--4 {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

body#view-batch-detail .batch-kv-grid--3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

body#view-batch-detail .batch-kv {
  min-width: 0;
}

body#view-batch-detail .batch-k {
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--c-muted);
  margin-bottom: 0.35rem;
}

body#view-batch-detail .batch-v {
  font-size: 1.1rem;
  font-weight: 500;
}

/* Contents grid */
body#view-batch-detail .batch-contents-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1.4fr;
  gap: 2rem;
}

body#view-batch-detail .batch-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

body#view-batch-detail .batch-list li {
  font-size: 1.1rem;
  padding: 0.15rem 0;
}

/* Events */
body#view-batch-detail .batch-events {
  display: grid;
  gap: 0.7rem;
  padding-top: 0.25rem;
}

body#view-batch-detail .batch-event {
  display: grid;
  grid-template-columns: 1.6fr 1fr auto;
  gap: 1rem;
  align-items: center;
  padding: 0.25rem 0;
}

body#view-batch-detail .batch-event-name {
  font-size: 1.05rem;
  font-weight: 500;
}

body#view-batch-detail .batch-event-date {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  font-size: 1.05rem;
}

body#view-batch-detail .batch-event-link {
  color: var(--c-text);
  text-decoration: underline;
  text-underline-offset: 2px;
  font-weight: 500;
  white-space: nowrap;
}

body#view-batch-detail .batch-event--muted {
  color: #b0b0b0;
}

body#view-batch-detail .batch-event--muted .batch-event-link {
  color: #b0b0b0;
}

/* Responsive */
@media (max-width: 980px) {
  body#view-batch-detail .batch-kv-grid--4 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.25rem;
  }

  body#view-batch-detail .batch-kv-grid--3 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.25rem;
  }

  body#view-batch-detail .batch-contents-grid {
    grid-template-columns: 1fr;
    gap: 1.25rem;
  }
}

@media (max-width: 560px) {
  body#view-batch-detail .batch-kv-grid--3 {
    grid-template-columns: 1fr;
  }

  body#view-batch-detail .batch-event {
    grid-template-columns: 1fr;
    gap: 0.25rem;
    align-items: start;
  }

  body#view-batch-detail .batch-event-link {
    justify-self: start;
  }
}

body#view-batch-detail .approval-actions h3 {
  padding-top: 0;
}

body#view-batch-detail .info-row {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

body#view-batch-detail .save-changes-button,
body#view-batch-detail .request-delivery-button {
  background-color: #FCCC0A;
  color: #000;
  border-color: #FCCC0A;
}

/* =========================
   View: Notifications
   ========================= */

body#view-notifications .page {
  max-width: 40rem;
}

.notif-page {
  padding-top: 0.25rem;
}

.notif-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 1rem;
}

.notif-title {
  font-size: 1.35rem;
  font-weight: 700;
  margin: 0;
}

.notif-header-actions {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.notif-btn-mark-all {
  background: none;
  border: 1px solid var(--c-link);
  color: var(--c-link);
  padding: 0.3rem 0.7rem;
  border-radius: var(--r-sm);
  font-size: 0.8rem;
  cursor: pointer;
}

.notif-btn-mark-all:hover {
  background: var(--c-link);
  color: #fff;
}

.notif-unread-count {
  font-size: 0.85rem;
  color: var(--c-muted);
}

/* Filters */
.notif-filters {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 1rem;
}

.notif-filter {
  padding: 0.3rem 0.85rem;
  border-radius: 999px;
  font-size: 0.85rem;
  text-decoration: none;
  background: #e4e6eb;
  color: var(--c-text);
}

.notif-filter:hover {
  background: #d0d2d6;
}

.notif-filter--active {
  background: var(--c-link);
  color: #fff;
}

/* Notification list */
.notif-list {
  border: 1px solid var(--c-border-soft);
  border-radius: var(--r-md);
  overflow: hidden;
}

.notif-item {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--c-border-soft);
}

.notif-item:last-child {
  border-bottom: none;
}

.notif-item--unread {
  background: #eef3ff;
}

.notif-item-body {
  flex: 1;
  min-width: 0;
}

.notif-item-top {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.2rem;
}

.notif-item-title {
  font-weight: 600;
  font-size: 0.95rem;
}

.notif-new-badge {
  display: inline-block;
  padding: 0.1rem 0.45rem;
  border-radius: var(--r-sm);
  background: #dbeafe;
  color: var(--c-link);
  font-size: 0.7rem;
  font-weight: 600;
}

.notif-item-message {
  margin: 0.15rem 0;
  font-size: 0.88rem;
  color: var(--c-muted);
}

.notif-item-time {
  font-size: 0.75rem;
  color: var(--c-muted-2);
}

.notif-item-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-left: 0.75rem;
  flex-shrink: 0;
}

.notif-link {
  font-size: 0.85rem;
  color: var(--c-link);
  text-decoration: none;
}

.notif-link:hover {
  text-decoration: underline;
}

.notif-btn-read {
  background: none;
  border: none;
  color: var(--c-muted-2);
  cursor: pointer;
  padding: 0.25rem;
  border-radius: var(--r-sm);
  display: inline-flex;
}

.notif-btn-read:hover {
  color: #28a745;
  background: #e8f5e9;
}

.notif-empty {
  padding: 2.5rem 1rem;
  text-align: center;
  color: var(--c-muted);
}

/* =========================
   View: Landing (Home)
   ========================= */

body#view-landing {
  --c-accent: #111; /* keep neutral for header/divider */
}

/* Layout - responsive */
body#view-landing .page {
  max-width: 40rem; /* mobile-first */
}

body#view-landing .landing {
  display: grid;
  gap: 1rem;
  padding-top: 0.25rem;
}

/* Tiles */
body#view-landing .landing-tile {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  min-height: 4.25rem;
  padding: 1.25rem 1.5rem;

  border-radius: var(--r-md);
  border: 1px solid rgba(0, 0, 0, 0.28);

  background: var(--subway-gray); /* Readable fallback for new workflow tiles. */
  color: #fff;
  text-decoration: none;
  font-weight: 700;
  font-size: 1.35rem;
  line-height: 1.1;

  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

body#view-landing .landing-tile:hover,
body#view-landing .landing-tile:focus-visible {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  outline: none;
}

/* Tile Colors (subway palette) */
body#view-landing .landing-tile--receive    { background: var(--subway-orange); }
body#view-landing .landing-tile--drafts     { background: #C06000; }          /* Amber — draft/in-progress */
body#view-landing .landing-tile--upcoming   { background: var(--subway-blue); }
body#view-landing .landing-tile--bol        { background: var(--subway-magenta); }
body#view-landing .landing-tile--complete   { background: var(--subway-green); }
body#view-landing .landing-tile--batches    { background: var(--subway-gray); }
body#view-landing .landing-tile--approval   { background: var(--subway-lime); }
/* Unassigned queue: the warehouse filter fits its longest name beside the arrow. */
body#view-unassigned-queue .filter-bar select[name="warehouse"] { min-width: 11rem; }

body#view-landing .landing-tile--unassigned { background: var(--subway-yellow); }
body#view-landing .landing-tile--kickback   { background: var(--subway-red); }
body#view-landing .landing-tile--invoice    { background: var(--subway-purple); }
body#view-landing .landing-tile--sites      { background: var(--subway-teal); }
body#view-landing .landing-tile--users      { background: var(--subway-brown); }
body#view-landing .landing-tile--database   { background: var(--subway-blue); }
body#view-landing .landing-tile--warehouses { background: var(--subway-yellow); }
body#view-landing .landing-tile--contractor { background: var(--subway-seagreen); }
body#view-landing .landing-tile--invoices    { background: #000; }
body#view-landing .landing-tile--rates      { background: var(--subway-teal); }  /* Reused (Site Management precedent — same-page reuse already exists for --subway-blue) */
body#view-landing .landing-tile--help       { background: var(--subway-teal); }

/* Tile badge (approval queue count — positioned like notification badge) */
body#view-landing .landing-tile { position: relative; }
body#view-landing .tile-badge {
  position: absolute;
  top: -0.45rem;
  right: -0.45rem;
  min-width: 1.4rem;
  height: 1.4rem;
  padding: 0 0.35rem;
  border-radius: 999px;
  background: #1a472a;
  color: #fff;
  font-size: 0.75rem;
  font-weight: 700;
  line-height: 1.4rem;
  text-align: center;
  pointer-events: none;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
}
body#view-landing .tile-badge--danger {
  background: #dc3545;
}

/* =========================
   Crane Badge (global)
   ========================= */

.crane-badge {
  display: inline-block;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 700;
  background-color: #f8d7da;
  color: #721c24;
  margin-left: 0.35rem;
  vertical-align: middle;
}


/* ==========================================================================
   Mobile Responsive Tables & Touch Targets
   ========================================================================== */

/* At 640px: remove fixed min-width, hide lower-priority columns */
@media (max-width: 640px) {
  .batches-table {
    min-width: 0 !important;
    table-layout: auto !important;
  }

  /* Legacy narrow-table column squeeze. Card-mode tables
     (.list-table--cards) manage their own column visibility via
     data-label/desktop-only and must be exempt — without the :not() this
     rule's 0-2-0 specificity beats the card block's display:block and
     silently hides Phase on cards below 640px (found in PR 3 review).
     Non-card .batches-table pages (contractor_portal until PR 4) keep the
     squeeze. */
  .batches-table:not(.list-table--cards) .col-phase,
  .batches-table:not(.list-table--cards) .col-count,
  .batches-table:not(.list-table--cards) .col-shipped {
    display: none;
  }

  .batches-table th,
  .batches-table td {
    padding: 0.55rem 0.35rem;
    font-size: 0.85rem;
  }
}

/* At 480px: tighter spacing */
@media (max-width: 480px) {
  .batches-table th,
  .batches-table td {
    padding: 0.4rem 0.25rem;
    font-size: 0.82rem;
  }
}

/* Stats bar reflow rules removed 2026-07-21 (issue #315 PR 4): the last
   template referencing .stats-bar/.stat-item was dashboard.html's contractor
   stats bar, and that block is gone now too (walk feedback rationale). */

/* Touch targets for non-.button submit buttons.
   Excludes .button--sm: table/list row actions are the one deliberate
   exception to the 44px floor (issue #315). Without the :not(), this
   selector's specificity (0,1,1) outranks .button--sm's (0,1,0) and the
   36px row size silently never applies to submit buttons — which was half
   of them. Decision: Matt, 2026-07-20. */
button[type="submit"]:not(.button--sm) {
  min-height: 44px;
}

/* Breadcrumbs */
.breadcrumb {
  padding: 0 0 1.5rem;
  font-size: 0.85rem;
  color: var(--c-muted);
}

/* Task 3b (axe AA-contrast fixup, 2026-07-21): breadcrumb links used to
   tint with the page's --c-accent, which failed color-contrast (as low as
   2.31:1 on approval_queue) and link-in-text-block (no non-color distinction
   from the surrounding #666666 breadcrumb text) at every accent shade the
   link ever took on. Fixed shared-CSS-level rather than per page: one fixed
   link color (var(--c-link), #0066cc, 5.57:1 on white) plus a permanent
   underline — the underline alone satisfies link-in-text-block regardless
   of the (low, 1.03:1) contrast against the adjacent muted text. */
.breadcrumb a {
  color: var(--c-link);
  text-decoration: underline;
}

.breadcrumb .separator {
  margin: 0 0.4rem;
  color: var(--c-muted);
}

/* =========================
   Dashboard Role Indicator
   ========================= */

.dashboard-role-indicator {
  text-align: center;
  margin-bottom: 1.5rem;
  color: var(--c-muted);
  font-size: 0.95rem;
}

.dashboard-role-indicator p {
  margin: 0 0 0.25rem;
}

.role-badge {
  display: inline-block;
  padding: 0.2rem 0.75rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
  background: #e9ecef;
  color: #495057;
}

/* Landing Section Headers (Admin tile grouping) */
.landing-section-header {
    grid-column: 1 / -1;
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--c-muted);
    margin: 1rem 0 0.25rem;
    padding-bottom: 0.25rem;
    border-bottom: 1px solid var(--c-border);
}

.landing-section-header:first-of-type {
    margin-top: 0;
    border-top: none;
}

/* Recent submissions strip (Dashboard, warehouse roles) */
.recent-submissions {
    max-width: 900px;
    margin: 1.5rem auto 0;
}

.recent-submissions-summary {
    cursor: pointer;
    list-style: none;
    user-select: none;
}

.recent-submissions-summary::-webkit-details-marker {
    display: none;
}

/* Firefox et al. use ::marker, not the WebKit pseudo — suppress both so
   only the custom ::after caret renders. */
.recent-submissions-summary::marker {
    content: '';
}

.recent-submissions-summary::after {
    content: '\25B8';
    margin-left: 0.4rem;
    color: var(--c-muted);
    font-size: 0.7rem;
}

.recent-submissions details[open] .recent-submissions-summary::after,
details[open] > .recent-submissions-summary::after {
    content: '\25BE';
}

.recent-submissions-list {
    list-style: none;
    margin: 0.5rem 0 0;
    padding: 0;
    border: 1px solid var(--c-border);
    border-radius: var(--r-sm);
    background: var(--c-surface, #fff);
    overflow: hidden;
}

.recent-submissions-list li + li {
    border-top: 1px solid var(--c-border-soft, var(--c-border));
}

.recent-submissions-list a {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.6rem 0.85rem;
    text-decoration: none;
    color: inherit;
}

.recent-submissions-list a:hover {
    background: #f5f5f5;
}

.recent-batch-name {
    flex: 1;
    min-width: 0;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Contractor Stats Bar (Dashboard) removed 2026-07-21 (issue #315 PR 4,
   walk feedback rationale) — the markup that used .contractor-stats-bar and
   .stat-card is gone from dashboard.html. */

/* =========================
   Reduced-motion (WCAG 2.3.3 / respects OS preference)
   Users who ask for reduced motion get near-instant animations and
   transitions — including the continuous upload spinner and item slide-ins.
   ========================= */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Sortable table headers (issue #258): the real <button> inside th gives
   native keyboard activation while the th keeps its columnheader role
   (required for aria-sort). The button visually disappears into the th. */
th[data-sort] .sort-header-btn {
    all: unset;
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    cursor: pointer;
    font: inherit;
    color: inherit;
}

th[data-sort] .sort-header-btn:focus-visible {
    outline: 2px solid var(--c-primary, #AC3F07);
    outline-offset: 2px;
    border-radius: 2px;
}

/* ---------------------------------------------------------------------------
   Status badge (issue #264): single source of truth. Previously copy-pasted
   into 12 templates with 4 different sizes; add new statuses HERE only. */
.status-badge {
    display: inline-block;
    padding: 0.2rem 0.6rem;
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 600;
    white-space: nowrap;
}

.status-badge--pending,
.status-badge--pending_verification,
.status-badge--flagged,
.status-badge--rescheduled,
.status-badge--on_site { background-color: #fff3cd; color: #856404; }

.status-badge--approved,
.status-badge--in_warehouse,
.status-badge--delivered,
.status-badge--fulfilled,
.status-badge--paid,
.status-badge--active { background-color: #d4edda; color: #155724; }

.status-badge--rejected,
.status-badge--kickback,
.status-badge--missing_info,
.status-badge--cancelled,
.status-badge--inactive { background-color: #f8d7da; color: #721c24; }

/* Pre-approval check chip (spec 02 §4.15): a question for the reviewer, not a verdict. */
.status-badge--check { background-color: #e2e3f1; color: #3b3f7a; margin-left: 0.35rem; }
.review-finding { display: block; }
.review-finding--blocking { color: #721c24; font-weight: 600; }

.status-badge--requested,
.status-badge--sent { background-color: #cce5ff; color: #004085; }

.status-badge--confirmed { background-color: #b8daff; color: #004085; }

.status-badge--in_transit,
.status-badge--delivering,
.status-badge--fulfilling { background-color: #d1ecf1; color: #0c5460; }

.status-badge--partially_delivered { background-color: #bee5eb; color: #0c5460; }

.status-badge--generated { background-color: #e9ecef; color: #495057; }

/* PR 7 review finding 4: a voided invoice stays visible on the Unpaid
   tab (excluded only from its totals, apps/inventory/views.py::
   invoice_list) -- muted so it reads as "not a real unpaid receivable"
   at a glance, distinct from the neutral .status-badge--generated. */
.status-badge--voided { background-color: #e9ecef; color: #6c757d; }

/* Empty state (issue #264): canonical list/table empty placeholder. */
.empty-state {
    text-align: center;
    padding: 3rem 1rem;
    color: var(--c-muted);
}

/* Form focus + error affordances (issues #264/#259). */
.form-control:focus-visible {
    outline: 2px solid var(--c-primary, #AC3F07);
    outline-offset: 1px;
}

.form-control[aria-invalid="true"] {
    border-color: #c62828;
}

/* ==========================================================================
   Entrance pages — login, password reset, set-password, install guide
   (templates/accounts/_auth_page.html). Shared so allauth's pages and the
   login page look like one system. Login-only rules (SSO, passkey divider)
   stay in login.html.
   ========================================================================== */
.page--centered {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    padding: 1rem;
}

.login-container {
    width: 100%;
    max-width: 24rem;
}

.login-container--wide {
    max-width: 34rem;
}

.login-header {
    text-align: center;
    margin-bottom: 2rem;
}

.login-header .logo--large {
    max-width: 10rem;
    margin: 0 auto 1rem;
}

.login-header h1 {
    font-size: 1.5rem;
    font-weight: 600;
    color: var(--c-text);
    margin: 0;
}

.login-intro {
    margin: 0 0 1.25rem;
    line-height: 1.5;
    color: var(--c-text);
}

.login-form {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
}

.login-form .form-group {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.login-form label {
    font-size: 0.95rem;
    font-weight: 600;
}

.login-form .required {
    color: #dc3545;
}

.login-form input {
    width: 100%;
    padding: 0.75rem;
    font-size: 1rem;
    border: 1px solid var(--c-border);
    border-radius: var(--r-sm);
}

.login-form input:focus {
    outline: none;
    border-color: var(--c-accent);
    box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1);
}

.login-form .help-text {
    font-size: 0.85rem;
    color: var(--c-muted);
    line-height: 1.4;
}

.form-footer {
    text-align: center;
    margin-top: 0.5rem;
}

.link-secondary {
    color: var(--c-link);
    text-decoration: underline;
    font-size: 0.9rem;
}

.login-footer {
    margin-top: 2rem;
    text-align: center;
}

/* Scoped to the auth shell (_auth_page.html): these came from login.html's
   local <style> and must not restyle .text-muted / .errorlist app-wide. */
.login-container .text-muted {
    color: var(--c-muted);
    font-size: 0.85rem;
}

.login-container .errorlist {
    list-style: none;
    padding: 0;
    margin: 0.25rem 0 0;
    color: #dc3545;
    font-size: 0.85rem;
}

.form-errors {
    background-color: #f8d7da;
    border: 1px solid #f5c6cb;
    border-radius: var(--r-sm);
    padding: 0.75rem 1rem;
    margin-bottom: 1rem;
    color: #721c24;
}

.form-errors ul {
    list-style: none;
    padding: 0;
    margin: 0;
}

.form-group--checkbox label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-weight: 400;
    font-size: 0.9rem;
    cursor: pointer;
}

.form-group--checkbox input[type="checkbox"] {
    width: auto;
    padding: 0;
    margin: 0;
    border: none;
    box-shadow: none;
    appearance: checkbox;
}

/* Install guide tabs (templates/install/index.html) */
.install-tabs {
    display: flex;
    gap: 0.5rem;
    margin: 0 0 1.25rem;
    border-bottom: 1px solid var(--c-border-soft);
}

.install-tab {
    flex: 1;
    padding: 0.75rem 1rem;
    font-size: 1rem;
    font-weight: 600;
    background: none;
    border: none;
    border-bottom: 3px solid transparent;
    color: var(--c-muted);
    cursor: pointer;
    min-height: 44px;
}

.install-tab[aria-selected="true"] {
    color: var(--c-accent);
    border-bottom-color: var(--c-accent);
}

.install-tab:focus-visible {
    outline: 2px solid var(--c-accent);
    outline-offset: -2px;
}

.install-steps {
    margin: 0 0 1.5rem;
    padding-left: 1.5rem;
    line-height: 1.55;
}

.install-steps li {
    margin-bottom: 0.75rem;
}

.install-steps li strong {
    font-weight: 600;
}

.install-note {
    background: var(--c-surface-alt, #f4f4f2);
    border-radius: var(--r-md);
    padding: 0.75rem 1rem;
    font-size: 0.9rem;
    line-height: 1.5;
    margin: 0 0 1.25rem;
}

/* =========================================================================
   Help Center (spec 14) -- what every page carries: the "?" badge, the
   quick-help drawer and the question item the drawer and the index share.
   The pages themselves are styled in help.css.
   ========================================================================= */

:root {
  /* Shadows, codified: these three values already appear verbatim on the
     notification dropdown, the modal and the landing tiles. */
  --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.10);
  --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.15);
  --shadow-lg: 0 12px 40px rgba(0, 0, 0, 0.25);
  /* The Help tile is teal; the Help Center pages take the same accent. */
  --c-help: var(--subway-teal);
}

body#view-help { --c-accent: var(--c-help); }

/* Visually hidden, still read by assistive tech. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.header-actions__cluster {
  display: flex;
  align-items: center;
  gap: 0.25rem;
}

/* The badge: a 44px round target, like the user-menu toggle. */
.help-badge {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--icon-touch-min);
  height: var(--icon-touch-min);
  border: none;
  border-radius: 999px;
  background: none;
  color: var(--icon-color);
  cursor: pointer;
}
.help-badge:hover,
.help-badge[aria-expanded="true"] {
  color: var(--icon-hover);
  background: rgba(0, 0, 0, 0.05);
}
.help-badge:focus-visible {
  outline: 2px solid var(--c-accent);
  outline-offset: 2px;
}
.help-badge svg {
  width: var(--icon-size);
  height: var(--icon-size);
}
.help-badge__dot {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 999px;
  background: var(--badge-danger);
  box-shadow: 0 0 0 2px var(--c-bg);
}

/* The drawer: a bottom sheet on phones, a right-hand panel from 768px. */
.help-drawer-backdrop {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(0, 0, 0, 0.45);
}
.help-drawer {
  position: fixed;
  display: flex;
  flex-direction: column;
  background: var(--c-bg);
  box-shadow: var(--shadow-lg);
  inset: auto 0 0 0;
  max-height: 92dvh;
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  padding-bottom: env(safe-area-inset-bottom);
}
.help-drawer__head {
  position: sticky;
  top: 0;
  isolation: isolate;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.75rem 1rem 0.5rem 1.25rem;
  border-bottom: 1px solid var(--c-border-soft);
  background: var(--c-bg);
}
.help-drawer__head::before {
  /* the sheet's grab bar */
  content: "";
  position: absolute;
  top: 0.3rem;
  left: 50%;
  width: 2.5rem;
  height: 4px;
  margin-left: -1.25rem;
  border-radius: 999px;
  background: var(--c-border-soft);
}
.help-drawer__title {
  margin: 0.35rem 0 0;
  font-size: 1.05rem;
  color: var(--c-text);
}
.help-drawer__body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 1rem 1.25rem;
}
.help-drawer__error {
  margin: 0 1.25rem;
}
.help-drawer__foot {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 0.75rem 1.25rem 1rem;
  border-top: 1px solid var(--c-border-soft);
}
.help-drawer__foot .button {
  flex: 1 1 10rem;
}
@media (min-width: 768px) {
  .help-drawer {
    inset: 0 0 0 auto;
    width: min(26rem, 100%);
    max-height: none;
    height: 100dvh;
    border-radius: 0;
    border-left: 1px solid var(--c-border-soft);
    padding-bottom: 0;
  }
  .help-drawer__head::before {
    display: none;
  }
  .help-drawer__title {
    margin-top: 0;
  }
}

/* One question, as a native <details>. Shared by the drawer and the index. */
.help-faq {
  border-top: 1px solid var(--c-border-soft);
}
.help-faq:last-of-type {
  border-bottom: 1px solid var(--c-border-soft);
}
.help-faq__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  min-height: 44px;
  padding: 0.6rem 0;
  list-style: none;
  cursor: pointer;
  font-weight: 600;
  line-height: 1.35;
}
.help-faq__q::-webkit-details-marker {
  display: none;
}
.help-faq__q::marker {
  content: "";
}
.help-faq__q:focus-visible {
  outline: 2px solid var(--c-accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}
.help-faq__icon {
  flex: 0 0 auto;
  width: 1.5rem;
  text-align: center;
  color: var(--c-muted);
  font-weight: 800;
  transition: transform 0.15s ease;
}
.help-faq__icon::before {
  content: "+";
}
.help-faq[open] > .help-faq__q .help-faq__icon {
  transform: rotate(45deg);
}
.help-faq__a {
  padding: 0 0 1rem;
  font-size: 0.95rem;
  line-height: 1.55;
}
.help-faq__a p {
  margin: 0 0 0.6rem;
}
.help-faq__a ol,
.help-faq__a ul {
  margin: 0.3rem 0 0.6rem 1.2rem;
}
.help-faq__a table {
  border-collapse: collapse;
  margin: 0.5rem 0 0.75rem;
  font-size: 0.9rem;
  display: block;
  overflow-x: auto;
}
.help-faq__a th,
.help-faq__a td {
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--c-border-soft);
  text-align: left;
}
.help-faq__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
  margin: 0.25rem 0 0;
  font-size: 0.85rem;
}
.help-faq__meta .chip {
  font-size: 0.75rem;
  padding: 0.15rem 0.5rem;
}
.help-faq__permalink {
  margin-left: auto;
  color: var(--c-link);
}
.help-faq--compact .help-faq__q {
  font-size: 0.95rem;
}
.help-panel__guide {
  margin-top: 1rem;
}
