/* ============================================================
   WOHub Components — Re-imagined migration foundation
   Ported verbatim from prototype WOHub.html lines 73–365.
   Depends on design-tokens.css for CSS variables.
   ============================================================ */

.mono { font-family: 'JetBrains Mono', ui-monospace, monospace; font-feature-settings: 'tnum'; }

/* === Buttons === */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 7px 14px; border-radius: 8px; font-size: 13px; font-weight: 500;
  background: var(--bg-3); border: 1px solid var(--border-2); color: var(--text);
  cursor: pointer; font-family: inherit; line-height: 1.2;
  text-decoration: none; box-sizing: border-box;
  /* Explicit properties, not `all` — `all` animates the focus-visible
     box-shadow, which makes the keyboard ring fade in late. */
  transition: background var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease),
              color var(--dur-1) var(--ease), transform var(--dur-1) var(--ease);
}
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn:hover { background: var(--bg-4); border-color: var(--border-3); color: var(--text); }
.btn-primary { background: var(--brand); color: var(--text-on-brand); border-color: var(--brand); font-weight: 600; }
.btn-primary:hover { background: var(--brand-hover); border-color: var(--brand-hover); color: var(--text-on-brand); }
.btn-view { background: var(--view-accent-bg); color: var(--view-accent-text); border-color: var(--view-accent-border); font-weight: 600; }
.btn-view:hover { background: color-mix(in srgb, var(--view-accent) 24%, transparent); border-color: var(--view-accent); color: var(--view-accent-text); }
.btn-secondary { background: var(--bg-2); color: var(--text); border-color: var(--border-2); }
.btn-secondary:hover { background: var(--bg-3); border-color: var(--border-3); }
.btn-outline { background: transparent; border-color: var(--border-2); color: var(--text); }
.btn-outline:hover { background: var(--bg-3); border-color: var(--border-3); }
.btn-blue { background: var(--blue); color: var(--text-inverted); border-color: var(--blue); }
.btn-blue:hover { background: var(--blue-hover); border-color: var(--blue-hover); color: var(--text-inverted); }
.btn-green { background: var(--green); color: var(--text-inverted); border-color: var(--green); }
.btn-green:hover { background: var(--green-hover); border-color: var(--green-hover); color: var(--text-inverted); }
.btn-red { background: var(--red); color: var(--text-inverted); border-color: var(--red); }
.btn-red:hover { background: var(--red-hover); border-color: var(--red-hover); color: var(--text-inverted); }
.btn-ghost { background: transparent; border-color: transparent; color: var(--text-2); }
.btn-ghost:hover { background: var(--bg-3); color: var(--text); }
.btn-lg { padding: 10px 18px; font-size: 15px; border-radius: 10px; }
.btn-sm { padding: 4px 10px; font-size: 12px; border-radius: 6px; }
.btn-xs { padding: 3px 8px; font-size: 11px; border-radius: 5px; }
.btn:disabled { opacity: .4; cursor: not-allowed; }

/* === Cards === */
.card {
  background: var(--eb-panel, var(--bg-1)); border: 1px solid var(--border); border-radius: var(--radius-md, 12px);
  background-image: radial-gradient(circle, rgba(255,255,255,var(--glass-noise)) 0 1px, transparent 1px);
  background-size: 5px 5px;
  box-shadow: var(--shadow);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
}
.card-pad { padding: 20px; }

/* === Inputs === */
/* `.ds-input` is an alias, not a second implementation. It was an opt-in class
   in base.html's inline copy; 142 call sites still use it, so it rides the
   canonical rule until they are renamed. Same for .ds-empty / .ds-modal-* /
   .ds-pulse below. */
input[type="text"], input[type="email"], input[type="password"], input[type="number"],
input[type="date"], input[type="search"], textarea, select, .ds-input {
  background: var(--bg-2); border: 1px solid var(--border-2); color: var(--text);
  padding: 7px 12px; border-radius: 7px; font-size: 13px; font-family: inherit;
  outline: none; transition: border-color var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
  width: 100%;
}
input:focus, textarea:focus, select:focus, .ds-input:focus { border-color: var(--text-3); }
input::placeholder, textarea::placeholder, .ds-input::placeholder { color: var(--text-4); }
/* Pin <select> option popups to the theme surface so the native dropdown stays
   readable in both themes (the option text would otherwise inherit the select's
   light --text onto a UA-painted popup). color-scheme (design-tokens.css) handles
   the popup chrome + selection highlight; this guarantees the exact surface color. */
select option { background-color: var(--bg-2); color: var(--text); }
label { font-size: 12px; color: var(--text-2); font-weight: 500; }

/* === Pills / Badges === */
.pill {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 8px; border-radius: 999px; font-size: 11px; font-weight: 500;
  border: 1px solid var(--border-2); background: var(--bg-3); color: var(--text-2);
  line-height: 1.5; white-space: nowrap;
}
/* The --*-text tokens already carry the light-mode darkening, so these need no
   html[data-theme="light"] block — six override rules were deleted here. */
.pill-blue   { background: var(--view-accent-bg); color: var(--view-accent-text); border-color: var(--view-accent-border); }
.pill-green  { background: var(--green-bg);  color: var(--green-text);  border-color: var(--green-border); }
.pill-amber  { background: var(--amber-bg);  color: var(--amber-text);  border-color: var(--amber-border); }
.pill-red    { background: var(--red-bg);    color: var(--red-text);    border-color: var(--red-border); }
.pill-orange { background: var(--orange-bg); color: var(--orange-text); border-color: var(--orange-border); }
.pill-purple { background: var(--purple-bg); color: var(--purple-text); border-color: var(--purple-border); }

/* === eb-badge — hue-coded status/priority/dept/tier badge ===
   RESTORED 2026-07-27. These rules were dropped by 67b6fe9's wholesale revert
   of this file and never came back, so for four days every status_badge(),
   priority_badge(), department_badge() and tier_badge() in the app — plus the
   shop floor's pill(), lot_badge() and routing_strip() — rendered as bare
   unstyled inline text. 50 call sites, zero definitions.

   Restored onto the --chip-* palette rather than the original's raw rgba/hex.
   The original hand-rolled nine html[data-theme="dark"] foreground overrides
   because its light values were baked in; --chip-* already carries an
   AA-verified foreground for each hue in BOTH themes, so the entire theme fork
   is deleted rather than maintained.

   Variants set --badge-fg/--badge-bg only — same indirection as .dept-chip
   below — so the base rule owns all the geometry and a variant is one line. */
.eb-badge {
  --badge-fg: var(--text-2);
  --badge-bg: var(--bg-3);
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 10px; border-radius: 999px; font-size: 11px; font-weight: 700;
  line-height: 1.5; white-space: nowrap;
  background: var(--badge-bg); color: var(--badge-fg);
  /* Two border-color declarations on purpose. The first is the tint itself and
     is always valid; the second upgrades the edge to the badge hue at 32% —
     the alpha the original used — and is simply dropped at parse time on an
     engine without color-mix(), leaving the tint. Neither uses !important, so
     department_badge()'s inline `border-color: {{ color }}55` still wins. */
  border: 1px solid var(--badge-bg);
  border-color: color-mix(in srgb, var(--badge-fg) 32%, transparent);
}
/* The status dot. currentColor, so a variant never has to name it twice. */
.eb-badge::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; opacity: 0.9;
}
/* Tier is a different KIND of thing from a status — a fixed product class, not
   a state that moves — so it reads as a squarer chip with a solid left rule and
   no status dot. The left rule takes the foreground rather than the 32% mix:
   at 3px a translucent edge just looks like a rendering artifact. */
.eb-badge-tier {
  border-radius: 6px; border-left-width: 3px; padding: 2px 8px;
  border-left-color: var(--badge-fg);
}
.eb-badge-tier::before { display: none; }

.eb-badge-emerald { --badge-fg: var(--chip-emerald); --badge-bg: var(--chip-emerald-bg); }
.eb-badge-amber   { --badge-fg: var(--chip-amber);   --badge-bg: var(--chip-amber-bg); }
.eb-badge-cyan    { --badge-fg: var(--chip-cyan);    --badge-bg: var(--chip-cyan-bg); }
.eb-badge-rose    { --badge-fg: var(--chip-rose);    --badge-bg: var(--chip-rose-bg); }
.eb-badge-orange  { --badge-fg: var(--chip-orange);  --badge-bg: var(--chip-orange-bg); }
.eb-badge-slate   { --badge-fg: var(--chip-slate);   --badge-bg: var(--chip-slate-bg); }
.eb-badge-teal    { --badge-fg: var(--chip-teal);    --badge-bg: var(--chip-teal-bg); }
.eb-badge-violet  { --badge-fg: var(--chip-violet);  --badge-bg: var(--chip-violet-bg); }
.eb-badge-indigo  { --badge-fg: var(--chip-indigo);  --badge-bg: var(--chip-indigo-bg); }

/* === Layout === */
.shell { display: flex; min-height: 100vh; }
.sidebar {
  width: 232px; background: var(--sidebar-bg); border-right: 1px solid var(--sidebar-border);
  padding: 16px 0; display: flex; flex-direction: column;
  position: fixed; top: 0; left: 0; bottom: 0; z-index: 30;
  box-shadow: 1px 0 0 var(--sidebar-edge);
}
.main {
  flex: 1; min-width: 0; margin-left: 232px; display: flex; flex-direction: column;
}
.content { min-width: 0; }
/* Cards that contain wide tables get horizontal scroll — overrides inline overflow:hidden. */
.card:has(table.tbl) { overflow-x: auto !important; overflow-y: visible !important; }
.modal, .modal-body { overflow-x: hidden; }
.topbar {
  height: 56px; background: var(--bg); border-bottom: 1px solid var(--border);
  display: flex; align-items: center; padding: 0 24px; gap: 16px;
  position: sticky; top: 0; z-index: 20;
}
.content { padding: 24px; flex: 1; }

/* === Sidebar nav === */
.sidebar-brand {
  display: flex; align-items: center; gap: 10px; padding: 4px 20px 20px;
  text-decoration: none;
  background: none; border: none; width: 100%; text-align: left;
  cursor: pointer; font-family: inherit;
}
/* The mark is a SIZED CONTAINER, nothing more. It used to paint an
   indigo→violet gradient with a matching coloured drop shadow — that is the
   retired WOHub identity, not this app's, and it was the only place in the
   shared layer asserting a brand colour that appears nowhere in the palette.
   The identity now lives in the inline <svg> the container holds, so the
   container carries no colour of its own and both theme forks are gone.
   `overflow: hidden` lets the svg fill the rounded corner edge-to-edge. */
.brand-mark {
  width: 28px; height: 28px; border-radius: 7px;
  display: grid; place-items: center;
  flex-shrink: 0; overflow: hidden;
}
/* The 60% inset is for a glyph-only svg that needs breathing room inside a
   tinted plate. A full-bleed mark opts out with .brand-mark--bleed. */
.brand-mark svg { width: 60%; height: 60%; }
.brand-mark--bleed svg { width: 100%; height: 100%; }
.brand-mark--bleed img { display: block; width: 100%; height: 100%; }
.brand-text { font-size: 15px; font-weight: 700; color: var(--sidebar-text-active); letter-spacing: -0.3px; }

.nav-section { padding: 14px 12px 6px; font-size: 10px; font-weight: 700; color: var(--sidebar-text-dim);
  text-transform: uppercase; letter-spacing: 0.09em; }
.nav { flex: 1; overflow-y: auto; padding: 4px 8px; }
.nav-item {
  display: flex; align-items: center; gap: 10px; padding: 8px 12px;
  border-radius: 8px; color: var(--sidebar-text); text-decoration: none; font-size: 13px;
  font-weight: 500; cursor: pointer; margin-bottom: 2px;
  transition: background var(--dur-2) var(--ease), color var(--dur-2) var(--ease),
              border-color var(--dur-2) var(--ease);
  border: 1px solid transparent; background: none;
  width: 100%; text-align: left; font-family: inherit; position: relative;
}
.nav-item:hover { background: var(--sidebar-item-hover); color: var(--sidebar-text-active); }
.nav-item.active {
  background: var(--sidebar-item-active);
  color: var(--sidebar-text-active);
  font-weight: 600;
  border-color: var(--sidebar-item-active-border);
}
.nav-item svg { width: 17px; height: 17px; flex-shrink: 0; opacity: 0.7;
  transition: opacity var(--dur-2) var(--ease), color var(--dur-2) var(--ease); }
.nav-item:hover svg { opacity: 0.95; }
.nav-item.active svg { opacity: 1; color: var(--sidebar-icon-active); }
.nav-item .nav-badge { margin-left: auto; font-size: 10px; padding: 1px 6px; border-radius: 999px;
  background: var(--sidebar-item-hover); color: var(--sidebar-text); font-weight: 600; }
.nav-item.active .nav-badge { background: var(--sidebar-item-active-border); color: var(--sidebar-text-active); }
.nav-sub { padding-left: 20px; margin-left: 12px; border-left: 1px solid var(--sidebar-border); margin-top: 2px; margin-bottom: 4px; }
.nav-sub .nav-item { padding: 6px 12px; font-size: 12px; color: var(--sidebar-text); }
.nav-sub-label { font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.09em; color: var(--sidebar-text-dim); padding: 6px 12px 2px; }
.nav-item-pair { margin: 6px 0; }
.nav-item-pair .nav-item { margin-bottom: 1px; }

/* Group header whose label navigates (a link) while a trailing chevron toggles
   the sub-items — used by the Jobs group. */
.nav-group-hdr { display: flex; align-items: center; }
.nav-group-hdr .nav-item { flex: 1 1 auto; }
.nav-group-toggle { flex: 0 0 auto; width: 34px; align-self: stretch;
  display: flex; align-items: center; justify-content: center; border: none;
  background: none; color: var(--text-3); cursor: pointer; border-radius: 7px;
  opacity: 0.55; transition: background var(--dur-1) var(--ease), opacity var(--dur-1) var(--ease),
              color var(--dur-1) var(--ease); }
.nav-group-toggle:hover { background: var(--bg-3); color: var(--text); opacity: 1; }
#sidebar.collapsed .nav-group-toggle { display: none; }

/* === Topbar === */
.topbar-title { font-size: 14px; font-weight: 600; color: var(--text); }
.topbar-sub { font-size: 12px; color: var(--text-3); }
.topbar-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.icon-btn {
  width: 32px; height: 32px; border-radius: 7px; background: var(--bg-1);
  border: 1px solid var(--border); color: var(--text-2); cursor: pointer;
  display: grid; place-items: center; position: relative;
  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease),
              border-color var(--dur-1) var(--ease);
}
.icon-btn:hover { background: var(--bg-3); color: var(--text); }
.icon-btn .icon-dot { position: absolute; top: 6px; right: 6px; width: 6px; height: 6px;
  border-radius: 50%; background: var(--red); }
.role-pill {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--bg-1); border: 1px solid var(--border);
  border-radius: 7px; padding: 5px 10px 5px 8px; font-size: 12px; color: var(--text-2);
  cursor: pointer; font-family: inherit;
}
.role-pill:hover { background: var(--bg-3); color: var(--text); }
.role-pill .role-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--green); }
.role-switcher { position: relative; }
.role-switcher-trigger { border: 1px solid var(--border); }
.role-switcher-trigger[aria-expanded="true"] { border-color: var(--view-accent-border); background: var(--view-accent-bg); color: var(--view-accent-text); }
.role-switcher-chevron { transition: transform var(--dur-1) var(--ease); }
.role-switcher-trigger[aria-expanded="true"] .role-switcher-chevron { transform: rotate(180deg); }
.role-switcher-menu,
.role-switcher-submenu {
  display: none; position: absolute; z-index: 220; min-width: 190px; padding: 6px;
  border: 1px solid var(--border-2); border-radius: 10px; background: var(--bg-1);
  box-shadow: 0 16px 42px rgba(0,0,0,.34);
}
.role-switcher-menu { top: calc(100% + 7px); right: 0; }
.role-switcher.is-open .role-switcher-menu { display: block; }
.role-switcher-menu form,.role-switcher-submenu form { margin: 0; }
.role-switcher-title { margin: 4px 7px 6px; color: var(--text-4); font-size: 8px; font-weight: 750; letter-spacing: .08em; text-transform: uppercase; }
.role-switcher-option {
  display: flex; width: 100%; min-height: 31px; align-items: center; justify-content: space-between; gap: 12px;
  padding: 0 8px; border: 0; border-radius: 7px; background: transparent; color: var(--text-2);
  cursor: pointer; font: 600 11px/1 var(--font-sans); text-align: left;
}
.role-switcher-option:hover,.role-switcher-option:focus-visible { outline: none; background: var(--bg-3); color: var(--text); }
.role-switcher-option.is-active { background: var(--view-accent-bg); color: var(--view-accent-text); }
.role-switcher-option small { color: inherit; font-size: 8px; font-weight: 750; opacity: .78; }
.role-switcher-dept { position: relative; margin-top: 3px; padding-top: 3px; border-top: 1px solid var(--border); }
.role-switcher-dept-trigger svg { flex: 0 0 auto; }
.role-switcher-submenu { top: -7px; right: calc(100% + 7px); }
.role-switcher-dept:hover .role-switcher-submenu,
.role-switcher-dept:focus-within .role-switcher-submenu,
.role-switcher-dept.is-open .role-switcher-submenu { display: block; }
@media (max-width: 639.98px) {
  .role-switcher-menu { right: -38px; }
  .role-switcher-submenu { right: 0; top: calc(100% + 4px); }
}

/* === Tables === */
table.tbl { width: 100%; border-collapse: collapse; }
table.tbl th, table.tbl td { padding: 10px 14px; text-align: left; }
table.tbl th { font-size: 10px; font-weight: 600; color: var(--text-3);
  text-transform: uppercase; letter-spacing: 0.06em;
  border-bottom: 1px solid var(--border); background: var(--bg);
  position: sticky; top: 0;
}
table.tbl td { border-bottom: 1px solid var(--border); font-size: 13px; vertical-align: middle; }
table.tbl tr:last-child td { border-bottom: none; }
table.tbl tr.row-click { cursor: pointer; }
table.tbl tr.row-click:hover td { background: var(--bg-3); }

/* === Tabs === */
.tabs { display: flex; gap: 0; border-bottom: 1px solid var(--border); margin-bottom: 16px; }
.tab {
  padding: 9px 16px; font-size: 13px; font-weight: 500;
  background: none; border: none; cursor: pointer; color: var(--text-3);
  border-bottom: 2px solid transparent; font-family: inherit;
}
.tab:hover { color: var(--text); }
.tab.active { color: var(--view-accent-text); border-bottom-color: var(--view-accent); }
.tab-count {
  display: inline-block; margin-left: 6px; padding: 0 6px; min-width: 18px;
  background: var(--bg-3); border-radius: 999px; font-size: 10px; line-height: 16px;
  font-weight: 600; text-align: center; color: var(--text-2);
}
.tab.active .tab-count { background: var(--view-accent); color: var(--text-on-brand); }

/* === Settings information architecture === */
.settings-context-nav {
  min-width: 0; min-height: 48px; padding: 7px 24px; display: flex;
  align-items: center; gap: 10px; border-bottom: 1px solid var(--border);
  background: var(--bg-1); overflow-x: auto; scrollbar-width: thin;
}
.settings-context-nav a { text-decoration: none; white-space: nowrap; }
.settings-context-home {
  display: inline-flex; align-items: center; gap: 7px; min-height: 34px;
  padding: 0 10px; border-radius: 7px; color: var(--text-2); font-size: 12px;
  font-weight: 600;
}
.settings-context-home:hover, .settings-context-home.active {
  background: var(--blue-bg); color: var(--view-accent-text);
}
.settings-context-divider { width: 1px; height: 22px; background: var(--border); flex: 0 0 auto; }
.settings-context-section {
  color: var(--text); font-size: 11px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; white-space: nowrap;
}
.settings-context-links { display: flex; align-items: center; gap: 4px; }
.settings-context-links a {
  min-height: 34px; padding: 0 10px; display: inline-flex; align-items: center;
  border-radius: 7px; color: var(--text-3); font-size: 12px; font-weight: 500;
}
.settings-context-links a:hover { background: var(--bg-3); color: var(--text); }
.settings-context-links a.active {
  background: var(--view-accent-bg); color: var(--view-accent-text);
  box-shadow: inset 0 0 0 1px var(--view-accent-border);
}

.settings-home { width: min(1120px, 100%); margin: 0 auto; }
.settings-home-hero {
  display: flex; justify-content: space-between; align-items: end; gap: 28px;
  margin-bottom: 30px; padding: 8px 0 24px; border-bottom: 1px solid var(--border);
}
.settings-home-eyebrow {
  margin: 0 0 5px; color: var(--view-accent-text); font-size: 10px;
  font-weight: 800; letter-spacing: .11em; text-transform: uppercase;
}
.settings-home-hero h1 { margin: 0; color: var(--text); font-size: 30px; letter-spacing: -.04em; }
.settings-home-hero p:not(.settings-home-eyebrow) {
  margin: 7px 0 0; max-width: 600px; color: var(--text-2); font-size: 14px;
}
.settings-search {
  width: min(360px, 100%); min-height: 44px; display: flex; align-items: center;
  gap: 9px; padding: 0 11px; border: 1px solid var(--border-2); border-radius: 10px;
  background: var(--bg-2); color: var(--text-3);
}
.settings-search:focus-within { border-color: var(--view-accent); box-shadow: var(--focus-ring); }
.settings-search input {
  min-width: 0; flex: 1; border: 0; outline: 0; background: transparent;
  color: var(--text); font: inherit; font-size: 13px;
}
.settings-search kbd {
  padding: 2px 6px; border: 1px solid var(--border); border-radius: 5px;
  background: var(--bg-3); color: var(--text-3); font-size: 10px;
}
.settings-sections { display: grid; gap: 30px; }
.settings-section-heading { margin-bottom: 12px; }
.settings-section-heading h2 { margin: 0; color: var(--text); font-size: 16px; }
.settings-section-heading p { margin: 4px 0 0; color: var(--text-3); font-size: 12px; }
.settings-card-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.settings-destination {
  min-width: 0; min-height: 92px; display: grid;
  grid-template-columns: 42px minmax(0, 1fr) 18px; align-items: center; gap: 12px;
  padding: 15px; border: 1px solid var(--border); border-radius: 12px;
  background: var(--bg-1); color: inherit; text-decoration: none;
  transition: border-color var(--dur-1) var(--ease), background var(--dur-1) var(--ease), transform var(--dur-1) var(--ease);
}
.settings-destination:hover {
  border-color: var(--view-accent-border); background: var(--bg-2); transform: translateY(-1px);
}
.settings-destination-icon {
  width: 42px; height: 42px; display: grid; place-items: center; border-radius: 10px;
  background: var(--view-accent-bg); color: var(--view-accent-text); font-size: 20px;
}
.settings-destination-copy { min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.settings-destination-title { color: var(--text); font-size: 13px; font-weight: 700; }
.settings-destination-description { color: var(--text-3); font-size: 12px; line-height: 1.45; }
.settings-destination-arrow { color: var(--text-4); transition: transform var(--dur-1) var(--ease); }
.settings-destination:hover .settings-destination-arrow { color: var(--view-accent-text); transform: translateX(2px); }
.settings-advanced {
  margin-left: 6px; padding: 2px 6px; border: 1px solid var(--amber-border);
  border-radius: 999px; background: var(--amber-bg); color: var(--amber);
  font-size: 9px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
}
.settings-no-results {
  min-height: 240px; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 7px; color: var(--text-3); text-align: center;
}
.settings-no-results i { font-size: 26px; }
.settings-no-results strong { color: var(--text); font-size: 14px; }
.settings-no-results span { font-size: 12px; }
.settings-no-results[hidden], .settings-section[hidden], .settings-destination[hidden] { display: none; }

@media (max-width: 760px) {
  .settings-context-nav { padding-inline: 14px; }
  .settings-home-hero { align-items: stretch; flex-direction: column; gap: 18px; }
  .settings-search { width: 100%; }
  .settings-card-grid { grid-template-columns: 1fr; }
}

@media (max-width: 480px) {
  .settings-context-section, .settings-context-divider { display: none; }
  .settings-destination { grid-template-columns: 38px minmax(0, 1fr) 16px; padding: 13px; }
  .settings-destination-icon { width: 38px; height: 38px; }
}

/* Phone ergonomics: compact desktop controls retain their density, while
   touch layouts get reliable targets without changing any control behavior. */
@media (max-width: 639.98px) {
  .content .btn,
  .content .icon-btn,
  .content .tab,
  .content .segmented > a,
  .content .segmented > button {
    min-height: 44px;
  }
  .content input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]),
  .content select,
  .content textarea {
    min-height: 44px;
    box-sizing: border-box;
  }
  .content .tabs {
    overflow-x: auto;
    overscroll-behavior-inline: contain;
    scrollbar-width: thin;
  }
  .content .tab { flex: 0 0 auto; }
  .card:has(table.tbl),
  [style*="overflow-x: auto"]:has(> table.tbl) {
    overscroll-behavior-inline: contain;
    scrollbar-width: thin;
  }

  /* Management lists become readable records on phones. Templates opt in and
     provide data-label values, preserving the comparative table on larger
     screens while keeping identity and actions together on narrow screens. */
  .admin-responsive-table,
  .admin-responsive-table tbody,
  .admin-responsive-table tr,
  .admin-responsive-table td {
    display: block;
    width: 100%;
    box-sizing: border-box;
  }
  .admin-responsive-table thead { display: none; }
  .admin-responsive-table tbody { padding: 10px; }
  .admin-responsive-table tr {
    margin-bottom: 10px;
    padding: 12px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--bg-1);
  }
  .admin-responsive-table tr:last-child { margin-bottom: 0; }
  .admin-responsive-table td {
    display: grid;
    grid-template-columns: minmax(82px, 0.38fr) minmax(0, 1fr);
    gap: 10px;
    align-items: start;
    padding: 7px 0;
    border-bottom: 1px solid var(--border);
    text-align: left !important;
    max-width: none !important;
    white-space: normal !important;
    overflow: visible !important;
  }
  .admin-responsive-table td::before {
    content: attr(data-label);
    color: var(--text-3);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.06em;
    line-height: 1.5;
    text-transform: uppercase;
  }
  .admin-responsive-table td:first-child { padding-top: 0; }
  .admin-responsive-table td:last-child {
    display: block;
    padding: 12px 0 0;
    border-bottom: 0;
  }
  .admin-responsive-table td:last-child::before { display: none; }
  .admin-responsive-table td:last-child.admin-mobile-actions {
    display: flex;
  }
  .admin-responsive-table .admin-mobile-primary {
    display: block;
    padding-bottom: 10px;
  }
  .admin-responsive-table .admin-mobile-primary::before { display: none; }
  .admin-responsive-table .admin-mobile-actions > *,
  .admin-responsive-table .admin-mobile-actions form,
  .admin-responsive-table .admin-mobile-actions .btn {
    min-height: 44px;
  }
  .admin-responsive-table .admin-mobile-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
  }
  .admin-responsive-table .admin-mobile-empty {
    display: block;
    padding: 0;
    border: 0;
  }
  .admin-responsive-table .admin-mobile-empty::before { display: none; }
}

/* === Segmented toggle — supports both <button> and <a> children === */
/* gap/padding are 1px/2px, not 3px/3px: the inline copy in base.html had been
   tightened and — being later in the cascade — was what actually shipped. Kept
   the shipping values when that copy was deleted. */
.segmented {
  display: inline-flex; background: var(--bg-3); border: 1px solid var(--border);
  border-radius: 8px; padding: 2px; gap: 1px;
}
.segmented button,
.segmented a {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  background: none; border: none; padding: 6px 12px; font-size: 12px; line-height: 1;
  border-radius: 6px; color: var(--text-3); cursor: pointer; font-family: inherit; font-weight: 500;
  text-decoration: none; transition: color var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
  white-space: nowrap;
}
.segmented button.active,
.segmented a.active { background: var(--view-accent-bg); color: var(--view-accent-text); box-shadow: 0 0 0 1px var(--view-accent-border); }
.segmented button:hover:not(.active),
.segmented a:hover:not(.active) { color: var(--text); background: var(--bg-2); }

/* === Skeleton === */
.skeleton { background: linear-gradient(90deg, var(--bg-2) 0%, var(--bg-3) 50%, var(--bg-2) 100%);
  background-size: 200% 100%; animation: skel 1.4s infinite; border-radius: 6px; }
@keyframes skel { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }

/* === KPI === */
.kpi {
  background: var(--bg-1); border: 1px solid var(--border); border-radius: 12px;
  padding: 18px 20px;
}
.kpi-label { font-size: 10px; font-weight: 600; color: var(--text-3);
  text-transform: uppercase; letter-spacing: 0.06em; }
.kpi-value { font-size: 28px; font-weight: 700; color: var(--text); margin-top: 6px;
  font-family: 'JetBrains Mono', monospace; letter-spacing: -0.02em; }
.kpi-spark { height: 28px; margin-top: 8px; }

/* Stat-card value, for macros/ui.html's stat_card(). The value brightens to the
   brand colour when its card is hovered, which reads as "this tile is
   interactive" on the tiles that carry an href.

   This rule exists because the macro used `group-hover:text-emerald-500`, a
   Tailwind colour utility, so the hover colour bypassed the light-theme
   override. Dropping the utility removed the affordance entirely; this restores
   it through --brand-text, which is the token that flips per theme. Scoped to
   `.card:hover` rather than a `.group` utility so it needs no new hover
   infrastructure. */
.stat-value {
  color: var(--text);
  transition: color var(--dur-2) var(--ease);
}
.card:hover .stat-value { color: var(--brand-text); }

/* === Modal === */
.modal-backdrop, .ds-modal-backdrop {
  position: fixed; inset: 0; background: var(--scrim); backdrop-filter: blur(6px);
  z-index: 100; display: flex; align-items: center; justify-content: center; padding: 24px;
  animation: fadeIn var(--dur-2) var(--ease);
}
.modal, .ds-modal {
  background: var(--bg-1); border: 1px solid var(--border-2); border-radius: 14px;
  max-width: 540px; width: 100%; box-shadow: var(--shadow-lg);
  max-height: 90vh; overflow: hidden; display: flex; flex-direction: column;
}
.modal-lg, .ds-modal-lg { max-width: 720px; }
.modal-xl, .ds-modal-xl { max-width: 960px; }
.modal-hdr, .ds-modal-hdr { padding: 18px 22px 14px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }
.modal-title, .ds-modal-title { font-size: 15px; font-weight: 600; color: var(--text); margin: 0; }
.modal-body, .ds-modal-body { padding: 20px 22px; overflow-y: auto; color: var(--text); }
.modal-foot, .ds-modal-foot { padding: 14px 22px; border-top: 1px solid var(--border); display: flex; justify-content: flex-end; gap: 8px; background: var(--bg); }

/* === Context menu === */
.ds-context-menu {
  position: fixed; z-index: 110; min-width: 160px;
  background: var(--bg-1); border: 1px solid var(--border-2); border-radius: 10px;
  box-shadow: var(--shadow-lg); padding: 4px; display: flex; flex-direction: column;
}
.ds-context-menu-item {
  display: block; width: 100%; text-align: left; padding: 8px 10px;
  font-size: 13px; color: var(--text); background: none; border: none;
  border-radius: 6px; cursor: pointer; font-family: inherit;
}
.ds-context-menu-item:hover { background: var(--bg-3); }

/* === Drawer === */
.drawer-backdrop { position: fixed; inset: 0; background: var(--scrim); z-index: 90;
  animation: fadeIn var(--dur-2) var(--ease); }
.drawer { position: fixed; top: 0; right: 0; bottom: 0; width: 440px; max-width: 95vw;
  background: var(--bg-1); border-left: 1px solid var(--border-2); z-index: 91;
  display: flex; flex-direction: column; box-shadow: var(--shadow-lg);
  animation: slideIn var(--dur-3) var(--ease-out);
}
@keyframes slideIn { from { transform: translateX(100%); } to { transform: translateX(0); } }

/* === Pagination ===
   Reusable pagination bar. Pairs with templates/macros/pagination.html.
   The wrapping <div id="<screen>-pagebox"> sits OUTSIDE this .pagination
   element so HTMX can swap a region that includes table + pagination. */
.pagination {
    display: flex; align-items: center; justify-content: center;
    gap: 12px; padding: 14px; border-top: 1px solid var(--border);
}
.pagination .page-info { font-size: 13px; color: var(--text-3); }
.pagination .disabled { opacity: 0.4; cursor: not-allowed; }
.pagination .btn-sm { font-family: inherit; }

/* Windowed page-number links — rendered only when the caller passes
   numbered=true to pagination_bar(). */
.pagination .page-nums { display: flex; align-items: center; gap: 4px; }
.pagination .page-nums .btn-sm {
    min-width: 30px; justify-content: center;
    font-family: 'JetBrains Mono', monospace; font-size: 12px;
}
.pagination .page-nums .btn-sm.active {
    background: var(--view-accent); color: var(--text-on-brand);
    border-color: var(--view-accent); font-weight: 700;
}
.pagination .page-gap { color: var(--text-4); font-size: 12px; padding: 0 2px; }

/* === Misc === */
.empty, .ds-empty {
  text-align: center; padding: 48px 20px; color: var(--text-3); font-size: 13px;
}
.empty-icon, .ds-empty-icon { font-size: 32px; margin-bottom: 8px; opacity: 0.5; }
.divider { height: 1px; background: var(--border); margin: 12px 0; }

.text-2 { color: var(--text-2); }
.text-3 { color: var(--text-3); }
.text-4 { color: var(--text-4); }

/* ============================================================
   PAGE HEADER
   The first thing on almost every screen, and it had been written twelve
   times under twelve names — .ar-page-hdr, .ai-head, .apw-head, .at-header,
   .awc-head, .au-page-hdr, .db-page-hdr, .it-page-hdr, .qadd-hero,
   .pocd-page-hdr, .pocn-page-hdr, .poc-page-hdr — with the same four rules
   each and small unexplained drift between them (one at weight 700, one with
   14px padding, three with a 2px .sub offset).

   The legacy names are aliased onto these rules so no markup had to change.
   New screens should use .page-hdr; the aliases can be retired in a rename
   pass whenever someone is in those files anyway.
   ============================================================ */
.page-hdr,
.ar-page-hdr, .ai-head, .apw-head, .at-header, .awc-head, .au-page-hdr,
.db-page-hdr, .it-page-hdr, .qadd-hero, .pocd-page-hdr, .pocn-page-hdr, .poc-page-hdr {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; padding: 18px 20px;
}
/* The poc_* screens centre their header content rather than baseline-aligning
   it, because their left slot holds an avatar rather than a title block. */
.page-hdr--center,
.pocd-page-hdr, .pocn-page-hdr, .poc-page-hdr { align-items: center; }
/* Dashboard sits its header inside an already-padded container. */
.page-hdr--flush, .db-page-hdr { padding: 0; }

.page-hdr h1, .page-title,
.ar-page-hdr h1, .ai-head h1, .apw-head h1, .at-header h1, .awc-head h1,
.au-page-hdr h1, .db-page-hdr h1, .it-page-hdr h1, .qadd-hero h1,
.pocd-page-hdr h1, .pocn-page-hdr h1, .poc-page-hdr h1 {
  font-size: 22px; font-weight: 600; margin: 0;
  letter-spacing: -0.02em; color: var(--text);
}
.page-hdr .eyebrow, .page-eyebrow {
  display: block;
  margin: 0 0 4px;
  color: var(--brand-text);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.page-hdr-block { display: flex; flex-direction: column; }
.page-hdr-block--crumbed { padding: 14px 20px 18px; gap: 10px; }
.page-hdr--nested { padding: 0; }
.page-hdr--tabbed { padding-bottom: 14px; }
.page-hdr-tabstrip {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 0 20px;
  border-top: 1px solid var(--border);
}
.page-hdr-tabstrip-control {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--text-3);
  font: 500 11.5px/1 var(--eb-mono);
}
.page-hdr-breadcrumb {
  display: flex;
  align-items: center;
  gap: 7px;
  color: var(--text-3);
  font-size: 11.5px;
  font-weight: 500;
}
.page-hdr-breadcrumb .crumb-back,
.page-hdr-breadcrumb .crumb-link { color: var(--view-accent-text); }
.page-hdr-breadcrumb .crumb-link:hover { text-decoration: underline; }
.page-hdr-breadcrumb .crumb-ancestor { color: var(--text-4); }
.page-hdr-breadcrumb .crumb-sep { color: var(--border-3); }
.page-hdr-breadcrumb .crumb-current {
  color: var(--text);
  font-family: var(--eb-mono);
  font-weight: 600;
}
.page-hdr-stepper {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.page-hdr-stepper a {
  padding: 3px 8px;
  border-radius: var(--radius-sm);
  color: var(--text-3);
  font-size: 11px;
}
.page-hdr-stepper a:hover { color: var(--text); background: var(--bg-2); }
.page-hdr-stepper .step-position { padding: 0 2px; color: var(--text-4); font-size: 10.5px; }
.page-hdr-title-row { display: flex; align-items: baseline; gap: 9px; }
.page-hdr h1.page-hdr-title--mono {
  font: 600 22px/1.2 var(--eb-mono);
  letter-spacing: normal;
}
.page-hdr .sub, .page-sub,
.ar-page-hdr .sub, .ai-head .sub, .apw-head .sub, .at-header .sub, .awc-head .sub,
.au-page-hdr .sub, .db-page-hdr .sub, .it-page-hdr .sub, .qadd-hero .sub,
.pocd-page-hdr .sub, .pocn-page-hdr .sub, .poc-page-hdr .sub {
  margin: 4px 0 0; color: var(--text-3); font-size: 13px;
}
.page-hdr .actions,
.ar-page-hdr .actions, .ai-head .actions, .apw-head .actions, .awc-head .actions,
.au-page-hdr .actions, .db-page-hdr .actions, .it-page-hdr .actions,
.pocd-page-hdr .actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.poc-page-hdr .left { display: flex; align-items: center; gap: 14px; }
/* Residual: .qadd-hero is a hero block sitting above page content and is the
   only one of the twelve that needed to space itself off what follows. */
.qadd-hero { margin-bottom: 18px; }

/* ============================================================
   SURFACES
   `glass` was pasted into six templates as a hardcoded translucent slate fill
   + a 1px emerald border + a 16px blur, each with its own theme override.
   (Values quoted as tokens, not literals: a raw colour in a comment here still
   trips the design-system guard grep, and it read as permission to paste.)
   The emerald is gone — a frosted panel is not a brand statement — and the
   blur is now --glass-blur so it can be dialled down globally for slow
   tablets. Add .surface-glass--brand where the brand edge is intentional.
   ============================================================ */
.surface-glass, .glass-card,
.it-page-hdr, .qadd-hero, .pocd-page-hdr, .pocn-page-hdr, .poc-page-hdr {
  background: var(--glass-bg);
  border: 1px solid var(--border-2);
  border-radius: 16px;
  box-shadow: var(--shadow-lg);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
}
.surface-glass--brand { border-color: var(--border-brand); }

/* ============================================================
   LAYOUT PRIMITIVES
   `display:flex; align-items:center; flex-wrap:wrap; gap:8px` was written out
   longhand in 12 places under 12 names. Naming it stops the thirteenth.
   ============================================================ */
.row   { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.row-tight { gap: 6px; } .row-loose { gap: 12px; }
.row-between { justify-content: space-between; }
.stack { display: flex; flex-direction: column; gap: 14px; }
.stack-tight { gap: 8px; } .stack-loose { gap: 20px; }

/* Selected chip — the .btn-primary treatment used as a selection state.
   Six sites reimplemented it: .mp-chip.active (×2), .dept-pill-sel,
   .qadd-dept-pill, .ewo-dept-list label:has(:checked), .wod-confirm-ok-default. */
.chip-select-on,
.mp-chip.active { background: var(--text); border-color: var(--text); color: var(--bg); }

/* Input with a leading icon — 4 sites positioned the icon by hand. */
.field-icon { position: relative; }
.field-icon > svg, .field-icon > .icon {
  position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
  color: var(--text-3); pointer-events: none;
}
.field-icon > input, .field-icon > .ds-input { padding-left: 32px; }

/* === Token-backed utilities ===
   The replacement target for Tailwind colour utilities in templates. A class
   like `text-slate-400` or `bg-slate-900` pins a literal colour and therefore
   bypasses the light theme entirely; these carry the same intent but resolve
   per-theme. Named for the ROLE, not the colour, so they stay correct when a
   token value changes.

   These are declared after Tailwind is linked in base.html only by virtue of
   different class names — do not rename one to shadow a Tailwind utility. */
.u-bg      { background: var(--bg); }
.u-bg-1    { background: var(--bg-1); }
.u-bg-2    { background: var(--bg-2); }
.u-bg-3    { background: var(--bg-3); }
.u-bg-4    { background: var(--bg-4); }
.u-border  { border-color: var(--border); }
.u-border-2{ border-color: var(--border-2); }
.u-border-3{ border-color: var(--border-3); }
.u-text    { color: var(--text); }
.u-text-2  { color: var(--text-2); }
.u-text-3  { color: var(--text-3); }
.u-text-4  { color: var(--text-4); }

/* Status foregrounds — theme-aware, AA-checked over their matching tint. */
.u-fg-green  { color: var(--green-text); }
.u-fg-amber  { color: var(--amber-text); }
.u-fg-red    { color: var(--red-text); }
.u-fg-blue   { color: var(--blue-text); }
.u-fg-orange { color: var(--orange-text); }
.u-fg-purple { color: var(--purple-text); }

/* Status tints — translucent, so they composite correctly over either theme. */
.u-tint-green  { background: var(--green-bg); }
.u-tint-amber  { background: var(--amber-bg); }
.u-tint-red    { background: var(--red-bg); }
.u-tint-blue   { background: var(--blue-bg); }
.u-tint-orange { background: var(--orange-bg); }
.u-tint-purple { background: var(--purple-bg); }

.u-bd-green  { border-color: var(--green-border); }
.u-bd-amber  { border-color: var(--amber-border); }
.u-bd-red    { border-color: var(--red-border); }
.u-bd-blue   { border-color: var(--blue-border); }

.grid { display: grid; gap: 12px; }
.flex { display: flex; }
.gap-2 { gap: 8px; } .gap-3 { gap: 12px; } .gap-4 { gap: 16px; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.flex-1 { flex: 1; min-width: 0; }
.flex-wrap { flex-wrap: wrap; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.text-xs { font-size: 11px; }
.text-sm { font-size: 12px; }
.text-md { font-size: 14px; }
.text-lg { font-size: 16px; }
.text-xl { font-size: 20px; }
.text-2xl { font-size: 28px; }
.font-semibold { font-weight: 600; }
.font-bold { font-weight: 700; }
.mb-1 { margin-bottom: 4px; }
.mb-2 { margin-bottom: 8px; }
.mb-3 { margin-bottom: 12px; }
.mb-4 { margin-bottom: 16px; }
.mb-6 { margin-bottom: 24px; }
.mt-1 { margin-top: 4px; }
.mt-2 { margin-top: 8px; }
.mt-3 { margin-top: 12px; }
.mt-4 { margin-top: 16px; }

/* === Scrollbars === */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--bg-4); border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: var(--border-3); }

/* === Login page — full-bleed === */
.no-shell .sidebar, .no-shell .topbar { display: none; }
.no-shell .main { margin-left: 0; }
.no-shell .content { padding: 0; }

/* === Dept colors ===
   Slug rule: dept_chip macro lowercases the name and replaces ' - ' then ' '
   with '-'. So "METAL FAB - MONUMENTS" → .dept-metal-fab-monuments,
   "VINYL APP" → .dept-vinyl-app, etc.
   Any dept name without a matching variant falls back to the neutral
   .dept-chip base style.

   Each variant sets --chip-bg/--chip-fg from the chip palette in
   design-tokens.css rather than hardcoding a hex. That indirection is the
   whole point: the palette carries a light-mode foreground, so these chips
   now flip with the theme. Previously they kept their dark 300/400-weight
   hues on a near-white surface and measured as low as 2.17:1 (AA needs 4.5).

   Three variants moved to the nearest palette hue because they were one-off
   near-duplicates of a neighbour: plotter (a sky-300 -> --chip-sky), braille
   (a violet-300 -> --chip-violet) and photopolymer (a cyan-300 -> --chip-cyan).
   At 10px the shift is not perceptible, and the palette stays closed. */
.dept-chip { display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; border-radius: 5px;
  font-size: 10px; font-weight: 600; line-height: 1.6; letter-spacing: 0.04em;
  white-space: nowrap;
  background: var(--chip-bg, var(--bg-3)); color: var(--chip-fg, var(--text-2)); }

/* — Prototype legacy slugs (kept so any template using them stays styled) — */
.dept-chip.dept-fab        { --chip-bg: var(--chip-blue-bg);   --chip-fg: var(--chip-blue); }
.dept-chip.dept-paint      { --chip-bg: var(--chip-purple-bg); --chip-fg: var(--chip-purple); }
.dept-chip.dept-electrical { --chip-bg: var(--chip-amber-bg);  --chip-fg: var(--chip-amber); }
.dept-chip.dept-install    { --chip-bg: var(--chip-green-bg);  --chip-fg: var(--chip-green); }
.dept-chip.dept-qc         { --chip-bg: var(--chip-pink-bg);   --chip-fg: var(--chip-pink); }
.dept-chip.dept-router     { --chip-bg: var(--chip-amber-bg);  --chip-fg: var(--chip-amber); }
.dept-chip.dept-flatbed    { --chip-bg: var(--chip-cyan-bg);   --chip-fg: var(--chip-cyan); }

/* — WOHub real dept names (PRODUCTION_DEPARTMENTS_FULL + EXTERNAL) — */
.dept-chip.dept-roland     { --chip-bg: var(--chip-green-bg);   --chip-fg: var(--chip-green); }
.dept-chip.dept-epson      { --chip-bg: var(--chip-purple-bg);  --chip-fg: var(--chip-purple); }
.dept-chip.dept-laser      { --chip-bg: var(--chip-yellow-bg);  --chip-fg: var(--chip-yellow); }
.dept-chip.dept-sanding    { --chip-bg: var(--chip-orange-bg);  --chip-fg: var(--chip-orange); }
.dept-chip.dept-dryroom    { --chip-bg: var(--chip-sky-bg);     --chip-fg: var(--chip-sky); }
.dept-chip.dept-engraver   { --chip-bg: var(--chip-violet-bg);  --chip-fg: var(--chip-violet); }
.dept-chip.dept-braille    { --chip-bg: var(--chip-violet-bg);  --chip-fg: var(--chip-violet); }
.dept-chip.dept-vinyl-app  { --chip-bg: var(--chip-emerald-bg); --chip-fg: var(--chip-emerald); }
.dept-chip.dept-plotter    { --chip-bg: var(--chip-sky-bg);     --chip-fg: var(--chip-sky); }
.dept-chip.dept-paintmask  { --chip-bg: var(--chip-fuchsia-bg); --chip-fg: var(--chip-fuchsia); }
.dept-chip.dept-metal-fab-monuments  { --chip-bg: var(--chip-slate-bg);  --chip-fg: var(--chip-slate); }
.dept-chip.dept-metal-fab-structural { --chip-bg: var(--chip-blue-bg);   --chip-fg: var(--chip-blue); }
.dept-chip.dept-metal-fab-handicap   { --chip-bg: var(--chip-teal-bg);   --chip-fg: var(--chip-teal); }
.dept-chip.dept-electrical-assembly  { --chip-bg: var(--chip-amber-bg);  --chip-fg: var(--chip-amber); }
.dept-chip.dept-metal-fab-channel-letter { --chip-bg: var(--chip-orange-bg); --chip-fg: var(--chip-orange); }
.dept-chip.dept-handicap-assembly    { --chip-bg: var(--chip-teal-bg);   --chip-fg: var(--chip-teal); }
.dept-chip.dept-kiss-print           { --chip-bg: var(--chip-rose-bg);   --chip-fg: var(--chip-rose); }
.dept-chip.dept-photopolymer         { --chip-bg: var(--chip-cyan-bg);   --chip-fg: var(--chip-cyan); }
.dept-chip.dept-digital-print        { --chip-bg: var(--chip-blue-bg);   --chip-fg: var(--chip-blue); }
.dept-chip.dept-final-assembly       { --chip-bg: var(--chip-lime-bg);   --chip-fg: var(--chip-lime); }
.dept-chip.dept-finished-goods       { --chip-bg: var(--chip-green-bg);  --chip-fg: var(--chip-green); }
.dept-chip.dept-central-station      { --chip-bg: var(--chip-slate-bg);  --chip-fg: var(--chip-slate); }
.dept-chip.dept-inventory            { --chip-bg: var(--chip-stone-bg);  --chip-fg: var(--chip-stone); }
.dept-chip.dept-scheduling           { --chip-bg: var(--chip-indigo-bg); --chip-fg: var(--chip-indigo); }
.dept-chip.dept-sales                { --chip-bg: var(--chip-amber-bg);  --chip-fg: var(--chip-amber); }
.dept-chip.dept-metal-fab            { --chip-bg: var(--chip-slate-bg);  --chip-fg: var(--chip-slate); }

/* Sparkline */
.spark-line { fill: none; stroke-width: 1.5; }

.pulse, .ds-pulse { animation: pulse 1.6s ease-in-out infinite; }
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .4; } }

/* ============================================================
   MOTION
   Four keyframes and three utilities. Everything that moves in the app
   should reach for one of these rather than inventing a duration — the
   durations and easings themselves live in design-tokens.css.

   Entrances use --ease-out (decelerating, slight settle). State changes on
   something already on screen use --ease. Nothing exceeds --dur-3 (.24s):
   this is a tool people use for eight hours, and animation that reads as
   "premium" on a marketing site reads as "slow" on the 400th interaction.
   ============================================================ */
@keyframes fadeIn   { from { opacity: 0; } to { opacity: 1; } }
@keyframes riseIn   { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
@keyframes popIn    { from { opacity: 0; transform: translateY(8px) scale(.98); } to { opacity: 1; transform: none; } }

.anim-fade { animation: fadeIn var(--dur-2) var(--ease); }
.anim-rise { animation: riseIn var(--dur-3) var(--ease-out); }
.anim-pop  { animation: popIn  var(--dur-3) var(--ease-out); }

/* Modal panel rises as its backdrop fades. */
.modal { animation: popIn var(--dur-3) var(--ease-out); }

/* Dropdown / popover / menu surfaces. */
.menu-pop { animation: riseIn var(--dur-2) var(--ease-out); transform-origin: top; }

/* Hover elevation for interactive cards. Opt-in via .card-hover, because most
   .card instances in the app are static containers, not click targets. */
.card-hover { transition: transform var(--dur-2) var(--ease), box-shadow var(--dur-2) var(--ease),
                          border-color var(--dur-2) var(--ease); }
.card-hover:hover { transform: translateY(-1px); box-shadow: var(--shadow-lg); border-color: var(--border-2); }

/* ============================================================
   FOCUS
   :focus-visible only, so pointer users never see a ring but keyboard and
   switch users always do. The ring is an offset box-shadow rather than an
   outline so it follows border-radius on every component.
   ============================================================ */
:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.btn:focus-visible,
.icon-btn:focus-visible,
.tab:focus-visible,
.nav-item:focus-visible,
.role-pill:focus-visible,
.segmented button:focus-visible,
.segmented a:focus-visible { outline: none; box-shadow: var(--focus-ring); }
input:focus-visible, textarea:focus-visible, select:focus-visible {
  border-color: var(--text-3); box-shadow: var(--focus-ring);
}
/* Skip link — present for keyboard users, off-screen until focused. */
.skip-link { position: absolute; left: -9999px; top: 0; z-index: 999;
  background: var(--bg-1); color: var(--text); border: 1px solid var(--border-2);
  padding: 10px 16px; border-radius: 0 0 8px 0; font-size: 13px; font-weight: 600; }
.skip-link:focus { left: 0; }

/* ============================================================
   SKELETONS
   Shape variants for the base .skeleton shimmer. Prefer these over a spinner
   wherever the shape of the incoming content is known — a skeleton communicates
   "this much is loading", a spinner communicates only "wait".
   ============================================================ */
.skel-line   { height: 12px; border-radius: 4px; }
.skel-line + .skel-line { margin-top: 8px; }
.skel-line.w-3\/4 { width: 75%; }
.skel-line.w-1\/2 { width: 50%; }
.skel-line.w-1\/4 { width: 25%; }
.skel-title  { height: 18px; border-radius: 5px; width: 40%; }
.skel-block  { height: 72px; border-radius: 10px; }
.skel-circle { border-radius: 50%; width: 32px; height: 32px; }
.skel-kpi    { height: 96px; border-radius: 12px; }
/* Drop-in row for table.tbl bodies. */
.skel-row td { padding: 10px 14px; }
.skel-row td > .skeleton { height: 12px; border-radius: 4px; }

/* ============================================================
   STATUS TONES
   Six tones, declared once, consumed by every banner-shaped component below
   (.scan-fb, .toast, .alert). They were being re-derived per component before,
   which is how the app ended up with a green that meant "success" in one place
   and "brand" in another.

   Each tone is a THREE-token set — tint, foreground, edge — because a tone has
   to survive both themes: the tints are translucent so they composite over
   either surface, and the *-text foregrounds already carry their light-mode
   darkening (design-tokens.css). Nothing here needs a theme fork.

   `dup` (duplicate scan) is purple deliberately: it is neither a success nor a
   failure, and on a shop floor "you already scanned that" must not look like
   either. `neutral` is the idle/ready state.
   ============================================================ */
.scan-fb, .toast, .alert {
  --tone-bg: var(--bg-2); --tone-fg: var(--text-2); --tone-bd: var(--border-2);
}
.scan-fb--ok,      .toast--ok,      .alert--ok      { --tone-bg: var(--green-bg);  --tone-fg: var(--green-text);  --tone-bd: var(--green-border); }
.scan-fb--warn,    .toast--warn,    .alert--warn    { --tone-bg: var(--amber-bg);  --tone-fg: var(--amber-text);  --tone-bd: var(--amber-border); }
.scan-fb--err,     .toast--err,     .alert--err     { --tone-bg: var(--red-bg);    --tone-fg: var(--red-text);    --tone-bd: var(--red-border); }
.scan-fb--info,    .toast--info,    .alert--info    { --tone-bg: var(--blue-bg);   --tone-fg: var(--blue-text);   --tone-bd: var(--blue-border); }
.scan-fb--dup,     .toast--dup,     .alert--dup     { --tone-bg: var(--purple-bg); --tone-fg: var(--purple-text); --tone-bd: var(--purple-border); }
.scan-fb--neutral, .toast--neutral, .alert--neutral { --tone-bg: var(--bg-2);      --tone-fg: var(--text-2);      --tone-bd: var(--border-2); }

/* ============================================================
   SCAN FEEDBACK BANNER
   The single most important piece of UI in the building: the answer to "did
   that scan work". Sized for a tablet mounted at arm's length under shop
   lighting, read by someone whose hands are full — so the message is 20px
   minimum, the block is at least 64px tall, and nothing important is smaller
   than 13px.

   State is encoded THREE ways, not one: colour, a glyph in the icon slot, and
   the weight/style of the left rule. That redundancy is not decoration —
   overhead shop lighting washes hue out badly on a glossy tablet, and roughly
   1 in 12 men on the floor will not separate the green from the amber. Anyone
   who can see the banner at all can see a ✓ versus a ✕.

   Two surfaces hand-roll this today (warehouse/scan.html with 15 hex + 15 rgba
   literals, and kiosk.css's .kiosk-result); both should collapse onto it.
   ============================================================ */
.scan-fb {
  display: flex; align-items: center; gap: 14px;
  min-height: 64px; padding: 14px 18px;
  border-radius: 12px;
  background: var(--tone-bg);
  border: 1px solid var(--tone-bd);
  /* The left rule is the peripheral-vision signal — it is what you catch
     without looking directly at the tablet. */
  border-left: 4px solid var(--tone-fg);
  color: var(--tone-fg);
  animation: riseIn var(--dur-3) var(--ease-out);
}
/* Icon slot. Empty in markup — the glyph comes from the variant, so a caller
   never has to keep an icon and a class in sync. Callers that want a real SVG
   put it inside and it wins, because a child out-paints the ::before. */
.scan-fb-icon {
  flex: 0 0 auto; width: 40px; height: 40px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 22px; font-weight: 700; line-height: 1;
  background: var(--tone-bg); box-shadow: inset 0 0 0 2px var(--tone-fg);
}
.scan-fb-icon::before { content: "•"; }
.scan-fb--ok      .scan-fb-icon::before { content: "✓"; }
.scan-fb--warn    .scan-fb-icon::before { content: "!"; }
.scan-fb--err     .scan-fb-icon::before { content: "✕"; }
.scan-fb--dup     .scan-fb-icon::before { content: "⧉"; }
.scan-fb--neutral .scan-fb-icon::before { content: "⌁"; }
/* Shape, on top of glyph and colour: an error is double-ruled and a duplicate
   is dashed, so the three states differ in silhouette on a washed-out screen. */
.scan-fb--err  { border-left-style: double; border-left-width: 6px; }
.scan-fb--dup  { border-left-style: dashed; }

.scan-fb-body  { min-width: 0; flex: 1 1 auto; }
.scan-fb-title {
  font-size: 13px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.08em; margin: 0; opacity: 0.85;
}
.scan-fb-msg {
  font-size: 20px; font-weight: 700; line-height: 1.25; margin: 2px 0 0;
  color: var(--text); word-break: break-word;
}
/* Meta is the audit trail — code, location, who, when. --text-3 rather than the
   tone colour: it is reference detail, and tinting it would compete with the
   message for the one glance the operator gives this. */
.scan-fb-meta {
  font-size: 13px; color: var(--text-3); margin: 4px 0 0;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-feature-settings: 'tnum';
}

/* Read-only four-stage job spine and compact scanned/printed/unprinted strip. */
.spine { display: inline-flex; align-items: center; }
.spine-step { display: inline-flex; align-items: center; gap: 6px; }
.spine-link {
  width: 22px;
  height: 1px;
  margin: 0 8px;
  flex: 0 0 auto;
  background: var(--border-2);
}
.spine-link.is-done { background: var(--eb-ok); }
.spine-link.is-next { background: var(--brand); }
.spine-dot {
  width: 9px;
  height: 9px;
  flex: 0 0 auto;
  box-sizing: border-box;
  border: 1.5px solid var(--border-3);
  border-radius: 50%;
}
.spine-label { color: var(--text-4); font-size: 11px; font-weight: 500; line-height: 1; }
.spine-step.is-done .spine-dot { border: 0; background: var(--eb-ok); }
.spine-step.is-done .spine-label { color: var(--eb-ok); font-weight: 600; }
.spine-step.is-current .spine-dot {
  width: 11px;
  height: 11px;
  border: 0;
  background: var(--brand);
  box-shadow: 0 0 0 4px var(--brand-bg);
}
.spine-step.is-current .spine-label { color: var(--brand-text); font-weight: 700; }
.progress-strip {
  display: flex;
  width: 150px;
  height: 6px;
  overflow: hidden;
  flex: 0 0 auto;
  border-radius: var(--radius-sm);
  background: var(--bg-4);
}
.progress-strip > * { display: block; height: 100%; }
.progress-strip > .is-ok { background: var(--eb-ok); }
.progress-strip > .is-attention { background: var(--eb-attention); }
/* Under way but not finished — an unclassed fill would be invisible. */
.progress-strip > .is-partial { background: var(--brand); }
.progress-count {
  color: var(--text-3);
  font: 500 11px/1 var(--eb-mono);
  font-feature-settings: "tnum";
}

/* Warehouse inventory stage spine and physical-scope controls. */
.warehouse-inventory { gap: 16px; }
.warehouse-inventory > .surface-glass { overflow: hidden; }
.warehouse-stage-grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(120px, 1fr));
  gap: 0;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.warehouse-stage {
  display: flex;
  min-height: 92px;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  padding: 12px 14px;
  border: 0;
  border-right: 1px solid var(--border);
  border-radius: 0;
  background: var(--bg-1);
  color: var(--text-3);
  text-decoration: none;
}
.warehouse-stage:last-child { border-right: 0; }
.warehouse-stage:hover,
.warehouse-stage.is-active { border-color: var(--border-brand); background: var(--bg-2); }
.warehouse-stage--accent.is-active {
  box-shadow: inset 0 -3px 0 var(--brand);
  background: var(--brand-bg);
}
.warehouse-stage-name {
  color: var(--text-3);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .09em;
  text-transform: uppercase;
}
.warehouse-stage strong {
  color: var(--text);
  font: 600 26px/1 var(--eb-mono);
  font-feature-settings: "tnum";
}
.warehouse-stage > span:last-child { font-size: 11px; }
.warehouse-stage--alert { border-left: 3px solid var(--eb-alert); }
.warehouse-stage--accent { border-left: 3px solid var(--brand); }
.warehouse-stage--ok { border-left: 3px solid var(--eb-ok); }
.warehouse-stage--attention { border-left: 3px solid var(--eb-attention); }
.warehouse-exception-strip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 16px;
  border: 1px solid var(--red-border);
  border-left: 4px solid var(--eb-alert);
  border-radius: var(--radius-md);
  background: var(--red-bg);
}
.warehouse-exception-strip strong { display: block; color: var(--text); font-size: 13px; }
.warehouse-exception-strip p { margin: 3px 0 0; color: var(--text-3); font-size: 12px; }
.warehouse-exception-eyebrow {
  display: block;
  margin-bottom: 4px;
  color: var(--red-text);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
}
.warehouse-commandbar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-1);
}
.warehouse-search-form { display: flex; align-items: center; gap: 6px; flex: 1 1 320px; }
.warehouse-search {
  display: flex;
  min-width: 220px;
  height: 36px;
  flex: 1;
  align-items: center;
  gap: 8px;
  padding: 0 11px;
  border: 1px solid var(--border-2);
  border-radius: var(--radius-sm);
  background: var(--bg-2);
  color: var(--text-4);
}
.warehouse-search input { width: 100%; border: 0; outline: 0; background: transparent; color: var(--text); }
.warehouse-scope { width: max-content; margin: 0; }
.warehouse-scope a {
  display: inline-flex;
  min-height: 34px;
  align-items: center;
  padding: 0 12px;
  border-radius: var(--radius-sm);
  color: var(--text-3);
  font-size: 12px;
}
.warehouse-scope a.active { background: var(--view-accent-bg); color: var(--view-accent-text); }
.warehouse-filters {
  display: grid;
  grid-template-columns: minmax(180px, 1.4fr) minmax(130px, .8fr) minmax(120px, .7fr) minmax(180px, 1fr) auto;
  align-items: end;
  gap: 10px;
}
.warehouse-filters-compact { display: flex; grid-template-columns: none; align-items: center; }
.warehouse-filters-compact select { min-width: 218px; height: 36px; }
.warehouse-filters input,
.warehouse-filters select { width: 100%; }
.warehouse-filter-actions { display: flex; gap: 6px; }
.warehouse-results { overflow: visible; }
.warehouse-selection-bar {
  position: sticky;
  top: 10px;
  z-index: 18;
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 0 0 12px;
  padding: 10px 12px;
  border: 1px solid var(--border-brand);
  border-radius: var(--radius-md);
  background: var(--brand-bg);
  box-shadow: var(--shadow-lg);
}
.warehouse-selection-bar.hidden { display: none; }
.warehouse-selection-count { min-width: 132px; color: var(--text); }
.warehouse-selection-count b {
  display: inline-grid; width: 30px; height: 30px; margin-right: 8px;
  place-items: center; border-radius: var(--radius-sm);
  background: var(--brand); color: var(--text-on-brand);
}
.warehouse-selection-actions { display: flex; gap: 8px; margin-left: auto; }
.warehouse-bulk-result { flex-basis: 100%; margin: 0; color: var(--text-2); font-size: 12px; }
.inventory-select { width: 16px; height: 16px; accent-color: var(--brand); }
.inventory-sign-row:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.warehouse-context-action {
  display: flex; width: 100%; align-items: center; gap: 10px;
  padding: 9px 14px; border: 0; background: transparent;
  color: var(--tooltip-text); font-size: 12px; font-weight: 600; text-align: left;
}
.warehouse-context-action:hover,
.warehouse-context-action:focus-visible { background: var(--tooltip-border); }
.warehouse-context-action.is-primary { background: var(--view-accent-bg); color: var(--view-accent-text); }
.warehouse-context-action.is-danger { color: var(--red-text); }
.warehouse-drawer-scrim { position: fixed; inset: 0; z-index: 88; background: var(--scrim); }
.warehouse-drawer-scrim.hidden,
.warehouse-detail-drawer.hidden { display: none; }
.warehouse-detail-drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 89;
  display: flex; width: min(440px, 94vw); flex-direction: column;
  border-left: 1px solid var(--border-2); background: var(--bg-1); box-shadow: var(--shadow-lg);
}
.warehouse-detail-drawer > header,
.warehouse-detail-drawer > footer { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 18px; border-bottom: 1px solid var(--border); }
.warehouse-detail-drawer > footer { justify-content: flex-start; border-top: 1px solid var(--border); border-bottom: 0; }
.warehouse-detail-drawer h2 { margin: 3px 0 0; font: 700 20px/1.2 var(--eb-mono); }
.warehouse-drawer-eyebrow { color: var(--text-3); font-size: 10px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; }
.warehouse-drawer-body { flex: 1; overflow: auto; padding: 18px; }
.warehouse-detail-list { display: grid; gap: 8px; margin: 0 0 24px; }
.warehouse-detail-list div { display: flex; justify-content: space-between; gap: 16px; padding: 10px 0; border-bottom: 1px solid var(--border); }
.warehouse-detail-list dt { color: var(--text-3); }
.warehouse-detail-list dd { margin: 0; color: var(--text); font-weight: 600; }
.warehouse-history { display: grid; gap: 8px; padding: 0; list-style: none; }
.warehouse-history li { display: grid; gap: 3px; padding: 10px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-2); }
.warehouse-history span { color: var(--text-3); font-size: 12px; }
@media (max-width: 1000px) {
  .warehouse-stage-grid { grid-template-columns: repeat(3, 1fr); }
  .warehouse-filters { grid-template-columns: repeat(2, 1fr); }
  .warehouse-filter-actions { grid-column: 1 / -1; }
  .warehouse-commandbar { flex-wrap: wrap; }
}
@media (max-width: 640px) {
  .warehouse-stage-grid { grid-template-columns: repeat(2, 1fr); }
  .warehouse-exception-strip { align-items: stretch; flex-direction: column; }
  .warehouse-filters { grid-template-columns: 1fr; }
  .warehouse-filter-actions { grid-column: auto; flex-direction: column; }
  .warehouse-stage { min-height: 78px; padding: 10px; }
  .warehouse-commandbar, .warehouse-search-form { align-items: stretch; flex-direction: column; }
  .warehouse-search { min-width: 0; width: 100%; flex: none; }
  .warehouse-scope { width: 100%; }
  .warehouse-scope a { flex: 1; justify-content: center; }
  .warehouse-filters-compact, .warehouse-filters-compact label, .warehouse-filters-compact select { width: 100%; }
  .warehouse-selection-bar { align-items: stretch; flex-wrap: wrap; }
  .warehouse-selection-actions { flex-wrap: wrap; margin-left: 0; }
}

/* Warehouse location/tote inspector. */
.warehouse-locations-page { display: grid; gap: 16px; }
.warehouse-location-layout {
  display: grid;
  min-height: 650px;
  grid-template-columns: 330px minmax(0, 1fr);
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-1);
}
.warehouse-location-tree { padding: 14px 10px; border-right: 1px solid var(--border); background: var(--bg-2); }
.warehouse-location-tree > .warehouse-search { width: 100%; margin-bottom: 18px; }
.location-facility { margin: 0 0 16px; }
.location-facility h2 {
  margin: 0 8px 8px; color: var(--text-4); font-size: 10px;
  font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
}
.location-tree-row {
  display: flex; min-height: 42px; align-items: center; justify-content: space-between;
  gap: 12px; padding: 7px 10px; border: 1px solid transparent;
  border-radius: var(--radius-sm); color: var(--text-2); text-decoration: none;
}
.location-tree-row:hover { background: var(--bg-3); }
.location-tree-row.is-active { border-color: var(--view-accent-border); background: var(--view-accent-bg); color: var(--text); }
.location-tree-row > span:first-child { display: flex; align-items: baseline; gap: 8px; }
.location-tree-row b { font: 700 13px/1 var(--eb-mono); }
.location-tree-row small { color: var(--text-4); font-size: 10px; }
.location-occupancy { display: flex; align-items: center; gap: 8px; }
.location-occupancy i { display: block; width: 34px; height: 5px; border-radius: 9px; background: linear-gradient(90deg,var(--eb-ok) var(--fill),var(--bg-4) var(--fill)); }
.location-occupancy strong { color: var(--text-3); font: 600 11px/1 var(--eb-mono); }
.location-tree-child { display: flex; align-items: center; justify-content: space-between; padding: 7px 18px 7px 34px; color: var(--text-3); font: 500 11px/1.2 var(--eb-mono); text-decoration: none; }
.location-tree-child:hover { color: var(--view-accent-text); }
.location-tree-child.is-nested {
  position: relative; padding-left: 54px; color: var(--text-2);
}
.location-tree-child.is-nested::before {
  position: absolute; left: 38px; top: 0; width: 9px; height: 50%;
  border-bottom: 1px solid var(--border-2); border-left: 1px solid var(--border-2);
  content: "";
}
.location-tree-section-label {
  margin: 10px 18px 5px 34px; color: var(--view-accent-text);
  font-size: 9px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
}
.location-tree-empty-group { margin: 6px 8px 10px 24px; }
.location-tree-empty-group summary {
  display: flex; align-items: center; gap: 7px; padding: 7px 10px;
  color: var(--text-3); cursor: pointer; font-size: 10px; font-weight: 700;
  letter-spacing: .08em; list-style: none; text-transform: uppercase;
}
.location-tree-empty-group summary::-webkit-details-marker { display: none; }
.location-tree-empty-group summary::before { content: "›"; font-size: 16px; line-height: 1; transform-origin: center; }
.location-tree-empty-group[open] summary::before { transform: rotate(90deg); }
.location-tree-empty-group .location-tree-child { padding-left: 28px; }
.warehouse-location-inspector { min-width: 0; }
.location-inspector-header {
  display: flex; min-height: 116px; align-items: center; justify-content: space-between;
  gap: 20px; padding: 22px 26px; border-bottom: 1px solid var(--border);
}
.location-title-line { display: flex; align-items: center; gap: 12px; }
.location-title-line h1 { margin: 0; font: 700 24px/1.2 var(--eb-mono); }
.location-inspector-header p { margin: 8px 0 0; color: var(--text-3); }
.location-header-actions,.location-tote-actions { display: flex; align-items: center; gap: 10px; }
.location-inline-form {
  display: flex; align-items: end; justify-content: flex-end; gap: 10px;
  padding: 12px 26px; border-bottom: 1px solid var(--border); background: var(--brand-bg);
}
.location-inline-form.hidden { display: none; }
.location-inline-form label { display: grid; gap: 4px; color: var(--text-3); font-size: 11px; }
.location-inline-form select { min-width: 260px; }
.location-tote-filters {
  display: flex; align-items: end; gap: 10px; flex-wrap: wrap;
  padding: 14px 26px; border-bottom: 1px solid var(--border); background: var(--bg-2);
}
.location-tote-filters label { display: grid; gap: 5px; color: var(--text-3); font-size: 11px; }
.location-tote-filters label:first-of-type { flex: 1 1 240px; }
.location-tote-filters input,.location-tote-filters select { min-height: 36px; }
.location-tote-filters input { width: 100%; }
.location-tote-list { display: grid; gap: 12px; padding: 20px 26px; }
.location-slot-list { display: grid; gap: 14px; padding: 20px 26px; }
.location-slot-card {
  overflow: hidden; border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--bg-2);
}
.location-slot-header {
  display: flex; min-height: 58px; align-items: center; gap: 12px;
  padding: 12px 18px; border-bottom: 1px solid var(--border);
}
.location-slot-header b { color: var(--text); font: 700 14px/1 var(--eb-mono); }
.location-slot-header > span:last-child { color: var(--text-3); font-size: 12px; }
.location-slot-contents { position: relative; display: grid; gap: 8px; padding: 12px 14px 12px 34px; }
.location-slot-contents::before {
  position: absolute; top: 12px; bottom: 26px; left: 26px; width: 1px;
  background: var(--border-2); content: "";
}
.location-content-row {
  position: relative; min-width: 0; padding: 10px 14px 10px 28px;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: color-mix(in srgb, var(--bg-1), transparent 30%);
}
.location-content-row::before {
  position: absolute; top: 0; left: -9px; width: 18px; height: 50%;
  border-bottom: 1px solid var(--border-2); content: "";
}
.location-content-row.is-loose-signs { display: flex; min-height: 54px; align-items: center; gap: 20px; }
.location-content-row.is-tote { display: grid; gap: 4px; }
.location-content-main { display: flex; min-width: 0; align-items: center; justify-content: space-between; gap: 16px; }
.location-content-label { display: flex; min-width: 145px; align-items: center; gap: 10px; color: var(--text-2); font-size: 12px; }
.location-content-label b { color: var(--text); font: 700 13px/1 var(--eb-mono); }
.location-content-label > span:not(.location-tote-icon) { color: var(--text-3); }
.location-content-label small { color: var(--text-4); }
.location-tote-icon {
  position: relative; display: inline-block; width: 18px; height: 14px; flex: 0 0 auto;
  border: 1px solid var(--view-accent-text); border-radius: 2px;
}
.location-tote-icon::before {
  position: absolute; top: -4px; left: -2px; width: 20px; height: 4px;
  border: 1px solid var(--view-accent-text); border-radius: 2px 2px 0 0; content: "";
}
.location-content-row .location-sign-chips { flex: 1; padding: 0; }
.location-content-row.is-tote > .location-sign-chips { padding: 7px 0 0 28px; }
.location-content-row .tote-warning { margin: 7px 0 0 28px; border: 1px solid var(--amber-border); border-radius: var(--radius-sm); }
.location-loose-totes { display: grid; gap: 8px; margin-top: 2px; }
.location-loose-totes h2 {
  margin: 2px 0 0; color: var(--text-3); font-size: 10px; letter-spacing: .1em; text-transform: uppercase;
}
.location-loose-totes h2 span { margin-left: 6px; color: var(--text-4); }
.location-loose-tote { overflow: hidden; border: 1px solid var(--amber-border); border-radius: var(--radius-md); background: var(--bg-2); }
.location-loose-tote > header { display: flex; min-height: 50px; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 16px; }
.location-loose-tote > header b { font: 700 13px/1 var(--eb-mono); }
.location-loose-tote > header > span:last-child { color: var(--text-3); font-size: 12px; }
.location-empty-slots { overflow: hidden; border: 1px solid var(--border); border-radius: var(--radius-md); background: color-mix(in srgb, var(--bg-2), transparent 24%); }
.location-empty-slots > summary {
  display: flex; min-height: 56px; align-items: center; padding: 10px 18px;
  color: var(--text-2); cursor: pointer; list-style: none;
}
.location-empty-slots > summary::-webkit-details-marker { display: none; }
.location-empty-slots > summary::before { margin-right: 14px; color: var(--view-accent-text); content: "›"; font-size: 22px; line-height: 1; }
.location-empty-slots[open] > summary::before { transform: rotate(90deg); }
.location-empty-slots > summary span { display: flex; align-items: baseline; gap: 18px; }
.location-empty-slots > summary b { font-size: 13px; }
.location-empty-slots > summary small { color: var(--text-4); font-size: 11px; }
.location-empty-slot-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 7px; padding: 0 14px 14px; border-top: 1px solid var(--border); }
.location-empty-slot {
  display: flex; min-height: 40px; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap;
  margin-top: 10px; padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--radius-sm); color: var(--text-3);
}
.location-empty-slot b { font: 600 11px/1 var(--eb-mono); }
.location-empty-slot small { color: var(--text-4); font-size: 9px; }
.location-empty-tote {
  width: 100%; padding-top: 6px; border-top: 1px solid var(--border);
  color: var(--text-4); font: 500 9px/1.2 var(--eb-mono);
}
.location-tote {
  overflow: hidden; border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--bg-2);
}
.location-tote.is-nested {
  position: relative; margin-left: 32px;
}
.location-tote.is-nested::before {
  position: absolute; top: 0; bottom: 0; left: 0; width: 3px;
  background: var(--view-accent-border); content: "";
}
.location-tote.is-ready { border-color: color-mix(in srgb, var(--eb-ok), var(--border) 50%); }
.location-tote.is-empty { background: color-mix(in srgb, var(--bg-2), transparent 24%); }
.location-tote.is-empty > header {
  min-height: 48px; padding-top: 8px; padding-bottom: 8px; border-bottom: 0;
}
.location-tote.is-empty .tote-cube { color: var(--text-4); font-size: 18px; }
.location-tote.is-empty .location-tote-name { gap: 8px; }
.location-tote > header {
  display: flex; min-height: 66px; align-items: center; justify-content: space-between;
  gap: 16px; padding: 12px 16px; border-bottom: 1px solid var(--border);
}
.location-tote-name { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.location-tote-name b { font: 700 14px/1 var(--eb-mono); }
.location-tote-name small { color: var(--text-3); }
.location-tote-name .tote-placement { color: var(--text-2); font-family: var(--eb-mono); }
.tote-cube { color: var(--eb-ok); font-size: 22px; }
.location-tote-actions a { color: var(--brand-text); font-size: 12px; text-decoration: none; }
.location-sign-chips { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 14px 16px; }
.location-sign-chip {
  padding: 7px 11px; border: 1px solid var(--view-accent-border); border-radius: var(--radius-sm);
  background: var(--view-accent-bg); color: var(--view-accent-text); font: 600 11px/1 var(--eb-mono); text-decoration: none;
}
.location-sign-chip:hover { border-color: var(--view-accent); color: var(--view-accent-text); }
.tote-warning { padding: 9px 16px; border-bottom: 1px solid var(--amber-border); background: var(--amber-bg); color: var(--amber-text); font-size: 12px; font-weight: 600; }
.location-capacity-note,.location-empty {
  margin: 0 26px 20px; padding: 14px 16px; border: 1px dashed var(--border-2);
  border-radius: var(--radius-md); color: var(--text-3); font-size: 12px;
}
.location-capacity-note { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.location-tote-pagination { display: flex; align-items: center; justify-content: center; gap: 12px; padding: 0 26px 20px; color: var(--text-3); font-size: 12px; }
/* ============================================================
   WAREHOUSE STRUCTURE ADMIN — collapsed drawer with three
   labelled form panels (facility / location / container).
   ============================================================ */
.warehouse-location-admin {
  padding: 14px 18px; border: 1px solid var(--border);
  border-radius: var(--radius-md); background: var(--bg-1); margin-top: 8px;
}
.warehouse-location-admin summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: center; gap: 10px; padding: 2px 0;
}
.warehouse-location-admin summary::-webkit-details-marker { display: none; }
.warehouse-location-admin summary::before {
  content: "›"; font-size: 16px; color: var(--text-3);
  transition: transform 0.15s; display: inline-block; line-height: 1;
}
.warehouse-location-admin[open] summary::before { transform: rotate(90deg); }
.wla-summary-label { color: var(--text-2); font-weight: 600; font-size: 13px; }
.wla-summary-hint { font-size: 11px; color: var(--text-4); font-weight: 400; }

.location-admin-panels {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-top: 16px;
}
.location-admin-panel {
  background: var(--bg-2); border: 1px solid var(--border-2);
  border-radius: var(--radius-md); padding: 14px 16px;
  display: flex; flex-direction: column; gap: 10px;
}
.location-admin-panel-header { border-bottom: 1px solid var(--border); padding-bottom: 10px; }
.location-admin-panel-header h4 {
  font-size: 11px; font-weight: 700; color: var(--text-3);
  text-transform: uppercase; letter-spacing: 0.07em; margin: 0 0 3px;
}
.location-admin-panel-header p { font-size: 11px; color: var(--text-4); margin: 0; line-height: 1.4; }
.location-admin-form { display: flex; flex-direction: column; gap: 10px; }
.location-admin-form .field-label {
  font-size: 10px; font-weight: 600; color: var(--text-3);
  text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 4px;
}
.location-admin-form .btn { align-self: flex-start; margin-top: 2px; }
@media (max-width: 900px) {
  .warehouse-location-layout { grid-template-columns: 260px minmax(0,1fr); }
  .location-inspector-header,.location-tote > header { align-items: flex-start; flex-direction: column; }
  .location-content-main { align-items: flex-start; flex-direction: column; }
  .location-admin-panels { grid-template-columns: 1fr; }
}
@media (max-width: 680px) {
  .warehouse-location-layout { display: block; }
  .warehouse-location-tree { max-height: 310px; overflow: auto; border-right: 0; border-bottom: 1px solid var(--border); }
  .location-inspector-header,.location-tote-list,.location-slot-list,.location-tote-filters { padding-left: 14px; padding-right: 14px; }
  .location-slot-contents { padding-left: 24px; }
  .location-slot-contents::before { left: 17px; }
  .location-content-row.is-loose-signs { align-items: flex-start; flex-direction: column; gap: 8px; }
  .location-empty-slots > summary span { align-items: flex-start; flex-direction: column; gap: 3px; }
  .location-capacity-note { margin-left: 14px; margin-right: 14px; align-items: stretch; flex-direction: column; }
}

/* ============================================================
   TOAST
   Replaces alert() in 10 places, and consolidates five competing
   implementations (base.html's .notif-card*, _flash_toast.html's Tailwind
   classes, the React shared/toast.jsx, .pl2-toast, pdf-review/UndoToast).

   Top-right, not bottom: the bottom of a shop-floor tablet screen is where the
   thumbs and the on-screen keyboard are. z-index sits above .drawer (91) but
   below .modal-backdrop (100) — a modal is a conversation and a toast must not
   land on top of it.
   ============================================================ */
.toast-stack {
  position: fixed; top: 16px; right: 16px; z-index: 95;
  display: flex; flex-direction: column; gap: 8px;
  width: min(380px, calc(100vw - 32px));
  /* The stack is a click-through overlay; only the toasts themselves take
     pointer events, so it never blocks the topbar controls underneath. */
  pointer-events: none;
}
.toast-stack > .toast { pointer-events: auto; }
.toast {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 12px 14px; border-radius: 10px;
  background: var(--bg-2); border: 1px solid var(--tone-bd);
  border-left: 3px solid var(--tone-fg);
  box-shadow: var(--shadow-lg); color: var(--text);
  font-size: 13px; line-height: 1.45;
  /* Opaque --bg-2 rather than the tone tint: a toast floats over arbitrary
     page content, and a translucent tint would let that content read through. */
  animation: toastIn var(--dur-3) var(--ease-out);
}
/* Enters from off the right edge — the direction it will leave in, so the
   motion tells you where to look and where it went. */
@keyframes toastIn {
  from { opacity: 0; transform: translateX(12px); }
  to   { opacity: 1; transform: none; }
}
.toast-icon  { flex: 0 0 auto; color: var(--tone-fg); font-weight: 700; line-height: 1.45; }
.toast-body  { flex: 1 1 auto; min-width: 0; }
.toast-title { font-weight: 600; color: var(--tone-fg); margin: 0 0 2px; }
.toast-close {
  flex: 0 0 auto; width: 22px; height: 22px; border-radius: 5px;
  background: none; border: none; cursor: pointer; font-family: inherit;
  color: var(--text-3); display: grid; place-items: center; font-size: 15px;
  line-height: 1; padding: 0;
  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.toast-close:hover { background: var(--bg-3); color: var(--text); }

/* ============================================================
   FORM FIELD
   login.html, change_password.html, create_work_order.html and
   edit_work_order.html each hand-roll this with ~20 rgba literals apiece. The
   input itself is already styled by the element selectors at the top of this
   file, so a field group only owns label / help / error / required.
   ============================================================ */
.field { display: flex; flex-direction: column; gap: 6px; }
.field-label {
  font-size: 12px; font-weight: 500; color: var(--text-2);
  display: inline-flex; align-items: baseline; gap: 3px;
}
/* Required marker. Colour alone cannot carry "required" — the asterisk is the
   signal and the colour is emphasis, which is also why it is a real character
   in the markup and not a ::after. */
.field-req { color: var(--red-text); font-weight: 700; }
.field-help  { font-size: 11px; color: var(--text-3); line-height: 1.4; }
/* Help and error are mutually exclusive in practice; if both render, the error
   is what matters, so it is declared last and callers should hide the help. */
.field-error { font-size: 11px; color: var(--red-text); font-weight: 500; line-height: 1.4; }
/* An invalid field re-colours its own control, so the error text is never the
   only indication of which of eight inputs is wrong. Scoped to :has() on the
   group rather than :invalid on the input, because :invalid fires on an empty
   required field before the user has typed anything. */
.field:has(.field-error) input,
.field:has(.field-error) textarea,
.field:has(.field-error) select { border-color: var(--red); }
/* .field-icon (above) is the input-level wrapper for a leading icon; it nests
   INSIDE .field, it is not an alternative to it. */

/* ============================================================
   ALERT — inline banner
   For flag / hold / exception surfaces that need to explain themselves in
   place. Distinct from .toast (transient, floating) and .scan-fb (one huge
   glanceable line): an alert is part of the document and can hold prose.
   Replaces the one-off #db-demo-banner.
   ============================================================ */
/* Component display rules must never override the platform's hidden contract.
   On a scan station, showing inactive-mode instructions can cause a real
   inventory movement in the wrong workflow. */
[hidden] { display: none !important; }

.alert {
  display: flex; gap: 10px; padding: 12px 14px; border-radius: 10px;
  background: var(--tone-bg); border: 1px solid var(--tone-bd);
  border-left: 3px solid var(--tone-fg);
  font-size: 13px; line-height: 1.5; color: var(--text-2);
}
/* Title takes the tone colour, body text stays --text-2. Tinting a whole
   paragraph in --amber-text is legible but reads as decoration; leading with a
   coloured line and then dropping to normal body copy reads as information. */
.alert-title { font-size: 13px; font-weight: 600; color: var(--tone-fg); margin: 0 0 2px; }
.alert-body  { min-width: 0; flex: 1 1 auto; }
.alert-icon  { flex: 0 0 auto; color: var(--tone-fg); }

/* ============================================================
   AVATAR
   FOUR independent implementations exist (admin_sales_reps.html:278,
   work_order_detail.html:340, _dashboard_tbody.html:11, and .role-avatar).

   The hash palette uses the chip TINT + FOREGROUND pair rather than a solid
   saturated fill with white text, which is what three of the four do. Two
   reasons: (1) the solid fills fail AA — white on the light-theme --amber
   measures ~2.9:1, and the same is true of teal and green — whereas every
   --chip-* pair is verified in both themes; (2) an avatar appears 25 times in a
   dense table and should not be the loudest thing in the row.

   .avatar-1…10 are hash slots, not colour names. Callers do
   `avatar-{{ (name|hash) % 10 + 1 }}`; the slot-to-hue mapping can change
   without touching a template.
   ============================================================ */
.avatar {
  --av-bg: var(--bg-3); --av-fg: var(--text-2);
  width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0;
  display: inline-grid; place-items: center;
  font-size: 12px; font-weight: 700; letter-spacing: 0.02em;
  background: var(--av-bg); color: var(--av-fg);
  /* The inset ring gives the circle a defined edge on a same-tone surface
     without spending a border on it. Its alpha flips with the theme. */
  box-shadow: var(--ring-inset);
  text-transform: uppercase; overflow: hidden; user-select: none;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar--sm { width: 24px; height: 24px; font-size: 10px; }
.avatar--lg { width: 44px; height: 44px; font-size: 16px; }
.avatar-1  { --av-bg: var(--chip-blue-bg);    --av-fg: var(--chip-blue); }
.avatar-2  { --av-bg: var(--chip-violet-bg);  --av-fg: var(--chip-violet); }
.avatar-3  { --av-bg: var(--chip-teal-bg);    --av-fg: var(--chip-teal); }
.avatar-4  { --av-bg: var(--chip-amber-bg);   --av-fg: var(--chip-amber); }
.avatar-5  { --av-bg: var(--chip-rose-bg);    --av-fg: var(--chip-rose); }
.avatar-6  { --av-bg: var(--chip-cyan-bg);    --av-fg: var(--chip-cyan); }
.avatar-7  { --av-bg: var(--chip-green-bg);   --av-fg: var(--chip-green); }
.avatar-8  { --av-bg: var(--chip-orange-bg);  --av-fg: var(--chip-orange); }
.avatar-9  { --av-bg: var(--chip-indigo-bg);  --av-fg: var(--chip-indigo); }
.avatar-10 { --av-bg: var(--chip-pink-bg);    --av-fg: var(--chip-pink); }

/* === Sidebar profile popover === */
.user-bar {
  position: relative; display: flex; align-items: center; gap: 6px;
  padding: 8px 10px; border-top: 1px solid var(--border);
}
.profile-trigger {
  display: flex; min-width: 0; flex: 1; align-items: center; gap: 9px;
  padding: 5px 6px; border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--text); cursor: pointer; text-align: left;
  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.profile-trigger:hover, .profile-trigger:focus-visible { background: var(--sidebar-item-hover); }
.profile-trigger-copy { display: flex; min-width: 0; flex: 1; flex-direction: column; gap: 1px; }
.profile-trigger-name, .profile-trigger-role { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.profile-trigger-name { color: var(--text); font-size: 12px; font-weight: 600; }
.profile-trigger-role { color: var(--text-3); font-size: 10px; }
.profile-avatar-preview { position: relative; }
.profile-avatar-preview > span { line-height: 1; }
.profile-avatar-image {
  position: absolute; inset: 0; width: 100%; height: 100%;
  border-radius: inherit; object-fit: cover;
}
.role-avatar.profile-avatar-preview { overflow: hidden; }
.profile-popover {
  position: absolute; bottom: calc(100% + 8px); left: 6px; z-index: 110;
  width: 220px; overflow: hidden;
  border: 1px solid var(--border-2); border-radius: var(--radius-lg);
  background: var(--bg-1); box-shadow: var(--shadow-lg);
}
.profile-popover-header {
  display: flex; align-items: center; gap: 11px; padding: 15px;
  border-bottom: 1px solid var(--border); background: var(--bg-2);
}
.profile-popover-identity { display: flex; min-width: 0; flex: 1; flex-direction: column; gap: 2px; }
.profile-popover-identity strong, .profile-popover-identity span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.profile-popover-identity strong { color: var(--text); font-size: 13px; }
.profile-popover-identity span { color: var(--text-3); font-size: 11px; }
.profile-popover-close {
  display: grid; width: 28px; height: 28px; flex-shrink: 0; place-items: center;
  border: 0; border-radius: var(--radius-sm); background: transparent;
  color: var(--text-3); cursor: pointer; font: 20px/1 inherit;
}
.profile-popover-close:hover { background: var(--bg-3); color: var(--text); }
.profile-tabs { display: flex; gap: 4px; padding: 8px 10px 0; }
.profile-tabs button {
  flex: 1; padding: 7px 10px; border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--text-3); cursor: pointer; font: 600 11px/1.2 inherit;
}
.profile-tabs button:hover { color: var(--text); }
.profile-tabs button.active { background: var(--bg-3); color: var(--text); }
.profile-popover-body { max-height: min(440px, calc(100vh - 150px)); overflow-y: auto; padding: 14px; }
.profile-form { display: grid; gap: 13px; }
.profile-file-input {
  position: absolute !important; width: 1px !important; height: 1px !important;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap;
}
.profile-file-picker {
  display: flex; min-height: 42px; align-items: center; justify-content: center; gap: 7px;
  padding: 9px 11px; border: 1px dashed var(--border-2); border-radius: var(--radius-sm);
  background: var(--bg-2); color: var(--text-2); cursor: pointer; text-align: center;
  transition: border-color var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
}
.profile-file-picker:hover { border-color: var(--brand); background: var(--brand-bg); }
.profile-file-input:focus-visible + .profile-file-picker { box-shadow: var(--focus-ring); }
.profile-remove-form { margin-top: 9px; text-align: center; }
#sidebar.collapsed .profile-trigger { justify-content: center; padding-inline: 4px; }
@media (prefers-reduced-motion: reduce) {
  .profile-trigger, .profile-file-picker { transition: none; }
}

/* ============================================================
   TOOLTIP
   CSS-only and attribute-driven: `<span class="tt" data-tt="Why this is held">`.
   No JS, because the surfaces that need it most (table cells, scan strips) are
   HTMX-swapped and a JS tooltip has to re-bind after every swap.

   This is the first consumer of --tooltip-bg/-text/-border, which have been
   orphaned tokens since they were added. They are deliberately always-dark in
   both themes — that is the near-universal tooltip convention, and it keeps the
   pill readable when it overhangs an arbitrary surface.
   ============================================================ */
.tt { position: relative; }
.tt[data-tt]::after {
  content: attr(data-tt);
  position: absolute; bottom: calc(100% + 7px); left: 50%;
  transform: translateX(-50%);
  background: var(--tooltip-bg); color: var(--tooltip-text);
  border: 1px solid var(--tooltip-border);
  padding: 5px 9px; border-radius: 6px;
  font-size: 11px; font-weight: 500; line-height: 1.4;
  white-space: normal; width: max-content; max-width: 240px;
  text-align: left; text-transform: none; letter-spacing: normal;
  box-shadow: var(--shadow-lg);
  /* Hidden via opacity, not display, so it can transition — and
     pointer-events:none so the tooltip can never eat the hover that
     summoned it, which is the classic flicker loop. */
  opacity: 0; pointer-events: none; z-index: 110;
  transition: opacity var(--dur-1) var(--ease);
}
/* :focus-visible as well as :hover — a tooltip that only exists on hover is
   invisible to keyboard and touch users. */
.tt[data-tt]:hover::after,
.tt[data-tt]:focus-visible::after { opacity: 1; }
.tt--right[data-tt]::after { bottom: auto; top: 50%; left: calc(100% + 7px);
  transform: translateY(-50%); }
.tt--below[data-tt]::after { bottom: auto; top: calc(100% + 7px); }

/* ============================================================
   TABLE EXTENSIONS
   Tables are this app's primary comparison surface and .tbl was four bare
   rules. These add the three things every screen re-invented: a sort
   affordance, a header that survives scrolling, and row selection.
   ============================================================ */

/* Sortable headers. The state comes from [aria-sort] — the same attribute a
   screen reader announces — so there is exactly one source of truth and it is
   the accessible one. The unsorted arrow is present but faint: a hidden
   affordance is not an affordance. */
.tbl-sortable th[aria-sort] { cursor: pointer; user-select: none; white-space: nowrap;
  transition: color var(--dur-1) var(--ease); }
.tbl-sortable th[aria-sort]:hover { color: var(--text); }
.tbl-sortable th[aria-sort]::after {
  content: "↕"; margin-left: 5px; font-size: 10px; opacity: 0.35;
  display: inline-block;
}
.tbl-sortable th[aria-sort="ascending"]::after  { content: "↑"; opacity: 1; }
.tbl-sortable th[aria-sort="descending"]::after { content: "↓"; opacity: 1; }
.tbl-sortable th[aria-sort="ascending"],
.tbl-sortable th[aria-sort="descending"] { color: var(--text); }

/* Sticky header. The base th rule already sets position:sticky, which does
   nothing on its own — without a stacking context and an opaque fill the rows
   scroll straight over it. --bg-2 is the "raised" step in the surface ramp,
   which is exactly what a floating header is. */
.tbl-sticky th {
  position: sticky; top: 0; z-index: 2;
  background: var(--bg-2);
  box-shadow: 0 1px 0 var(--border);
}

/* Row selection. The selected state is a fill PLUS a left rule: on a long list
   the rule is what lets you count selections at a glance, and it survives the
   row-hover fill landing on top of it. */
.tbl-selectable tbody tr { cursor: pointer;
  transition: background var(--dur-1) var(--ease); }
.tbl-selectable tbody tr:hover td { background: var(--bg-3); }
.tbl-selectable td:first-child, .tbl-selectable th:first-child { width: 1%; white-space: nowrap; }
.tbl-selectable input[type="checkbox"] { width: auto; cursor: pointer; }
.tbl-row-selected td { background: var(--bg-4); }
.tbl-row-selected td:first-child { box-shadow: inset 3px 0 0 var(--accent); }

/* Bulk-action bar. Fixed and centred rather than pushed into the page flow:
   appearing in flow would reflow the table under the cursor mid-selection.
   Hidden by default and revealed with .is-open by whatever owns the selection
   count, so no layout thrash and no JS-injected inline styles. */
.tbl-bulkbar {
  position: fixed; bottom: 20px; left: 50%; transform: translate(-50%, 8px);
  z-index: 96;
  display: none; align-items: center; gap: 12px;
  padding: 10px 14px; border-radius: 12px;
  background: var(--bg-2); border: 1px solid var(--border-2);
  box-shadow: var(--shadow-lg); font-size: 13px; color: var(--text);
}
.tbl-bulkbar.is-open { display: flex; animation: bulkbarIn var(--dur-3) var(--ease-out); transform: translate(-50%, 0); }
@keyframes bulkbarIn {
  from { opacity: 0; transform: translate(-50%, 8px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}
.tbl-bulkbar-count { font-weight: 700; font-variant-numeric: tabular-nums; }
.tbl-bulkbar-sep { width: 1px; align-self: stretch; background: var(--border); }

/* ============================================================
   COMMAND PALETTE — shell only
   Style layer for the global palette; the JS lives elsewhere. Keyboard-first,
   so every state a key can put the list in has a rule: .cmdk-item-active is
   driven by arrow keys, NOT by :hover, because on a keyboard-driven list the
   mouse position must not silently move the Enter target.

   640px and top-anchored at 12vh rather than vertically centred: the list grows
   downward as you type, and a centred panel would slide the input under your
   eyes while you are still typing into it.
   ============================================================ */
.cmdk-backdrop {
  position: fixed; inset: 0; z-index: 120;
  background: var(--scrim); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 12vh 16px 16px;
  animation: fadeIn var(--dur-2) var(--ease);
}
.cmdk-panel {
  width: 100%; max-width: 640px; max-height: 70vh;
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--bg-2); border: 1px solid var(--border-2);
  border-radius: 14px; box-shadow: var(--shadow-lg);
  animation: popIn var(--dur-3) var(--ease-out);
}
/* Deliberately borderless and 16px: it is the only input on screen, so a box
   around it is redundant chrome, and 16px stops iOS Safari zooming the page on
   focus. The `.ds-input`/element selectors above are overridden on purpose. */
/* Written as `input.cmdk-input`, not `.cmdk-input`: the bare class loses to the
   `input[type="text"]` element selector at the top of this file, which would
   put the app's 13px bordered input treatment back. */
input.cmdk-input {
  width: 100%; border: none; border-radius: 0; outline: none;
  background: transparent; color: var(--text);
  padding: 16px 18px; font-size: 16px; font-family: inherit;
  border-bottom: 1px solid var(--border);
}
input.cmdk-input:focus { border-color: var(--border); box-shadow: none; }
input.cmdk-input::placeholder { color: var(--text-4); }
.cmdk-list { overflow-y: auto; padding: 6px; flex: 1 1 auto; min-height: 0; }
.cmdk-group {
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.09em; color: var(--text-4); padding: 10px 10px 4px;
}
.cmdk-item {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 10px; border-radius: 8px;
  font-size: 13px; color: var(--text-2); cursor: pointer;
  background: none; border: none; width: 100%; text-align: left;
  font-family: inherit; line-height: 1.3;
}
.cmdk-item svg { width: 16px; height: 16px; flex-shrink: 0; opacity: 0.7; }
.cmdk-item-sub { margin-left: auto; font-size: 11px; color: var(--text-4); flex-shrink: 0; }
/* One rule for both the keyboard cursor and hover, so the two cannot drift out
   of sync and show the user two "selected" rows at once. */
.cmdk-item-active, .cmdk-item:hover {
  background: var(--bg-3); color: var(--text);
  box-shadow: inset 2px 0 0 var(--accent);
}
.cmdk-item-active svg { opacity: 1; }
.cmdk-empty { padding: 32px 16px; text-align: center; color: var(--text-3); font-size: 13px; }
.cmdk-hint {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 9px 14px; border-top: 1px solid var(--border);
  background: var(--bg); color: var(--text-4); font-size: 11px;
}

/* ============================================================
   SPINNER
   For genuinely indeterminate waits only. Where the shape of the incoming
   content is known, use a .skel-* skeleton instead — it says "this much is
   coming", a spinner only says "wait".
   ============================================================ */
.spinner {
  display: inline-block; width: 16px; height: 16px; flex-shrink: 0;
  border: 2px solid var(--border-2);
  /* One side takes the foreground: that asymmetry is what makes the rotation
     readable. currentColor, so a spinner inside a .btn-primary or a toast
     inherits the right contrast instead of naming a colour. */
  border-top-color: currentColor;
  border-radius: 50%;
  animation: spin .7s linear infinite;
}
.spinner--sm { width: 12px; height: 12px; border-width: 2px; }
.spinner--lg { width: 28px; height: 28px; border-width: 3px; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ============================================================
   KBD
   Keyboard shortcut key cap — needed by the command palette hint row and the
   `?` shortcut sheet. Sized to sit inline in 11–13px body copy without
   changing the line box.
   ============================================================ */
.kbd {
  display: inline-block; min-width: 18px; padding: 1px 5px;
  border-radius: 4px; background: var(--bg-3);
  border: 1px solid var(--border-2); border-bottom-width: 2px;
  color: var(--text-2); font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 10px; font-weight: 600; line-height: 16px; text-align: center;
  white-space: nowrap; vertical-align: baseline;
}

/* One recognizable shape for empty, filtered, all-done, load failure,
   permission, offline, loading, and stale-data states. */
.state {
  --state-fg: var(--text-3);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 48px 20px;
  color: var(--text-3);
  text-align: center;
}
.state-icon { color: var(--state-fg); font-size: 28px; line-height: 1; }
.state-title { margin: 0; color: var(--text-2); font-size: 14px; font-weight: 600; }
.state-msg { max-width: 46ch; margin: 0; color: var(--text-3); font-size: 13px; line-height: 1.5; }
.state-action { margin-top: 10px; }
.state--empty { --state-fg: var(--text-4); }
.state--loading { --state-fg: var(--brand-text); }
.state--filtered, .state--degraded { --state-fg: var(--eb-attention); }
.state--error { --state-fg: var(--eb-alert); }
.state--all-done {
  --state-fg: var(--eb-ok);
  border: 1px solid var(--green-border);
  border-radius: var(--radius-md);
  background: var(--green-bg);
}
.state--offline {
  --state-fg: var(--brand-text);
  border: 1px solid var(--border-brand);
  border-radius: var(--radius-md);
  background: var(--brand-bg);
}
.state-ref,
.state-queue {
  color: var(--text-4);
  font: 500 11px/1.3 var(--eb-mono);
  font-feature-settings: "tnum";
}
.state--degraded {
  flex-direction: row;
  justify-content: flex-start;
  padding: 8px 12px;
  border: 1px solid var(--amber-border);
  border-left: 3px solid var(--eb-attention);
  border-radius: var(--radius-md);
  background: var(--amber-bg);
  text-align: left;
}

/* Floor density is a sizing contract, not a page-specific restyle. */
[data-density="floor"] .btn,
[data-density="floor"] .btn-sm,
[data-density="floor"] .btn-xs,
[data-density="floor"] .icon-btn,
[data-density="floor"] .btn-icon,
[data-density="floor"] .tab,
[data-density="floor"] .segmented button,
[data-density="floor"] .segmented a,
[data-density="floor"] input,
[data-density="floor"] select,
[data-density="floor"] textarea,
[data-density="floor"] .ds-input {
  min-height: var(--eb-target);
  font-size: var(--eb-text);
}
[data-density="floor"] .btn-sm,
[data-density="floor"] .btn-xs {
  padding: 0 14px;
  border-radius: var(--radius-md);
}
[data-density="floor"] .icon-btn,
[data-density="floor"] .btn-icon {
  width: var(--eb-target);
  height: var(--eb-target);
}

/* ============================================================
   REDUCED MOTION
   Required, not optional: several people on a shop floor will have vestibular
   sensitivity, and every animation above is decorative. Honour the OS setting
   by collapsing durations rather than disabling rules, so nothing that relies
   on an animation's end state gets stuck mid-transition.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .card-hover:hover { transform: none; }
  /* The blanket rule above collapses every animation to .01ms, which is right
     for entrances (they land instantly at their end state) but wrong for the
     one animation whose whole message IS the motion: a frozen spinner is
     indistinguishable from a hung page. Keep it turning, slower. Rotation of a
     16px symmetric ring carries no vestibular risk — the guidance targets
     large-area movement and parallax, not a loading glyph. */
  .spinner {
    animation-duration: 1.4s !important;
    animation-iteration-count: infinite !important;
  }
  /* Entrances that translate: collapse the movement, keep the element. Without
     this they still snap into place, but from the offset position for one
     frame, which reads as a glitch on a slow tablet. */
  .toast, .tbl-bulkbar.is-open, .scan-fb { animation-name: fadeIn !important; }
}
