/* web/schedule-opcenter.css
 *
 * PR-OPC-1 (2026-05-25) — Siemens Opcenter APS aesthetic for the
 * Schedule page rebuild. Scoped entirely to `#pg-schedule-v2 .opc-*`
 * — zero selector collision with the existing `#pg-schedule .schedp-*`
 * stylesheet that lives in web/schedule3.css.
 *
 * Goal: industrial-terminal flat-panel look. Hairline 1px borders,
 * zero rounded corners on functional surfaces, tabular monospace
 * numerics, 28px sticky bottom status bar, dense 4-line cards.
 *
 * Strict color tokens only — every color is `var(--token)` or
 * `color-mix(in srgb, var(--token) X%, var(--bg))`. No hardcoded hex.
 * Tokens defined in index.html lines 368-426 for both Studio Light
 * and Onyx Dark.
 *
 * Sign-off gates (from QA package):
 *   GATE-1: stranger reads 5 items in ≤5s
 *   GATE-2: zero hardcoded hex
 *   GATE-3: drag-drop preserved
 *   GATE-4: footer visible after scroll
 *   GATE-5: theme switch no anomaly
 *   GATE-6: third-party calls "clearly different visual styles"
 */

/* ── Page frame ─────────────────────────────────────────────────── */

/* PR-OPC-2-FIX (2026-05-25): only paint the page when go() has added
   .on — earlier OPC-1B shipped this as a bare `#pg-schedule-v2 {…}`
   rule whose id-specificity (1,0,0) clobbered `.pg{display:none}`
   (0,1,0) from index.html line 943, so the schedule grid leaked onto
   Home / Libraries / every other page underneath. Pairing display
   with `.on` restores the standard `.pg` visibility contract. */
/* PR-SCHED-CAT (2026-08-04): `height: 100vh` → `100%`.
   #app-workspace (shell.css:632) is `position:fixed; top:56px; bottom:0`
   — and `bottom:56px` below 768px where the bottom nav shows. A 100vh
   child inside a viewport-minus-56px host overflowed by exactly the
   topbar height, pushing the 28px statusbar below the fold and making
   #app-workspace scroll. The host has both `top` and `bottom` set, so
   its height is definite and `100%` resolves correctly. */
#pg-schedule-v2 {
    background: var(--bg);
    color: var(--txt);
    font: 13px/1.4 var(--ff-ui, system-ui);
    flex-direction: column;
    height: 100%;
    overflow: hidden;

    /* ── Board type scale (PR-SCHED-MAST, 2026-08-04) ──────────────
       Named roles instead of a bare `font:` shorthand repeated in
       thirty rules. Family per role is deliberate:

       --ff-mono for anything an operator COMPARES down a column
       (dates, %, counts, job #). Monospace holds digit columns in
       vertical register, and JetBrains Mono disambiguates 0/O and 1/l
       — which matters reading a ship date at arm's length in shop light.

       --ff-ui (Inter) for anything read as a WORD (customer, station).
       Humanist letterforms read faster as prose than a mono grid does. */
    --opc-fs-board-title: 13px;   /* masthead wordmark          mono 700 */
    --opc-fs-day-dow:     10px;   /* MON / TUE                  ui   700 */
    --opc-fs-day-date:    17px;   /* AUG 4 — the scan anchor    mono 700 */
    --opc-fs-chip:         9px;   /* TODAY / risk / category    mono 700 */
    --opc-fs-metric:      10px;   /* 62% · 4 jobs · 11 doors    mono 500 */
}

#pg-schedule-v2.on { display: flex; }

/* Top-of-page anchor — literally the first pixel row of the board, in the
   active board's hue. Pairs with the existing board-hue seam above
   .opc-weeks so both ends of the masthead agree on which board this is. */
#pg-schedule-v2::before {
    content: "";
    display: block;
    flex: 0 0 3px;
    background: var(--opc-board);
}

/* ── Toolbar / page header ──────────────────────────────────────── */

/* PR-SCHED-CAT: `flex-wrap: wrap` + `height: auto`. The row was
   `display:flex` with NO wrap at a fixed 40px, so below ~900px its tail
   (Print, the calendar link, What-If) was pushed past the right edge and
   clipped by `#pg-schedule-v2{overflow:hidden}` with no scroll
   affordance — the buttons were simply unreachable. */
/* PR-SCHED-MAST (2026-08-04): `--sf` → `--pn`.
   THE BUG THIS FIXES: in the dark theme --sf and --bg are BOTH #000000,
   and #pg-schedule-v2's own background is --bg. So the toolbar painted
   the exact same black as the empty page behind it, held apart only by a
   1px hairline — the masthead was effectively invisible, which is the
   opposite of "the first thing you see".

   --pn is the ONLY surface token that separates from --bg in both
   directions: #FFFFFF vs #F3F5F8 light, #16181C vs #000000 dark. This
   file already documents the same trap one layer down on .opc-day-head;
   it just was never applied up here. */
#pg-schedule-v2 .opc-toolbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    row-gap: 4px;
    height: auto;
    min-height: 40px;
    padding: 4px 14px;
    background: var(--pn);
    border-bottom: 1px solid var(--bdr);
    flex: 0 0 auto;
}
/* Single trailing auto-margin. index.html previously carried TWO inline
   `margin-left:auto` declarations in this row; with wrapping enabled two
   auto margins distribute unpredictably across flex lines. */
#pg-schedule-v2 .opc-toolbar-tail {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-left: auto;
}
/* Wordmark. Open tracking because all-caps at small sizes crowds into a
   grey smear without it (Bringhurst; Butterick gives the same rule for
   UI caps under ~14px). The board-hue rule is the third redundant
   "which board am I on" cue, after the top anchor and the name chip. */
#pg-schedule-v2 .opc-toolbar-title {
    font: 700 var(--opc-fs-board-title) / 1 var(--ff-mono);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--white);
    margin-right: 12px;
    padding-left: 10px;
    border-left: 3px solid var(--opc-board);
}
/* PR-SCHED-CAT: dropped `margin-right: auto`. It was the second of three
   competing auto margins in this row; with wrapping enabled it shoved the
   view toggles into a ragged mid-line float. Controls now sit left next to
   the title, secondary links right via .opc-toolbar-tail. */
#pg-schedule-v2 .opc-toolbar-sub {
    font: 400 11px/1 var(--ff-mono);
    color: var(--dim);
    margin-right: 6px;
}
/* PR-SCHED-CAT: `min-height: var(--touch-min)`. These were ~23px tall
   (6px padding on 11px text) — barely half the 44px WCAG 2.5.5 / Apple
   HIG floor the rest of the app holds, on the page most likely to be
   used from a shop tablet. --touch-min resolves to 44px fine-pointer,
   48px hybrid (iPad + Magic Keyboard), 56px pure-coarse (tokens.css:399). */
#pg-schedule-v2 .opc-toolbar button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--pn);
    border: 1px solid var(--bdr);
    border-radius: 0;
    color: var(--txt);
    font: 500 11px/1 var(--ff-mono);
    min-height: var(--touch-min, 44px);
    padding: 0 12px;
    cursor: pointer;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
#pg-schedule-v2 .opc-toolbar button:hover { background: var(--pn-h); border-color: var(--bdr-h); }
#pg-schedule-v2 .opc-toolbar button.active { background: var(--acc); color: var(--acc-ink, var(--white)); border-color: var(--acc); }

/* ── Week / month navigator ────────────────────────────────────────
   PR-SCHED-WEEKNAV (2026-08-04): one welded control instead of three
   loose buttons, so the arrows visibly belong to the label between them.
   Two lines of type, because the two questions are different questions:
   "am I where I think I am?" is answered by the relative name, and "what
   dates is that?" by the range. PayClock prints only the range, which is
   why you end up counting on your fingers to tell whether you are on the
   current period. */
#pg-schedule-v2 .opc-weeknav {
    display: inline-flex;
    align-items: stretch;
    border: 1px solid var(--bdr);
    background: var(--pn);
    /* Shared borders — the seams between the three buttons are the
       internal border-left rules below, so the cluster reads as one
       object rather than three. */
    margin: 0 2px;
}
#pg-schedule-v2 .opc-weeknav[hidden] { display: none; }

/* Arrows keep the toolbar's 44px touch floor but drop the horizontal
   padding — they are glyphs, not words, and 12px each side made the
   cluster wider than the Print button for no gain. */
#pg-schedule-v2 .opc-toolbar .opc-nav-arrow {
    border: none;
    padding: 0 10px;
    font-size: 12px;
    color: var(--dim);
}
#pg-schedule-v2 .opc-toolbar .opc-nav-arrow:hover {
    background: var(--pn-h);
    color: var(--acc);
}

/* The label IS the home button. Disabled (and visibly inert) when you are
   already on the current week — a live control that does nothing is worse
   than a dead one. */
#pg-schedule-v2 .opc-toolbar .opc-nav-home {
    border: none;
    border-left: 1px solid var(--bdr);
    border-right: 1px solid var(--bdr);
    flex-direction: column;
    justify-content: center;
    gap: 1px;
    padding: 0 12px;
    min-width: 122px;
    text-transform: none;
    letter-spacing: 0;
}
#pg-schedule-v2 .opc-nav-rel {
    font: 700 11px/1.1 var(--ff-mono);
    letter-spacing: 0.08em;
    color: var(--white);
}
#pg-schedule-v2 .opc-nav-abs {
    font: 500 10px/1.1 var(--ff-mono);
    color: var(--dim);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
/* Off the current week: the label goes accent-coloured and gains a "click
   me to get back" affordance. On it, the control is quiet — being on this
   week is the resting state and should not shout. */
#pg-schedule-v2 .opc-weeknav[data-now="false"] .opc-nav-rel { color: var(--acc); }
#pg-schedule-v2 .opc-weeknav[data-now="false"] .opc-nav-home { cursor: pointer; }
#pg-schedule-v2 .opc-toolbar .opc-nav-home:disabled {
    cursor: default;
    background: var(--pn);
}
#pg-schedule-v2 .opc-toolbar .opc-nav-home:not(:disabled):hover {
    background: color-mix(in srgb, var(--acc) 10%, var(--pn));
}

/* ── Top infobar (28px, monospace, week health line) ───────────── */

/* Same --pn as the toolbar so the two read as ONE masthead band rather
   than two stacked grey strips, sealed underneath by --shadow-1. That is
   deliberately the lightest shadow in the scale — --shadow-3/4 read as
   "floating popover", the wrong metaphor for chrome that is permanently
   docked. Height is unchanged: this screen is checked dozens of times a
   day from across a shop, and every pixel spent on chrome is a pixel
   taken from the five-day grid that is the actual job of the page. */
#pg-schedule-v2 .opc-infobar {
    display: flex;
    align-items: center;
    gap: 16px;
    height: 28px;
    padding: 0 14px;
    background: var(--pn);
    border-bottom: 1px solid var(--bdr);
    box-shadow: var(--shadow-1);
    font: 500 11px/1 var(--ff-mono);
    font-variant-numeric: tabular-nums;
    color: var(--txt);
    flex: 0 0 auto;
    position: relative;   /* so the shadow paints over .opc-shell below */
    z-index: 1;
}
#pg-schedule-v2 .opc-infobar-ok    { color: var(--ok); }
#pg-schedule-v2 .opc-infobar-warn  { color: var(--warn); }
#pg-schedule-v2 .opc-infobar-no    { color: var(--no); }
#pg-schedule-v2 .opc-infobar-sep   { color: var(--bdr); }
#pg-schedule-v2 .opc-infobar-btl   { margin-left: auto; }
#pg-schedule-v2 .opc-infobar-btl strong { color: var(--no); font-weight: 700; }

/* ── Body shell ────────────────────────────────────────────────── */

#pg-schedule-v2 .opc-shell {
    display: grid;
    grid-template-columns: clamp(212px, 16vw, 264px) 1fr 280px;
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
}

/* ── Left column: category rail above the unscheduled tray ─────────
   PR-SCHED-CAT: the rail is added BESIDE the tray, never replacing it.
   `_opcOnDropTray` (app-schedule.js) is the ONLY unschedule affordance
   in the entire app — there is no keyboard, menu, or detail-pane path to
   clear a ship date — so the tray must keep both its DOM node and its
   three inline drag handlers.

   `minmax(140px, 1fr)` on the tray row is load-bearing: it guarantees
   that drop target can never be squeezed to zero height no matter how
   tall the rail grows. */
#pg-schedule-v2 .opc-leftrail {
    display: grid;
    /* Row 1 category rail · row 2 unscheduled tray. (The job search moved
       to the toolbar 2026-08-09 — PR-SCHED-SEARCH-MOVE.) The tray pins
       itself to the last row explicitly (see .opc-tray) rather than
       auto-placing, so adding a row above it can never collapse it into a
       content-height sliver. */
    grid-template-rows: auto minmax(140px, 1fr);
    min-height: 0;
    overflow: hidden;
    border-right: 1px solid var(--bdr);
}

/* ── Job search (PR-SCHED-SEARCH, 2026-08-07 · moved to toolbar
   PR-SCHED-SEARCH-MOVE 2026-08-09) ─────────────────────────────────
   Customer / PO / job number. Now an inline field in the schedule
   toolbar (not a full-width rail block). Sized to the same gloved-hand
   floor as the toolbar buttons: --touch-min is resolved per pointer type
   in tokens.css (44 fine / 48 hybrid / 56 coarse), so the shop-floor tap
   target is met by construction rather than by a magic number. */
#pg-schedule-v2 .opc-search {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 0 10px;
    min-height: var(--touch-min, 44px);
    width: 240px;
    max-width: 42vw;
    background: var(--bg);
    border: 1px solid var(--bdr);
    border-radius: 6px;
    /* The armed cue is the border + icon brightening (see [data-active]);
       transition both so typing the first character reads as "on". */
    transition: border-color 120ms ease;
}
#pg-schedule-v2 .opc-search[data-active="true"] { border-color: var(--acc); }
#pg-schedule-v2 .opc-search:focus-within { border-color: var(--acc); }

#pg-schedule-v2 .opc-search-icon {
    flex: 0 0 auto;
    width: 15px;
    height: 15px;
    fill: none;
    stroke: var(--dim);
    stroke-width: 2;
    stroke-linecap: round;
    /* Track the input: the glyph brightening is the cue that the field
       is armed, which survives the placeholder being replaced by text. */
    transition: stroke 120ms ease;
}
#pg-schedule-v2 .opc-search[data-active="true"] .opc-search-icon { stroke: var(--acc); }

#pg-schedule-v2 .opc-search-input {
    flex: 1 1 auto;
    min-width: 0;               /* let it shrink inside the grid column */
    background: transparent;
    border: none;
    outline: none;
    color: var(--txt);
    /* 16px floor: iOS Safari zooms the viewport on focus for anything
       smaller, which on a shop iPad throws the whole board off-screen
       mid-search. */
    font: 500 16px/1.2 var(--ff-mono);
    padding: 0;
}
#pg-schedule-v2 .opc-search-input::placeholder {
    color: var(--dim);
    font-weight: 400;
}
/* Our own clear button is the larger target; suppress the native one so
   the two don't stack. */
#pg-schedule-v2 .opc-search-input::-webkit-search-cancel-button { display: none; }

#pg-schedule-v2 .opc-search-clear {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    background: transparent;
    border: none;
    border-radius: 4px;
    color: var(--dim);
    cursor: pointer;
}
#pg-schedule-v2 .opc-search-clear:hover { background: var(--bdr); color: var(--txt); }
#pg-schedule-v2 .opc-search-clear svg {
    width: 14px;
    height: 14px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
}
/* `hidden` on a flex child is overridden by display:inline-flex above —
   restate it or the clear button shows on an empty search. */
#pg-schedule-v2 .opc-search-clear[hidden] { display: none; }

/* ── Board identity (PR-SCHED-CAT-BOARDS, 2026-08-04) ──────────────
   Each rail entry is its own schedule board. `data-cat` on the page root
   publishes the active board's hue as --opc-board, which the title chip
   and the grid's top edge pick up — so switching boards changes the
   surface, not just which cards are on it. */
/* PR-SCHED-DEPT-COLOR (2026-08-08): the active board's hue is now the
   DEPARTMENT accent, published by JS as --opc-dept-color (Okabe–Ito-based
   palette, one per department). Everything that reads --opc-board — the
   board-name chip, the grid's top edge, the infobar strip — recolours to
   the department you're on. Falls back to the neutral accent on "All". */
#pg-schedule-v2 { --opc-board: var(--opc-dept-color, var(--acc)); }

#pg-schedule-v2 .opc-board-name {
    display: inline-flex;
    align-items: center;
    padding: 3px 8px;
    border-radius: 2px;
    background: color-mix(in srgb, var(--opc-board) 16%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--opc-board) 45%, transparent);
    color: var(--opc-board);
    font: 700 11px/1 var(--ff-mono);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    white-space: nowrap;
}
/* Board's hue as a hairline along the top of the grid — a peripheral cue
   that the surface changed, readable without looking at the toolbar. */
#pg-schedule-v2 .opc-weeks {
    border-top: 2px solid color-mix(in srgb, var(--opc-board) 55%, transparent);
}
/* ── Board-switch animation ────────────────────────────────────────
   A board change swaps the entire contents of the grid. Without motion
   the cards just teleport, and the operator cannot tell "I switched
   boards" from "the 30s poll repainted". A short staggered deal-in reads
   unambiguously as *a new board arriving*.

   Stagger is left-to-right across the day columns (40ms apart), matching
   reading order, so the eye is led across the new week rather than having
   all five columns pop at once. Total 160ms of stagger + 220ms of travel
   — under the ~400ms where motion starts feeling like latency.

   `backwards` fill matters: without it a column would flash at full
   opacity during its delay, then jump back to 0 to animate. */
@keyframes opcBoardIn {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
}
@keyframes opcBoardChipIn {
    from { opacity: 0; transform: scale(0.88); }
    to   { opacity: 1; transform: none; }
}

#pg-schedule-v2.board-switching .opc-week > .opc-day {
    animation: opcBoardIn var(--dur-panel, 0.22s) var(--ease-out, ease-out) backwards;
}
#pg-schedule-v2.board-switching .opc-week > .opc-day:nth-child(1) { animation-delay: 0ms; }
#pg-schedule-v2.board-switching .opc-week > .opc-day:nth-child(2) { animation-delay: 40ms; }
#pg-schedule-v2.board-switching .opc-week > .opc-day:nth-child(3) { animation-delay: 80ms; }
#pg-schedule-v2.board-switching .opc-week > .opc-day:nth-child(4) { animation-delay: 120ms; }
#pg-schedule-v2.board-switching .opc-week > .opc-day:nth-child(5) { animation-delay: 160ms; }
/* The empty-state banner and the agenda/month renderers put non-.opc-week
   children in the grid — fade those as one block. */
#pg-schedule-v2.board-switching .opc-weeks > .opc-week-empty-hint {
    animation: opcBoardIn var(--dur-panel, 0.22s) var(--ease-out, ease-out) backwards;
}
/* The tray belongs to the board too — it holds that board's unscheduled
   work, so it must not sit visually still while the grid changes. */
#pg-schedule-v2.board-switching .opc-tray-list > * {
    animation: opcBoardIn var(--dur-panel, 0.22s) var(--ease-out, ease-out) backwards;
    animation-delay: 60ms;
}
/* The board-name chip pops, tying the toolbar to the grid change. */
#pg-schedule-v2.board-switching .opc-board-name {
    animation: opcBoardChipIn var(--dur-quick, 0.14s) var(--spring-snappy, ease-out);
}

/* Visually-hidden live region for board-change announcements. */
#pg-schedule-v2 .opc-sr-only {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* ── Category nav rail ─────────────────────────────────────────────
   Follows the .lib-ed-shell-nav conventions (shell.css:5966) — the
   codebase's existing vertical sub-nav — restyled into the Opcenter
   idiom (zero radius, mono caps). */

#pg-schedule-v2 .opc-nav {
    grid-row: 1;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 4px;
    background: var(--sf);
    min-height: 0;
    overflow-y: auto;
}
#pg-schedule-v2 .opc-nav-btn {
    display: flex;
    align-items: center;
    gap: 9px;
    /* 44px fine-pointer / 48px hybrid / 56px coarse — resolved by
       tokens.css, so no magic number and the gloved-hand floor is met by
       construction. `calc(... + 6px)` gives the rail a little more
       presence than a bare minimum control without breaking that floor. */
    min-height: calc(var(--touch-min, 44px) + 6px);
    /* Constant padding: the active state uses an inset shadow, NOT a
       border-left, precisely so the label never shifts on selection. */
    padding: 0 10px;
    background: transparent;
    border: none;
    border-radius: 0;
    /* --txt, not --dim. These are the board switchers, the primary control
       on the page — they were reading as disabled secondary chrome. */
    color: var(--txt);
    font: 600 12px/1 var(--ff-mono);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    text-align: left;
    cursor: pointer;
    transition: background var(--dur-chrome, 0.15s) var(--ease-out, ease-out),
                color var(--dur-chrome, 0.15s) var(--ease-out, ease-out),
                box-shadow var(--dur-chrome, 0.15s) var(--ease-out, ease-out);
}

/* Per-category hue, held in a custom property so every rule below stays
   single. See tokens.css for why these are dedicated --cat-* tokens
   rather than the --acc-cabinet / --acc-door / --acc-drawer domain
   browns (three shades inside one perceptual step). Colour REINFORCES;
   the label and the rail position still carry the meaning, so the rail
   is fully usable in monochrome. */
#pg-schedule-v2 .opc-nav-btn[data-opc-cat="all"]          { --opc-cat: var(--acc); }
#pg-schedule-v2 .opc-nav-btn[data-opc-cat="cabinets"]     { --opc-cat: var(--cat-cabinets, var(--acc)); }
#pg-schedule-v2 .opc-nav-btn[data-opc-cat="doors"]        { --opc-cat: var(--cat-doors, var(--acc)); }
#pg-schedule-v2 .opc-nav-btn[data-opc-cat="drawer_boxes"] { --opc-cat: var(--cat-drawer-boxes, var(--acc)); }
#pg-schedule-v2 .opc-nav-btn[data-opc-cat="molding"]      { --opc-cat: var(--cat-molding, var(--acc)); }

/* Colour swatch — the always-visible category cue. Reads at a glance
   even when the item is inactive, which the 4px active bar alone can't
   do (only one item is ever active). */
#pg-schedule-v2 .opc-nav-dot {
    flex: 0 0 auto;
    width: 10px;
    height: 10px;
    border-radius: 2px;
    background: var(--opc-cat);
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--opc-cat) 40%, transparent);
    transition: transform var(--dur-quick, 0.1s) var(--ease-out, ease-out);
}
/* "All" is not a product — a hollow swatch keeps it visually apart from
   the four real categories instead of implying a fifth colour code. */
#pg-schedule-v2 .opc-nav-btn[data-opc-cat="all"] .opc-nav-dot {
    background: transparent;
    box-shadow: inset 0 0 0 2px var(--opc-cat);
}

#pg-schedule-v2 .opc-nav-btn:hover {
    background: color-mix(in srgb, var(--opc-cat) 14%, transparent);
    color: var(--white);
}
#pg-schedule-v2 .opc-nav-btn:hover .opc-nav-dot { transform: scale(1.2); }

/* SELECTED. Four simultaneous cues, because one alone was not carrying:
     1. a 5px solid bar down the leading edge
     2. a stronger hue wash
     3. the label at max weight + max ink + open tracking
     4. an outline ring in the board hue
   The bar is an inset shadow rather than a border-left so the label never
   shifts horizontally on selection. */
#pg-schedule-v2 .opc-nav-btn[data-active="true"] {
    background: color-mix(in srgb, var(--opc-cat) 30%, transparent);
    color: var(--white);
    font-weight: 800;
    letter-spacing: 0.1em;
    box-shadow: inset 5px 0 0 var(--opc-cat),
                inset 0 0 0 1px color-mix(in srgb, var(--opc-cat) 55%, transparent);
}
#pg-schedule-v2 .opc-nav-btn[data-active="true"] .opc-nav-dot { transform: scale(1.3); }
#pg-schedule-v2 .opc-nav-btn:focus-visible {
    outline: 2px solid var(--acc);
    outline-offset: -2px;
}
#pg-schedule-v2 .opc-nav-label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Count badge: tinted pill so the number reads as belonging to its
   category rather than floating loose at the row end. */
#pg-schedule-v2 .opc-nav-n {
    flex: 0 0 auto;
    font: 700 12px/1 var(--ff-mono);
    color: var(--white);
    font-variant-numeric: tabular-nums;
    min-width: 2.5ch;
    padding: 3px 6px;
    border-radius: 3px;
    background: color-mix(in srgb, var(--opc-cat) 22%, transparent);
    text-align: right;
}
#pg-schedule-v2 .opc-nav-btn[data-active="true"] .opc-nav-n {
    background: var(--opc-cat);
    color: var(--acc-ink, var(--white));
}
/* --txt, not --dim: these counts are the rail's actual payload ("how much
   drawer-box work is there?") and were reading as disabled. */
#pg-schedule-v2 .opc-nav-btn[data-active="false"] .opc-nav-n { color: var(--txt); }

/* Empty category: NO opacity dimming on the row.
   History — this rule was `[data-n="0"] { opacity: .45 }`, then 0.62.
   Both were wrong. On a fresh shop every category reads 0, so it faded
   every row (originally including the SELECTED one), and the rail looked
   like four disabled controls. Opacity is the wrong channel for "empty":
   across every UI convention it means unavailable, and these boards are
   always available — an empty Molding board is exactly what you click to
   confirm there is no molding work.

   "Empty" is already stated, precisely, by the count badge reading 0.
   The only cue kept is dropping that badge's tint, so a 0 doesn't carry
   the same filled-pill weight as a live number. Label ink is untouched. */
#pg-schedule-v2 .opc-nav-btn[data-n="0"]:not([data-active="true"]) .opc-nav-n {
    background: transparent;
    color: var(--dim);
}
/* Selected-but-empty keeps the solid pill: "this board is selected and
   has nothing in it" must not look like "this board is unimportant". */
#pg-schedule-v2 .opc-nav-btn[data-active="true"][data-n="0"] .opc-nav-n {
    background: var(--opc-cat);
    color: var(--acc-ink, var(--white));
}

/* Server hasn't shipped `categories` yet — disable rather than lie.
   Failing OPEN would silently show everything under a category label;
   failing CLOSED would show an empty schedule that reads as broken.

   This is the ONE state where fading is correct, because the control
   genuinely cannot be used (pointer-events: none). Raised 0.35 → 0.55 so
   it still reads as a legible label rather than a ghost — and it is a
   temporary condition (an old server build), never the normal case.
   Everything else in this rail stays at full ink. */
#pg-schedule-v2 .opc-nav[data-unavailable="true"] .opc-nav-btn[data-opc-cat]:not([data-opc-cat="all"]) {
    opacity: 0.55;
    pointer-events: none;
}

/* ── Unscheduled tray ──────────────────────────────────────────── */

#pg-schedule-v2 .opc-tray {
    /* Explicit row so the tray keeps its sizing regardless of what sits
       above it — an auto-placed child would take an `auto` track and
       collapse to content height. Row 2 as of PR-SCHED-SEARCH-MOVE
       (2026-08-09): the search box left this rail for the toolbar, so the
       rail is now just [category rail | tray]. */
    grid-row: 2;
    background: var(--sf);
    border-top: 1px solid var(--bdr);
    border-radius: 0;
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow: hidden;
}
#pg-schedule-v2 .opc-tray-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: 28px;
    padding: 0 10px;
    background: var(--bg);
    border-bottom: 1px solid var(--bdr);
    font: 600 10px/1 var(--ff-mono);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--dim);
}
#pg-schedule-v2 .opc-tray-count {
    color: var(--white);
    font-weight: 700;
}
/* PR-SCHED-MANUALJOB (2026-08-08): the "+ Job" action sits inside the 28px
   tray head. It uses the head's own font/color tokens so it scales with the
   design at any zoom, stays a fixed compact height (never taller than the
   row), and the label group truncates first so the button is never clipped. */
#pg-schedule-v2 .opc-tray-head { gap: 8px; }
#pg-schedule-v2 .opc-tray-head-lbl {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
#pg-schedule-v2 .opc-newjob-btn {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    height: 20px;
    padding: 0 9px;
    border: 1px solid var(--acc);
    border-radius: 6px;
    background: var(--acc);
    color: #fff;
    font: 700 10px/1 var(--ff-mono);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    white-space: nowrap;
    cursor: pointer;
}
#pg-schedule-v2 .opc-newjob-btn:hover { filter: brightness(1.1); }

/* ── Outsourced board (PR-SCHED-OUTSOURCED 2026-08-08) ─────────────────
   Rendered into #opc-weeks when [data-mode="outsourced"]. One lane per
   outsourced department (vendor + lead time), holding the manual jobs
   sent out to it. */
#pg-schedule-v2 .opc-os-board {
    display: flex;
    gap: 14px;
    padding: 16px;
    height: 100%;
    align-items: flex-start;
    overflow-x: auto;
    box-sizing: border-box;
}
#pg-schedule-v2 .opc-os-lane {
    flex: 0 0 268px;
    background: var(--pn);
    border: 1px solid var(--bdr);
    border-radius: 12px;
    overflow: hidden;
    max-height: 100%;
    display: flex;
    flex-direction: column;
}
#pg-schedule-v2 .opc-os-lane-head {
    padding: 12px 14px;
    border-bottom: 1px solid var(--bdr);
    position: relative;
}
#pg-schedule-v2 .opc-os-lane-name { display: block; font-weight: 700; color: var(--txt); font-size: 14px; }
#pg-schedule-v2 .opc-os-lane-meta { display: block; color: var(--dim); font-size: 12px; margin-top: 3px; }
#pg-schedule-v2 .opc-os-lane-count {
    position: absolute; top: 12px; right: 14px;
    color: var(--white); font-weight: 700; font-size: 13px; font-family: var(--ff-mono);
}
#pg-schedule-v2 .opc-os-lane-body { padding: 10px 12px; display: flex; flex-direction: column; gap: 8px; overflow-y: auto; }
#pg-schedule-v2 .opc-os-job { border: 1px solid var(--bdr); border-radius: 8px; padding: 9px 11px; background: var(--bg); }
#pg-schedule-v2 .opc-os-job-name { font-weight: 600; color: var(--txt); font-size: 13px; }
#pg-schedule-v2 .opc-os-job-meta { color: var(--dim); font-size: 11px; margin-top: 2px; }
#pg-schedule-v2 .opc-os-job-notes { color: var(--dim); font-size: 11px; margin-top: 4px; font-style: italic; }
#pg-schedule-v2 .opc-os-none { color: var(--dim); font-size: 12px; font-style: italic; padding: 4px 0; }
#pg-schedule-v2 .opc-os-empty { padding: 56px 24px; text-align: center; color: var(--dim); max-width: 540px; margin: 0 auto; }
#pg-schedule-v2 .opc-os-empty h3 { color: var(--txt); margin: 0 0 8px; font-size: 18px; }
#pg-schedule-v2 .opc-os-empty a { color: var(--acc); }

#pg-schedule-v2 .opc-tray-list {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 4px;
}
/* PR-SCHED-CAT: the tray is the app's only unschedule affordance but had
   NO drop feedback — _opcOnDragOver looked for a .opc-day-list, which the
   tray does not contain, so it never received .drop-target. */
#pg-schedule-v2 .opc-tray-list.drop-target {
    background: color-mix(in srgb, var(--acc) 10%, var(--bg));
    outline: 1px dashed var(--acc);
    outline-offset: -2px;
}
#pg-schedule-v2 .opc-tray-empty {
    color: var(--muted);
    font-size: 11px;
    text-align: center;
    padding: 20px 8px;
    font-style: italic;
}

/* ── Week grid ─────────────────────────────────────────────────── */

#pg-schedule-v2 .opc-weeks {
    background: var(--bg);
    overflow: hidden;
    display: flex;
    flex-direction: column;
}
/* PR-SCHED-CAT: grid → flex. Day columns must be able to COLLAPSE
   individually when a category/customer filter hides their contents.
   Under `grid-template-columns: repeat(5, 1fr)` a per-child collapse
   would force the renderer to compute and inline an explicit track list
   — presentational math in JS, which this project forbids. Flex does it
   declaratively via flex-basis on the child. */
#pg-schedule-v2 .opc-week {
    flex: 1 1 auto;
    display: flex;
    min-height: 0;
}
#pg-schedule-v2 .opc-day {
    flex: 1 1 0;
    min-width: 0;
    border-right: 1px solid var(--bdr);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    transition: flex-basis var(--dur-panel, 0.15s) var(--ease-out, ease-out);
}
#pg-schedule-v2 .opc-day:last-child { border-right: none; }

/* Collapsed = zero MATCHING jobs under an active filter. Deliberately
   still rendered and still a live drop target: removing the day from the
   DOM would destroy the drop zone for the very day you most want to drag
   a job onto, and would break the operator's spatial memory of the week. */
#pg-schedule-v2 .opc-day[data-collapsed="true"] {
    flex: 0 0 var(--touch-min, 44px);
    background: color-mix(in srgb, var(--dim) 6%, var(--bg));
}
#pg-schedule-v2 .opc-day[data-collapsed="true"] .opc-day-list,
#pg-schedule-v2 .opc-day[data-collapsed="true"] .opc-cap-bar,
#pg-schedule-v2 .opc-day[data-collapsed="true"] .opc-day-meta { display: none; }
#pg-schedule-v2 .opc-day[data-collapsed="true"] .opc-day-head {
    height: auto;
    flex: 1 1 auto;
    padding: 6px 2px;
}
/* Collapsed column: ONE sideways text run, so `display: block` with
   inline children — the wrapping flex layout used when expanded would
   otherwise rotate three separate fragments independently and the label
   would be unreadable. */
#pg-schedule-v2 .opc-day[data-collapsed="true"] .opc-day-date {
    display: block;
    writing-mode: vertical-rl;
    margin: 0 auto;
    white-space: nowrap;
    width: auto;
}
#pg-schedule-v2 .opc-day[data-collapsed="true"] .opc-day-dow,
#pg-schedule-v2 .opc-day[data-collapsed="true"] .opc-day-md {
    display: inline;
    font-size: var(--opc-fs-day-dow);
    letter-spacing: 0.08em;
}

/* While a card is in flight EVERY collapsed day re-opens, so a filtered
   view never blocks a drop. This is the whole reason days are collapsed
   rather than removed. */
#pg-schedule-v2[data-dragging="true"] .opc-day[data-collapsed="true"] { flex: 1 1 0; }
#pg-schedule-v2[data-dragging="true"] .opc-day[data-collapsed="true"] .opc-day-list { display: block; }
#pg-schedule-v2[data-dragging="true"] .opc-day[data-collapsed="true"] .opc-day-date { writing-mode: horizontal-tb; }

/* Day header — capacity bar above metadata line */
/* Header stays on --pn against the --bg column body. Do NOT "lift" it to
   --sf: in the dark theme --sf and --bg are BOTH #000000, so the header
   would vanish into the list. --pn is #FFFFFF light / #16181C dark, which
   separates from --bg in both directions. */
#pg-schedule-v2 .opc-day-head {
    height: 64px;
    padding: 6px 7px 5px;
    background: var(--pn);
    border-bottom: 1px solid var(--bdr);
    display: flex;
    flex-direction: column;
    align-items: center;      /* centered column titles — see .opc-day-date */
    justify-content: center;
    gap: 3px;
    flex: 0 0 auto;
}
/* The capacity bar must stay full-bleed: its meaning is WIDTH, so a
   centered, content-width fill bar would be a category error. The parent
   now centers its children, so stretch this one back out explicitly. */
#pg-schedule-v2 .opc-day-head .opc-cap-bar { align-self: stretch; }

/* Days already gone. Recede toward the page background so TODAY reads as
   the boundary between done and upcoming rather than as one more
   identical column. Works in both themes because it moves --pn toward
   --bg, and --pn/--bg differ in both. */
#pg-schedule-v2 .opc-day[data-past="true"] .opc-day-head {
    background: color-mix(in srgb, var(--pn) 45%, var(--bg));
}
#pg-schedule-v2 .opc-day[data-past="true"] .opc-day-date { opacity: 0.6; }

/* ── TODAY ─────────────────────────────────────────────────────────
   PR-SCHED-CAT (2026-08-04): today was a 12% --acc wash on the header
   only — the same blue used for selection, focus and every CTA in the
   app, so on the one screen where the date matters most, "today" and
   "the card I just clicked" read identically. Now it gets its own
   signal-orange hue (--cal-today) and marks the WHOLE COLUMN, not just
   the header, so it's findable from across a shop.

   Four redundant cues, so it survives colour-blindness, glare, and a
   greyscale print: a solid top cap, a column wash, bold ink, and a
   literal "TODAY" chip. */
#pg-schedule-v2 .opc-day.is-today {
    background: color-mix(in srgb, var(--cal-today) 9%, transparent);
    /* Inset rather than a real border so the column keeps its width and
       nothing reflows when the date rolls over at midnight. */
    box-shadow: inset 0 4px 0 var(--cal-today),
                inset 1px 0 0 color-mix(in srgb, var(--cal-today) 55%, transparent),
                inset -1px 0 0 color-mix(in srgb, var(--cal-today) 55%, transparent);
}
#pg-schedule-v2 .opc-day-head.today {
    background: color-mix(in srgb, var(--cal-today) 22%, var(--pn));
    border-bottom: 2px solid var(--cal-today);
}
#pg-schedule-v2 .opc-day-head.overload {
    background: color-mix(in srgb, var(--no) 12%, var(--pn));
}
/* Overload on TODAY: keep the today identity (it is still the column you
   navigate by) and let the capacity bar + meta carry the overload alarm,
   rather than two full-bleed washes fighting. */
#pg-schedule-v2 .opc-day-head.today.overload {
    background: color-mix(in srgb, var(--cal-today) 18%, var(--pn));
}
/* Centered two-line title: weekday on its own line, then the date (with
   the TODAY chip beside it, not below — stacking the chip would cost
   another 13px of grid height on exactly one column and break the
   columns' vertical register).
   Achieved by wrapping, not by extra markup: the weekday takes a full
   flex line, so the date + chip flow onto the second. */
#pg-schedule-v2 .opc-day-date {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: baseline;
    gap: 2px 6px;
    width: 100%;
}
#pg-schedule-v2 .opc-day-dow {
    flex: 0 0 100%;
    text-align: center;
    font: 700 var(--opc-fs-day-dow) / 1 var(--ff-ui);
    letter-spacing: 0.11em;
    text-transform: uppercase;
    color: var(--dim);
}
/* The column's scan anchor. Hierarchy comes from WEIGHT + SIZE contrast
   inside a fixed-height header (Lupton, Thinking with Type) rather than
   from extra vertical space, so the week grid keeps its room.
   tabular-nums so "4" and "18" have the same advance width and the
   centred stack doesn't shift between columns. */
#pg-schedule-v2 .opc-day-md {
    font: 700 var(--opc-fs-day-date) / 1 var(--ff-mono);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.01em;
    text-transform: uppercase;
    color: var(--ink);
}
#pg-schedule-v2 .opc-day-head.today .opc-day-dow,
#pg-schedule-v2 .opc-day-head.today .opc-day-md { color: var(--cal-today); }
#pg-schedule-v2 .opc-day-head.today .opc-day-date {
    color: var(--cal-today);
    font-weight: 700;
}
#pg-schedule-v2 .opc-day-head.overload .opc-day-date { color: var(--no); }
#pg-schedule-v2 .opc-day-head.today.overload .opc-day-date { color: var(--cal-today); }

/* The literal word. Redundant with the colour on purpose — this is the
   cue that still works in greyscale, in direct sun, and for a viewer
   with colour-vision deficiency. */
#pg-schedule-v2 .opc-today-chip {
    flex: 0 0 auto;
    align-self: center;
    padding: 2px 5px;
    border-radius: 2px;
    background: var(--cal-today);
    color: var(--cal-today-ink, var(--acc-ink));
    font: 700 var(--opc-fs-chip) / 1 var(--ff-mono);
    letter-spacing: 0.1em;
}
/* Rotated day label in a collapsed column — the chip would be unreadable
   sideways, so the top cap and ink colour carry it there. */
#pg-schedule-v2 .opc-day[data-collapsed="true"] .opc-today-chip { display: none; }
#pg-schedule-v2 .opc-cap-bar {
    position: relative;
    height: 6px;
    background: var(--bdr);
    overflow: hidden;
}
#pg-schedule-v2 .opc-cap-bar > i {
    display: block;
    height: 100%;
    background: var(--ok);
    transition: width 0.3s ease-out, background 0.2s ease-out;
}
#pg-schedule-v2 .opc-cap-bar.warn > i { background: var(--warn); }
#pg-schedule-v2 .opc-cap-bar.no   > i { background: var(--no); }
#pg-schedule-v2 .opc-day-meta {
    font: 500 var(--opc-fs-metric) / 1.2 var(--ff-mono);
    letter-spacing: 0.02em;
    color: var(--dim);
    text-align: center;
    /* Counts repaint on the 30s poll and every WS push — tabular figures
       stop the line jittering as the numbers change. */
    font-variant-numeric: tabular-nums;
}
/* The load % carries the same status colour as the bar above it, so the
   number and the bar always tell the same story. --ok / --warn / --no
   are the app's existing status triad, not new colours.

   Mixed 80% toward --ink because the raw tokens are tuned for FILLS (the
   capacity bar, badges), not for 10px text: --ok and --warn both measure
   only 3.46:1 as text on --pn in the light theme, under the 4.5 floor.
   Mixing toward --ink is theme-correct in both directions — --ink is
   near-black in light and near-white in dark, so this darkens on light
   and lightens on dark. Measured after the mix: light 4.86 / 4.86 / 6.43,
   dark 9.91 / 10.33 / 6.34. */
#pg-schedule-v2 .opc-day-meta .opc-load {
    font-weight: 700;
    color: color-mix(in srgb, var(--ok) 80%, var(--ink));
}
#pg-schedule-v2 .opc-day-meta .opc-load.warn { color: color-mix(in srgb, var(--warn) 80%, var(--ink)); }
#pg-schedule-v2 .opc-day-meta .opc-load.no   { color: color-mix(in srgb, var(--no) 80%, var(--ink)); }
/* An empty day is not "healthy" — grey it rather than reporting 0% in
   success-green, which would read as a good outcome. */
#pg-schedule-v2 .opc-day-meta .opc-load.idle { color: var(--muted); font-weight: 600; }

/* ── Card category chips ───────────────────────────────────────────
   PR-SCHED-CAT (2026-08-04): what's actually IN this job. The letters
   (CAB / DR / BOX / MLD) are load-bearing — colour is a second,
   redundant channel, never the only one. Same --cat-* hues as the rail,
   so a chip on a card and its row in the rail are visibly the same
   thing. */
#pg-schedule-v2 .opc-card-cats {
    display: flex;
    flex-wrap: wrap;
    gap: 3px;
    margin-top: 3px;
}
#pg-schedule-v2 .opc-cat-chip {
    --opc-cat: var(--dim);
    padding: 1px 5px;
    border-radius: 2px;
    font: 700 9px/1.5 var(--ff-mono);
    letter-spacing: 0.06em;
    color: var(--opc-cat);
    /* 12%, not 16%: the tint is the SAME hue as the text, so every extra
       point of tint eats contrast. 12% is the measured ceiling that keeps
       all four categories above AA 4.5:1 in light mode. */
    background: color-mix(in srgb, var(--opc-cat) 12%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--opc-cat) 38%, transparent);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}
#pg-schedule-v2 .opc-cat-chip[data-cat="cabinets"]     { --opc-cat: var(--cat-cabinets, var(--acc)); }
#pg-schedule-v2 .opc-cat-chip[data-cat="doors"]        { --opc-cat: var(--cat-doors, var(--acc)); }
#pg-schedule-v2 .opc-cat-chip[data-cat="drawer_boxes"] { --opc-cat: var(--cat-drawer-boxes, var(--acc)); }
#pg-schedule-v2 .opc-cat-chip[data-cat="molding"]      { --opc-cat: var(--cat-molding, var(--acc)); }

/* When a category filter is active, fade the chips that aren't the one
   you asked for — the matching chip stays lit, so a glance confirms WHY
   each card is on screen. */
#pg-schedule-v2[data-filtered="true"] .opc-cat-chip { opacity: 0.4; }
#pg-schedule-v2 .opc-cat-chip.is-match { opacity: 1; }

#pg-schedule-v2 .opc-day-list {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 4px;
}
#pg-schedule-v2 .opc-day-list.drop-target {
    background: color-mix(in srgb, var(--acc) 8%, var(--bg));
}

/* ── Cards (the Opcenter centerpiece) ──────────────────────────── */

#pg-schedule-v2 .opc-card {
    background: var(--pn);
    border: 1px solid var(--bdr);
    /* Risk stripe widened 2px → 4px: it is the card's single most
       important signal and was the thinnest mark on it. */
    border-left: 4px solid var(--ok);
    border-radius: 0;
    padding: 8px 9px;
    margin-bottom: 5px;
    cursor: pointer;
    /* PR-SCHED-LOC (2026-08-04): 11px → 12px base. The card is read at
       arm's length on a tablet and across the room on a wall monitor;
       11px was below the legible floor for both. */
    font: 400 12px/1.3 var(--ff-ui);
    min-height: 84px;
    display: flex;
    flex-direction: column;
    gap: 3px;
    box-shadow: var(--shadow-1);
    transition: background var(--dur-quick, 0.1s) var(--ease-out, ease-out),
                box-shadow var(--dur-quick, 0.1s) var(--ease-out, ease-out),
                transform var(--dur-quick, 0.1s) var(--ease-out, ease-out);
}
#pg-schedule-v2 .opc-card:hover {
    background: var(--pn-h);
    border-color: var(--bdr-h);
    box-shadow: var(--shadow-3);
    transform: translateY(-1px);
}
#pg-schedule-v2 .opc-card:focus-visible {
    outline: 2px solid var(--acc);
    outline-offset: 1px;
}
#pg-schedule-v2 .opc-card.selected {
    border-color: var(--acc);
    background: color-mix(in srgb, var(--acc) 8%, var(--pn));
}
#pg-schedule-v2 .opc-card.risk-at_risk     { border-left: 3px solid var(--warn); }
#pg-schedule-v2 .opc-card.risk-late        { border-left: 4px solid var(--no); }
#pg-schedule-v2 .opc-card.risk-already_late{ border-left: 4px solid var(--no); }
#pg-schedule-v2 .opc-card.risk-unknown     { border-left: none; padding-left: 10px; }
#pg-schedule-v2 .opc-card.rush             { border-left: 4px solid var(--no); }
#pg-schedule-v2 .opc-card.material-hold    { border-left: 4px solid var(--warn); }

/* PR-SCHED-URGENCY (2026-08-09): 🔴/🟡/🟢 Kanban deadline band.
   ORTHOGONAL to the risk stripe above — risk owns the LEFT edge, so
   urgency frames the other three sides at 2px. Result: a card reads
   "deadline hardness" from its frame and "will-it-be-late" from its
   left bar, two independent signals that never overwrite each other.
   Colour is never the sole carrier: the aria-label spells the tier out
   and flagged rushes still show the RUSH stamp.
     🔴 rush     — ships within 48h / office rush
     🟡 standard — normal timeline (resting state)
     🟢 flexible — stock / filler, no firm deadline */
#pg-schedule-v2 .opc-card.urgency-rush {
    border-top: 2px solid var(--no);
    border-right: 2px solid var(--no);
    border-bottom: 2px solid var(--no);
}
#pg-schedule-v2 .opc-card.urgency-standard {
    border-top: 2px solid var(--warn);
    border-right: 2px solid var(--warn);
    border-bottom: 2px solid var(--warn);
}
#pg-schedule-v2 .opc-card.urgency-flexible {
    border-top: 2px solid var(--ok);
    border-right: 2px solid var(--ok);
    border-bottom: 2px solid var(--ok);
}
/* Keep hover/selected chrome winning over the urgency frame so the
   pointer affordance stays legible. */
#pg-schedule-v2 .opc-card.selected {
    border-color: var(--acc);
}

/* Line 1 — job# + customer + rush stamp */
#pg-schedule-v2 .opc-card-l1 {
    display: flex;
    align-items: baseline;
    gap: 6px;
    font: 600 13px/1.2 var(--ff-mono);
}
/* The job number is the card's identity — it should be findable without
   reading anything else on it. */
#pg-schedule-v2 .opc-card-jobnum { color: var(--white); font-weight: 700; }
#pg-schedule-v2 .opc-card-customer {
    color: var(--txt);
    font-family: var(--ff-ui);
    font-weight: 500;
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
#pg-schedule-v2 .opc-card-rush {
    color: var(--no);
    font-weight: 700;
    letter-spacing: 0.06em;
    font-size: 10px;
}

/* Line 2 — style + piece count */
#pg-schedule-v2 .opc-card-l2 {
    font: 400 11px/1.2 var(--ff-ui);
    color: var(--dim);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ── Line 3 + route strip — the shop-location report ───────────────
   PR-SCHED-LOC (2026-08-04): "where is this job in the shop right now?"
   Line 3 names the department (plus the machine + operator when a labor
   entry is open); the route strip under it draws the whole 7-phase path
   with the work already behind the job filled in. This pair is the
   loudest thing on the card after the job number, because at wall-monitor
   distance it is the only thing anyone is actually reading. */
#pg-schedule-v2 .opc-card-l3 {
    font: 600 12px/1.25 var(--ff-mono);
    color: var(--acc);
    display: flex;
    align-items: center;
    gap: 5px;
    letter-spacing: 0.01em;
}
#pg-schedule-v2 .opc-card-l3 .opc-loc-icon { flex: 0 0 auto; }
#pg-schedule-v2 .opc-card-l3 .opc-loc-txt {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
#pg-schedule-v2 .opc-card-l3.active   { color: var(--acc); }
#pg-schedule-v2 .opc-card-l3.waiting  { color: var(--warn); }
#pg-schedule-v2 .opc-card-l3.complete { color: var(--ok); }
#pg-schedule-v2 .opc-card-l3.idle     { color: var(--muted); }
#pg-schedule-v2 .opc-card-l3 .opc-age {
    margin-left: auto;
    flex: 0 0 auto;
    color: var(--dim);
    font-weight: 500;
    font-size: 11px;
    font-variant-numeric: tabular-nums;
}

/* The 7-segment route bar. Fills left-to-right as the job moves through
   the shop. Never the sole carrier of any fact: the active phase is named
   in line 3, the count is printed at the right, and each segment has a
   title tooltip — so the strip survives greyscale and colour-vision
   deficiency. */
#pg-schedule-v2 .opc-card-route {
    display: flex;
    align-items: center;
    gap: 6px;
}
#pg-schedule-v2 .opc-route-bar {
    display: flex;
    flex: 1 1 auto;
    gap: 2px;
    height: 7px;
}
#pg-schedule-v2 .opc-ph {
    flex: 1 1 0;
    border-radius: 1px;
    background: color-mix(in srgb, var(--dim) 22%, transparent);
    box-shadow: inset 0 0 0 1px var(--bdr);
}
#pg-schedule-v2 .opc-ph.done {
    background: var(--ok);
    box-shadow: none;
}
#pg-schedule-v2 .opc-ph.in_progress {
    background: var(--acc);
    box-shadow: 0 0 0 1px var(--acc);
}
#pg-schedule-v2 .opc-route-count {
    flex: 0 0 auto;
    font: 600 10px/1 var(--ff-mono);
    color: var(--dim);
    font-variant-numeric: tabular-nums;
}

/* Line 4 — ship date + risk badge */
#pg-schedule-v2 .opc-card-l4 {
    display: flex;
    align-items: center;
    gap: 6px;
    font: 500 11px/1.2 var(--ff-mono);
    color: var(--dim);
    font-variant-numeric: tabular-nums;
}
#pg-schedule-v2 .opc-card-l4 .opc-ship   { color: var(--txt); }
#pg-schedule-v2 .opc-card-l4 .opc-risk {
    margin-left: auto;
    padding: 1px 5px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    font-size: 10px;
    border: 1px solid currentColor;
}
#pg-schedule-v2 .opc-card-l4 .opc-risk.on_track    { color: var(--ok); }
#pg-schedule-v2 .opc-card-l4 .opc-risk.at_risk     { color: var(--warn); }
#pg-schedule-v2 .opc-card-l4 .opc-risk.late        { color: var(--no); }
#pg-schedule-v2 .opc-card-l4 .opc-risk.already_late{ color: var(--no); font-weight: 700; }
#pg-schedule-v2 .opc-card-l4 .opc-risk.unknown     { color: var(--muted); }

/* Material hold inner band (appears at card bottom when hold is set) */
#pg-schedule-v2 .opc-card-mat {
    background: color-mix(in srgb, var(--warn) 18%, transparent);
    color: var(--warn);
    font: 700 9px/1 var(--ff-mono);
    letter-spacing: 0.06em;
    padding: 3px 6px;
    margin: 4px -8px -6px;
    border-top: 1px solid var(--warn);
}

/* PR-AUDIT-2 (2026-05-25): andon BLOCKED inner band — same geometry as
   .opc-card-mat but the no-saturation token. Stacks below the mat band
   when both are present (rare: held + blocked is a serious double-
   trouble signal the operator should see). */
#pg-schedule-v2 .opc-card-andon {
    background: color-mix(in srgb, var(--no) 18%, transparent);
    color: var(--no);
    font: 700 9px/1 var(--ff-mono);
    letter-spacing: 0.06em;
    padding: 3px 6px;
    margin: 4px -8px -6px;
    border-top: 1px solid var(--no);
}
#pg-schedule-v2 .opc-card-mat + .opc-card-andon {
    margin-top: 0;
}

/* Drag states (preserve existing schedule3.js animation classes) */
#pg-schedule-v2 .opc-card.schedp-dragging {
    opacity: 0.4;
    transform: scale(1.02);
    transition: transform 0.15s ease-out, opacity 0.15s ease-out;
}
#pg-schedule-v2 .opc-card.schedp-drop-flash {
    animation: opc-flash 0.6s ease-out;
}
@keyframes opc-flash {
    0%   { background: color-mix(in srgb, var(--ok) 30%, var(--pn)); }
    100% { background: var(--pn); }
}

/* ── Right pane (3-tab inspector) ──────────────────────────────── */

#pg-schedule-v2 .opc-detail {
    background: var(--sf);
    border-left: 1px solid var(--bdr);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
#pg-schedule-v2 .opc-detail-empty {
    flex: 1 1 auto;
    padding: 14px;
    overflow-y: auto;
}
#pg-schedule-v2 .opc-detail-empty-head {
    font: 600 10px/1 var(--ff-mono);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--dim);
    margin-bottom: 8px;
}
#pg-schedule-v2 .opc-detail-empty-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: 6px 0;
    border-bottom: 1px solid var(--bdr);
    font: 400 12px/1.4 var(--ff-ui);
}
#pg-schedule-v2 .opc-detail-empty-row:last-child { border-bottom: none; }
#pg-schedule-v2 .opc-detail-empty-row .opc-label { color: var(--dim); }
#pg-schedule-v2 .opc-detail-empty-row .opc-value { color: var(--white); font-weight: 500; }
#pg-schedule-v2 .opc-detail-empty-row .opc-value.warn { color: var(--warn); font-weight: 600; }

#pg-schedule-v2 .opc-detail-body { display: none; flex: 1 1 auto; flex-direction: column; overflow: hidden; }
#pg-schedule-v2 .opc-detail-body.active { display: flex; }

#pg-schedule-v2 .opc-detail-head {
    padding: 10px 14px;
    border-bottom: 1px solid var(--bdr);
    background: var(--pn);
    flex: 0 0 auto;
}
#pg-schedule-v2 .opc-detail-jobnum { font: 700 14px/1 var(--ff-mono); color: var(--white); }
#pg-schedule-v2 .opc-detail-customer { font: 500 13px/1.3 var(--ff-ui); color: var(--txt); margin-top: 4px; }
#pg-schedule-v2 .opc-detail-risk {
    margin-top: 6px;
    font: 400 11px/1.2 var(--ff-ui);
    color: var(--dim);
}

#pg-schedule-v2 .opc-tabs {
    display: flex;
    height: 32px;
    background: var(--sf);
    border-bottom: 1px solid var(--bdr);
    flex: 0 0 auto;
}
#pg-schedule-v2 .opc-tab {
    background: transparent;
    border: none;
    border-bottom: 2px solid transparent;
    padding: 0 14px;
    font: 600 10px/1 var(--ff-mono);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--dim);
    cursor: pointer;
    border-radius: 0;
}
#pg-schedule-v2 .opc-tab[aria-selected="true"] {
    color: var(--acc);
    border-bottom-color: var(--acc);
}
#pg-schedule-v2 .opc-tab:hover { color: var(--txt); }

#pg-schedule-v2 .opc-tab-panel {
    flex: 1 1 auto;
    padding: 12px 14px;
    overflow-y: auto;
}
#pg-schedule-v2 .opc-tab-panel.hidden { display: none; }
#pg-schedule-v2 .opc-detail-field {
    margin-bottom: 12px;
    font: 400 12px/1.4 var(--ff-ui);
}
#pg-schedule-v2 .opc-detail-field label {
    display: block;
    font: 600 10px/1 var(--ff-mono);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--dim);
    margin-bottom: 4px;
}
#pg-schedule-v2 .opc-detail-field input,
#pg-schedule-v2 .opc-detail-field select {
    width: 100%;
    padding: 6px 8px;
    background: var(--inp, var(--pn));
    border: 1px solid var(--bdr);
    border-radius: 0;
    color: var(--txt);
    font: 400 12px/1.2 var(--ff-mono);
}
#pg-schedule-v2 .opc-detail-actions {
    display: flex;
    gap: 6px;
    margin-top: 12px;
}
#pg-schedule-v2 .opc-detail-actions button {
    padding: 6px 12px;
    background: var(--pn);
    border: 1px solid var(--bdr);
    border-radius: 0;
    color: var(--txt);
    font: 500 11px/1 var(--ff-mono);
    cursor: pointer;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
#pg-schedule-v2 .opc-detail-actions button.primary {
    background: var(--acc);
    color: var(--acc-ink, var(--white));
    border-color: var(--acc);
}

/* ── Bottom status bar (the Opcenter signature) ────────────────── */

#pg-schedule-v2 .opc-statusbar {
    height: 28px;
    padding: 0 14px;
    background: var(--sf);
    border-top: 1px solid var(--bdr);
    display: flex;
    align-items: center;
    gap: 14px;
    font: 500 11px/1 var(--ff-mono);
    color: var(--dim);
    flex: 0 0 auto;
}
#pg-schedule-v2 .opc-statusbar-field {
    display: flex;
    align-items: center;
    gap: 6px;
}
#pg-schedule-v2 .opc-statusbar-field strong { color: var(--white); font-weight: 600; }
#pg-schedule-v2 .opc-statusbar-sep {
    width: 1px;
    height: 14px;
    background: var(--bdr);
}
#pg-schedule-v2 .opc-statusbar-field.clickable { cursor: pointer; }
#pg-schedule-v2 .opc-statusbar-field.clickable:hover strong { color: var(--acc); }
#pg-schedule-v2 .opc-statusbar-warn { color: var(--warn) !important; font-weight: 600; }
#pg-schedule-v2 .opc-statusbar-no   { color: var(--no) !important; font-weight: 600; }
#pg-schedule-v2 .opc-live-dot {
    display: inline-block;
    width: 6px;
    height: 6px;
    background: var(--ok);
    border-radius: 50%;
    margin-right: 4px;
    animation: opc-pulse 2s infinite;
}
#pg-schedule-v2 .opc-live-dot.stale { background: var(--warn); }
#pg-schedule-v2 .opc-live-dot.dead  { background: var(--no); animation: none; }
@keyframes opc-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.35; }
}
#pg-schedule-v2 .opc-clock { color: var(--white); font-weight: 600; margin-left: auto; }

/* ── Reduced motion ────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
    #pg-schedule-v2 .opc-card,
    #pg-schedule-v2 .opc-cap-bar > i,
    #pg-schedule-v2 .opc-card.schedp-dragging,
    #pg-schedule-v2 .opc-card.schedp-drop-flash,
    #pg-schedule-v2 .opc-live-dot,
    /* PR-SCHED-CAT-BOARDS: the board-switch deal-in (every animated
       target), the rail's swatch scale, and the day-collapse width
       transition. Delays are zeroed too — a 160ms stagger would still be
       perceptible motion even with the travel time collapsed. */
    #pg-schedule-v2.board-switching .opc-week > .opc-day,
    #pg-schedule-v2.board-switching .opc-weeks > .opc-week-empty-hint,
    #pg-schedule-v2.board-switching .opc-tray-list > *,
    #pg-schedule-v2.board-switching .opc-board-name,
    #pg-schedule-v2 .opc-nav-btn,
    #pg-schedule-v2 .opc-nav-dot,
    #pg-schedule-v2 .opc-day {
        animation-duration: 0.01ms !important;
        animation-delay: 0ms !important;
        transition-duration: 0.01ms !important;
    }
}

/* PR-AUDIT-2 (2026-05-25): consolidated zero-orders empty state for
   the week grid. Replaces the prior "empty × 5 day columns" which
   read like a bug on fresh shops or quiet weeks. */
/* PR-SCHED-CAT: `grid-column: 1/-1` → flex basis. .opc-week is now a
   flex row (see the collapse rules above), where grid-column is inert. */
#pg-schedule-v2 .opc-week-empty {
    flex: 1 1 100%;
    align-self: center;
    text-align: center;
    padding: 60px 40px;
    margin: 0 auto;
    color: var(--dim);
    max-width: 600px;
}
#pg-schedule-v2 .opc-week-empty-head {
    font: 600 18px/1.3 var(--ff-ui);
    color: var(--ink);
    margin-bottom: 8px;
}
#pg-schedule-v2 .opc-week-empty-sub {
    font: 400 13px/1.5 var(--ff-ui);
    color: var(--dim);
}

/* PR-AUDIT-35 (2026-05-26): subtle empty-week hint that sits ABOVE
   the 5-day grid instead of replacing it. Lets the operator keep
   clicking ◀/▶ to scan history + future without the calendar
   disappearing on quiet weeks. */
/* Was a flat grey band that read as dead space. An accent-tinted strip
   with a left rule reads as an intentional prompt instead. */
#pg-schedule-v2 .opc-week-empty-hint {
    padding: 9px 14px;
    color: var(--txt);
    font: 500 13px/1.4 var(--ff-ui);
    background: color-mix(in srgb, var(--acc) 8%, transparent);
    border-left: 3px solid var(--acc);
    margin-bottom: 8px;
    text-align: center;
}
#pg-schedule-v2 .opc-week-empty-hint .opc-week-empty-sub { color: var(--dim); }
#pg-schedule-v2 .opc-week-empty-hint .opc-week-empty-sub {
    font: 400 12px/1.4 var(--ff-ui);
    color: var(--dim);
    margin-left: 4px;
}

/* ── Print mode ────────────────────────────────────────────────── */
@media print {
    #pg-schedule-v2 .opc-toolbar,
    #pg-schedule-v2 .opc-statusbar,
    #pg-schedule-v2 .opc-detail { display: none !important; }
    #pg-schedule-v2 .opc-shell { grid-template-columns: 1fr; }
    /* PR-SCHED-CAT: hide the whole left column (rail + tray), not just
       .opc-tray — the tray is now nested inside .opc-leftrail, so hiding
       it alone would leave an empty rail column on the printout. */
    #pg-schedule-v2 .opc-leftrail { display: none; }
    /* Collapsed days must print full-width — a filtered screen view must
       not silently produce 44px slivers on paper. */
    #pg-schedule-v2 .opc-day[data-collapsed="true"] { flex: 1 1 0; }
    #pg-schedule-v2 .opc-day[data-collapsed="true"] .opc-day-list { display: block; }
    #pg-schedule-v2 .opc-day[data-collapsed="true"] .opc-day-date { writing-mode: horizontal-tb; }
    #pg-schedule-v2 .opc-card {
        page-break-inside: avoid;
        /* PR-AUDIT-2 (2026-05-25): print backgrounds use the canonical
           --bg / --ink tokens with fallback to printer-safe defaults.
           Prior `white`/`black` keywords violated the strict-color-
           tokens rule (feedback_strict_color_tokens). */
        background: var(--bg, #fff) !important;
        color: var(--ink, #000) !important;
    }
}

/* ── Responsive ladder ─────────────────────────────────────────────
   PR-SCHED-CAT (2026-08-04): replaces the single `@media (max-width:820px)`
   rule, which was the ONLY width breakpoint in this file and left the page
   unusable on a shop tablet. Two hard bugs it caused:

     1. `.opc-week` stacked its 5 day rows vertically inside
        `.opc-weeks { overflow: hidden }` with no scroll — Thursday and
        Friday were rendered but physically unreachable.
     2. The 821-1180px band (iPad Pro 11" portrait = 834px) kept the full
        three-column grid: 834 - 220 - 280 leaves 334px for five day
        columns, ~67px each.

   Breakpoints reuse the shell's existing 980/640 vocabulary
   (shell.css:6223) so the app speaks one responsive language. */

/* ≤1180px — drop the inspector first. It is the least-critical column and
   the detail pane is reachable by tapping a card on any width. */
@media (max-width: 1180px) {
    #pg-schedule-v2 .opc-shell {
        grid-template-columns: clamp(212px, 20vw, 264px) 1fr;
    }
    #pg-schedule-v2 .opc-detail { display: none; }
}

/* ≤980px — left column becomes a top band; the week becomes a scrolling
   vertical stack. Matches the .lib-ed-shell-nav collapse at shell.css:6223. */
@media (max-width: 980px) {
    #pg-schedule-v2 .opc-shell {
        grid-template-columns: 1fr;
        grid-template-rows: auto 1fr;
    }
    #pg-schedule-v2 .opc-leftrail {
        /* Two rows, matching the desktop track count — chip strip (row 1),
           tray (row 2). The search box left this rail for the toolbar
           (PR-SCHED-SEARCH-MOVE 2026-08-09); the tray still pins to its
           explicit row so it never collapses into an unsized implicit
           track on the tablet width the shop floor uses. */
        grid-template-rows: auto minmax(96px, 140px);
        border-right: none;
        border-bottom: 1px solid var(--bdr);
    }
    /* Rail → horizontal scroll-snap chip strip, mirroring the
       .lib-ed-shell-nav collapse at shell.css:6223. */
    #pg-schedule-v2 .opc-nav {
        flex-direction: row;
        flex-wrap: nowrap;
        overflow-x: auto;
        overflow-y: hidden;
        gap: 6px;
        padding: 6px;
        scroll-snap-type: x proximity;
        -webkit-overflow-scrolling: touch;
        border-bottom: 1px solid var(--bdr);
    }
    #pg-schedule-v2 .opc-nav-btn {
        flex: 0 0 auto;
        white-space: nowrap;
        min-height: var(--touch-comfortable, 56px);
        scroll-snap-align: start;
        border: 1px solid var(--bdr);
    }
    /* A left inset bar is meaningless once the items sit side by side. */
    #pg-schedule-v2 .opc-nav-btn[data-active="true"] {
        box-shadow: inset 0 -4px 0 var(--opc-cat);
        border-color: var(--opc-cat);
    }
    /* Tray survives as a horizontal card strip, so drag-to-unschedule
       keeps working at every width. */
    #pg-schedule-v2 .opc-tray-list {
        display: flex;
        flex-direction: row;
        overflow-x: auto;
        overflow-y: hidden;
        -webkit-overflow-scrolling: touch;
    }
    #pg-schedule-v2 .opc-tray-list .opc-card { flex: 0 0 200px; margin: 0 4px 0 0; }

    /* THE FIX: the outer pane scrolls, so stacked days are reachable. */
    #pg-schedule-v2 .opc-weeks { overflow-y: auto; -webkit-overflow-scrolling: touch; }
    #pg-schedule-v2 .opc-week  { flex-direction: column; }
    #pg-schedule-v2 .opc-day   {
        flex: 0 0 auto;
        min-height: 160px;
        border-right: none;
        border-bottom: 1px solid var(--bdr);
    }
    /* Nested-scroller trap: with the outer pane now scrolling, an inner
       scroller means a finger landing inside a day column scrolls that
       column instead of the page. Days grow to content; the outer pane
       owns scrolling. */
    #pg-schedule-v2 .opc-day-list { overflow-y: visible; }
    /* Vertical text is meaningless once days are stacked rows. */
    #pg-schedule-v2 .opc-day[data-collapsed="true"] { flex: 0 0 auto; min-height: 0; }
    #pg-schedule-v2 .opc-day[data-collapsed="true"] .opc-day-date { writing-mode: horizontal-tb; }
}

/* ≤640px — the two 28px mono bands wrap instead of clipping their tails. */
@media (max-width: 640px) {
    #pg-schedule-v2 .opc-infobar,
    #pg-schedule-v2 .opc-statusbar {
        flex-wrap: wrap;
        height: auto;
        min-height: 28px;
        row-gap: 2px;
        padding: 4px 14px;
    }
    #pg-schedule-v2 .opc-infobar-btl,
    #pg-schedule-v2 .opc-clock { margin-left: 0; }
}

/* ══════════════════════════════════════════════════════════════════════
   PR-SCHED-MODE (2026-08-08) — Planning Board and Live Status.
   A mode axis on top of the week/month view. Both modes share the
   department rail and filter (orientation locked); only the center board
   and its visual treatment change. Mode lives in [data-mode] on
   #pg-schedule-v2, flipped by _schedpSetMode.
   ══════════════════════════════════════════════════════════════════════ */

/* The mode toggle (primary control, sits first in the toolbar). */
.opc-modeseg {
    display: inline-flex; gap: 2px; padding: 3px; border-radius: 10px;
    background: var(--bg, #0e1116); border: 1px solid var(--bdr, #2a2f3a); align-self: center;
}
.opc-mode-btn {
    display: inline-flex; flex-direction: column; align-items: flex-start; gap: 1px;
    padding: 5px 14px; min-height: var(--touch-min, 44px); justify-content: center;
    border: 1px solid transparent; border-radius: 8px; background: transparent;
    color: var(--dim, #8b94a3); cursor: pointer; font-family: var(--ff-ui, inherit);
    transition: background var(--dur-quick, 120ms) var(--ease-out, ease),
                color var(--dur-quick, 120ms) var(--ease-out, ease),
                border-color var(--dur-quick, 120ms) var(--ease-out, ease);
}
.opc-mode-btn:hover { color: var(--txt, #d5dae3); }
.opc-mode-name { font-size: 13px; font-weight: 700; line-height: 1.1; white-space: nowrap; }
.opc-mode-hint { font-size: 10px; font-weight: 500; letter-spacing: .02em; opacity: .8; white-space: nowrap; }

/* Active = the mode you are in. Planning is cool/blue (deliberate,
   decision-making); Live is warm/green (heartbeat, floor). The subtle
   colour difference is the instant "which mode am I in" cue. */
.opc-mode-btn[data-schedp-mode="planning"][aria-selected="true"] {
    background: color-mix(in srgb, var(--acc, #4c8dff) 22%, transparent);
    color: var(--white, #fff); border-color: color-mix(in srgb, var(--acc, #4c8dff) 55%, transparent);
}
.opc-mode-btn[data-schedp-mode="live"][aria-selected="true"] {
    background: color-mix(in srgb, var(--ok, #35c48a) 22%, transparent);
    color: var(--white, #fff); border-color: color-mix(in srgb, var(--ok, #35c48a) 55%, transparent);
}

/* Operators (shop-floor) only watch the floor: hide the Planning toggle
   for them; the JS forces Live mode. */
body[data-role="shop_floor"] .opc-mode-btn[data-schedp-mode="planning"] { display: none; }

/* Whole-board top strip in the ACTIVE DEPARTMENT's colour (mode is
   signalled separately by the toggle's blue/green). --opc-board resolves
   to --opc-dept-color, so this strip repaints as you change department. */
#pg-schedule-v2 .opc-infobar {
    box-shadow: inset 0 2px 0 0 color-mix(in srgb, var(--opc-board) 65%, transparent);
}
/* In Live Status, each column header underlines in the department colour
   when a single department is selected — so a filtered live board reads
   as that department at a glance. */
#pg-schedule-v2[data-mode="live"] .slive-col-head {
    border-bottom-color: color-mix(in srgb, var(--opc-board) 40%, var(--bdr, #2a2f3a));
}
/* Planning: darker, calmer day columns (a plan-on-paper feel). */
#pg-schedule-v2[data-mode="planning"] .opc-day {
    background: color-mix(in srgb, var(--bg, #0e1116) 55%, transparent);
}
/* Week/month sub-views and the week navigator are Planning-only. */
#pg-schedule-v2[data-mode="live"] [data-opc-view],
#pg-schedule-v2[data-mode="live"] .opc-weeknav { display: none !important; }
#pg-schedule-v2[data-mode="live"] .opc-tray { display: none; }

/* One-line orientation helper (first couple of times, then dismissed). */
.opc-mode-hintbar {
    display: flex; align-items: center; gap: 10px; padding: 7px 14px;
    background: color-mix(in srgb, var(--acc, #4c8dff) 10%, var(--bg, #0e1116));
    border-bottom: 1px solid var(--bdr, #2a2f3a);
    font-size: 12.5px; color: var(--txt, #d5dae3);
}
.opc-mode-hintbar[hidden] { display: none; }
.opc-mode-hintbar-text strong { color: var(--white, #fff); }
.opc-mode-hintbar-dot { opacity: .5; margin: 0 6px; }
.opc-mode-hintbar-x {
    margin-left: auto; min-width: 32px; min-height: 32px;
    border: none; border-radius: 6px; background: transparent;
    color: var(--dim, #8b94a3); cursor: pointer; font-size: 13px;
}
.opc-mode-hintbar-x:hover { background: color-mix(in srgb, var(--white,#fff) 8%, transparent); color: var(--white,#fff); }

/* LIVE STATUS BOARD — jobs grouped by area / progress. */
.slive-board {
    display: flex; gap: 12px; align-items: flex-start; overflow-x: auto;
    padding: 12px; height: 100%; box-sizing: border-box;
}
.slive-col {
    flex: 0 0 240px; display: flex; flex-direction: column; max-height: 100%;
    background: color-mix(in srgb, var(--bg, #0e1116) 40%, transparent);
    border: 1px solid var(--bdr, #2a2f3a); border-radius: 10px; overflow: hidden;
}
.slive-col-head {
    display: flex; align-items: center; gap: 8px; padding: 9px 12px;
    font-family: var(--ff-ui, inherit); font-weight: 700; font-size: 12px;
    letter-spacing: .04em; text-transform: uppercase; color: var(--txt, #d5dae3);
    border-bottom: 1px solid var(--bdr, #2a2f3a);
    background: color-mix(in srgb, var(--bg, #0e1116) 70%, transparent);
}
.slive-col-n {
    margin-left: auto; font-family: var(--ff-mono, monospace); font-size: 12px;
    color: var(--dim, #8b94a3); background: color-mix(in srgb, var(--white,#fff) 6%, transparent);
    border-radius: 999px; padding: 1px 8px;
}
.slive-col[data-kind="blocked"] .slive-col-head { color: var(--warn, #e0a03a); }
.slive-col[data-kind="done"]    .slive-col-head { color: var(--dim, #8b94a3); }
.slive-col-list { display: flex; flex-direction: column; gap: 8px; padding: 10px; overflow-y: auto; }
.slive-empty { padding: 14px 10px; color: var(--dim,#8b94a3); font-size: 11.5px; font-style: italic; text-align: center; }

.slive-card {
    border: 1px solid var(--bdr, #2a2f3a); border-left: 3px solid var(--dim, #8b94a3);
    border-radius: 8px; padding: 9px 10px;
    background: color-mix(in srgb, var(--white,#fff) 4%, var(--bg,#0e1116)); cursor: pointer;
}
.slive-card:hover { border-color: var(--acc, #4c8dff); }
.slive-card[data-risk="at_risk"]      { border-left-color: var(--warn, #e0a03a); }
.slive-card[data-risk="late"],
.slive-card[data-risk="already_late"] { border-left-color: var(--no, #e5544b); }
.slive-card[data-risk="on_track"]     { border-left-color: var(--ok, #35c48a); }
.slive-card-top { display: flex; align-items: baseline; gap: 6px; }
.slive-deptdot { flex: 0 0 auto; width: 8px; height: 8px; border-radius: 2px; align-self: center; box-shadow: 0 0 0 1px color-mix(in srgb, #000 25%, transparent); }
.slive-cust { font-weight: 700; font-size: 13px; color: var(--white,#fff); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.slive-job  { margin-left: auto; font-family: var(--ff-mono,monospace); font-size: 11px; color: var(--dim,#8b94a3); }
.slive-where { font-size: 11.5px; color: var(--txt,#d5dae3); margin-top: 3px; }
.slive-where b { color: var(--white,#fff); font-weight: 600; }
.slive-ago  { font-family: var(--ff-mono,monospace); font-size: 10.5px; color: var(--dim,#8b94a3); margin-top: 2px; }

.slive-badges { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.slive-badge { font-size: 10px; font-weight: 700; letter-spacing: .03em; padding: 2px 7px; border-radius: 999px; text-transform: uppercase; white-space: nowrap; }
.slive-badge[data-b="live"]    { background: color-mix(in srgb, var(--ok,#35c48a) 22%, transparent);  color: var(--ok,#35c48a); }
.slive-badge[data-b="waiting"] { background: color-mix(in srgb, var(--warn,#e0a03a) 18%, transparent); color: var(--warn,#e0a03a); }
.slive-badge[data-b="blocked"] { background: color-mix(in srgb, var(--no,#e5544b) 20%, transparent);   color: var(--no,#e5544b); }
.slive-badge[data-b="hold"]    { background: color-mix(in srgb, var(--no,#e5544b) 20%, transparent);   color: var(--no,#e5544b); }

/* Live pulse on actively-worked cards — a quiet heartbeat, Live mode only. */
.slive-card[data-active="true"] { border-left-color: var(--ok, #35c48a); animation: slive-pulse 2.4s var(--ease-out, ease) infinite; }
.slive-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--ok,#35c48a); margin-right: 5px; vertical-align: middle; animation: slive-blink 1.6s ease-in-out infinite; }
@keyframes slive-pulse { 0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--ok,#35c48a) 45%, transparent); } 50% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--ok,#35c48a) 0%, transparent); } }
@keyframes slive-blink { 0%,100% { opacity: 1; } 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) {
    .slive-card[data-active="true"] { animation: none; }
    .slive-dot { animation: none; }
}
@media (max-width: 980px) {
    .opc-mode-btn { min-height: var(--touch-comfortable, 56px); }
    .slive-col { flex-basis: 82vw; }
}
