@import url("noli-crew-targets.css");
@import url("noli-a11y.css");
@import url('/static/noli-responsive.css');
@import url('/static/river-fonts.css');

/* noli-glass.css — THE River kit (GLASS-LOOK-PASS, Cole 2026-09-23; RIVER-KIT, Cole 2026-09-24:
   "one shared kit; every screen is built function-first from kit parts").

   Target, pinned UNCHANGED: docs/design/reference-library/crew-glass-mock-v2-2026-09-23.html
   and crew-today-mock-2026-09-23.html; controls from finance-app-nexo-controls-2026-09-23.png.
   Every River surface (crew /apps, FOH, BOH, SOPs, sales, desktop) builds from these ng-*
   parts instead of restyling by hand; a fix here fixes every screen. Page CSS holds layout
   only — scripts/design_lint.py rules K2/K3 fail a page that restyles a kit part or hand-rolls
   its own button/sheet/stepper/PIN pad/chip/toast. Behaviour shared by parts (sheet, stepper,
   PIN pad, picker, retry state, toast) lives in static/noli-kit.js (window.NG).
   Gallery, every part in every state: /apps/_glass (templates/glass_kitchen_sink.html).
   Inventory + which screen moves to which part: docs/design/KIT_INVENTORY_2026-09-24.md.

   Two densities, same parts: CREW (default; iPad-first, 44 px targets, 48 px primary) and
   DESKTOP (class ng-desktop on body or any container; 36/40 px on a wide screen with a mouse,
   and back to crew sizes on a phone or any touch screen — the iPhone look-up case).

   Load order: noli-ui.css (spacing --s-*, radius --r-*, type --t-* scale), then
   stage-colors.css (--stage-*), then this file.

   Rules this file keeps (DESIGN_SYSTEM_BLUEPRINT.md §5-6, docs/validators/design-critic.md):
   - Colours live ONLY in the token blocks between the ng:tokens markers. Components read
     tokens; no hex below the end marker (pinned by tests/test_noli_glass.py).
   - Every light token has a value in all three dark forms (also pinned).
   - Spacing 2/4/8/12/16/24/32 via --s-*; radius 8/12/999 via --r-* (the mock's 16-22 px
     card radii are not on the blueprint, so cards use --r-card 12).
   - One red primary per view. Secondary actions are text or hairline. The floating tab bar
     is the ONLY blur, with a solid fallback.
   - Anton for numbers, DM Sans for UI, Instrument Serif for page titles only.
   - Targets 44 px (48 px primary). Pills that look 32 px tall keep a 44 px hit area.
   - Every control: default / hover / active / focus-visible / disabled. Hover is mouse-only
     (@media (hover:hover)) so a tapped row, chip or key on an iPad never stays lit. */

/* KIT-10 (2026-09-25): light ink2/ink3/ok/warn/link and dark ink3 darkened (lightened) so
   every text token holds >=4.5:1 on every kit surface a row can sit on: canvas, card, soft,
   soft-press, and each status tint. Measured before: ng-flag--ok 4.26 on its tint in a table,
   .ng-picker__sub 4.38 on a selected (soft) row, ink3 3.89 on a pressed row. */
/* ng:tokens:start */
:root{
  --ng-canvas:#F6F4EF;
  --ng-card:#FFFFFF;
  --ng-soft:#EEEBE5;
  --ng-soft-press:#E2DED6;
  --ng-ink1:#1B1A17;
  --ng-ink2:#5F5B54;
  --ng-ink3:#65615A;
  --ng-hairline:rgba(27,26,23,.08);
  --ng-hairline-strong:rgba(27,26,23,.16);
  --ng-control-edge:#7D786F;
  --ng-danger-press:rgba(180,35,24,.10);
  --ng-hero:#1F1E1B;
  --ng-hero-ink:#F6F4EF;
  --ng-hero-mute:rgba(246,244,239,.64);
  --ng-hero-tint-up:#0F3D2B;
  --ng-hero-tint-down:#6B1A12;
  --ng-hero-tint-even:#143A63;
  --ng-hero-tint-warn:#8C5A00;
  --ng-hero-tint-warn-mute:rgba(246,244,239,.92);
  --ng-hero-hair:rgba(246,244,239,.12);
  --ng-red:#C8102E;
  --ng-red-hover:#B50E29;
  --ng-red-press:#A50D26;
  --ng-red-ink:#FFFFFF;
  --ng-red-glow:rgba(200,16,46,.28);
  --ng-link:#24684A;
  --ng-ok:#22644A;
  --ng-ok-ink:#FFFFFF;
  --ng-ok-bg:rgba(47,125,91,.12);
  --ng-info:#2F5F8F;
  --ng-compare:#6B21A8;
  --ng-scheduled:#155E75;
  --ng-info-bg:rgba(47,95,143,.1);
  --ng-danger-ink:#FFFFFF;
  --ng-hero-ok:#6FCF97;
  --ng-hero-danger:#FF8A7A;
  --ng-warn:#7A500D;
  --ng-warn-bg:rgba(183,121,31,.12);
  --ng-danger:#B42318;
  --ng-danger-bg:rgba(180,35,24,.08);
  --ng-focus:#1B1A17;
  --ng-scrim:rgba(18,17,16,.36);
  --ng-shadow-raised:0 8px 24px rgba(27,26,23,.08);
  --ng-shadow-lift:0 1px 2px rgba(27,26,23,.08);
  --ng-tabbar-glass:rgba(255,255,255,.94);
  /* the toast: the dark hero card in light; in dark a raised charcoal card, never an
     inverted white slab (KIT ROUND 2) */
  --ng-toast:#1F1E1B;
  --ng-toast-ink:#F6F4EF;
  --ng-toast-hair:rgba(246,244,239,.14);
  --ng-toast-ok:#6FCF97;
  --ng-toast-danger:#FF8A7A;
  /* the hero inverts, so its event dots take the OTHER theme's stage values */
  --ng-hero-brew:#6FCF97;
  --ng-hero-filter:#FF6B6B;
  --ng-hero-package:#F2C94C;
  --ng-hero-cnb:#A1A1AA;
  --ng-hero-pickup:#FB923C;
  --ng-hero-delivery:#C084FC;
  /* Approved team-email/report context: exported report paper and semantic roles stay
     white/ink in either UI theme; these opt-in roles do not recolour shell chrome. */
  --ng-report-paper:#FFFFFF;
  --ng-report-ink:#1D1B18;
  --ng-report-sub:#595959;
  --ng-report-faint:#6B6760;
  --ng-report-hair:#E6E2DA;
  --ng-report-control:#D6D3D1;
  --ng-report-up:#1F8A5B;
  --ng-report-down:#C0392B;
  --ng-report-even:#2F5F8F;
  --ng-report-warn:#6B5B2A;
  --ng-report-pending:#FFF7DF;
  --ng-report-pending-border:#D6A84F;
  --ng-report-pending-ink:#5B4300;
  --ng-report-highlight:#F1F8F4;
  --ng-report-highlight-border:#CFE8DA;
  --ng-report-title:28px;
}
:root[data-theme="dark"],html.nc-dark{
  --ng-canvas:#121110;
  --ng-card:#1C1B19;
  --ng-soft:#232220;
  --ng-soft-press:#2E2C29;
  --ng-ink1:#F2EFE8;
  --ng-ink2:#A8A298;
  --ng-ink3:#9D978D;
  --ng-hairline:rgba(255,255,255,.07);
  --ng-hairline-strong:rgba(255,255,255,.16);
  --ng-control-edge:#8B857B;
  --ng-danger-press:rgba(255,138,122,.10);
  --ng-hero:var(--ng-soft);
  --ng-hero-ink:#F2EFE8;
  --ng-hero-mute:#B9B4AA;
  --ng-hero-tint-up:#0F3D2B;
  --ng-hero-tint-down:#6B1A12;
  --ng-hero-tint-even:#143A63;
  --ng-hero-tint-warn:#614000;
  --ng-hero-tint-warn-mute:#F2EFE8;
  --ng-hero-hair:rgba(242,239,232,.12);
  --ng-red:#C8102E;
  --ng-red-hover:#D81B39;
  --ng-red-press:#A50D26;
  --ng-red-ink:#FFFFFF;
  --ng-red-glow:rgba(200,16,46,.36);
  --ng-link:#7FC8A2;
  --ng-ok:#6FCF97;
  --ng-ok-ink:#121110;
  --ng-ok-bg:rgba(111,207,151,.12);
  --ng-info:#8DB8E0;
  --ng-compare:#C4B5FD;
  --ng-scheduled:#67E8F9;
  --ng-info-bg:rgba(141,184,224,.12);
  --ng-danger-ink:#121110;
  --ng-hero-ok:#6FCF97;
  --ng-hero-danger:#FF8A7A;
  --ng-warn:#E8B64C;
  --ng-warn-bg:rgba(232,182,76,.12);
  --ng-danger:#FF8A7A;
  --ng-danger-bg:rgba(255,138,122,.1);
  --ng-focus:#F2EFE8;
  --ng-scrim:rgba(0,0,0,.56);
  --ng-shadow-raised:0 8px 24px rgba(0,0,0,.36);
  --ng-shadow-lift:0 1px 2px rgba(0,0,0,.4);
  --ng-tabbar-glass:rgba(28,27,25,.94);
  --ng-toast:#2E2C29;
  --ng-toast-ink:#F2EFE8;
  --ng-toast-hair:rgba(255,255,255,.12);
  --ng-toast-ok:#6FCF97;
  --ng-toast-danger:#FF8A7A;
  --ng-hero-brew:#6FCF97;
  --ng-hero-filter:#FF6B6B;
  --ng-hero-package:#F2C94C;
  --ng-hero-cnb:#A1A1AA;
  --ng-hero-pickup:#FB923C;
  --ng-hero-delivery:#C084FC;
  /* Approved team-email/report context: exported report paper and semantic roles stay
     white/ink in either UI theme; these opt-in roles do not recolour shell chrome. */
  --ng-report-paper:#FFFFFF;
  --ng-report-ink:#1D1B18;
  --ng-report-sub:#595959;
  --ng-report-faint:#6B6760;
  --ng-report-hair:#E6E2DA;
  --ng-report-control:#D6D3D1;
  --ng-report-up:#1F8A5B;
  --ng-report-down:#C0392B;
  --ng-report-even:#2F5F8F;
  --ng-report-warn:#6B5B2A;
  --ng-report-pending:#FFF7DF;
  --ng-report-pending-border:#D6A84F;
  --ng-report-pending-ink:#5B4300;
  --ng-report-highlight:#F1F8F4;
  --ng-report-highlight-border:#CFE8DA;
  --ng-report-title:28px;
}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    --ng-canvas:#121110;
    --ng-card:#1C1B19;
    --ng-soft:#232220;
    --ng-soft-press:#2E2C29;
    --ng-ink1:#F2EFE8;
    --ng-ink2:#A8A298;
    --ng-ink3:#9D978D;
    --ng-hairline:rgba(255,255,255,.07);
    --ng-hairline-strong:rgba(255,255,255,.16);
    --ng-control-edge:#8B857B;
    --ng-danger-press:rgba(255,138,122,.10);
    --ng-hero:var(--ng-soft);
    --ng-hero-ink:#F2EFE8;
    --ng-hero-mute:#B9B4AA;
    --ng-hero-tint-up:#0F3D2B;
    --ng-hero-tint-down:#6B1A12;
    --ng-hero-tint-even:#143A63;
    --ng-hero-tint-warn:#614000;
    --ng-hero-tint-warn-mute:#F2EFE8;
    --ng-hero-hair:rgba(242,239,232,.12);
    --ng-red:#C8102E;
    --ng-red-hover:#D81B39;
    --ng-red-press:#A50D26;
    --ng-red-ink:#FFFFFF;
    --ng-red-glow:rgba(200,16,46,.36);
    --ng-link:#7FC8A2;
    --ng-ok:#6FCF97;
    --ng-ok-ink:#121110;
    --ng-ok-bg:rgba(111,207,151,.12);
    --ng-info:#8DB8E0;
  --ng-compare:#C4B5FD;
  --ng-scheduled:#67E8F9;
    --ng-info-bg:rgba(141,184,224,.12);
    --ng-danger-ink:#121110;
    --ng-hero-ok:#6FCF97;
    --ng-hero-danger:#FF8A7A;
    --ng-warn:#E8B64C;
    --ng-warn-bg:rgba(232,182,76,.12);
    --ng-danger:#FF8A7A;
    --ng-danger-bg:rgba(255,138,122,.1);
    --ng-focus:#F2EFE8;
    --ng-scrim:rgba(0,0,0,.56);
    --ng-shadow-raised:0 8px 24px rgba(0,0,0,.36);
    --ng-shadow-lift:0 1px 2px rgba(0,0,0,.4);
    --ng-tabbar-glass:rgba(28,27,25,.94);
    --ng-toast:#2E2C29;
    --ng-toast-ink:#F2EFE8;
    --ng-toast-hair:rgba(255,255,255,.12);
    --ng-toast-ok:#6FCF97;
    --ng-toast-danger:#FF8A7A;
    --ng-hero-brew:#6FCF97;
    --ng-hero-filter:#FF6B6B;
    --ng-hero-package:#F2C94C;
    --ng-hero-cnb:#A1A1AA;
    --ng-hero-pickup:#FB923C;
    --ng-hero-delivery:#C084FC;
  /* Approved team-email/report context: exported report paper and semantic roles stay
     white/ink in either UI theme; these opt-in roles do not recolour shell chrome. */
  --ng-report-paper:#FFFFFF;
  --ng-report-ink:#1D1B18;
  --ng-report-sub:#595959;
  --ng-report-faint:#6B6760;
  --ng-report-hair:#E6E2DA;
  --ng-report-control:#D6D3D1;
  --ng-report-up:#1F8A5B;
  --ng-report-down:#C0392B;
  --ng-report-even:#2F5F8F;
  --ng-report-warn:#6B5B2A;
  --ng-report-pending:#FFF7DF;
  --ng-report-pending-border:#D6A84F;
  --ng-report-pending-ink:#5B4300;
  --ng-report-highlight:#F1F8F4;
  --ng-report-highlight-border:#CFE8DA;
  --ng-report-title:28px;
  }
}
/* ng:tokens:end */

/* Colour that means something (Cole 2026-09-30). Named by meaning, never by hue; every value is a
   status token above (no parallel palette), each >=4.5:1 on canvas, card and its own tint in both themes.
   up = ahead of last year, even = on pace (within +/-2%), behind = mildly behind (-2% to -5%),
   down = clearly behind (-5% or worse). Never the only signal: pair with a sign or an arrow. */
:root{--ng-up:var(--ng-ok);--ng-even:var(--ng-info);--ng-behind:var(--ng-warn);--ng-down:var(--ng-danger)}

/* hidden means hidden: a part's own display (.ng-actions, .ng-sheet__body, .ng-alert-*) must
   never un-hide a view a page has switched off — a sheet with two states showed both. */
.ng-body [hidden]{display:none !important}

/* Theme-independent roles (no colour, so one block). The type sizes the blueprint scale
   has no slot for are the mock's: the serif page title, the 15 px strong line and the
   13 px control label (blueprint §5 "functional text 13-16px"), the hero and count numbers. */
:root{
  --ng-font-num:'Anton', sans-serif;
  --ng-font-ui:'DM Sans', system-ui, -apple-system, sans-serif;
  --ng-font-title:'Instrument Serif', Georgia, 'Times New Roman', serif;
  --ng-t-title:34px;
  --ng-t-hero-num:48px;
  --ng-t-count:24px;
  --ng-t-strong:16px;
  --ng-t-control:14px;
  --ng-t-section:20px;
  --ng-touch-crew:44px;
  --ng-touch:44px;
  --ng-touch-primary:48px;
  --ng-pill-h:32px;
  --ng-h-xs:24px;
  --ng-h-sm:32px;
  --ng-ease:cubic-bezier(.2,.7,.2,1);
  --ng-fast:120ms;
  --ng-std:180ms;
  /* RIVER-KIT additions: 16 px inputs stop iOS zooming on focus; PIN keys are a 72 px
     round pad; the floating tab bar is 44 + 2×4 padding + 2 hairline = 54 px tall, which
     is what a toast clears. */
  --ng-t-input:16px;
  --ng-t-key:28px;
  --ng-key:72px;
  --ng-tabbar-h:54px;
  /* KIT ROUND 9: a typed or stepped value is UI text (DM Sans, tabular) — the display face is
     only for hero numbers. Every unit sits in one fixed slot, so a column of steppers and of
     typed readings keeps one right edge whether a row has a unit or not. */
  --ng-t-value:20px;
  --ng-unit-w:40px;
}
/* KIT ROUND 10 — a kit part opened on a page that does not wear the kit (mobile_ops.js's PIN
   sheets — sign-in and per-action — on a classic crew page without noli-ui.css). The part
   carries, on itself only, the shared scale tokens noli-ui.css would give it and the page
   base .ng-body would, so the ONE PIN pad looks the same everywhere. mobile_ops.js adds the
   class only when noli-ui.css is absent (a part's own value would shadow the page's). */
.ng-standalone{
  --s-0:2px;--s-1:4px;--s-2:8px;--s-3:12px;--s-4:16px;--s-6:24px;--s-8:32px;
  --r-std:8px;--r-card:12px;--r-pill:999px;
  --t-body:14px;--t-dense:12px;--t-micro:12px;--t-section:17px;
  --nc-primary:var(--ng-ink1);--nc-primary-ink:var(--ng-canvas);
  --nc-primary-hover:color-mix(in srgb, var(--ng-ink1) 88%, var(--ng-ink2));
  --nc-primary-press:color-mix(in srgb, var(--ng-ink1) 76%, var(--ng-ink2));
  --nc-primary-shadow:var(--ng-shadow-lift);--nc-primary-shade:var(--ng-hairline-strong);
  font-family:var(--ng-font-ui);font-size:var(--t-body);line-height:1.4;color:var(--ng-ink1);
  -webkit-font-smoothing:antialiased;-webkit-tap-highlight-color:transparent;
}
.ng-standalone,.ng-standalone *,.ng-standalone *::before,.ng-standalone *::after{box-sizing:border-box}
.ng-standalone [hidden]{display:none !important}
/* DESKTOP density: only with a fine pointer on a wide screen, so the same page opened on
   an iPhone or an iPad keeps crew-sized targets without any page code. */
@media (min-width:768px) and (pointer:fine){
  .ng-desktop{
    --ng-touch:36px;
    --ng-touch-primary:40px;
    --ng-pill-h:32px;
    --ng-t-section:16px;
    --ng-t-title:28px;
    --ng-t-count:20px;
    --ng-t-input:14px;
    --ng-t-key:20px;
    --ng-key:56px;
    --ng-t-value:20px;
  }
}

/* The River shell (river-shell.css) reads --paper, --paper-2, --ink, --ink-2, --hair and --sans, which
   only html[data-palette="river"] defines. Glass pages that include the shell without that palette
   (the two sales pages) got them empty: transparent ground, full-ink 400-weight tabs, no hairline.
   Default them from the glass tokens. :has() takes its argument's specificity, so this is (0,1,0):
   equal to a bare :root{}, and below html[data-palette] (0,1,1) and :root[data-palette] (0,2,0),
   so a page that sets the palette, or its own inline :root value AFTER this sheet, still wins. */
:where(:root):has(.ng-body){--paper:var(--ng-card);--paper-2:var(--ng-canvas);--ink:var(--ng-ink1);--ink-2:var(--ng-ink2);--hair:var(--ng-hairline);--sans:var(--ng-font-ui)}

/* ── Page base ─────────────────────────────────────────────────────────── */
/* Paint the root too: short pages and modal backdrops expose it. When a
   shell is present, river-shell.css owns both grounds, regardless of load order. */
:root:has(> .ng-body):not(:has(.rs-shell)){background:var(--ng-canvas);min-height:100vh}
.ng-body{
  margin:0;
  min-height:100vh;
  min-height:100dvh;
  box-sizing:border-box;
  background:var(--ng-canvas);
  color:var(--ng-ink1);
  font-family:var(--ng-font-ui);
  font-size:var(--t-body);
  line-height:1.4;
  -webkit-font-smoothing:antialiased;
  -webkit-tap-highlight-color:transparent;
}
.ng-body *,.ng-body *::before,.ng-body *::after{box-sizing:border-box}

/* ── Type roles ────────────────────────────────────────────────────────── */
.ng-eyebrow{font-size:var(--t-dense);font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ng-ink3);margin:0}
/* a title that is a beer's full name wraps inside its screen, never runs off it */
.ng-title{font:400 var(--ng-t-title)/1.05 var(--ng-font-title);margin:0;color:var(--ng-ink1);overflow-wrap:break-word}
.ng-title em{font-style:italic;color:var(--ng-ink2)}
.ng-section{font-size:var(--ng-t-section);font-weight:700;margin:0}
.ng-num{font-family:var(--ng-font-num);font-weight:400;font-variant-numeric:tabular-nums;letter-spacing:0}
.ng-muted{color:var(--ng-ink2)}
.ng-faint{color:var(--ng-ink3)}
/* Title and its note share a line when both fit; when they don't, the note drops whole onto
   the next line (never two squeezed columns — KIT ROUND 5, the phone shift header). */
.ng-section-head{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:baseline;gap:var(--s-0) var(--s-3);margin:var(--s-6) 0 var(--s-2)}
.ng-section-head .ng-faint{font-size:var(--t-dense)}

/* ── Icons (static/icons/ng-icons.svg, one line set, currentColor) ────── */
.ng-icon{width:20px;height:20px;flex:none;stroke:currentColor;fill:none;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;vertical-align:middle}
.ng-icon--sm{width:16px;height:16px}
.ng-icon--lg{width:var(--ng-h-xs);height:var(--ng-h-xs)}

/* Shared focus ring: visible on keyboard only, same everywhere. */
:where(.ng-primary:where(:not(.mer-save-btn)),.ng-text-action,.ng-outline-action,.ng-danger-action,.ng-icon-action,.ng-seg__item,.ng-chip,
  .ng-switch__item,.ng-tabbar__item,.ng-stepper__key,.ng-tick,.ng-row--action,.ng-hero__go,.ng-pin__key,
  .ng-picker__option,.ng-toast__action,.ng-steps__item):focus-visible{
  outline:2px solid var(--ng-focus);
  outline-offset:2px;
}
:where(.ng-primary:where(:not(.mer-save-btn)),.ng-text-action,.ng-outline-action,.ng-danger-action,.ng-icon-action,.ng-seg__item,.ng-chip,
  .ng-switch__item,.ng-tabbar__item,.ng-stepper__key,.ng-tick,.ng-row--action,.ng-hero__go,.ng-pin__key,
  .ng-picker__option,.ng-toast__action,.ng-steps__item){
  -webkit-appearance:none;appearance:none;font-family:inherit;margin:0;cursor:pointer;
  touch-action:manipulation;
}
:where(.ng-primary:where(:not(.mer-save-btn)),.ng-text-action,.ng-outline-action,.ng-danger-action,.ng-icon-action,.ng-seg__item,.ng-chip,
  .ng-switch__item,.ng-tabbar__item,.ng-stepper__key,.ng-tick,.ng-row--action,.ng-hero__go,.ng-pin__key,
  .ng-picker__option,.ng-toast__action,.ng-steps__item):is(:disabled,[aria-disabled="true"]){
  cursor:not-allowed;
}

/* ── ng-hero: the dark focal card (inverts in dark) ────────────────────── */
.ng-hero{
  background:var(--ng-hero);
  color:var(--ng-hero-ink);
  border-radius:var(--r-card);
  padding:var(--s-1) var(--s-4);
}
.ng-hero--pad{padding:var(--s-4)}
/* Approved dark edge polish: an inset hairline preserves every consumer's geometry.
   Supporting cards step down to card; explicit light remains byte-identical. */
:is(:root[data-theme="dark"],html.nc-dark) .ng-hero{
  box-shadow:inset 0 0 0 1px var(--ng-hairline-strong);
}
:is(:root[data-theme="dark"],html.nc-dark) .ng-soft-card--support{
  background:var(--ng-card);
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]):not(.nc-light) .ng-hero{
    box-shadow:inset 0 0 0 1px var(--ng-hairline-strong);
  }
  :root:not([data-theme="light"]):not(.nc-light) .ng-soft-card--support{
    background:var(--ng-card);
  }
}
.ng-hero .ng-eyebrow{color:var(--ng-hero-mute)}
.ng-hero__num{font:400 var(--ng-t-hero-num)/1 var(--ng-font-num);margin:var(--s-1) 0 0}
.ng-hero__divider{height:1px;background:var(--ng-hero-hair);margin:var(--s-3) 0}
.ng-hero__item{display:flex;align-items:center;gap:var(--s-3);padding:var(--s-3) 0;border-top:1px solid var(--ng-hero-hair)}
.ng-hero__item:first-child{border-top:0}
.ng-hero__text{flex:1;min-width:0}
.ng-hero__kicker{display:block;font-size:var(--t-dense);font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ng-hero-mute)}
.ng-hero__label{display:block;font-size:var(--ng-t-strong);font-weight:600;margin-top:var(--s-0)}
.ng-hero__meta{display:block;font-size:var(--t-dense);color:var(--ng-hero-mute)}
.ng-hero__go{
  display:inline-flex;align-items:center;gap:var(--s-0);min-height:var(--ng-touch);padding:0 var(--s-1);
  background:none;border:0;border-radius:var(--r-std);
  font-size:var(--ng-t-control);font-weight:600;color:var(--ng-hero-mute);white-space:nowrap;text-decoration:none;
  transition:color var(--ng-fast) var(--ng-ease);
}
@media (hover:hover){.ng-hero__go:hover{color:var(--ng-hero-ink)}}
.ng-hero__go:active{opacity:.7}
.ng-hero__go:focus-visible{outline-color:var(--ng-hero-ink)}
.tc-tomorrow .ng-hero__go:focus-visible{outline-color:var(--ng-focus)}
.ng-hero__go:is(:disabled,[aria-disabled="true"]){opacity:.45}

/* ── ng-row: list row, no borders ──────────────────────────────────────── */
.ng-rows{display:flex;flex-direction:column;margin:0;padding:0;list-style:none}
.ng-row{display:flex;align-items:center;gap:var(--s-3);min-height:var(--ng-touch);padding:var(--s-2) 0;color:inherit;text-decoration:none}
.ng-row__icon{width:var(--ng-pill-h);height:var(--ng-pill-h);border-radius:var(--r-pill);background:var(--ng-soft);display:grid;place-items:center;flex:none;color:var(--ng-ink2)}
.ng-row__text{flex:1;min-width:0}
/* Titles WRAP: a beer, tank or task name is never cut off (KIT ROUND 2). A dense desktop
   list that truly needs one line opts in with .ng-row__title--clip. */
.ng-row__title{display:block;font-size:var(--t-body);font-weight:600;line-height:1.3;overflow-wrap:anywhere}
.ng-row__title--clip{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ng-row__sub{display:block;font-size:var(--t-dense);color:var(--ng-ink3)}
.ng-row__value{font-size:var(--t-body);font-weight:700;font-variant-numeric:tabular-nums;text-align:right;white-space:nowrap}
.ng-row__value small{display:block;font-size:var(--t-dense);font-weight:500;color:var(--ng-ink3)}
.ng-row__value--ok{color:var(--ng-ok)}
.ng-row__value--warn{color:var(--ng-warn)}
.ng-row__value--danger{color:var(--ng-danger)}
.ng-row--action{width:100%;background:none;border:0;border-radius:var(--r-std);text-align:left;font:inherit;transition:background var(--ng-fast) var(--ng-ease)}
@media (hover:hover){.ng-row--action:hover{background:var(--ng-soft)}}
.ng-row--action:active{background:var(--ng-soft-press)}
.ng-row--action:is(:disabled,[aria-disabled="true"]){opacity:.45;background:none}

/* ── Pill hit-area pattern: 44 px target, 32 px visible pill ─────────────
   A 6 px transparent top/bottom border is part of the hit area but the
   background is clipped to the padding box, so the pill paints 32 px tall on
   the element itself (a measurer reads the real ground, not a pseudo). The
   hairline is an inset shadow, which also stops at the padding edge. */
.ng-seg__item,.ng-chip,.ng-tick{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--s-2);
  min-height:var(--ng-touch);min-width:var(--ng-touch);padding:0 var(--s-3);
  border:0;border-block:calc((var(--ng-touch) - var(--ng-pill-h)) / 2) solid transparent;border-radius:var(--r-pill);
  background:transparent;background-clip:padding-box;
  box-shadow:inset 0 0 0 1px var(--ng-hairline-strong);
  font-size:var(--ng-t-control);font-weight:600;color:var(--ng-ink2);white-space:nowrap;text-decoration:none;
  transition:background-color var(--ng-fast) var(--ng-ease),color var(--ng-fast) var(--ng-ease),transform var(--ng-fast) var(--ng-ease);
}
.ng-seg__item:focus-visible,.ng-chip:focus-visible,.ng-tick:focus-visible{outline-offset:-4px}

/* ── ng-seg / ng-chip: pill filters, dark selected state ─────────────────
   ng-seg = pick ONE (views, brewery, period); ng-chip = toggle MANY (filters),
   with an optional count. Same pill, same selected look, one language. */
.ng-seg,.ng-chips{display:flex;flex-wrap:wrap;column-gap:var(--s-2);row-gap:0;margin:0;padding:0}
@media (hover:hover){:is(.ng-seg__item,.ng-chip):hover{background-color:var(--ng-soft);color:var(--ng-ink1)}}
:is(.ng-seg__item,.ng-chip):active{background-color:var(--ng-soft-press)}
:is(.ng-seg__item,.ng-chip):is([aria-pressed="true"],[aria-current="page"],[aria-selected="true"]){
  background-color:var(--ng-ink1);color:var(--ng-canvas);box-shadow:none;
}
@media (hover:hover){:is(.ng-seg__item,.ng-chip):is([aria-pressed="true"],[aria-current="page"],[aria-selected="true"]):hover,:is(.ng-seg__item,.ng-chip):is([aria-pressed="true"],[aria-current="page"],[aria-selected="true"]):active{background-color:var(--ng-ink1);color:var(--ng-canvas)}}
:is(.ng-seg__item,.ng-chip):is(:disabled,[aria-disabled="true"]){color:var(--ng-ink3);background-color:transparent;opacity:.6;transform:none}
.ng-chip__count{font-variant-numeric:tabular-nums;font-weight:500;opacity:.72}
/* --stack: a pill that carries a second, smaller line under its label (the
   pallet number over "short"); tight leading keeps both inside the 32 px pill. */
.ng-seg__item--stack{flex-direction:column;gap:0;line-height:1.1}

/* ── ng-switch: brewery switch (two-up track) ──────────────────────────── */
.ng-switch{display:flex;background:var(--ng-soft);border-radius:var(--r-pill);padding:var(--s-1);margin:0}
.ng-switch__item{
  position:relative;flex:1;min-height:var(--ng-touch);padding:0 var(--s-3);border:0;border-radius:var(--r-pill);background:none;
  font-size:var(--ng-t-control);font-weight:600;color:var(--ng-ink2);white-space:nowrap;
  transition:background var(--ng-fast) var(--ng-ease),color var(--ng-fast) var(--ng-ease);
}
/* KIT ROUND 13: the item itself is the full control height (44 crew, 36 desktop), so a
   measured target is the real one; the 4 px track padding is only the frame around it. */
@media (hover:hover){.ng-switch__item:hover{color:var(--ng-ink1)}}
.ng-switch__item:active{background:var(--ng-soft-press)}
.ng-switch__item[aria-pressed="true"],.ng-switch__item[aria-checked="true"]{background:var(--ng-card);color:var(--ng-ink1);box-shadow:var(--ng-shadow-lift)}
.ng-switch__item:is(:disabled,[aria-disabled="true"]){opacity:.45;background:none}

/* ── ng-tabbar: floating pill nav, the ONLY blur ───────────────────────── */
.ng-tabbar{
  position:fixed;left:50%;transform:translateX(-50%);
  bottom:calc(var(--s-4) + env(safe-area-inset-bottom, 0px));z-index:40;
  display:flex;gap:var(--s-1);padding:var(--s-1);border-radius:var(--r-pill);
  background:var(--ng-card);border:1px solid var(--ng-hairline);box-shadow:var(--ng-shadow-raised);
  max-width:calc(100vw - 2 * var(--s-4) - env(safe-area-inset-left, 0px) - env(safe-area-inset-right, 0px));
}
@supports ((-webkit-backdrop-filter:blur(1px)) or (backdrop-filter:blur(1px))){
  .ng-tabbar{
    background:var(--ng-tabbar-glass);
    -webkit-backdrop-filter:blur(16px) saturate(1.3);
    backdrop-filter:blur(16px) saturate(1.3);
  }
}
.ng-tabbar--inline{position:relative;left:auto;bottom:auto;transform:none;margin:0 auto;width:max-content;max-width:100%}
.ng-tabbar__item{
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--s-0);
  min-width:var(--ng-tab-min);min-height:var(--ng-touch);padding:var(--s-1) var(--s-3);border:0;border-radius:var(--r-pill);background:none;
  font-size:var(--t-dense);font-weight:600;color:var(--ng-ink2);text-decoration:none;
  transition:background var(--ng-fast) var(--ng-ease),color var(--ng-fast) var(--ng-ease);
}
@media (hover:hover){.ng-tabbar__item:hover{color:var(--ng-ink1)}}
.ng-tabbar__item:active{background:var(--ng-soft-press)}
.ng-tabbar__item[aria-current="page"]{background:var(--ng-ink1);color:var(--ng-canvas)}
.ng-tabbar__item:is(:disabled,[aria-disabled="true"]){opacity:.45;background:none}

/* ── ng-primary: the ONE red button per view ───────────────────────────── */
.ng-primary:where(:not(.mer-save-btn)){
  position:relative;display:inline-flex;align-items:center;justify-content:center;gap:var(--s-2);
  min-height:var(--ng-touch-primary);padding:0 var(--s-6);width:100%;
  font-size:var(--ng-t-strong);text-decoration:none;
}
/* Colour, depth, radius and every state: the shared River primary rule set in
   noli-ui.css (Cole 2026-09-24, one primary button across River). */
.ng-primary--inline:where(:not(.mer-save-btn)){width:auto}
/* LOADING (aria-busy) for every button: the label hides, a spinner in the button's own
   ink turns in place, the button keeps its size. The primary's spinner reads the primary
   ink token, so it shows on the ink key in both themes. */
:is(.ng-primary:where(:not(.mer-save-btn)),.ng-outline-action,.ng-danger-action)[aria-busy="true"]{position:relative;color:transparent;pointer-events:none}
:is(.ng-primary:where(:not(.mer-save-btn)),.ng-outline-action,.ng-danger-action)[aria-busy="true"]::after{
  content:"";position:absolute;inset:0;margin:auto;width:var(--ng-spinner);height:var(--ng-spinner);border-radius:var(--r-pill);
  border:2px solid var(--ng-spin, var(--ng-ink2));border-right-color:transparent;animation:ng-spin .8s linear infinite;
}
.ng-primary:where(:not(.mer-save-btn)){--ng-spin:var(--nc-primary-ink, var(--ng-red-ink))}
.ng-danger-action{--ng-spin:var(--ng-danger)}
@keyframes ng-spin{to{transform:rotate(360deg)}}

/* ── ng-text-action: quiet secondary, 44 px target ─────────────────────── */
.ng-text-action{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--s-1);min-height:var(--ng-touch);min-width:var(--ng-touch);padding:0 var(--s-1);
  background:none;border:0;border-radius:var(--r-std);
  font-size:var(--ng-t-control);font-weight:600;color:var(--ng-link);text-decoration:none;white-space:nowrap;
  transition:opacity var(--ng-fast) var(--ng-ease);
}
.ng-text-action--quiet{color:var(--ng-ink2)}
@media (hover:hover){.ng-text-action:hover{text-decoration:underline;text-underline-offset:3px}}
/* KIT-10 round 2: opacity faded the link ink toward the same grey as the
   disabled state below (Pressed measured almost the same as Disabled) —
   pressed now keeps full ink colour and shows via a soft-press fill, the
   same mechanism every other action in this kit already uses. */
.ng-text-action:active{background:var(--ng-soft-press)}
.ng-text-action:is(:disabled,[aria-disabled="true"]){color:var(--ng-ink3);text-decoration:none;opacity:.7}

/* ── ng-outline-action: hairline pill ──────────────────────────────────── */
.ng-outline-action{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--s-2);
  min-height:var(--ng-touch);padding:0 var(--s-4);background:transparent;
  border:1px solid var(--ng-hairline-strong);border-radius:var(--r-pill);
  font-size:var(--t-body);font-weight:600;color:var(--ng-ink1);text-decoration:none;white-space:nowrap;
  transition:background var(--ng-fast) var(--ng-ease),transform var(--ng-fast) var(--ng-ease);
}
@media (hover:hover){.ng-outline-action:hover{background:var(--ng-soft)}}
.ng-outline-action:active{background:var(--ng-soft-press)}
.ng-outline-action:is(:disabled,[aria-disabled="true"]){color:var(--ng-ink3);background:transparent;transform:none}

/* outline pill in the danger tone (an urgent suggestion beside neutral ones; not the bare-text danger action) */
.ng-outline-action--danger{border-color:var(--ng-danger);color:var(--ng-danger)}
@media (hover:hover){.ng-outline-action--danger:hover{background:var(--ng-danger-bg)}}

/* ── ng-icon-action: round icon-only control (bell, theme, back) ───────── */
.ng-icon-action{
  display:inline-grid;place-items:center;width:var(--ng-touch);height:var(--ng-touch);padding:0;
  background:transparent;border:1px solid var(--ng-hairline-strong);border-radius:var(--r-pill);color:var(--ng-ink2);font-size:var(--ng-t-control);
  transition:background var(--ng-fast) var(--ng-ease),color var(--ng-fast) var(--ng-ease);
}
@media (hover:hover){.ng-icon-action:hover{background:var(--ng-soft);color:var(--ng-ink1)}}
.ng-icon-action:active{background:var(--ng-soft-press)}
.ng-icon-action:is(:disabled,[aria-disabled="true"]){color:var(--ng-ink3);background:transparent;opacity:.6}
/* FILTRATION ROUND 13 — the shared voice mic (static/voice_intent.js) docked in a kit head is
   an icon action: the theme's ink on the canvas, never voice_intent's light paper disc (which
   left a grey mic on a white disc in the dark theme). Recording keeps its solid state. */
.ng-head .vi-fab#vi-fab.docked:not(.listening){width:var(--ng-touch)!important;height:var(--ng-touch)!important;padding:0!important;
  border:1px solid var(--ng-hairline-strong)!important;border-radius:var(--r-pill)!important;background:transparent!important;color:var(--ng-ink1)!important;box-shadow:none!important}
.ng-head .vi-fab#vi-fab.docked.listening{background:var(--ng-ink1)!important;border-color:var(--ng-ink1)!important;color:var(--ng-canvas)!important}

/* ── ng-stepper: pressable ± keys ──────────────────────────────────────── */
.ng-stepper{display:grid;grid-template-columns:var(--ng-touch) calc(var(--ng-touch) + var(--s-6)) var(--ng-touch) var(--ng-unit-w);align-items:center;gap:var(--s-2)}
.ng-stepper__key{
  display:grid;place-items:center;width:var(--ng-touch);height:var(--ng-touch);padding:0;
  border:0;border-radius:var(--r-pill);background:var(--ng-soft);color:var(--ng-ink1);
  box-shadow:var(--ng-shadow-lift);
  transition:background var(--ng-fast) var(--ng-ease),transform var(--ng-fast) var(--ng-ease),box-shadow var(--ng-fast) var(--ng-ease);
}
@media (hover:hover){.ng-stepper__key:hover{background:var(--ng-soft-press)}}
.ng-stepper__key:active{background:var(--ng-soft-press);box-shadow:inset 0 1px 2px var(--ng-hairline-strong)}
.ng-stepper__key:is(:disabled,[aria-disabled="true"]){color:var(--ng-ink3);box-shadow:none;transform:none;background:var(--ng-soft);opacity:.55}
.ng-stepper__value{min-width:0;text-align:center;font:600 var(--ng-t-value)/1 var(--ng-font-ui);font-variant-numeric:tabular-nums}
.ng-count{display:flex;align-items:center;justify-content:space-between;gap:var(--s-3);padding:var(--s-3) 0;border-bottom:1px solid var(--ng-hairline)}
.ng-count:last-child{border-bottom:0}

/* ── ng-tick: tap-to-tick task pill ────────────────────────────────────── */
.ng-ticks{display:flex;flex-wrap:wrap;column-gap:var(--s-2);row-gap:0;margin-top:var(--s-1)}
.ng-tick{font-weight:500;padding:0 var(--s-3) 0 var(--s-2)}
.ng-tick__mark{
  display:grid;place-items:center;width:16px;height:16px;border-radius:var(--r-pill);
  border:1.5px solid var(--ng-ink3);color:var(--ng-ok-ink);flex:none;
}
.ng-tick__mark .ng-icon{width:12px;height:12px;stroke-width:2.5;opacity:0}
@media (hover:hover){.ng-tick:hover{color:var(--ng-ink1);box-shadow:inset 0 0 0 1px var(--ng-ink3)}}
.ng-tick:active{filter:brightness(.95)}
.ng-tick[aria-pressed="true"]{color:var(--ng-ink2);text-decoration:line-through;text-decoration-color:var(--ng-ink3)}
.ng-tick[aria-pressed="true"]{background-color:var(--ng-soft);box-shadow:none}
.ng-tick[aria-pressed="true"] .ng-tick__mark{background:var(--ng-ok);border-color:var(--ng-ok)}
.ng-tick[aria-pressed="true"] .ng-tick__mark .ng-icon{opacity:1}
/* Confirmation pill ("Set aside", "Counted", "Skipped"): the label states what
   was confirmed, so a strike would read as its opposite. Keeps mark + fill. */
.ng-tick--confirm[aria-pressed="true"]{text-decoration:none;color:var(--ng-ink1)}
.ng-tick.tc-tick--late:not([aria-pressed="true"]){box-shadow:inset 0 0 0 1.5px var(--ng-warn)}
.ng-tick.tc-tick--late:not([aria-pressed="true"]) .ng-tick__mark{border-color:var(--ng-warn)}
.ng-tick:is(:disabled,[aria-disabled="true"]){opacity:.5;transform:none}
.ng-tick:is(:disabled,[aria-disabled="true"]){box-shadow:inset 0 0 0 1px var(--ng-hairline)}

/* ── TM duty row: shared by crew Today (static/today-crew.css) and the brew
   log's TM/ready-to-filter section (static/brew-sheet.css) — the same
   duties record (erp_cellar_monitoring), the same look and controls
   wherever the crew stamps it (Cole 2026-09-25: moved here off Today so a
   second page stops hand-rolling its own tick/toggle/number-box CSS).
   `.duty` renders nothing of its own (display:contents) — it only groups a
   tick with its optional inline reading box so both flow as one unit in
   whatever layout the page wraps them in. `.duty-psi` is the two-part
   shape: `.duty-top` keeps the tick and its `.duty-controls` (the psi/lb
   box) on one line; a flag or note is a sibling of `.duty-top`, appended to
   `.duty` itself, so it lands on its own full-width line underneath. */
.duty,.duty.duty-psi{display:contents}
/* Overflow fix (2026-09-25): the optional yeast-harvest variant packs a
   tick, an amount box, its unit, an lb-equivalent label AND two more ticks
   (Dumped, Not needed) into this one row -- more children than the
   pressure_check pair `.duty-top` was built for. Without wrap, inline-flex
   with no basis SHRINKS every child below its own text's width instead of
   overflowing, so the tick pills painted a border around only part of
   their label and the rest of the text spilled past it unstyled. Measured
   on /apps/today-crew (Cole's tablet, iPad-width 2-column grid): the
   "Harvested yeast" tick's own box shrank to 84px against a ~160px label.
   flex-wrap lets the excess controls drop to their own line instead. */
.duty.duty-psi .duty-top{display:inline-flex;flex-wrap:wrap;align-items:center;gap:var(--s-2);max-width:100%}
.duty.duty-psi .duty-controls{margin-right:var(--s-2)}
.duty.duty-psi .duty-controls{display:inline-flex;align-items:center}
.duty.duty-psi .duty-controls:empty{display:none}
.tc-tick-by{display:inline-block;font-size:var(--t-dense);font-weight:500;color:var(--ng-ink3)}
.ng-tick[aria-pressed="true"] .tc-tick-by{color:var(--ng-ink2)}
/* The reading box (psi, lbs harvested, biofine rate, BBL): a filled soft
   well (not the pills' outline), square-ish radius, unit shown beside it. */
.psi-input{
  width:56px;min-height:var(--ng-touch);padding:0 var(--s-2);
  border:0;border-block:6px solid transparent;border-radius:var(--r-std);
  background:var(--ng-soft);background-clip:padding-box;
  font:500 var(--ng-t-input)/1 var(--ng-font-ui);font-variant-numeric:tabular-nums;text-align:center;color:var(--ng-ink1);
  -moz-appearance:textfield;transition:background-color var(--ng-fast) var(--ng-ease);
}
.psi-input::placeholder{color:var(--ng-ink3)}
.psi-input::-webkit-outer-spin-button,.psi-input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
@media (hover:hover){.psi-input:hover{background-color:var(--ng-soft-press)}}
.psi-input:focus-visible{outline:2px solid var(--ng-focus);outline-offset:-4px}
.tc-psi-unit{margin-left:var(--s-1);font-size:var(--t-dense);font-weight:600;color:var(--ng-ink3)}
.psi-flag{flex-basis:100%;margin:var(--s-1) 0 var(--s-2);padding:var(--s-2) var(--s-3);font-size:var(--t-dense);font-weight:600}

/* ── ng-alert: icon + text (+ optional trailing ng-text-action) ─────────── */
.ng-alert-danger,.ng-alert-warn,.ng-alert-ok,.ng-alert-info{
  display:flex;align-items:flex-start;gap:var(--s-3);padding:var(--s-3) var(--s-4);border-radius:var(--r-card);
  font-size:var(--t-body);color:var(--ng-ink1);margin:0;
}
.ng-alert-danger{background:var(--ng-danger-bg)}
.ng-alert-warn{background:var(--ng-warn-bg)}
.ng-alert-danger > .ng-icon{color:var(--ng-danger)}
.ng-alert-warn > .ng-icon{color:var(--ng-warn)}
.ng-alert-ok{background:var(--ng-ok-bg)}
.ng-alert-info{background:var(--ng-info-bg)}
.ng-alert-ok > .ng-icon{color:var(--ng-ok)}
.ng-alert-info > .ng-icon{color:var(--ng-info)}
.ng-alert__body{flex:1;min-width:0}
.ng-alert__action{align-self:center;margin:calc(-1 * var(--s-2)) 0}
.ng-alert__title{display:block;font-weight:700}

/* ── ng-sheet: bottom sheet (use on a <dialog>) ────────────────────────── */
.ng-sheet{
  position:fixed;inset:auto 0 0 0;margin:0 auto;width:100%;max-width:640px;max-height:min(85dvh,calc(var(--ng-sheet-viewport, 100dvh) - env(safe-area-inset-top, 0px) - var(--s-4)));bottom:var(--ng-sheet-keyboard,0px);
  padding:var(--s-2) var(--s-4) calc(var(--s-6) + env(safe-area-inset-bottom, 0px));
  border:0;border-radius:var(--r-card) var(--r-card) 0 0;
  background:var(--ng-card);color:var(--ng-ink1);box-shadow:var(--ng-shadow-raised);overflow:auto;
}
.ng-sheet::backdrop{background:var(--ng-scrim)}
/* a sheet that takes focus itself (the PIN sheets) is not a control: no ring round it */
.ng-sheet:focus{outline:none}
.ng-sheet[open]{animation:ng-sheet-in var(--ng-std) var(--ng-ease)}
.ng-sheet__grab{display:block;width:var(--ng-grab-w);height:var(--s-1);border-radius:var(--r-pill);background:var(--ng-hairline-strong);margin:0 auto var(--s-3)}
.ng-sheet__head{display:flex;align-items:center;justify-content:space-between;gap:var(--s-3);margin-bottom:var(--s-2)}
/* Long sheets scroll inside the dialog; carry the handle in the pinned head. */
.ng-sheet__head--sticky{
  --ng-sheet-head-pad-y:var(--s-2);--ng-sheet-head-pad-x:var(--s-4);
  position:sticky;top:calc(-1 * var(--ng-sheet-head-pad-y));z-index:2;
  display:grid;grid-template-columns:minmax(0,1fr) auto;gap:var(--s-2) var(--s-3);
  margin:calc(-1 * var(--ng-sheet-head-pad-y)) calc(-1 * var(--ng-sheet-head-pad-x)) var(--s-3);
  padding:var(--ng-sheet-head-pad-y) var(--ng-sheet-head-pad-x) var(--s-2);
  background:var(--ng-card);border-bottom:1px solid var(--ng-hairline);
}
.ng-sheet__head--sticky > .ng-sheet__grab{grid-column:1/-1;margin-bottom:0}
@keyframes ng-sheet-in{from{transform:translateY(24px);opacity:0}to{transform:none;opacity:1}}

/* ── ng-event-dot: stage colour + always a word beside it ──────────────── */
.ng-event-dot{display:inline-block;width:var(--s-2);height:var(--s-2);border-radius:var(--r-pill);flex:none;background:var(--ng-ink3)}
.ng-event-dot[data-kind="brew"]{background:var(--stage-brew)}
.ng-event-dot[data-kind="filter"]{background:var(--stage-filter)}
.ng-event-dot[data-kind="package"]{background:var(--stage-package)}
.ng-event-dot[data-kind="cnb"]{background:var(--stage-cnb)}
.ng-event-dot[data-kind="ferment"]{background:var(--stage-ferment)}
.ng-event-dot[data-kind="cellar"]{background:var(--stage-cellar)}
.ng-event-dot[data-kind="pickup"]{background:var(--stage-pickup)}
.ng-event-dot[data-kind="delivery"]{background:var(--stage-delivery)}
/* legacy = an old hand-typed calendar note: a ring, not a new hue. ghost = keeps the
   column aligned for a daily routine line without drawing a dot. */
.ng-event-dot[data-legacy="1"]{box-shadow:0 0 0 1.5px var(--ng-ink3)}
.ng-event-dot[data-ghost="1"]{visibility:hidden}
.ng-event-dot[data-kind="maintenance"]{background:var(--stage-maintenance)}
.ng-event-dot[data-kind="note"]{background:var(--stage-note)}
.ng-hero .ng-event-dot[data-kind="brew"]{background:var(--ng-hero-brew)}
.ng-hero .ng-event-dot[data-kind="filter"]{background:var(--ng-hero-filter)}
.ng-hero .ng-event-dot[data-kind="package"]{background:var(--ng-hero-package)}
.ng-hero .ng-event-dot[data-kind="cnb"]{background:var(--ng-hero-cnb)}
.ng-hero .ng-event-dot[data-kind="pickup"]{background:var(--ng-hero-pickup)}
.ng-hero .ng-event-dot[data-kind="delivery"]{background:var(--ng-hero-delivery)}

/* ── Quiet raised card (tomorrow strip, count panels) ──────────────────── */
.ng-card{background:var(--ng-card);border:1px solid var(--ng-hairline);border-radius:var(--r-card);padding:var(--s-1) var(--s-4)}
.ng-soft-card{background:var(--ng-soft);border-radius:var(--r-card);padding:var(--s-3) var(--s-4)}

/* ════ RIVER-KIT parts (Cole 2026-09-24) ════════════════════════════════════════
   Everything below was hand-rolled page by page before this slice (inventory:
   docs/design/KIT_INVENTORY_2026-09-24.md). Behaviour: static/noli-kit.js. */

/* ── ng-danger-action: destructive. A TEXT action by default (red words, no outline — KIT ROUND 11);
   the filled red is --solid and lives only inside its confirm sheet, never beside the primary. ── */
.ng-danger-action{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--s-2);
  min-height:var(--ng-touch);padding:0 var(--s-3);background:transparent;
  border:0;border-radius:var(--r-pill);
  font-size:var(--t-body);font-weight:600;color:var(--ng-danger);text-decoration:none;white-space:nowrap;
  transition:background var(--ng-fast) var(--ng-ease),color var(--ng-fast) var(--ng-ease);
}
@media (hover:hover){.ng-danger-action:hover{background:var(--ng-danger-bg)}}
.ng-danger-action:active{background:var(--ng-danger-press)}
.ng-danger-action--solid{background:var(--ng-danger);border-color:var(--ng-danger);color:var(--ng-danger-ink)}
@media (hover:hover){.ng-danger-action--solid:hover{background:color-mix(in srgb, var(--ng-danger) 88%, var(--ng-ink1))}}
.ng-danger-action--solid:active{background:color-mix(in srgb, var(--ng-danger) 76%, var(--ng-ink1))}
.ng-danger-action--solid{--ng-spin:var(--ng-danger-ink)}
/* The legacy console rule `button:hover:not(:disabled){background:var(--nc-btn-hover)}` (noli-console.css, loaded on River pages) is not
   limited to a mouse and is more specific than the kit's one-class rules, so a solid danger button that was hovered OR just tapped (sticky
   hover on a phone) went cream with white text and read as disabled. The solid button's own colours are restated at higher specificity
   (no :hover, so it holds in every pointer state), and the kit's mouse-only darkening follows. */
button.ng-danger-action.ng-danger-action--solid:not(:disabled){background:var(--ng-danger);border-color:var(--ng-danger);color:var(--ng-danger-ink)}
@media (hover:hover){button.ng-danger-action.ng-danger-action--solid:hover:not(:disabled){background:color-mix(in srgb, var(--ng-danger) 88%, var(--ng-ink1))}}
button.ng-danger-action.ng-danger-action--solid:active:not(:disabled){background:color-mix(in srgb, var(--ng-danger) 76%, var(--ng-ink1))}
.ng-danger-action:is(:disabled,[aria-disabled="true"]){color:var(--ng-ink3);background:transparent}
/* An action row: primary last on the right on wide screens, full-width stack on a phone. */
.ng-actions{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:var(--s-2) var(--s-3)}
.ng-actions > .ng-primary:where(:not(.mer-save-btn)){width:auto}
@media (max-width:767px){
  .ng-actions{flex-direction:column-reverse;align-items:stretch}
  .ng-actions > .ng-primary:where(:not(.mer-save-btn)){width:100%}
}

/* ── ng-field: label + control + hint / error ─────────────────────────────── */
.ng-field{display:grid;gap:var(--s-1);min-width:0;margin:0}
.ng-field__label{font-size:var(--ng-t-control);font-weight:600;color:var(--ng-ink2)}
.ng-field__hint{font-size:var(--t-dense);color:var(--ng-ink3)}
.ng-field__err{font-size:var(--t-dense);font-weight:600;color:var(--ng-danger)}
.ng-field__err:empty{display:none}
.ng-input,.ng-select,.ng-textarea{
  -webkit-appearance:none;appearance:none;box-sizing:border-box;width:100%;min-height:var(--ng-touch);margin:0;
  padding:0 var(--s-3);border:1px solid var(--ng-control-edge);border-radius:var(--r-std);
  background:var(--ng-card);color:var(--ng-ink1);font-family:var(--ng-font-ui);font-size:var(--ng-t-input);font-weight:500;
  transition:border-color var(--ng-fast) var(--ng-ease),box-shadow var(--ng-fast) var(--ng-ease);
}
/* a generic input[type=search] / select reset (noli-console.css, specificity 0,1,1) was beating .ng-input's
   min-height and left phone search fields at 34px; on a phone the kit's touch size wins again */
@media (max-width:767px){input.ng-input:is([type="search"],[type="number"],[list]),select.ng-select{min-height:var(--ng-touch)}}
.ng-textarea{min-height:calc(2 * var(--ng-touch));padding:var(--s-2) var(--s-3);line-height:1.4;resize:vertical}
.ng-input::placeholder,.ng-textarea::placeholder{color:var(--ng-ink3)}
@media (hover:hover){:is(.ng-input,.ng-select,.ng-textarea):hover{border-color:var(--ng-ink1)}}
:is(.ng-input,.ng-select,.ng-textarea):focus{outline:2px solid var(--ng-focus);outline-offset:0;border-color:transparent}
:is(.ng-input,.ng-select,.ng-textarea)[aria-invalid="true"]{border-color:var(--ng-danger);box-shadow:inset 0 0 0 1px var(--ng-danger)}
:is(.ng-input,.ng-select,.ng-textarea):is(:disabled,[readonly]){background:var(--ng-soft);border-color:transparent;color:var(--ng-ink2);cursor:not-allowed}
.ng-input--num{font-family:var(--ng-font-ui);font-weight:500;font-variant-numeric:tabular-nums;text-align:right}
/* A number / search / list field is the SAME field as a text one. The console's bare
   input[type="number"] rule (specificity 0,1,1) outranks .ng-input (0,1,0) and painted every number
   field tan, 96px wide and thinner than its text neighbours, so it read as disabled. This outranks it. */
input.ng-input:is([type="number"],[type="search"],[list]){
  box-sizing:border-box;width:100%;min-height:var(--ng-touch);padding:0 var(--s-3);
  border:1px solid var(--ng-control-edge);border-radius:var(--r-std);background:var(--ng-card);color:var(--ng-ink1)}
input.ng-input:is([type="number"],[type="search"],[list]):is(:disabled,[readonly]){background:var(--ng-soft);border-color:transparent;color:var(--ng-ink2)}
input.ng-input:is([type="number"],[type="search"],[list])[aria-invalid="true"]{border-color:var(--ng-danger)}
@media (hover:hover){input.ng-input:is([type="number"],[type="search"],[list]):not(:disabled):hover{border-color:var(--ng-ink1)}}
input.ng-input:is([type="number"],[type="search"],[list]):focus{outline:2px solid var(--ng-focus);outline-offset:0;border-color:transparent}
/* A legitimate short receipt is a warning, not invalid data. Keep its
   warning treatment above the numeric input reset in every pointer state. */
input.ng-input.ng-input--warning:is([type="number"],[type="search"],[list]){border-color:var(--ng-warn);box-shadow:inset 0 0 0 1px var(--ng-warn);background:var(--ng-warn-bg)}
@media (hover:hover){input.ng-input.ng-input--warning:is([type="number"],[type="search"],[list]):not(:disabled):hover{border-color:var(--ng-warn)}}
input.ng-input.ng-input--warning:is([type="number"],[type="search"],[list]):focus{outline:2px solid var(--ng-warn);outline-offset:0;border-color:var(--ng-warn)}
/* The wrapper paddings below name input.ng-input so they keep outranking the number/search/list rule above
   (0,2,1): icon and unit clearance must survive it. */
/* ng-unit: a number and the one unit its instrument shows — a fixed label, never a picker
   (KIT ROUND 7). ng-unit--pair: one display that shows two units side by side (the Brew 80's
   BBL and m³) — both typed as read, neither worked out from the other. */
.ng-unit{position:relative;display:block;min-width:0}
.ng-unit > input.ng-input,.ng-unit > .ng-input{width:100%;min-width:0;padding-inline-end:calc(var(--ng-unit-label-w, var(--ng-unit-w)) + var(--s-3) + var(--s-2))} /* page sheets that restyle every input skip .ng-input (noli-mobile.css, distribution_river.css); input.ng-input keeps it above the number/search/list rule */
/* KIT ROUND 11 — the unit sits INSIDE the control's right edge, so the input runs the field's
   full width and lines up with the selects, hints and buttons above and below it. */
.ng-unit__u{position:absolute;right:var(--s-3);top:50%;transform:translateY(-50%);pointer-events:none;
  font-size:var(--t-dense);font-weight:600;color:var(--ng-ink3);white-space:nowrap}
/* a column of readings keeps one right edge: every input in it holds a unit slot, empty or not */
.ng-unit--pair{display:grid;grid-template-columns:1fr 1fr;gap:var(--s-4)}
/* select: the chevron is a mask painted in the ink token, so it follows the theme. */
.ng-select-wrap{position:relative;display:block}
.ng-select-wrap > .ng-select{padding-right:var(--s-8)}
.ng-select-wrap::after{
  content:"";position:absolute;right:var(--s-3);top:50%;width:16px;height:16px;transform:translateY(-50%);
  background:var(--ng-ink2);pointer-events:none;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9.5 6 6 6-6'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9.5 6 6 6-6'/%3E%3C/svg%3E") center/contain no-repeat;
}
/* search: icon inside the field. The field reserves room for its own icon (inset + icon + gap, never
   less than the 40px the kit always had), so the first glyph of the placeholder cannot sit under it.
   The page sheets that restyle every input (body.nm-mobile in noli-mobile.css, .nc-page in
   distribution_river.css) now skip .ng-input; before that they out-specified this rule and reset the
   left padding to 12px, which put the icon over the first letter ("Pind"). A strip search
   (.ng-strip__search) keeps its icon in the flow and is excluded. */
.ng-search{--ng-search-inset:var(--s-3);--ng-search-icon:20px;--ng-search-gap:var(--s-1);position:relative;display:block}
.ng-search:has(> .ng-icon--sm){--ng-search-icon:16px}
.ng-search:has(> .ng-icon--lg){--ng-search-icon:24px}
.ng-search > .ng-icon{position:absolute;left:var(--ng-search-inset);top:50%;transform:translateY(-50%);color:var(--ng-ink3);pointer-events:none}
.ng-search:not(.ng-strip__search) > input.ng-input,.ng-search:not(.ng-strip__search) > .ng-input{padding-inline-start:max(calc(var(--s-8) + var(--s-2)),calc(var(--ng-search-inset) + var(--ng-search-icon) + var(--ng-search-gap)))}
/* the stepper's value can be a typed field (tap the number, type 212) */
input.ng-stepper__value{
  -webkit-appearance:none;appearance:textfield;width:100%;min-height:var(--ng-touch);margin:0;padding:0;
  border:0;border-radius:var(--r-std);background:transparent;color:var(--ng-ink1);
}
input.ng-stepper__value::-webkit-inner-spin-button,input.ng-stepper__value::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}
@media (hover:hover){input.ng-stepper__value:hover{background:var(--ng-soft)}}
input.ng-stepper__value:focus{outline:2px solid var(--ng-focus);outline-offset:0;background:var(--ng-card)}
/* FILTRATION ROUND 14 — empty, the value is a quiet slot to fill (a soft well with a short
   rule, like the blank on the paper sheet), never a "—" that reads as a second minus between
   the − and + keys. NG sets the one-space placeholder that lets :placeholder-shown mark it. */
input.ng-stepper__value:placeholder-shown:not(:focus){background:var(--ng-soft) linear-gradient(var(--ng-hairline-strong),var(--ng-hairline-strong)) no-repeat center calc(100% - var(--s-2)) / var(--s-6) 2px}

/* ── ng-picker: beer (or any object) picker — FULL names, never truncated ── */
.ng-picker{display:grid;gap:var(--s-2);min-width:0}
.ng-picker__list{display:flex;flex-direction:column;margin:0;padding:0;list-style:none;max-height:var(--ng-picker-max, 60vh);overflow:auto;overscroll-behavior:contain}
.ng-picker__group{position:sticky;top:0;z-index:1;margin:0;padding:var(--s-3) var(--s-3) var(--s-1);background:var(--ng-card);
  font-size:var(--t-dense);font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ng-ink3)}
.ng-picker__option{
  display:flex;align-items:center;gap:var(--s-3);width:100%;min-height:calc(var(--ng-touch) + var(--s-3));
  padding:var(--s-2) var(--s-3);border:0;border-radius:var(--r-std);background:none;color:var(--ng-ink1);text-align:left;
  transition:background var(--ng-fast) var(--ng-ease);
}
/* hover only where a mouse can hover: on iPad a tapped row must not stay lit */
@media (hover:hover){.ng-picker__option:hover{background:var(--ng-soft)}}
.ng-picker__option:active{background:var(--ng-soft-press)}
.ng-picker__option[aria-selected="true"]{background:var(--ng-soft)}
.ng-picker__option:is(:disabled,[aria-disabled="true"]){opacity:.5;background:none}
.ng-picker__option[hidden]{display:none}
.ng-picker__text{flex:1;min-width:0}
.ng-picker__name{display:block;font-size:var(--ng-t-strong);font-weight:600;line-height:1.25;overflow-wrap:anywhere}
.ng-picker__sub{display:block;font-size:var(--t-dense);color:var(--ng-ink3)}
.ng-picker__value{font-size:var(--t-body);font-weight:700;font-variant-numeric:tabular-nums;white-space:nowrap;text-align:right}
.ng-picker__value small{display:block;font-size:var(--t-dense);font-weight:500;color:var(--ng-ink3)}
.ng-picker__check{display:grid;place-items:center;width:var(--ng-h-xs);height:var(--ng-h-xs);flex:none;border-radius:var(--r-pill);color:var(--ng-canvas);background:transparent}
.ng-picker__check .ng-icon{width:16px;height:16px;stroke-width:2.5;opacity:0}
.ng-picker__option[aria-selected="true"] .ng-picker__check{background:var(--ng-ink1)}
.ng-picker__option[aria-selected="true"] .ng-picker__check .ng-icon{opacity:1}
.ng-picker__empty{margin:0;padding:var(--s-4) var(--s-3);color:var(--ng-ink2);font-size:var(--t-body)}
/* marker noli-kit.js reads: if absent it loads this sheet itself */
.ng-kit-probe{--ng-kit-loaded:1}
/* KIT BEHAVIOUR 2026-10-06 (kit-behaviour builder): picker is one listbox; the active option shows
   a ring, the listbox itself takes focus. PIN count is announced, not drawn. Sticky layers pad scrolling. */
.ng-picker__list:focus-visible{outline:2px solid var(--ng-focus);outline-offset:2px}
.ng-picker__option[data-active]{box-shadow:inset 0 0 0 2px var(--ng-ink1)}
.ng-picker__list:not(:focus-within) .ng-picker__option[data-active]:not([aria-selected="true"]){box-shadow:none}
html{scroll-padding-top:var(--ng-sticky-top,0px);scroll-padding-bottom:var(--ng-sticky-bottom,0px)}

/* ── ng-row additions ───────────────────────────────────────────────────── */
.ng-rows--divided > * + *{border-top:1px solid var(--ng-hairline)}
/* A step-list row (tank QR, Cole 2026-09-30). --next: the schedule's next open
   step, filled. --done: a RECORDED actual gets the ok mark. --planned: a past
   step the schedule computed but nobody recorded stays neutral — a projection
   is never drawn as a fact (Cole 2026-09-26). --later: still ahead. */
.ng-row--next .ng-row__icon{background:var(--ng-ink1);color:var(--ng-canvas)}
.ng-row--next .ng-row__title{font-weight:700}
.ng-row--done .ng-row__icon{color:var(--ng-ok)}
.ng-row--planned .ng-row__value{color:var(--ng-ink2);font-weight:500}
.ng-row--later .ng-row__title{color:var(--ng-ink2)}
.ng-row--later .ng-row__value{font-weight:600}
/* --fold: one quiet summary row standing in for the steps above the next
   one (tank QR); aria-expanded turns the chevron. */
.ng-row--fold .ng-row__title{color:var(--ng-ink2)}
.ng-row--fold .ng-row__value{font-size:var(--t-dense);font-weight:600;color:var(--ng-ink3)}
.ng-row--fold .ng-row__chev{transition:transform var(--ng-fast) var(--ng-ease)}
.ng-row--fold[aria-expanded="true"] .ng-row__chev{transform:rotate(180deg)}
.ng-row[hidden]{display:none}
.ng-row__chev{color:var(--ng-ink3);flex:none}
.ng-row--action[aria-selected="true"],.ng-row--action[aria-current="true"]{background:var(--ng-soft)}

/* ── ng-sheet additions: body, sticky foot, centred modal on iPad/desktop ── */
.ng-sheet__body{display:grid;gap:var(--s-3);background:var(--ng-card);color:var(--ng-ink1)}
.ng-sheet__foot{
  position:sticky;bottom:0;z-index:1;margin-top:var(--s-4);padding-top:var(--s-3);
  background:var(--ng-card);border-top:1px solid var(--ng-hairline);
  box-shadow:0 var(--s-6) 0 var(--ng-card);
}
.ng-sheet__sub{margin:0;color:var(--ng-ink2);font-size:var(--t-body)}
/* Sheet title hierarchy: the title leads (section size, 600); the eyebrow above it is quiet context, smaller and lighter. */
.ng-sheet__head .ng-section{font-size:var(--ng-t-section);font-weight:600;line-height:1.25}
.ng-sheet__head .ng-eyebrow{font-size:var(--t-dense);font-weight:500;letter-spacing:.04em;margin-bottom:var(--s-1)}
@media (min-width:768px){
  .ng-sheet--center{inset:0;bottom:var(--ng-sheet-keyboard,0px);margin:auto;height:fit-content;max-width:560px;border-radius:var(--r-card);padding:var(--s-6)}
  .ng-sheet--center .ng-sheet__head--sticky{--ng-sheet-head-pad-y:var(--s-6);--ng-sheet-head-pad-x:var(--s-6)}
  .ng-sheet--center .ng-sheet__grab{display:none}
  .ng-sheet--center[open]{animation-name:ng-sheet-pop}
}
@keyframes ng-sheet-pop{from{transform:scale(.98);opacity:0}to{transform:none;opacity:1}}

/* ── ng-id-context: the "what's today" fact shown above an auto-nudged PIN
   pad (KIT-10 round 3) — a gate that opens itself on load must not be the
   first and only thing on the page; this line is read before any digit. ── */
.ng-id-context{margin:0 0 var(--s-3);padding:var(--s-3);border-radius:var(--r-std);
  background:var(--ng-soft);color:var(--ng-ink1);font-size:var(--t-body);font-weight:600;text-align:center}

/* ── ng-pin: the ONE PIN pad (mount with NG.pinPad) ─────────────────────── */
.ng-pin{display:grid;justify-items:center;gap:var(--s-4);width:100%;max-width:320px;margin:0 auto;text-align:center}
.ng-pin__title{font-family:var(--ng-font-title);font-size:var(--ng-t-title);font-weight:400;line-height:1.05;margin:0;color:var(--ng-ink1)}
.ng-pin__sub{margin:calc(-1 * var(--s-2)) 0 0;font-size:var(--t-body);color:var(--ng-ink2)}
.ng-pin__dots{display:flex;align-items:center;justify-content:center;gap:var(--s-3);min-height:var(--s-6)}
.ng-pin__dot{width:12px;height:12px;border-radius:var(--r-pill);border:1.5px solid var(--ng-ink3);background:transparent;
  transition:background var(--ng-fast) var(--ng-ease),border-color var(--ng-fast) var(--ng-ease)}
.ng-pin__dot[data-on]{background:var(--ng-ink1);border-color:var(--ng-ink1)}
.ng-pin[data-state="error"] .ng-pin__dot{border-color:var(--ng-danger)}
.ng-pin[data-state="error"] .ng-pin__dots{animation:ng-shake .32s var(--ng-ease)}
.ng-pin__err{min-height:1.4em;margin:calc(-1 * var(--s-2)) 0 0;font-size:var(--t-dense);font-weight:600;color:var(--ng-danger)}
.ng-pin__keys{display:grid;grid-template-columns:repeat(3,var(--ng-key));gap:var(--s-3) var(--s-6);justify-content:center}
.ng-pin__key{
  position:relative;display:grid;place-items:center;width:var(--ng-key);height:var(--ng-key);padding:0;
  border:0;border-radius:50%;background:var(--ng-soft);color:var(--ng-ink1);box-shadow:var(--ng-shadow-lift);
  font-family:var(--ng-font-ui);font-size:var(--ng-t-key);font-weight:500;font-variant-numeric:tabular-nums;
  transition:background var(--ng-fast) var(--ng-ease),box-shadow var(--ng-fast) var(--ng-ease);
}
@media (hover:hover){.ng-pin__key:hover{background:var(--ng-soft-press)}}
.ng-pin__key:active{background:var(--ng-soft-press);box-shadow:inset 0 1px 2px var(--ng-hairline-strong)}
.ng-pin__key:is(:disabled,[aria-disabled="true"]){opacity:.45;box-shadow:none}
.ng-pin__key--quiet{background:transparent;box-shadow:none;color:var(--ng-ink2);font-size:var(--ng-t-control);font-weight:600}
@media (hover:hover){.ng-pin__key--quiet:hover{background:var(--ng-soft);color:var(--ng-ink1)}}
/* Enter is the pad's one primary: the shared ink tokens (static/noli-ui.css). */
.ng-pin__key--go{background:var(--nc-primary);color:var(--nc-primary-ink);box-shadow:var(--nc-primary-shadow);font-size:var(--ng-t-control);font-weight:600}
@media (hover:hover){.ng-pin__key--go:hover{background:var(--nc-primary-hover)}}
.ng-pin__key--go:active{background:var(--nc-primary-press);box-shadow:inset 0 1px 3px var(--nc-primary-shade)}
.ng-pin__key--go:is(:disabled,[aria-disabled="true"]){background:var(--ng-soft);color:var(--ng-ink2);opacity:1}
.ng-pin[aria-busy="true"] .ng-pin__key--go{color:transparent;pointer-events:none}
.ng-pin[aria-busy="true"] .ng-pin__key--go::after{
  content:"";position:absolute;inset:0;margin:auto;width:var(--ng-spinner);height:var(--ng-spinner);border-radius:var(--r-pill);
  border:2px solid var(--nc-primary-ink);border-right-color:transparent;animation:ng-spin .8s linear infinite;
}
@keyframes ng-shake{20%,60%{transform:translateX(-6px)}40%,80%{transform:translateX(6px)}}

/* ── ng-receipt: what just happened, in plain words, with its numbers ───── */
.ng-receipt{display:grid;gap:var(--s-3);padding:var(--s-4);background:var(--ng-card);border:1px solid var(--ng-hairline);border-radius:var(--r-card)}
.ng-receipt__head{display:flex;align-items:flex-start;gap:var(--s-3)}
.ng-receipt__mark{display:grid;place-items:center;width:var(--ng-pill-h);height:var(--ng-pill-h);flex:none;border-radius:var(--r-pill);background:var(--ng-ok);color:var(--ng-ok-ink)}
.ng-receipt--pending .ng-receipt__mark{background:var(--ng-warn-bg);color:var(--ng-warn)}
.ng-receipt--failed .ng-receipt__mark{background:var(--ng-danger-bg);color:var(--ng-danger)}
.ng-receipt__title{display:block;margin:0;font-size:var(--ng-t-strong);font-weight:700}
.ng-receipt__meta{display:block;font-size:var(--t-dense);color:var(--ng-ink3)}
/* label | value, two columns: the label column is as wide as its longest (short) label and a
   label never wraps; a sentence-long value wraps in its own column. In a card narrower than
   440 px (a phone) each line stacks — the label small above, the value full width below — so
   no label ever breaks onto two or three lines. Labels are written short to fit either way.
   The gutter is the label's own padding, so a total's rule runs unbroken across both columns. */
.ng-receipt{container:ng-receipt / inline-size}
/* ng-receipt--compact: a receipt that sits in the page flow (not a full card): the title and the one line of what
   changed on the left, its actions on the right, so it costs about 100px of page instead of 220. */
.ng-receipt--compact{grid-template-columns:minmax(0,1fr) auto;gap:var(--s-1) var(--s-4);padding:var(--s-2) var(--s-3);align-items:start}
.ng-receipt--compact .ng-receipt__head{grid-column:1}
.ng-receipt--compact .ng-receipt__lines{grid-column:1;display:flex;flex-wrap:wrap;gap:0 var(--s-2);border:0;padding:0}
.ng-receipt--compact .ng-receipt__lines dt{padding:0;font-size:var(--t-dense);white-space:normal}
.ng-receipt--compact .ng-receipt__lines dd{text-align:left;font-size:var(--t-dense);margin-right:var(--s-3)}
.ng-receipt--compact .ng-actions{grid-column:2;grid-row:1 / span 2;flex-direction:column;align-items:flex-end;gap:0}
.ng-receipt__lines{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:var(--s-1) 0;margin:0;padding-top:var(--s-3);border-top:1px dashed var(--ng-hairline-strong)}
.ng-receipt__lines dt{align-self:start;padding-right:var(--s-6);color:var(--ng-ink2);font-size:var(--t-body);white-space:nowrap}
.ng-receipt__lines dd{margin:0;text-align:right;font-size:var(--t-body);font-weight:600;font-variant-numeric:tabular-nums;overflow-wrap:anywhere;text-wrap:pretty}
.ng-receipt__lines .ng-receipt__total{padding-top:var(--s-2);border-top:1px solid var(--ng-hairline);color:var(--ng-ink1);font-weight:700}
@container ng-receipt (max-width: 440px){
  .ng-receipt__lines{grid-template-columns:minmax(0,1fr);gap:0}
  .ng-receipt__lines dt{padding-top:var(--s-2);padding-right:0;font-size:var(--t-dense);color:var(--ng-ink3);white-space:normal}
  .ng-receipt__lines dt:first-child{padding-top:0}
  .ng-receipt__lines dd{text-align:left}
  .ng-receipt__lines dd.ng-receipt__total{padding-top:0;border-top:0}
  .ng-receipt__lines dt.ng-receipt__total{margin-top:var(--s-2);font-size:var(--t-dense)}
}
/* FILTRATION ROUND 16 — a line whose value is a sentence (a fix, a note, a tank change) stacks
   at every width: the label above, the value full width and left-aligned below, instead of
   wrapping ragged down a right-aligned column. The dt and dd both carry the modifier. */
.ng-receipt__lines .ng-receipt__line--stack{grid-column:1 / -1}
.ng-receipt__lines dt.ng-receipt__line--stack{padding-top:var(--s-2);padding-right:0;white-space:normal}
.ng-receipt__lines dd.ng-receipt__line--stack{text-align:left;font-weight:500;overflow-wrap:anywhere}
.ng-receipt__foot{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--s-2)}

/* ── ng-flag: small status word in a tinted pill (always a word, never colour alone) ── */
.ng-flag{
  display:inline-flex;align-items:center;gap:var(--s-1);min-height:var(--ng-h-xs);padding:0 var(--s-2);border-radius:var(--r-pill);
  background:var(--ng-soft);color:var(--ng-ink2);font-size:var(--t-dense);font-weight:600;line-height:1;white-space:nowrap;vertical-align:middle;
}
.ng-flag .ng-icon{width:14px;height:14px}
.ng-flag--ok{background:var(--ng-ok-bg);color:var(--ng-ok)}
.ng-flag--warn{background:var(--ng-warn-bg);color:var(--ng-warn)}
.ng-flag--danger{background:var(--ng-danger-bg);color:var(--ng-danger)}
.ng-flag--info{background:var(--ng-info-bg);color:var(--ng-info)}
/* Dense lists keep the explicit exception words without painting most rows as alarms. Opt-in only. */
.ng-flag--quiet{background:transparent;color:var(--ng-ink2);font-weight:500}
/* a flag that carries a sentence wraps instead of running off the screen */
.ng-flag--wrap{white-space:normal;line-height:1.3;padding-block:var(--s-1)}
/* a verdict flag: the instruction is the loudest word on its row ("Don't count") */
.ng-flag--lg{min-height:var(--ng-h-sm);padding:0 var(--s-3);font-size:var(--t-body);font-weight:700}

/* ── ng-state: empty / loading / error ("Couldn't load — Retry"), NG.state ── */
.ng-state{display:grid;justify-items:center;gap:var(--s-2);padding:var(--s-8) var(--s-4);text-align:center;color:var(--ng-ink2)}
.ng-state--compact{padding:var(--s-4) var(--s-3)}
.ng-state__icon{display:grid;place-items:center;width:var(--ng-touch);height:var(--ng-touch);border-radius:var(--r-pill);background:var(--ng-soft);color:var(--ng-ink2)}
.ng-state--error .ng-state__icon{background:var(--ng-danger-bg);color:var(--ng-danger)}
.ng-state__title{margin:0;font-size:var(--ng-t-strong);font-weight:700;color:var(--ng-ink1)}
.ng-state__text{margin:0;max-width:40ch;font-size:var(--t-body)}
.ng-state > .ng-outline-action,.ng-state > .ng-text-action{margin-top:var(--s-1)}
.ng-spinner{display:inline-block;width:20px;height:20px;flex:none;border-radius:var(--r-pill);border:2px solid var(--ng-hairline-strong);border-top-color:var(--ng-ink1);animation:ng-spin .8s linear infinite}
.ng-skeleton{display:block;height:12px;border-radius:var(--r-pill);background:linear-gradient(90deg,var(--ng-soft) 0%,var(--ng-soft-press) 50%,var(--ng-soft) 100%);background-size:200% 100%;animation:ng-shimmer 1.4s ease-in-out infinite}
.ng-skeleton--title{height:20px;width:60%}
.ng-skeleton--row{height:var(--ng-touch);border-radius:var(--r-std)}
@keyframes ng-shimmer{from{background-position:100% 0}to{background-position:-100% 0}}
/* save indicator: Saving… / Saved / Couldn't save */
.ng-save{display:inline-flex;align-items:center;gap:var(--s-1);min-height:var(--ng-h-xs);font-size:var(--t-dense);font-weight:600;color:var(--ng-ink3)}
.ng-save .ng-spinner{width:14px;height:14px}
.ng-save[data-state="saved"]{color:var(--ng-ok)}
.ng-save[data-state="failed"]{color:var(--ng-danger)}

/* ── ng-head: back · actions / eyebrow + serif title / one line under ───── */
.ng-head{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;column-gap:var(--s-3);row-gap:var(--s-1);margin:0 0 var(--s-4)}
.ng-head__back{justify-self:start;margin-left:calc(-1 * var(--s-1))}
.ng-head__actions{display:flex;align-items:center;justify-content:flex-end;gap:var(--s-2);grid-column:2;min-width:0}
/* KIT ROUND 11 — the save line in a head never wraps onto the back link: it is one line and gives way (ellipsis) */
.ng-head__actions .ng-save{min-width:0;max-width:var(--ng-head-status-max);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* KIT ROUND 15 - a phone head: the back link takes its own width, the tools (mic, name, theme) are
   flex siblings in the row beside it, and the save words drop to their own line under the tools
   instead of printing over the back link. Nothing in the head is ever positioned over another. */
@media (max-width:520px){
  .ng-head{grid-template-columns:auto minmax(0,1fr)}
  .ng-head__back{align-self:start}
  /* the status row is RESERVED: the tools cell is always one touch-row plus the status line tall,
     so the head does not jump when "Saved" appears */
  .ng-head__actions:has(.ng-save){min-height:calc(var(--ng-touch) + var(--ng-status-h));align-content:flex-start}
  /* the name is initials in a circle; the full name is the button's label and title */
  .ng-idpill[data-signed]{width:var(--ng-touch);height:var(--ng-touch);box-sizing:border-box;line-height:calc(var(--ng-touch) - 2px);max-width:none;padding:0;border:1px solid var(--ng-hairline-strong);border-radius:var(--r-pill);text-align:center;overflow:visible;color:var(--ng-ink1)}
  .ng-idpill[data-signed] .ng-idpill__full{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
  .ng-idpill[data-signed] .ng-idpill__ini{display:inline}
  .ng-head__actions{flex-wrap:wrap;row-gap:0;column-gap:var(--s-1)}
  .ng-head__actions > .ng-text-action{min-width:0;max-width:var(--ng-head-action-max);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;line-height:var(--ng-touch);padding:0 var(--s-2)}
  .ng-head__actions > *{flex:none}
  .ng-head__actions .ng-save{order:9;flex:0 0 100%;max-width:none;justify-content:flex-end;min-height:var(--ng-status-h)}
}
.ng-head__text{grid-column:1 / -1;min-width:0;margin-top:var(--s-1)}
.ng-head__text .ng-title{margin-top:var(--s-0)}
.ng-head__sub{margin:var(--s-1) 0 0;font-size:var(--t-body);color:var(--ng-ink2)}

/* ── ng-toast: one message, above the tab bar when there is one (NG.toast) ── */
.ng-toasts{
  position:fixed;left:50%;transform:translateX(-50%);z-index:60;
  bottom:calc(var(--s-4) + env(safe-area-inset-bottom, 0px));
  display:grid;gap:var(--s-2);width:min(calc(100% - 2 * var(--s-4)), 480px);margin:0;padding:0;list-style:none;pointer-events:none;
  margin-bottom:var(--ng-dock-lift,0px);
}
/* KIT ROUND 10 — the floating tab bar has its OWN LANE at every crew width (phone, iPad
   portrait and landscape): an opaque band of canvas from the screen's foot (through the
   home-indicator inset) to just above the bar. --ng-lane is that band's height, and the one
   number everything else reads: the page's last row ends above it, the docked Next / Sign
   rests on its edge, a toast rises clear of it. Nothing the crew reads or taps is ever drawn
   under the bar — before round 10 a phone scrolled the form under a see-through bar. (The
   class is for browsers without :has.) */
body:has(.ng-tabbar:not(.ng-tabbar--inline):not([hidden])),.ng-has-tabbar{
  --ng-lane:calc(var(--s-4) + var(--ng-tabbar-h) + var(--s-3) + env(safe-area-inset-bottom, 0px));
  padding-bottom:calc(var(--ng-lane) + var(--s-3) + var(--ng-toast-room,0px));
}
body:has(.ng-tabbar:not(.ng-tabbar--inline):not([hidden])) :is(.ng-toasts,.ng-dock),.ng-has-tabbar :is(.ng-toasts,.ng-dock){
  bottom:var(--ng-lane);
}
/* The River desktop shell's own phone tab bar (.rs-tabbar) floats over the foot of the screen:
   a toast on a River page rises clear of it instead of hiding behind it. */
@media (max-width:699px){
  body:has(.rs-tabbar) .ng-toasts:not(.ng-toasts--inline){bottom:calc(var(--s-6, 24px) + var(--ng-shell-tabbar-h) + env(safe-area-inset-bottom, 0px))}
}
/* KIT ROUND 11 — the docked action clears the tab bar's lane with air to spare, and the page's
   own last controls (a quiet Discard under the form) are never left under the dock's plate. */
body:has(.ng-tabbar:not(.ng-tabbar--inline):not([hidden])) .ng-dock,.ng-has-tabbar .ng-dock{
  bottom:calc(var(--ng-lane) + var(--s-2));margin-bottom:var(--s-4);
}
body:has(.ng-tabbar:not(.ng-tabbar--inline):not([hidden]))::after,.ng-has-tabbar::after{
  content:"";position:fixed;left:0;right:0;bottom:0;z-index:39;pointer-events:none;
  height:var(--ng-lane);
  background:var(--ng-canvas);border-top:1px solid var(--ng-hairline);
}
.ng-toasts--inline{position:static;transform:none;width:auto;max-width:480px}
/* inside an open sheet (NG.toast puts it there) the toast sits at the top, so it never
   covers the sheet's own Save / Send in the sticky foot */
.ng-sheet > .ng-toasts{top:calc(var(--s-4) + env(safe-area-inset-top, 0px));bottom:auto}
.ng-toast{
  pointer-events:auto;display:flex;align-items:center;gap:var(--s-3);min-height:var(--ng-touch-primary);
  padding:var(--s-1) var(--s-2) var(--s-1) var(--s-4);border-radius:var(--r-card);
  background:var(--ng-toast);color:var(--ng-toast-ink);border:1px solid var(--ng-toast-hair);box-shadow:var(--ng-shadow-raised);
  font-size:var(--t-body);font-weight:600;animation:ng-toast-in var(--ng-std) var(--ng-ease);
}
.ng-toast > .ng-icon{color:var(--ng-toast-ok)}
/* a refused save needs to outrank the ng-sync summary pill above it (KIT-10 round 3, critic
   2026-09-25), not read calmer than it — a tint + accent edge, text stays full contrast */
.ng-toast--danger{background:color-mix(in srgb, var(--ng-toast-danger) 14%, var(--ng-toast));
  border-left:3px solid var(--ng-toast-danger);padding-left:calc(var(--s-4) - 3px)}
.ng-toast--danger > .ng-icon{color:var(--ng-toast-danger)}
.ng-toast__text{flex:1;min-width:0;padding:var(--s-2) 0}
.ng-toast__action{
  min-height:var(--ng-touch);padding:0 var(--s-3);border:0;border-radius:var(--r-std);background:none;
  color:var(--ng-toast-ink);font-size:var(--ng-t-control);font-weight:700;white-space:nowrap;
  text-decoration:underline;text-underline-offset:3px;transition:background var(--ng-fast) var(--ng-ease);
}
@media (hover:hover){.ng-toast__action:hover{background:var(--ng-toast-hair)}}
.ng-toast__action:active{opacity:.7}
.ng-toast__action:focus-visible{outline-color:var(--ng-toast-ink)}
/* KIT-10: the Close on a toast that waits for the person (NG.toast ms 0) */
.ng-toast__close{
  display:grid;place-items:center;width:var(--ng-touch);height:var(--ng-touch);flex:none;padding:0;border:0;
  border-radius:var(--r-std);background:none;color:var(--ng-toast-ink);transition:background var(--ng-fast) var(--ng-ease);
}
@media (hover:hover){.ng-toast__close:hover{background:var(--ng-toast-hair)}}
.ng-toast__close:active{opacity:.7}
.ng-toast__close:focus-visible{outline:2px solid var(--ng-toast-ink);outline-offset:-2px}
@keyframes ng-toast-in{from{transform:translateY(8px);opacity:0}to{transform:none;opacity:1}}

/* ── ng-table: dense desktop worksheet (numbers right, tabular) ─────────── */
.ng-table-wrap{overflow:auto;border:1px solid var(--ng-hairline);border-radius:var(--r-card);background-color:var(--ng-card)}
/* KIT ROUND 16 - a table that scrolls sideways shows it: a soft edge fades in on whichever side
   has more to see, and is gone when there is nothing more (the classic local/scroll trick). */
.ng-table-wrap{background-image:
  linear-gradient(to right,var(--ng-card),var(--ng-card)),linear-gradient(to left,var(--ng-card),var(--ng-card)),
  linear-gradient(to right,var(--ng-hairline-strong),transparent),linear-gradient(to left,var(--ng-hairline-strong),transparent);
  background-position:0 0,100% 0,0 0,100% 0;background-size:var(--s-6) 100%,var(--s-6) 100%,var(--s-4) 100%,var(--s-4) 100%;
  background-repeat:no-repeat;background-attachment:local,local,scroll,scroll}
.ng-table{width:100%;border-collapse:collapse;font-size:var(--t-body);font-variant-numeric:tabular-nums;color:var(--ng-ink1)}
.ng-table th{position:sticky;top:0;padding:var(--s-2) var(--s-3);background:var(--ng-card);border-bottom:1px solid var(--ng-hairline-strong);
  text-align:left;font-size:var(--t-dense);font-weight:600;color:var(--ng-ink3);white-space:nowrap}
.ng-table td{height:var(--ng-touch);padding:var(--s-1) var(--s-3);border-bottom:1px solid var(--ng-hairline);vertical-align:middle}
.ng-table tbody:last-of-type tr:last-child td{border-bottom:0}
@media (hover:hover){.ng-table tbody tr:hover td{background:var(--ng-soft)}}
.ng-table tbody tr[aria-selected="true"] td{background:var(--ng-soft-press)}
/* a state tint over the selected row's deeper ground needs a touch more ink to hold 4.5:1 */
.ng-table tbody tr:is([aria-selected="true"],[data-selected],[data-active],:has(.ng-check__input:checked)) .ng-flag--info{color:color-mix(in srgb,var(--ng-info),var(--ng-ink1) 28%)}
.ng-table tbody tr:is([aria-selected="true"],[data-selected],[data-active],:has(.ng-check__input:checked)) .ng-flag--warn{color:color-mix(in srgb,var(--ng-warn),var(--ng-ink1) 28%)}
.ng-table tbody tr:is([aria-selected="true"],[data-selected],[data-active],:has(.ng-check__input:checked)) .ng-flag--ok{color:color-mix(in srgb,var(--ng-ok),var(--ng-ink1) 28%)}
.ng-table tbody tr:is([aria-selected="true"],[data-selected],[data-active],:has(.ng-check__input:checked)) .ng-flag--danger{color:color-mix(in srgb,var(--ng-danger),var(--ng-ink1) 28%)}
.ng-table .ng-table__num{text-align:right;white-space:nowrap}

/* ── ng-progress: how far through (counted 40 of 112) ───────────────────── */
.ng-progress{display:block;height:6px;border-radius:var(--r-pill);background:var(--ng-soft);overflow:hidden}
.ng-progress__bar{display:block;height:100%;width:var(--ng-progress, 0%);border-radius:inherit;background:var(--ng-ink1);transition:width var(--ng-std) var(--ng-ease)}
.ng-progress--ok .ng-progress__bar{background:var(--ng-ok)}

/* ════ KIT ROUND 2 (2026-09-24) ════════════════════════════════════════════════
   What a real crew shift needed that round 1 lacked: a stepper that knows its unit and
   says when a typed number is out of range, a queue badge on the tab bar, a lookup table
   that stacks on a phone and opens a record in one tap, a compact PIN pad, a receipt that
   carries who / when / what next, flow steps, and a chain of readings. */

/* ── ng-stepper: unit + out-of-range. The kit reads data-min/data-max; the PAGE derives
   them from context (the brite's size, what went in) and writes the words (data-over /
   data-under). ± keys clamp; a TYPED number outside the range is kept and flagged, never
   silently changed — a typo must stay visible. ── */
.ng-stepper__unit{grid-column:4;min-width:0;font-size:var(--t-dense);font-weight:600;color:var(--ng-ink3)}
.ng-stepper__value[aria-invalid="true"]{color:var(--ng-danger);border-radius:var(--r-std);box-shadow:inset 0 0 0 2px var(--ng-danger)}
/* On a narrow phone the stepper drops under its label instead of squeezing it. */
.ng-count{flex-wrap:wrap}
.ng-count__text{flex:1 1 180px;min-width:0}
.ng-count > .ng-stepper{margin-left:auto}
.ng-count__text .ng-field__hint,.ng-count__text .ng-field__err{display:block;margin-top:var(--s-0)}
.ng-count__text .ng-field__err:empty{display:none}

/* ── ng-tabbar badge: the one queue count (blueprint §7 nc-queue-badge) ── */
.ng-tabbar__item{position:relative}
.ng-tabbar__badge{
  position:absolute;top:var(--s-0);left:calc(50% + var(--s-2));
  display:grid;place-items:center;min-width:var(--ng-spinner);height:var(--ng-spinner);padding:0 var(--s-1);border-radius:var(--r-pill);
  background:var(--ng-red);color:var(--ng-red-ink);font-size:var(--t-dense);font-weight:700;line-height:1;font-variant-numeric:tabular-nums;
}

/* ── ng-table: a row that opens its record in one tap, and the phone stack ── */
.ng-table tbody tr{position:relative}
.ng-table__open{display:inline-flex;align-items:center;min-height:var(--ng-h-xs);color:inherit;font-weight:600;text-decoration:none}
.ng-table__open::after{content:"";position:absolute;inset:0}
.ng-table__open:focus-visible{outline:2px solid var(--ng-focus);outline-offset:2px;border-radius:var(--r-std)}
.ng-table tbody tr:has(.ng-table__open:focus-visible){outline:2px solid var(--ng-focus);outline-offset:-2px}
.ng-table tbody tr:has(.ng-table__open):active td{background:var(--ng-soft-press)}
.ng-table__chev{color:var(--ng-ink3);width:var(--s-8);text-align:right}
/* Below 600 px a .ng-table--stack becomes one card per row: the name (.ng-table__key) and
   its status (.ng-table__status) on the first line, then every other column as
   label · value, labels from each cell's data-label. Nothing scrolls sideways. */
@media (max-width:599px){
  .ng-table--stack thead{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
  .ng-table--stack,.ng-table--stack tbody{display:block}
  .ng-table--stack tbody tr{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:var(--s-1) var(--s-3);padding:var(--s-3) var(--s-4);border-bottom:1px solid var(--ng-hairline)}
  .ng-table--stack tbody tr:last-child{border-bottom:0}
  .ng-table--stack td{display:flex;justify-content:space-between;align-items:baseline;gap:var(--s-3);grid-column:1 / -1;height:auto;padding:0;border:0;background:none}
  .ng-table--stack td[data-label]::before{content:attr(data-label);font-size:var(--t-dense);font-weight:600;color:var(--ng-ink3)}
  .ng-table--stack .ng-table__key{grid-column:1;order:-2;font-size:var(--ng-t-strong);overflow-wrap:anywhere}
  .ng-table--stack .ng-table__status{grid-column:2;order:-1;justify-self:end;align-self:start}
  .ng-table--stack .ng-table__chev{display:none}
}

/* ── ng-pin--compact: the same pad, smaller, for a pad inside a flow or a card ── */
.ng-pin--compact{--ng-key:56px;--ng-t-key:20px;gap:var(--s-3);max-width:280px}
.ng-pin--compact .ng-pin__title{font-size:var(--ng-t-count)}
.ng-pin--compact .ng-pin__keys{gap:var(--s-2) var(--s-4)}

/* ── ng-receipt: who did it, and where it goes next (the trust spine) ── */
.ng-receipt__who{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-1) var(--s-2);font-size:var(--t-dense);color:var(--ng-ink2)}
.ng-receipt__trail{display:grid;margin:0;padding:var(--s-3) 0 0;list-style:none;border-top:1px solid var(--ng-hairline)}
.ng-receipt__stop{position:relative;display:grid;grid-template-columns:16px minmax(0,1fr);column-gap:var(--s-3);padding-bottom:var(--s-3)}
.ng-receipt__stop:last-child{padding-bottom:0}
.ng-receipt__stop::before{
  content:"";grid-row:1 / span 2;justify-self:center;margin-top:var(--s-1);width:10px;height:10px;box-sizing:border-box;
  border-radius:var(--r-pill);border:2px solid var(--ng-hairline-strong);background:var(--ng-card);
}
.ng-receipt__stop:not(:last-child)::after{content:"";position:absolute;left:7px;top:18px;bottom:0;width:2px;background:var(--ng-hairline)}
.ng-receipt__stop[data-state="done"]::before{background:var(--ng-ok);border-color:var(--ng-ok)}
.ng-receipt__stop[data-state="next"]::before{border-color:var(--ng-warn)}
.ng-receipt__stop-title{grid-column:2;font-size:var(--t-body);font-weight:600;color:var(--ng-ink1)}
.ng-receipt__stop-meta{grid-column:2;font-size:var(--t-dense);color:var(--ng-ink3)}

/* ── ng-steps: where you are in a flow (pick → check → read → sign → receipt) ── */
.ng-steps{display:flex;gap:var(--s-1);margin:0;padding:0;list-style:none;overflow-x:auto;scrollbar-width:none}
.ng-steps::-webkit-scrollbar{display:none}
.ng-steps__item{
  display:inline-flex;align-items:center;gap:var(--s-2);min-height:var(--ng-touch);padding:0 var(--s-3) 0 var(--s-1);
  border:0;border-radius:var(--r-pill);background:none;font-size:var(--ng-t-control);font-weight:600;color:var(--ng-ink3);white-space:nowrap;
  transition:color var(--ng-fast) var(--ng-ease);
}
.ng-steps__num{display:grid;place-items:center;width:var(--ng-h-xs);height:var(--ng-h-xs);flex:none;border-radius:var(--r-pill);box-shadow:inset 0 0 0 1px var(--ng-hairline-strong);font-variant-numeric:tabular-nums}
.ng-steps__num .ng-icon{width:14px;height:14px;stroke-width:2.5}
@media (hover:hover){.ng-steps__item:hover{color:var(--ng-ink1)}}
.ng-steps__item:active{opacity:.7}
.ng-steps__item[data-done]{color:var(--ng-ink2)}
.ng-steps__item[data-done] .ng-steps__num{background:var(--ng-ok);color:var(--ng-ok-ink);box-shadow:none}
.ng-steps__item[aria-current="step"]{color:var(--ng-ink1)}
.ng-steps__item[aria-current="step"] .ng-steps__num{background:var(--ng-ink1);color:var(--ng-canvas);box-shadow:none}
.ng-steps__item:is(:disabled,[aria-disabled="true"]){opacity:1;color:var(--ng-ink3);cursor:default}
/* KIT ROUND 11 — an upcoming step is never faded: it keeps the ink3 token's full contrast (>=4.5:1,
   opacity included); "not open yet" is said by the number's dashed ring, not by dimming the words. */
.ng-steps__item:is(:disabled,[aria-disabled="true"]):not([data-done]):not([data-waiting]) .ng-steps__num{box-shadow:none;border:1px dashed var(--ng-ink3)}
/* FILTRATION ROUND 18 — a step handed on and waiting on someone else (a run sent to its
   approver): a warn ring and a clock, at full strength even though it can't be opened —
   never a greyed check beside the done ones. */
.ng-steps__item[data-waiting]{color:var(--ng-ink2)}
/* ...and a done step that can't be reopened (a signed run's Sign) keeps its green check. */
.ng-steps__item[data-done]:is(:disabled,[aria-disabled="true"]){opacity:1}
.ng-steps__item[data-waiting]:is(:disabled,[aria-disabled="true"]){opacity:1}
.ng-steps__item[data-waiting] .ng-steps__num{background:var(--ng-warn-bg);color:var(--ng-ink1);box-shadow:inset 0 0 0 2px var(--ng-warn)}

/* ── ng-chain: readings that only mean something together (DO: fermenter → into brite →
   carbonated), read left to right; each step may carry an input and a flag. ── */
.ng-chain{display:grid;grid-template-columns:repeat(auto-fit,minmax(96px,1fr));gap:var(--s-2);margin:0;padding:0;list-style:none}
.ng-chain__step{display:grid;align-content:start;gap:var(--s-1);min-width:0;padding:var(--s-3);border-radius:var(--r-std);background:var(--ng-soft)}
.ng-chain__label{font-size:var(--t-dense);font-weight:600;color:var(--ng-ink2)}
.ng-chain__step .ng-flag{justify-self:start;white-space:normal;line-height:1.2;padding-block:var(--s-1)}

/* ── ng-stepnav: the step strip, and below 600 px one line in its place — the step you are
   on, a progress bar, and "All steps" opening the same strip as a list (noli-kit.js builds
   the bar). The strip never clips off the edge of a phone. ── */
.ng-stepnav__bar{display:none}
@media (max-width:599px){
  .ng-stepnav__bar{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:var(--s-1) var(--s-3);padding:var(--s-2) var(--s-2) 0}
  .ng-stepnav__num{background:var(--ng-ink1);color:var(--ng-canvas);box-shadow:none;width:var(--ng-h-sm);height:var(--ng-h-sm);font-size:var(--ng-t-control);font-weight:700}
  .ng-stepnav__text{display:flex;flex-direction:column;min-width:0}
  .ng-stepnav__title{font-size:var(--ng-t-strong);font-weight:700;color:var(--ng-ink1)}
  .ng-stepnav__sub{font-size:var(--t-dense);color:var(--ng-ink3)}
  .ng-stepnav__toggle{display:inline-flex;align-items:center;gap:var(--s-1);min-height:var(--ng-touch);padding:0 var(--s-1);border:0;border-radius:var(--r-std);
    background:none;font:600 var(--ng-t-control)/1 var(--ng-font-ui);color:var(--ng-link)}
  .ng-stepnav__toggle .ng-icon{width:16px;height:16px;transition:transform var(--ng-fast) var(--ng-ease)}
  .ng-stepnav[data-open] .ng-stepnav__toggle .ng-icon{transform:rotate(180deg)}
  .ng-stepnav__progress{grid-column:1 / -1;height:4px}
  .ng-stepnav > .ng-steps{display:none}
  .ng-stepnav[data-open] > .ng-steps{display:flex;flex-direction:column;gap:0;margin-top:var(--s-2);padding:var(--s-1) 0;border-top:1px solid var(--ng-hairline)}
  .ng-stepnav[data-open] .ng-steps__item{width:100%;border-radius:var(--r-std);padding:0 var(--s-2)}
}

/* ── ng-group: one ordered block of a long form, in the order the work happens. The open
   block shows its fields; a closed one shows what it holds in one line, and a word for
   its state (Done / 2 needed / Fix 1). ── */
.ng-groups{display:flex;flex-direction:column;margin:0;padding:0;list-style:none;border-top:1px solid var(--ng-hairline)}
.ng-group{border-bottom:1px solid var(--ng-hairline)}
.ng-group__head{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:var(--s-3);width:100%;min-height:calc(var(--ng-touch) + var(--s-3));
  padding:var(--s-2) 0;border:0;background:none;text-align:left;font:inherit;color:var(--ng-ink1)}
.ng-group__head .ng-steps__num{width:var(--ng-h-sm);height:var(--ng-h-sm);font-size:var(--ng-t-control);font-weight:700;color:var(--ng-ink2)}
.ng-group[data-state="done"] .ng-group__head .ng-steps__num{background:var(--ng-ok);color:var(--ng-ok-ink);box-shadow:none}
.ng-group[data-open] .ng-group__head .ng-steps__num{background:var(--ng-ink1);color:var(--ng-canvas);box-shadow:none}
.ng-group__text{display:flex;flex-direction:column;min-width:0}
.ng-group__title{font-size:var(--ng-t-strong);font-weight:700}
.ng-group__sum{font-size:var(--t-dense);color:var(--ng-ink3);overflow-wrap:anywhere}
.ng-group__body{padding:0 0 var(--s-6)}
.ng-group:not([data-open]) > .ng-group__body{display:none}
@media (hover:hover){.ng-group__head:hover .ng-group__title{text-decoration:underline;text-underline-offset:3px}}

/* ── ng-dock: the next / sign action, always in reach while a long form scrolls ── */
.ng-dock{position:sticky;bottom:max(var(--s-3), env(safe-area-inset-bottom));z-index:3;display:flex;flex-direction:column;gap:var(--s-2);
  margin-top:var(--s-4);padding:var(--s-3);border-radius:var(--r-card);background:var(--ng-card);
  box-shadow:0 0 0 1px var(--ng-hairline),var(--ng-shadow-raised)}
.ng-dock__why{margin:0;font-size:var(--t-dense);color:var(--ng-ink2);display:flex;gap:var(--s-2);align-items:flex-start}
.ng-dock__why .ng-icon{flex:none;width:16px;height:16px;color:var(--ng-warn)}
.ng-dock__row{display:flex;align-items:center;justify-content:space-between;gap:var(--s-3)}
.ng-dock__row > .ng-primary:where(:not(.mer-save-btn)){width:auto;flex:0 1 auto}
.ng-dock__where{font-size:var(--t-dense);color:var(--ng-ink3);min-width:0}
@media (max-width:599px){.ng-dock__row > .ng-primary:where(:not(.mer-save-btn)){flex:1}.ng-dock__where{display:none}}
/* KIT ROUND 9 — two extra shadows in the ground's colour make a plate round the dock: one
   spread past its rounded corners, one under it down to the screen's foot, so a field scrolling
   past never peeks round a corner or through the gap below (a stepper's + key did, at iPad
   landscape). --ng-dock-under is the ground the dock sits on: the canvas, or a card's colour. */
.ng-dock{box-shadow:0 0 0 1px var(--ng-hairline),var(--ng-shadow-raised),0 0 0 var(--s-3) var(--ng-dock-under, var(--ng-canvas)),
  0 calc(var(--s-6) + env(safe-area-inset-bottom, 0px)) 0 0 var(--ng-dock-under, var(--ng-canvas))}
.ng-card .ng-dock{--ng-dock-under:var(--ng-card)}
/* a field focused (or scrolled to) inside a docked form lands above the dock, never under it */
html:has(.ng-dock){scroll-padding-bottom:calc(var(--ng-touch-primary) + var(--s-8) * 3 + var(--ng-toast-room,0px))}

/* ── ng-trend: one reading across a beer's past runs, today last. Faint = before an
   equipment switch (never in the range); the band = the range from runs that count. ── */
.ng-trend{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s-1);min-width:0}
.ng-trend > *{min-width:0}
.ng-trend__plot{display:block;overflow:visible}
.ng-trend__band{fill:var(--ng-ok-bg)}
.ng-trend__base{stroke:var(--ng-hairline-strong);stroke-width:1}
.ng-trend__switch{stroke:var(--ng-ink3);stroke-width:1;stroke-dasharray:3 3}
.ng-trend__switch-label{fill:var(--ng-ink3);font-family:var(--ng-font-ui);font-size:var(--t-micro);font-weight:600}
/* FILTRATION ROUND 14 — the scale's two ends, in a left gutter, so a dot reads as a number */
.ng-trend__scale{fill:var(--ng-ink3);font-family:var(--ng-font-ui);font-size:var(--t-micro);font-weight:600;font-variant-numeric:tabular-nums}
.ng-trend__dot{fill:var(--ng-ink2);stroke:var(--ng-card);stroke-width:2}
.ng-trend__dot--old{fill:var(--ng-hairline-strong)}
.ng-trend__today{fill:var(--ng-ink1);stroke:var(--ng-card);stroke-width:2}
.ng-trend__today--out{fill:var(--ng-warn)}
.ng-trend__cap{margin:0;font-size:var(--t-dense);color:var(--ng-ink2)}

/* ════ KIT ROUND 9 (2026-09-24) — the platform pass ═════════════════════════════════
   The kit is platform UI: crew iPad workflows AND the owner's desktop (1440) and iPhone
   look-ups. What both critics found missing: a dense owner worksheet with money conventions,
   one shared workflow gate, the stepper's range contract on typed fields, a date field and a
   no-future-time guard, a queued / offline save state, a crew task with due / overdue / done,
   a four-stop transfer receipt, signed and queued receipts, an editable grid, a costly-action
   confirm with a money PIN — and desktop never carrying the crew tab bar. */

/* ── Desktop never carries the crew tab bar (desktop and mobile never blend) ──
   Under desktop density on a wide screen with a mouse the floating bar, its lane and every
   lift it gives the page, the dock and toasts are gone. The same page on an iPad keeps it. */
@media (min-width:768px) and (pointer:fine){
  :is(.ng-desktop .ng-tabbar,.ng-desktop.ng-tabbar,body:has(> .ng-desktop) > .ng-tabbar):not(.ng-tabbar--inline){display:none}
  body:has(.ng-tabbar:not(.ng-tabbar--inline):not([hidden])):is(.ng-desktop,:has(> .ng-desktop)){padding-bottom:0}
  body:has(.ng-tabbar:not(.ng-tabbar--inline):not([hidden])):is(.ng-desktop,:has(> .ng-desktop))::after{display:none}
  body:has(.ng-tabbar:not(.ng-tabbar--inline):not([hidden])):is(.ng-desktop,:has(> .ng-desktop)) .ng-dock{bottom:max(var(--s-3), env(safe-area-inset-bottom))}
  body:has(.ng-tabbar:not(.ng-tabbar--inline):not([hidden])):is(.ng-desktop,:has(> .ng-desktop)) .ng-toasts:not(.ng-toasts--inline){bottom:var(--s-4)}
}

/* ── ng-fields: a grid of fields with no orphan cell. 2, 3 or 4 across on a wide screen, one
   on a phone. The last row always fills: in a 3-up grid one leftover field runs full width
   and two leftovers share it half and half (a 12-track grid underneath). Hidden fields are
   not counted (:nth-child(… of :not([hidden]))), so a field a page switches off leaves no hole. ── */
.ng-fields{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s-4);align-items:start}
.ng-fields > .ng-fields__full{grid-column:1 / -1}
@media (min-width:640px){
  .ng-fields{grid-template-columns:repeat(2,minmax(0,1fr))}
  .ng-fields > :nth-last-child(1 of :not([hidden])):nth-child(odd of :not([hidden])){grid-column:1 / -1}
  .ng-fields--3,.ng-fields--4{grid-template-columns:repeat(12,minmax(0,1fr))}
  .ng-fields--3 > *{grid-column:span 4}
  .ng-fields--3 > :nth-last-child(1 of :not([hidden])):nth-child(3n+1 of :not([hidden])){grid-column:1 / -1}
  .ng-fields--3 > :nth-last-child(2 of :not([hidden])):nth-child(3n+1 of :not([hidden])),
  .ng-fields--3 > :nth-last-child(1 of :not([hidden])):nth-child(3n+2 of :not([hidden])){grid-column:span 6}
  .ng-fields--3 > :nth-last-child(1 of :not([hidden])):nth-child(3n of :not([hidden])){grid-column:span 4}   /* a third field that ends the row stays a third, not the odd-cell stretch above */
  .ng-fields--4 > *{grid-column:span 6}
  .ng-fields--4 > :nth-last-child(1 of :not([hidden])):nth-child(odd of :not([hidden])){grid-column:1 / -1}
  .ng-fields--3 > .ng-fields__full,.ng-fields--4 > .ng-fields__full{grid-column:1 / -1}
}
/* FILTRATION ROUND 15 — a row of one field that stays half width, like the pairs around it
   (the orphan rule above would stretch it across): the cell still spans, the field doesn't. */
@media (min-width:640px){.ng-fields--half > *{max-width:calc((100% - var(--s-4)) / 2)}}
@media (min-width:900px){
  .ng-fields--4 > *{grid-column:span 3}
  .ng-fields--4 > :nth-last-child(1 of :not([hidden])):nth-child(4n+1 of :not([hidden])){grid-column:1 / -1}
  .ng-fields--4 > :nth-last-child(2 of :not([hidden])):nth-child(4n+1 of :not([hidden])),
  .ng-fields--4 > :nth-last-child(1 of :not([hidden])):nth-child(4n+2 of :not([hidden])){grid-column:span 6}
  .ng-fields--4 > :nth-last-child(3 of :not([hidden])):nth-child(4n+1 of :not([hidden])),
  .ng-fields--4 > :nth-last-child(2 of :not([hidden])):nth-child(4n+2 of :not([hidden])),
  .ng-fields--4 > :nth-last-child(1 of :not([hidden])):nth-child(4n+3 of :not([hidden])){grid-column:span 4}
  .ng-fields--4 > .ng-fields__full{grid-column:1 / -1}
}

/* ── Date and clock fields: the UI face, tabular, and the picker's own glyph in the theme ── */
.ng-input:is([type="date"],[type="time"]){font-variant-numeric:tabular-nums;text-align:left;color-scheme:light}
:root[data-theme="dark"] .ng-input:is([type="date"],[type="time"]),html.nc-dark .ng-input:is([type="date"],[type="time"]){color-scheme:dark}
@media (prefers-color-scheme: dark){:root:not([data-theme="light"]) .ng-input:is([type="date"],[type="time"]){color-scheme:dark}}

/* ── ng-table worksheet: the owner's dense desktop tables (month-end, tip-outs, the schedule
   board). --dense = 32 px rows under desktop density; --pin keeps the first column in view
   while the rest scrolls sideways; a sort button in a header shows its direction; money is
   tabular and right-aligned, a negative carries a true minus AND the danger ink (never colour
   alone); a subtotal row closes each group and the sum row closes the table. ── */
.ng-table th{z-index:2}
.ng-table--pin :is(th,td):first-child{position:sticky;left:0;z-index:1;background:var(--ng-card);box-shadow:inset -1px 0 0 var(--ng-hairline)}
.ng-table--pin thead th:first-child{z-index:3}
@media (hover:hover){.ng-table--pin tbody tr:hover td:first-child{background:var(--ng-soft)}}
.ng-table--dense td{height:auto;padding:var(--s-1) var(--s-3)}
@media (min-width:768px) and (pointer:fine){
  .ng-desktop .ng-table--dense{font-size:var(--ng-t-control)}
  .ng-desktop .ng-table--dense td{height:var(--ng-h-sm);padding:var(--s-0) var(--s-2)}
  .ng-desktop .ng-table--dense th{padding:var(--s-1) var(--s-2)}
  .ng-desktop .ng-table--dense th[aria-sort]{padding:0}
}
.ng-table th[aria-sort]{padding:0}
.ng-table__sort{display:flex;align-items:center;gap:var(--s-1);width:100%;min-height:var(--ng-touch);padding:0 var(--s-3);border:0;background:none;
  font:inherit;color:inherit;text-align:inherit;cursor:pointer;white-space:nowrap}
.ng-table__num .ng-table__sort{justify-content:flex-end}
.ng-table__sort .ng-icon{width:14px;height:14px;opacity:0}
.ng-table__sort .ng-icon + .ng-icon{display:none}
.ng-table th:is([aria-sort="ascending"],[aria-sort="descending"]) .ng-table__sort{color:var(--ng-ink1)}
.ng-table th[aria-sort="ascending"] .ng-table__sort .ng-icon:first-of-type{opacity:1}
.ng-table th[aria-sort="descending"] .ng-table__sort .ng-icon:first-of-type{display:none}
.ng-table th[aria-sort="descending"] .ng-table__sort .ng-icon + .ng-icon{display:inline;opacity:1}
@media (hover:hover){.ng-table__sort:hover{color:var(--ng-ink1)}}
@media (hover:hover){.ng-table th[aria-sort="none"] .ng-table__sort:hover .ng-icon:first-of-type{opacity:.5}}
.ng-table__sort:focus-visible{outline:2px solid var(--ng-focus);outline-offset:-2px}
@media (min-width:768px) and (pointer:fine){.ng-desktop .ng-table--dense .ng-table__sort{min-height:var(--ng-h-sm);padding:0 var(--s-2)}}
.ng-num--neg{color:var(--ng-danger)}
.ng-table tr td.ng-num--neg{color:var(--ng-danger)}
.ng-table .ng-table__group td{padding-top:var(--s-3);background:var(--ng-card);font-size:var(--t-dense);font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ng-ink3)}
.ng-table .ng-table__sub td{font-weight:600;color:var(--ng-ink1);border-top:1px solid var(--ng-hairline-strong);background:var(--ng-soft)}
.ng-table tfoot td{position:sticky;bottom:0;z-index:1;background:var(--ng-card);border-top:2px solid var(--ng-ink1);border-bottom:0;font-weight:700}
.ng-table--pin tfoot td:first-child{z-index:3}
/* a schedule board: tanks across, days down (the owner reads it that way); a cell is an event
   dot AND its word, and an empty day is a quiet dash */
.ng-table__event{display:inline-flex;align-items:center;gap:var(--s-1);white-space:nowrap;font-weight:600}
.ng-table__event + .ng-table__event{margin-left:var(--s-2)}
.ng-table__none{color:var(--ng-ink3)}
.ng-table tbody tr[aria-current="date"] td{background:var(--ng-soft)}
.ng-table tbody tr[aria-current="date"] td:first-child{font-weight:700}

/* ── ng-gate: why the next step or Sign is off, from ONE shared check (NG.gate) — never a
   page's own list. Needed, to fix and held-for-a-note items block; a soft flag is shown but
   never blocks. Each item is a button that takes you to its field. ── */
.ng-gate{display:grid;gap:var(--s-1);margin:0}
.ng-gate__line{display:flex;align-items:center;gap:var(--s-2);margin:0;font-size:var(--ng-t-control);font-weight:600;color:var(--ng-ink1)}
.ng-gate__line .ng-icon{width:16px;height:16px;color:var(--ng-warn)}
.ng-gate[data-state="ready"] .ng-gate__line .ng-icon{color:var(--ng-ok)}
.ng-gate__list{display:flex;flex-wrap:wrap;gap:0 var(--s-2);margin:0;padding:0;list-style:none}
.ng-gate__item{display:inline-flex;align-items:center;gap:var(--s-1);min-height:var(--ng-touch);padding:0 var(--s-1);border:0;border-radius:var(--r-std);background:none;
  font:600 var(--t-dense)/1.2 var(--ng-font-ui);color:var(--ng-ink2);text-align:left;cursor:pointer;text-decoration:underline;text-underline-offset:3px;text-decoration-color:var(--ng-hairline-strong)}
.ng-gate__item[data-kind="fix"]{color:var(--ng-danger)}
.ng-gate__item:is([data-kind="hold"],[data-kind="soft"]){color:var(--ng-warn)}
@media (hover:hover){.ng-gate__item:hover{text-decoration-color:currentColor}}
.ng-gate__item:active{opacity:.7}
.ng-gate__item:focus-visible{outline:2px solid var(--ng-focus);outline-offset:0}
/* FILTRATION ROUND 13 — a dock's gate (.ng-gate[data-gate-list="here"], NG.gate): the open
   block's items as ONE row of buttons that scrolls sideways, so the dock stays three short
   lines on a phone and never grows over the form. */
.ng-dock .ng-gate[data-gate-list="here"] .ng-gate__list{flex-wrap:nowrap;overflow-x:auto;gap:0 var(--s-3);scrollbar-width:none}
.ng-dock .ng-gate[data-gate-list="here"] .ng-gate__list::-webkit-scrollbar{display:none}
.ng-dock .ng-gate[data-gate-list="here"] .ng-gate__item{flex:none;white-space:nowrap}
/* FILTRATION ROUND 14 — the row never cuts a word at the edge: on a phone the right edge
   fades while more buttons lie past it (NG.gate marks data-more); on an iPad the row wraps
   to at most two lines, and the lower edge fades if a third would be needed. While a reading
   is being typed (data-entering) the row steps aside so the field stays clear; the counted
   sentence and the dock's buttons stay. */
.ng-dock .ng-gate[data-gate-list="here"] .ng-gate__list[data-more]{
  -webkit-mask-image:linear-gradient(to right,var(--ng-ink1) calc(100% - var(--s-8)),transparent);mask-image:linear-gradient(to right,var(--ng-ink1) calc(100% - var(--s-8)),transparent)}
@media (min-width:720px){
  .ng-dock .ng-gate[data-gate-list="here"] .ng-gate__list{flex-wrap:wrap;overflow-x:hidden;overflow-y:auto;max-height:calc(var(--ng-touch) * 2)}
  .ng-dock .ng-gate[data-gate-list="here"] .ng-gate__list[data-more]{
    -webkit-mask-image:linear-gradient(to bottom,var(--ng-ink1) calc(100% - var(--s-4)),transparent);mask-image:linear-gradient(to bottom,var(--ng-ink1) calc(100% - var(--s-4)),transparent)}
}
.ng-dock[data-entering]:not([data-expanded]) .ng-gate[data-gate-list="here"]{display:none}
/* FILTRATION ROUND 15 — while a reading is typed the dock is ONE line: the counted sentence
   (ellipsis) beside the next action; Back and "Block 2 of 5" step aside. The sentence is a
   toggle (NG sets role=button + aria-expanded): a tap opens the full dock (data-expanded). */
.ng-dock[data-entering]:not([data-expanded]){flex-direction:row;align-items:center;gap:var(--s-3);padding-block:var(--s-2)}
.ng-dock[data-entering]:not([data-expanded]) .ng-dock__why{flex:1 1 auto;min-width:0;align-items:center}
.ng-dock[data-entering]:not([data-expanded]) .ng-dock__why [data-gate-text]{min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
.ng-dock[data-entering]:not([data-expanded]) .ng-dock__why[hidden] + .ng-gate + .ng-dock__row{flex:1 1 auto;justify-content:flex-end}
.ng-dock[data-entering]:not([data-expanded]) .ng-dock__row{flex:none}
.ng-dock[data-entering]:not([data-expanded]) .ng-dock__row > :is(.ng-text-action,.ng-dock__where){display:none}
.ng-dock[data-entering]:not([data-expanded]) .ng-dock__row > :is(.ng-primary:where(:not(.mer-save-btn)),.ng-outline-action){flex:none;width:auto}
/* folded dock: the primary's long label ("Next: <section>") must not squeeze the short sentence on a phone — tighter side padding, 16px label */
@media (max-width:599px){.ng-dock[data-entering]:not([data-expanded]) .ng-dock__row > :is(.ng-primary:where(:not(.mer-save-btn)),.ng-outline-action){padding-inline:var(--s-4)}}
.ng-dock__why [data-gate-short]{display:none}
.ng-dock[data-entering]:not([data-expanded]) .ng-dock__why [data-gate-short]{display:inline;white-space:nowrap}
.ng-dock[data-entering]:not([data-expanded]) .ng-dock__why [data-gate-text]{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
.ng-group[data-progress-in-dock][data-open] [data-gstate]{display:none}
/* FILTRATION ROUND 16 — a dock folded at rest (data-fold-rest, NG.dock.rest): the form just
   above it keeps the folded dock's height (--ng-dock-rest, measured by the kit) plus the
   plate's spread as bottom padding, and a focused field lands above it — no label sliced. */
:has(+ .ng-dock[data-fold-rest]){padding-bottom:calc(var(--ng-dock-h, var(--ng-dock-rest, var(--ng-touch))) + var(--s-3))}
/* FILTRATION ROUND 17 — the dock's current height, recomputed as it folds and opens, plus the
   lane it floats in (its own bottom offset, above a tab bar) and its plate: --ng-dock-clear,
   measured by the kit. Anything scrolled to lands fully above the dock, mid-scroll too. */
html:has(.ng-dock[data-fold-rest]){scroll-padding-bottom:calc(var(--ng-dock-clear, calc(var(--ng-dock-rest, var(--ng-touch)) + var(--ng-lane, var(--s-3)) + var(--s-8))) + var(--ng-toast-room,0px))}
@media (max-width:599px){
  .ng-dock[data-entering]:not([data-expanded]) .ng-dock__why [data-gate-short]{display:inline;white-space:nowrap}
  .ng-dock[data-entering]:not([data-expanded]) .ng-dock__why > .ng-icon{display:none}
  .ng-dock[data-entering]:not([data-expanded]) .ng-dock__why [data-gate-text]{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
}
.ng-dock__why[role="button"]{cursor:pointer;min-height:var(--ng-touch);border-radius:var(--r-std);align-items:center}
.ng-dock__why[role="button"]::after{content:"";flex:none;width:var(--s-2);height:var(--s-2);margin-inline:var(--s-1);border:solid var(--ng-ink3);border-width:0 2px 2px 0;transform:rotate(-135deg) translate(-2px,-2px)}
.ng-dock__why[aria-expanded="true"]::after{transform:rotate(45deg) translate(-2px,-2px)}
.ng-dock__why[role="button"]:focus-visible{outline:2px solid var(--ng-focus);outline-offset:2px}

/* ── ng-sync: what has not reached the server yet (static/mobile_ops.js's post queue).
   Waiting to send is calm — ink and a clock, never red: the save is safe on the device.
   Only a save the server refused is red, and it says what to do. ── */
.ng-sync{display:inline-flex;align-items:center;gap:var(--s-1);min-height:var(--ng-h-xs);padding:0 var(--s-2);border-radius:var(--r-pill);
  background:var(--ng-soft);color:var(--ng-ink2);font-size:var(--t-dense);font-weight:600;white-space:nowrap}
.ng-sync .ng-icon{width:14px;height:14px}
.ng-sync .ng-spinner{width:12px;height:12px;border-width:1.5px}
.ng-sync[data-state="sent"]{background:var(--ng-ok-bg);color:var(--ng-ok)}
.ng-sync[data-state="failed"]{background:var(--ng-danger-bg);color:var(--ng-danger)}
.ng-toast--queued > .ng-icon{color:var(--ng-toast-ink)}

/* ── ng-task: one piece of assigned work — what, when it's due (a word: Overdue / Today /
   Tomorrow, never colour alone), one tap to Done, a note, and who did it when. Tomorrow's
   tasks are read-only: no tick. ── */
.ng-tasks{display:flex;flex-direction:column;margin:0;padding:0;list-style:none}
.ng-tasks > * + *{border-top:1px solid var(--ng-hairline)}
.ng-task{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:start;gap:var(--s-1) var(--s-3);padding:var(--s-2) 0}
.ng-task__tick{display:grid;place-items:center;width:var(--ng-touch);height:var(--ng-touch);margin:0;padding:0;border:0;background:none;border-radius:var(--r-pill);cursor:pointer;color:var(--ng-ok-ink)}
.ng-task__tick::before{content:"";grid-area:1 / 1;width:var(--ng-h-xs);height:var(--ng-h-xs);border-radius:var(--r-pill);box-shadow:inset 0 0 0 1.5px var(--ng-ink3);background:var(--ng-card)}
.ng-task__tick .ng-icon{grid-area:1 / 1;width:14px;height:14px;stroke-width:2.5;opacity:0}
@media (hover:hover){.ng-task__tick:hover::before{box-shadow:inset 0 0 0 2px var(--ng-ink1)}}
.ng-task__tick:active::before{background:var(--ng-soft-press)}
.ng-task__tick:focus-visible{outline:2px solid var(--ng-focus);outline-offset:-2px}
.ng-task__tick[aria-pressed="true"]::before{background:var(--ng-ok);box-shadow:none}
.ng-task__tick[aria-pressed="true"] .ng-icon{opacity:1}
.ng-task__tick[aria-busy="true"]{pointer-events:none}
.ng-task__tick[aria-busy="true"]::before{background:none;box-shadow:none;border:2px solid var(--ng-ink2);border-right-color:transparent;animation:ng-spin .8s linear infinite}
.ng-task__tick[aria-busy="true"] .ng-icon{opacity:0}
.ng-task__text{display:grid;gap:var(--s-0);min-width:0;padding-top:var(--s-2)}
.ng-task__title{font-size:var(--t-body);font-weight:600;line-height:1.3;overflow-wrap:anywhere}
.ng-task__meta{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-1) var(--s-2);font-size:var(--t-dense);color:var(--ng-ink3)}
.ng-task__due{display:inline-flex;align-items:center;gap:var(--s-1);font-weight:600;color:var(--ng-ink2)}
.ng-task__due .ng-icon{width:12px;height:12px}
.ng-task[data-due="late"]:not([data-done]) .ng-task__due{padding:var(--s-0) var(--s-2);border-radius:var(--r-pill);background:var(--ng-warn-bg);color:var(--ng-warn)}
.ng-task[data-done] .ng-task__title{color:var(--ng-ink2)}
.ng-task[data-queued] .ng-task__by{color:var(--ng-ink2)}
.ng-task__by{display:inline-flex;align-items:center;gap:var(--s-1);font-size:var(--t-dense);font-weight:600;color:var(--ng-ok)}
.ng-task__by .ng-icon{width:14px;height:14px}
.ng-task__note{margin:var(--s-1) 0 0;padding-left:var(--s-2);border-left:2px solid var(--ng-hairline-strong);font-size:var(--t-dense);color:var(--ng-ink2)}
.ng-task__noteform{display:grid;gap:var(--s-2);margin-top:var(--s-2)}
.ng-task__noteform .ng-actions{justify-content:flex-start}
.ng-task[data-due="later"] .ng-task__tick{visibility:hidden}
.ng-task[data-due="later"] .ng-task__title{color:var(--ng-ink2)}
/* a tick pill on Today can be late too: a word inside it, not a colour */
.ng-tick__due{font-weight:700;color:var(--ng-warn)}
.ng-tick[data-due="late"]:not([aria-pressed="true"]){box-shadow:inset 0 0 0 1px var(--ng-warn)}

/* ── ng-receipt states: queued (on this device, will send when back online) and signed
   (locked; who signed, said once — never repeated on every line). A transfer's trail has
   four stops, and a stop can carry the action that completes it (the receiving crew's
   Arrived). ── */
.ng-receipt--queued .ng-receipt__mark{background:var(--ng-soft);color:var(--ng-ink2)}
.ng-receipt--signed .ng-receipt__mark{background:var(--ng-ink1);color:var(--ng-canvas)}
.ng-receipt__seal{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-1) var(--s-2);margin:0;padding:var(--s-2) var(--s-3);border-radius:var(--r-std);background:var(--ng-soft);font-size:var(--t-dense);color:var(--ng-ink2)}
.ng-receipt__seal strong{color:var(--ng-ink1)}
.ng-receipt__seal .ng-icon{width:14px;height:14px}
.ng-receipt__stop[data-state="wait"]::before{border-style:dashed}
.ng-receipt__stop-action{grid-column:2;justify-self:start;margin-top:var(--s-1)}

/* ── ng-grid: an editable grid — a number per cell (per-head DO, can weight, per pallet),
   an error under the cell it belongs to, add and remove a row, column totals in the foot
   (NG.grid). It scrolls sideways inside its frame with the row name pinned; the page never
   does. ── */
.ng-grid td{padding:var(--s-1);vertical-align:top}
.ng-grid th{padding:var(--s-2)}
.ng-grid :is(th,td):first-child{padding-left:var(--s-3);white-space:nowrap;font-weight:600}
.ng-grid tbody td:first-child{padding-top:calc(var(--s-1) + var(--s-3))}
.ng-grid__in{min-width:var(--ng-grid-cell-min);padding:0 var(--s-2)}
.ng-grid__err{display:block;max-width:var(--ng-error-max);margin-top:var(--s-0);font-size:var(--t-dense);font-weight:600;line-height:1.25;color:var(--ng-danger);white-space:normal}
.ng-grid__err:empty{display:none}
.ng-grid__remove{width:var(--ng-touch)}
.ng-grid tfoot td{padding:var(--s-2);text-align:right;font-variant-numeric:tabular-nums}
.ng-grid tfoot td:first-child{text-align:left}
.ng-grid__total small{display:block;font-size:var(--t-dense);font-weight:500;color:var(--ng-ink3)}
.ng-grid__add{margin-top:var(--s-2)}

/* ── ng-confirm: a costly action asks first, and says what happens in one sentence. With a
   money PIN the pad IS the confirm: nothing happens until a PIN that may spend money is in. ── */
.ng-confirm .ng-sheet__body{gap:var(--s-4)}
.ng-confirm__what{margin:0}
/* FILTRATION ROUND 16 — NG.confirmInline: a skip asked where the tap was, one warn line and
   two buttons, never a sheet. */
.ng-confirm-inline{margin-top:var(--s-3)}
.ng-confirm-inline__row{margin-top:var(--s-2);flex-wrap:wrap}
/* FILTRATION ROUND 17 — a skip that needs a short reason: the field sits under the question */
.ng-confirm-inline__why{margin-top:var(--s-2)}
.ng-confirm .ng-pin{margin-top:var(--s-1)}

/* ════ KIT ROUND 11 — the parts the Warehouse screens were drawing for themselves (Cole
   2026-09-24: a screen is built FROM kit parts and never restyles one). Each is a named
   variant any screen can use; the gallery is /apps/_glass. ═════════════════════════════ */

/* A kit part keeps its own face and ink on a crew page that still loads the legacy mobile
   sheet: noli-mobile.css sets body.nm-mobile h1/h2 to a display face and body.nm-mobile a to
   its link blue (both 0,1,2 — above a lone kit class). Scoped to a page wearing that sheet
   (body.nm-mobile); measured on the Warehouse hub. */
.ng-body.nm-mobile :is(h1,h2,h3).ng-title{font-family:var(--ng-font-title);letter-spacing:0;text-transform:none}
.ng-body.nm-mobile a.ng-seg__item{color:var(--ng-ink2)}
@media (hover:hover){.ng-body.nm-mobile a.ng-seg__item:hover{color:var(--ng-ink1)}}
.ng-body.nm-mobile a.ng-seg__item:is([aria-pressed="true"],[aria-current="page"],[aria-selected="true"]){color:var(--ng-canvas)}

/* ng-title--compact: a working screen's title (counting, a run under way) steps down to the
   desktop-density size on a phone, so the work gets the room. An iPad keeps the full title. */
@media (max-width:719px){.ng-title--compact{--ng-t-title:28px}}

/* Icon tones: an icon that carries a state beside its words (a done check, a warning). */
.ng-icon--ok{color:var(--ng-ok)}
.ng-icon--warn{color:var(--ng-warn)}
.ng-icon--danger{color:var(--ng-danger)}
.tc-tasks__empty .ng-icon{width:28px;height:28px;color:var(--ng-ink3)}

/* A count-sized number in the display face (a line on the truck, a receipt quantity). */
.ng-num--count{font-size:var(--ng-t-count)}

/* ng-outline-action--lg: a secondary that stands beside or stacks with the primary takes the
   primary's height, so the pair reads as one row. */
.ng-outline-action--lg{min-height:var(--ng-touch-primary)}

/* ng-text-action--inverse: a quiet action on an ink band (an undo strip). */
.ng-text-action--inverse{color:var(--ng-canvas)}

/* ng-seg--lg: iPad-sized choice pills for a sheet the crew sets with a thumb (a truck's route,
   where a keg came from): 52 px target, 40 px visible pill, the strong label size. */
.ng-seg--lg{--ng-touch:52px;--ng-pill-h:40px}
.ng-seg--lg .ng-seg__item{padding:0 var(--s-4);font-size:var(--ng-t-strong)}
@media (max-width:719px){.ng-seg--lg .ng-seg__item{padding:0 var(--s-3)}}
/* ng-seg__item--stack: a pill with a second line under its label (a pallet number and the
   word that it still needs something). */


/* ng-stepper--lg: the crew count stepper (load a truck, count one in). The keys grow to 52 px
   on an iPad and keep the 44 px target on a phone; the typed count between them sets its own
   width. No unit slot: the row's own words carry the unit. ng-stepper--on-soft: the keys lift
   to the card colour when the stepper sits on a soft panel. */
.ng-stepper--lg{--ng-step-key:calc(var(--ng-touch) + var(--s-2));grid-template-columns:var(--ng-step-key) auto var(--ng-step-key)}
.ng-stepper--lg .ng-stepper__key{width:var(--ng-step-key);height:var(--ng-step-key)}
@media (max-width:719px){.ng-stepper--lg{--ng-step-key:var(--ng-touch)}}
.ng-stepper--on-soft .ng-stepper__key{background:var(--ng-card)}

/* A field that needs the crew's attention (a note Cole will read) but is not wrong. */
.ng-input--warn,.ng-select--warn,.ng-textarea--warn{border-color:var(--ng-warn)}

/* ng-sheet--work: a working sheet (load a truck, repack kegs). Its head, scrolling body and
   pinned foot each own their padding, so the sheet has none. iPad and up: a centred panel,
   as wide as the page's layout gives it, never taller than the screen. Phone: a bottom
   sheet. Every control inside shows the kit's keyboard ring. */
.ng-sheet--work{padding:0;font-family:var(--ng-font-ui)}
@media (min-width:720px){
  .ng-sheet--work{inset:0;max-width:none;border-radius:var(--r-card)}
  .ng-sheet--work[open]{animation-name:ng-sheet-pop}
}
@media (max-width:719px){.ng-sheet--work{max-width:none}}
.ng-sheet--work :is(button,input,select,textarea):focus-visible{outline:2px solid var(--ng-focus);outline-offset:2px}

/* ng-wells: a form built by shared code whose fields carry no classes (the crew transfer and
   repack sheets). Every bare input, select and textarea inside takes the crew's soft well:
   48 px tall, 16 px text (no iOS zoom), the ink edge on focus. Scoped under .ng-body so it
   outranks the desk sheet (keg-repack.css) those forms also wear. */
.ng-body .ng-wells :where(input:not([type=checkbox]):not([type=radio]),select,textarea){
  box-sizing:border-box;width:100%;min-width:0;min-height:var(--ng-touch-primary);margin:0;padding:0 var(--s-3);
  border:1px solid var(--ng-hairline);border-radius:var(--r-std);background:var(--ng-soft);color:var(--ng-ink1);
  font:500 var(--ng-t-strong)/1.2 var(--ng-font-ui);font-size:var(--ng-t-input);font-variant-numeric:tabular-nums;
}
.ng-body .ng-wells :where(textarea){min-height:72px;padding:var(--s-2) var(--s-3);resize:vertical}
.ng-body .ng-wells :where(input,select,textarea):focus{outline:none;border-color:var(--ng-ink1);background:var(--ng-card)}
.ng-body .ng-wells :where(input,select,textarea)::placeholder{color:var(--ng-ink3)}
.ng-body .ng-wells :where(select){
  -webkit-appearance:none;appearance:none;padding-right:var(--s-8);
  background-image:linear-gradient(45deg,transparent 50%,var(--ng-ink2) 50%),linear-gradient(135deg,var(--ng-ink2) 50%,transparent 50%);
  background-position:calc(100% - var(--ng-select-arrow-end)) 50%,calc(100% - var(--ng-select-arrow-inner)) 50%;background-size:var(--ng-select-arrow) var(--ng-select-arrow),var(--ng-select-arrow) var(--ng-select-arrow);background-repeat:no-repeat;
}
.ng-body .ng-wells input[type=number]{-moz-appearance:textfield;appearance:textfield}
.ng-body .ng-wells input[type=number]::-webkit-inner-spin-button,
.ng-body .ng-wells input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}
.ng-body .ng-wells :where(input,select,textarea):focus-visible{outline:2px solid var(--ng-focus);outline-offset:2px}

/* KIT-10 (2026-09-25) — the K1 safety net. A text control with NO class at all (design lint
   K1: usually one a page's script builds) inside a kit page used to render as the browser's
   grey widget at 13 px, which iOS zooms on focus. It now wears the kit input at zero
   specificity: any class, page rule or kit part still wins, and a control that has a class
   is never touched. The lint still reports K1 — the fix for a bare control is its kit class. */
:where(.ng-body) :where(input:not([class]):not([type=checkbox]):not([type=radio]):not([type=hidden]):not([type=range]):not([type=file]):not([type=color]),select:not([class]),textarea:not([class])){
  box-sizing:border-box;max-width:100%;min-height:var(--ng-touch);padding:0 var(--s-3);
  border:1px solid var(--ng-hairline-strong);border-radius:var(--r-std);background:var(--ng-card);color:var(--ng-ink1);
  font-family:var(--ng-font-ui);font-size:var(--ng-t-input);font-variant-numeric:tabular-nums;
}
:where(.ng-body) :where(textarea:not([class])){min-height:72px;padding:var(--s-2) var(--s-3)}
:where(.ng-body) :where(input:not([class]),select:not([class]),textarea:not([class])):focus-visible{outline:2px solid var(--ng-focus);outline-offset:2px}

@media (prefers-reduced-motion: reduce){
  .ng-body *,.ng-body *::before,.ng-body *::after{transition:none !important;animation:none !important}
  :is(.ng-toast,.ng-sheet,.ng-pin__dots,.ng-skeleton){animation:none !important}
}

/* Weekly task checkbox variant: the native input stays accessible. */
.tc-weekly .ng-tick__mark--weekly{width:20px;height:20px}
.tc-weekly .ng-tick__mark--weekly .ng-icon{width:14px;height:14px}
.tc-weekly input:checked + .ng-tick__mark--weekly{background:var(--ng-ok);border-color:var(--ng-ok)}
.tc-weekly input:checked + .ng-tick__mark--weekly .ng-icon{opacity:1}
.tc-weekly input:focus-visible + .ng-tick__mark--weekly{outline:2px solid var(--ng-focus);outline-offset:2px}
@media (hover:hover){.tc-weekly:hover .ng-tick__mark--weekly{border-color:var(--ng-ink2)}}

/* ── ng-strip: the control strip (UX-33, 2026-09-28) ─────────────────────
   One convention for every glass page's filter/toggle row, taken from
   Distribution › Dist inventory (distribution_inventory_glass.css,
   .gi-head__tools): SEARCH first, then the view toggles, then actions
   (.ng-strip__actions, pushed to the end). Pills paint 34 px on a desktop
   (fine pointer) and 44 px everywhere a finger is the pointer; selected is a
   quiet tinted pill, never the solid primary fill, so the page's one primary
   action stays the one filled control. Wraps the existing kit parts
   (ng-seg, ng-chip, ng-switch, ng-select, ng-input) — markup keeps its ids,
   aria-pressed and data-* hooks, so no script changes. */
.ng-strip{--ng-strip-h:var(--ng-touch);--ng-strip-pad:var(--s-3);display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-2);min-width:0;margin:0}
@media (min-width:768px) and (pointer:fine){.ng-strip{--ng-strip-h:var(--ng-strip-h-fine);--ng-strip-pad:var(--s-4)}}
.ng-strip > *{min-width:0}
.ng-strip__actions{display:flex;align-items:center;gap:var(--s-2);margin-left:auto}
/* search: a pill with the glass inside it; the field itself carries no box */
.ng-strip__search{
  position:relative;display:flex;align-items:center;gap:var(--s-2);flex:0 1 auto;box-sizing:border-box;
  height:var(--ng-strip-h);margin:0;padding:0 var(--s-3);
  border:1px solid var(--ng-hairline);border-radius:var(--r-pill);background:none;color:var(--ng-ink3);
}
.ng-strip__search:focus-within{border-color:var(--ng-hairline-strong)}
.ng-strip .ng-strip__search > .ng-icon{position:static;transform:none;width:16px;height:16px;color:currentColor}
.ng-strip .ng-strip__search > input[type]{
  flex:1 1 auto;width:140px;min-width:0;height:auto;min-height:0;margin:0;padding:0;
  border:0;border-radius:0;background:none;box-shadow:none;outline:none;
  color:var(--ng-ink1);font-family:var(--ng-font-ui);font-size:var(--ng-t-control);font-weight:500;
}
.ng-strip .ng-strip__search > input[type]:focus{outline:none;border-color:transparent}
.ng-strip .ng-strip__search > input[type]:focus-visible{outline:2px solid var(--ng-focus);outline-offset:6px;border-radius:var(--r-pill)}
.ng-strip .ng-strip__search > input[type]::placeholder{color:var(--ng-ink3)}
.ng-strip .ng-strip__search > .ng-text-action{flex:none;min-height:0;font-size:var(--ng-t-control)}
/* toggle groups: separate pills, no switch track */
.ng-strip :is(.ng-seg,.ng-chips,.ng-switch){display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-2);margin:0;padding:0;background:none;border-radius:0}
.ng-strip :is(.ng-seg__item,.ng-chip,.ng-switch__item,.ng-strip__toggle){
  position:relative;display:inline-flex;align-items:center;justify-content:center;flex:none;box-sizing:border-box;
  height:var(--ng-strip-h);min-height:0;min-width:0;margin:0;padding:0 var(--ng-strip-pad);
  border:1px solid var(--ng-hairline-strong);border-radius:var(--r-pill);background:none;box-shadow:none;
  color:var(--ng-ink2);font-family:var(--ng-font-ui);font-size:var(--ng-t-control);font-weight:600;white-space:nowrap;text-decoration:none;cursor:pointer;
  transition:background-color var(--ng-fast) var(--ng-ease),color var(--ng-fast) var(--ng-ease);
}
.ng-strip .ng-switch__item::after{content:none}
.ng-strip :is(.ng-seg__item,.ng-chip,.ng-switch__item,.ng-strip__toggle):focus-visible{outline:2px solid var(--ng-focus);outline-offset:2px}
@media (hover:hover){.ng-strip :is(.ng-seg__item,.ng-chip,.ng-switch__item,.ng-strip__toggle):hover{background-color:var(--ng-soft);color:var(--ng-ink1)}}
.ng-strip :is(.ng-seg__item,.ng-chip,.ng-switch__item):is([aria-pressed="true"],[aria-current="page"],[aria-selected="true"]),
.ng-strip .ng-strip__toggle:has(input:checked){background-color:var(--ng-soft-press);border-color:var(--ng-ink3);color:var(--ng-ink1)}
/* a checkbox toggle keeps its real input (scripts read .checked); the pill shows the state */
.ng-strip .ng-strip__toggle input{position:absolute;width:1px;height:1px;margin:0;padding:0;opacity:0;pointer-events:none}
.ng-strip .ng-strip__toggle:has(input:focus-visible){outline:2px solid var(--ng-focus);outline-offset:2px}
/* selects and date fields sit at the pill height, pill-shaped */
.ng-strip :is(.ng-select,.ng-input){width:auto;background:none;height:var(--ng-strip-h);min-height:0;border-radius:var(--r-pill);font-size:var(--ng-t-control);font-weight:600}
.ng-strip .ng-select{padding:0 var(--s-8) 0 var(--ng-strip-pad)}
.ng-strip input.ng-input,.ng-strip .ng-input{padding:0 var(--ng-strip-pad)}
/* a labelled field: the pill carries a VISIBLE short label, then the native
   picker (kept as the real input, so scripts still read .value) with no box of its own */
.ng-strip__field{display:inline-flex;align-items:center;gap:var(--s-2);box-sizing:border-box;height:var(--ng-strip-h);margin:0;padding:0 var(--ng-strip-pad);border:1px solid var(--ng-hairline-strong);border-radius:var(--r-pill);background:none;color:var(--ng-ink2)}
.ng-strip__field:focus-within{border-color:var(--ng-ink3)}
.ng-strip .ng-strip__field > .ng-input:focus-visible{outline:2px solid var(--ng-focus);outline-offset:4px;border-radius:var(--r-pill)}
.ng-strip__field-label{font-size:var(--t-dense);font-weight:600;color:var(--ng-ink2);white-space:nowrap}
.ng-strip .ng-strip__field > .ng-input{height:auto;min-height:0;padding:0;border:0;border-radius:0;background:none;box-shadow:none;outline:none;color:var(--ng-ink1);font-weight:600;color-scheme:light}
:root[data-theme="dark"] .ng-strip .ng-strip__field > .ng-input{color-scheme:dark}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]) .ng-strip .ng-strip__field > .ng-input{color-scheme:dark}}
/* a tab row that must not orphan a label on a phone: one row, scrolls sideways, fades at the edge */
@media (max-width:767px){.ng-strip .ng-seg.ng-seg--scroll{flex-wrap:nowrap;overflow-x:auto;max-width:100%;scrollbar-width:none;-webkit-mask-image:linear-gradient(90deg,var(--ng-ink1) calc(100% - var(--s-6)),transparent);mask-image:linear-gradient(90deg,var(--ng-ink1) calc(100% - var(--s-6)),transparent)}.ng-strip .ng-seg.ng-seg--scroll::-webkit-scrollbar{display:none}}
/* one placement for the brewery switch on every crew header: the right end of the strip, under the title */
@media (min-width:768px){.ng-strip .ng-strip__end{margin-left:auto}}
/* crew-screen nav (.chips) is navigation, not a filter: quiet text tabs with an underline on the current one, so the pills that follow are the only tinted controls */
.ng-strip .ng-seg.chips{gap:var(--s-1)}
.ng-strip .ng-seg.chips .ng-seg__item{border-color:transparent;background:none;border-radius:var(--r-std);color:var(--ng-ink2)}
.ng-strip .ng-seg.chips .ng-seg__item:is([aria-current="page"],[aria-pressed="true"]){background:none;border-color:transparent;color:var(--ng-ink1);box-shadow:inset 0 -2px 0 var(--ng-ink1);border-radius:0}
@media (hover:hover){.ng-strip .ng-seg.chips .ng-seg__item:hover{background:none;color:var(--ng-ink1)}}
/* a control's text label stays for screen readers only */
.ng-strip__label{position:absolute;width:1px;height:1px;margin:0;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
/* phone: the search takes what the pills leave, and a row that runs out of
   width wraps whole groups rather than scroll sideways */
@media (max-width:767px){
  .ng-strip__search{flex:1 1 160px}
  .ng-strip .ng-strip__search > input[type]{width:100%}
  .ng-strip :is(.ng-seg,.ng-chips,.ng-switch){gap:var(--s-1)}
}

/* ── ng-crew-frame: the desktop crew screen (KIT ROUND 11). A crew page's one column, plus the
   facts of the thing being worked on in a rail beside it, from 1280 px up. Below that — a phone,
   an iPad in either turn — the rail is not drawn and the column is exactly as before. ── */
.ng-crew-frame__rail{display:none}
.ng-crew-frame__rail > .ng-card{padding:var(--s-4)}
.ng-crew-frame__facts{display:grid;gap:var(--s-3);margin:var(--s-3) 0 0}
.ng-crew-frame__fact{display:grid;gap:var(--s-0)}
.ng-crew-frame__fact dt{font-size:var(--t-dense);font-weight:600;color:var(--ng-ink3)}
.ng-crew-frame__fact dd{margin:0;font-size:var(--t-body);font-weight:600;color:var(--ng-ink1)}
@media (min-width:1280px) and (pointer:fine){
  .ng-crew-frame{display:grid;grid-template-columns:minmax(0,var(--ng-col-main)) minmax(var(--ng-rail),1fr);gap:var(--s-8);align-items:start;justify-content:center}
  .ng-crew-frame__main{min-width:0}
  .ng-crew-frame__rail{display:block;position:sticky;top:var(--s-4)}
}

/* ── ng-stats: a stat row — a few labelled figures side by side (KIT ROUND 11). Ink tokens only;
   the figure is the UI face, tabular, never a coloured blob. ── */
.ng-stats{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-4);margin:0}
.ng-stats:has(> :nth-child(2):last-child){grid-template-columns:repeat(2,minmax(0,1fr))}
.ng-stats:has(> :nth-child(4)){grid-template-columns:repeat(auto-fit,minmax(var(--ng-stat-min),1fr))}
.ng-stats__item{display:grid;align-content:start;gap:var(--s-0);min-width:0;margin:0}
.ng-stats dt{font-size:var(--t-dense);font-weight:600;color:var(--ng-ink3)}
.ng-stats dd{margin:0;font-size:var(--ng-t-value);font-weight:600;font-variant-numeric:tabular-nums;color:var(--ng-ink1)}
.ng-stats dd.ng-stats__sub{font-size:var(--t-dense);font-weight:400;color:var(--ng-ink2)}

/* KIT ROUND 12 - a table's first column (the beer's name) keeps a floor, so a phone scrolls the
   table sideways instead of wrapping "Cascade Fog" over two lines. */
.ng-table:not(.ng-table--stack) :is(th,td):first-child{min-width:calc(var(--s-8) * 3)}
.ng-table:not(.ng-table--stack) :is(th,td):first-child:not(.ng-table__num){white-space:nowrap}
/* boundary note: owner desktop pages still on the console styles */
.ng-boundary{display:flex;gap:var(--s-2);align-items:flex-start;margin:var(--s-4) 0 0;padding:var(--s-3) var(--s-4);border-radius:var(--r-std);background:var(--ng-soft);color:var(--ng-ink2);font-size:var(--t-body)}
.ng-boundary .ng-icon{flex:none;width:16px;height:16px;margin-top:2px;color:var(--ng-ink3)}

.ng-idpill__ini{display:none;font-size:var(--t-dense);font-weight:700;letter-spacing:.04em}

/* a step strip inside a narrow card wraps instead of clipping its last step */
.ng-steps--wrap{flex-wrap:wrap;overflow:visible}

/* KIT ROUND 17 - a toast over a docked action is a thin bar on the dock's edge; if it would cover
   the field being typed in (or the next), it takes the top of the screen instead. */
.ng-toasts--docked .ng-toast{min-height:0;padding:var(--s-1) var(--s-3);font-size:var(--t-dense)}
.ng-toasts--top{bottom:auto;top:calc(var(--s-4) + env(safe-area-inset-top, 0px));margin-bottom:0}
body:has(.ng-tabbar:not(.ng-tabbar--inline):not([hidden])) .ng-toasts--top{bottom:auto}

/* KIT ROUND 18 - a stacked table on a phone puts the status UNDER the name (never a pill clipped
   at the edge); a step chain that wraps goes two by two, and an odd chain goes one under another,
   so no single step ever wraps alone. */
@media (max-width:520px){
  .ng-table--stack .ng-table__status{grid-column:1;order:-1;justify-self:start;align-self:start}
  .ng-table--stack .ng-table__key{order:-2}
  .ng-steps--wrap{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s-1) var(--s-3)}
  .ng-steps--wrap:has(> :nth-child(3):last-child),.ng-steps--wrap:has(> :nth-child(5)){grid-template-columns:minmax(0,1fr)}
  /* a chain with a long waiting step: that step takes a full row; the rest keep the two-by-two */
  .ng-steps--wrap:has([data-waiting]){grid-template-columns:repeat(2,minmax(0,1fr))}
  .ng-steps--wrap > :has([data-waiting]){grid-column:1 / -1}
}

.ng-toasts--nudged{bottom:var(--ng-nudge-bottom) !important;margin-bottom:0}

@media (max-width:759px){
  .ng-switch--wrap .ng-switch__item{min-height:var(--ng-touch);white-space:normal;overflow-wrap:anywhere}
}

/* River semantic adapters share the kit's theme pairs; no page layout rules. */
html[data-palette="river"] {
  --river-selected:var(--ink,var(--ng-ink1));
  --river-selected-ink:var(--paper,var(--ng-card));
  --river-control:var(--paper-2,var(--ng-soft));
  --river-control-ink:var(--ink,var(--ng-ink1));
  --river-muted-ink:var(--ink-2,var(--ng-ink2));
}
html[data-palette="river"] :is(.filter-chip,.cchip,.schip,.daychip,.chip).on,
html[data-palette="river"] .chips a.active {
  background:var(--river-selected);color:var(--river-selected-ink);border-color:var(--river-selected);
}
html[data-palette="river"] :is(.filter-chip,.cchip,.schip,.daychip,[data-river-dialog]) :focus-visible,
html[data-palette="river"] :is(.filter-chip,.cchip,.schip,.daychip):focus-visible {
  outline:2px solid var(--river-control-ink);outline-offset:2px;
}
html[data-palette="river"] :is(.tiny-link,.filter-chip,.cchip,.schip,.daychip,.chip,.hdr-btn) {
  min-height:var(--ng-h-xs);min-width:var(--ng-h-xs);
}
html[data-palette="river"] :is(a.tiny-link,a.ng-text-action) { display:inline-flex;align-items:center; }
html[data-palette="river"] #noli-identity-pill { color:var(--river-muted-ink)!important; }

/* Existing row, badge and field kit adapters keep the same colors at tablet widths. */
html[data-palette="river"] body:is(.crew-support-river,.crew-approval-river) :is(.brew-row,.tank-card,.modal,.brewery-chips .chip,.view-tab) {
  background:var(--paper-2);color:var(--ink);border-color:var(--hair);
}
html[data-palette="river"] body:is(.crew-support-river,.crew-approval-river) :is(.brew-row.on,.tank-card.cip-active) { background:var(--paper-2); }
html[data-palette="river"] body:is(.crew-support-river,.crew-approval-river) :is(.brewery-chips .chip.on,.view-tab.on) {
  background:var(--ink);color:var(--paper);border-color:var(--ink);
}
html[data-palette="river"] body:is(.crew-support-river,.crew-approval-river) :is(.brew-brand,.tank-id) { color:var(--ink); }
html[data-palette="river"] body:is(.crew-support-river,.crew-approval-river) :is(.hdr-sub,.card-title,.section-head,.brew-meta,.tank-meta,.preview-meta) { color:var(--ink-2); }
html[data-palette="river"] body:is(.crew-support-river,.crew-approval-river) .tank-card>div[style] { color:var(--ink-2)!important; }
html[data-palette="river"] body:is(.crew-support-river,.crew-approval-river) :is(.stage-empty,.stage-fermenting) { background:var(--paper);color:var(--ink-2); }
html[data-palette="river"] body:is(.crew-support-river,.crew-approval-river) .stage-due-soon { background:var(--chip-warn);color:var(--warn); }
html[data-palette="river"] body:is(.crew-support-river,.crew-approval-river) :is(.stage-filtered,.stage-cip) { background:var(--chip-good);color:var(--good); }
html[data-palette="river"] body:is(.crew-support-river,.crew-approval-river) :is(.btn.checkout,.btn.checkin) { background:var(--paper-2);color:var(--ink); }
html[data-palette="river"] body:is(.crew-support-river,.crew-approval-river) :is(input,select,textarea) { background:var(--paper-2)!important;color:var(--ink)!important; }
html[data-palette="river"] body:is(.crew-support-river,.crew-approval-river) :is(#identity-pill,#loading,#brew-list>div[style],#existing-photos-wrap>div[style]) { color:var(--ink-2)!important; }

/* The header and card are shared surfaces, including in the tablet adapter. */
html[data-palette="river"] body:is(.crew-support-river,.crew-approval-river) :is(.hdr,.card) { background:var(--paper);color:var(--ink);border-color:var(--hair); }

html[data-palette="river"] :is(.sublinks a,.tiny-link) { color:var(--river-muted-ink)!important; }
html[data-palette="river"] .sublinks a.active { background:var(--river-selected);color:var(--river-selected-ink)!important; }

/* A lifecycle warning uses the kit pair, including above inline vessel colors. */
html[data-palette="river"] .tank .river-vessel-warning {
  background:var(--ng-warn-bg)!important;color:var(--ng-warn)!important;
}

/* Keep the page ground behind long crew lists in the requested theme. */
html[data-palette="river"] { background:var(--paper,var(--ng-canvas)); }
html[data-palette="river"] .cat-tab.on { background:var(--river-selected);color:var(--river-selected-ink);border-color:var(--river-selected); }
html[data-palette="river"] .cat-progress { background:var(--river-control);color:var(--river-control-ink);opacity:1; }
html[data-palette="river"] body.crew-support-river .file-btn { background:var(--paper-2);color:var(--ink);border-color:var(--hair); }
/* The strip's entire input remains tappable, rather than only its text line. */
html[data-palette="river"] .ng-strip .ng-strip__search > input[type] { min-height:var(--ng-h-xs);align-self:stretch; }
/* a phone search field is a 44 px touch target */
@media(max-width:600px){.ng-search > .ng-input,.ng-dtable__search .ng-input{min-height:var(--ng-touch);box-sizing:border-box}}
html[data-palette="river"] .ng-strip :is(.ng-select,.ng-input),html[data-palette="river"] .ng-strip .ng-strip__field > .ng-input { min-height:var(--ng-h-xs); }

/* Toolbar controls wrap as a group instead of shrinking icon hit areas. */
html[data-palette="river"] .ng-icon-action { flex:none;min-width:var(--ng-touch); }
html[data-palette="river"] .ng-toolbar { display:flex;flex-wrap:wrap;align-items:center; }

/* Small linked rows keep their current type and color, with a complete hit area. */
html[data-palette="river"] .ng-hit-target { display:inline-flex;min-width:var(--ng-h-xs);min-height:var(--ng-h-xs); }


/* ════ KIT R1 (2026-10-06) — the standard the kit is scored against ═══════════════════
   TYPE SCALE (one scale, eight sizes; a kit part reads a role token, never a literal):
     12 caption / hint / flag / table header   --t-dense, --t-micro
     14 body / control label / table cell       --t-body, --ng-t-control   (desktop input too)
     16 strong line / input on crew / primary   --ng-t-strong, --ng-t-input
     20 section (crew) / typed value / stat     --ng-t-section, --ng-t-value
     24 count number (crew)                     --ng-t-count
     28 PIN key (crew) / page title (desktop)   --ng-t-key, --ng-t-title
     34 page title (crew, serif)                --ng-t-title
     48 hero number (display face)              --ng-t-hero-num
   Desktop density steps three roles down one notch: section 16, count 20, key 20.
   CONTROL HEIGHT SCALE (one scale; every control height is one of these tokens):
     24 --ng-h-xs   flag, save indicator, sync pill, row-open link, checkbox box
     28 --ng-h-xs2  compact mouse worksheet control (touch stays --ng-touch)
     32 --ng-h-sm   pill, step number, dense table row, flag--lg
     44 / 36 --ng-touch           control (crew / desktop): button, field, select, chip, table row
     48 / 40 --ng-touch-primary   the one primary action (crew / desktop)
   BREAKPOINT SCALE: phone <600, tablet 600–767, desktop >=768, wide >=1100, max >=1280.
   Legacy 640/700/719/720/759 thresholds stay OUTSIDE the scale on purpose: crew iPads sit at 701-767 (iPad mini 744
   portrait) and must keep the layout they had; moving them to 767/768 would flip phone variants on there.
   ICON scale: 16 (--sm), 20 (default), 24 (--lg); glyphs inside a 24 px badge are 12-14.
   CONTRAST: every text token holds 4.5:1 on canvas, card, soft, soft-press and each status
   tint, both themes; --ng-control-edge (field and checkbox edges) holds 3:1 on canvas,
   card and soft; the danger pressed fill (--ng-danger-press) keeps danger text at 4.5:1. */

/* Focus is never hidden behind a sticky bar, dock or tab bar: a page sets these two
   tokens to the height of what it pins, and the browser keeps a focused control clear. */
html{scroll-padding-block:max(var(--ng-sticky-top,0px),var(--ng-scroll-top,0px)) max(var(--ng-sticky-bottom,0px),var(--ng-scroll-bottom,0px))}

/* ── FIELDS: label · control · hint · error. Pattern (see the gallery "Field pattern"):
   <label for="id"> + control id + aria-describedby="id-hint id-err" + aria-invalid when wrong;
   the error carries an icon AND words, never colour alone; "required" is a word. ── */
.ng-field__req{margin-left:var(--s-1);font-size:var(--t-dense);font-weight:500;color:var(--ng-ink3)}
.ng-field__err::before{
  content:"";display:inline-block;width:16px;height:16px;margin-right:var(--s-1);vertical-align:-3px;background:currentColor;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 7.5v5.5M12 16.5v.01'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 7.5v5.5M12 16.5v.01'/%3E%3C/svg%3E") center/contain no-repeat;
}
.ng-field__err:empty::before{display:none}
.ng-field:has([aria-invalid="true"]) .ng-field__label{color:var(--ng-ink1)}
.ng-field:has([aria-invalid="true"]) .ng-field__hint{display:none}

/* ── ng-check: checkbox and radio. The box is 24 px; the label around it is the full control
   height (44 crew / 36 desktop), so a thumb or a pointer never has to hit 24 px. ── */
.ng-check{position:relative;display:inline-flex;align-items:center;justify-content:center;gap:var(--s-2);min-width:var(--ng-touch);min-height:var(--ng-touch);margin:0;
  font-size:var(--t-body);font-weight:500;color:var(--ng-ink1);cursor:pointer}
.ng-check--text{justify-content:flex-start;padding-right:var(--s-3)}
.ng-check__input{
  -webkit-appearance:none;appearance:none;display:inline-grid;place-content:center;flex:none;width:var(--ng-h-xs);height:var(--ng-h-xs);margin:0;
  border:1.5px solid var(--ng-control-edge);border-radius:calc(var(--r-std) / 2);background:var(--ng-card);color:var(--ng-canvas);cursor:pointer;
  transition:background var(--ng-fast) var(--ng-ease),border-color var(--ng-fast) var(--ng-ease);
}
.ng-check__input[type="radio"]{border-radius:var(--r-pill)}
.ng-check__input::after{content:"";width:var(--s-4);height:var(--s-4);background:currentColor;opacity:0;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 12.5 4.5 4.5L19 7.5'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 12.5 4.5 4.5L19 7.5'/%3E%3C/svg%3E") center/contain no-repeat}
.ng-check__input[type="radio"]::after{width:var(--s-2);height:var(--s-2);border-radius:var(--r-pill);-webkit-mask:none;mask:none}
.ng-check__input:is(:checked,[type="checkbox"]:indeterminate){background:var(--ng-ink1);border-color:var(--ng-ink1)}
.ng-check__input:is(:checked,[type="checkbox"]:indeterminate)::after{opacity:1}
.ng-check__input[type="checkbox"]:indeterminate::after{
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M6 12h12'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M6 12h12'/%3E%3C/svg%3E") center/contain no-repeat}
.ng-check__input:focus-visible{outline:2px solid var(--ng-focus);outline-offset:2px}
.ng-check__input[aria-invalid="true"]{border-color:var(--ng-danger)}
.ng-check__input:disabled{background:var(--ng-soft);border-color:var(--ng-hairline-strong);cursor:not-allowed}
.ng-check__input:disabled:checked{background:var(--ng-ink3);border-color:var(--ng-ink3)}
@media (hover:hover){.ng-check:hover .ng-check__input:not(:disabled):not(:checked):not([type="checkbox"]:indeterminate){border-color:var(--ng-ink1)}}
/* Phone: the control itself is a 44 px hit area while the drawn box stays 24 px. The 10 px ring is a transparent
   border (part of the element, so the whole 44 px takes the tap); the visible edge is an inset shadow on the box. */
@media (max-width:700px){
  .ng-check__input{--ng-ring:calc((var(--ng-touch) - var(--ng-h-xs)) / 2);box-sizing:border-box;width:var(--ng-touch);height:var(--ng-touch);margin:calc(var(--ng-ring) * -1);border:var(--ng-ring) solid transparent;
    border-radius:calc(var(--ng-ring) + var(--r-std) / 2);background-clip:padding-box;box-shadow:inset 0 0 0 1.5px var(--ng-control-edge)}
  .ng-check__input[type="radio"]{border-radius:var(--r-pill)}
  .ng-check__input:is(:checked,[type="checkbox"]:indeterminate){border-color:transparent;box-shadow:inset 0 0 0 1.5px var(--ng-ink1)}
  .ng-check__input[aria-invalid="true"]{border-color:transparent;box-shadow:inset 0 0 0 1.5px var(--ng-danger)}
  .ng-check__input:disabled{border-color:transparent;box-shadow:inset 0 0 0 1.5px var(--ng-hairline-strong)}
  .ng-check__input:disabled:checked{border-color:transparent;box-shadow:inset 0 0 0 1.5px var(--ng-ink3)}
  /* the state rules paint with the background shorthand, which resets background-clip to border-box and would
     fill the whole 44 px ring: keep the paint inside the 24 px box in every state, radios included */
  .ng-check__input:is(:checked,[type="checkbox"]:indeterminate,:disabled,:disabled:checked,[aria-invalid="true"]),
  .ng-check__input[type="radio"]:is(:checked,:disabled,:disabled:checked,[aria-invalid="true"]){background-clip:padding-box}
}
.ng-check:has(.ng-check__input:disabled){color:var(--ng-ink3);cursor:not-allowed}

/* ── DESKTOP DATA TABLE v2 (IBM Carbon grade). Markup in the gallery chapter "Month-end
   worksheet" -> "Data table". A table is one .ng-dtable: optional toolbar, a labelled focusable
   scroll region holding <table class="ng-table ng-table--v2">, an optional footer.
     <caption>             the table's name (visible, or .ng-table__caption--hidden)
     <th scope="col">      every column header; a sortable one holds .ng-table__sort + aria-sort
     .ng-table__num        right-aligned, tabular numerals; the header over it aligns the same
     .ng-table__select     a checkbox column (.ng-check, aria-label "Select <row name>")
     .ng-table__actions    row actions (.ng-icon-action, each aria-label names its row)
     tbody.ng-table__state empty / error row;  tbody.ng-table__skel loading rows (aria-busy)
   Row density follows the kit: crew 44 px, desktop 36 px, .ng-table--dense 32 px. ── */
.ng-dtable{display:grid;min-width:0;background:var(--ng-card);border:1px solid var(--ng-hairline);border-radius:var(--r-card);overflow:clip}
.ng-dtable > .ng-table-wrap{border:0;border-radius:0}
.ng-dtable__bar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-2) var(--s-3);padding:var(--s-2) var(--s-3);border-bottom:1px solid var(--ng-hairline)}
.ng-dtable__search{flex:1 1 220px;max-width:360px;min-width:0}
.ng-dtable__tools{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-2);margin-left:auto}
.ng-dtable__batch{background:var(--ng-soft)}
.ng-dtable__batch-count{margin-right:auto;font-size:var(--ng-t-control);font-weight:700;color:var(--ng-ink1)}
.ng-dtable__foot{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--s-2) var(--s-3);padding:var(--s-1) var(--s-3);border-top:1px solid var(--ng-hairline);font-size:var(--t-dense);color:var(--ng-ink2)}
.ng-dtable__pager{display:inline-flex;align-items:center;gap:var(--s-1)}
/* the scroll region: named, reachable by keyboard, ringed when focused */
:is(.ng-table-wrap,.nc-table-wrap,.pd-table-wrap,.pv-table-wrap,.cc-weekbody,.ad-digest)[tabindex="0"]:focus-visible{outline:2px solid var(--ng-focus);outline-offset:-2px}
.ng-table-wrap[role="region"]:focus-visible{outline:2px solid var(--ng-focus);outline-offset:-2px}
.ng-table-wrap--scroll{max-height:min(70vh,calc(var(--ng-touch) * 14))}
/* page-sticky: no inner scroll (the page scrolls); the header pins under the page's own bar */
.ng-dtable--page{overflow:visible}
/* from a tablet up the page scrolls and the header pins to the page; on a phone the table keeps its own sideways scroll */
@media (min-width:768px){.ng-dtable--page > .ng-table-wrap{overflow-x:clip;overflow-y:visible}}
/* a page table never outgrows its card: when the card is narrow (a detail panel sits beside it)
   the low-priority columns drop out, exactly as they do on a phone, instead of spilling into the gutter */
.ng-dtable--page{container:ng-dtable / inline-size}
@container ng-dtable (max-width:659px){.ng-dtable--page .ng-table--prio [data-prio="low"]{display:none}}
@media (min-width:768px){.ng-dtable--page .ng-table th{top:var(--ng-sticky-top,0px)}}
.ng-table__caption{caption-side:top;padding:var(--s-3) var(--s-3) var(--s-2);text-align:left;font-size:var(--ng-t-strong);font-weight:700;color:var(--ng-ink1)}
.ng-table__caption small{display:block;margin-top:var(--s-0);font-size:var(--t-dense);font-weight:500;color:var(--ng-ink2)}
.ng-table__caption--hidden{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.ng-table--v2 th{vertical-align:middle}
.ng-table--v2 th[scope="col"]:not([aria-sort]){padding:0 var(--s-3);height:var(--ng-touch)}
.ng-table--v2.ng-table--dense th[scope="col"]:not([aria-sort]){height:var(--ng-h-sm)}
/* numeric columns: the header shares the numerals' right edge. The sort arrow sits LEFT of the
   label, so the label (not the arrow) lands on the same edge as the numbers below. */
.ng-table--v2 th.ng-table__num{text-align:right}
.ng-table__num .ng-table__sort{flex-direction:row-reverse;justify-content:flex-start}
.ng-table__sort:focus-visible .ng-icon:first-of-type{opacity:.6}
.ng-table__select{width:var(--ng-touch);padding:0 0 0 var(--s-2);text-align:center}
.ng-table--dense .ng-table__select{width:var(--ng-h-sm)}
.ng-table__select .ng-check{vertical-align:middle}
.ng-table--dense .ng-check{min-width:var(--ng-h-sm);min-height:var(--ng-h-sm)}
.ng-table--v2 th.ng-table__select,.ng-table--v2 td.ng-table__select{padding-right:0}
.ng-table__actions{width:1%;padding-right:var(--s-2);text-align:right;white-space:nowrap}
.ng-table__actions .ng-icon-action{border-color:transparent}
@media (hover:hover){.ng-table__actions .ng-icon-action:hover{border-color:var(--ng-hairline-strong)}}
.ng-table tbody tr[data-selected] td,.ng-table tbody tr:has(.ng-check__input:checked) td{background:var(--ng-soft-press)}
/* states: a row that says one sentence and the next step, never a blank box */
.ng-table__state td{height:auto;padding:0;border-bottom:0}
.ng-table__state .ng-state{padding:var(--s-8) var(--s-4)}
.ng-table__skel td{height:var(--ng-touch)}
.ng-table--dense .ng-table__skel td{height:var(--ng-h-sm)}
.ng-table__skel .ng-skeleton{width:70%}
.ng-table__skel .ng-table__num .ng-skeleton{width:40%;margin-left:auto}
.ng-table__skel .ng-table__select .ng-skeleton{width:var(--ng-h-xs);height:var(--ng-h-xs);margin:0 auto}
@media (prefers-reduced-motion:reduce){.ng-skeleton{animation:none}}

/* ── RECEIPT / CONFIRMATION. A receipt is a human sentence about what happened, the numbers,
   what happens next, and the next actions. After it renders, focus moves to its title
   (tabindex="-1", data-ng-receipt-focus) so a keyboard or screen-reader user lands on the
   outcome. role="status" for done and pending, role="alert" for failed. It never shows raw
   JSON, an empty result or an ISO date. ── */
.ng-receipt__title:focus{outline:none}
.ng-receipt__title:focus-visible{outline:2px solid var(--ng-focus);outline-offset:var(--s-1);border-radius:var(--r-std)}
.ng-receipt__body{display:grid;gap:var(--s-3)}
.ng-receipt__next{display:grid;gap:var(--s-2);align-content:start}
.ng-receipt__next-title{margin:0;font-size:var(--t-dense);font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--ng-ink3)}
.ng-receipt__actions{padding-top:var(--s-3);border-top:1px solid var(--ng-hairline)}
@container ng-receipt (min-width: 640px){
  .ng-receipt--confirm .ng-receipt__body{grid-template-columns:minmax(0,3fr) minmax(0,2fr);gap:var(--s-6)}
}
@media (min-width:768px) and (pointer:fine){.ng-desktop .ng-table--v2.ng-table--dense th[scope="col"]:not([aria-sort]){padding:0 var(--s-2)}}


/* ══ KIT ROUND 13 ═══════════════════════════════════════════════════════════════════════════
   The desktop page parts: menu, in-page tabs, breadcrumb, tooltip, filter, pager, and the page
   recipe that holds them. Behaviour lives in noli-kit.js (NG.menu, NG.tabs, NG.tip, NG.dtable);
   a page writes only markup and data-attributes, never CSS. Every size follows --ng-touch, so
   crew density stays 44 and desktop density is 36. ══ */

/* ── menu (role=menu). One shared list per page; a trigger names it with aria-controls. ── */
.ng-menu{
  position:fixed;z-index:60;min-width:200px;max-width:min(320px,calc(100vw - 2 * var(--s-4)));max-height:min(60vh,420px);overflow:auto;
  margin:0;padding:var(--s-1);background:var(--ng-card);border:1px solid var(--ng-hairline-strong);border-radius:var(--r-card);
  box-shadow:var(--ng-shadow-raised);color:var(--ng-ink1);
}
.ng-menu[hidden]{display:none}
.ng-menu__item{
  display:flex;align-items:center;gap:var(--s-2);width:100%;min-height:var(--ng-touch);padding:0 var(--s-3);margin:0;border:0;border-radius:var(--r-std);
  background:none;color:inherit;font:inherit;font-size:var(--ng-t-control);font-weight:600;text-align:left;cursor:pointer;text-decoration:none;
  padding-block:var(--s-2); /* a label that wraps to two lines keeps its air */
}
.ng-menu__item .ng-icon{color:var(--ng-ink2)}
a.ng-menu__item{text-decoration:none}
@media (hover:hover){.ng-menu__item:hover{background:var(--ng-soft)}}
@media (hover:hover){a.ng-menu__item:hover{text-decoration:none}}
.ng-menu__item:focus-visible{outline:2px solid var(--ng-focus);outline-offset:-2px;background:var(--ng-soft)}
.ng-menu__item:is(:disabled,[aria-disabled="true"]){color:var(--ng-ink3);cursor:not-allowed}
.ng-menu__item--danger,.ng-menu__item--danger .ng-icon{color:var(--ng-danger)}
.ng-menu__sep{height:1px;margin:var(--s-1) calc(-1 * var(--s-1));background:var(--ng-hairline)}
[data-ng-menu][aria-expanded="true"]{background:var(--ng-soft-press);color:var(--ng-ink1)}

/* ── tabs (in-page; role=tablist). Underline tabs, ink for the selected one. ── */
.ng-tabs{display:flex;gap:var(--s-1);margin:0;padding:0;border-bottom:1px solid var(--ng-hairline);overflow-x:auto;scrollbar-width:none}
.ng-tabs::-webkit-scrollbar{display:none}
/* a strip wider than its box fades where more waits (data-fade set by noli-kit.js on .ng-tabs and any [data-ng-fade] row) */
:is(.ng-tabs,[data-ng-fade],.river-dist-nav-domain)[data-fade~="end"]{--ng-fade-mask:linear-gradient(to right,currentColor calc(100% - var(--ng-fade-w)),transparent)}
:is(.ng-tabs,[data-ng-fade],.river-dist-nav-domain)[data-fade~="start"]{--ng-fade-mask:linear-gradient(to left,currentColor calc(100% - var(--ng-fade-w)),transparent)}
:is(.ng-tabs,[data-ng-fade],.river-dist-nav-domain)[data-fade~="start"][data-fade~="end"]{--ng-fade-mask:linear-gradient(to right,transparent,currentColor var(--ng-fade-w),currentColor calc(100% - var(--ng-fade-w)),transparent)}
:is(.ng-tabs,[data-ng-fade],.river-dist-nav-domain)[data-fade]{-webkit-mask-image:var(--ng-fade-mask);mask-image:var(--ng-fade-mask)}
.ng-tabs__tab{
  position:relative;flex:none;display:inline-flex;align-items:center;gap:var(--s-2);min-height:var(--ng-touch);padding:0 var(--s-3);margin:0;
  border:0;border-radius:var(--r-std) var(--r-std) 0 0;background:none;color:var(--ng-ink2);font:inherit;font-size:var(--ng-t-control);font-weight:600;white-space:nowrap;cursor:pointer;
}
.ng-tabs__tab::after{content:"";position:absolute;left:var(--s-2);right:var(--s-2);bottom:-1px;height:2px;border-radius:var(--r-pill);background:transparent}
@media (hover:hover){.ng-tabs__tab:hover{color:var(--ng-ink1);background:var(--ng-soft)}}
.ng-tabs__tab[aria-selected="true"]{color:var(--ng-ink1)}
.ng-tabs__tab[aria-selected="true"]::after{background:var(--ng-ink1)}
.ng-tabs__tab:focus-visible{outline:2px solid var(--ng-focus);outline-offset:-2px}
.ng-tabs__count{font-variant-numeric:tabular-nums;font-weight:500;color:var(--ng-ink2)}
.ng-tabpanel{padding-top:var(--s-4)}
.ng-tabpanel[hidden]{display:none}
.ng-tabpanel:focus-visible{outline:2px solid var(--ng-focus);outline-offset:2px;border-radius:var(--r-std)}

/* ── breadcrumb ── */
.ng-crumbs{margin:0 0 var(--s-2)}
/* a page that also wears mobile_tier1.css (the River palette sheet) gets a bare `nav` pinned as a
   bottom bar; the breadcrumb is a plain strip and says so */
nav.ng-crumbs{position:static;display:block;border-top:0;background:none;padding-bottom:0;z-index:auto}
.ng-crumbs__list{display:flex;flex-wrap:wrap;align-items:center;gap:0;margin:0;padding:0;list-style:none;font-size:var(--ng-t-control)}
.ng-crumbs__item{display:inline-flex;align-items:center}
.ng-crumbs__item + .ng-crumbs__item::before{content:"/" / "";padding:0 var(--s-1);color:var(--ng-ink3)}
.ng-crumbs__link{display:inline-flex;align-items:center;min-height:var(--ng-touch);padding:0 var(--s-1);border-radius:var(--r-std);color:var(--ng-ink2);font-weight:600;text-decoration:none}
@media (hover:hover){.ng-crumbs__link:hover{color:var(--ng-ink1);text-decoration:underline;text-underline-offset:3px}}
.ng-crumbs__link[aria-current="page"]{color:var(--ng-ink1);text-decoration:none;cursor:default}
.ng-crumbs__item:first-child .ng-crumbs__link{margin-left:calc(-1 * var(--s-1))}
.ng-crumbs__link:focus-visible{outline:2px solid var(--ng-focus);outline-offset:1px}

/* ── tooltip (role=tooltip): hover or focus, Escape dismisses, the pointer may enter it ── */
.ng-tip{
  position:fixed;z-index:70;max-width:260px;padding:var(--s-1) var(--s-2);border-radius:var(--r-std);
  background:var(--ng-ink1);color:var(--ng-canvas);font-size:var(--t-dense);font-weight:600;line-height:1.35;box-shadow:var(--ng-shadow-raised);
}
.ng-tip[hidden]{display:none}

/* ── filter: selects (and a clear) in the table toolbar ── */
.ng-filter{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-2) var(--s-4)}
.ng-filter__field{display:inline-flex;align-items:center;gap:var(--s-2)}
.ng-filter__label{font-size:var(--t-dense);font-weight:600;color:var(--ng-ink2)}
.ng-filter .ng-select-wrap{min-width:140px}
.ng-filter__count{font-size:var(--t-dense);font-weight:600;color:var(--ng-ink2);white-space:nowrap}
.ng-filter__count:empty{display:none}

/* ── pager (table footer): page size, a spoken range, previous and next ── */
.ng-dtable__pager{gap:var(--s-3)}
.ng-pager__size{display:inline-flex;align-items:center;gap:var(--s-1)}
.ng-pager__size .ng-select-wrap{min-width:84px}
.ng-pager__range{min-width:9ch;font-variant-numeric:tabular-nums;font-weight:600;color:var(--ng-ink1);text-align:center}
.ng-pager__btns{display:inline-flex;align-items:center;gap:var(--s-1)}
.ng-pager__btns .ng-icon-action[aria-disabled="true"]{opacity:.45;cursor:not-allowed}

/* ── desktop page recipe: header, toolbar, filter bar, table, detail panel ── */
.ng-page{max-width:var(--ng-page-max);margin:0 auto;padding:var(--s-4) var(--s-4) var(--s-8)}
.ng-pagehead{display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:var(--s-3) var(--s-6);margin:0 0 var(--s-4)}
.ng-pagehead__text{min-width:0;flex:1 1 320px}
.ng-pagehead__text .ng-title{margin-top:var(--s-0)}
.ng-pagehead__actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-2)}
.ng-page__grid{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s-4);align-items:start;margin-top:var(--s-3)}
@media (min-width:1100px){.ng-page__grid--aside{grid-template-columns:minmax(0,1fr) 320px}}
.ng-aside{display:grid;gap:var(--s-3);padding:var(--s-4);background:var(--ng-card);border:1px solid var(--ng-hairline);border-radius:var(--r-card)}
@media (min-width:1100px){.ng-aside{position:sticky;top:calc(var(--ng-sticky-top,0px) + var(--s-4))}}
.ng-aside__head{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--s-2)}
.ng-aside__title{margin:0;font-size:var(--ng-t-strong);font-weight:700}
.ng-aside__facts{display:grid;grid-template-columns:auto 1fr;gap:var(--s-1) var(--s-4);margin:0;font-size:var(--ng-t-control)}
.ng-aside__facts dt{color:var(--ng-ink2)}
.ng-aside__facts dd{margin:0;font-weight:600;text-align:right;font-variant-numeric:tabular-nums}
.ng-aside__empty{margin:0;color:var(--ng-ink2);font-size:var(--ng-t-control)}
/* a row's name as a button (opens the detail panel / record without leaving the page) */
.ng-table__link{display:inline-flex;align-items:center;min-height:var(--ng-h-xs);padding:0;margin:0;border:0;border-radius:var(--r-std);background:none;
  color:inherit;font:inherit;font-weight:600;text-align:left;cursor:pointer}
@media (hover:hover){.ng-table__link:hover{text-decoration:underline;text-underline-offset:3px}}
.ng-table__link:focus-visible{outline:2px solid var(--ng-focus);outline-offset:2px}
.ng-table tbody tr[data-active] td{background:var(--ng-soft)}
.ng-page--embedded{padding:0}
/* a page that opens with its section's sub-tab strip: the strip is the top edge, the head sits right under it */
.ng-page--tabbed{padding-top:0}
.ng-page--tabbed > .ng-tabs:first-child{margin-bottom:var(--s-1)}
/* a card whose content (a table, a divided list) runs edge to edge */
.ng-card--flush{padding:0}
.ng-table--v2 th.ng-table__select[scope="col"]:not([aria-sort]){padding:0 0 0 var(--s-2)}


/* ══ KIT ROUND 14 — critic pass on the desktop page recipe ═══════════════════════════════════
   One checkbox column x for header and rows; a phone table that keeps Status and the number in
   view without sideways scroll (column priority, not a scroll); a detail panel that collapses
   when it has nothing to show; filters that stack their label over the select on a phone. ══ */
.ng-table th.ng-table__select,.ng-table--v2 th.ng-table__select[scope="col"]{text-align:center}
.ng-table__select .ng-check{display:inline-flex;justify-content:center;margin:0}
.ng-table--v2 td.ng-table__select{padding-left:var(--s-2)}
/* PHONE TABLE (.ng-table--prio). A desktop-density table never scrolls sideways on a phone.
   Below 600 px each row becomes a two-line card whose cells name their slot:
     data-slot="title"  line 1 left (the id, bold)      data-slot="flag"  line 1 right (status)
     data-slot="sub"    line 2 left, one line, ellipsis data-slot="num"   line 2 right (the number;
                                                         data-unit="cases" adds its unit)
   .ng-table__actions keeps its own column spanning both lines. data-prio="low" cells (the
   checkbox, a second text column) drop out. The header (and its sort buttons) is dropped with it: a phone
   finds a row by search and filter, and the row menu and detail panel reach everything a
   dropped cell held. */
@media (max-width:600px){
  .ng-table--prio [data-prio="low"]{display:none}
  .ng-table--prio thead{display:none}
  .ng-table--prio,.ng-table--prio tbody{display:block}
  .ng-table--prio tbody:not(.ng-table__state):not(.ng-table__skel) tr{display:grid;grid-template-columns:minmax(0,1fr) auto var(--ng-touch);grid-template-areas:"title flag act" "sub num act";align-items:center;gap:0 var(--s-3);padding:var(--s-2) var(--s-1) var(--s-2) var(--s-3);border-bottom:1px solid var(--ng-hairline)}
  .ng-table--prio tbody tr[data-active]{background:var(--ng-soft)}
  .ng-table--prio tbody tr td{display:block;height:auto;min-width:0;padding:0;border:0;background:none;white-space:nowrap}
  .ng-table--prio [data-slot="title"]{grid-area:title;font-weight:700}
  .ng-table--prio [data-slot="flag"]{grid-area:flag;justify-self:end}
  .ng-table--prio [data-slot="sub"]{grid-area:sub;overflow:hidden;text-overflow:ellipsis;color:var(--ng-ink2)}
  .ng-table--prio [data-slot="num"]{grid-area:num;justify-self:end;text-align:right}
  .ng-table--prio [data-unit]::after{content:" " attr(data-unit);font-weight:500;color:var(--ng-ink2)}
  .ng-table--prio td.ng-table__actions{grid-area:act;width:auto;padding:0;text-align:center}
  .ng-table--prio .ng-table__state td{display:table-cell}
  /* Long status notes must not take the beer/package column's width. Opt-in keeps
     compact badges on existing tables; all content remains in the reading order. */
  .ng-table--prio.ng-table--status-notes tbody:not(.ng-table__state):not(.ng-table__skel) tr:has(.ng-flag--wrap){grid-template-columns:minmax(0,1fr) auto var(--ng-touch);grid-template-areas:"title title act" "sub num act" "flag flag act";row-gap:var(--s-1)}
  .ng-table--prio.ng-table--status-notes tr:has(.ng-flag--wrap) [data-slot="flag"]{justify-self:start;max-width:100%;white-space:normal}
  .ng-table--prio.ng-table--status-notes tr:has(.ng-flag--wrap) [data-slot="flag"] .ng-flag{max-width:100%;white-space:normal;overflow-wrap:anywhere}
  .ng-table--prio.ng-table--status-notes tr:has(.ng-flag--wrap) [data-slot="title"] .ng-table__link{max-width:100%;white-space:normal}
  .ng-table--prio.ng-table--status-notes tr:has(.ng-flag--wrap) [data-slot="sub"]{white-space:normal;overflow-wrap:anywhere}
  .ng-filter{display:grid;grid-template-columns:1fr 1fr;gap:var(--s-2)}
  .ng-filter__field{display:flex;flex-direction:column;align-items:stretch;gap:var(--s-1);min-width:0}
  .ng-filter .ng-select-wrap{min-width:0;width:100%}
  .ng-filter__count,.ng-filter .ng-text-action{grid-column:1 / -1}
}
/* the detail panel: nothing chosen means no empty column — the table takes the width */
.ng-aside[data-empty]{display:none}
@media (min-width:1100px){.ng-page__grid--aside:has(> .ng-aside[data-empty]){grid-template-columns:minmax(0,1fr)}}

/* ── R15: a touch target in a table is 44 px (the box stays 24 px inside it) in crew density and
   on any coarse pointer, whatever the density. Desktop density with a mouse is unchanged. ── */
@media (pointer:coarse){
  .ng-table .ng-check,.ng-table--dense .ng-check{min-width:var(--ng-touch);min-height:var(--ng-touch)}
  .ng-table__actions .ng-icon-action{width:var(--ng-touch);height:var(--ng-touch)}
  .ng-table__select{width:var(--ng-touch)}
}
@media (max-width:640px){.ng-table--prio .ng-table__link{display:inline-flex;align-items:center;min-height:var(--ng-touch)}}
.ng-table:not(.ng-desktop *) .ng-check,.ng-table--dense:not(.ng-desktop *) .ng-check{min-width:var(--ng-touch);min-height:var(--ng-touch)}
.ng-table:not(.ng-desktop *) .ng-table__actions .ng-icon-action{width:var(--ng-touch);height:var(--ng-touch)}

/* ── ng-aside--sheet: the row detail panel as a phone bottom sheet (.ng-dtable[data-ng-detail-sheet]) ── */
.ng-aside-scrim{position:fixed;inset:0;z-index:99;background:var(--ng-scrim)}
.ng-aside-scrim[hidden]{display:none}
@media (max-width:700px){
  .ng-aside.ng-aside--sheet{
    position:fixed;inset:auto 0 0 0;z-index:100;max-height:85vh;overflow:auto;overscroll-behavior:contain;
    border:0;border-radius:var(--r-card) var(--r-card) 0 0;box-shadow:var(--ng-shadow-raised);
    padding:var(--s-4) var(--s-4) calc(var(--s-6) + env(safe-area-inset-bottom,0px));
    animation:ng-sheet-in var(--ng-std) var(--ng-ease);
  }
  .ng-aside.ng-aside--sheet:focus{outline:none}
  .ng-aside.ng-aside--sheet .ng-aside__head{position:sticky;top:calc(-1 * var(--s-4));background:var(--ng-card);z-index:1;padding:var(--s-1) 0}
  html.ng-detail-sheet-open{overflow:hidden}
}

/* ── ng-date: a typed date field with a kit calendar (NG.date; replaces the native picker) ── */
.ng-date{position:relative;display:inline-flex;align-items:center;min-width:0}
.ng-date > .ng-input{padding-right:calc(var(--ng-touch) + var(--s-1));min-width:9.5rem;font-variant-numeric:tabular-nums}
.ng-date__words{position:absolute;left:1px;right:calc(var(--ng-touch) + var(--s-1));top:1px;bottom:1px;display:flex;align-items:center;padding-left:var(--s-3);background:transparent;border-radius:var(--r-std);color:var(--ng-ink1);font-size:var(--ng-t-control);font-weight:500;font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;pointer-events:none}
/* the words sit over the field's own surface (no card colour of their own); the typed text under them is hidden until the person edits */
.ng-date > .ng-input[data-words]{color:transparent;-webkit-text-fill-color:transparent;caret-color:transparent}
.ng-date > .ng-input[data-words]::selection{color:transparent;-webkit-text-fill-color:transparent;background:var(--ng-soft)}
.ng-date__btn{position:absolute;right:2px;top:50%;transform:translateY(-50%);width:calc(var(--ng-touch) - 6px);height:calc(var(--ng-touch) - 6px);border-color:transparent}
@media (max-width:600px){.ng-date__btn{right:0;width:var(--ng-touch);height:var(--ng-touch)}}
.ng-datepop{position:fixed;z-index:100;width:var(--ng-datepop-desktop);padding:var(--s-3);background:var(--ng-card);border:1px solid var(--ng-hairline-strong);border-radius:var(--r-card);box-shadow:var(--ng-shadow-raised);color:var(--ng-ink1)}
.ng-datepop__head{display:flex;align-items:center;justify-content:space-between;gap:var(--s-2);margin-bottom:var(--s-2)}
.ng-datepop__title{font-size:var(--ng-t-control);font-weight:700}
.ng-datepop__dow,.ng-datepop__week{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}
.ng-datepop__dow span{display:grid;place-items:center;height:28px;font-size:var(--t-dense);font-weight:600;color:var(--ng-ink3)}
.ng-datepop__day{display:grid;place-items:center;height:36px;margin:0;padding:0;border:1px solid transparent;border-radius:var(--r-std);background:none;color:var(--ng-ink1);font:inherit;font-size:var(--ng-t-control);font-variant-numeric:tabular-nums;cursor:pointer}
@media (hover:hover){.ng-datepop__day:hover:not(:disabled){background:var(--ng-soft)}}
.ng-datepop__day[data-today]{border-color:var(--ng-ink3)}
.ng-datepop__day[aria-pressed="true"]{background:var(--ng-ink1);color:var(--ng-canvas);font-weight:700}
.ng-datepop__day:focus-visible{outline:2px solid var(--ng-focus);outline-offset:1px}
.ng-datepop__day:disabled{color:var(--ng-ink3);opacity:.5;cursor:not-allowed}
/* Crew density keeps 44 px days even with a trackpad. NG.date carries the field's
   desktop designation into the body-level popup without changing its font tokens. */
.ng-datepop:not(.ng-datepop--desktop){width:min(var(--ng-datepop-crew),calc(100vw - var(--s-2)));padding:var(--s-3)}
.ng-datepop:not(.ng-datepop--desktop) .ng-datepop__day{min-width:var(--ng-touch-crew);height:var(--ng-touch-crew)}
/* Touch calendars need seven real 44 px cells, including at 320px. Mouse desktop keeps
   its compact 288px popover. Reduce gutters on narrow phones, never the hit area. */
@media (max-width:600px),(pointer:coarse){
  .ng-datepop{width:min(var(--ng-datepop-crew),calc(100vw - var(--s-2)));padding:var(--s-3)}
  .ng-datepop__dow,.ng-datepop__week{gap:2px}
  .ng-datepop__day{min-width:var(--ng-touch);height:var(--ng-touch)}
}

.ng-datepop__foot{display:flex;justify-content:space-between;margin-top:var(--s-2)}


/* a disabled check reads as disabled: dimmed box and label, no pointer */

.ng-check__input:disabled{opacity:.55}
/* a phone card is a grid of cells: the selected tint belongs to the whole row, not to each cell (cells paint patches between the gaps) */
@media (max-width:600px){
  .ng-table--prio tbody tr:is([aria-selected="true"],[data-selected],[data-active],:has(.ng-check__input:checked)){background:var(--ng-soft-press)}
  .ng-table--prio tbody tr:is([aria-selected="true"],[data-selected],[data-active],:has(.ng-check__input:checked)) td{background:none}
}
@media (hover:hover){@media (max-width:600px){.ng-table--prio tbody tr:hover td{background:none}.ng-table--prio tbody:not(.ng-table__state):not(.ng-table__skel) tr:hover{background:var(--ng-soft)}}}

/* a wide table that must not scroll sideways: column heads wrap onto two lines (ng-table--wrapheads) */
/* Numeric cell alignment must not force unsorted headers back onto one line. */
.ng-table--wrapheads thead th.ng-table__num{white-space:normal}
@media (min-width:641px){
  .ng-table--wrapheads thead .ng-table__sort{white-space:normal;text-align:right;max-width:6.5rem}
  .ng-table--wrapheads thead th:first-child .ng-table__sort{text-align:left;max-width:none}
  .ng-table--wrapheads .ng-table__actions{width:var(--ng-touch);padding-left:var(--s-1)}
}
/* a tighter segmented control for a phone row that must share its line (ng-seg--tight) */
.ng-seg--tight .ng-seg__item{padding-inline:var(--s-2);white-space:nowrap}
/* a date field inside a bordered strip field: the field draws the one border */
.ng-strip__field .ng-date>.ng-input{border-color:transparent;background:transparent;box-shadow:none}
/* ── ng-stats--strip: the same figures on ONE compact line (label and number inline) ── */
.ng-stats--strip{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--s-1) var(--s-6)}
.ng-stats--strip .ng-stats__item{display:inline-flex;align-items:baseline;gap:var(--s-2)}
.ng-stats--strip dt{font-size:var(--ng-t-control)}
.ng-stats--strip dd{font-size:var(--ng-t-strong)}

/* ── ng-sheet--scroll: a long sheet scrolls its BODY between a fixed head and foot, so a line scrolled
   past the foot is clipped by the body, not left half-visible beneath it (axe target-size "partially
   obscured"). Add the class to a sheet with a head, a body and a foot. ── */
.ng-sheet--scroll[open]{display:flex;flex-direction:column;overflow:hidden}
.ng-sheet--scroll > :is(.ng-sheet__grab,.ng-sheet__head,.ng-sheet__foot){flex:none}
.ng-sheet--scroll > .ng-sheet__body{flex:1 1 auto;min-height:0;overflow-y:auto;align-content:start;margin-inline:calc(-1 * var(--s-4));padding:2px var(--s-4)}
/* a field focused or scrolled to inside a scrolling sheet stops clear of the edge above the sticky footer */
.ng-sheet--scroll > .ng-sheet__body{scroll-padding-block:var(--s-6)}
@media (min-width:768px){.ng-sheet--center.ng-sheet--scroll > .ng-sheet__body{margin-inline:calc(-1 * var(--s-6));padding-inline:var(--s-6)}}

/* ── ng-aside--overlay: the detail panel floats at the right edge over the table instead of taking a
   column from it (the table keeps all its columns). Desktop only; on a phone the aside stacks. ── */
@media (min-width:1100px){
  .ng-aside--overlay:not([data-empty]){position:fixed;z-index:40;top:var(--ng-aside-top);right:var(--s-4);bottom:var(--s-4);align-content:start;width:min(380px,calc(100vw - var(--s-4) * 2));overflow:auto;box-shadow:var(--ng-shadow-raised)}
}

/* ── ng-filter--row: the filter group never stacks into a screen of controls on a phone: it is one
   row that scrolls sideways. ── */
@media (max-width:600px){
  .ng-dtable > *{min-width:0}
  .ng-filter.ng-filter--row{display:flex;flex-wrap:nowrap;overflow-x:auto;gap:var(--s-2);padding-bottom:var(--s-1);scrollbar-width:thin;min-width:0;max-width:100%}
  .ng-filter--row .ng-filter__field{flex:none;flex-direction:row;align-items:center}
  .ng-filter--row .ng-select-wrap{min-width:9.5rem;width:auto}
}

/* ── stat tone: a figure that means something wears it (past due red, needs a look amber, all clear green);
   the number never leans on colour alone, the label beside it says what it is. ── */
.ng-stats__item[data-tone="danger"] :is(dd,.ng-text-action){color:var(--ng-danger)}
.ng-stats__item[data-tone="warn"] :is(dd,.ng-text-action){color:var(--ng-warn)}
.ng-stats__item[data-tone="ok"] :is(dd,.ng-text-action){color:var(--ng-ok)}

/* ── ng-detail sheet (data-ng-detail-sheet on an .ng-aside): on a phone the detail panel is a bottom sheet over a scrim ── */
.ng-detail-scrim{display:none}
.ng-filter-toggle{display:none}
@media (width < 1100px){
  .ng-aside[data-ng-detail-sheet]:not([data-empty]){position:fixed;z-index:120;left:0;right:0;bottom:0;top:auto;max-height:82vh;max-width:640px;margin-inline:auto;overflow:auto;align-content:start;border-radius:var(--r-card) var(--r-card) 0 0;box-shadow:var(--ng-shadow-raised);padding-bottom:calc(var(--s-6) + env(safe-area-inset-bottom,0px))}
  .ng-aside[data-ng-detail-sheet]:focus{outline:none}
  html.ng-detail-sheet-open{overflow:hidden}
  .ng-detail-scrim{display:block;position:fixed;inset:0;z-index:119;background:var(--ng-scrim)}
}
@media (max-width:600px){
  /* a folded filter group: one button beside the search, the group opens as two columns underneath */
  .ng-filter-toggle{display:inline-flex}
  .ng-filter--fold:not([data-open]){display:none}
  .ng-filter.ng-filter--row.ng-filter--fold[data-open]{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));overflow:visible;gap:var(--s-2)}
  .ng-filter--fold[data-open] .ng-filter__field{flex-direction:column;align-items:stretch;min-width:0}
  .ng-filter--fold[data-open] > .ng-filter__field:first-child{grid-column:1 / -1}
  .ng-filter--fold[data-open] .ng-select-wrap{min-width:0;width:100%}
  .ng-filter--fold[data-open] .ng-date,.ng-filter--fold[data-open] .ng-date > .ng-input{min-width:0;width:100%}
  .ng-filter--fold[data-open] .ng-filter__field[role="group"]{grid-column:1 / -1;flex-direction:row;align-items:center}
  .ng-filter--fold[data-open] .ng-filter__field[role="group"] .ng-date{flex:1 1 0}
}

/* KIT ROUND 19 (Prices critic r1) - the page ground never has a seam. body and html paint different
   greys, so a short state (locked, filtered to nothing) left the body's grey ending mid-viewport
   with the lighter html ground beneath. The body is always at least a screen tall. */

/* A table that must not jump when a filter narrows its rows: columns are laid out by the header,
   not by whatever the visible rows happen to contain. */
.ng-table--fixed:not(.ng-table--stack-now){table-layout:fixed;width:100%}
.ng-table--fixed :is(th,td){overflow-wrap:anywhere}

/* A package a beer does not come in: on a phone the row is simply not shown (the kit's stacked-row
   pattern, so a card is as tall as what it says); on a wide table the cell says "Not sold". */
@media (max-width:599px){
  .ng-table--stack .ng-table__none{display:none}
  /* every editable value on a phone is a full 44 px target, not the 24px desktop link */
  .ng-table--stack td .ng-table__link{min-height:var(--ng-touch);flex:1 1 auto;justify-content:flex-end}
  .ng-table--stack td:has(.ng-table__link){align-items:center}
  .ng-search .ng-input,.ng-dtable__bar .ng-select{min-height:var(--ng-touch)}
}
@media (pointer:coarse){
  .ng-table .ng-table__link{min-height:var(--ng-touch)}
}
.ng-table__none-label{font-size:var(--t-dense);color:var(--ng-ink2)}

/* ng-skip: the first Tab stop on a page, hidden until focused; jumps past the shell chrome to the page's work. */
.ng-skip{position:absolute;left:var(--s-3);top:-100px;z-index:100;padding:0 var(--s-4);min-height:var(--ng-touch-crew);display:inline-flex;align-items:center;
  background:var(--ng-card);color:var(--ng-ink1);border:2px solid var(--ng-focus);border-radius:var(--r-std);font-weight:600;text-decoration:none}
.ng-skip:focus{top:var(--s-3)}

/* KIT44: a row-open link and a row-detail link are 44 px in crew density too (they were 24 px tall, one 43.4 px wide).
   Desktop density with a mouse keeps its 24 px link; a coarse pointer gets 44 whatever the density. */
.ng-table:not(.ng-desktop *) :is(.ng-table__open,.ng-table__link){min-height:var(--ng-touch);min-width:var(--ng-touch)}
@media (pointer:coarse){.ng-table :is(.ng-table__open,.ng-table__link){min-height:var(--ng-touch);min-width:var(--ng-touch)}}

/* Stacked phone rows carry their own labels; controls in the clipped desktop header must not take focus. */
@media(max-width:599px){.ng-table--stack thead :is(input,button,select,a){visibility:hidden}}

/* Compact summaries keep collapsed worksheet rows at the kit's desktop density. */
.ng-table--summary .ng-text-action{min-height:var(--ng-h-xs);padding-block:0}
.ng-table--summary summary > span{display:inline}
@media(min-width:601px){
  .ng-table--summary{table-layout:fixed}
  .ng-table--summary td{white-space:normal;overflow-wrap:anywhere}
  .ng-table--summary summary{max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
}
/* Lookup tables without row actions use all phone width for identity and quantity. */
@media(max-width:600px){
  .ng-table--prio.ng-table--wraptext tbody:not(.ng-table__state):not(.ng-table__skel) tr{grid-template-columns:minmax(0,1fr) auto;grid-template-areas:"title num" "sub flag"}
  .ng-table--prio.ng-table--wraptext tbody tr td,
  .ng-table.ng-table--prio.ng-table--wraptext tbody tr td:first-child:not(.ng-table__num){white-space:normal;overflow-wrap:anywhere;overflow:visible}
  .ng-table--summary td > div{min-width:0;max-width:100%}
  .ng-table--summary summary{white-space:normal;overflow-wrap:anywhere}
}

/* Record lookup rows: identity, quantity and exceptions precede supplementary detail. */
@media(max-width:600px){
  .ng-table--stack.ng-table--record tbody tr{grid-template-columns:var(--ng-h-sm) minmax(0,1fr) auto;grid-template-areas:"select title num" "select sub sub" "select meta meta" "select flag flag" "select items items" "select checks checks";gap:var(--s-1) var(--s-2)}
  .ng-table--stack.ng-table--record td{grid-column:auto;min-width:0;overflow-wrap:anywhere;display:block}
  .ng-table--stack.ng-table--record td::before{display:none}
  .ng-table--stack.ng-table--record [data-slot="select"]{grid-area:select}
  .ng-table--stack.ng-table--record [data-slot="title"]{grid-area:title;font-weight:600}
  .ng-table--stack.ng-table--record [data-slot="num"]{grid-area:num}
  .ng-table--stack.ng-table--record [data-slot="num"]::after{content:" " attr(data-unit)}
  .ng-table--stack.ng-table--record [data-slot="sub"]{grid-area:sub}
  .ng-table--stack.ng-table--record [data-slot="meta"]{grid-area:meta}
  .ng-table--stack.ng-table--record [data-slot="flag"]{grid-area:flag}
  .ng-table--stack.ng-table--record [data-slot="items"]{grid-area:items}
  .ng-table--stack.ng-table--record [data-slot="checks"]{grid-area:checks}
  .ng-table--stack.ng-table--record [data-slot="location"]{display:none}
  .ng-table--record td:empty{display:none}
}

/* Secondary columns remain available in a labelled phone disclosure. */
.ng-table__phone-detail{display:none}
.ng-table--summary button.ng-text-action{display:block;max-width:100%;white-space:normal;overflow-wrap:anywhere}
.ng-table--summary summary.ng-text-action{display:flex;gap:var(--s-1)}
.ng-table__summary-text{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ng-table__summary-text > span{display:inline}
.ng-table__summary-count{flex:none}
@media(max-width:600px){.ng-table__summary-text{white-space:normal;overflow:visible}}
@media(max-width:600px){
  .ng-table--prio.ng-table--wraptext tbody:not(.ng-table__state):not(.ng-table__skel) tr{grid-template-areas:"title num" "sub flag" "extra extra"}
  .ng-table--prio.ng-table--wraptext tbody tr td.ng-table__phone-detail{display:block;grid-area:extra;white-space:normal}
  .ng-table--stack.ng-table--record tbody tr{grid-template-areas:"select title num" "select sub sub" "select meta meta" "select flag flag" "select items items" "select checks checks" "select extra extra"}
  .ng-table--stack.ng-table--record td.ng-table__phone-detail{display:block;grid-area:extra}
}
/* Compact record cards (critic 2026-10-07): a phone look-up row is a few short lines, not a
   stacked form. Identity + quantity, then who + status, then when + the row's one quiet action,
   then the items (wrapping, never clipped). Bulk select and the From column stay desktop-only. */
@media(max-width:600px){
  .ng-table--stack.ng-table--record.ng-table--compact tbody tr{grid-template-columns:minmax(0,1fr) auto;grid-template-areas:"title num" "sub flag" "meta actions" "items items" "checks checks";gap:var(--s-1) var(--s-3);align-items:center;padding:var(--s-3) var(--s-4)}
  .ng-table--stack.ng-table--record.ng-table--compact td:is([data-slot="select"],[data-slot="location"],.ng-table__phone-detail){display:none}
  .ng-table--stack.ng-table--record.ng-table--compact td[data-slot="flag"]{justify-self:end}
  .ng-table--stack.ng-table--record.ng-table--compact td[data-slot="actions"]{grid-area:actions;justify-self:end}
  .ng-table--stack.ng-table--record.ng-table--compact td[data-slot="actions"]:empty{display:none}
  .ng-table--stack.ng-table--record.ng-table--compact td{padding:0}
  .ng-table--stack.ng-table--record.ng-table--compact tbody tr{row-gap:var(--s-1)}
  /* the row action keeps a 44 px target but borrows the row gap, so it does not make the card taller */
  .ng-table--stack.ng-table--record.ng-table--compact td[data-slot="actions"] > :is(button,a){min-height:var(--ng-touch);margin-block:calc(-1 * var(--s-2))}
  .ng-table--stack.ng-table--record.ng-table--compact td:is([data-slot="meta"],[data-slot="sub"]){color:var(--ng-ink2);font-size:var(--t-dense)}
  .ng-table--stack.ng-table--record.ng-table--compact td[data-slot="sub"]{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
}
/* A heading or message the page moves focus to (tabindex=-1, for screen readers) is not a control:
   no ring on load or after a result lands (critic 2026-10-07, recall). Controls keep theirs. */
:is(h1,h2,h3,h4,p)[tabindex="-1"]:focus{outline:none}
/* Link tabs (a strip of pages, not panels) mark the current page the way tabs mark a selected panel. */
a.ng-tabs__tab{text-decoration:none}
.ng-tabs__tab[aria-current]:not([aria-current="false"]){color:var(--ng-ink1)}
.ng-tabs__tab[aria-current]:not([aria-current="false"])::after{background:var(--ng-ink1)}
/* KIT: a sheet under its own confirm is covered by it on a phone (opacity, not visibility, so focus can return to it) */
@media (max-width:599px){dialog.ng-sheet[data-ng-under]{opacity:0;pointer-events:none}}

/* Opt-in parts for long disclosures, short numeric fields and tall desktop forms. */
.ng-table--summary-wrap summary.ng-text-action{white-space:normal;overflow:visible;align-items:baseline}
.ng-table--summary-wrap .ng-table__summary-text{white-space:normal;overflow:visible;text-overflow:clip;overflow-wrap:anywhere}
input.ng-input.ng-input--short{width:6rem;max-width:100%}
.ng-row__body{display:block;font-size:var(--t-body);font-weight:400;line-height:1.5;overflow-wrap:anywhere}
.ng-body-copy{font-size:var(--t-body);font-weight:400;line-height:1.5;margin-block:var(--s-2)}
@media(min-width:768px){.ng-sheet--tall{max-height:calc(100vh - 2 * var(--s-4));max-height:calc(100dvh - 2 * var(--s-4))}}
/* Grid rows need one continuous selection ground across gaps and spanning cells. */
@media (hover:hover){@media (max-width:640px){.ng-table--gridrows tbody tr:hover{background:var(--ng-soft)}.ng-table--gridrows tbody tr:hover td{background:none}}}
@media (max-width:640px){
  .ng-table--gridrows tbody tr[aria-selected="true"]{background:var(--ng-soft-press)}
  .ng-table--gridrows tbody tr[aria-selected="true"] td{background:none}
}
/* ── CALM WORKSHEET (2026-10-07, Dist inventory "not easy on the eyes"). Measured on prod data: 27
   tinted pills, 46 boxed inputs and 3-line column heads in one 1440 viewport. These parts keep every
   figure and control and take the noise out. ── */
/* ng-tone: a figure that carries its band in its own ink, no tinted pill. The pill (ng-flag) stays for
   the one column that IS the status; a derived figure beside it wears the tone. Weight is the
   non-colour cue: the danger band is bolder than the warn band, and both bolder than plain ink. */
.ng-tone--danger{color:var(--ng-danger);font-weight:700}
.ng-tone--warn{color:var(--ng-warn);font-weight:600}
/* ng-input--cell: an editable figure inside a desktop worksheet row. At rest it reads as a number; hover and focus show the full
   field. A touch screen keeps the full kit field. */
@media (min-width:768px) and (pointer:fine){
  .ng-table .ng-input--cell{min-height:var(--ng-h-sm);height:var(--ng-h-sm);padding:0;border-color:transparent;border-radius:0;background:transparent}
  .ng-table .ng-input--cell:focus{border-radius:var(--r-std);background:var(--ng-card)}
  .ng-table .ng-input--cell:is(:disabled,[readonly]){border-bottom-color:transparent;background:transparent}
}
@media (hover:hover){@media (min-width:768px) and (pointer:fine){.ng-table .ng-input--cell:not(:disabled):hover{border-color:var(--ng-ink1);border-radius:var(--r-std);background:var(--ng-card)}}}
/* ng-outline-action--sm: a row action inside a dense worksheet row (32 px, 44 on touch) */
.ng-outline-action--sm{min-height:var(--ng-h-sm);padding:0 var(--s-3);font-size:var(--ng-t-control)}
@media (pointer:coarse){.ng-outline-action--sm{min-height:var(--ng-touch)}}
/* wrapped column heads break into balanced lines (two short lines, never "16- / day / sales") */
@media (min-width:641px){.ng-table--wrapheads thead .ng-table__sort{text-wrap:balance}}
/* ng-table__aside: the quiet second fact after a row's name ("12oz cans") on the SAME line, so every
   row keeps one height. In a narrow column it drops under the name as a unit; the name never wraps. */
.ng-table:not(.ng-table--stack) td:first-child:has(> .ng-table__aside){white-space:normal}
.ng-table:not(.ng-table--stack) td:first-child:has(> .ng-table__aside) > .ng-table__link{white-space:nowrap}
.ng-table__aside{display:inline-block;margin-left:var(--s-2);font-weight:400;color:var(--ng-ink3);white-space:nowrap}

/* Forecast consumers supply data-low once; cover elsewhere is neutral ink. */
.ng-table tr[data-low="red"] [data-band="cover"]{color:var(--ng-danger);font-weight:700}
.ng-table tr[aria-current] td:first-child{box-shadow:inset 3px 0 var(--ng-ink1)}
.ng-page--compact{padding-top:0}
@media(min-width:641px){
 .ng-table__phone-unit{display:none}
 .ng-desktop .ng-table .ng-input--cell{padding-inline:0}
 .ng-table--aligned.ng-table--v2.ng-table--dense th.ng-table__num .ng-table__sort{position:relative;gap:0;padding-inline:0}
 .ng-table--aligned.ng-table--v2.ng-table--dense th.ng-table__num[aria-sort] .ng-table__sort{padding-inline:var(--s-2)}
 .ng-table--aligned.ng-table--v2.ng-table--dense th.ng-table__num .ng-table__sort .ng-icon{position:absolute;left:0;top:50%;transform:translateY(-50%)}
 .ng-desktop .ng-table--aligned.ng-table--dense td.ng-table__num .ng-text-action{padding-inline:0;min-height:var(--ng-h-sm)}
}
/* Dialog copy keeps native line breaks; controls retain crew-size targets. */
.ng-confirm__what .ng-alert__body{white-space:pre-wrap;overflow-wrap:anywhere}
.ng-sheet :is(button,input:not([type="checkbox"]):not([type="radio"]),select,textarea){min-height:var(--ng-touch)}
/* Legacy theme files paint every focused field red with !important. Inside
   the kit sheet focus is ink; validation keeps its separate error voice. */
dialog.ng-sheet :is(input,select,textarea):focus:not([aria-invalid="true"]){
  outline:2px solid var(--ng-focus);outline-color:var(--ng-focus) !important;outline-offset:2px;
  border-color:var(--ng-ink1) !important;
}

.ng-sheet a[href]{display:inline-flex;align-items:center;min-height:var(--ng-touch);min-width:var(--ng-touch)}

/* Classic pages do not load the River palette primary rule. Keep its ink key
   and interaction states inside the kit dialog, without changing their page. */
:root:not([data-palette="river"]) .ng-sheet .ng-primary,.ng-sheet.ng-standalone .ng-primary{
  appearance:none;background:var(--ng-ink1);color:var(--ng-canvas);border:1px solid var(--ng-ink1);
  border-radius:var(--r-pill);box-shadow:var(--ng-shadow-lift);font-family:var(--ng-font-ui);font-weight:600;cursor:pointer;
}
@media (hover:hover){:root:not([data-palette="river"]) .ng-sheet .ng-primary:hover:not(:disabled),.ng-sheet.ng-standalone .ng-primary:hover:not(:disabled){background:var(--nc-primary-hover,var(--ng-ink2))}}
:root:not([data-palette="river"]) .ng-sheet .ng-primary:active:not(:disabled),.ng-sheet.ng-standalone .ng-primary:active:not(:disabled){background:var(--nc-primary-press,var(--ng-ink2))}
:root:not([data-palette="river"]) .ng-sheet .ng-primary:focus-visible,.ng-sheet.ng-standalone .ng-primary:focus-visible{outline:2px solid var(--ng-focus);outline-offset:3px}
:root:not([data-palette="river"]) .ng-sheet .ng-primary:disabled,.ng-sheet.ng-standalone .ng-primary:disabled{opacity:.5;cursor:default}
:root:not([data-palette="river"]) .ng-sheet .ng-primary[aria-busy="true"],.ng-sheet.ng-standalone .ng-primary[aria-busy="true"]{color:transparent;pointer-events:none}

/* Scanner guidance is opaque: camera pixels cannot change text contrast. */
.ng-scanner-status{padding:var(--s-3) var(--s-4);color:var(--ng-ink1);background:var(--ng-card);text-align:center;font-size:var(--t-body);min-height:var(--ng-touch)}
/* KIT: a sheet under its own confirm is covered by it on a phone (opacity, not visibility, so focus can return to it) */


/* Advisory review reuses the alert and focus parts at section hierarchy. */
[data-count-review] .ng-alert__title,[data-count-paste-review] .ng-alert__title{font-size:var(--t-section);font-weight:700;margin:0}
[data-count-review] summary.ng-text-action{border-radius:var(--r-std)}

[data-count-review-list] .ng-text-action{text-align:left;white-space:normal;height:auto}

/* Long confirmation evidence is an opt-in stacked kit row; numeric rows stay compact. */
.ng-row--wrap{flex-direction:column;align-items:stretch;gap:var(--s-1);min-width:0}
.ng-row--wrap .ng-row__value{white-space:normal;overflow-wrap:anywhere;text-align:left;font-weight:500}

/* Count review: discoverable jumps and compact crew lists. */
[data-count-review-list]{padding-inline-start:var(--s-6)}
[data-count-review-list] li{hyphens:none;overflow-wrap:normal;word-break:normal}
.ng-count-row-action{text-decoration:underline;text-underline-offset:3px}
/* A short trailing link stays on the warning's own last line instead of
   wrapping alone onto a tall 44 px row; the ::after hit-slop keeps the tap
   target real without growing the visible text (Cole 2026-10-07, phone 390).
   The slop is phone-only: at desktop widths rows sit close enough that a
   symmetric expansion reaches into the next link's text (R4 B1). */
[data-count-review-list] .ng-count-row-action{display:inline;position:relative;min-height:0;min-width:0;padding:0;white-space:nowrap;vertical-align:baseline}
[data-count-explain]{margin:var(--s-1) 0 0}
[data-count-explain] summary{cursor:pointer}
[data-count-explain] p{margin:var(--s-1) 0 0;color:var(--ng-ink2)}
[data-count-note]{margin:var(--s-1) 0 0}
[data-count-review-status]{display:block;margin-top:var(--s-1)}
/* All-clear is a quiet line, not a repeated card: no icon, no title, no tint. */
.ng-count-quiet{background:none;padding-block:var(--s-1)}
.ng-count-quiet > .ng-icon,.ng-count-quiet [data-count-review-title]{display:none}
.ng-count-source-error{margin-top:var(--s-3)}
.ng-count-show-all{display:none}
.ng-confirm__lines-title{margin:0;font-size:var(--t-section)}
.ng-row--wrap .ng-row__value{hyphens:none;overflow-wrap:normal;word-break:normal}
@media(max-width:600px){
  .ng-count-show-all{display:inline-flex}
  [data-count-review-list][data-count-collapsed] > [data-count-extra]{display:none}
  .ng-sheet.ng-confirm{background:var(--ng-canvas)}
  /* The defined list inset narrows nested rows: trailing hit-slop must not
     protrude beyond the link into the dialog's horizontal scroll area. */
  [data-count-review-list] .ng-count-row-action::after{content:'';position:absolute;inset:-14px 0 -14px -8px}
}

/* Wrapped numeric headings keep their label against the same inset as the figures. */
@media(min-width:641px){
 .ng-table--aligned.ng-table--wrapheads th.ng-table__num .ng-table__sort{margin-left:auto}
}

/* Strip search is a full crew target on phones, including River palette overrides. */
@media(max-width:640px){
 html[data-palette="river"] .ng-strip .ng-strip__search > input[type]{min-height:var(--ng-touch);height:var(--ng-touch);box-sizing:border-box}
}

/* Opt-in worksheet rows: two compact facts fit the same dense row as one. */
.ng-table--dense.ng-table--compact-lines td { line-height: 1.15; }

/* Opt-in phone lookup totals retain the same strip at the kit's dense type scale. */
@media (max-width: 640px) {
  .ng-stats--lookup { --ng-t-control: var(--t-dense); --ng-t-strong: var(--t-body); }
}

@media (min-width: 641px) {
  .ng-table--dense.ng-table--compact-lines td:first-child { padding-block: 0; }
}

/* The same opted-in numeric cell can live in a sheet's labelled row, outside its table. */
@media (min-width:768px) and (pointer:fine){
 dialog.ng-sheet .ng-input.ng-input--cell{min-height:var(--ng-h-sm);height:var(--ng-h-sm);padding:0;border-color:transparent;border-radius:0;background:transparent}
 .ng-sheet .ng-input--cell:focus{border-radius:var(--r-std);background:var(--ng-card)}
 .ng-sheet .ng-input--cell:is(:disabled,[readonly]){border-bottom-color:transparent;background:transparent}
}
@media (hover:hover){@media (min-width:768px) and (pointer:fine){.ng-sheet .ng-input--cell:not(:disabled):hover{border-color:var(--ng-ink1);border-radius:var(--r-std);background:var(--ng-card)}}}

/* Opt-in four-stat lookup: a stable label row and a separate number baseline. */
@media (max-width:640px){
  .ng-stats.ng-stats--compact-four{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--s-2)}
  .ng-stats--compact-four .ng-stats__item{display:grid;grid-template-rows:minmax(2.5em,auto) auto;align-items:end;gap:var(--s-1)}
  .ng-stats--compact-four dt{align-self:start}
}
/* Opt-in long-detail reading: summaries never cover headings or focused fields. */
.ng-reading-summary{position:static}
/* Opt-in history table: the key answer stays together on a phone. */
.ng-table--history td{white-space:normal;overflow-wrap:anywhere}
.ng-table--history details{max-width:100%}
@media (max-width:640px){
  .ng-table--history thead{display:none}
  .ng-table--history tbody{display:grid}
  .ng-table--history tr{display:grid;grid-template-columns:minmax(0,1fr) auto;padding:var(--s-3) 0;border-bottom:1px solid var(--ng-hairline)}
  .ng-table--history td{border:0;padding:var(--s-1) var(--s-2);min-width:0}
  .ng-table--history [data-history="date"]{grid-column:1;grid-row:1}
  .ng-table--history [data-history="quantity"]{grid-column:2;grid-row:1 / 3;align-self:center}
  .ng-table--history [data-history="movement"]{grid-column:1;grid-row:2}
  .ng-table--history [data-history="details"]{grid-column:1 / -1}
}

/* Opt-in editable numeric tables share the header and number right edge at every density. */
.ng-table--numeric-inputs td.ng-table__num .ng-input--cell{padding-inline-end:0;border-inline-end-width:0}
/* Opt-in sheet content: shrink grid tracks and wrap document actions without changing other sheets. */
.ng-sheet__reflow{grid-template-columns:minmax(0,1fr);min-width:0}
.ng-sheet__reflow > *, .ng-sheet__reflow .ng-field, .ng-sheet__reflow .ng-select-wrap{min-width:0;max-width:100%}
.ng-sheet__reflow > form{grid-template-columns:minmax(0,1fr)}
.ng-kv--reflow{display:grid;grid-template-columns:minmax(0,.7fr) minmax(0,1fr);gap:var(--s-2) var(--s-3)}
.ng-kv--reflow > :is(dt,dd){min-width:0;overflow-wrap:anywhere}
.ng-kv--reflow > dd{margin:0}
.ng-kv--reflow .ng-kv__link{max-width:100%;min-width:0;white-space:normal;overflow-wrap:anywhere}
/* Phone distributor identity wraps; numeric/status slots and desktop density retain their contracts. */
@media(max-width:600px){
  .ng-table--stack.ng-table--record.ng-table--compact.ng-table--distributor-wrap td.ng-table__distributor[data-slot="sub"]{white-space:normal;overflow:visible;text-overflow:clip;overflow-wrap:anywhere}
}

/* Opt-in readable KPI cards: long measured values stay beside their own labels. */
.ng-stats.ng-stats--readable{grid-template-columns:repeat(auto-fit,minmax(min(100%,170px),1fr))}
.ng-stats--readable :is(dt,dd){min-width:0;white-space:normal;overflow-wrap:anywhere}

/* Opt-in worksheet chrome and provenance. Other kit consumers retain their spacing. */
.ng-workspace--compact .ng-pagehead{padding-block:var(--s-2);margin-bottom:var(--s-2)}
.ng-workspace--compact .ng-pagehead .ng-eyebrow{margin-block:0}
.ng-workspace--compact .nc-control-strip{margin-block:var(--s-2)}
.ng-workspace--compact .nc-control-strip-row{padding-block:var(--s-1);gap:var(--s-2)}
.ng-workspace--compact .bs-ledger-head{padding-block:var(--s-2)}
.ng-workspace--compact .bs-ledger-head :is(h2,p){margin-block:var(--s-1)}
.ng-source-details{margin:var(--s-2) 0;padding:var(--s-2) var(--s-3);border:1px solid var(--ng-hairline);border-radius:var(--r-std)}
.ng-source-details>summary{cursor:pointer;font-size:var(--t-dense)}
.ng-source-details p,.ng-source-summary{font-size:var(--t-dense);line-height:1.4;overflow-wrap:anywhere}
.ng-workspace--compact .bs-hop-variety span{font-size:var(--t-dense);line-height:1.25}
.ng-workspace--compact .bs-grid-wrap{max-width:100%;overflow:auto}
.ng-workspace--compact .bs-grid-wrap:has(table)::before{content:'Scroll across for more columns · select a row or supply for detail';display:block;position:sticky;left:0;width:fit-content;max-width:100%;padding:var(--s-1) var(--s-2);font-size:var(--t-dense);color:var(--ng-ink2)}
.ng-workspace--compact .bs-transfers-note{margin:0}
@media(max-width:700px){
 .ng-workspace--compact .nc-control-strip-lead{display:flex;flex-wrap:wrap}
 .ng-workspace--compact .bs-control-field{flex:0 1 150px}
 .ng-workspace--compact .bs-view-nav{display:flex;flex-wrap:nowrap;overflow-x:auto;width:100%;order:2}
 .ng-workspace--compact .nc-stat-row{display:flex;flex-wrap:wrap;gap:var(--s-2);padding:var(--s-1) 0}
 .ng-workspace--compact .nc-stat{display:flex;align-items:baseline;gap:var(--s-1);padding:0;border:0;min-width:0}
 .ng-workspace--compact .nc-stat :is(.nc-summary-value,.nc-summary-name){font-size:var(--t-dense);margin:0}
 .ng-workspace--compact .bs-order-lifecycle{display:flex;flex-wrap:nowrap;overflow-x:auto}
 .ng-workspace--compact .bs-grid-wrap .bs-event-grid :is(.bs-col-date,.bs-canning-period){position:sticky;left:0;z-index:3;background:var(--nc-panel)}
 .ng-workspace--compact .bs-hop-variety{min-width:100px}
 .ng-workspace--compact .bs-yields-grid .bs-sku-head{min-width:115px;white-space:normal}
}

/* Opt-in crew envelope for standalone row actions and close controls, not inline links. */
.ng-crew-targets :is(.bs-event-popup-close,.river-dist-nav-link,.river-global-link,.mer-count-row button,.mer-btn,.po2-summary button,.po2-table button,.nc-overflow>summary){min-height:var(--ng-touch-crew);min-width:var(--ng-touch-crew)}
/* The compact opt-in takes precedence over older worksheet phone wrapping. */
body.ng-workspace--compact .ng-pagehead__text{flex:1 1 180px}
body.ng-workspace--compact .ng-pagehead{align-items:center;gap:var(--s-2);margin-block:0}
body.ng-workspace--compact .river-wrap{gap:var(--s-2)}
@media(max-width:700px){
 body.ng-workspace--compact.river-brewery-stock .bs-view-nav{flex:1 1 100%;flex-wrap:nowrap!important;overflow-x:auto;order:2}
 body.ng-workspace--compact.river-brewery-stock .nc-stat-row{width:auto;flex:1 1 auto;order:1;gap:var(--s-2)}
 body.ng-workspace--compact.river-brewery-stock .nc-stat-row .nc-stat{display:flex;align-items:baseline;gap:var(--s-1)}
 body.ng-workspace--compact.river-brewery-stock .nc-stat-row :is(.nc-summary-value,.nc-summary-name){font-size:var(--t-dense);line-height:1.25;letter-spacing:normal}
 body.ng-workspace--compact .bs-ledger-head>div:first-child p{-webkit-line-clamp:unset}
}
body.ng-workspace--compact .ng-source-details--compact{margin:0;padding-block:var(--s-1);border-width:0 0 1px;border-radius:0}
body.ng-workspace--compact .bs-filter-fold>.bs-filter-fold-summary{display:flex;align-items:center;gap:var(--s-2);min-height:32px;cursor:pointer}
body.ng-workspace--compact .bs-filter-fold:not([open]) .bs-filter-fields{display:none}
@media(min-width:701px){
 body.ng-workspace--compact.river-brewery-stock .nc-control-strip{display:flex;flex-wrap:wrap;padding:var(--s-1);gap:var(--s-1)}
 body.ng-workspace--compact.river-brewery-stock .nc-control-strip>.nc-control-strip-row{flex:1 1 auto}
 body.ng-workspace--compact .bs-ledger-head{padding-block:var(--s-1)}
 body.ng-workspace--compact .bs-grid-wrap:has(table)::before{display:none}
}

/* Brewery Stock re-score F17/F19/F29: explicit workspace/crew opt-ins.
   Numerical cell drills and unclassified inline links retain their existing envelopes. */
/* Compact worksheet opt-in, keep full labels and use real 44 px controls. */
:root body.ng-workspace--compact .river-wrap{gap:var(--s-1);padding-top:var(--s-1)}
:root body.ng-workspace--compact .ng-pagehead{padding-block:var(--s-1)}
:root body.ng-workspace--compact .rs-shell.rs-shell .river-dist-nav-domain{margin-bottom:0}
:root body.ng-workspace--compact .nc-control-strip{margin-block:0;padding:var(--s-1);gap:var(--s-1)}
:root body.ng-workspace--compact .nc-control-strip-row{padding-block:0}
:root body.ng-workspace--compact .bs-ledger-head{padding-block:var(--s-1)}
:root body.ng-crew-targets :is(button:not(.bs-cell-drill),select,input:not([type=checkbox]):not([type=radio]):not([type=hidden]),summary,.ng-text-action:not(.bs-cell-drill),.ng-outline-action:not(.bs-cell-drill),.ng-count-row-action,.nc-btn,.mer-btn,.river-dist-nav-link,.bs-event-popup-close,.bs-event-edit-link){min-height:var(--ng-touch-crew)!important;min-width:var(--ng-touch-crew)}
/* A 44 px action plus dense cell padding and border made 49px rows. Keep
   the action target and let its dense cell supply only the border at every width. */
:root body.ng-crew-targets .ng-table--dense td:has(> :is(a.ng-text-action,a.ng-outline-action)){padding-block:0}
@media(min-width:701px){
 :root body.ng-workspace--compact .ng-pagehead__text{display:flex;align-items:baseline;gap:var(--s-2)}
 :root body.ng-workspace--compact .ng-pagehead__text .ng-title{margin:0}
 :root body.ng-workspace--compact .nc-stat-row{display:flex;flex-wrap:wrap;gap:var(--s-2);padding:0}
 :root body.ng-workspace--compact .nc-stat{display:flex;align-items:baseline;gap:var(--s-1);padding:0;border:0;min-width:0}
 :root body.ng-workspace--compact .nc-stat :is(.nc-summary-value,.nc-summary-name){font-size:var(--t-dense);line-height:1.25;letter-spacing:normal;margin:0}
}
@media(max-width:700px){
 :root body.ng-workspace--compact .bs-grid-wrap:has(table)::before{content:'Scroll across for more columns';white-space:normal}
 :root body.ng-workspace--compact .bs-ledger-head>div:first-child{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 var(--s-2)}
 :root body.ng-workspace--compact .bs-ledger-head>div:first-child p{flex-basis:100%}
 :root body.ng-workspace--compact .bs-grid-wrap .bs-event-grid .bs-col-date{left:48px}
}
@media(min-width:1200px){
 :root body.ng-workspace--compact .river-wrap{grid-template-columns:auto minmax(0,1fr);column-gap:var(--s-6)}
 :root body.ng-workspace--compact .river-wrap>*{grid-column:1/-1}
 :root body.ng-workspace--compact .river-wrap>.ng-pagehead{grid-column:1;grid-row:1;flex-wrap:nowrap}
 :root body.ng-workspace--compact .river-wrap>.ng-pagehead .ng-pagehead__text{flex:0 1 auto}
 :root body.ng-workspace--compact .river-wrap>.rs-shell .river-dist-nav-domain{grid-column:2;grid-row:1;align-self:center;min-width:0}
 :root body.ng-workspace--compact .bs-ingredient-sheet:has(>.ng-source-details--compact:not([open])){display:grid;grid-template-columns:minmax(0,1fr) auto}
 :root body.ng-workspace--compact .bs-ingredient-sheet:has(>.ng-source-details--compact:not([open]))>*{grid-column:1/-1}
 :root body.ng-workspace--compact .bs-ingredient-sheet:has(>.ng-source-details--compact:not([open]))>.bs-ledger-head{grid-column:1;grid-row:1}
 :root body.ng-workspace--compact .bs-ingredient-sheet>.ng-source-details--compact:not([open]){grid-column:2;grid-row:1;max-width:400px}
}
:root body.ng-workspace--compact .bs-canning-workspace #bs-canning-content{display:flex;flex-direction:column}
:root body.ng-workspace--compact .bs-canning-workspace #bs-canning-content>.bs-grid-wrap{order:1}
:root body.ng-workspace--compact .bs-canning-workspace #bs-canning-content>.bs-identity-todo{order:2}
@media(min-width:1200px){
 /* Keep the lead's variable number of controls in their own wrapping row.
    Flattening them into four grid tracks squeezed the Add ingredient action
    to 44 px and let the packaged summary paint over its click target. */
 :root body.ng-workspace--compact .nc-control-strip{display:flex;flex-wrap:wrap;align-items:center}
 :root body.ng-workspace--compact .nc-control-strip-lead{display:flex}
 :root body.ng-workspace--compact .nc-control-strip .nc-stat-row{min-width:0}
 :root body.ng-workspace--compact .nc-control-strip .nc-stat{flex-wrap:wrap}
 :root body.ng-workspace--compact .nc-control-strip .bs-filter-row{min-width:0;justify-content:flex-end}
 :root body.ng-workspace--compact .ng-source-details--compact>summary{align-content:center}
}
@media(min-width:1200px){
 :root body.ng-workspace--compact.river-brewery-stock .nc-control-strip{display:flex;flex-wrap:wrap;align-items:center}
 :root body.ng-workspace--compact.river-brewery-stock .nc-control-strip-lead{display:flex}
}
@media(min-width:1200px){
 :root body.ng-workspace--compact .bs-ledger-head{padding-block:0}
 :root body.ng-workspace--compact .bs-ledger-head :is(h2,p){margin-block:0}
}

/* A grouped header's first material cell is not the frozen row identifier. */
:root body.ng-workspace--compact .bs-ingredient-grid:not(.bs-hops-grid) thead tr:not(:first-child)>th:first-child{left:auto}
:root body.ng-workspace--compact .bs-alert{padding-block:var(--s-0)}
@media(max-width:700px){
 :root body.ng-workspace--compact .nc-stat-row>.nc-stat{flex-direction:row;flex:0 1 auto;flex-wrap:wrap}
 :root body.ng-workspace--compact .bs-filter-row>.bs-filter-fold{flex:1 1 auto;min-width:0}
}
@media(min-width:1200px){
 :root body.ng-workspace--compact .river-wrap{padding-top:var(--s-0)}
 :root body.ng-workspace--compact .river-wrap>.ng-pagehead{padding-block:0}
 :root body.ng-workspace--compact .ng-source-details--compact{padding-block:0}
}

/* Production crew identity keeps the full name accessible in narrow headers. */
:is(body[data-ng-production-colors],body.brewer-guide-river) #noli-identity-pill{max-width:min(200px,50vw)!important;white-space:normal!important;overflow:visible!important;overflow-wrap:anywhere}


/* Opt-in editable worksheet: shared headings, compact desktop cells, stacked touch fields.
   Default sheets and tables retain their existing density. */
.ng-edit-table { min-width:0; }
.ng-edit-table__head,.ng-edit-table__cells { display:grid;grid-template-columns:minmax(0,1fr) 9rem 4rem 7rem;gap:var(--s-2);align-items:center; }
.ng-edit-table__head { grid-template-columns:minmax(0,1fr) 9rem 4rem 7rem 4.5rem;padding-right:0;position:sticky;top:0;background:var(--ng-canvas);z-index:1;font-size:var(--t-dense);font-weight:600; }
.ng-edit-table__row.dv-line { display:grid;grid-template-columns:minmax(0,1fr) 4.5rem;gap:var(--s-2);margin:0;border-bottom:1px solid var(--ng-hairline);align-items:center; }
.ng-edit-table__cells .ng-field,.ng-edit-table__cells .ng-select-wrap { min-width:0; }
.ng-edit-table__cells .ng-field__label { display:none; }
.ng-edit-table__cells .ng-edit-table__detail { grid-column:1 / -1; }
.ng-edit-table__cells .ng-edit-table__detail .ng-field__label { display:block; }
.ng-edit-table__actions { gap:var(--s-2);justify-content:end; }
.ng-edit-table__source { font-size:var(--t-dense); }
.ng-edit-table__notice { grid-column:1 / -1; }
@media(min-width:1100px) and (pointer:fine){
 dialog.ng-sheet.ng-sheet--worksheet { --ng-worksheet-rail:18rem;max-width:min(1320px,calc(100vw - var(--s-4) * 2));padding:var(--s-3); }
 dialog.ng-sheet.ng-sheet--worksheet > .ng-sheet__body { display:grid;grid-template-columns:minmax(0,1fr) var(--ng-worksheet-rail);column-gap:var(--s-4);row-gap:var(--s-2);margin-inline:0;padding-inline:2px; }
 .ng-sheet--worksheet > .ng-sheet__body > * { grid-column:2;min-width:0; }
 .ng-sheet--worksheet > .ng-sheet__body > section:has(.ng-edit-table) { grid-column:1;grid-row:1 / span 8; }
 .ng-sheet--worksheet > .ng-sheet__body > .ng-fields { grid-template-columns:minmax(0,1fr);gap:var(--s-2); }
 .ng-sheet--worksheet > .ng-sheet__body > .ng-fields > * { grid-column:1; }
 .ng-sheet--worksheet section:has(.ng-edit-table) > .ng-aside__title { display:none; }
 dialog.ng-sheet.ng-sheet--worksheet section:has(.ng-edit-table) > .ng-text-action { min-height:var(--ng-h-xs2);height:var(--ng-h-xs2);padding-block:0; }
 .ng-edit-table__head { min-height:var(--ng-h-xs); }
 dialog.ng-sheet.ng-sheet--worksheet .ng-edit-table :is(input,select,button) { min-height:var(--ng-h-xs2);height:var(--ng-h-xs2);padding-block:0;font-size:var(--t-dense); }
 dialog.ng-sheet .ng-edit-table .ng-icon-action { min-width:var(--ng-h-xs2);width:var(--ng-h-xs2);padding:0; }
 .ng-edit-table__actions .ng-text-action { min-width:var(--ng-h-xs2);width:var(--ng-h-xs2);padding:0; }
 .ng-edit-table__cells .ng-field { gap:0; }
 dialog.ng-sheet .ng-edit-table__cells .ng-input.ng-input--short { max-width:none;width:100%; }
 .ng-edit-table__row.dv-line { min-height:calc(var(--ng-h-xs2) + 1px); }
 dialog.ng-sheet--worksheet .ng-sheet__head { --ng-sheet-head-pad-x:var(--s-3);--ng-sheet-head-pad-y:var(--s-3);margin-bottom:var(--s-2); }
 .ng-sheet--worksheet .ng-sheet__foot { margin-top:var(--s-2); }
}
/* Ghost actions share one treatment; destruction is signalled only on intent. */
dialog.ng-sheet .ng-edit-table__ghost { border-color:transparent;border-radius:var(--r-std);color:var(--ng-ink2);background:transparent; }
dialog.ng-sheet .ng-edit-table__remove:focus { color:var(--ng-danger);background:var(--ng-danger-bg); }
@media (hover:hover){dialog.ng-sheet .ng-edit-table__remove:hover{color:var(--ng-danger);background:var(--ng-danger-bg)}}
dialog.ng-sheet.ng-sheet--worksheet :is(.ng-input,.ng-select,.ng-textarea):focus:not([aria-invalid="true"]) { box-shadow:none;outline-offset:0;border-color:transparent !important; }
dialog.ng-sheet--worksheet .ng-select:disabled { opacity:1;-webkit-text-fill-color:var(--ng-ink2); }
dialog.ng-sheet.ng-sheet--worksheet .ng-textarea { min-height:88px;height:auto;flex-shrink:0; }
@media(width < 1100px){
 .ng-edit-table__head { display:none; }
 .ng-edit-table__row.dv-line { position:relative;display:block;padding-block:var(--s-1); }
 .ng-edit-table__cells { grid-template-columns:minmax(0,1fr) 3.5rem minmax(0,1fr);gap:var(--s-1); }
 .ng-edit-table__cells > .ng-field:first-child { grid-column:1 / -1; }
 .ng-edit-table__cells > .ng-field:first-child .ng-select { padding-right:calc(3 * var(--ng-touch)); }
 .ng-edit-table__cells > .ng-field:first-child .ng-select-wrap::after { right:112px; }
 .ng-edit-table__cells .ng-field__label { display:none; }
 .ng-edit-table__cells .ng-edit-table__detail .ng-field__label { display:block; }
 .ng-edit-table__cells .ng-field { gap:0; }
 dialog.ng-sheet .ng-edit-table__cells .ng-input.ng-input--short { width:100%;max-width:none; }
 .ng-edit-table__actions { position:absolute;top:var(--s-1);right:0;flex-direction:row; }
 dialog.ng-sheet .ng-edit-table :is(input,select,button) { min-height:var(--ng-touch-crew);height:var(--ng-touch-crew);font-size:var(--t-dense); }
 dialog.ng-sheet .ng-edit-table .ng-icon-action { min-width:var(--ng-touch-crew);width:var(--ng-touch-crew);padding:0; }
 .ng-edit-table__source { min-height:var(--ng-touch-crew);display:flex;align-items:center; }
}
@media(pointer:coarse){
 dialog.ng-sheet .ng-edit-table :is(input,select,button) { min-height:var(--ng-touch);height:var(--ng-touch);font-size:var(--t-dense); }
 dialog.ng-sheet .ng-edit-table .ng-icon-action { min-width:var(--ng-touch);width:var(--ng-touch);padding:0; }
 .ng-edit-table__head { grid-template-columns:minmax(0,1fr) 9rem 4rem 7rem 6rem; }
 .ng-edit-table__row.dv-line { grid-template-columns:minmax(0,1fr) 6rem; }
}
@media(min-width:1100px) and (pointer:fine){
 dialog.ng-sheet--worksheet > .ng-sheet__body { align-content:start;grid-auto-rows:min-content; }
 dialog.ng-sheet--worksheet .ng-soft-card { padding:var(--s-2); }
 dialog.ng-sheet.ng-sheet--worksheet > .ng-sheet__body > .ng-fields :is(input,select) { min-height:var(--ng-touch);height:var(--ng-touch); }
 dialog.ng-sheet.ng-sheet--worksheet .ng-edit-table__cells { font-size:var(--t-dense); }
 dialog.ng-sheet--worksheet .ng-aside__title { margin-block:var(--s-1); }
 dialog.ng-sheet--worksheet .dv-merch-row .ng-fields { grid-template-columns:minmax(0,1fr) 4rem var(--ng-touch);gap:var(--s-2); }
 dialog.ng-sheet.ng-sheet--worksheet .dv-merch-row .ng-fields.ng-fields--3 > * { grid-column:auto; }
}
/* Independent worksheet columns keep a long/open beer row from stretching
   the rail's tracks and pushing Notes below the footer. DOM/tab order stays put. */
@media(min-width:1100px) and (pointer:fine){
 dialog.ng-sheet--worksheet > .ng-sheet__body:has(.ng-edit-table) { display:flow-root; }
 .ng-sheet--worksheet > .ng-sheet__body:has(.ng-edit-table) > * { margin-left:calc(100% - var(--ng-worksheet-rail));margin-bottom:var(--s-1); }
 .ng-sheet--worksheet > .ng-sheet__body:has(.ng-edit-table) > .ng-fields { float:right;width:var(--ng-worksheet-rail);margin-left:0; }
 .ng-sheet--worksheet > .ng-sheet__body > section:has(.ng-edit-table) { float:left;width:calc(100% - var(--ng-worksheet-rail) - var(--s-4));margin-left:0;margin-bottom:0; }
 .ng-sheet--worksheet > .ng-sheet__body > section:has(.dv-brewery) { clear:right; }
 dialog.ng-sheet--worksheet > .ng-sheet__body > section:not(:has(.ng-edit-table)) .ng-text-action { min-height:var(--ng-h-xs2);height:var(--ng-h-xs2);padding-block:0; }
}

/* Create worksheets retain the complete printed-name action; wrap it below the
   cells rather than forcing a sentence into the edit worksheet's icon column. */
.ng-edit-table__actions--labeled { grid-column:1 / -1;position:static;min-width:0;justify-content:space-between; }
dialog.ng-sheet.ng-sheet--worksheet .ng-edit-table__actions--labeled .ng-text-action { width:auto;min-width:0;max-width:100%;height:auto;white-space:normal;text-align:left; }
.ng-edit-table__actions--labeled .ng-text-action > span { min-width:0;overflow-wrap:anywhere; }
.ng-edit-table__actions--labeled .ng-text-action > svg { flex-shrink:0; }
/* Anchor the edit icon's accessible text inside its control's bounds. */
dialog.ng-sheet .ng-edit-table__ghost { position:relative; }
/* Inline padding also leaves room for subpixel glyph bounds in shrink-fit labels. */
dialog.ng-sheet.ng-sheet--worksheet .ng-edit-table__actions--labeled .ng-text-action { padding-inline:var(--s-1); }
@media(width < 1100px){
 .ng-edit-table__row:has(.ng-edit-table__actions--labeled) .ng-edit-table__cells { grid-template-columns:minmax(0,1fr) 4.5rem minmax(0,1fr); }
}


/* Opt-in direct row controls: one desktop line, equal crew touch tracks. */
.ng-row-actions.ng-row-actions { display:flex; flex-wrap:nowrap; align-items:center; justify-content:flex-end; gap:var(--s-2); }
.ng-row-actions > :is(button,a) { flex:none; white-space:nowrap; }
@media(max-width:700px){
  .ng-row-actions.ng-row-actions { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); width:100%; }
  .ng-table--stack td:has(> .ng-row-actions) { display:block; width:auto; min-width:0; }
  .ng-table--stack td:has(> .ng-row-actions)::before { display:none; }
  .ng-row-actions > :is(button,a) { width:100%; min-height:var(--ng-touch); min-width:var(--ng-touch); padding-inline:var(--s-2); justify-content:center; white-space:normal; text-wrap:balance; font-size:var(--ng-t-control); border:1px solid var(--ng-hairline-strong); border-radius:var(--r-pill); }
  .ng-row-actions > .ng-danger-action { border-color:var(--ng-danger); }
  :root body.ng-workspace--compact .bs-grid-wrap:has(.ng-table--stack)::before { display:none; }
  :root body.ng-workspace--compact .bs-view-nav { flex-wrap:wrap!important; overflow:visible; }
}

@media(min-width:1100px){.ng-stats--nowrap .ng-stats__item{flex:none;white-space:nowrap}}

.ng-crew-action { min-height:var(--ng-touch-crew); min-width:var(--ng-touch-crew); }
@media(max-width:600px){
 .ng-table--prio td[data-slot="select"] .ng-check { min-height:var(--ng-touch); min-width:var(--ng-touch); }
 .ng-filter.ng-filter--wrap { display:flex; flex-wrap:wrap; overflow:visible; }
 .ng-filter--wrap .ng-filter__field { flex:1 1 140px; min-width:0; }
 .ng-filter--wrap .ng-select-wrap { width:100%; min-width:0; }
 .ng-filter--wrap select { max-width:100%; width:100%; padding-left:var(--s-2); padding-right:var(--s-6); font-size:var(--ng-t-control); }
 .ng-filter--wrap .ng-select-wrap::after { right:var(--s-2); }
 .ng-filter--wrap .ng-filter__field[role="group"] { flex-basis:100%; flex-direction:row; align-items:center; }
 .ng-filter--wrap .ng-filter__field[role="group"] .ng-date { flex:1 1 0; width:100%; }
 .ng-filter--wrap .ng-date > .ng-input { width:100%; min-width:0; }
 .ng-filter--wrap .ng-date__words { padding-left:var(--s-2); }
}

/* Opt-in card guidance, balanced phone header actions and mixed row controls. */
.ng-source-summary--inset { margin:0; padding:var(--s-2) var(--s-4); font-family:var(--ng-font-ui); font-size:var(--t-dense); line-height:1.4; color:var(--ng-ink2); }
.ng-table__actions--center > :is(button,a) { vertical-align:middle; }
@media(max-width:600px){
 .ng-pagehead__actions--grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); width:100%; }
 .ng-pagehead__actions--grid > .ng-actions { display:contents; }
 .ng-pagehead__actions--grid :is(button,a) { width:100%; min-width:0; min-height:48px; height:100%; white-space:normal; text-wrap:balance; }
}

/* Daily crew actions: opt-in colour adapters keep legacy geometry/contracts.
   Ink actions/selected chips use a known pair in both themes; contact links
   remain underlined at rest, so colour is never the only affordance. */
.ng-action-ink,
.ng-daily-selected-ink:is(.active,.on){
  background:var(--ng-ink1)!important;
  color:var(--ng-canvas)!important;
  border-color:var(--ng-ink1)!important;
}
.ng-readable-rank{color:var(--ng-ink1)!important;opacity:1!important}
.ng-contact-link{
  color:var(--ng-link)!important;
  text-decoration:underline!important;
  text-underline-offset:3px;
}
.ng-action-ink:focus-visible,.ng-daily-selected-ink:focus-visible,.ng-contact-link:focus-visible{
  outline:2px solid var(--ng-focus);outline-offset:2px;
}

/* polish 10/12: inside a strip field the date box is never squeezed below its input; squeezed, the
   calendar button sat on the date text ("Wed, Oct 2") on phones. The input takes a set width that
   fits "Wed, Oct 21" (78px) beside the button, and the field grows to hold it (neighbours wrap). */
.ng-strip__field .ng-date{flex:none}
.ng-strip__field .ng-date>.ng-input{width:9rem;min-width:9rem}
.ng-strip__field:has(.ng-date){min-width:max-content}

/* Opt-in name/year pair and compact phone KPIs: four answers plus a quiet count. */
.ng-table__key-inline { display:inline-flex; align-items:center; gap:var(--s-1); white-space:nowrap; }
@media(max-width:600px){
 .ng-stats.ng-stats--compact-grid { grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--s-2); }
 .ng-stats--compact-grid dd { font-size:var(--ng-t-strong); }
 .ng-stats--compact-grid .ng-stats__item:nth-child(5):last-child { grid-column:1/-1; display:flex; flex-wrap:wrap; align-items:baseline; gap:var(--s-2); }
}

/* Opt-in stacked-card header action and two-line name; desktop columns stay intact. */
@media(max-width:599px){
 .ng-table--stack.ng-table--header-action .ng-table__key { grid-row:1; min-width:0; }
 .ng-table--stack.ng-table--header-action .ng-table__actions { grid-column:2; grid-row:1; order:-2; width:auto; justify-self:end; align-self:start; }
 .ng-table--stack.ng-table--header-action .ng-table__actions::before { display:none; }
 .ng-table--stack.ng-table--header-action .ng-table__status { grid-column:1/-1; justify-self:start; }
 .ng-table--header-action .ng-table__key-inline { width:100%; min-width:0; white-space:normal; }
 .ng-table--header-action .ng-table__key-inline > .ng-text-action { flex:1; min-width:0; white-space:normal; text-align:left; justify-content:flex-start; }
 .ng-table--header-action .ng-table__key-inline > .ng-flag { flex-shrink:0; }
 .ng-table--header-action .ng-table__title { display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; line-clamp:2; overflow:hidden; text-overflow:ellipsis; overflow-wrap:anywhere; }
}

/* Verbose basis notes: keep the existing desktop table and stacked phone recipe,
   but let second-line facts wrap instead of forcing a row wider than its page. */
.ng-table--notes .ng-table__aside { display:block; margin-left:0; white-space:normal; overflow-wrap:anywhere; }
.ng-table--notes td > div { min-width:0; }
.ng-table--notes td .ng-text-action { display:flex; }
@media (max-width:599px) {
  .ng-table--stack.ng-table--notes td[data-label] { display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1.5fr); }
}

/* One-line worksheet rows with verbose facts reachable in kit detail sheets. */
.ng-basis-strip:not([hidden]) { display:flex; align-items:center; gap:var(--s-2); min-width:0; }
.ng-basis-strip > [role="note"] { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ng-basis-strip > .ng-text-action { flex-shrink:0; }
@media (min-width:600px) {
  .ng-table--single-line td { height:var(--ng-h-sm); padding-block:0; }
  .ng-table--single-line td > div { display:flex; align-items:center; gap:var(--s-2); overflow:hidden; white-space:nowrap; }
  .ng-table--single-line .ng-table__aside { display:inline; white-space:nowrap; }
  .ng-table--single-line td, .ng-table--single-line td .ng-text-action { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .ng-table--single-line td .ng-text-action { display:block; min-width:0; max-width:100%; min-height:var(--ng-h-sm); }
  .ng-table--single-line .ng-table__detail-column { width:28%; }
  .ng-table--single-line .ng-table__clip { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0; }
  .ng-table--single-line td[data-label="Scheduled brew / action"] .ng-text-action { flex-shrink:0; }
}

/* Compact interactive chips fit dense worksheets; ordinary chips keep their touch target. */
.ng-chip.ng-chip--compact { min-height:var(--ng-h-xs); border:0; border-radius:var(--r-pill); padding:0 var(--s-1); gap:var(--s-1); font-size:var(--t-dense); color:var(--ng-ink2); }
.ng-chip--compact .ng-flag { background:none; border:0; padding:0; color:inherit; }
@media (max-width:599px) {
  .ng-chip.ng-chip--compact { min-height:var(--ng-touch); }
}

/* Coverage worksheet: reserve name space, compact figures, and readable verdicts.
   Explicit header widths are used because grid tracks do not size a native table. */
.ng-table--coverage .ng-table__phone-details { display:none; }
@media (min-width:600px) {
  .ng-table--coverage th.ng-table__beer-column { min-width:220px; }
  .ng-table--coverage th.ng-table__num { width:80px; }
  .ng-table--coverage th.ng-table__site-column { width:80px; }
  .ng-table--coverage th.ng-table__detail-column { width:160px; }
  .ng-table--coverage th.ng-table__action-column { width:120px; }
  .ng-table--coverage :is(th,td) { box-sizing:border-box; }
  .ng-table--coverage td.ng-table__key > div { display:flex; overflow:visible; white-space:normal; line-height:16px; overflow-wrap:anywhere; }
  .ng-table--coverage .ng-table__beer-name { display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; max-height:32px; overflow:hidden; min-width:0; flex:1; }
  .ng-table--coverage td.ng-table__key .ng-table__aside { white-space:normal; }
  .ng-table--coverage td.ng-table__key .ng-text-action { display:inline-flex; flex-shrink:0; min-height:0; white-space:normal; }
  .ng-table--coverage td[data-slot="verdict"], .ng-table--coverage td[data-slot="verdict"] .ng-text-action { overflow:visible; text-overflow:clip; }
}
@media (max-width:599px) {
  .ng-table.ng-table--stack.ng-table--coverage { table-layout:auto; width:100%; }
  .ng-table--stack.ng-table--coverage .ng-table__caption { display:block; width:100%; box-sizing:border-box; white-space:normal; }
  .ng-table--stack.ng-table--coverage tbody tr { grid-template-columns:repeat(3,minmax(0,1fr)); }
  .ng-table--stack.ng-table--coverage td { min-width:0; max-width:100%; overflow-wrap:anywhere; }
  .ng-table--stack.ng-table--coverage td.ng-table__key { grid-column:1 / -1; }
  .ng-table--stack.ng-table--coverage td[data-label] { display:block; text-align:left; }
  .ng-table--stack.ng-table--coverage td[data-label]::before { display:block; }
  .ng-table--stack.ng-table--coverage .ng-table__format-separator { display:none; }
  .ng-table--stack.ng-table--coverage td[data-slot="verdict"] { order:-1; grid-column:1 / -1; }
  .ng-table--stack.ng-table--coverage td[data-slot="verdict"]::before { display:none; }
  .ng-table--stack.ng-table--coverage td:is([data-slot="demand"],[data-slot="stock"],[data-slot="cover"]) { grid-column:auto; order:0; white-space:normal; }
  .ng-table--stack.ng-table--coverage td:not(.ng-table__key):not([data-slot]) { order:1; }
  .ng-table--stack.ng-table--coverage td .ng-text-action { flex-wrap:wrap; box-sizing:border-box; height:auto; max-width:100%; white-space:normal; text-align:left; }
  .ng-table--stack.ng-table--coverage td .ng-chip--compact { padding-block:var(--s-1); }
  .ng-table--stack.ng-table--coverage .ng-table__phone-details { display:block; flex-basis:100%; min-width:0; max-width:100%; overflow-wrap:anywhere; }
  .ng-table--stack.ng-table--coverage .ng-flag { min-width:0; max-width:100%; white-space:normal; }
}

/* Keep numeric-cell nowrap from overriding the opt-in header recipe. */
.ng-table.ng-table--wrapheads thead th{white-space:normal}

/* Grouped tables (several tbody): only the table's final row drops its rule, so a
   group boundary keeps its hairline. Added here so the base kit bytes stay intact. */
.ng-table tbody:not(:last-of-type) tr:last-child td{border-bottom:1px solid var(--ng-hairline)}
.ng-table tbody:last-of-type tr:last-child th{border-bottom:0}

/* Crew metadata uses the kit's 12px dense-text floor at every viewport. */
body.crew-core-river #tank-board :is(.eyebrow,.stage-label),
body.crew-close-river #tank-farm :is(.eyebrow,.kicker,.sync-line,.zone-card-label,.unitank-badge,.vessel-plate,.vessel-chip,.vessel-label,.vessel-seal),
body.brewer-guide-river :is(.topbar-section,h3.site-head),
body.crew-batch-river :is(.batch-heading .eyebrow,.card h2,.rcard h2) {
  font-size:var(--t-dense);
}

/* Legacy mobile_ops topbars on kit pages: opt in without changing identity
   labels or handlers. Inline legacy fallback colours must yield to the kit. */
.ng-body [data-ng-kit-topbar] :is(#noli-contrast-btn,#noli-identity-pill){
  min-width:var(--ng-touch);min-height:var(--ng-touch);padding:0 var(--s-2);
  color:var(--ng-ink2)!important;background:transparent!important;
  border:1px solid var(--ng-hairline-strong)!important;border-radius:var(--r-pill)!important;opacity:1!important;
}

/* Inline navigation with whole labels on multiple rows uses a card contour. */
:where(body.rep-glass) .ng-tabbar--wrap{flex-wrap:wrap;overflow:visible;border-radius:var(--r-std)}

/* Dashboard loading composition mirrors one hero and two supporting cards. */
:where(body.rep-glass) .ng-loading-stack{display:grid;gap:var(--s-3);width:100%}
:where(body.rep-glass) .ng-loading-stack > .ng-soft-card{display:grid;gap:var(--s-3);padding:var(--s-4)}
:where(body.rep-glass) .ng-loading-stack > :first-child .ng-skeleton--row{height:96px}

/* Transparent brand masters: white ink on dark cards, original dark ink on light. */
:root body.ng-body:where(.rep-glass) img.ng-brand-logo{content:url('/static/noli_script_dark.png')!important;mix-blend-mode:normal!important;filter:none!important}
:root[data-theme="dark"] body.ng-body:where(.rep-glass) img.ng-brand-logo{content:url('/static/noli_script.png')!important}
@media(prefers-color-scheme:dark){:root:not([data-theme="light"]) body.ng-body:where(.rep-glass) img.ng-brand-logo{content:url('/static/noli_script.png')!important}}

body.rep-glass{--ng-header-field-max:200px}

/* ═══ KIT GAPS G-01 / G-02 / G-03 (Fable audit 2026-10-14) ═══════════════════════════
   Three parts the audit found written locally seven, five and ten times. */

/* G-02 page width. --ng-page-max is the outer box (.ng-page); the content a page lays out
   inside it is --ng-page-content-max (1216 px at the desktop gutter). --ng-col-main and
   --ng-rail are the crew frame's main column and rail. */
:root{--ng-page-max:1280px;--ng-page-content-max:calc(var(--ng-page-max) - 2 * var(--s-8,32px));--ng-col-main:880px;--ng-rail:240px}

/* G-01 one scroll strip. The row (.ng-scroll__row) scrolls sideways with its scrollbar hidden;
   noli-kit.js seats it in a .ng-scroll frame, fades the edge where more waits (data-fade),
   adds a tappable 44 px cue at each end that scrolls the row, keeps the active item in view
   and clears the cue for a focused one. data-ng-scroll-whole="N" keeps N labels whole at rest
   (no fade, a gap, then the rest); --ng-scroll-slot (default the cue's 44 px) reserves room for the cue after the Nth,
   --ng-scroll-pad-end is end padding the row keeps, --ng-scroll-gap is the gap before item N+1, --ng-scroll-tail-gap the gap between the items after it.
   data-ng-scroll-max="700" moves the whole-label band from 600 px to 700 px;
   data-ng-scroll-cue="none" drops the cue buttons and keeps the labels whole. */
.ng-scroll{position:relative;min-width:0;isolation:isolate}
.ng-scroll__row{overflow-x:auto;overflow-y:hidden;flex-wrap:nowrap;min-width:0;max-width:100%;box-sizing:border-box;scrollbar-width:none;overscroll-behavior-x:contain}
.ng-scroll__row::-webkit-scrollbar{display:none}
:where(.ng-scroll__row){display:flex;align-items:center;gap:var(--s-2)}
:where(.ng-scroll__row > *){flex:0 0 auto;white-space:nowrap}
.ng-scroll > .ng-scroll__row{width:100%;margin-inline:0}
.ng-scroll__row[data-ng-scroll-cue="none"]{--ng-scroll-slot:0px}
.ng-scroll > .ng-scroll__more{display:none}
.ng-scroll__more{position:absolute;top:50%;transform:translateY(-50%);z-index:41;width:var(--ng-scroll-slot,var(--ng-touch));min-width:var(--ng-scroll-slot,var(--ng-touch));height:var(--ng-touch);padding:0;border:0;border-radius:var(--r-pill);background:var(--ng-card);color:var(--ng-ink1);cursor:pointer;place-items:center}
.ng-scroll__more::before{content:'';width:var(--s-2);height:var(--s-2);border:solid currentColor;border-width:2px 2px 0 0;transform:rotate(45deg)}
.ng-scroll__more--end{right:0}
.ng-scroll__more--start{left:0}
.ng-scroll__more--start::before{transform:rotate(-135deg)}
.ng-scroll:has(> .ng-scroll__row[data-fade~="end"]) > .ng-scroll__more--end,
.ng-scroll:has(> .ng-scroll__row[data-fade~="start"]) > .ng-scroll__more--start{display:inline-grid}
.ng-scroll > .ng-scroll__more:focus-visible{outline:2px solid var(--ng-focus,currentColor);outline-offset:-2px}
/* labels whole at rest: phone band 600 px, or 700 px with data-ng-scroll-max="700". :root x3 so the
   rule outranks the shell's own phone rules on .river-dist-nav-domain (a 0,5,0 mask and gap). */
@media(max-width:600px){
:root:root:root .ng-scroll__row[data-ng-scroll-whole]:not([data-ng-scroll-max]){gap:0;padding-inline-start:0;padding-inline-end:var(--ng-scroll-pad-end,0px);scroll-padding-inline:var(--ng-scroll-pad-end,0px);-webkit-mask-image:none;mask-image:none}
:root .ng-scroll__row[data-ng-scroll-whole="3"]:not([data-ng-scroll-max]) > :nth-child(-n+3){box-sizing:border-box;flex:0 0 calc((100% + var(--ng-scroll-pad-end,0px) - var(--ng-scroll-slot,var(--ng-touch))) / 3);min-width:max-content;margin-inline:0;padding-inline:2px}
:root .ng-scroll__row[data-ng-scroll-whole="3"]:not([data-ng-scroll-max]) > :nth-child(4){margin-inline-start:calc(var(--ng-scroll-slot,var(--ng-touch)) + var(--ng-scroll-gap,0px))}
:root .ng-scroll__row[data-ng-scroll-whole="3"]:not([data-ng-scroll-max]) > :nth-child(n+5){margin-inline-start:var(--ng-scroll-tail-gap,0px)}
:root .ng-scroll__row[data-ng-scroll-whole="4"]:not([data-ng-scroll-max]) > :nth-child(-n+4){box-sizing:border-box;flex:0 0 calc((100% + var(--ng-scroll-pad-end,0px) - var(--ng-scroll-slot,var(--ng-touch))) / 4);min-width:max-content;margin-inline:0;padding-inline:2px}
:root .ng-scroll__row[data-ng-scroll-whole="4"]:not([data-ng-scroll-max]) > :nth-child(5){margin-inline-start:calc(var(--ng-scroll-slot,var(--ng-touch)) + var(--ng-scroll-gap,0px))}
:root .ng-scroll__row[data-ng-scroll-whole="4"]:not([data-ng-scroll-max]) > :nth-child(n+6){margin-inline-start:var(--ng-scroll-tail-gap,0px)}
:root .ng-scroll__row[data-ng-scroll-whole="5"]:not([data-ng-scroll-max]) > :nth-child(-n+5){box-sizing:border-box;flex:0 0 calc((100% + var(--ng-scroll-pad-end,0px) - var(--ng-scroll-slot,var(--ng-touch))) / 5);min-width:max-content;margin-inline:0;padding-inline:2px}
:root .ng-scroll__row[data-ng-scroll-whole="5"]:not([data-ng-scroll-max]) > :nth-child(6){margin-inline-start:calc(var(--ng-scroll-slot,var(--ng-touch)) + var(--ng-scroll-gap,0px))}
:root .ng-scroll__row[data-ng-scroll-whole="5"]:not([data-ng-scroll-max]) > :nth-child(n+7){margin-inline-start:var(--ng-scroll-tail-gap,0px)}
:root .ng-scroll__row[data-ng-scroll-whole="6"]:not([data-ng-scroll-max]) > :nth-child(-n+6){box-sizing:border-box;flex:0 0 calc((100% + var(--ng-scroll-pad-end,0px) - var(--ng-scroll-slot,var(--ng-touch))) / 6);min-width:max-content;margin-inline:0;padding-inline:2px}
:root .ng-scroll__row[data-ng-scroll-whole="6"]:not([data-ng-scroll-max]) > :nth-child(7){margin-inline-start:calc(var(--ng-scroll-slot,var(--ng-touch)) + var(--ng-scroll-gap,0px))}
:root .ng-scroll__row[data-ng-scroll-whole="6"]:not([data-ng-scroll-max]) > :nth-child(n+8){margin-inline-start:var(--ng-scroll-tail-gap,0px)}
}
/* data-ng-scroll-max="700" is a named consumer band, not a scale step: the Canning nav pairs it with
   its own min-width:701px desktop rules, so the band stays 700 px. */
@media(max-width:700px){
:root:root:root .ng-scroll__row[data-ng-scroll-whole][data-ng-scroll-max="700"]{gap:0;padding-inline-start:0;padding-inline-end:var(--ng-scroll-pad-end,0px);scroll-padding-inline:var(--ng-scroll-pad-end,0px);-webkit-mask-image:none;mask-image:none}
:root .ng-scroll__row[data-ng-scroll-whole="3"][data-ng-scroll-max="700"] > :nth-child(-n+3){box-sizing:border-box;flex:0 0 calc((100% + var(--ng-scroll-pad-end,0px) - var(--ng-scroll-slot,var(--ng-touch))) / 3);min-width:max-content;margin-inline:0;padding-inline:2px}
:root .ng-scroll__row[data-ng-scroll-whole="3"][data-ng-scroll-max="700"] > :nth-child(4){margin-inline-start:calc(var(--ng-scroll-slot,var(--ng-touch)) + var(--ng-scroll-gap,0px))}
:root .ng-scroll__row[data-ng-scroll-whole="3"][data-ng-scroll-max="700"] > :nth-child(n+5){margin-inline-start:var(--ng-scroll-tail-gap,0px)}
:root .ng-scroll__row[data-ng-scroll-whole="4"][data-ng-scroll-max="700"] > :nth-child(-n+4){box-sizing:border-box;flex:0 0 calc((100% + var(--ng-scroll-pad-end,0px) - var(--ng-scroll-slot,var(--ng-touch))) / 4);min-width:max-content;margin-inline:0;padding-inline:2px}
:root .ng-scroll__row[data-ng-scroll-whole="4"][data-ng-scroll-max="700"] > :nth-child(5){margin-inline-start:calc(var(--ng-scroll-slot,var(--ng-touch)) + var(--ng-scroll-gap,0px))}
:root .ng-scroll__row[data-ng-scroll-whole="4"][data-ng-scroll-max="700"] > :nth-child(n+6){margin-inline-start:var(--ng-scroll-tail-gap,0px)}
:root .ng-scroll__row[data-ng-scroll-whole="5"][data-ng-scroll-max="700"] > :nth-child(-n+5){box-sizing:border-box;flex:0 0 calc((100% + var(--ng-scroll-pad-end,0px) - var(--ng-scroll-slot,var(--ng-touch))) / 5);min-width:max-content;margin-inline:0;padding-inline:2px}
:root .ng-scroll__row[data-ng-scroll-whole="5"][data-ng-scroll-max="700"] > :nth-child(6){margin-inline-start:calc(var(--ng-scroll-slot,var(--ng-touch)) + var(--ng-scroll-gap,0px))}
:root .ng-scroll__row[data-ng-scroll-whole="5"][data-ng-scroll-max="700"] > :nth-child(n+7){margin-inline-start:var(--ng-scroll-tail-gap,0px)}
:root .ng-scroll__row[data-ng-scroll-whole="6"][data-ng-scroll-max="700"] > :nth-child(-n+6){box-sizing:border-box;flex:0 0 calc((100% + var(--ng-scroll-pad-end,0px) - var(--ng-scroll-slot,var(--ng-touch))) / 6);min-width:max-content;margin-inline:0;padding-inline:2px}
:root .ng-scroll__row[data-ng-scroll-whole="6"][data-ng-scroll-max="700"] > :nth-child(7){margin-inline-start:calc(var(--ng-scroll-slot,var(--ng-touch)) + var(--ng-scroll-gap,0px))}
:root .ng-scroll__row[data-ng-scroll-whole="6"][data-ng-scroll-max="700"] > :nth-child(n+8){margin-inline-start:var(--ng-scroll-tail-gap,0px)}
}

/* G-03 one action row for a header, a bar and a table row. Right-aligned; on a phone it wraps
   into one full-width row of equal targets, primary last; a status or sync pill takes its own
   line. Touch targets are the kit's --ng-touch (44 px) whenever the pointer is coarse or the
   screen is a phone. The base .ng-actions (form footer) is unchanged. */
.ng-actions.ng-actions--head,.ng-actions.ng-actions--bar,.ng-actions.ng-actions--row{display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:var(--s-2)}
.ng-actions.ng-actions--row{flex-wrap:nowrap;gap:var(--s-1)}
.ng-actions.ng-actions--head > .ng-primary:where(:not(.mer-save-btn)),.ng-actions.ng-actions--bar > .ng-primary:where(:not(.mer-save-btn)),.ng-actions.ng-actions--row > .ng-primary:where(:not(.mer-save-btn)){width:auto}
.ng-actions > .ng-actions__status{flex:0 0 100%;order:-1;text-align:end;min-width:0}
@media(max-width:767px){
  .ng-actions.ng-actions--head,.ng-actions.ng-actions--bar,.ng-actions.ng-actions--row{flex-direction:row;flex-wrap:wrap;align-items:stretch;justify-content:stretch;width:100%}
  .ng-actions.ng-actions--head > :not(.ng-actions__status),.ng-actions.ng-actions--bar > :not(.ng-actions__status),.ng-actions.ng-actions--row > :not(.ng-actions__status){flex:1 1 0;min-width:var(--ng-touch);justify-content:center}
  .ng-actions.ng-actions--head > .ng-primary:where(:not(.mer-save-btn)),.ng-actions.ng-actions--bar > .ng-primary:where(:not(.mer-save-btn)),.ng-actions.ng-actions--row > .ng-primary:where(:not(.mer-save-btn)){width:auto;order:1}
}
@media(max-width:767px),(pointer:coarse){
  .ng-actions.ng-actions--head > :is(button,a,.ng-icon-action),.ng-actions.ng-actions--bar > :is(button,a,.ng-icon-action),.ng-actions.ng-actions--row > :is(button,a,.ng-icon-action){min-height:var(--ng-touch);min-width:var(--ng-touch)}
}

/* Batch C/D: geometric sizes are shared roles, separate from the density scale.
   Calendar gutters shrink with available width so seven crew targets fit at 320px. */
:root{--ng-strip-h-fine:34px;--ng-h-xs2:28px;--ng-grid-cell-min:72px;--ng-aside-top:72px;--ng-head-status-max:34vw;--ng-status-h:20px;--ng-select-arrow:5px;--ng-select-arrow-end:18px;--ng-select-arrow-inner:13px;--ng-spinner:18px;--ng-grab-w:36px;--ng-tab-min:56px;--ng-head-action-max:96px;--ng-shell-tabbar-h:74px;--ng-error-max:120px;--ng-stat-min:112px;--ng-datepop-desktop:288px;--ng-datepop-crew:346px;--ng-fade-w:36px}
@media(pointer:coarse){.ng-desktop{--ng-touch:var(--ng-touch-crew);--ng-touch-primary:48px}}
.ng-datepop:not(.ng-datepop--desktop) :is(.ng-datepop__dow,.ng-datepop__week){gap:var(--ng-date-gap)}
.ng-datepop:not(.ng-datepop--desktop){padding-inline:clamp(0px,calc((100vw - 334px) / 2),var(--s-3));--ng-date-gap:clamp(0px,calc((100vw - 322px) / 6),var(--s-0))}
@media(max-width:599px),(pointer:coarse){
  .ng-datepop{padding-inline:clamp(0px,calc((100vw - 334px) / 2),var(--s-3));--ng-date-gap:clamp(0px,calc((100vw - 322px) / 6),var(--s-0))}
  .ng-datepop__dow,.ng-datepop__week{--ng-date-gap:clamp(0px,calc((100vw - 322px) / 6),var(--s-0));gap:var(--ng-date-gap)}
}

/* Opt-in editor primitives: rounded evidence focus, quiet field chrome,
   strong outline actions and an inset footer scroll cue. Existing parts are unchanged. */
.ng-focus-region:focus{outline:2px solid color-mix(in srgb,var(--ng-focus) 70%,var(--ng-card));outline-offset:2px;border-radius:var(--r-std)}
@media (hover:hover){@media (pointer:fine){.ng-fields--quiet :is(.ng-input,.ng-select,.ng-textarea):hover:not(:focus):not([aria-invalid="true"]){border-color:var(--ng-control-edge)}}}
.ng-fields--quiet :is(.ng-input,.ng-select,.ng-textarea):focus:not([aria-invalid="true"]){box-shadow:none;border-color:transparent}
.ng-outline-action--strong{border-color:var(--ng-control-edge)}
.ng-sheet--scroll-cue > .ng-sheet__foot{position:relative}
.ng-sheet--scroll-cue > .ng-sheet__foot::before{content:"";position:absolute;inset-inline:0;top:-16px;height:16px;pointer-events:none;background:linear-gradient(transparent,var(--ng-card))}

/* The phone week controls occupy one touch row; scrolling/focus clears it. */
@media (max-width:600px) {
  html:has(#crewCalendar .cc-phone-week) {
    scroll-padding-top:calc(var(--ng-touch) + 2 * var(--s-2));
  }
  #crewCalendar .cc-phone-header { flex-wrap:nowrap; }
  #crewCalendar .cc-phone-header h3 { font-size:var(--t-dense); }
  #crewCalendar .cc-phone-header .ng-field { flex:0 0 80px; }
  #crewCalendar .cc-phone-header .ng-field__label {
    position:absolute; width:1px; height:1px; padding:0; overflow:hidden;
    clip-path:inset(50%); white-space:nowrap;
  }
  #crewCalendar .cc-phone-week :is(.cc-phone-day,.cc-phone-job,.cc-phone-day-head) {
    scroll-margin-top:var(--s-2);
  }
}

/* Kit modifiers (xfer direction): a disabled primary that keeps full-ink label, and
   a stacked segmented control whose rows are 56px full-width. */
.ng-primary.ng-primary--ink-disabled:disabled{opacity:1;background-color:var(--ng-soft);color:var(--ng-ink1);box-shadow:inset 0 0 0 1px var(--ng-hairline-strong)}
.ng-strip .ng-seg.ng-seg--stack .ng-seg__item{width:100%;justify-content:flex-start;min-height:56px;border-block-width:4px;padding:0 var(--s-4);font-size:var(--ng-t-strong,1rem)}

/* Daily sign-off uses the kit checkbox on a full-width crew row. Keep the
   visible box at 24 px and put its transparent tap ring inside the input. */
body.crew-close-river label.item.ng-check{display:flex;justify-content:flex-start;width:100%;min-height:48px}
body.crew-close-river label.item .ng-check__input{
  --ng-ring:10px;box-sizing:border-box;flex:0 0 calc(var(--ng-h-xs) + 2 * var(--ng-ring));
  width:calc(var(--ng-h-xs) + 2 * var(--ng-ring));height:calc(var(--ng-h-xs) + 2 * var(--ng-ring));
  margin:0;border:var(--ng-ring) solid transparent;border-radius:calc(var(--ng-ring) + var(--r-std) / 2);
  background-clip:padding-box;box-shadow:inset 0 0 0 1.5px var(--ng-control-edge)
}
body.crew-close-river label.item .ng-check__input:is(:checked,[aria-invalid="true"]){
  border-color:transparent;background-clip:padding-box;box-shadow:inset 0 0 0 1.5px var(--ng-ink1)
}

/* Crew shell Stage 1: shared job rail and Up next card. */
.ng-job-rail{background:var(--ng-card);border-right:1px solid var(--ng-hairline)}
html.apps-river body.crew-shell :is(.daily-grid,.tools-grid,.mini-grid){display:flex;flex-direction:column;gap:0;margin:0}
html.apps-river body.crew-shell :is(.tile-group-head,.section-head){font:700 var(--t-dense) var(--ng-font-ui);letter-spacing:.06em;color:var(--ng-ink2);padding:16px;margin:0}
html.apps-river body.crew-shell .ng-job-row{display:grid!important;grid-template-columns:32px minmax(0,1fr) auto;align-items:center;gap:4px 12px;min-height:64px;padding:12px 16px;border:0;border-top:1px solid var(--ng-hairline);border-radius:0;background:var(--ng-card);color:var(--ng-ink1);box-shadow:none;text-decoration:none}
html.apps-river body.crew-shell .ng-job-row .nicon{width:24px;height:24px;color:var(--ng-ink1);grid-column:1;grid-row:1 / 3}
html.apps-river body.crew-shell .ng-job-row .icon{grid-column:1;grid-row:1 / 3;background:none;width:auto;height:auto;color:var(--ng-ink1)}
html.apps-river body.crew-shell .ng-job-row :is(.body,.lbl,.sub){grid-column:2;min-width:0}
html.apps-river body.crew-shell .ng-job-row .lbl{font:700 var(--ng-t-strong) var(--ng-font-ui);line-height:1.3;color:var(--ng-ink1)}
html.apps-river body.crew-shell .ng-job-row .sub{font:400 var(--t-dense) var(--ng-font-ui);line-height:1.4;color:var(--ng-ink2);margin:0}
html.apps-river body.crew-shell .ng-job-row :is(.arrow,.pulse){display:none}
@media (hover:hover){html.apps-river body.crew-shell .ng-job-row:hover{background:var(--ng-soft)}}
html.apps-river body.crew-shell .ng-job-row[aria-current]{background:var(--ng-soft)}
.ng-job-row__state{grid-column:3;grid-row:1 / 3;display:flex;flex-direction:column;align-items:flex-end;gap:4px;font-size:var(--t-dense);color:var(--ng-ink2)}
.ng-job-row__draft{font-weight:700;color:var(--ng-warn)}
.ng-job-note{font-size:var(--ng-t-control);line-height:1.5;color:var(--ng-ink2);padding:16px}
.ng-up-next{background:var(--ng-hero);color:var(--ng-hero-ink);border-radius:var(--r-card);padding:24px;margin:16px 0}
.ng-up-next .ng-eyebrow,.ng-up-next p{color:var(--ng-hero-ink);font-size:var(--ng-t-control);line-height:1.5;text-transform:none;letter-spacing:0}
.ng-up-next h2{font-family:var(--ng-font-title);font-size:var(--ng-t-title);line-height:1.2;margin:8px 0;overflow-wrap:anywhere}
.ng-up-next .ng-outline-action{margin-top:16px;background:var(--ng-hero-ink);color:var(--ng-hero);border-color:var(--ng-hero-ink);min-height:var(--ng-touch)}
.ng-job-section{font-size:var(--t-dense);color:var(--ng-ink2);margin:24px 0 8px}
.ng-job-details{border:1px solid var(--ng-hairline);border-radius:var(--r-card);background:var(--ng-card);overflow:hidden}
.ng-job-details>div,.ng-job-summary{padding:12px 16px;border-bottom:1px solid var(--ng-hairline);font-size:var(--t-body);color:var(--ng-ink1)}
.ng-job-details dt{font-size:var(--t-dense);color:var(--ng-ink2)}
.ng-job-details dd{margin:4px 0 0;font-weight:600;overflow-wrap:anywhere}
.ng-job-summary{display:block;min-height:56px;text-decoration:none;background:var(--ng-card)}
.ng-job-summary small{display:block;font-size:var(--t-dense);color:var(--ng-ink2);margin-top:4px}
body.crew-shell .ng-outline-action{min-height:var(--ng-touch);font-size:var(--ng-t-control)}
body.crew-shell #topbar{background:var(--ng-card);color:var(--ng-ink1)}
body.crew-shell #topbar :is(#title,#back-btn,#noli-identity-pill,#noli-contrast-btn){color:var(--ng-ink1)!important;font-size:var(--t-dense);opacity:1!important;min-height:var(--ng-touch)}
body.crew-shell :is(.ng-job-row,.ng-outline-action):focus-visible{outline:2px solid var(--ng-focus);outline-offset:-3px}
body.crew-shell .owner-links{padding:16px;color:var(--ng-ink1)}
body.crew-shell .m-tier1-link{display:flex;align-items:center;min-height:var(--ng-touch);color:var(--ng-link);font-size:var(--ng-t-control)}

html.apps-river body.crew-shell{background:var(--ng-canvas);color:var(--ng-ink1)}
html.apps-river body.crew-shell .crew-shell__rail{display:block}
html.apps-river body.crew-shell .crew-shell__switch a{color:var(--ng-ink1);min-height:var(--ng-touch)}
html.apps-river body.crew-shell .crew-shell__switch a[aria-current]{background:var(--ng-soft);color:var(--ng-ink1);border-color:var(--ng-ink1);text-decoration:underline;text-underline-offset:4px}
html.apps-river body.crew-shell .ng-up-next h2{font-family:var(--ng-font-title);font-size:var(--ng-t-title);font-weight:400;text-transform:none;letter-spacing:0;color:var(--ng-hero-ink)}
html.apps-river body.crew-shell .ng-job-section{font-family:var(--ng-font-ui);font-size:var(--ng-t-control);text-transform:none;letter-spacing:0;color:var(--ng-ink2)}

html.apps-river body.crew-shell :is(main,#topbar){border-radius:0;box-shadow:none;background:var(--ng-canvas)}
html.apps-river body.crew-shell{padding:0;margin:0}
html.apps-river body.crew-shell #topbar :is(#noli-identity-pill,#noli-contrast-btn){min-width:var(--ng-touch)}


html.apps-river body.crew-shell .ng-job-row[style*="display:none"]{display:none!important}

/* Phone shell labels stay on one line; the title and identity give up width
   before any 44 px action does. Keep full accessible names on the controls. */
@media(max-width:699px){
 html.apps-river body.crew-shell #topbar{flex-wrap:nowrap;padding-inline:16px}
 html.apps-river body.crew-shell #topbar :is(#title,#back-btn,#noli-identity-pill,#noli-contrast-btn){white-space:nowrap!important;overflow:hidden!important;text-overflow:ellipsis!important;overflow-wrap:normal!important;min-width:var(--ng-touch)}
 html.apps-river body.crew-shell #topbar #title{min-width:0;flex:1 1 auto}
 html.apps-river body.crew-shell #topbar #noli-identity-pill{max-width:120px}
}

/* Crew shell round 2: compose the kit's status, stage and primary parts. */
html.apps-river body.crew-shell .ng-job-row[aria-current]{box-shadow:inset 4px 0 0 var(--ng-red)}
html.apps-river body.crew-shell .ng-job-row>.ng-event-dot{grid-column:1;grid-row:1 / 3;justify-self:center}
html.apps-river body.crew-shell .ng-job-due{grid-column:2;font-size:var(--t-dense);color:var(--ng-ink2)}
html.apps-river body.crew-shell .ng-job-summary{display:flex;align-items:center;gap:12px;min-height:64px;color:var(--ng-danger)!important}
.ng-job-summary__body{flex:1;min-width:0;font-weight:700}
.ng-job-summary__state{display:flex;flex-direction:column;align-items:flex-end;gap:4px;color:var(--ng-ink2);font-size:var(--t-dense)}
.ng-job-heading{display:flex;align-items:center;justify-content:space-between;gap:16px;font-size:var(--t-dense);color:var(--ng-ink2)}
html.apps-river body.crew-shell :is(.ng-job-section,.tile-group-head){display:flex;align-items:center;gap:12px;text-transform:uppercase;letter-spacing:.06em;font-size:var(--t-dense)}
html.apps-river body.crew-shell :is(.ng-job-section,.tile-group-head)::after{content:"";height:1px;background:var(--ng-hairline);flex:1}
html.apps-river body.crew-shell .ng-up-next .ng-eyebrow{text-transform:uppercase;letter-spacing:.06em}
html.apps-river body.crew-shell .ng-up-next .ng-primary{background:var(--ng-red);color:var(--ng-red-ink);min-height:var(--ng-touch);margin-top:16px}
html.apps-river body.crew-shell #topbar #title.topbar-section{font-size:var(--t-dense)!important;display:flex;align-items:center;align-self:center;line-height:1.4;margin:0;position:static;color:var(--ng-ink2)!important}

/* A date field inside a dialog reserves its one error line, so a bad-date message appearing or
   collapsing (on blur) never moves the actions below it. The kit adds that line lazily, so the
   space is held by a spacer until a message occupies it. */
dialog .ng-field:has(.ng-date) .ng-field__err{line-height:1.4}
dialog .ng-field:has(.ng-date):not(:has(.ng-field__err:not(:empty)))::after{content:"";display:block;min-height:calc(1.4 * var(--t-dense))}

/* Phone calendar jobs stack title and metadata, including River text links. */
@media (max-width:600px) {
  html[data-palette="river"] #crewCalendar .cc-phone-week .cc-phone-job {
    display:block; text-align:left;
  }
  html[data-palette="river"] #crewCalendar .cc-phone-week :is(.cc-phone-title,.cc-phone-meta) {
    display:block; text-align:left;
  }
}

/* Secondary header tools stay quiet without shrinking their touch targets. */
body.crew-shell #topbar :is(#title,#back-btn){font-size:var(--ng-t-control)!important}
body.crew-shell #topbar #noli-contrast-btn{border-color:transparent!important;background:transparent!important}

/* Native crew workspaces share the UI title face and glove-sized chips. */
.ng-title--crew{font-family:var(--ng-font-ui);text-transform:none}
[data-crew-targets] :is(.ng-chip,.ng-seg__item){min-height:var(--ng-touch-crew);min-width:var(--ng-touch-crew)}

/* Crew records are text, not editable boxes; locked controls use a dashed outline. */
:is(.crew-native-job,.crew-run-river) .crew-readonly{display:flex;flex-direction:column;gap:8px;min-width:0}
:is(.crew-native-job,.crew-run-river) .crew-readonly output{display:block;min-height:var(--ng-touch-crew);padding:var(--s-3) 0;border-bottom:1px solid var(--ng-hairline);white-space:normal;overflow-wrap:anywhere;color:var(--ng-ink1)}
:is(.crew-native-job,.wc-body,.count-photo-river) :is(input,select,textarea):is(:disabled,[readonly]){background:transparent!important;border-style:dashed!important;box-shadow:none!important;cursor:default;opacity:1}

/* Crew opt-in only: keep the checkbox's 24 px painted box inside its 44 px input at every
   density, including tablet/desktop legacy input minimums. */
:where(body[data-ng-crew-targets]:not(.ng-desktop):not(.ng-crew-targets):not(.ng-supplier-surface)) .ng-check__input[type="checkbox"]{
  --ng-check-hit:max(var(--ng-touch),var(--ng-touch-crew));
  --ng-ring:calc((var(--ng-check-hit) - var(--ng-h-xs)) / 2);
  box-sizing:border-box;width:var(--ng-check-hit);height:var(--ng-check-hit);min-width:var(--ng-check-hit);min-height:var(--ng-check-hit);margin:calc(var(--ng-ring) * -1);
  border:var(--ng-ring) solid transparent;border-radius:calc(var(--ng-ring) + var(--r-std));
  background-clip:padding-box;box-shadow:inset 0 0 0 1.5px var(--ng-control-edge)
}
:where(body[data-ng-crew-targets]:not(.ng-desktop):not(.ng-crew-targets):not(.ng-supplier-surface)) .ng-check__input[type="checkbox"]:is(:checked,:indeterminate,:disabled,[aria-invalid="true"]){border-color:transparent;background-clip:padding-box}
:where(body[data-ng-crew-targets]:not(.ng-desktop):not(.ng-crew-targets):not(.ng-supplier-surface)) .ng-check__input[type="checkbox"]:is(:checked,:indeterminate){box-shadow:inset 0 0 0 1.5px var(--ng-ink1)}
:where(body[data-ng-crew-targets]:not(.ng-desktop):not(.ng-crew-targets):not(.ng-supplier-surface)) .ng-check__input[type="checkbox"][aria-invalid="true"]{box-shadow:inset 0 0 0 1.5px var(--ng-danger)}
:where(body[data-ng-crew-targets]:not(.ng-desktop):not(.ng-crew-targets):not(.ng-supplier-surface)) .ng-check__input[type="checkbox"]:disabled{box-shadow:inset 0 0 0 1.5px var(--ng-hairline-strong)}
:where(body[data-ng-crew-targets]:not(.ng-desktop):not(.ng-crew-targets):not(.ng-supplier-surface)) .ng-check__input[type="checkbox"]:disabled:checked{box-shadow:inset 0 0 0 1.5px var(--ng-ink3)}

/* ══ KIT UPGRADE 2026-10-29 (feat/kit-upgrade-1029 — Cole: one dedicated kit upgrade so every crew
   screen gets these at once instead of per-screen patches). Inserted before the compatibility
   anchor so the base-kit byte pins (tests/kit_gap_accounting.py) keep proving nothing above moved.
   Item 2 (row precedence) lives in noli-a11y.css / noli-crew-targets.css; 3 (stage palette) in
   stage-colors.css; 5 (reserved vessel glyph) in noli-layout.css; 4 (mic dock) also in
   static/voice_intent.js + noli-kit.js. Tests: tests/test_kit_upgrade_1029.py. ══ */

/* (1) iPad selects. WebKit ignores height and min-height on a <select> that keeps the native
   menulist appearance, so a bare or .nc-select rendered 23–26 px tall on the iPad while the kit's
   .ng-select (appearance:none) was 44. Every single-choice select on a crew surface now drops the
   native appearance and takes the 44 px crew target, 16 px type (no iOS focus zoom) and the kit
   chevron. Desktop-density pages (.ng-desktop) and list boxes ([multiple], [size]) are untouched;
   .ng-select-wrap paints its own chevron and .ng-wells its own, so neither gets a second one. */
body:is(.ng-body,[data-ng-crew-targets],[data-crew-targets]):not(.ng-desktop) select:not([multiple]):not([size]):not(.ng-desktop *){
  -webkit-appearance:none;appearance:none;box-sizing:border-box;min-height:var(--ng-touch-crew);
  padding-top:0;padding-bottom:0;padding-inline-end:var(--s-8);line-height:1.25;font-size:var(--ng-t-input);
}
body:is(.ng-body,[data-ng-crew-targets],[data-crew-targets]):not(.ng-desktop) select:not([multiple]):not([size]):not(.ng-desktop *):not(.ng-select-wrap > *):not(.ng-wells *){
  background-image:linear-gradient(45deg,transparent 50%,var(--ng-ink2) 50%),linear-gradient(135deg,var(--ng-ink2) 50%,transparent 50%);
  background-position:calc(100% - var(--ng-select-arrow-end)) 50%,calc(100% - var(--ng-select-arrow-inner)) 50%;
  background-size:var(--ng-select-arrow) var(--ng-select-arrow),var(--ng-select-arrow) var(--ng-select-arrow);background-repeat:no-repeat;
}

/* (4) Voice mic dock. One kit slot: a page names its tools row [data-ng-mic-slot] and the shared
   mic (static/voice_intent.js) docks itself there in flow as an icon action, so the page never
   reserves the 52 px strip NG.stickyPad kept for a floating mic (the tap jumps). A page that docks
   into its kit .ng-head itself reads the same rule. Replaces the per-page
   `.vi-fab#vi-fab.docked{position:static!important…}` copies. (No hover media block here: the
   crew-week and KIT GAPS pins count one media query between their markers and the anchor.) */
:is([data-ng-mic-slot],.ng-head) .vi-fab#vi-fab.docked{position:static!important;inset:auto!important;margin:0!important;z-index:auto!important;flex:none}
:is([data-ng-mic-slot],.ng-head) .vi-fab#vi-fab.docked:not(.listening){width:var(--ng-touch)!important;height:var(--ng-touch)!important;padding:0!important;
  border:1px solid var(--ng-hairline-strong)!important;border-radius:var(--r-pill)!important;background:transparent!important;color:var(--ng-ink1)!important;box-shadow:none!important;font-size:var(--ng-t-control)}
:is([data-ng-mic-slot],.ng-head) .vi-fab#vi-fab.docked.listening{width:var(--ng-touch)!important;height:var(--ng-touch)!important;padding:0!important;border-radius:var(--r-pill)!important;
  background:var(--ng-ink1)!important;border-color:var(--ng-ink1)!important;color:var(--ng-canvas)!important;box-shadow:none!important;animation:none}
:is([data-ng-mic-slot],.ng-head) .vi-fab#vi-fab.docked:focus-visible{outline:2px solid var(--ng-focus)!important;outline-offset:2px}

/* (6) Type and inputs — consistency, not a new brand. Titles are .ng-title (Instrument Serif);
   numbers are .ng-num; a tank ID is .ng-id (tabular sans, never the serif). Every crew field —
   whatever class the page gave it — shows the one kit error state on aria-invalid, and a sheet's
   bare heading and its [data-ng-close] read as the kit's section title and icon action. Helper
   and error copy: .ng-field__hint / .ng-field__err (already the kit's). */
.ng-id{font-family:var(--ng-font-ui);font-weight:600;font-variant-numeric:tabular-nums;letter-spacing:.01em;white-space:nowrap}
body:is(.ng-body,[data-ng-crew-targets],[data-crew-targets]) :is(input,select,textarea)[aria-invalid="true"]:not(.ng-desktop *){border-color:var(--ng-danger)!important;box-shadow:inset 0 0 0 1px var(--ng-danger)!important}
.ng-sheet__head > :is(h1,h2,h3):not([class]){margin:0;font-size:var(--ng-t-section);font-weight:600;line-height:1.25;color:var(--ng-ink1)}
.ng-sheet__head > [data-ng-close]{flex:none;display:inline-grid;place-items:center;width:var(--ng-touch);height:var(--ng-touch);padding:0;
  border:1px solid var(--ng-hairline-strong);border-radius:var(--r-pill);background:transparent;color:var(--ng-ink2)}
.ng-sheet__head > [data-ng-close]:focus-visible{outline:2px solid var(--ng-focus);outline-offset:2px}
/* ══ end KIT UPGRADE 2026-10-29 ══ */

/* ══ KIT LEFTOVERS 2026-10-29 (fix/kit-leftovers-1029 — Cole approved the kit upgrade and said "fix
   leftovers next"). Before the compatibility anchor, like the upgrade above, so the base-kit byte
   pins hold. Tests: tests/test_kit_leftovers_1029.py. ══ */

/* (1) A busy button: the spinner replaces the label, whatever colour a variant gives its label. The
   busy rule's color:transparent lost to any variant that sets its label colour more specifically
   (the solid destructive's button.ng-danger-action.ng-danger-action--solid:not(:disabled)), so
   "Delete 3" printed under the spinner. The glyph fill and any icon inside are hidden instead, which
   no colour rule reaches; the spinner (::after) keeps the variant's --ng-spin ink. */
:is(.ng-primary:where(:not(.mer-save-btn)),.ng-outline-action,.ng-danger-action)[aria-busy="true"]{-webkit-text-fill-color:transparent}
:is(.ng-primary:where(:not(.mer-save-btn)),.ng-outline-action,.ng-danger-action)[aria-busy="true"] > *{visibility:hidden}

/* (2) A table that scrolls sideways fades its rows into the table's own ground on the side where
   more waits — a real overlay over the cells, the card colour at the edge to clear in 36 px, in
   either theme. It replaces KIT ROUND 16's grey hairline shadow, which sat UNDER the cells and read
   as a hard band. The fade rides the visible edge with position:sticky, so it moves with the scroll
   on the compositor (no script-driven lag): the wrap becomes a one-cell grid exactly as wide as the
   table (min-content, else the wrap's width; a percentage minimum resolves to the wrap's width in
   both engines and the fade would scroll away), the table and both fades share that cell, and noli-kit.js says which edge has more
   (data-fade="start end", as on every kit strip). A wrap holding more than its one table keeps the
   round-16 cue. --ng-fade-bg names another ground when the wrap sits on one. The grid itself is
   zero-specificity and skips [hidden], so a page's own visibility rule still wins: the credit
   memos' phone .cm-desktop-list{display:none} and the crew check's hidden #cwCheckWrap stay hidden. */
:where(.ng-table-wrap:not([hidden]):has(> table:only-child)){display:grid;grid-template-columns:minmax(min-content,1fr)}
.ng-table-wrap:has(> table:only-child){background-image:none}
.ng-table-wrap:has(> table:only-child) > table{grid-area:1 / 1}
.ng-table-wrap:has(> table:only-child)::before,.ng-table-wrap:has(> table:only-child)::after{
  content:"";grid-area:1 / 1;align-self:stretch;position:sticky;z-index:3;width:var(--ng-fade-w);pointer-events:none;
  opacity:0;transition:opacity var(--ng-fast) var(--ng-ease)}
.ng-table-wrap:has(> table:only-child)::before{justify-self:start;left:0;background:linear-gradient(to right,var(--ng-fade-bg,var(--ng-card)),transparent)}
.ng-table-wrap:has(> table:only-child)::after{justify-self:end;right:0;background:linear-gradient(to left,var(--ng-fade-bg,var(--ng-card)),transparent)}
.ng-table-wrap:has(> table:only-child)[data-fade~="start"]::before,.ng-table-wrap:has(> table:only-child)[data-fade~="end"]::after{opacity:1}

/* (3) The River primary on a River page that does not load noli-ui.css (the tank board and farm):
   the ink key, from the --nc-primary* tokens the page defines (as .ng-standalone does). Zero
   specificity, so noli-ui.css's own rule set, and any page that paints its primary, still win. */
:where(html[data-palette="river"] .ng-primary:not(.mer-save-btn)){background-color:var(--nc-primary,var(--ng-ink1));color:var(--nc-primary-ink,var(--ng-canvas));
  border:1px solid var(--nc-primary,var(--ng-ink1));border-radius:var(--r-pill);box-shadow:var(--nc-primary-shadow,var(--ng-shadow-lift));font-family:var(--ng-font-ui);font-weight:600;cursor:pointer}
:where(html[data-palette="river"] .ng-primary:not(.mer-save-btn):active:not(:disabled)){background-color:var(--nc-primary-press,var(--ng-ink2));box-shadow:inset 0 1px 3px var(--nc-primary-shade,var(--ng-hairline-strong))}
:where(html[data-palette="river"] .ng-primary:not(.mer-save-btn):focus-visible){outline:2px solid var(--ng-focus);outline-offset:2px}
:where(html[data-palette="river"] .ng-primary:not(.mer-save-btn):disabled){opacity:.5;cursor:default}
/* ══ end KIT LEFTOVERS 2026-10-29 ══ */

/* ══ CREW CONTRAST 2026-10-29 (fix/crew-contrast-owner-shift-1029, rescore 1029a S11). Before the
   compatibility anchor so the base-kit byte pins hold. Tests: tests/test_crew_contrast_owner_shift_1029.py.
   A seg pill that becomes current swaps light text onto a dark fill (dark: dark text onto a light
   fill). Cross-fading both, the pair met mid-way at 2.36:1 (Today's sticky seg in dark, while the
   scroll-spy moved the current pill). Fill and text now swap together at once; only the press
   scale animates. ══ */
.ng-seg__item{transition:transform var(--ng-fast) var(--ng-ease)}

/* G-01 scroll strip, 2026-10-30: an overflowing row with cues (data-ng-scroll-over, set by noli-kit.js) keeps both buttons
   beside its scroller, never over an item; the one at an end already reached is aria-disabled. A data-ng-scroll-whole
   row does the same: its whole labels fill the scroller itself, so the end slot their basis takes off is given back
   (the label after them keeps its slot-wide gap, now just past the scroller's edge at rest). */
.ng-scroll > .ng-scroll__row[data-ng-scroll-over]:not([data-ng-scroll-cue="none"]){width:auto;margin-inline:var(--ng-scroll-slot,var(--ng-touch));--ng-scroll-row-pad-end:var(--ng-scroll-pad-end,0px)}
.ng-scroll > .ng-scroll__row[data-ng-scroll-over][data-ng-scroll-whole]:not([data-ng-scroll-cue="none"]) > *{--ng-scroll-pad-end:calc(var(--ng-scroll-row-pad-end) + var(--ng-scroll-slot,var(--ng-touch)))}
.ng-scroll:has(> .ng-scroll__row[data-ng-scroll-over]:not([data-ng-scroll-cue="none"])) > .ng-scroll__more{display:inline-grid}
.ng-scroll > .ng-scroll__more[aria-disabled="true"]{background:transparent;color:var(--ng-ink3,var(--ng-ink2));cursor:default}

/* ── KIT FIELD GROUPS 2026-10-30 (Astra re-score: the shared fix on 28 crew rows — month-end count
   tabs, the brew sheet's front and back, cellar readings and held tank, Add duty, Taste). Every
   field keeps its visible label (.ng-field__label) and its unit inside the box (.ng-unit__u); on a
   phone two SHORT fields share a row and a long field runs the full width. A short field is a
   reading — a number with its unit (.ng-unit), a day, a clock time, a decimal or numeric box — so
   temp + gravity, start + stop, Brew 1 + Brew 2 sit side by side, each still a 44px control with
   its own label. Words, a select, a search and a textarea stay full width. A page forces either:
   .ng-field--short pairs a typed time or code; .ng-field--long keeps a reading with a long label
   on its own phone row (it still shares an iPad row); .ng-fields__full spans every width. Rows
   sit 12px apart, columns 16px, on every screen (the worksheet sheet keeps its own 8px rows).
   Appended before the compatibility anchor so the base kit bytes stay pinned
   (tests/kit_gap_accounting.py); the phone pairing is a media query, which this region may not
   carry (the crew-week and KIT GAPS pins), so it lives in static/noli-responsive.css. ── */
.ng-field__label{line-height:1.3;overflow-wrap:anywhere}
.ng-fields{gap:var(--s-3) var(--s-4)}

/* ══ KIT SWEEP 2026-10-31 (Astra 10/30 re-score, the cross-screen pass; kit parts only). The dated fact under an
   empty or failed state (NG.state {meta}: "Checked 8:47 AM · Spokane"): quiet, tabular, one line, so "nothing
   waiting" reads as an answer with a time and a place. Inserted before the compatibility anchor, no media query
   (tests/kit_gap_accounting.py). ══ */
.ng-state__meta{margin:0;font-size:var(--t-dense);color:var(--ng-ink3);font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
.ng-state--line .ng-state__meta{margin-left:auto;white-space:nowrap}
/* ══ end KIT SWEEP 2026-10-31 ══ */

/* 2026-10-31 brew sheet round 5 — ng-tick--locked: a done task on a screen with no undo. It cannot
   be pressed, but who/when stays readable at full ink (the kit's .5 disabled fade is for a task that
   is not available yet). The mark and fill say done, so no strike runs through who/when (a flex
   item takes its parent's strike). Opt-in. */
.ng-tick--locked[aria-pressed="true"]:is(:disabled,[aria-disabled="true"]){opacity:1;text-decoration:none;color:var(--ng-ink1)}

/* Full-height review: an opaque working panel with fixed head and actions.
   Opt in alongside --scroll; the body retains the kit's scroll/focus behavior. */
dialog.ng-sheet.ng-sheet--full{
  inset:0;margin:0 auto;bottom:auto;width:100%;max-width:860px;
  height:var(--ng-sheet-viewport,100dvh);max-height:var(--ng-sheet-viewport,100dvh);
  border-radius:0;background:var(--ng-canvas);
}
dialog.ng-sheet.ng-sheet--full > :is(.ng-sheet__head,.ng-sheet__body,.ng-sheet__foot){background:var(--ng-canvas)}

/* Opt-in for crew-authored evidence: retain line breaks and exact spacing. */
.ng-row--verbatim .ng-row__value{white-space:pre-wrap;overflow-wrap:anywhere}

/* A page can feed the chip count its opacity. Before the compatibility anchor so the base kit
   bytes stay pinned. */
.ng-chip__count{opacity:var(--ng-chip-count-opacity,.72)}

/* ══ CREW HEADER 2026-10-29 (feat/crew-header-kit-1029, rounds 2–4; Astra round 4: 9+ on all bars, FUNCTION
   pass). Shipped for crew Today and the kit gallery only this round, so every rule reaches a page only
   through the two partials (partials/_crew_header.html, partials/_crew_page_links.html) or the kit's own
   PIN sheet. Last block before the compatibility anchor: the base-kit byte pins (tests/kit_gap_accounting.py)
   take insertions only here, and no media query may sit in this region (the crew-week and KIT GAPS pins count
   one between their markers and the anchor), so the header's phone grammar lives in
   static/noli-responsive.css. Tests: tests/test_crew_header_partial_1029.py. ══ */

/* (1) The page links (_crew_page_links.html): a crew page's screens are quiet text actions on one scroll
   strip; the current screen is ink with an underline marker — a word and a mark, never colour alone, never
   a filled pill. Scoped to the strip's nav so no other page's quiet link changes; outranks the hover offset. */
nav.ng-scroll__row > .ng-text-action.ng-text-action--quiet[aria-current="page"]{color:var(--ng-ink1);text-decoration:underline;text-decoration-thickness:2px;text-underline-offset:6px}

/* (2) The sign-in sheet's name field (Cole 2026-10-29: name typed, then the PIN), wherever the kit's PIN
   sheet opens: the pad's full width, read left to right. */
.ng-pin__name{width:100%;justify-self:stretch;text-align:left}

/* (3) The skip link the header partial renders (skip_href): focused, it is fixed to the viewport at the
   top-most z-index, so it shows above any sticky strip in every environment — the staging banner
   (erp_env.py) is sticky at z-index 2147483647 and sits earlier in the body; at an equal z-index the later
   box paints on top (round 2). Scoped to the skip link the crew header follows; other pages' skip links
   keep the base rule above until their screens take the header. */
.ng-skip:has(+ .ng-head--crew):focus{position:fixed;top:var(--s-3);z-index:2147483647}
/* ══ end CREW HEADER 2026-10-29 ══ */


/* Mobile production compatibility colours. Geometry, fields, state and status
   semantics stay with the consumer; theme-aware ink comes from the shared kit.
   Important is required for the legacy inline colours and noli-mobile rules. */
body[data-ng-production-colors] :is(#noli-identity-pill,#noli-contrast-btn){color:var(--ng-ink2)!important;opacity:1!important}
body[data-ng-production-colors="contacts"] #contacts a[href^="mailto:"]{color:var(--ng-link)!important}
body[data-ng-production-colors="contacts"] #contacts a[href^="tel:"]{background:var(--ng-ink1)!important;color:var(--ng-canvas)!important}
body[data-ng-production-colors="brew-sheet"] #sheet .yield-block .yl-plan{color:var(--ng-ink2);opacity:1}
body[data-ng-production-colors="brew-sheet"] #sheet .yield-block .yl-sheet{color:var(--ng-warn);border-color:var(--ng-warn)}
body[data-ng-production-colors="brew-sheet"] #sheet .yield-block .yl-actual{color:var(--ng-ok);border-color:var(--ng-ok)}
body[data-ng-production-colors="loadout"] :is(.stage-btn,.confirm-cta,#confirm-ok),
body[data-ng-production-colors="signoff"] .primary,
body[data-ng-production-colors="sensory"] #btn-submit,
body[data-ng-production-colors="photo"] #submit-btn:not(:disabled),
body[data-ng-production-colors="parts"] .btn:is(.checkout,.checkin),
body[data-ng-production-colors="maintenance"] :is(.btn:not(.ghost),[id="fab"]){background:var(--ng-ink1)!important;color:var(--ng-canvas)!important;border-color:var(--ng-ink1)!important;box-shadow:var(--ng-shadow-lift)!important}
body[data-ng-production-colors="loadout"] :is(.route-move,a.back[href^="/shipments"]){color:var(--ng-ink1)!important;border-color:var(--ng-ink1)!important}

/* Sensory intensity semantics share the same kit pair on phones and tablets. */
body[data-ng-production-colors="sensory"] #dim-panel .chip.on.i1{background:var(--ng-ok-bg);color:var(--ng-ok);border-color:var(--ng-ok)}
body[data-ng-production-colors="sensory"] #dim-panel .chip.on.i2{background:var(--ng-warn-bg);color:var(--ng-warn);border-color:var(--ng-warn)}
body[data-ng-production-colors="sensory"] #dim-panel .chip.on.i3{background:var(--ng-danger-bg);color:var(--ng-danger);border-color:var(--ng-danger)}
