/* v2.51.0 — Legacy :root block deleted. Replaced by legacy-shim.css
   which aliases the same names (`--bg`, `--text`, …) to the new
   `--pw-*` semantic tokens, giving dark-mode parity for free.
   The body rule below still references `var(--bg)` / `var(--text)`
   — those resolve through the shim to `var(--pw-bg)` /
   `var(--pw-text)` automatically. */
* { box-sizing: border-box; }
body { margin: 0; font: 14px/1.45 var(--pw-font-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif); background: var(--pw-bg); color: var(--pw-text); }
/* v2.49.0 — `.mono` + `.muted` retired in favor of `.pw-mono` +
 * `.pw-muted` (components.css). Zero templates reference the
 * legacy names anymore (verified by audit grep before delete).
 * The standalone `.small` class is kept because it's an
 * orthogonal utility (font-size only, no color), heavily used
 * across templates, and has no Phase 5 replacement yet.
 * v2.50.16 — bumped 12 → 13 px so the muted-metadata text
 * (timestamps, store names, hints) reads cleanly on phones
 * without triggering iOS auto-zoom (which kicks in <16px in
 * <input> contexts only, so 13px is safe for non-input text). */
.small { font-size: 13px; line-height: 1.4; }
.good { color: var(--good); }
.warn { color: var(--warn); }
.bad  { color: var(--bad); }
.break { word-break: break-all; }
/* v2.51.8 — The admin top bar is INTENTIONALLY always-dark (acts
   like a kiosk-style chrome bar regardless of which theme the body
   is on — matches the operator station + QA agent banner pattern).
   Colors stay literal here so the bar reads consistently in both
   light + dark mode. nav link contrast bumped from #cdd3dc (~5:1
   on #1f2328) to #e8eaef (~9:1) so it reads cleanly even at
   distance from a 4 k screen. */
/* v2.51.46 — Gap B1 fix. Scope the admin nav chrome rule so it
   doesn't cascade into editorial QA / operator / TV / brand /
   styleguide templates that have their own <header> (qa_index
   .pw-qi-head, qa_order .pw-qad-head, etc.). The intent of this
   rule was always "the admin app shell's top bar"; the
   unscoped form painted a dark band on every QA route's
   editorial header in light mode (audit screenshots 1, 29, 57).
   `header > nav` targets the admin shell pattern (base.html:24-25
   = <header><nav>...) without matching editorial headers whose
   children are <h1>/<div>, never <nav>. */
header:has(> nav) { background: #1f2328; color: #f7f7f8; }
nav { display: flex; align-items: center; gap: 1.5em; padding: 0 1.5em; height: 48px; }
nav a { color: #e8eaef; text-decoration: none; }
nav a:hover { color: #fff; }
nav a.active { color: #fff; border-bottom: 2px solid var(--accent); padding-bottom: 4px; }
nav .brand { font-weight: 600; color: #fff; }
nav .spacer { flex: 1; }
main { padding: 1.5em 2em; max-width: 1400px; margin: 0 auto; }
h1, h2 { margin-top: 1.2em; }
h1 { font-size: 22px; }
h2 { font-size: 16px; color: var(--muted); margin-bottom: .5em; text-transform: uppercase; letter-spacing: .04em; }
table { width: 100%; border-collapse: collapse; background: var(--surface); border: 1px solid var(--border); border-radius: 6px; overflow: hidden; }
table.jobs td.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
/* v2.51.8 — Table header + hover row use semantic surface tokens
   so they adapt to dark mode. The old #f0f2f5 (very light grey)
   was nearly invisible on the dark-mode paper-900 surface and
   #fafbfc rows didn't lift visibly on dark either. */
thead { background: var(--pw-surface-2); color: var(--pw-text); }
th, td { text-align: left; padding: .55em .8em; border-bottom: 1px solid var(--border); }
tbody tr:hover { background: var(--pw-surface-2); }
tr.row-failed td:first-child { border-left: 3px solid var(--bad); }
tr.row-running td:first-child { border-left: 3px solid var(--accent); }
tr.row-queued td:first-child { border-left: 3px solid var(--warn); }
tr.row-succeeded td:first-child { border-left: 3px solid var(--good); }
tr.row-skipped td:first-child { border-left: 3px solid var(--muted); }
.status { font-weight: 600; }
.status-succeeded { color: var(--good); }
.status-failed, .status-dead { color: var(--bad); }
.status-running { color: var(--accent); }
.status-queued { color: var(--warn); }
.status-skipped { color: var(--muted); }
.status-placed { color: var(--good); }
.kpi-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1em; margin-top: 1em; }
.kpi { background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: 1em 1.2em; }
.kpi-label { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .05em; }
.kpi-value { font-size: 28px; font-weight: 600; margin-top: .15em; }
.meta-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: .4em 1em; background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: 1em 1.2em; margin: 1em 0; }
.meta-grid .label { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .03em; margin-right: .4em; }
.actions { margin: 1em 0; display: flex; gap: .5em; }
/* v2.49.0 — .btn + .btn-primary retired (zero templates left
 * referencing them; components.css's .pw-btn variants cover
 * every action). */
/* v2.51.8 — Error / log / payload containers all theme-aware now.
   - .error-block: subtle danger tint instead of hardcoded #fef5f5
   - .error-pre: surface token instead of hardcoded #fff
   - .manual-urls: warn tint instead of hardcoded #fff8e6
   - .logs: surface-3 + text-muted (was unreadable on dark)
   - .payload: surface instead of hardcoded #fff */
.error-block { background: color-mix(in srgb, var(--pw-danger) 8%, var(--pw-surface)); border: 1px solid var(--pw-danger); border-radius: 6px; padding: .8em 1em; margin: 1em 0; color: var(--pw-text); }
.error-pre { background: var(--pw-surface); color: var(--pw-text); border: 1px solid var(--border); border-radius: 4px; padding: .6em; white-space: pre-wrap; word-break: break-word; font-family: ui-monospace, Menlo, monospace; font-size: 12px; max-height: 200px; overflow: auto; }
.manual-urls { background: color-mix(in srgb, var(--pw-warning) 10%, var(--pw-surface)); border: 1px solid var(--pw-warning); border-radius: 4px; padding: .6em .8em; margin-bottom: .6em; color: var(--pw-text); }
.manual-urls a { color: var(--accent); }
.logs { background: var(--pw-surface-3); color: var(--pw-text); border-radius: 6px; padding: .8em 1em; font-family: ui-monospace, Menlo, monospace; font-size: 12px; max-height: 320px; overflow: auto; white-space: pre-wrap; }
.payload { background: var(--pw-surface); color: var(--pw-text); border: 1px solid var(--border); border-radius: 4px; padding: .6em; font-size: 12px; max-height: 360px; overflow: auto; }
details summary { cursor: pointer; padding: .3em 0; color: var(--accent); }
.filters { display: flex; gap: .8em; align-items: end; background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: .8em 1em; margin: 1em 0; }
.filters label { display: flex; flex-direction: column; font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .03em; gap: .2em; }
.filters input, .filters select { padding: .45em .6em; border: 1px solid var(--border); border-radius: 4px; font-size: 14px; font-family: inherit; min-width: 140px; }
.filters button { padding: .55em 1.2em; }
.pagination { margin: 1em 0; display: flex; gap: 1em; align-items: center; }
/* v2.49.0 — .flash retired in favor of .pw-flash + variants. */
.store-form { display: grid; grid-template-columns: 1fr 1fr; gap: .6em 1em; background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: 1.2em 1.4em; max-width: 920px; }
.store-form label { display: flex; flex-direction: column; gap: .25em; }
.store-form label.wide { grid-column: 1 / span 2; }
.store-form label.checkbox { flex-direction: row; align-items: center; gap: .5em; }
.store-form span { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .03em; }
.store-form input, .store-form select {
    padding: .5em .7em; border: 1px solid var(--border);
    border-radius: 4px; font-size: 14px; font-family: inherit;
    /* v2.51.21 — explicit theme-aware bg + color so the input
       doesn't render as white-on-white in dark mode. */
    background: var(--pw-surface); color: var(--pw-text);
}
.store-form input.mono { font-family: ui-monospace, Menlo, monospace; font-size: 12px; }
.store-form .actions { grid-column: 1 / span 2; }
td.num { text-align: right; font-variant-numeric: tabular-nums; }

/* Verify banner */
.verify-banner { padding: .55em 1em; border-radius: 6px; margin: 1em 0; border: 1px solid; display: flex; align-items: center; gap: .5em; flex-wrap: wrap; }
/* v2.51.8 — Verify banner tints now theme-aware via color-mix
   (mixes semantic state token with current surface). Works
   correctly in both light + dark mode without per-mode rules. */
.verify-banner.verify-passed { background: color-mix(in srgb, var(--pw-success) 12%, var(--pw-surface)); border-color: var(--pw-success); color: var(--pw-success); }
.verify-banner.verify-failed { background: color-mix(in srgb, var(--pw-danger) 12%, var(--pw-surface)); border-color: var(--pw-danger); color: var(--pw-danger); }
.verify-banner.verify-unknown { background: color-mix(in srgb, var(--pw-warning) 12%, var(--pw-surface)); border-color: var(--pw-warning); color: var(--pw-warning); }
.verify-step { display: inline-block; padding: .15em .5em; border-radius: 3px; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .03em; }
.verify-step.verify-pass { background: color-mix(in srgb, var(--pw-success) 20%, transparent); color: var(--pw-success); }
.verify-step.verify-fail { background: color-mix(in srgb, var(--pw-danger) 20%, transparent); color: var(--pw-danger); }
.verify-banner details { width: 100%; margin-top: .5em; }


/* External link indicator (e.g. to OrderDesk) */
a.ext-link { color: var(--accent); text-decoration: none; }
a.ext-link::after { content: " ↗"; font-size: 11px; color: var(--muted); }
a.ext-link:hover { text-decoration: underline; }

/* 7-day bar chart */
.bar-chart {
    display: grid; grid-template-columns: repeat(7, 1fr); gap: .5em;
    background: var(--surface); border: 1px solid var(--border); border-radius: 6px;
    padding: 1em; margin: 1em 0; align-items: end; min-height: 140px;
}
.bar-wrap { display: flex; flex-direction: column; align-items: center; gap: .3em; }
.bar-stack { height: 100px; width: 100%; display: flex; align-items: flex-end; justify-content: center; }
.bar { width: 70%; min-height: 2px; border-radius: 3px 3px 0 0; position: relative; }
/* v2.51.8 — bar-fill + bar-empty theme-aware. */
.bar-fill { background: linear-gradient(180deg, var(--pw-primary), var(--pw-primary-hover)); }
.bar-empty { background: var(--pw-border); height: 4px; }
.bar-count { position: absolute; top: -18px; left: 50%; transform: translateX(-50%); font-size: 11px; color: var(--muted); }
.bar-label { font-size: 11px; color: var(--muted); font-family: ui-monospace, Menlo, monospace; }

/* Hour-of-day heatmap */
.heatmap {
    background: var(--surface); border: 1px solid var(--border); border-radius: 6px;
    padding: .8em; overflow-x: auto;
}
.heatmap-row {
    display: grid;
    grid-template-columns: 36px repeat(24, 1fr);
    gap: 2px;
    min-width: 700px;
    margin-bottom: 2px;
}
.heatmap-header { margin-bottom: 4px; }
.heatmap-day-label { font-size: 11px; color: var(--muted); font-family: ui-monospace, Menlo, monospace; display: flex; align-items: center; padding-right: 4px; }
.heatmap-hour-label { font-size: 9px; color: var(--muted); text-align: center; }
.heatmap-hour-major { color: var(--text); font-weight: 600; }
.heatmap-cell {
    aspect-ratio: 1.6 / 1;
    min-height: 22px;
    background: rgba(43, 108, 176, calc(var(--i, 0) / 100));
    border: 1px solid rgba(43, 108, 176, .12);
    border-radius: 3px;
    font-size: 12px; font-family: ui-monospace, Menlo, monospace;
    color: var(--text);
    display: flex; align-items: center; justify-content: center;
    transition: transform .1s;
}
.heatmap-cell:hover { transform: scale(1.18); z-index: 2; position: relative; }

/* Low-stock pill in the admin nav. v2.51.8 — semantic tokens. */
.low-stock-pill {
    background: var(--pw-warning); color: var(--pw-warning-on); padding: .25em .7em; border-radius: 999px;
    font-size: 12px; font-weight: 600; text-decoration: none; margin-right: .8em;
}
.low-stock-pill:hover { background: var(--pw-warning); filter: brightness(.9); color: var(--pw-warning-on); }

/* Persistent refresh picker in the nav */
.refresh-picker {
    display: inline-flex; align-items: center; gap: .3em;
    background: rgba(255,255,255,.05); padding: .15em .55em .15em .5em;
    border-radius: 4px; color: #cdd3dc; font-size: 12px; margin-right: 1em;
}
.refresh-picker .refresh-icon { font-size: 14px; line-height: 1; }
.refresh-picker select {
    background: transparent; color: #f7f7f8; border: 0; font: inherit; padding: 0; cursor: pointer;
}
.refresh-picker select option { background: #1f2328; color: #f7f7f8; }
.refresh-countdown { color: #c4cad4 !important; min-width: 38px; text-align: right; }

/* v2.50.15 + v2.51.0 — Defensive global dropdown rule, now
   theme-aware. iOS Safari + some Android browsers IGNORE per-
   option background + color rules in custom <select> contexts
   and render with OS-native colors. v2.50.15 forced contrast
   universally; v2.51.0 honors the explicit theme toggle
   (`:root[data-theme="dark"]`) AND the auto-mode media query
   so the option list inverts correctly when the user picks
   dark or their OS is dark + theme=auto. !important wins
   against wrapper-select color inheritance regardless of
   theme. */
select option,
select optgroup {
    background-color: #ffffff !important;
    color: #1f2328 !important;
}
:root[data-theme="dark"] select option,
:root[data-theme="dark"] select optgroup {
    background-color: #1f2328 !important;
    color: #f7f7f8 !important;
}
@media (prefers-color-scheme: dark) {
    :root[data-theme="auto"] select option,
    :root[data-theme="auto"] select optgroup {
        background-color: #1f2328 !important;
        color: #f7f7f8 !important;
    }
}
.refresh-picker-op { position: absolute; right: 4em; top: 1em; }

/* Inventory table row states */
tr.row-low td:first-child { border-left: 3px solid var(--warn); }
tr.row-disabled td { color: var(--muted); }
.restock-form { display: inline-flex; align-items: center; gap: .3em; }
.restock-form input { width: 70px; padding: .3em .4em; font-size: 13px; border: 1px solid var(--border); border-radius: 4px; }
.edit-pop summary { display: inline-block; cursor: pointer; list-style: none; }
.edit-pop summary::-webkit-details-marker { display: none; }
.edit-form { display: flex; flex-direction: column; gap: .4em; background: var(--surface); border: 1px solid var(--border); padding: .8em; border-radius: 6px; margin-top: .5em; min-width: 240px; }
.edit-form label { display: flex; justify-content: space-between; align-items: center; gap: .5em; font-size: 12px; }
.edit-form label input { padding: .3em .4em; font-size: 13px; border: 1px solid var(--border); border-radius: 4px; }
.event-restocked { color: var(--good); font-weight: 600; }
.event-consumed { color: var(--accent); font-weight: 600; }
.event-adjusted { color: var(--warn); font-weight: 600; }
.low-item { color: var(--warn); }

/* v2.49.0 — .flash-* retired in favor of .pw-flash + .pw-flash--{success,error,info}. */

/* Operator station */
/* v2.64.0 (PRI-75) — padding-bottom covers the iPhone home indicator so
   bottom-of-page buttons aren't hidden behind the system gesture bar.
   env(safe-area-inset-bottom) is 0px on non-notch devices and ~34px on
   iPhone X+. viewport-fit=cover in _base.html activates this value. */
.operator-body { background: #1f2328; color: #f7f7f8; min-height: 100vh; margin: 0; padding-bottom: env(safe-area-inset-bottom, 0px); }

/* v2.51.26 — The operator station is INTENTIONALLY kiosk-dark
   regardless of the user's page theme (it's mounted on wall
   monitors + tablets in the shop and needs to read from across
   the room). But the .pw-op-* / .pw-page-* / .pw-flash etc.
   editorial chrome that lives inside it pulls colors from the
   --pw-* tokens, which in LIGHT theme resolve to DARK text — so
   in light mode we got dark-on-dark and the whole station page
   was unreadable.

   Fix: redefine the --pw-* tokens INSIDE .operator-body to their
   dark-mode values. The body itself stays dark; every nested
   element using --pw-text / --pw-text-muted / --pw-surface etc.
   now gets the light-on-dark variants regardless of which page
   theme the user picked. */
.operator-body {
  --pw-bg:            #1f2328;
  --pw-surface:       #2a2d35;
  --pw-surface-2:     #34373f;
  --pw-surface-3:     #3e414a;
  --pw-text:          #f7f7f8;
  --pw-text-muted:    #cdd3dc;
  --pw-text-faint:    #a8b0bc;
  --pw-border:        rgba(255, 255, 255, 0.16);
  --pw-border-strong: rgba(255, 255, 255, 0.30);
  /* Semantic states: use the lighter -300 variants so they're
     readable on the dark kiosk bg. */
  --pw-primary:       #6088c8;
  --pw-primary-on:    #ffffff;
  --pw-primary-soft:  rgba(96, 136, 200, 0.18);
  --pw-success:       #3F9560;
  --pw-success-on:    #0E1014;
  --pw-warning:       #D4A833;
  --pw-warning-on:    #0E1014;
  --pw-danger:        #D14F40;
  --pw-danger-on:     #0E1014;
  /* Legacy aliases used by older templates via legacy-shim.css. */
  --bg:               #1f2328;
  --surface:          #2a2d35;
  --text:             #f7f7f8;
  --muted:            #cdd3dc;
  --border:           rgba(255, 255, 255, 0.16);
  --good:             #3F9560;
  --warn:             #D4A833;
  --bad:              #D14F40;
}
.operator-login-main { max-width: 480px; margin: 4em auto; padding: 2em; background: var(--surface); color: var(--text); border-radius: 8px; }
.operator-login-form { display: flex; flex-direction: column; gap: 1em; margin: 1.5em 0; }
.operator-login-form label { display: flex; flex-direction: column; gap: .3em; }
.operator-login-form input { padding: .8em; font-size: 16px; border: 1px solid var(--border); border-radius: 4px; }
/* v2.49.0 — .btn-big retired in favor of .pw-btn--lg. */

.operator-header {
    /* v2.39.1 — Two-row header. Top row = printer name (left) +
       refresh + sign-out (right). Second row = full-width team
       picker so the name buttons stretch across the top of the
       station instead of being squeezed into a narrow flex column.
       flex-wrap lets `.member-picker` (with width: 100% below)
       break onto its own row. */
    display: flex;
    flex-wrap: wrap;
    row-gap: .75em;
    justify-content: space-between;
    align-items: center;
    padding: 1em 1.5em;
    background: #2b3036;
    border-bottom: 1px solid #3a4048;
}
.operator-printer { font-size: 24px; font-weight: 600; }
.operator-user { color: #c4cad4 !important; }

.operator-grid {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 1em; padding: 1.5em; max-width: 1200px; margin: 0 auto;
}
.op-button-form { margin: 0; }
.op-button {
    width: 100%; padding: 1.5em 1em; font-size: 18px; font-family: inherit;
    background: linear-gradient(180deg, #3a4048, #2b3036);
    color: #f7f7f8; border: 1px solid #4a5058; border-radius: 8px;
    cursor: pointer; display: flex; flex-direction: column; gap: .4em;
    transition: transform .1s, background .15s;
}
.op-button:hover, .op-button:active { background: linear-gradient(180deg, #4a5058, #3a4048); transform: translateY(-1px); }
.op-button:active { transform: translateY(1px); }
.op-button-title { font-weight: 600; }
.op-button-sub { font-size: 13px; color: #c4cad4 !important; }
.op-button.category-powder { border-color: #b7791f; }
.op-button.category-ink    { border-color: #2b6cb0; }
.op-button.category-film   { border-color: #2f855a; }

/* v2.51.8 — operator-footer was #6b7280 which fell below 1.5:1
   on the dark operator background — nearly invisible. Bumped to
   text-faint token which is dark-mode-aware. */
.operator-footer { text-align: center; padding: 2em 1em; color: var(--pw-text-faint) !important; }
.op-printer-list { list-style: none; padding: 0; display: flex; flex-direction: column; gap: .5em; }
.op-category { display: inline-block; padding: .15em .5em; font-size: 12px; background: #4a5058; border-radius: 3px; margin-left: .4em; }

/* Team member picker (station header) */
.operator-header { position: relative; border-top: 4px solid var(--member-color, transparent); transition: border-top-color .15s; }
.member-picker {
    /* v2.39.1 — Full-width second row. `order: 2` puts the picker
       below the printer name / sign-out row even though it's the
       second HTML child. `flex-basis: 100%` claims the whole next
       line in the wrapping flex header. */
    display: flex;
    align-items: center;
    gap: .6em;
    padding: .35em .6em;
    background: rgba(255, 255, 255, .03);
    border-radius: 6px;
    margin: 0;
    width: 100%;
    flex-basis: 100%;
    order: 2;
}
.member-active {
    display: inline-flex; align-items: center; gap: .4em;
    font-size: 20px; font-weight: 600; color: #f7f7f8;
}
.member-active .member-dot {
    display: inline-block; width: 12px; height: 12px; border-radius: 50%;
    background: var(--member-color, #6b7280);
    box-shadow: 0 0 0 2px rgba(255,255,255,.15);
}
/* v2.51.8 — was hardcoded #f0c674; semantic warning token. */
.member-prompt { font-size: 16px; font-weight: 600; color: var(--pw-warning); }
.member-select {
    background: rgba(255,255,255,.07); color: #f7f7f8; border: 1px solid rgba(255,255,255,.15);
    border-radius: 4px; padding: .35em .55em; font-size: 14px;
}
.member-select option { background: #1f2328; color: #f7f7f8; }
.member-clear {
    background: #c53030; color: #fff; border: 0; border-radius: 4px;
    padding: .35em .8em; font-size: 13px; font-weight: 600; cursor: pointer;
}
.member-clear:hover { background: #a52626; }

.member-flash {
    display: none; position: fixed; top: 1em; left: 50%; transform: translateX(-50%);
    z-index: 100; min-width: 280px; text-align: center; font-weight: 600;
    box-shadow: 0 4px 16px rgba(0,0,0,.3);
}
.member-flash.flash-visible { display: block; }

/* Activity feed */
.activity-feed { list-style: none; padding: 0 1.5em 1em; margin: 0; }
/* v2.51.8 — activity-row + all activity-* text colors now use
   semantic tokens so they read on both light + dark surfaces. */
.activity-row {
    display: grid; grid-template-columns: 120px 130px 1fr; gap: .8em;
    padding: .55em .8em; background: var(--pw-surface-2); border-radius: 4px;
    border-left: 4px solid var(--member-color, var(--pw-text-faint));
    margin-bottom: 4px; font-size: 14px;
}
.activity-when { color: var(--pw-text-muted) !important; }
.activity-member { font-weight: 600; color: var(--pw-text); }
.activity-action { color: var(--pw-text); }

/* Team member color swatch used in admin tables */
.member-swatch {
    display: inline-block; width: 12px; height: 12px; border-radius: 3px;
    border: 1px solid rgba(0,0,0,.15); vertical-align: middle; margin-right: .3em;
}

.signout-link {
    margin-left: 1em; padding: .2em .7em; border-radius: 4px;
    background: rgba(255,255,255,.08); font-size: 12px;
}
.signout-link:hover { background: rgba(255,255,255,.18); }

/* Operator per-printer stats strip */
.op-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 1em; padding: 1em 1.5em; }
.op-stat { background: #2b3036; border: 1px solid #3a4048; border-radius: 6px; padding: 1em 1.2em; text-align: center; }
.op-stat-num { font-size: 28px; font-weight: 600; color: #f7f7f8; }
.op-stat-label { color: #c4cad4; font-size: 11px; text-transform: uppercase; letter-spacing: .05em; margin-top: .2em; }
.op-section-title { color: #cdd3dc; padding: 1em 1.5em 0; font-size: 13px; text-transform: uppercase; letter-spacing: .04em; }
/* The operator page itself is dark-themed (.operator-body color: #f7f7f8)
   but recent-jobs lives on a white surface. Re-anchor text color to the
   admin --text so rows aren't whitish-on-white. */
.op-jobs { background: var(--surface); color: var(--text); margin: 0 1.5em 1.5em; }
.op-jobs a { color: var(--text); }
.op-jobs .muted { color: var(--muted); }

/* Inventory: category pills */
.cat-pill { display: inline-block; padding: .1em .5em; background: #e0e5ec; color: var(--text); border-radius: 3px; font-size: 11px; margin-right: .2em; }
.cat-fieldset { border: 1px solid var(--border); border-radius: 4px; padding: .4em .6em; margin: 0; }
.cat-fieldset legend { padding: 0 .3em; font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .03em; }
.cat-checkbox { display: inline-flex; align-items: center; gap: .3em; margin-right: .8em; font-size: 12px; text-transform: none; letter-spacing: 0; color: var(--text); }
.cat-checkbox input { margin: 0; }
.cat-checkboxes { display: flex; flex-wrap: wrap; gap: .3em .8em; padding: .4em .6em; background: var(--surface); border: 1px solid var(--border); border-radius: 4px; }


/* ─── v2.0 setup pages (PrintFactory, Locations, Printers, Queues, Routing) ── */

.nav-sep { color: var(--border); margin: 0 .3em; }

.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 1em 1.25em;
    margin: 1em 0;
}
.card h2 { margin-top: 0; font-size: 16px; font-weight: 600; }

.form-stack { display: flex; flex-direction: column; gap: .75em; }
.form-stack label { display: flex; flex-direction: column; gap: .25em; font-size: 13px; }
.form-stack label.checkbox { flex-direction: row; align-items: center; gap: .5em; }
.form-stack label span { color: var(--muted); font-size: 12px; }
.form-stack input[type=text], .form-stack input:not([type]), .form-stack select,
.form-stack input[type=email], .form-stack input[type=password], .form-stack input[type=number] {
    padding: .4em .55em; font-size: 13px;
    border: 1px solid var(--border); border-radius: 4px;
    /* v2.51.21 — was hardcoded #fff bg with no color, which rendered
       as white-on-white in dark mode. Use tokens so it theme-flips. */
    background: var(--pw-surface); color: var(--pw-text);
}
.pw-login .form-stack input[type=text],
.pw-login .form-stack input:not([type]),
.pw-login .form-stack input[type=email],
.pw-login .form-stack input[type=password],
.pw-login .form-stack input[type=number],
.pw-login .form-stack select {
    min-height: var(--pw-touch-md);
    padding: .65em .75em;
    font-size: max(16px, 1em);
}
.form-row { display: flex; gap: .75em; flex-wrap: wrap; }
.form-row > label { flex: 1; min-width: 180px; }

.kv { border-collapse: collapse; }
.kv th, .kv td { padding: .35em .75em .35em 0; text-align: left; vertical-align: top; }
.kv th { color: var(--muted); font-weight: 500; font-size: 12px; text-transform: uppercase; letter-spacing: .03em; }

.pill { display: inline-block; padding: .15em .55em; border-radius: 999px; font-size: 11px; text-transform: uppercase; letter-spacing: .04em; font-weight: 600; }
/* v2.51.21 — pills now use semantic tokens so they theme-flip.
   Pre-fix: hardcoded pastel bg + dark text rendered as low-contrast
   light-on-light in dark mode. */
.pill-ok   { background: color-mix(in srgb, var(--pw-success) 18%, var(--pw-surface)); color: var(--pw-success); border: 1px solid var(--pw-success); }
.pill-warn { background: color-mix(in srgb, var(--pw-warning) 18%, var(--pw-surface)); color: var(--pw-warning); border: 1px solid var(--pw-warning); }

/* v2.49.0 — .btn-small / .btn-danger / .btn-secondary retired
 * in favor of .pw-btn--sm / .pw-btn--danger / .pw-btn--secondary
 * in components.css. */

/* Inline reroute strip cells: keep buttons together but allow wrap on
   narrow screens. Tap-friendly minimum on touch displays. */
.op-jobs-actions { white-space: normal; text-align: right; }
.op-jobs-actions .pw-btn--sm {
    min-height: 36px;        /* desktop */
    padding: .35em .7em;
}
@media (pointer: coarse) {
    .op-jobs-actions .btn-small { min-height: 44px; font-size: 14px; }
}
.op-reroute-inline { margin: 0; }

/* v2.14.2 — operator IP-debug footer chip. Visible on every operator
   page so floor staff can read it aloud when auto-login fails. */
.op-ip-debug {
    display: inline-block;
    padding: .05em .35em;
    background: rgba(0,0,0,.06);
    border-radius: 3px;
    font-size: .95em;
}

table.grid input, table.grid select {
    padding: .25em .35em; font-size: 12px;
    border: 1px solid var(--border); border-radius: 3px;
    /* v2.51.21 — was hardcoded #fff bg with no color, which rendered
       as white-on-white in dark mode (browser color-scheme defaults
       text to light when --pw-text resolves dark, then this rule
       slammed the bg white). Use tokens so it theme-flips. */
    background: var(--pw-surface); color: var(--pw-text);
}
table.grid form { margin: 0; }

/* ─── Bigger team-member active indicator ─────────────────────────────────── */
/* Replace the thin 4px border with a thick 8px one, plus a glow when active,
   so it's unmissable on a phone across the shop floor. */
.operator-header { border-top-width: 8px; }
.operator-header[style*="--member-color"] {
    box-shadow: 0 4px 0 rgba(0,0,0,.04);
}
.member-active { font-size: 22px; }
.member-active .member-dot { width: 16px; height: 16px; }

/* Tint the member-picker block itself when a member is active so the
   selection chip stands out from the dark header. */
.member-picker { transition: background .15s; }
.operator-header[style*="--member-color"] .member-picker {
    background: var(--member-color, transparent);
    border: 1px solid rgba(255,255,255,.25);
}
.operator-header[style*="--member-color"] .member-active,
.operator-header[style*="--member-color"] .member-prompt {
    color: #fff;
    text-shadow: 0 1px 1px rgba(0,0,0,.3);
}

/* ─── Mobile / narrow-viewport responsive nav + headers ───────────────────── */

@media (max-width: 720px) {
    /* Admin top nav — wrap, no horizontal scroll, smaller padding. */
    nav { flex-wrap: wrap; gap: .6em 1em; padding: .5em .8em; height: auto; }
    nav .spacer { display: none; }
    nav a { font-size: 13px; padding: .15em 0; }
    nav a.active { border-bottom-width: 2px; padding-bottom: 2px; }
    nav .brand { width: 100%; padding-bottom: .25em; border-bottom: 1px solid rgba(255,255,255,.06); }
    .nav-sep { display: none; }  /* the · separator is hidden in wrap mode */
    .refresh-picker { font-size: 12px; }

    /* Main content padding tighter on phones — and explicitly fill
       the device width including the iOS safe-area (notch / dynamic
       island) since `viewport-fit=cover` enables those insets. Without
       this, iOS PWAs render the content column inset to the safe area,
       leaving big empty bars on each side. */
    html, body { width: 100%; max-width: 100vw; overflow-x: hidden; }
    main {
        padding: 0 calc(.6em + env(safe-area-inset-left))
                 0 calc(.6em + env(safe-area-inset-right));
        max-width: 100%;
        box-sizing: border-box;
    }
    /* v2.16.8 — `viewport-fit=cover` paints UNDER the iOS dynamic
       island / notch. The header background colour extends naturally
       into that inset (good), but its content (brand text + nav links
       + user chip) would render BEHIND the island. Push the content
       down past `safe-area-inset-top` so it's tappable and readable. */
    header {
        padding-top: env(safe-area-inset-top);
        padding-left: env(safe-area-inset-left);
        padding-right: env(safe-area-inset-right);
    }
    h1 { font-size: 22px; }
    h2 { font-size: 17px; }

    /* Operator station header stacks vertically so each chunk gets its full
       width. The team-picker becomes a full-width prominent row in the
       middle so it's the obvious thing to interact with. */
    .operator-header {
        flex-direction: column; align-items: stretch; gap: .6em;
        padding: .75em .8em;
    }
    .member-picker {
        margin: 0; padding: .5em .75em;
        flex-wrap: wrap; justify-content: center;
    }
    .member-active, .member-prompt { width: 100%; text-align: center; justify-content: center; }
    .member-select { flex: 1; min-width: 0; font-size: 16px; padding: .55em .55em; }
    .member-clear { padding: .55em 1em; font-size: 14px; }
    .refresh-picker-op { justify-content: flex-end; }
    .operator-printer { font-size: 20px; }

    /* Supply tap buttons take the full row on phones. */
    .operator-grid { padding: .8em .6em; gap: .8em; }
    .op-button { padding: 1.2em 1em; font-size: 17px; min-height: 84px; }

    /* Activity feed lines wrap nicely. */
    .activity-row {
        grid-template-columns: 1fr; gap: .15em;
        padding: .55em .7em;
    }
    .activity-when { font-size: 11px; }

    /* Admin tables: allow horizontal scroll instead of breaking the layout. */
    main table.grid, main table.op-jobs { display: block; overflow-x: auto; }
    /* Forms inside cards stack their .form-row labels. */
    .form-row > label { flex-basis: 100%; }
    /* Cards take full width with less padding. */
    .card { padding: .8em; }
}

/* Even more compact on very small phones */
@media (max-width: 400px) {
    nav { gap: .5em .8em; }
    nav a { font-size: 12px; }
    .operator-printer { font-size: 18px; }
    .op-button { font-size: 16px; padding: 1em; }
}

/* Priority indicators on print-jobs page */
tr.prio-urgent td:nth-child(2) { color: var(--bad); font-weight: 700; }
tr.prio-high   td:nth-child(2) { color: var(--warn); font-weight: 600; }
tr.prio-normal td:nth-child(2) { color: var(--muted); }
tr.prio-low    td:nth-child(2) { color: var(--muted); opacity: .7; }
tr.prio-urgent td:first-child  { border-left: 3px solid var(--bad); }
tr.prio-high td:first-child    { border-left: 3px solid var(--warn); }
tr.prio-urgent select          { color: var(--bad); font-weight: 700; }
tr.prio-high   select          { color: var(--warn); font-weight: 600; }

/* ─── Compact nav with dropdowns (v2.8) ─────────────────────────────────── */

/* The new Setup / user dropdowns use <details>. Reset native arrow + theme. */
.nav-dropdown { position: relative; }
.nav-dropdown summary {
    cursor: pointer; list-style: none;
    padding: 0; margin: 0;
    color: #cdd3dc;
    user-select: none;
    display: inline-flex; align-items: center; gap: .35em;
}
.nav-dropdown summary::-webkit-details-marker { display: none; }
.nav-dropdown summary:hover { color: #fff; }
.nav-dropdown[data-active] summary { color: #fff; border-bottom: 2px solid var(--accent); padding-bottom: 2px; }

/* The dot inside an active summary shows that one of the contained
   items is the current page. Subtle blue circle. */
.nav-dropdown .dot {
    display: inline-block; width: 6px; height: 6px; border-radius: 50%;
    background: var(--accent); vertical-align: middle;
}

.nav-dropdown-menu {
    position: absolute;
    top: calc(100% + 6px); left: 0;
    background: #1f2328;
    border: 1px solid #3a4048;
    border-radius: 6px;
    padding: .35em .15em;
    min-width: 220px;
    box-shadow: 0 6px 18px rgba(0,0,0,.35);
    z-index: 50;
    display: flex; flex-direction: column;
}
/* v2.56.0 — multi-column mega-menu variant for Setup buckets that
   bundle many items (e.g. Settings). Two columns on desktop, stacks
   on phone so the dropdown still fits the viewport. */
.nav-dropdown-menu--mega {
    flex-direction: row;
    gap: .25em;
    min-width: 480px;
    padding: .35em;
}
.nav-dropdown-menu--mega .nav-mega-col {
    display: flex; flex-direction: column;
    min-width: 220px;
    flex: 1 1 220px;
}
@media (max-width: 720px) {
    .nav-dropdown-menu--mega {
        flex-direction: column;
        min-width: 220px;
    }
}
.nav-dropdown-menu a, .nav-dropdown-menu .nav-static {
    padding: .35em .8em;
    color: #cdd3dc; text-decoration: none;
    font-size: 13px; font-weight: 400;
    white-space: nowrap;
    border-bottom: 0 !important;
}
/* v2.51.21 — the admin nav-dropdown bg is a fixed dark color
   regardless of page theme, so muted/faint text inside it must use
   light-on-dark values instead of inheriting --pw-text-muted (which
   in light theme resolves to dark gray and reads as invisible-on-
   dark inside the dropdown). */
.nav-dropdown-menu .pw-muted,
.nav-dropdown-menu .pw-text-muted,
.nav-dropdown-menu small.pw-muted,
.nav-dropdown summary .pw-muted,
.nav-dropdown summary small.pw-muted,
.nav-user summary .pw-muted,
/* Also catch muted text directly in the admin nav header (e.g. the
   v{{ version }} chip next to the user dropdown when no avatar). */
header > nav > .pw-muted,
header > nav > span.pw-muted { color: #a8b0bc !important; }
.nav-dropdown-menu code,
.nav-dropdown-menu .pw-mono { color: #e8eaef; background: rgba(255,255,255,.08); }
.nav-dropdown-menu strong { color: #f7f7f8; }
.nav-dropdown-menu a:hover { background: #2b3036; color: #fff; }
.nav-dropdown-menu a.active { color: #fff; background: #2b3036; }
.nav-section-title {
    padding: .55em .8em .15em;
    font-size: 10px; text-transform: uppercase;
    letter-spacing: .07em;
    /* v2.51.21 — bumped from #6b7280 (3.5:1) to #a8b0bc (~6:1) on
       the dark dropdown bg so the section labels read clearly. */
    color: #a8b0bc;
    font-weight: 600;
}
.nav-dropdown-right {
    /* v2.59.29 — F-011: prior `right: 0` could overflow the viewport
       at narrow widths (Audit reported horizontal scrollbar at 1440px
       caused by this dropdown). Clamp width and position so the menu
       never extends past the viewport's right edge. */
    left: auto;
    right: 0;
    max-width: calc(100vw - 16px);
    box-sizing: border-box;
}

.nav-user summary {
    background: rgba(255,255,255,.06);
    border-radius: 4px;
    padding: .2em .55em;
    font-size: 13px;
}
.nav-user summary .muted { font-size: 11px; }

@media (max-width: 720px) {
    /* The dropdown menu becomes inline-stacked on mobile so it doesn't
       overflow off-screen. (The whole nav also wraps via the earlier rules.) */
    .nav-dropdown-menu { position: static; min-width: unset; box-shadow: none;
                        border: none; background: rgba(0,0,0,.15);
                        padding: .25em 0; margin: .25em 0 .5em; }
    .nav-dropdown[open] summary { background: #2b3036; }
}

/* ─── v2.14.4 — touch-screen sizing pass ──────────────────────────────────
   Floor staff will use the operator UI from wall-mounted touch monitors
   (24" landscape) AND from phones. `pointer: coarse` matches both. Bump
   key tap targets to ≥60 px and prevent iOS zoom on <select> by forcing
   a 16-px font on form controls. */
@media (pointer: coarse) {
    .btn, .btn-primary, .btn-big {
        min-height: 60px;
        font-size: 17px;
        padding: .8em 1.1em;
    }
    .btn-small { min-height: 44px; font-size: 14px; }
    select, input[type="text"], input[type="number"], input[type="password"] {
        font-size: 16px;
        min-height: 44px;
        padding: .5em .6em;
    }
    .op-button { min-height: 88px; font-size: 18px; }
}

/* Touch screens at landscape kiosk resolution — bigger numbers, taller
   recent-jobs rows, wider supply tile grid. Reads cleanly from across
   the shop floor. */
@media (min-width: 1100px) and (pointer: coarse) {
    .operator-printer { font-size: 28px; }
    .op-stat-num { font-size: 32px; }
    .operator-grid { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)) !important; }
    .op-jobs th, .op-jobs td { padding: .95em .8em; font-size: 15px; }
    .op-section-title { font-size: 16px; }
}

/* ─── v2.14.4 — operator RIP control buttons ─────────────────────────────
   Visible on the job detail page when a print_job is in-flight at PF
   (status 'processing'/'printing' with a pf_job_id set). Lets the floor
   nudge the local RIP without leaving the operator UI. */
.op-rip-controls {
    display: flex;
    gap: .6em;
    margin: 1em 0 .5em;
    flex-wrap: wrap;
}
.op-rip-controls .btn { min-width: 110px; }
.op-rip-controls .btn-danger { color: var(--bad); border-color: #f1bdbd; }
.op-rip-controls .btn-danger:hover { background: #fef2f2; }

/* ─── v2.18.3 — App footer + changelog page ──────────────────────────── */
.app-footer {
    text-align: center;
    padding: 1.6em 1em 2.4em;
    margin-top: 2em;
    border-top: 1px solid var(--border);
}
.app-footer a { color: var(--muted); text-decoration: none; font-size: 12px; }
.app-footer a:hover { color: var(--accent); }

.changelog { max-width: 760px; }
.cl-entry {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    margin-bottom: .5em;
}
/* Each release with detail is a native <details>; flat ones are a <div>.
   Both share the same summary-row layout so the list reads evenly. */
.cl-entry > summary, .cl-flat {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .15em .7em;
    padding: .7em .9em;
}
.cl-entry > summary { cursor: pointer; list-style: none; color: var(--text); }
.cl-entry > summary::-webkit-details-marker { display: none; }
.cl-entry > summary::before {
    content: "▸";
    color: var(--muted);
    display: inline-block;
    transition: transform .12s ease;
}
.cl-entry[open] > summary::before { transform: rotate(90deg); }
.cl-flat::before { content: "·"; color: var(--border); }
.cl-ver { font-weight: 600; color: var(--accent); }
.cl-date { white-space: nowrap; }
.cl-desc { flex: 1 1 100%; }          /* own line on phones */
@media (min-width: 620px) { .cl-desc { flex: 1 1 auto; } }

.cl-body { padding: .1em .9em 1em 1.9em; border-top: 1px solid var(--border); }
.cl-section { margin-top: .9em; }
.cl-section-title {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .05em;
    margin: 0 0 .25em;
}
.cl-added   { color: var(--good); }
.cl-changed { color: var(--accent); }
.cl-fixed   { color: var(--warn); }
.cl-body ul { margin: .2em 0; padding-left: 1.2em; }
.cl-body li { margin: .3em 0; }

/* ─── v2.19.1 — Triage board ──────────────────────────────────────────── */
.nowrap { white-space: nowrap; }
/* Dashboard "needs attention" banner — a full-width amber call to action
   linking to /admin/triage; only rendered when the count is non-zero. */
.attention-banner {
    display: block;
    margin: .5em 0 1em;
    padding: .8em 1em;
    background: #fffbeb;
    border: 1px solid var(--warn);
    border-left: 4px solid var(--warn);
    border-radius: 6px;
    color: var(--warn);
    font-weight: 600;
    text-decoration: none;
}
.attention-banner:hover { background: #fff4d6; }

/* v2.40.1 — Station recent-jobs row tints driven by QA aggregate.
   Red = failure with no replacement; yellow = respawn in flight;
   green = Ready-to-Ship; blue = still awaiting QA.
   v2.50.16 — alpha bumped (.14 → .28) + inset-bar width 4 → 8 px
   so a row's status is unmissable from across the print shop.
   Operator station is the kiosk surface — distance readability
   wins over typographic restraint. */
.op-jobs tr.qa-failed {
    background: rgba(160, 32, 32, .28);
    box-shadow: inset 8px 0 0 #a02020;
}
.op-jobs tr.qa-pending_respawn {
    background: rgba(183, 121, 31, .28);
    box-shadow: inset 8px 0 0 #b7791f;
}
.op-jobs tr.qa-passed {
    background: rgba(45, 143, 77, .26);
    box-shadow: inset 8px 0 0 #2d8f4d;
}
.op-jobs tr.qa-pending {
    background: rgba(28, 74, 138, .24);
    box-shadow: inset 8px 0 0 #1c4a8a;
}

/* ─────────────────────────────────────────────────────────────────
   v2.39.0 — QA active-agent banner + site-wide colour accent.

   `--qa-agent-color` is set on <body> by qa_base.html when there's
   a logged-in agent. Everything below keys off that variable so the
   "this is YOUR session" colour cue propagates across the QA UI.
   Designed for the 2K touchscreens in Eric's shop (2560×1440) —
   targets are big, type is generous, contrast is high.
   ─────────────────────────────────────────────────────────────── */

.qa-agent-banner {
    /* The whole bar IS the swatch — no separate dot needed.
       Background colour comes from the inline style on the element
       so each agent gets their own. Text auto-contrasts white. */
    color: #fff;
    width: 100%;
    padding: 0;
    box-shadow: 0 2px 6px rgba(0, 0, 0, .15);
    /* Don't take the click out of the page — the inner <a> is the
       only interactive bit. */
    user-select: none;
}
.qa-agent-banner-inner {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1.5em;
    max-width: 2400px; margin: 0 auto;
    padding: 1.1em 1.5em;
}
.qa-agent-banner-label {
    font-size: 13px; letter-spacing: .15em; text-transform: uppercase;
    opacity: .85; font-weight: 600;
    /* v2.51.21 — outline for readability on any --qa-agent-color. */
    text-shadow:
      -1px -1px 0 rgba(0,0,0,.6), 1px -1px 0 rgba(0,0,0,.6),
      -1px  1px 0 rgba(0,0,0,.6), 1px  1px 0 rgba(0,0,0,.6);
}
.qa-agent-banner-name {
    display: inline-flex; align-items: center; gap: .75em;
    color: #fff; text-decoration: none;
    font-weight: 800;
    /* v2.51.21 — outline for readability on any --qa-agent-color. */
    text-shadow:
      -1px -1px 0 #000, 1px -1px 0 #000,
      -1px  1px 0 #000, 1px  1px 0 #000,
      0 2px 4px rgba(0, 0, 0, .35);
    /* The fullname size is the visual anchor for the banner; the
       initials chip is a complementary, smaller token. */
    flex: 1;
    justify-content: center;
}
.qa-agent-banner-initials {
    display: inline-flex; align-items: center; justify-content: center;
    background: rgba(255, 255, 255, .22);
    border: 2px solid rgba(255, 255, 255, .55);
    border-radius: 999px;
    width: 56px; height: 56px;
    font-size: 22px; font-weight: 900;
    text-shadow: 0 1px 2px rgba(0, 0, 0, .25);
}
.qa-agent-banner-fullname {
    font-size: 30px;
    text-shadow: 0 1px 2px rgba(0, 0, 0, .25);
}
.qa-agent-banner-signout-form { margin: 0; }
.qa-agent-banner-signout {
    background: rgba(0, 0, 0, .28);
    color: #fff;
    border: 2px solid rgba(255, 255, 255, .55);
    border-radius: 8px;
    padding: .65em 1.1em;
    font-size: 16px; font-weight: 600;
    cursor: pointer;
    min-height: 56px; min-width: 110px;
}
.qa-agent-banner-signout:active { background: rgba(0, 0, 0, .42); }

/* QA pages: site-wide accent driven by --qa-agent-color. Keeps
   the UI looking neutral until a session is active, then warms it
   up with the current agent's colour. */
body.qa-signed-in {
    /* v2.51.26 — Soft tinted background so the page reads as "this
       is the active agent's session". Tint over var(--pw-bg) so it
       theme-flips correctly (was hardcoded #fff which forced a
       light bg even in dark mode → made the QA pages an island of
       white inside a dark admin chrome). */
    background:
        linear-gradient(180deg,
            color-mix(in srgb, var(--qa-agent-color) 6%, var(--pw-bg)) 0%,
            var(--pw-bg) 220px);
}
/* Visible focus + tap ring in the agent's colour — for QA action
   buttons (PASS / FAIL / MISSING). The buttons themselves keep
   their semantic colour (green / red / amber); the ring is the
   "this is YOUR tap" cue. */
body.qa-signed-in button:focus-visible,
body.qa-signed-in a:focus-visible,
body.qa-signed-in input:focus-visible,
body.qa-signed-in select:focus-visible,
body.qa-signed-in textarea:focus-visible {
    outline: 3px solid var(--qa-agent-color);
    outline-offset: 2px;
}

/* The QA pill on the mode switcher gets a coloured underline when
   a session is live (subtle — doesn't fight the dark nav). */
body.qa-signed-in .mode-switcher .mode-active {
    box-shadow: inset 0 -3px 0 0 var(--qa-agent-color);
}

/* ─────────────────────────────────────────────────────────────────
   v2.39.0 — Station team-picker as a button grid (replaces <select>).
   Renders alongside the operator-header. Each button is a wide,
   coloured target sized for fat-finger taps on a 2K display.
   ─────────────────────────────────────────────────────────────── */

/* v2.39.1 — Horizontal-row layout. The picker takes its own row
   (see `.operator-header` rule that wraps `.member-picker` to full
   width), so buttons spread across the full width instead of
   stacking in a 140px-wide vertical strip inside the header.
   flex-wrap lets the row break onto a second line only when the
   names actually run out of horizontal room. `flex: 1 1 auto` lets
   the grid claim the remaining horizontal space inside
   `.member-picker` after the prompt label and Clear button take
   theirs. */
.team-picker-buttons {
    display: flex;
    flex-wrap: wrap;
    gap: .55em;
    margin: 0;
    align-items: stretch;
    justify-content: flex-start;
    flex: 1 1 auto;
    min-width: 0;
}
.team-picker-btn {
    display: flex; align-items: center; justify-content: center;
    gap: .45em;
    /* Each button is auto-sized to its content but caps at a
       comfortable touch target. flex: 1 1 auto makes them share
       leftover width evenly; min-width keeps the smallest button
       finger-tappable. */
    flex: 1 1 auto;
    min-width: 130px;
    max-width: 240px;
    min-height: 60px;
    padding: .65em 1em;
    border: 3px solid transparent;
    border-radius: 10px;
    background: var(--member-color, #6b7280);
    color: #fff;
    font-size: 18px; font-weight: 700;
    cursor: pointer;
    /* v2.51.21 — strong 4-direction text outline so the name reads
       on any --member-color, including pale yellows + lime greens
       where a soft shadow still leaves white text invisible. */
    text-shadow:
      -1px -1px 0 #000, 1px -1px 0 #000,
      -1px  1px 0 #000, 1px  1px 0 #000,
      0 2px 4px rgba(0, 0, 0, .45);
    /* Slight outer ring so adjacent same-colour buttons stay distinct */
    box-shadow: 0 1px 0 rgba(255, 255, 255, .25) inset;
    transition: transform .05s ease, border-color .1s ease;
    user-select: none;
}
.team-picker-btn:active { transform: scale(.97); }
.team-picker-btn.is-active {
    /* Selected: white ring + a soft check chip in the corner. */
    border-color: #fff;
    box-shadow: 0 0 0 3px rgba(0, 0, 0, .55), 0 1px 0 rgba(255, 255, 255, .25) inset;
}
.team-picker-btn.is-active::after {
    content: "✓";
    margin-left: .25em;
    font-weight: 900;
}
.team-picker-clear-btn {
    background: #2d3138;
    color: #f7f7f8;
    border: 2px solid #4a4f57;
    border-radius: 10px;
    min-height: 64px;
    padding: .65em 1em;
    font-size: 16px; font-weight: 600;
    cursor: pointer;
}
.team-picker-clear-btn:active { background: #1f2328; }

/* ─────────────────────────────────────────────────────────────────
   v2.39.0 — 2K touchscreen (≥ 2560 px wide) scale-up. Eric's shop
   monitors are 2K; on those, push the agent banner taller, the
   fonts bigger, the buttons fatter. Smaller screens keep the
   defaults above.
   ─────────────────────────────────────────────────────────────── */

/* v2.50.16 — Operator station 1080p kiosk pass. Previously the
   only kiosk-size scale-up rule was @media (min-width: 2200px)
   targeting the 2K shop monitors. The 1920×1080 wall-mount TVs
   fell into a gap where everything stayed at 1080p default sizes,
   leaving team picker names unreadable from across the floor.
   This 1920px breakpoint splits the difference: bigger than
   desktop / phone defaults, smaller than the 2200px 2K mode. */
@media (min-width: 1920px) {
    .team-picker-btn {
        min-height: 72px;
        font-size: 20px;
        padding: .75em 1em;
        min-width: 150px;
    }
    .qa-agent-banner-fullname { font-size: 32px; }
    .qa-agent-banner-label { font-size: 14px; }
}

@media (min-width: 2200px) {
    .qa-agent-banner-inner { padding: 1.6em 2.5em; }
    .qa-agent-banner-fullname { font-size: 42px; }
    .qa-agent-banner-initials {
        width: 80px; height: 80px; font-size: 30px; border-width: 3px;
    }
    .qa-agent-banner-label { font-size: 16px; }
    .qa-agent-banner-signout {
        font-size: 20px; padding: .9em 1.5em; min-height: 72px;
    }
    .team-picker-btn {
        min-height: 84px;
        font-size: 22px;
        padding: .85em 1.1em;
        min-width: 160px;
        max-width: 280px;
    }
    .team-picker-buttons {
        gap: .75em;
    }
    .team-picker-clear-btn {
        min-height: 84px; font-size: 20px;
    }
}

/* v2.51.23 — Global search dropdown result rows. Styles live here
   instead of inline in global_search.js so they respect the theme
   tokens (--pw-text, --pw-text-muted, --pw-border, --pw-bg). The
   dropdown's outer wrapper (#global-search-dropdown) already uses
   var(--pw-surface) bg via the inline style in base.html. */
#global-search-dropdown .search-empty {
    padding: 8px 12px;
    color: var(--pw-text-muted);
    font-size: 13px;
}
#global-search-dropdown .search-group-header {
    padding: 6px 12px;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--pw-text-muted);
    background: color-mix(in srgb, var(--pw-text) 6%, transparent);
    border-top: 1px solid var(--pw-border);
    font-weight: 600;
}
#global-search-dropdown .search-result {
    display: block;
    padding: 8px 12px;
    color: var(--pw-text);
    text-decoration: none;
    border-top: 1px solid var(--pw-border);
    font-size: 13px;
}
#global-search-dropdown .search-result:hover,
#global-search-dropdown .search-result.is-highlighted {
    background: color-mix(in srgb, var(--pw-primary) 12%, transparent);
    color: var(--pw-text);
}
#global-search-dropdown .search-badge {
    color: var(--pw-text-faint);
    font-size: 11px;
    margin-left: 4px;
}
