/* ═══════════════ Vendor Tracker design system, applied to Scheduling ═══════════════
 *
 * `app.css` is AR Pro's stylesheet, ported verbatim — navy #16277A, gold
 * #BE9E74, the system UI font, pill buttons. Vendor Tracker
 * (`../../../Vendor Tracker/vendor-tracker`) carries the newer ZUS house
 * style: Poppins, navy #223488 with a tan accent, a slate ink ramp, 16px
 * cards on a barely-there shadow, and the real ZUS logomark instead of a
 * lettered "Z" tile. This file moves Scheduling onto that language.
 *
 * It is a layer, not a rewrite. Loaded last, it overrides `app.css`'s design
 * tokens in place, which re-skins every `var(--…)` call site in that
 * 889-line sheet at once — and leaves the port itself untouched, so a future
 * diff against AR Pro's copy still reads cleanly and the whole adaptation
 * can be reverted by deleting one <link>. What it can't do through tokens
 * alone (typography, button geometry, the brand block) is the short list of
 * explicit rules at the bottom.
 *
 * Values are Vendor Tracker's, traced to their source:
 *   colours + radii + shadow  tailwind.config.ts
 *   splash / topbar chrome    src/app/globals.css, src/app/page.tsx
 *   card / button / badge     src/components/ui/{Card,Button,Badge}.tsx
 *
 * Two of the old tokens needed no change at all — `--navy2` was already
 * #283C9D (Vendor Tracker's `blue`) and `--gold-l` already #EFD9BB (its
 * `cream`). The two design systems share an ancestor; this is less a
 * repaint than a reconciliation.
 */

/* ── Poppins ──
 * The latin subset only (u+00??, u+2000-206f), lifted out of Vendor
 * Tracker's own Next build so the app keeps working with no network — an
 * Electron window on an outlet's machine cannot depend on fonts.googleapis.
 * 4 weights x ~8KB. Poppins carries 400/600/700/800; there is no 500, and
 * asking for one gets a synthesised weight, so the rules below round the
 * ported sheet's 500s to 600 rather than let the renderer guess. */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/poppins-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/poppins-600.woff2') format('woff2');
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/poppins-700.woff2') format('woff2');
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('../fonts/poppins-800.woff2') format('woff2');
}

:root {
  --font-sans: 'Poppins', -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;

  /* ── brand ──
   * AR Pro's navy is #16277A; Vendor Tracker's is #223488, a touch lighter
   * and warmer. `--gold` becomes `tan` — the accent is no longer metallic,
   * which is the most visible single change: every gold rule in app.css
   * (sidebar active bar, brand tile, avatars, gantt bars) moves with it. */
  --navy:#223488;                     /* tailwind `navy` */
  --navy2:#283C9D;                    /* tailwind `blue` — unchanged */
  --wrapped:#0D1747;                  /* hub-stage gradient mid-stop */
  --gold:#A9835A;                     /* tailwind `tan` */
  --gold-d:#D9A94A;                   /* tailwind `tan-line` */
  --gold-l:#EFD9BB;                   /* tailwind `cream` — unchanged */
  --gold-p:#F7F2EA;                   /* the palest tan wash, for fills */

  /* ── status ──
   * Vendor Tracker's `eco`/`gold` ramps, which carry outlet-ops's real
   * status-ok / status-soon values, plus its one red (#d03b3b — softer than
   * AR Pro's #EC0D40 alarm red, and the same hue the vendor Badge uses). */
  --ok:#0CA30C; --ok2:#157A17;
  --warn:#FAB219; --yel:#FCD88A;
  --err:#D03B3B; --err-bg:#FBEAEA;

  /* ── ink + surface ──
   * The `base` slate ramp. app.css's grey tokens are named by lightness, so
   * they map straight across: g50 is the page, g100/g200 are lines, g400
   * and g600 are muted text, g800 is body copy. Everything gains the ramp's
   * slight blue cast, which is what stops the navy chrome from looking
   * pasted onto a neutral-grey app. */
  --g50:#FAFAFA;                      /* base-50   surface-page */
  --g100:#E4E6F0;                     /* base-200  line */
  --g200:#C7CBE0;                     /* base-300 */
  --g300:#9A9FC2;                     /* base-400 */
  --g400:#767A99;                     /* base-500  ink-faint */
  --g600:#4B4F70;                     /* base-700  ink-soft */
  --g800:#181C30;                     /* base-900  ink */
  --tint:#F4F5F9;                     /* base-100  surface-tint (new) */

  /* ── shape ──
   * Cards are 16px (`rounded-2xl`), controls 10px, near Tailwind's
   * `rounded-lg`. The pill survives only where it is a badge. */
  --rsm:8px; --rmd:10px; --rlg:16px; --rxl:22px; --rpill:100px;

  /* ── shadow ──
   * `shadow-card` is deliberately almost invisible: Vendor Tracker separates
   * surfaces with a 1px `base-200` border and lets the shadow do nothing but
   * lift. --sr/--sf keep the same ink for things that genuinely float. */
  --sc:0 1px 2px rgba(16,24,40,.04), 0 1px 3px rgba(16,24,40,.03);
  --sr:0 2px 6px rgba(16,24,40,.06), 0 4px 14px rgba(16,24,40,.05);
  --sf:0 10px 34px rgba(16,24,40,.14);
}

/* ── typography ──
 * app.css repeats the system stack in ten rules (body, .nb, .btn, .inp,
 * .sel, .pgb, .fi/.fs/.ft, .tab, .bi-tab, .bi-search-input). They are listed
 * out rather than caught with a `*` so the monospace rules — .mono,
 * .pov-chip, the SQL editor, the cheatsheet modal — keep their own face,
 * which is load-bearing: shift text and outlet codes line up in columns
 * because the digits are fixed width. */
body,
.btn, .tab, .bi-tab,
.inp, .sel, .fi, .fs, .ft, .pgb, .bi-search-input,
.rc-time {
  font-family: var(--font-sans);
}
/* Poppins has no 500. Round up: 400 would go light where the ported sheet
   asked for emphasis. */
.tab, .bi-tab { font-weight: 600; }
/* Poppins runs wider than Segoe UI at the same size — the tighter tracking
   Vendor Tracker uses on its headings keeps them from sprawling. */
#tbh, .sht, .hub-tool-name { letter-spacing: -0.02em; }

/* ── buttons ──
 * Vendor Tracker: `rounded-lg px-3 py-2 text-sm`, navy filled, hovering to
 * blue. app.css's pill + scale-on-press becomes a rectangle that changes
 * colour instead of size. Only three of its variants are ever rendered here
 * — `.bp` (primary), `.bo` (Auto-Fill Part-Time and the Gantt day arrows),
 * `.bg` (inline text actions); `.bd` and the ten badge classes are AR Pro
 * leftovers nothing in frontend/js/ emits, and are left as they are rather
 * than restyled into the dark. */
.btn { border-radius: var(--rmd); transition: background-color .15s, color .15s, border-color .15s; }
.btn:active { transform: none; }
.bp:hover { background: var(--navy2); }
/* Was a 2px navy outline, which shouts next to a flat navy fill. Vendor
   Tracker's nearest thing is the `ghost` button and the `Select` control:
   a hairline on the base ramp, navy label, surface-tint on hover. */
.bo { border: 1px solid var(--g200); padding: 9px 19px; }
.bo:hover { background: var(--tint); }
.bg:hover { background: var(--tint); border-radius: var(--rsm); }

/* ── cards + surfaces ──
 * A card is a white 16px box on a 1px `base-200` border, not a shadow
 * floating free of one (Card.tsx: `rounded-2xl border border-base-200
 * bg-white shadow-card`). */
.card { border: 1px solid var(--g100); }
.tbl tbody tr:hover { background: var(--tint); }

/* ── topbar ──
 * Vendor Tracker's chrome: white, a hairline of navy at 9%, and the real
 * ZUS lockup sitting to the left of the page title behind a thin divider
 * (page.tsx's .hub-topbar). The lockup is the product's actual signature —
 * the app had been showing a lettered tile instead. */
.topbar { border-bottom: 1px solid rgba(34,52,136,.09); gap: var(--md); }
.tb-brand { display: flex; align-items: center; gap: var(--md); flex-shrink: 0; }
.tb-lockup { height: 30px; width: auto; object-fit: contain; display: block; }
.tb-div { width: 1px; height: 20px; background: var(--g100); flex: none; }
/* The topbar's right corner. Label and control on one line here, unlike the
   stacked fields elsewhere — the topbar is 62px tall and a stacked field
   would either overflow it or force it taller, and the strip is worth more
   as roster than as chrome. */
.tb-slot {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; justify-content: flex-end; gap: 8px;
  /* Stop short of the dock. It is `position: fixed` and vertically centred,
     so on any window shorter than about 1300px its top corner reaches up
     into the topbar — measured at 68px of overlap on the month field, 23px
     of it vertical on a 557px viewport, and the dock paints over the top
     (z 40). The rail is 76px wide at `right: var(--lg)`, and the topbar
     already pads itself by var(--xl), so this is the difference. */
  padding-right: calc(76px + var(--lg) - var(--xl));
}
.tb-slot .fld-l { padding: 0; }
/* These now carry an outlet's name and its details rather than a fixed view
   title, so they have to cope with "ZUS Coffee - Bandar Sri Damansara 2" as
   readily as with "Calendar" — one line each, truncated rather than wrapped,
   since the topbar is a fixed 62px and a second line would push the roster
   down for every outlet with a long name. */
#tbh, #tbp { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 46ch; }
#tbh { font-size: 19px; font-weight: 800; }
#tbp { font-size: 11px; color: var(--g400); margin-top: 1px; }

/* ── the dock ──
 * Navigation is one navy bar at the right edge (index.html, shell.css); the
 * left sidebar this file used to restyle is gone. All that is left to say
 * here is the accent on the active marker — tan-line rather than tan, which
 * at 3px wide is the difference between a marker you see and one you don't. */
.bi-tab.active::after { background: var(--gold-d); }

/* ── shift times ──
 * The one number on the card a manager actually reads, and it was the one
 * thing on the card not in the app's typeface. `.rc-time` is an <input>, and
 * an input does not inherit font-family — every other control in the grid
 * says so explicitly (`.rc-tasktxt` sets `font: inherit`), this one never
 * did, so it rendered in the UA's default Arial while the labels and values
 * millimetres away were Poppins. Now in the list above.
 *
 * Every shift time is now drawn identically — same face, size, weight,
 * colour — whatever wrote it. app.css dimmed and italicised one case,
 * `source === 'pattern'`, and the reason that had to go is not just that it
 * looked like a second font: it did not mean what it appeared to mean.
 * A Part-Time shift placed by auto-fill (`source === 'auto'`) is exactly as
 * machine-generated as a Full-Timer's rest-day pattern, and it rendered
 * bright and bold like a hand-typed one. So the two treatments did not read
 * "computed vs typed"; they read "full-timer vs everybody else", which is
 * already obvious from the staff column. Where a shift came from now lives
 * in the card's tooltip, which can say it in words and costs no ink.
 *
 * Digits stay tabular so the columns line up, which app.css already had. */
.rc-time, .rc-time-pattern {
  font-size: 12.5px;
  font-weight: 800;
  font-style: normal;
  color: var(--navy);
}
.rc-MIDDLE .rc-time, .rc-LATE .rc-time,
.rc-MIDDLE .rc-time-pattern, .rc-LATE .rc-time-pattern { color: #fff; }

/* Same value, same treatment, in the other place it is drawn: the Gantt's
   bar labels are shift times too, and had no tabular figures. */
.gantt-bar { font-variant-numeric: tabular-nums; }

/* ── shift-card rows ──
 * The card's task role used to be a boxed <select> sitting under the
 * Hours/Break stack: its own border, its own fill, its own 17px height — the
 * loudest element on a 34px-wide card, for the shortest value on it. It is
 * now the fourth row of that stack and reads as the row's value. Still the
 * same select, still editable; only its chrome is gone.
 *
 * Kept deliberately: the italic/muted treatment for an auto-computed role
 * (.rc-role-auto), which is the only thing distinguishing "the engine
 * decided this" from "a person typed this". */
.rc-role-sel {
  appearance: none; -webkit-appearance: none;
  border: none; background: transparent; padding: 0;
  font: inherit; font-size: 9.5px; font-weight: 700;
  line-height: inherit; text-align: right; text-align-last: right;
  cursor: pointer; height: auto; width: auto; max-width: 100%;
}
/* No border to highlight, so hovering/focusing has to say "editable" some
   other way — the same dotted underline a definition would use. */
.rc-role-sel:hover { text-decoration: underline dotted; text-underline-offset: 2px; }
.rc-role-sel:focus-visible { outline: 1.5px solid var(--navy2); outline-offset: 1px; border-radius: 3px; }
.rc-card .rc-rows { padding-bottom: 6px; }
/* The en dash a shift with no break shows was the last value drawn
   differently from its neighbours — app.css gives `.rc-v-off` weight 600
   against `.rc-v`'s 700. Every row in the stack now reads the same. */
.rc-v-off { font-weight: 700; }

/* On a coloured card the value ramp is white; app.css pointed those rules at
   `.rc-role select`, which no longer exists.
   The `.rc-role-auto` variant — italic, weight 500, dimmed — is gone for the
   same reason the shift time's is: Role is a value in a stack of values, and
   a second treatment for one of them reads as a second font, not as "the
   engine chose this". The class is still on the element and still carries
   the distinction in the select's own tooltip; it just no longer changes how
   the row is drawn. */
.rc-MIDDLE .rc-role-sel, .rc-LATE .rc-role-sel { color: #fff; }
.rc-role-sel option { color: var(--g800); background: #fff; }
.rc-MORNING .rc-role-sel option, .rc-MIDDLE .rc-role-sel option,
.rc-CLOSING .rc-role-sel option, .rc-LATE .rc-role-sel option { color: var(--navy2); background: #fff; }

/* ── session colours ──
 * Two hues, two strengths, and the pairing carries meaning: blue for the
 * early half of the day, orange for the late half, saturated for the two
 * sessions that are the day's pressure points and soft for the two that
 * bracket them.
 *
 *   Opening       soft blue      the day opening up
 *   Middle        blue           the busy middle
 *   Closing       soft orange    the day winding down
 *   Late closing  orange         whoever locks up
 *
 * "Soft" is a pale tint with dark ink of the same hue, not a lighter fill
 * under white text — white on either of these pales sits near 2.6:1, which
 * is unreadable at 8px. Inverting the ramp for those two costs the rules
 * below and buys about 7:1 on both. It also gives the grid a second signal
 * for free: a saturated card is a session under pressure, and you can see
 * that across a month without reading a single word.
 *
 * app.css paints all four cards from one white-on-dark ramp, so everything
 * here is an override of it. The greens and purples it shipped with are
 * gone; the only colour kept is #C2410C, which was already Late closing.
 */
.rc-MORNING { background: #DDE4F8; border-color: #B9C6EF; }
.rc-MIDDLE  { background: #283C9D; border-color: #1D2E7D; }
.rc-CLOSING { background: #FBE4D6; border-color: #F1C3A6; }
.rc-LATE    { background: #C2410C; border-color: #9A340A; }

/* ── the day with nothing on it ──
 * White and empty, at the manager's instruction (2026-08-23).
 *
 * It used to be the darkest of the pale fills (#C9CCD8) carrying the words
 * "— off —", on the argument that a rest day is not a gap in the roster but the
 * thing the law requires you to schedule, and that "who is off today" should be
 * answerable by squinting. That argument is not wrong, and it lost anyway: on a
 * month where most cells are empty while it is being built, a grid of grey slabs
 * shouting "off" reads as a wall rather than as a roster, and the eye has to
 * work to find the shifts that are actually there.
 *
 * So: white, a hairline edge to keep the cell's shape, and no text at all. The
 * shifts carry the colour now; absence carries none. A REST DAY the law
 * requires is still visible where it matters — in the rotation, in the rest_day
 * rule that refuses a seventh shift, and in the day cards — rather than by
 * being the loudest thing on the grid. */
.rc-off { background: #fff; border: 1px solid var(--g100); box-shadow: none; }
.rc-off .rc-time { color: var(--g600); }
/* It cycles to a shift on click like every other card, so it lifts like
   every other card. app.css suppressed both, which made the one cell you
   most often need to fill the one cell that felt disabled. */
.rc-off:hover { box-shadow: var(--sc); }

/* The dark-ink ramp for the two soft sessions, mirroring app.css's white one
   step for step so nothing is left reading at 40% of the wrong colour. */
.rc-MORNING .rc-time, .rc-MORNING .rc-time-pattern,
.rc-MORNING .rc-v, .rc-MORNING .rc-role-sel { color: #223488; }
.rc-MORNING .rc-k    { color: rgba(34,52,136,.82); }
.rc-MORNING .rc-sess { color: rgba(34,52,136,.72); }
.rc-MORNING .rc-time::placeholder { color: rgba(34,52,136,.45); }
.rc-MORNING .rc-time:focus { outline-color: #223488; }

.rc-CLOSING .rc-time, .rc-CLOSING .rc-time-pattern,
.rc-CLOSING .rc-v, .rc-CLOSING .rc-role-sel { color: #9A3B0C; }
.rc-CLOSING .rc-k    { color: rgba(154,59,12,.88); }
.rc-CLOSING .rc-sess { color: rgba(154,59,12,.76); }
.rc-CLOSING .rc-time::placeholder { color: rgba(154,59,12,.45); }
.rc-CLOSING .rc-time:focus { outline-color: #9A3B0C; }

/* Legend swatches. The two pale ones need a border of their own — a 10px
   square of #DDE4F8 on a white page is not a colour, it is a smudge. */
.rc-key-m { background: #DDE4F8; border: 1px solid #B9C6EF; }
.rc-key-d { background: #283C9D; }
.rc-key-c { background: #FBE4D6; border: 1px solid #F1C3A6; }
.rc-key-x { background: #C2410C; }
.rc-key-o { background: #fff; border: 1px solid var(--g200); }

/* The row labels — HOURS / BREAK / AT / ROLE — at 8px uppercase. app.css
   draws them at 60% of the ink colour, which measured 2.8:1 on the orange
   card and 3.2-3.7:1 on the pale ones: under AA's 4.5 for small text, and
   the smallest text on the screen. Raised until all four clear it. The
   orange one was already failing before this repaint; measuring the new
   palette is just what surfaced it. */
.rc-MIDDLE .rc-k { color: rgba(255,255,255,.84); }
/* Orange is the darkest-ink background of the four, so its label needs the
   most: .84 still measured 4.1:1. Full white clears it at 5.2. */
.rc-LATE .rc-k { color: #fff; }

/* ── the day header card ──
 * Date, weekday and alerts on one card. The weekday sits under the number
 * rather than beside it: a column is only ~95px wide, and "08 Sat" on one
 * line pushes the tags below it out of shape on the days that carry three.
 * Muted, because the number is what you navigate by and the day name is
 * what you orient by — they are not the same job. */
.ac-d { display: flex; flex-direction: column; align-items: center; line-height: 1.05; }
.ac-dow {
  font-size: 8.5px; font-weight: 600; letter-spacing: .02em;
  color: var(--g400); margin-top: 1px;
}
/* ── two answers, not four ──
 * A day is either fine or it is not. app.css shipped three tints — green,
 * cream, pink — all within a few percent of white, which asked the reader to
 * distinguish #FFFDF7 from #FFFAFA across a scrolling month and told them
 * nothing they could act on when they managed it. The severity that got
 * folded away is not lost: it is still on the tags inside the card, where it
 * belongs, because that is where it says WHICH problem.
 *
 * Both tints are deliberately several shades past app.css's. A row of alert
 * cards is the first thing above the roster and the thing you scan sideways
 * to find a bad day; a tint you have to look for is not doing that job.
 *
 * Blue and orange, not green and red — the grid already has a two-colour
 * vocabulary and this row now speaks it. Blue is Morning's family (#DDE4F8
 * / #223488) and orange is Closing and Late closing's (#FBE4D6 / #9A3B0C),
 * so the screen carries two hues in total instead of four, and nobody has
 * to learn that a green header and a blue card are unrelated facts.
 *
 * Each is a shade deeper than the shift card it borrows from, which is what
 * keeps the header row from reading as more data: same hue, different
 * weight. That is the separation — previously it was hue, and hue is what
 * this change spends. */
.ac-day.ac-clean { background: #CBD7F3; border-color: #A8BCE8; }
.ac-day.ac-has-warn,
.ac-day.ac-has-crit { background: #F8D2B8; border-color: #EDB183; }

/* The number takes the same answer as the card, so a day states its case
   twice — once in a tint you catch peripherally and once in ink you read.
   Both inks are the shift palette's own: Morning's navy and Closing's burnt
   orange, measured on these deeper grounds at 7.7:1 and 5.0:1. */
.ac-day.ac-clean .ac-d { color: #223488; }
.ac-day.ac-clean .ac-dow { color: rgba(34,52,136,.75); }
.ac-day.ac-has-warn .ac-d,
.ac-day.ac-has-crit .ac-d { color: #9A3B0C; }
.ac-day.ac-has-warn .ac-dow,
.ac-day.ac-has-crit .ac-dow { color: rgba(154,59,12,.75); }
.ac-day.ac-clean .ac-ok { color: #223488; }
/* The tag pills were drawn to sit on near-white. On a card with real colour
   in it they need their own ground back, or an amber pill on pink reads as
   a smudge with letters in it. */
.ac-day .ac-tag { background: rgba(255,255,255,.82); }
.ac-day .ac-more { color: rgba(154,59,12,.8); }

/* ── frozen panes ──
 * Three things hold still while the month scrolls: the staff column, the
 * date row, and the alert cards above it.
 *
 * The staff column was already sticky-left in app.css. The two header rows
 * are new, and getting them to work needed two structural changes rather
 * than a `position: sticky` anywhere:
 *
 * 1. The alert cards were their own grid in their own block. Sticky only
 *    holds an element while its PARENT is on screen, so a row in a separate
 *    block scrolls away with that block no matter what. They are now the
 *    first row of the roster grid (schedulingAlertRowHtml).
 * 2. `.rc-wrap` scrolled horizontally and was `overflow-y: visible`, which
 *    the spec turns into `auto` the moment the other axis scrolls — so the
 *    wrapper was already a vertical scrollport exactly as tall as its own
 *    content, and sticky-top inside it could only ever stick to nothing.
 *    Handing the vertical scroll back to `.page` with `overflow-y: clip`
 *    does not work either: with `overflow-x: auto` beside it, `clip`
 *    computes to `hidden`, which is still a scroll container. Measured, not
 *    assumed — the computed value came back "hidden" and the header sailed
 *    825px off the top.
 *
 *    So the grid takes both axes instead and becomes its own viewport. That
 *    is what a frozen-pane grid is anyway: a window onto a table too big for
 *    it. The month scrolls inside; everything else on the page — the cost
 *    card, the legend, the add-staff form — keeps scrolling with the page.
 */
.rc-wrap { overflow: auto; min-height: 200px; }

/* The grid box has to be as wide as its own columns, or the frozen staff
   column comes unstuck partway across the month.
   `position: sticky` holds an element inside its CONTAINING BLOCK, which for
   these cells is `.rc-grid` — and the grid box was only as wide as the pane
   (1121px) while its 31 columns overflowed to 3290px. So the column pinned
   correctly for the first ~1100px of sideways scroll and then travelled off
   with the right edge of a box that had run out. Easy to miss: scrolling
   "a bit" to test it looks perfect.
   `min-width: max-content` gives the box its real width without giving up
   the `minmax(140px, 1fr)` columns — a short month still fills the pane
   rather than huddling on the left. */
.rc-grid { min-width: max-content; }


/* ── the grid owns the viewport ──
 * "Frozen" has to mean permanently, and a max-height cannot deliver that.
 * The pane was shorter than the window, so the PAGE still scrolled — and the
 * whole pane, frozen header and all, slid up and off with it. Sticky was
 * working perfectly inside a box that was itself walking away.
 *
 * So the roster view claims the height instead of guessing at it: a flex
 * column from `.page` down to `.rc-wrap`, every link `flex: 1; min-height:
 * 0`, and the pane takes whatever is left after the header and the legend.
 * The page then has nothing to scroll, the pane has everything to scroll,
 * and the frozen row and column stay exactly where they are for as long as
 * the month is open.
 *
 * `min-height: 0` on every link is the load-bearing part: a flex item's
 * default `min-height: auto` refuses to shrink below its content, so one
 * missing declaration anywhere in the chain and the pane grows to fit all 31
 * days and pushes the page back into scrolling.
 *
 * Scoped to the Roster. Calendar and Outlets are ordinary documents that
 * should scroll as documents. */
/* app.css pads the page 32px on every side. Below the topbar that reads as a
   gap rather than as breathing room — the topbar already ends in a hairline,
   so the space under it is doing the separating twice. The sides and bottom
   keep their 32px; only the top comes in. */
.page { display: flex; flex-direction: column; padding-top: var(--md); }
#v-scheduling.active { display: flex; flex-direction: column; flex: 1; min-height: 0; }
#scheduling-root, #sched-main, #sched-grid { display: flex; flex-direction: column; flex: 1; min-height: 0; }
#sched-grid > .rc-wrap { flex: 1; min-height: 0; }
/* The legend under the grid keeps its natural height — only the pane flexes.
   It is effectively frozen: outside the scroll pane, so it is on screen the
   whole time the month is. That makes every pixel of it a pixel the roster
   does not get, which matters most on exactly the screens that have fewest —
   hence tighter than app.css draws it: smaller swatches, tighter gaps, and
   the how-to-use sentence cut to the half that is not already obvious from
   using it once. */
#sched-grid > .rc-key {
  flex: 0 0 auto;
  margin-top: 6px;
  gap: 2px 13px;
  font-size: 9.5px;
}
#sched-grid > .rc-key i { width: 9px; height: 9px; border-radius: 2px; }

/* One frozen row, so it sticks at 0 and needs no arithmetic. It used to be
   two — alert cards over a date row — and the lower one's offset had to be
   measured after every render and every resize, because the cards grow with
   the busiest day's tag count. Merging the date INTO the card deleted both
   the second row and the measurement. */
/* Sticks at the very top of the pane, which is directly under the topbar —
   the day row is the first thing below the logo, and nothing floats over the
   roster any more to complicate the offset. */
.rc-frz-a { position: sticky; top: 0; z-index: 6; }
/* The corner is in the frozen row and the frozen column at once, so it has
   to outrank both or the cards slide over it. */
.rc-grid > .rc-corner.rc-frz-a { z-index: 9; }
.rc-staff { z-index: 3; }

/* Every 6px grid gap beside a frozen cell is a window onto whatever is
   scrolling underneath — below the two stacked header rows, and down the
   right-hand edge of the staff column, where slivers of card were showing
   through. Each frozen cell paints a copy of itself in page colour one full
   gap away, which covers the gap exactly and touches no layout. A 3px ring
   from each side does not do it: only the frozen cell has one, so it closes
   half the gap and leaves the other half looking through. */
/* Solid, and sealed on every side. Each frozen cell has an opaque fill of
   its own, but the 6px grid gaps BETWEEN them were still windows onto the
   month sliding past underneath — a row of cards with daylight between them
   rather than a header. The ring closes the horizontal gaps (3px from each
   neighbour meets in the middle of a 6px gap) and the offset copy closes the
   one below the row, where the next neighbour is an ordinary shift card with
   no ring of its own to contribute. */
.rc-frz-a {
  box-shadow: 0 0 0 3px var(--g50), 0 var(--rc-gap) 0 var(--g50);
  background-color: var(--white);
}
/* The tinted states paint over that white — they are opaque themselves, and
   naming them here keeps the "solid" promise from depending on which answer
   a given day happens to be. */
.rc-frz-a.ac-clean { background-color: #CBD7F3; }
.rc-frz-a.ac-has-warn,
.rc-frz-a.ac-has-crit { background-color: #F8D2B8; }
/* White cards, and a strip with no daylight in it.
 *
 * app.css fills these with --g50, the page colour, which makes the frozen
 * column read as a gutter rather than as the row labels it holds. White
 * makes each name a card like every other card on the grid.
 *
 * The curtain to the right was already here. The 3px ring is new and does
 * the same job vertically: the 6px gaps BETWEEN staff cards were windows
 * onto shift cards sliding past sideways, so the column flickered as you
 * scrolled the month. Same fix as the day row above — 3px from each
 * neighbour meets in the middle of a 6px gap. */
.rc-staff { background: var(--white); }
.rc-staff, .rc-grid > .rc-corner {
  box-shadow: 0 0 0 3px var(--g50), var(--rc-gap) 0 0 var(--g50);
}
/* The corner is in the frozen row and the frozen column at once. */
.rc-grid > .rc-corner.rc-frz-a {
  box-shadow: 0 0 0 3px var(--g50), var(--rc-gap) 0 0 var(--g50), 0 var(--rc-gap) 0 var(--g50);
}


/* A field is its label and its control, stacked. Nothing else. */
.fld { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.fld-l {
  font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--g400); padding-left: 2px; white-space: nowrap;
}

/* Overtime gets said out loud — it is the one part of the working that
   changes a decision — but as a two-character flag rather than a clause.
   The rest of the cost strip went with the header card it sat on; this
   survives because the panel and the dock button both still fly it. */
.sched-cost-ot {
  font-size: 9.5px; font-weight: 800; color: var(--err);
  background: var(--err-bg); border-radius: var(--rpill); padding: 2px 7px;
}

/* ── focus ──
 * One ring, navy, everywhere — replacing the mix of border-colour swaps the
 * ported sheet uses per control. */
.inp:focus, .sel:focus, .fi:focus, .fs:focus, .ft:focus, .bi-search-input:focus {
  border-color: var(--navy2);
  box-shadow: 0 0 0 3px rgba(40,60,157,.12);
}

/* ── no outlet chosen ──
 * The way into the app used to be a picker sitting right above this message,
 * so "select an outlet above" was a direction. The picker is a dock panel
 * now, and "look to your right" is a worse instruction than a button, so the
 * empty state opens the panel itself. */
.sched-empty {
  max-width: 420px; margin: 12vh auto 0; text-align: center;
  padding: var(--xl); background: var(--white);
  border: 1px solid var(--g100); border-radius: var(--rlg); box-shadow: var(--sc);
}
.sched-empty-h { font-size: 15px; font-weight: 800; color: var(--navy); letter-spacing: -.01em; }
.sched-empty-p { font-size: 11.5px; line-height: 1.55; color: var(--g600); margin: 6px 0 var(--md); }

/* ── icons ──
 * The dock shipped emoji. The ZUS design system does not allow them as an
 * icon system (s.9, "Emoji are not an icon system"), and DBI's rail already
 * uses drawn glyphs -- so the two navigations in one window were drawn from
 * two different systems. These are DBI's: filled, rounded, currentColor, so
 * they inherit the tab's own colour and its active/hover states for free.
 * .ni was sized for a text glyph (font-size:15px); an svg needs a box. */
.ni{display:grid; place-items:center; height:18px}
.ni svg{width:17px; height:17px; fill:currentColor; display:block}

/* ── management: cost of labour ──
 * The one cross-outlet view, so the only one that needs a summary strip. Four
 * figures, because four is what fits on one row at 1100px (the window's
 * minWidth) without any of them wrapping — a fifth would push the last one
 * onto a line of its own and read as an afterthought.
 *
 * The variance colours are the design system's status tokens through app.css's
 * aliases (--err, --ok), and there are THREE states rather than two: over,
 * under, and level. Level is the point — a roster inside 2% of its budget is
 * on target, and painting that the same red as a 50% overspend is how the
 * colour stops meaning anything. */
.mgmt-kpis {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: var(--md); margin-bottom: var(--lg);
}
.mgmt-kpi {
  border: 1px solid var(--g100); border-radius: var(--rmd);
  padding: var(--md) var(--lg); background: #fff;
}
.mgmt-kpi.is-over { border-color: #F0C5C5; background: var(--err-bg); }
.mgmt-kpi.is-under { border-color: #C7E4C7; background: #F2FAF2; }
.mgmt-kpi-l {
  font-size: 10px; font-weight: 800; letter-spacing: .04em;
  text-transform: uppercase; color: var(--g400);
}
.mgmt-kpi-v {
  font-size: 21px; font-weight: 800; color: var(--navy);
  letter-spacing: -.02em; margin: 3px 0 1px;
  font-variant-numeric: tabular-nums;
}
.mgmt-kpi.is-over .mgmt-kpi-v { color: var(--err); }
.mgmt-kpi.is-under .mgmt-kpi-v { color: var(--ok2); }
.mgmt-kpi-s { font-size: 10.5px; line-height: 1.45; color: var(--g600); }

/* Whose outlets these are, in place of the manager picker, when the view is
   one DM's (signed in as one, or ABP previewing one). */
.mgmt-scope {
  font-size: 11px; font-weight: 700; color: var(--navy);
  padding: 4px 10px; border-radius: 999px; background: var(--tint);
}
.mgmt-note {
  font-size: 11.5px; line-height: 1.55; color: var(--g600);
  background: var(--tint); border-left: 2px solid var(--g200);
  border-radius: 0 var(--rsm) var(--rsm) 0;
  padding: 9px var(--md); margin: 0 0 var(--lg);
}
.mgmt-controls { align-items: center; margin-bottom: var(--md); }
/* The Assistant pill floats bottom-left over everything, so a footnote
   flush to the end of a short page sits behind it. The padding is the
   pill's height plus its own offset. */
.mgmt-foot {
  font-size: 10.5px; line-height: 1.5; color: var(--g400);
  margin: var(--md) 2px 0; padding-bottom: 64px;
}

.mgmt-tbl td { vertical-align: top; }
.mgmt-outlet { font-weight: 800; color: var(--navy); text-decoration: none; }
.mgmt-outlet:hover { text-decoration: underline; }
.mgmt-name { display: block; font-size: 11px; color: var(--g600); }
.mgmt-detail { display: block; font-size: 10px; line-height: 1.45; color: var(--g400); margin-top: 2px; }
.mgmt-model { white-space: nowrap; }
/* Two selects in one cell, and the cell is not the widest thing in the row —
   stacked rather than side by side, so a long band name does not set the
   column's width for every other row. */
.mgmt-model .fi { display: block; width: 190px; }
.mgmt-model .fi + .fi { margin-top: 4px; }
.mgmt-model-name { font-size: 12px; font-weight: 700; color: var(--navy); margin-bottom: 4px; }
.fi.xs { font-size: 11px; padding: 3px 6px; }
.mgmt-ot { display: block; font-size: 10px; color: var(--warn); font-weight: 700; }
.mgmt-tbl .is-over { color: var(--err); font-weight: 700; }
.mgmt-tbl .is-under { color: var(--ok2); font-weight: 700; }
.mgmt-tbl .is-level { color: var(--g400); }

/* ── promotions: the monthly deck ──
 * The strip is the point of the screen. A list of promotions cannot answer
 * "how busy is the 12th", because overlap is the normal state of a promotion
 * calendar and two promotions over one weekend is a fact about that weekend
 * rather than about either row. One cell per day, coloured by the heaviest
 * thing running on it, answers it at a glance.
 *
 * Three bands, not a gradient: the reader's question is whether the roster has
 * to change, and a smooth ramp would imply a precision that a figure typed
 * into a slide deck does not have. `--warn` and `--err-bg` are the design
 * system's own status tokens — heavy is not an ERROR, so it takes the amber
 * end rather than the red. */
.promo-strip-wrap { margin-bottom: var(--lg); }
.promo-strip-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--lg); margin-bottom: var(--md);
}
.promo-strip {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(30px, 1fr));
  gap: 3px; border: 1px solid var(--g100); border-radius: var(--rmd);
  padding: var(--sm); background: #fff;
}
.promo-day {
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 5px 2px; border-radius: var(--rsm);
  background: var(--g50); cursor: default;
}
.promo-day.is-moderate { background: #FFF3DA; }
.promo-day.is-heavy { background: var(--err-bg); }
.promo-day-d { font-size: 11px; font-weight: 800; color: var(--g800); font-variant-numeric: tabular-nums; }
.promo-day-w { font-size: 9px; color: var(--g400); text-transform: uppercase; }
.promo-day-u {
  font-size: 9.5px; font-weight: 800; color: var(--g300);
  font-variant-numeric: tabular-nums;
}
.promo-day.is-moderate .promo-day-u { color: #9A6B00; }
.promo-day.is-heavy .promo-day-u { color: var(--err); }

.promo-legend { display: flex; align-items: center; gap: var(--md); margin-top: 7px; flex-wrap: wrap; }
.promo-key { font-size: 10px; font-weight: 700; color: var(--g600); display: inline-flex; align-items: center; gap: 5px; }
.promo-key::before {
  content: ''; width: 10px; height: 10px; border-radius: 3px; background: var(--g50);
}
.promo-key.is-moderate::before { background: #FFF3DA; }
.promo-key.is-heavy::before { background: var(--err-bg); }

.promo-form { padding: var(--lg); margin-bottom: var(--lg); }
.promo-form-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--md);
}
.promo-wd { margin-top: var(--lg); }
.promo-wd-box {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px; font-weight: 600; color: var(--g600);
  border: 1px solid var(--g100); border-radius: var(--rsm); padding: 4px 8px;
}
.promo-tbl td { vertical-align: top; }
.promo-kind, .promo-local {
  display: inline-block; font-size: 9px; font-weight: 800; letter-spacing: .03em;
  text-transform: uppercase; border-radius: var(--rpill);
  padding: 2px 7px; margin-left: 6px; vertical-align: 1px;
}
.promo-kind { background: var(--tint); color: var(--g600); }
/* A row somebody typed here rather than one the deck sent. Flagged rather
   than hidden: the whole point of the marker is that this calendar has an
   owner elsewhere, and a stand-in row should look like one. */
.promo-local { background: #FFF3DA; color: #9A6B00; }
.promo-note, .promo-mask {
  display: block; font-size: 10px; line-height: 1.45; color: var(--g400); margin-top: 3px;
}
.promo-mask { font-weight: 700; color: var(--g600); }
.promo-effect { font-size: 10.5px; color: var(--g600); }
.promo-pill {
  display: inline-block; font-size: 11px; font-weight: 800;
  border-radius: var(--rpill); padding: 2px 8px;
  background: var(--g50); color: var(--g400);
  font-variant-numeric: tabular-nums;
}
.promo-pill.is-moderate { background: #FFF3DA; color: #9A6B00; }
.promo-pill.is-heavy { background: var(--err-bg); color: var(--err); }
.promo-foot {
  font-size: 10.5px; line-height: 1.5; color: var(--g400);
  margin: var(--lg) 2px 0; padding-bottom: 64px;
}

/* ── deliveries: ordering & receiving ──
 * The load row is a bar chart of seven bars and nothing more, because the
 * question is "which morning has several" and seven labelled classes is a
 * comparison of counts. It is the one genuinely visual thing on the screen;
 * the rest is a lookup, which is a table. */
.deliv-load {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--sm);
  border: 1px solid var(--g100); border-radius: var(--rmd);
  padding: var(--md); background: #fff; margin-bottom: var(--md);
}
.deliv-load-day { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.deliv-bar-track {
  width: 100%; height: 64px; display: flex; align-items: flex-end;
  background: var(--g50); border-radius: var(--rsm); overflow: hidden;
}
.deliv-bar { width: 100%; background: var(--g200); border-radius: var(--rsm) var(--rsm) 0 0; }
.deliv-load-day.has-load .deliv-bar { background: var(--navy); }
.deliv-load-n {
  font-size: 13px; font-weight: 800; color: var(--g300);
  font-variant-numeric: tabular-nums;
}
.deliv-load-day.has-load .deliv-load-n { color: var(--navy); }
.deliv-load-l { font-size: 10px; font-weight: 700; color: var(--g400); text-transform: uppercase; }

/* An outlet nobody has entered is not an outlet without deliveries — every
   outlet takes them — so this is a gap in the data and reads as one. */
.deliv-gap {
  font-size: 11.5px; line-height: 1.55; color: #9A6B00;
  background: #FFF3DA; border-left: 2px solid var(--warn);
  border-radius: 0 var(--rsm) var(--rsm) 0;
  padding: 9px var(--md); margin: 0 0 var(--lg);
}
.deliv-tbl td { vertical-align: top; }
.deliv-name { display: block; font-size: 11px; color: var(--g600); }
.deliv-note { display: block; font-size: 10px; line-height: 1.45; color: var(--g400); margin-top: 2px; }
.deliv-form { padding: var(--lg); }
.deliv-form-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--md);
}
.deliv-foot {
  font-size: 10.5px; line-height: 1.5; color: var(--g400);
  margin: var(--lg) 2px 0; padding-bottom: 64px;
}
