/* Redesign rollout -- shared foundation for the "new look" dark/card
   visual system: design tokens (dark default + light override) and every
   generic, portable primitive (cards, panels, buttons, pills, modals,
   tables, chips, KPI tiles, forms). Anything genuinely specific to one
   feature area (PC Monitoring's charts/disk-SMART cards/remediation-
   script list, etc.) stays in that area's own stylesheet, which
   @imports this file rather than redefining any of it.

   Every rule is scoped under .nl-scope and every class is prefixed nl-
   so this can never collide with, or be affected by, the real
   static/css/style.css (6000+ lines, its own .panel/.btn/.pill/etc.)
   loaded on every page including this one. The one exception is the
   shared `pcm.script_run_status_pill` macro's own `.pill`/`.pill-*`
   output (see _pc_monitoring_macros.html) -- deliberately not forked,
   so its old-look classes are restyled here too, but only inside
   .nl-scope. */

.nl-scope {
  --nl-ink:#0a0f1c; --nl-surface:#121a2c; --nl-surface-raised:#182339; --nl-surface-hover:#1c2842;
  --nl-line:#232f4a; --nl-line-soft:#1a2438;
  --nl-paper:#eef2fa; --nl-muted:#8b97b3; --nl-faint:#5b6785;
  --nl-brand:#3b82f6; --nl-brand-soft:#152340; --nl-brand-ring:#1e3a66;
  --nl-good:#22c55e; --nl-good-soft:#123322;
  --nl-warn:#f5a524; --nl-warn-soft:#3a2a10;
  --nl-bad:#ef4444;  --nl-bad-soft:#3a1616;
  --nl-info:#14b8a6; --nl-info-soft:#0f2e2b;
  --nl-violet:#8b5cf6; --nl-violet-soft:#241a3d;
  --nl-radius-lg:16px; --nl-radius-md:12px; --nl-radius-sm:8px;
  --nl-font-display:"Manrope",system-ui,-apple-system,"Segoe UI",sans-serif;
  --nl-font-mono:"JetBrains Mono",ui-monospace,"SF Mono",Consolas,monospace;

  display:block; background:var(--nl-ink); color:var(--nl-paper); font-family:var(--nl-font-display);
  line-height:1.5; -webkit-font-smoothing:antialiased; border-radius:var(--nl-radius-lg);
  padding:22px clamp(16px,3vw,28px) 40px; margin:0 0 24px;
}
/* As a page's top-level card the scope now sits flush against the nav
   above and the side/browser edges (style.css's full-bleed .content), so
   rounded top corners would leave two little wedges of bare page
   background at the seam. Direct child only: a scope nested inside other
   markup (e.g. .nl-public-scope, which has no box of its own) is untouched. */
.content > .nl-scope { border-top-left-radius:0; border-top-right-radius:0; }
/* Light-theme token override. _theme_init.html always resolves
   data-theme to a concrete 'light' or 'dark' value before any .nl-scope
   content can paint (every code path that sets it does so synchronously
   in <head>, or hardcodes data-theme="light" directly on <html> for the
   two standalone customer-upload pages) -- so the unattributed dark
   block above staying the default is correct, not a gap. Specificity:
   :root[data-theme="light"] .nl-scope is (0,3,0) vs the dark block's
   (0,1,0) -- this wins outright regardless of source order, no
   :where()/reordering needed; placed after the dark block purely for
   readability, matching style.css's own :root / :root[data-theme=light]
   convention. Values follow the same darken-for-light-backgrounds move
   style.css's own light theme already makes for its accents (a color
   with fine contrast on a dark card can fail WCAG AA as text on white),
   contrast-checked against their paired background/text roles. */
:root[data-theme="light"] .nl-scope {
  --nl-ink:#eef1f8; --nl-surface:#ffffff; --nl-surface-raised:#e4e9f4; --nl-surface-hover:#d7deec;
  --nl-line:#d3dae8; --nl-line-soft:#e2e7f1;
  --nl-paper:#0f1729; --nl-muted:#4b5670; --nl-faint:#7c88a3;
  --nl-brand:#2563eb; --nl-brand-soft:#dbeafe; --nl-brand-ring:#93c5fd;
  --nl-good:#15803d; --nl-good-soft:#dcfce7;
  --nl-warn:#b45309; --nl-warn-soft:#fef3c7;
  --nl-bad:#b91c1c;  --nl-bad-soft:#fee2e2;
  --nl-info:#0f766e; --nl-info-soft:#ccfbf1;
  --nl-violet:#6d28d9; --nl-violet-soft:#ede9fe;
}
.nl-scope *, .nl-scope *::before, .nl-scope *::after { box-sizing:border-box; }
/* The real static/css/style.css defines bare-element color rules (h1, p,
   and label all confirmed by reading style.css directly -- label at
   "label { ...; color: var(--text-dim); }") that beat inherited color
   regardless of our selector's specificity -- ANY explicit declaration
   on the matched element wins over an inherited one, no matter how low
   its specificity. Every heading, paragraph and label below states
   color explicitly so ours is the competing declaration, never a value
   we hoped would merely inherit from .nl-scope -- every <label> in this
   file happens to sit inside .nl-form-stack today (whose own higher-
   specificity rule would already win), but that's incidental, not a
   guard, so label gets the same unconditional fix as h1/h2/h3/p rather
   than relying on every future label having that same ancestor. Elements
   verified clean (no competing bare-element rule found: dd, td, strong,
   button, and every span/li that already carries its own explicit
   colour via a semantic class like .nl-pill-good or .nl-disk-reasons)
   are left to inherit -- no need to armor what isn't broken. */
.nl-scope, .nl-scope p { color:var(--nl-paper); }
.nl-scope h1, .nl-scope h2, .nl-scope h3 { margin:0; font-family:var(--nl-font-display); text-wrap:balance; color:var(--nl-paper); }
.nl-scope label { color:var(--nl-paper); }
.nl-scope a { color:var(--nl-brand); text-decoration:none; }
.nl-scope a:hover { text-decoration:underline; }
.nl-scope .nl-mono { font-family:var(--nl-font-mono); font-variant-numeric:tabular-nums; }
.nl-scope .nl-muted { color:var(--nl-muted) !important; }
/* _token_chips.html's macro emits a bare, unprefixed .template-token-intro
   paragraph (shared with not-yet-converted pages) -- same specificity trap
   as .settings-card p.muted above, fixed the same way so every _new.html
   call site gets the dimmed intro text without a per-page override. */
.nl-scope .template-token-intro { color:var(--nl-muted); }
.nl-scope .nl-small { font-size:12.5px; }
.nl-scope .nl-empty-state { color:var(--nl-faint) !important; font-size:13px; margin:0; }
/* mailbox_new.html's inbox list and reading pane are almost entirely built by
   JS (loading/empty/error states, the RSVP and reply panels), and several of
   those class strings were left as the bare old-look .empty-state rather than
   nl-empty-state -- same bare-class-in-new-look-markup situation .pill/.muted/
   .small already have a safety net for above, so give .empty-state one too:
   without it those spots pick up style.css's dashed-border box look instead
   of blending into the page. */
.nl-scope .empty-state { color:var(--nl-faint) !important; font-size:13px; margin:0; padding:0;
  border:none; border-radius:0; text-align:left; line-height:1.5; background:none; }
.nl-scope .nl-note-text { background:var(--nl-surface-raised); border:1px solid var(--nl-line-soft);
  border-radius:var(--nl-radius-sm); padding:11px 13px; font-size:13px; color:var(--nl-muted) !important; white-space:pre-line; }
/* Job detail's ticket-stub card (.repair-tag) is old-look markup left as
   verbatim style.css classes in detail_new.html, same as .empty-state
   above -- it still paints with style.css's own --panel/--border tokens,
   which this scope's --nl-* palette doesn't touch. Coincidentally fine in
   the light theme (old-look --panel and this scope's --nl-surface are
   both white there), but a flat grey card stranded in the middle of this
   scope's otherwise-blue dark theme otherwise. */
.nl-scope .repair-tag { background:var(--nl-surface); border-color:var(--nl-line); }
.nl-scope .repair-tag-hole { background:var(--nl-ink); border-color:var(--nl-line); }

/* ---------- page header (the mockup's dashboard-style header, reused by every new-look page) ---------- */
.nl-dash-head { display:flex; align-items:flex-start; justify-content:space-between; gap:16px;
  flex-wrap:wrap; margin-bottom:18px; }
.nl-dash-title { display:flex; align-items:flex-start; gap:14px; }
.nl-dash-icon { width:46px; height:46px; border-radius:12px; background:var(--nl-surface); border:1px solid var(--nl-line);
  display:flex; align-items:center; justify-content:center; flex:none; color:var(--nl-brand); }
.nl-dash-icon svg { width:22px; height:22px; }
.nl-dash-title h1 { font-size:21px; font-weight:800; letter-spacing:-.01em; display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.nl-dash-sub { font-size:12.5px; color:var(--nl-muted) !important; margin:4px 0 0; }
.nl-dash-sub a { font-weight:600; }
.nl-sep { color:var(--nl-line); margin:0 2px; }
.nl-dash-actions { display:flex; gap:8px; flex-wrap:wrap; align-items:center; }
.nl-btn-ico { width:15px; height:15px; }

/* ---------- grid of cards ---------- */
.nl-grid { display:grid; gap:14px; margin-bottom:14px; }
.nl-grid-2 { grid-template-columns:repeat(2,minmax(0,1fr)); }
.nl-grid-3 { grid-template-columns:repeat(3,minmax(0,1fr)); }
.nl-grid-4 { grid-template-columns:repeat(4,minmax(0,1fr)); }
@media (max-width:1100px) { .nl-grid-3, .nl-grid-4 { grid-template-columns:repeat(2,minmax(0,1fr)); } }
@media (max-width:640px) { .nl-grid-2, .nl-grid-3, .nl-grid-4 { grid-template-columns:1fr; } }

.nl-card { background:var(--nl-surface); border:1px solid var(--nl-line); border-radius:var(--nl-radius-lg);
  padding:16px 18px; display:flex; flex-direction:column; cursor:pointer; transition:border-color .12s ease; }
.nl-card:hover, .nl-card:focus-visible { border-color:var(--nl-brand-ring); }
.nl-card:focus-visible, .nl-kpi:focus-visible, .nl-card-benchy .nl-card-head:focus-visible {
  outline:2px solid var(--nl-brand); outline-offset:1px;
}
.nl-card-wide { grid-column:1 / -1; cursor:default; }
.nl-card-wide-2 { grid-column:span 2; }
@media (max-width:1100px) { .nl-card-wide-2 { grid-column:1 / -1; } }
.nl-card-benchy { margin-bottom:14px; cursor:default; }
.nl-card-benchy .nl-card-head { cursor:pointer; user-select:none; }
@media (max-width:480px) { .nl-card-benchy .nl-card-head-action { margin-left:44px; } }
.nl-benchy-chevron { width:15px; height:15px; color:var(--nl-faint); margin-left:6px; transition:transform .15s ease; flex:none; }
.nl-card-benchy.nl-collapsed .nl-benchy-chevron { transform:rotate(-90deg); }
.nl-card-benchy.nl-collapsed #nl-benchy-body { display:none; }
/* flex-wrap (not nowrap) so a long title never overlaps its trailing
   status/badge/action -- it drops to its own row instead. This is a
   card-width, not viewport-width, problem (a card in a 3-up grid can be
   narrower than a full-width card at a much wider viewport), so this is
   unconditional rather than behind a breakpoint. */
.nl-card-head { display:flex; align-items:center; flex-wrap:wrap; gap:10px; row-gap:6px; margin-bottom:12px; }
.nl-card-ico { width:32px; height:32px; border-radius:9px; display:flex; align-items:center; justify-content:center; flex:none; }
.nl-card-ico svg { width:16px; height:16px; }
.nl-card-head h2 { font-size:14px; font-weight:700; min-width:0; }
.nl-card-val { margin-left:auto; font:700 16px var(--nl-font-mono); flex:none; }
.nl-card-status { margin-left:auto; font-size:12px; font-weight:700; flex:none; }
.nl-card-head-action { margin-left:auto; flex:none; }
.nl-card > .nl-card-foot { margin-top:auto; padding-top:12px; }
.nl-card-foot { display:block; font-size:12px; font-weight:600; color:var(--nl-brand) !important; text-decoration:none; }
.nl-card-foot:hover { text-decoration:underline; }
.nl-count-badge { margin-left:auto; background:var(--nl-surface-hover); color:var(--nl-muted) !important; font:700 11px var(--nl-font-mono);
  padding:2px 9px; border-radius:100px; white-space:nowrap; flex:none; }
.nl-count-badge.nl-count-bad { background:var(--nl-bad-soft); color:var(--nl-bad) !important; }
.nl-link { color:var(--nl-brand) !important; font-size:12.5px; font-weight:600; text-decoration:none; }
.nl-link:hover { text-decoration:underline; }

/* compact key/value list used inside cards (not the fuller .nl-detail-list used in modals) */
.nl-mini-list { display:flex; flex-direction:column; gap:0; }
.nl-mini-list > div { display:flex; justify-content:space-between; gap:10px; padding:5px 0; font-size:12.5px; }
.nl-mini-list dt { color:var(--nl-muted) !important; flex:none; }
.nl-mini-list dd { margin:0; text-align:right; font-weight:600; color:var(--nl-paper) !important; }

.nl-plain-list { margin:0; padding-left:16px; font-size:12.5px; color:var(--nl-muted) !important; display:flex; flex-direction:column; gap:4px; }
.nl-plain-list li { color:var(--nl-muted) !important; overflow-wrap:anywhere; }

.nl-usage-list { display:flex; flex-direction:column; gap:10px; }
.nl-usage-row { display:flex; flex-direction:column; gap:5px; }
.nl-usage-label { display:flex; justify-content:space-between; font-size:12.5px; font-weight:600; }
.nl-usage-bar { margin-top:0; }

/* ---------- buttons ---------- */
.nl-btn { display:inline-flex; align-items:center; gap:6px; font:600 13px var(--nl-font-display);
  padding:9px 14px; border-radius:var(--nl-radius-sm); border:1px solid var(--nl-line);
  background:var(--nl-surface-raised); color:var(--nl-paper); cursor:pointer; }
.nl-btn:hover { border-color:var(--nl-brand); text-decoration:none; }
.nl-btn-primary { background:var(--nl-brand); border-color:transparent; color:#fff; }
.nl-btn-primary:hover { filter:brightness(1.08); }
.nl-btn-danger { border-color:var(--nl-bad); color:var(--nl-bad); background:transparent; }
.nl-btn-warn { border-color:var(--nl-warn); color:var(--nl-warn); background:var(--nl-warn-soft); }
.nl-btn-small { padding:6px 11px; font-size:12px; }
/* .nl-btn is very often an <a> (Cancel links, list-page action buttons,
   the "+ New X" header button) -- .nl-scope a's explicit color (0,1,1)
   otherwise beats every variant's own color (0,1,0) regardless of which
   one, turning .nl-btn-primary/.nl-btn-danger's text the same blue as
   .nl-scope a and, since .nl-btn-primary's own background IS that same
   blue, genuinely invisible -- found live on a real "+ New fault" button
   during Phase 1. Same fix/reasoning as .nl-scope .nl-kpi-link earlier
   in this file. */
.nl-scope .nl-btn { color:var(--nl-paper); }
.nl-scope .nl-btn:hover { text-decoration:none; }
.nl-scope .nl-btn-primary { color:#fff; }
.nl-scope .nl-btn-danger { color:var(--nl-bad); }
.nl-scope .nl-btn-warn { color:var(--nl-warn); }

/* repairs/detail_new.html's Print and Add-note split buttons -- style.css's
   own .print-split-btn-main/-caret and .note-split-btn-main/-caret zero two
   corners and the shared inner border via longhand properties so the pair
   reads as one fused control. .nl-btn's own "border" and "border-radius"
   shorthands set those same longhands, and this file loads after style.css,
   so without restating the zeroing here .nl-btn's shorthand would win the
   cascade and pull the corners back round / the seam back into a double
   border. Their dropdowns (.print-menu-dropdown, .note-status-dropdown)
   get restyled below, alongside the page's other small popup menus. */
.nl-btn.print-split-btn-main, .nl-btn.note-split-btn-main {
  border-top-right-radius:0; border-bottom-right-radius:0; border-right:none;
}
.nl-btn.print-split-btn-caret, .nl-btn.note-split-btn-caret {
  border-top-left-radius:0; border-bottom-left-radius:0;
}

/* repairs/detail_new.html's small popup menus -- the "More" actions menu,
   the Print format picker, the Add-note status picker, the Benchy
   assistant popover, and the booking label prompt's print-quantity picker. style.css documents the Print and Benchy ones as
   deliberately sharing one "house style" popup shape, and the other two
   are built the exact same way, so all four are restyled together here
   rather than one at a time -- converting only the Benchy popover would
   swap a "these all look old" inconsistency for a "one looks new, three
   look old" one instead of actually fixing it. Only colours change; every
   position/size/hide rule stays in style.css untouched.

   .repair-tab-more-dropdown is reparented to a direct child of <body>
   while open (see the JS below), so by the time it's visible it's no
   longer a descendant of .nl-scope at all -- a plain ".nl-scope
   .repair-tab-more-dropdown" rule silently never matches it open. body
   already carries .nl-nav whenever new_look_enabled is on (base.html),
   which survives the reparenting since body is never moved, so that one
   selector uses .nl-nav instead; the other three never leave .nl-scope
   and keep the usual selector. */
body.nl-nav .repair-tab-more-dropdown,
.nl-scope .print-menu-dropdown,
.nl-scope .note-status-dropdown,
.nl-scope .benchy-popover,
.nl-scope .label-qty-menu {
  background:var(--nl-surface); border-color:var(--nl-line); box-shadow:0 12px 40px rgba(0,0,0,.45);
}
body.nl-nav .repair-tab-more-item,
.nl-scope .print-menu-dropdown a,
.nl-scope .note-status-dropdown-item,
.nl-scope .benchy-more-toggle,
.nl-scope .benchy-action-item,
.nl-scope .label-qty-option {
  color:var(--nl-paper);
}
body.nl-nav .repair-tab-more-item:hover,
.nl-scope .print-menu-dropdown a:hover,
.nl-scope .note-status-dropdown-item:hover,
.nl-scope .benchy-more-toggle:hover,
.nl-scope .benchy-action-item:hover,
.nl-scope .label-qty-option:hover {
  background:var(--nl-surface-hover);
}
.nl-scope .print-menu-group-label,
.nl-scope .note-status-dropdown-label,
.nl-scope .benchy-more-toggle {
  color:var(--nl-faint) !important;
}
.nl-scope .print-menu-group-label ~ .print-menu-group-label {
  border-top-color:var(--nl-line-soft);
}
.nl-scope .print-menu-direct-icon { color:var(--nl-good); }

/* The Benchy trigger icon (the sparkle button beside the ticket) and the
   content its popover shows once opened -- similar-past-repairs rows and
   the AI summary panel are Benchy-only, not shared with the other three
   menus above, so there's no shared-shape reason to leave them bare. */
.nl-scope .benchy-trigger {
  border-color:var(--nl-line); background:var(--nl-surface-raised); color:var(--nl-brand);
}
.nl-scope .benchy-trigger:hover { border-color:var(--nl-brand-ring); background:var(--nl-surface-hover); }
.nl-scope .benchy-row { background:var(--nl-surface-raised); border-color:var(--nl-line-soft); color:var(--nl-paper); text-decoration:none; }
.nl-scope .benchy-row:hover { border-color:var(--nl-line); text-decoration:none; }
.nl-scope .benchy-ticket { color:var(--nl-faint) !important; }
.nl-scope .benchy-sub { color:var(--nl-muted) !important; }
.nl-scope .benchy-facts { color:var(--nl-info); }
.nl-scope .benchy-imported { color:var(--nl-faint) !important; }
.nl-scope .benchy-summary { background:var(--nl-surface-raised); border-color:var(--nl-line-soft); }

/* benchy.js (static/js/benchy.js) is the same unforked file the old-look
   page loads -- it builds "muted small"/"mono" markup directly into its
   results HTML rather than through a template, so those bare classes
   can't be swapped to nl-muted/nl-small/nl-mono the way a template's own
   markup can be. Restyled here the same way calendar.js's cal- classes
   are elsewhere in this file: bare class kept (so old-look, which has no
   .nl-scope ancestor, is completely unaffected), colour/size scoped only
   inside .nl-scope. This is a safety net for any other bare .muted/.small/
   .mono this rollout has missed on an already-shipped page, not only
   Benchy's -- same role .nl-scope .pill already plays for that class. */
.nl-scope .muted { color:var(--nl-muted); }
.nl-scope .small { font-size:12.5px; }
.nl-scope .mono { font-family:var(--nl-font-mono); font-variant-numeric:tabular-nums; }

/* ---------- attachments: upload dropzone, photo/file cards, upload
   progress rows, camera capture, QR upload -- shared by
   repairs/detail_new.html's Photos & Files tab, files/folder_new.html,
   documents/list_new.html, help/page_new.html and
   help_articles/form_new.html. static/js/file_preview.js is the same
   unforked file the old-look pages load and builds/selects several of
   these classes itself (.attachment-card, .attachment-upload-row/-bar/
   -bar-fill, .attachment-capture-shot, .dragover), so every class keeps
   its old-look name and only colour is overridden, only inside .nl-scope.
   None of these elements is ever moved out of .nl-scope by JS, unlike
   .repair-tab-more-dropdown above. Left bare on purpose: the shared
   .file-lightbox-* viewer shell (house style, see the Calendar section),
   .attachment-qr-backdrop (extends that shell), the dark round
   .attachment-capture-shot-remove button (sits over arbitrary photo
   content, same reason .file-lightbox-nav does) and the QR image's own
   white background (scan contrast, not decoration). ---------- */
.nl-scope .attachment-dropzone { border-color:var(--nl-line-soft); }
.nl-scope .attachment-dropzone.dragover,
.nl-scope .attachment-dropzone:focus-visible { border-color:var(--nl-info); background:var(--nl-info-soft); }
.nl-scope .attachment-card { border-color:var(--nl-line-soft); }
.nl-scope .attachment-card:hover { border-color:var(--nl-brand-ring); }
.nl-scope .attachment-card .attachment-thumb-wrap { background:var(--nl-surface-raised); }
.nl-scope .attachment-card .attachment-caption { color:var(--nl-muted); }
.nl-scope .attachment-upload-row .attachment-upload-bar { background:var(--nl-line); }
.nl-scope .attachment-upload-row .attachment-upload-bar-fill { background:var(--nl-brand); }
/* A plain text link in button form -- same brand colour as .nl-scope a,
   which also keeps its colour on hover rather than shifting it. */
.nl-scope .link-button, .nl-scope .link-button:hover { color:var(--nl-brand); }
.nl-scope .attachment-qr-box {
  background:var(--nl-surface); border-color:var(--nl-line); border-radius:var(--nl-radius-md);
  box-shadow:0 12px 40px rgba(0,0,0,.45);
}
.nl-scope .attachment-qr-head { border-bottom-color:var(--nl-line-soft); }
.nl-scope .attachment-capture-review { border-color:var(--nl-line-soft); background:var(--nl-surface-raised); }
.nl-scope .attachment-capture-shot { border-color:var(--nl-line-soft); }
/* style.css dims the tray's disabled "Upload photos" via ".btn:disabled",
   which stops matching once that button is .nl-btn. */
.nl-scope .attachment-capture-tray-actions .nl-btn:disabled { opacity:.5; cursor:not-allowed; }
/* The camera button is phone-only: style.css hides it and shows it again
   under 700px. .nl-btn's own display:inline-flex loads after style.css at
   the same specificity and would otherwise win, putting a "Take photo"
   button on every desktop. */
.nl-scope .nl-btn.attachment-camera-btn { display:none; }
@media (max-width:700px) {
  .nl-scope .nl-btn.attachment-camera-btn { display:inline-flex; width:100%; margin-top:10px; }
}
/* The ?capture=1 deep-link's "look here" pulse. style.css's keyframes are
   hard-coded old-look amber, and a @keyframes rule can't be scoped to
   .nl-scope (this file loads on every page, so redefining that same name
   here would recolour the old-look page too) -- so a separately named copy
   is swapped in, on the same ring colour nl-tracker-pulse below uses. */
.nl-scope .attachment-camera-btn-highlight { animation-name:nl-attachment-camera-pulse; }
@keyframes nl-attachment-camera-pulse {
  0% { box-shadow:0 0 0 0 var(--nl-brand-ring); }
  100% { box-shadow:0 0 0 10px rgba(0,0,0,0); }
}
/* Companion [hidden] rule, same standing trap as .nl-form-stack below:
   .nl-btn's own display:inline-flex (0,1,0) ties with the UA's
   [hidden]{display:none} (0,1,0) and wins on source order, so a
   JS-toggled button/link (a retry action, a lightbox trigger revealed
   only once content loads) stays visible without this -- found live on
   customers/form's email-verify-retry button and three separate
   lightbox-office links, none of which were ever actually hidden. */
.nl-scope .nl-btn[hidden] { display:none; }
/* header "Request Update"-style poll button */
.nl-btn-waiting { opacity:.7; cursor:default; }
.nl-spin { animation:nl-spin 1.1s linear infinite; }
@keyframes nl-spin { from { transform:rotate(0deg); } to { transform:rotate(360deg); } }
@media (prefers-reduced-motion:reduce) { .nl-spin { animation:none; } }

/* ---------- confirm panels (inline-style shown/hidden, unchanged mechanism) ---------- */
.nl-confirm-panel { margin-bottom:20px; padding:16px; background:var(--nl-surface-raised);
  border-radius:var(--nl-radius-md); border:1px solid var(--nl-bad); }
.nl-confirm-panel.nl-neutral { border-color:var(--nl-line); }

/* ---------- panels ---------- */
.nl-panel { background:var(--nl-surface); border:1px solid var(--nl-line); border-radius:var(--nl-radius-lg);
  padding:18px 20px; margin-bottom:16px; }
.nl-panel > h2 { font-size:15px; font-weight:700; display:flex; align-items:center; gap:8px; }
/* Only a panel actually wired to the collapse JS (data-collapsible, see
   the inline script) looks clickable -- a plain .nl-panel used inside a
   modal is static and shouldn't imply an interaction it doesn't have. */
.nl-panel[data-collapsible] > h2 { cursor:pointer; user-select:none; }
.nl-panel[data-collapsible] > h2::after { content:"\25BE"; margin-left:auto; font-size:12px; color:var(--nl-faint);
  transition:transform .15s ease; }
.nl-panel.collapsed > h2::after { transform:rotate(-90deg); }
.nl-panel.collapsed > *:not(h2) { display:none; }
.nl-panel > h2 + p, .nl-panel > h2 + .nl-detail-list, .nl-panel > h2 ~ * { margin-top:12px; }
.nl-panel > h2:first-child { margin-top:0; }
.nl-detail-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:16px; }
/* The mobile override must keep the same minmax(0,...) as the desktop
   rule above, not plain 1fr -- a grid track's default min-width is
   auto (its content's own intrinsic min-content size), so a wide
   child (e.g. a .nl-table-wrap'd table, which has its own overflow-x:
   auto but still reports a real min-content width) silently forces
   the WHOLE single-column track that wide at this breakpoint even
   though only one panel actually has wide content -- confirmed live
   on Customer detail's two-column Contact/Repair-history row, where
   dropping to 1fr here (no minmax) stretched the page to ~550px on a
   375px viewport. minmax(0,1fr) lets the track shrink to the
   viewport's actual width and hands overflow back to the table's own
   scroll wrapper, where it belongs. */
@media (max-width:860px) { .nl-detail-grid { grid-template-columns:minmax(0,1fr); } }

/* ---------- collapsible grouped section (category groups on a list
   page, e.g. Storage positions) -- new-look counterpart of old-look's
   own .settings-section/.settings-section-body (style.css): same
   native <details>/<summary> mechanism and behaviour (including which
   groups start open), only the classes/colors differ. ---------- */
.nl-settings-section { border:1px solid var(--nl-line); border-radius:var(--nl-radius-md); margin-bottom:12px; background:var(--nl-surface); }
.nl-settings-section summary { cursor:pointer; padding:13px 16px; font-weight:700; list-style:none; display:flex; align-items:center; gap:8px; justify-content:space-between; }
.nl-settings-section summary::-webkit-details-marker { display:none; }
.nl-settings-section summary::after { content:'\25BE'; color:var(--nl-faint); transition:transform .15s; }
.nl-settings-section[open] summary::after { transform:rotate(180deg); }
.nl-settings-section summary:hover { color:var(--nl-brand); }
.nl-settings-section-body { padding:4px 16px 16px; border-top:1px solid var(--nl-line-soft); }

/* Settings cards, shared with old-look settings.html via
   _pc_monitoring_settings_cards.html's own <a class="panel settings-card">
   markup. .nl-scope h1/h2/h3 already forces color:var(--nl-paper)
   unconditionally (see the block comment near the top of this file) --
   safe on a dark card, but the card's own background still comes from the
   old-look --panel variable, which is light in the old-look's light theme.
   Pin both explicitly so the card is never light-text-on-light-background
   regardless of which old-look theme is active. */
.nl-scope .settings-card { background:var(--nl-surface); border:1px solid var(--nl-line); border-radius:var(--nl-radius-lg); }
/* .nl-scope p's explicit color (specificity 0,1,1) beats the old-look
   .muted class (0,1,0) regardless of source order -- without this the
   card's description paragraph renders at full brightness instead of
   dimmed. Cosmetic, not a contrast bug, but cheap to fix alongside the
   background pin above. */
.nl-scope .settings-card p.muted { color:var(--nl-muted) !important; }

/* ---------- Settings hub (settings.html) -- persistent left sidebar,
   macOS System Settings style; the sidebar item's is-active state
   matches the same convention as .nl-chip/.nl-tab/.nl-kpi-link
   elsewhere in this file. ---------- */
.nl-settings-shell { display:grid; grid-template-columns:220px 1fr; gap:24px; align-items:start; }
@media (max-width:860px) { .nl-settings-shell { grid-template-columns:1fr; } }
.nl-settings-sidebar { display:flex; flex-direction:column; gap:2px; border:1px solid var(--nl-line);
  border-radius:var(--nl-radius-sm); padding:6px; position:sticky; top:20px; }
@media (max-width:860px) { .nl-settings-sidebar { position:static; flex-direction:row; flex-wrap:wrap; } }
.nl-settings-sidebar-item { display:block; width:100%; text-align:left; padding:10px 12px; font-size:14px;
  font-weight:600; color:var(--nl-muted); background:none; border:none; border-radius:var(--nl-radius-sm); cursor:pointer; }
.nl-scope .nl-settings-sidebar-item:hover { background:var(--nl-surface-hover); color:var(--nl-paper); }
.nl-scope .nl-settings-sidebar-item.is-active { background:var(--nl-brand-soft); color:var(--nl-brand);
  box-shadow:inset 0 0 0 2px var(--nl-brand-ring); }
.nl-settings-category-title { font-size:18px; margin:4px 0 16px 0; }
.nl-settings-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(220px,1fr)); gap:14px; margin-bottom:8px; }
.nl-scope .nl-settings-card { display:block; margin-bottom:0; color:var(--nl-paper); text-decoration:none; transition:border-color .15s; }
.nl-scope .nl-settings-card:hover { text-decoration:none; border-color:var(--nl-brand); }

/* ---------- KPI tile row (the mockup's summary strip) ---------- */
.nl-kpi-row { display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:12px; margin-bottom:16px; }
.nl-kpi-row-4 { grid-template-columns:repeat(4,minmax(0,1fr)); }
.nl-kpi-row-6 { grid-template-columns:repeat(6,minmax(0,1fr)); }
.nl-kpi-row-7 { grid-template-columns:repeat(7,minmax(0,1fr)); }
@media (max-width:1200px) { .nl-kpi-row, .nl-kpi-row-4, .nl-kpi-row-6, .nl-kpi-row-7 { grid-template-columns:repeat(3,minmax(0,1fr)); } }
@media (max-width:640px) { .nl-kpi-row, .nl-kpi-row-4, .nl-kpi-row-6, .nl-kpi-row-7 { grid-template-columns:repeat(2,minmax(0,1fr)); } }
.nl-kpi { background:var(--nl-surface); border:1px solid var(--nl-line); border-radius:var(--nl-radius-md);
  padding:14px 15px; display:flex; flex-direction:column; gap:10px; cursor:pointer; transition:border-color .12s ease; }
.nl-kpi:hover { border-color:var(--nl-brand-ring); }
/* .nl-kpi as an <a> (dashboard/list tiles link to a filtered view rather
   than opening a modal) would otherwise inherit .nl-scope a's brand color
   and hover underline -- both overridden here so the tile keeps its own
   label/value colors and signals interactivity via the border-color
   transition above, same as the plain-div .nl-kpi already does.
   .nl-scope a has specificity (0,1,1) -- a bare .nl-kpi-link class alone
   is only (0,1,0) and loses, so this is written as .nl-scope .nl-kpi-link
   to match/beat it, same convention as every other override in this file. */
.nl-scope .nl-kpi-link, .nl-scope .nl-kpi-link:hover { color:inherit; text-decoration:none; }
.nl-kpi-link.is-active { border-color:var(--nl-brand); background:var(--nl-brand-soft); }
/* Revenue tiles (dashboard) carry no url -- not clickable, so neither the
   pointer cursor nor the hover border-color animation should imply they are. */
.nl-kpi-static { cursor:default; }
.nl-kpi-static:hover { border-color:var(--nl-line); }
.nl-kpi-top { display:flex; align-items:flex-start; gap:10px; }
.nl-kpi-top > div:last-child { min-width:0; }
.nl-kpi-icon { width:34px; height:34px; border-radius:9px; display:flex; align-items:center; justify-content:center; flex:none; }
.nl-kpi-icon svg { width:17px; height:17px; }
.nl-kpi-label { font-size:11px; color:var(--nl-muted); font-weight:600; overflow-wrap:anywhere; }
.nl-kpi-value { font:700 19px var(--nl-font-mono); margin-top:1px; letter-spacing:-.01em; }
.nl-kpi-value-text { font-family:var(--nl-font-display); font-size:15px; }
.nl-kpi-mini-stats { display:flex; flex-direction:column; gap:1px; margin-top:2px; font:700 12px var(--nl-font-mono); }
.nl-bar-track { width:100%; height:5px; border-radius:100px; background:var(--nl-line); overflow:hidden; }
.nl-bar-fill { height:100%; border-radius:100px; }

/* ---------- modals: the "larger box" a card opens ---------- */
.nl-modal { position:fixed; inset:0; z-index:1000; display:none; align-items:flex-start; justify-content:center;
  padding:5vh 16px; overflow-y:auto; }
/* .is-open beats [hidden]-style display:none the same way this app's own
   style.css documents (grep style.css for "[hidden]") -- toggled via a
   class, never the hidden attribute, for exactly that reason. */
.nl-modal.is-open { display:flex; }
.nl-modal-backdrop { position:fixed; inset:0; background:rgba(5,8,16,.6); }
.nl-modal-box { position:relative; background:var(--nl-ink); border:1px solid var(--nl-line); border-radius:var(--nl-radius-lg);
  width:100%; max-width:900px; max-height:90vh; display:flex; flex-direction:column; box-shadow:0 24px 60px -12px rgba(0,0,0,.6); }
.nl-modal-box.nl-modal-narrow { max-width:560px; }
.nl-modal-box.nl-modal-wide { max-width:1080px; }
.nl-modal-head { display:flex; align-items:center; gap:10px; padding:18px 20px; border-bottom:1px solid var(--nl-line); flex:none; }
.nl-modal-head h2 { font-size:16px; font-weight:800; display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.nl-modal-close { margin-left:auto; width:30px; height:30px; border-radius:8px; border:1px solid var(--nl-line);
  background:var(--nl-surface-raised); color:var(--nl-paper) !important; font-size:16px; line-height:1; cursor:pointer; flex:none; }
.nl-modal-close:hover { border-color:var(--nl-brand); }
.nl-modal-body { padding:20px; overflow-y:auto; }
.nl-modal-body > .nl-panel:last-child { margin-bottom:0; }
body.nl-modal-lock { overflow:hidden; }
@media (max-width:640px) {
  .nl-modal { padding:0; }
  .nl-modal-box { max-width:100%; max-height:100%; height:100%; border-radius:0; }
}

/* ---------- key/value detail list ---------- */
.nl-detail-list { display:flex; flex-direction:column; gap:0; min-width:0; }
.nl-detail-list > div { display:flex; justify-content:space-between; gap:16px; padding:8px 0;
  border-bottom:1px solid var(--nl-line-soft); font-size:13px; min-width:0; }
.nl-detail-list > div:last-child { border-bottom:none; }
.nl-detail-list dt { color:var(--nl-muted); flex:none; }
/* min-width:0 -- without it a wide child (a <select> with long option
   text, or an inline-flex form) forces this flex item past its
   container's width instead of shrinking to fit, same CSS Grid/flex
   min-width:auto trap already fixed once for .nl-detail-grid. */
.nl-detail-list dd { margin:0; text-align:right; font-weight:600; min-width:0; }

/* ---------- pills ---------- */
.nl-pill { display:inline-flex; align-items:center; gap:5px; font:600 11.5px var(--nl-font-display);
  padding:3px 10px; border-radius:100px; white-space:nowrap; }
.nl-pill-good { background:var(--nl-good-soft); color:var(--nl-good); }
.nl-pill-warn { background:var(--nl-warn-soft); color:var(--nl-warn); }
.nl-pill-bad { background:var(--nl-bad-soft); color:var(--nl-bad); }
.nl-pill-neutral { background:var(--nl-surface-hover); color:var(--nl-muted); }
/* style.css's own .payment-status-pill/.collection-status-pill modifiers set
   their own border-radius (rounded-rectangle trigger shape, not the full
   capsule) via the old-look --radius-sm token, which is a different pixel
   value per old-look theme (5px default, 10px Glass) than --nl-radius-sm
   (8px) -- rescoped here so the job page's payment/collection triggers use
   the same fixed radius regardless of which old-look theme happens to be
   the underlying page shell. */
.nl-scope .payment-status-pill { border-radius: var(--nl-radius-sm); }
.nl-pill-info { background:var(--nl-info-soft); color:var(--nl-info); }

/* repairs/detail_new.html's estimate/invoice total row -- style.css's own
   .wt-total sets old-look's amber directly, so it's overridden here the
   same way .payment-status-pill is above, rather than renaming a class
   only used on this one summary line. */
.nl-scope .estimate-totals .wt-total { color:var(--nl-brand); }

/* ---------- the shared macro's old-look pill classes, restyled ONLY inside .nl-scope ---------- */
/* Companions for both pill forms' own display rules -- same [hidden] trap
   as style.css's .pill[hidden], which .nl-scope .pill below would
   otherwise outrank (it loads later at equal specificity). */
.nl-pill[hidden], .nl-scope .pill[hidden] { display:none; }
.nl-scope .pill { display:inline-flex; align-items:center; gap:5px; font:600 11.5px var(--nl-font-display);
  padding:3px 10px; border-radius:100px; white-space:nowrap; border:none; }
.nl-scope .pill-good { background:var(--nl-good-soft); color:var(--nl-good); }
.nl-scope .pill-warn { background:var(--nl-warn-soft); color:var(--nl-warn); }
.nl-scope .pill-bad { background:var(--nl-bad-soft); color:var(--nl-bad); }
.nl-scope .pill-neutral { background:var(--nl-surface-hover); color:var(--nl-muted); }
.nl-scope .pill-info { background:var(--nl-info-soft); color:var(--nl-info); }

/* ---------- generic filter row (a long list's own .filter-row,
   style.css) -- a plain flex-wrap row holding any mix of a search
   form, chips, and small per-field forms (tech picker, rows-per-page).
   Not chip-specific like .nl-chip-row below (no forced item gap
   assumption beyond spacing), so it's the right wrapper when the row
   holds more than just chips. ---------- */
.nl-filter-row { display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-bottom:20px; }

/* ---------- filter chips ---------- */
.nl-chip-group { display:flex; flex-direction:column; gap:6px; margin-bottom:12px; }
.nl-chip-group-label { font-size:11px; color:var(--nl-faint) !important; font-weight:600; text-transform:uppercase; letter-spacing:.05em; }
.nl-chip-row { display:flex; gap:8px; flex-wrap:wrap; }
.nl-chip { display:inline-flex; align-items:center; font:600 12px var(--nl-font-display); padding:5px 12px;
  border-radius:100px; background:var(--nl-surface-hover); color:var(--nl-muted) !important; border:1px solid transparent;
  text-decoration:none; white-space:nowrap; }
.nl-chip:hover { text-decoration:none; border-color:var(--nl-line); }
.nl-chip.is-active { background:var(--nl-brand-soft); color:var(--nl-brand) !important; border-color:var(--nl-brand-ring); }

/* ---------- status filter "tape measure" (a long list's own
   status-tape-wrap/-trigger/-strip, style.css) -- the strip of status
   chips extends out from its own trigger button rather than opening as
   a dropdown below it. The trigger itself is a plain .nl-chip (same
   markup convention as old-look's own class="chip status-tape-trigger"). ---------- */
.nl-status-tape-wrap { position:relative; display:inline-flex; }
.nl-status-tape-trigger { cursor:pointer; font-family:inherit; }
.nl-status-tape-trigger-arrow { font-size:10px; transition:transform .15s ease; }
.nl-status-tape-trigger[aria-expanded="true"] .nl-status-tape-trigger-arrow { transform:rotate(90deg); }
.nl-status-tape-strip {
  position:absolute; top:0; left:calc(100% + 8px); display:flex; align-items:center; gap:8px;
  background:var(--nl-surface); border:1px solid var(--nl-line); border-radius:100px; padding:6px;
  box-shadow:0 4px 16px rgba(0,0,0,.35); white-space:nowrap; z-index:15;
  transform-origin:left center; transform:scaleX(0); opacity:0; pointer-events:none;
  transition:transform .18s ease, opacity .18s ease;
}
/* .status-tape-open (no nl- prefix) is the toggle state class a
   page's own <script> block adds/removes verbatim -- kept unprefixed
   on purpose so that script can be copied byte-for-byte from the
   old-look template with zero class-name edits, same discipline as
   .has-more-right below. */
.nl-status-tape-strip.status-tape-open { transform:scaleX(1); opacity:1; pointer-events:auto; }
@media (max-width:860px) {
  .nl-status-tape-strip { left:0; top:calc(100% + 8px); flex-wrap:wrap; white-space:normal; width:max-content; max-width:90vw; }
}

/* ---------- tables ---------- */
.nl-table-wrap { overflow-x:auto; }
/* Right-edge scroll-fade affordance for a table that can genuinely
   overflow its panel (a long list's own .ticket-table-scroll-wrap,
   style.css) -- macOS renders its overflow-x scrollbar as an
   invisible overlay at rest, so this fade is the cue independent of
   whatever the OS/browser does with the native scrollbar. Pairs with
   .nl-table-wrap as the actual overflow-x:auto scrolling element;
   this outer, non-scrolling wrapper carries the .has-more-right class
   a page's own JS toggles off once scrolled to the end (same
   mechanism as the old-look version, only the classes/fade color
   differ -- fades to var(--nl-surface) to match the .nl-panel it
   normally sits inside, not old-look's var(--panel)). */
.nl-table-scroll-wrap { position:relative; }
.nl-table-scroll-wrap.has-more-right::after {
  content:""; position:absolute; top:0; right:0; bottom:0; width:32px;
  background:linear-gradient(to right, transparent, var(--nl-surface) 85%); pointer-events:none;
}
.nl-table { width:100%; border-collapse:collapse; font-size:13px; min-width:520px; }
.nl-table th { text-align:left; font:600 11px var(--nl-font-display); color:var(--nl-faint);
  text-transform:uppercase; letter-spacing:.05em; padding:0 10px 9px; border-bottom:1px solid var(--nl-line-soft); }
.nl-table td { padding:11px 10px; border-bottom:1px solid var(--nl-line-soft); vertical-align:top; }
.nl-table tr:last-child td { border-bottom:none; }
.nl-table.nl-small td, .nl-table.nl-small th { padding:7px 9px; font-size:12px; }
.nl-table tr[onclick] { cursor:pointer; }
.nl-table tr[onclick]:hover { background:var(--nl-surface-hover); }
/* Subtle even-row zebra tint, ID-scoped exactly like old-look's own
   #customers-table-scroll rule (style.css) -- never widen to bare
   .nl-table, which every converted list page shares; a flagged
   customer's own inline style="background:..." still wins regardless
   of row parity since an inline style always outranks an id selector. */
#customers-table-scroll .nl-table tbody tr:nth-child(even):not(:hover) {
  background:var(--nl-surface-hover);
}

/* ---------- sortable column headers (a long list's own th-sort/
   th-sort-active/th-sort-arrow, style.css) -- same quiet-until-hovered
   behaviour: a header that sorts looks like plain text until you
   hover it, and the arrow only appears at full opacity on the column
   actually in use. See sort_url()/sort_indicator() in app.py. ---------- */
.nl-th-sort { display:inline-flex; align-items:center; gap:4px; white-space:nowrap; text-decoration:none; }
/* .nl-scope a's own explicit color (0,1,1) otherwise beats a bare
   .nl-th-sort's (0,1,0), same specificity trap as .nl-btn/.nl-kpi-link
   above -- the whole point here is plain-text-until-hovered/active, so
   this can't be left to inherit a permanent brand-blue link color. */
.nl-scope .nl-th-sort { color:var(--nl-faint); }
.nl-scope .nl-th-sort:hover { color:var(--nl-paper); text-decoration:none; }
.nl-scope .nl-th-sort-active { color:var(--nl-brand) !important; }
.nl-th-sort-arrow { font-size:10px; line-height:1; }
.nl-th-sort:not(.nl-th-sort-active) .nl-th-sort-arrow { opacity:.35; }
.nl-th-sort:not(.nl-th-sort-active):hover .nl-th-sort-arrow { opacity:.75; }

/* ---------- generic folder/disclosure row toggle ---------- */
.nl-folder-trigger { display:flex; align-items:center; gap:10px; background:none; border:none; padding:0;
  font:inherit; color:var(--nl-paper); cursor:pointer; text-align:left; width:100%; }
.nl-folder-chevron { display:inline-block; color:var(--nl-faint); transition:transform .15s ease; flex:none; }
.nl-folder-trigger[aria-expanded="true"] .nl-folder-chevron { transform:rotate(90deg); }

/* ---------- forms ---------- */
.nl-form-stack { display:flex; flex-direction:column; gap:12px; margin-top:12px; }
/* Companion [hidden] rule, same standing trap as the input/select/
   textarea one above: display:flex here beats the UA's own
   [hidden]{display:none} at equal specificity, so a JS-toggled panel
   built from .nl-form-stack (POS's register/refund forms, a settings
   page's conditional section) would stay visible without this. */
.nl-scope .nl-form-stack[hidden] { display:none; }
.nl-form-stack label { display:block; font-size:12px; color:var(--nl-muted); font-weight:600; }
/* Same trap, one level deeper: a label's own display:block above
   (0,1,1) beats a bare [hidden] (0,1,0) even without a source-order
   tie, so a JS-toggled label nested in a form-stack (POS's printer/
   card-reader picker rows) needs its own companion rule too. */
.nl-scope .nl-form-stack label[hidden] { display:none; }
.nl-scope input[type="text"], .nl-scope input[type="number"], .nl-scope input[type="email"],
.nl-scope input[type="url"], .nl-scope input[type="tel"], .nl-scope input[type="password"],
.nl-scope input[type="search"], .nl-scope input[type="date"], .nl-scope input[type="datetime-local"],
.nl-scope input[type="time"], .nl-scope textarea, .nl-scope select {
  display:block; width:100%; margin-top:5px; background:var(--nl-surface-raised); border:1px solid var(--nl-line);
  border-radius:var(--nl-radius-sm); color:var(--nl-paper); padding:9px 11px; font:500 13px var(--nl-font-display);
}
.nl-scope input[type="text"]:focus, .nl-scope input[type="number"]:focus, .nl-scope input[type="email"]:focus,
.nl-scope input[type="url"]:focus, .nl-scope input[type="tel"]:focus, .nl-scope input[type="password"]:focus,
.nl-scope input[type="search"]:focus, .nl-scope input[type="date"]:focus, .nl-scope input[type="datetime-local"]:focus,
.nl-scope input[type="time"]:focus,
.nl-scope textarea:focus, .nl-scope select:focus { outline:2px solid var(--nl-brand); outline-offset:1px; }
.nl-scope input::placeholder, .nl-scope textarea::placeholder { color:var(--nl-faint); }
.nl-scope textarea { resize:vertical; min-height:80px; font-family:var(--nl-font-display); }
.nl-scope input[type="checkbox"], .nl-scope input[type="radio"] {
  accent-color:var(--nl-brand); width:16px; height:16px; margin:0; cursor:pointer;
}
.nl-scope input[type="color"] {
  width:40px; height:34px; padding:3px; background:var(--nl-surface-raised); border:1px solid var(--nl-line);
  border-radius:var(--nl-radius-sm); cursor:pointer;
}
.nl-scope input[type="color"]::-webkit-color-swatch-wrapper { padding:0; }
.nl-scope input[type="color"]::-webkit-color-swatch { border:none; border-radius:4px; }
/* Companion [hidden] rule, per this app's own standing rule (see
   style.css's many precedents): the explicit display:block on text
   inputs/selects/textareas above would otherwise beat the UA's
   [hidden]{display:none}, so a JS-hidden field (e.g. xero_settings'
   input<->select swap once Xero accounts load) would stay visible. */
.nl-scope input[hidden], .nl-scope select[hidden], .nl-scope textarea[hidden] { display:none; }
.nl-form-actions { display:flex; gap:8px; flex-wrap:wrap; }

/* ---------- search box ---------- */
.nl-search-form { display:flex; gap:8px; flex-wrap:wrap; align-items:center; margin-bottom:14px; }
.nl-search-input { flex:1 1 260px; min-width:0; background:var(--nl-surface-raised); border:1px solid var(--nl-line);
  border-radius:var(--nl-radius-sm); color:var(--nl-paper); padding:9px 12px; font:500 13px var(--nl-font-display); }
.nl-search-input:focus { outline:2px solid var(--nl-brand); outline-offset:1px; }
.nl-search-input::placeholder { color:var(--nl-faint); }

/* ---------- compact pill search box (a long list's own
   .job-search-form/-input, style.css) -- sized to sit in the same
   chip row as filter chips, so the row reads as one set of controls
   rather than a form bolted on beside chips. ---------- */
.nl-job-search-form { display:flex; align-items:center; gap:6px; }
.nl-job-search-input { font-size:12px; padding:6px 12px; border-radius:100px; border:1px solid var(--nl-line);
  background:var(--nl-surface-raised); color:var(--nl-paper); width:240px; max-width:100%; }
.nl-job-search-input::placeholder { color:var(--nl-faint); }
/* Why a wide search found this row ("Order no. BR260924038") when
   the reason isn't the number or name already on screen. */
.job-match-hint { margin-top:2px; max-width:340px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.nl-job-search-input:focus-visible { outline:2px solid var(--nl-brand); outline-offset:1px; }

/* ---------- rows-per-page control (a long list's own .page-size,
   style.css) -- sits in the search/filter row rather than beside the
   pager, since the pager only renders past one page. ---------- */
.nl-page-size { margin-left:auto; display:inline-flex; align-items:center; gap:6px; font-size:12px;
  color:var(--nl-muted) !important; white-space:nowrap; }
.nl-page-size select { font-size:12px; padding:4px 8px; min-width:64px; margin-top:0; width:auto; display:inline-block; }
/* When a .nl-page-size control needs its own <form> (a filter row with
   more than one auto-submitting select, e.g. tech + rows-per-page on
   the Jobs list) -- the margin-left:auto moves here, onto the form
   itself, since .nl-page-size is then nested one level deeper and no
   longer a direct flex child of the row. */
.nl-page-size-form { margin-left:auto; display:flex; align-items:center; }
.nl-page-size-form .nl-page-size { margin-left:0; }

/* ---------- ordinary page header (lighter than .nl-dash-head -- no icon
   box, for the ~180 remaining pages that just need a label + title + a
   couple of actions). Unlike old-look's .page-head/.eyebrow, there is no
   hide-by-default CSS trick here: old-look unconditionally renders an
   <h1> and an eyebrow label on every page, then hides both by default
   (Dave asked for the redundant page-context label gone site-wide, since
   the nav already gives that context) with narrow exceptions (a real
   "Settings > X" breadcrumb link, or a page whose title IS real content
   like a customer's name). A new-look template controls this directly in
   its own Jinja instead -- only render .nl-eyebrow when it's a genuine
   breadcrumb link, only render an <h1> when the title is real content --
   so neither class needs a default-hidden state to override. */
.nl-page-head { display:flex; justify-content:space-between; align-items:flex-end; gap:16px; flex-wrap:wrap; margin-bottom:22px; }
.nl-page-head h1 { font-size:21px; font-weight:800; letter-spacing:-.01em; }
.nl-eyebrow { display:block; font-size:12.5px; color:var(--nl-muted) !important; margin-bottom:4px; }
.nl-eyebrow a { color:var(--nl-muted) !important; }
.nl-eyebrow a:hover { color:var(--nl-paper) !important; }
.nl-page-head-actions { display:flex; gap:8px; flex-wrap:wrap; align-items:center; }

/* ---------- form layout ---------- */
.nl-form-wide { max-width:640px; }
.nl-form-grid { display:grid; grid-template-columns:1fr 1fr; gap:16px; }
@media (max-width:640px) { .nl-form-grid { grid-template-columns:1fr; } }
/* Companion [hidden] rule, same standing trap as .nl-form-stack's own
   above: display:grid beats the UA's [hidden]{display:none} on source
   order, so a conditionally-revealed row built from .nl-form-grid (the
   quick-quote form's 2nd/3rd line-item rows, a calendar peek panel's
   custom-date fields) stayed visible from page load without this. */
.nl-scope .nl-form-grid[hidden] { display:none; }
.nl-checkbox-label { display:flex !important; flex-direction:row; align-items:center; gap:8px; font-size:13px; color:var(--nl-paper) !important; font-weight:500; }
.nl-checkbox-label input { width:auto; }

/* ---------- checkbox checklist (e.g. "applies to statuses" on Aging
   alert rules) -- new-look counterpart of old-look's own .checklist-grid
   (style.css): a bordered grid of checkbox-label items. ---------- */
.nl-checklist-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(180px,1fr)); gap:8px 16px;
  background:var(--nl-surface-raised); border:1px solid var(--nl-line); border-radius:var(--nl-radius-sm); padding:12px 14px; }

/* ---------- pager (long lists) ---------- */
.nl-pager { display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin-top:14px; padding-top:12px; border-top:1px solid var(--nl-line-soft); }
.nl-pager-count { font-size:13px; color:var(--nl-muted) !important; font-variant-numeric:tabular-nums; }
.nl-pager [aria-disabled="true"] { opacity:.45; pointer-events:none; }

/* ---------- tiny uppercase label badge (distinct from .nl-pill: a plain
   status word, not a semantic good/warn/bad state) ---------- */
.nl-tiny-badge { font-size:10px; text-transform:uppercase; letter-spacing:.06em; color:var(--nl-info); background:var(--nl-info-soft); padding:2px 8px; border-radius:100px; }
/* The shared contact-thread macro's bare .tiny-badge and its state
   modifiers, restyled only inside .nl-scope (same approach as .pill). */
.nl-scope .tiny-badge { color:var(--nl-info); background:var(--nl-info-soft); }
.nl-scope .tiny-badge-good { color:var(--nl-good); background:var(--nl-good-soft); }
.nl-scope .tiny-badge-bad { color:var(--nl-bad); background:var(--nl-bad-soft); }
.nl-scope .tiny-badge-warn { color:var(--nl-warn); background:var(--nl-warn-soft); }

/* ---------- horizontal tab bar (in-page tabs, not the site nav) ---------- */
.nl-tab-bar { display:flex; gap:6px; overflow-x:auto; overflow-y:hidden; white-space:nowrap; scrollbar-width:thin; }
.nl-tab {
  font:600 13px var(--nl-font-display); padding:9px 14px; border:1px solid var(--nl-line); border-radius:var(--nl-radius-sm);
  background:var(--nl-surface); color:var(--nl-muted); flex-shrink:0; cursor:pointer; transition:border-color .12s ease, background .12s ease, color .12s ease;
}
.nl-tab:hover { color:var(--nl-paper); border-color:var(--nl-brand-ring); background:var(--nl-surface-hover); text-decoration:none; }
.nl-tab.is-active { color:var(--nl-brand); border-color:var(--nl-brand); background:var(--nl-brand-soft); }
/* Same .nl-scope a specificity trap as .nl-btn above -- a tab is
   sometimes a real <a href="#tab-x">, not a <button>. */
.nl-scope .nl-tab, .nl-scope .nl-tab:hover { text-decoration:none; }
.nl-scope .nl-tab { color:var(--nl-muted); }
.nl-scope .nl-tab:hover { color:var(--nl-paper); }
.nl-scope .nl-tab.is-active { color:var(--nl-brand); }

/* repairs/detail_new.html's tab row -- .repair-tab-row is that page's own
   plain flex wrapper (no color tokens of its own, so it's left unrenamed);
   this just keeps the shared .nl-tab-bar shrinking inside it instead of
   forcing the row wider than the page on a ticket with many tabs. */
.nl-scope .repair-tab-row .nl-tab-bar { flex:1 1 auto; min-width:0; }

/* repairs/detail_new.html's Estimate/Invoice total, sized to match .nl-tab
   exactly so it reads as belonging to the same row, but rounded on all
   four corners since it's a separate box, not a connected tab. */
.nl-tab-total {
  font:600 13px var(--nl-font-display); padding:9px 14px; border:1px solid var(--nl-line); border-radius:var(--nl-radius-sm);
  cursor:pointer; flex-shrink:0; white-space:nowrap; transition:transform .12s ease, box-shadow .12s ease;
}
.nl-tab-total:hover { transform:translateY(-1px); box-shadow:0 4px 10px rgba(0,0,0,.25); }
.nl-tab-total-warn { color:var(--nl-warn); border-color:var(--nl-warn); background:var(--nl-warn-soft); }
.nl-tab-total-good { color:var(--nl-good); border-color:var(--nl-good); background:var(--nl-good-soft); }

/* ---------- Today screen -- four "what needs me" blocks ---------- */
.nl-today-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:16px; align-items:start; }
@media (max-width:900px) { .nl-today-grid { grid-template-columns:1fr; } }
.nl-today-panel { display:flex; flex-direction:column; gap:8px; }
.nl-today-panel-title { font-family:var(--nl-font-display); font-weight:600; font-size:16px; letter-spacing:.08em;
  text-transform:uppercase; color:var(--nl-faint); margin:0 0 4px; }
.nl-scope .nl-today-row { display:flex; align-items:center; gap:12px; background:var(--nl-surface-raised);
  border:1px solid var(--nl-line); border-radius:var(--nl-radius-sm); padding:9px 12px; color:var(--nl-paper);
  text-decoration:none; line-height:1.3; transition:transform .15s ease, box-shadow .15s ease; }
.nl-scope .nl-today-row:hover { transform:translateY(-1px); box-shadow:0 4px 10px rgba(0,0,0,.25); text-decoration:none; }
.nl-today-when { flex:0 0 74px; font-size:12px; color:var(--nl-faint); text-align:right; font-variant-numeric:tabular-nums; }
.nl-today-main { flex:1; min-width:0; }
.nl-today-main strong { font-weight:600; display:block; }
.nl-today-sub { color:var(--nl-faint); font-size:11.5px; display:block; }
/* .nl-scope-prefixed so this beats .nl-scope .nl-today-row's own
   border shorthand above -- same specificity trap as .nl-btn/.nl-tab. */
.nl-scope .nl-today-overdue { border-color:var(--nl-bad); }
.nl-today-overdue .nl-today-when { color:var(--nl-bad); font-weight:600; }
.nl-scope .nl-today-aging { border-color:var(--nl-warn); }
.nl-today-aging .nl-today-when { color:var(--nl-warn); font-weight:600; }
.nl-today-row-split { padding-right:9px; }
.nl-today-row-link { display:flex; align-items:center; gap:12px; flex:1; min-width:0; color:inherit; text-decoration:none; }

/* ---------- Booking settings: weekday picker + blackout-date calendar ---------- */
.nl-weekday-checkbox-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(70px,1fr)); gap:6px; max-width:480px; }
.nl-weekday-checkbox { justify-content:center; padding:8px 4px; border:1px solid var(--nl-line); border-radius:var(--nl-radius-sm); background:var(--nl-surface-raised); }

.nl-blackout-cal-legend { display:flex; flex-wrap:wrap; gap:16px; margin-bottom:4px; }
.nl-blackout-cal-legend span { display:inline-flex; align-items:center; gap:6px; }
.nl-blackout-cal-swatch { display:inline-block; width:12px; height:12px; border-radius:3px; }
.nl-blackout-cal-swatch.is-selectable { background:var(--nl-surface-raised); border:1px solid var(--nl-line); }
.nl-blackout-cal-swatch.is-blocked { background:var(--nl-bad-soft); border:1px solid var(--nl-bad); }
.nl-blackout-cal-swatch.is-past { background:var(--nl-surface); border:1px solid var(--nl-line); opacity:.5; }
.nl-blackout-cal { display:flex; flex-wrap:wrap; gap:24px; }
.nl-blackout-cal-month { flex:1 1 260px; min-width:240px; }
.nl-blackout-cal-month-label { margin:0 0 8px; font-size:13.5px; font-weight:600; color:var(--nl-paper); }
.nl-blackout-cal-grid { display:grid; grid-template-columns:repeat(7,1fr); gap:4px; }
.nl-blackout-cal-weekday { text-align:center; font-size:11px; color:var(--nl-faint); padding-bottom:2px; }
.nl-blackout-cal-day { display:flex; align-items:center; justify-content:center; aspect-ratio:1; border-radius:6px; font-size:12.5px; border:1px solid transparent; }
.nl-blackout-cal-day.is-other-month { visibility:hidden; }
.nl-blackout-cal-day.is-past { background:var(--nl-surface); color:var(--nl-faint); }
.nl-blackout-cal-day.is-blocked { background:var(--nl-bad-soft); color:var(--nl-bad); border-color:var(--nl-bad); }
label.nl-blackout-cal-day.is-selectable { background:var(--nl-surface-raised); border-color:var(--nl-line); cursor:pointer; position:relative; }
label.nl-blackout-cal-day.is-selectable:hover { border-color:var(--nl-info); }
.nl-blackout-cal-day.is-selectable input[type="checkbox"] { position:absolute; inset:0; margin:0; opacity:0; cursor:pointer; }
.nl-blackout-cal-day.is-selectable:has(input:checked) { background:var(--nl-info-soft); border-color:var(--nl-info); color:var(--nl-info); font-weight:600; }

@media (max-width:600px) {
  .nl-scope { padding:16px 14px 32px; }
  .nl-panel { padding:14px 15px; }
  .nl-detail-list > div { flex-direction:column; gap:2px; }
  .nl-detail-list dd { text-align:left; }
  /* A dd containing an inline-flex form (a <select> beside a save
     button, e.g. Collection status) can force the whole row past the
     viewport at narrow widths: a <select>'s min-content is its longest
     option text, which an inline-flex child never shrinks below
     without this -- same flex min-width:auto trap already fixed once
     for .nl-detail-grid, just one level deeper here. */
  .nl-detail-list dd form { flex-wrap:wrap; min-width:0; }
  .nl-detail-list dd select { min-width:0; max-width:100%; }
}

/* ==========================================================================
   Dashboard -- the fixed-slot block grid (dashboard_new.html). Its
   structural/widget classes (db-slot-…, db-block-inner, stat-tile-inner,
   db-jobstatus-…, mini-list, newsfeed-…, exchange-mail-…, rfp-…, qa-…,
   im-conv-row, dashboard-layout-picker) keep their old-look names
   completely unrenamed -- only colour/border/font are overridden here,
   scoped under .nl-scope, so dashboard_new.html's own ~700-line inline
   script (which selects purely by these same class names) needed no
   changes at all, and layout/sizing rules keep coming from style.css
   unchanged.
   .db-context-menu/.db-context-menu-item, .db-config-form and its whole
   "Configure" popup family (.db-config-form-heading/-title/-checkbox…/
   -reorder…), .db-block-configure/.db-block-expand and the block-expand
   modal's .modal-… chrome are deliberately left WITHOUT an override --
   they're the same "house style" already reused bare by
   repairs/list_new.html, worksheet_designer.html, dashboard_kiosk.html
   and monitored_devices/detail_new.html, so they render exactly as they
   do on those already-shipped pages today. .topnav-search-result/-empty
   (Quick actions' customer/job lookup) and .tm-presence-dot (Who's on)
   are also left alone -- both are already restyled by their own shared
   CSS (new_look_nav.css, _team_message_widget.html) independent of this
   page. .priority/.priority-* stays bare everywhere in the app.
   ========================================================================== */
.nl-scope .db-block-inner { background:var(--nl-surface); border-color:var(--db-border-color, var(--nl-line)); color:var(--nl-paper); }

/* Worksheet Designer's field-type picker chips, and the selection accent
   on its canvas -- worksheet_designer.js is unforked between the old and
   new page, and JS-builds these with bare old-look classes that were
   missed (unlike the Configure-popup family named above, these were
   never meant to stay old-look). The canvas's own dashed-border/black-text
   base look stays untouched on purpose -- it mirrors the real printed
   label, so only the selection/hover accent (never printed) moves from
   old-look's amber to this page's blue. */
.nl-scope .worksheet-palette-item { border-color:var(--nl-line); background:var(--nl-surface-raised); color:var(--nl-paper); }
.nl-scope .worksheet-palette-item:hover { border-color:var(--nl-brand); }
.nl-scope .worksheet-canvas .worksheet-field-resize-handle { background:var(--nl-brand); border-color:#fff; }
.nl-scope .worksheet-field-box:hover { border-color:var(--nl-brand); }
.nl-scope .worksheet-field-box-selected { border-color:var(--nl-brand); background:var(--nl-brand-soft); }

/* The dictation mic button (_voice_tools.html's shared attachDictation(),
   included globally from base.html and used by repairs/form_new.html,
   repairs/detail_new.html and customers/detail_new.html) -- JS toggles
   the bare old-look .mic-btn-active class with no nl- equivalent, so the
   "listening" pulse showed the old red/panel palette. */
.nl-scope .mic-btn { border-color:var(--nl-line); background:var(--nl-surface-raised); color:var(--nl-muted); }
.nl-scope .mic-btn:hover { border-color:var(--nl-brand-ring); color:var(--nl-paper); }
.nl-scope .mic-btn.mic-btn-active { background:var(--nl-bad-soft); border-color:var(--nl-bad); color:var(--nl-bad); }

/* The shared search-to-link combo box (repairs/form_new.html,
   repairs/detail_new.html, repairs/quick_quote_form_new.html,
   quote_engine/catalog_search_new.html, catalog/form_new.html) --
   deliberately unforked JS, but its bare .combo-result/.combo-results
   classes were never given a new-look override, so the dropdown popped
   up in the old look's grey/amber panel regardless of theme. */
.nl-scope .combo-results { background:var(--nl-surface-raised); border-color:var(--nl-line); box-shadow:0 12px 40px rgba(0,0,0,.45); }
.nl-scope .combo-result:hover, .nl-scope .combo-result.active { background:var(--nl-surface-hover); }

/* The blank-signature validation flash (signature_pad.js) -- style.css's
   own .signature-pad-missing border colour is old-look's red, with no
   new-look equivalent. Reuses the same warn token .nl-today-aging does
   for other border-colour-only error states. */
.nl-scope .signature-pad-missing { border-color:var(--nl-warn); }

/* contact_log.js's ?message_id= deep-link row highlight -- the same
   fade-highlight idiom as .attachment-camera-btn-highlight above, and
   the same fix: a bare @keyframes can't be scoped to .nl-scope, so a
   separately-named copy on the brand ring colour is swapped in instead
   of style.css's hard-coded amber one. */
.nl-scope .contact-thread-row-highlight { animation-name:nl-contact-thread-highlight-fade; }
@keyframes nl-contact-thread-highlight-fade {
  0% { box-shadow:0 0 0 3px var(--nl-brand-ring); border-radius:12px; }
  100% { box-shadow:0 0 0 0 transparent; border-radius:12px; }
}
.nl-scope .db-block-inner h2 { color:var(--nl-paper); }
.nl-scope .db-block-inner:hover { box-shadow:0 8px 20px rgba(0,0,0,.25); }
.nl-scope .db-slot-empty { border-color:var(--nl-line); }
.nl-scope .db-slot-add-btn { color:var(--nl-faint); }
.nl-scope .db-slot-add-btn:hover { color:var(--nl-brand); background:var(--nl-surface-hover); }

.nl-scope .dashboard-layout-picker button { border-color:var(--nl-line); color:var(--nl-muted); font-family:var(--nl-font-display); }
.nl-scope .dashboard-layout-picker button:hover { border-color:var(--nl-brand); color:var(--nl-paper); }
.nl-scope .dashboard-layout-picker button.active { background:var(--nl-brand); color:#fff; border-color:var(--nl-brand); }

.nl-scope .stat-num { color:var(--nl-paper); font-family:var(--nl-font-display); }
.nl-scope .stat-label { color:var(--nl-muted) !important; }
.nl-scope .stat-tile-inner.stat-warn .stat-num { color:var(--nl-warn); }
.nl-scope a.stat-tile-inner:hover { color:var(--nl-brand); }

.nl-scope .db-jobstatus-tile { background:var(--nl-surface-raised); color:var(--nl-paper); }
.nl-scope .db-jobstatus-tile:hover { color:var(--nl-brand); }
.nl-scope .db-jobstatus-count { color:var(--nl-paper); font-family:var(--nl-font-display); }
.nl-scope .db-jobstatus-label { color:var(--nl-muted) !important; }

.nl-scope .mini-list a { color:var(--nl-paper); text-decoration:none; }
.nl-scope .mini-list a:hover { text-decoration:underline; }
.nl-scope .mini-list li > strong { color:var(--nl-paper); }

.nl-scope .newsfeed-item { color:var(--nl-paper); border-bottom-color:var(--nl-line-soft) !important; }
.nl-scope .newsfeed-item:hover { background:var(--nl-surface-hover); color:var(--nl-paper); }
/* Only the :hover state was ever overridden here -- the idle "x" kept
   style.css's own explicit color:var(--text-faint), which (per this
   file's own "explicit beats inherited" rule above) wins over the
   .newsfeed-item parent's inherited nl-paper. */
.nl-scope .newsfeed-item-dismiss { color:var(--nl-faint); }
.nl-scope .newsfeed-item-dismiss:hover { color:var(--nl-paper); }
.nl-scope .newsfeed-empty { color:var(--nl-faint); }

.nl-scope .exchange-mail-row:hover { background:var(--nl-surface-hover); }
.nl-scope .exchange-mail-row-dot { background:var(--nl-info); }
.nl-scope .exchange-mail-row-subject, .nl-scope .exchange-mail-row-time { color:var(--nl-muted); }
.nl-scope .exchange-mail-row-sender { color:var(--nl-paper); }

.nl-scope .rfp-row { color:var(--nl-paper); }
.nl-scope .rfp-row-link { color:inherit; text-decoration:none; }
.nl-scope .rfp-row-link:hover { text-decoration:underline; }

.nl-scope .im-conv-row { color:var(--nl-paper); text-decoration:none; }
.nl-scope .im-conv-row:hover { text-decoration:underline; }

/* ==========================================================================
   Live Bench / Jobs board (bench_new.html, board_new.html). Both keep
   every bench- and board- prefixed structural and drag-engine class
   unrenamed --
   static/js/bench.js and static/js/board.js are the exact same,
   unforked files the old-look pages load, and several selectors query
   these classes directly rather than only data-* attributes (e.g.
   bench.js's positionHead()/insertNewPositionBlock()) -- so renaming
   would mean maintaining two class vocabularies in one script for no
   visual gain. Only colour/border/font are overridden here, scoped
   under .nl-scope; layout/sizing rules keep coming from style.css
   unchanged. _bench_macros.html and _bench_position_fragment.html (the
   inline-edit/inline-add JSON routes' response body) are reused as-is
   too -- a posted-back fragment carries no class of its own to fork,
   so it inherits this same styling once inserted into either page.
   .board-picker/-overlay/-title/-options/-other and .bench-toast/-error
   are deliberately left without an override -- the same shared "house
   style" already reused bare by repairs/detail_new.html,
   repairs/list_new.html and calendar/index.html. */
.nl-scope .bench-title { color:var(--nl-paper); }
.nl-scope .bench-blurb { color:var(--nl-muted) !important; }
.nl-scope .bench-legend { color:var(--nl-muted); }
.nl-scope .bench-edit-btn[aria-pressed="true"] { background:var(--nl-brand); color:#fff; border-color:transparent; }

.nl-scope .bench-edit-input { background:var(--nl-surface-raised); border-color:var(--nl-line); color:var(--nl-paper); }
/* The pencil-edit Save/Cancel buttons above are JS-built (bench.js) with
   the bare old-look .btn/.btn-primary/.btn-ghost classes, not .nl-btn --
   unlike .board-picker/.bench-toast above, these were never meant to stay
   old-look, just missed. Scoped to this one form rather than a blanket
   .nl-scope .btn, since .board-picker's own buttons a few lines up use
   the same bare classes and ARE meant to stay bare. */
.nl-scope .bench-inline-edit-form .btn-primary { background:var(--nl-brand); border-color:transparent; color:#fff; }
.nl-scope .bench-inline-edit-form .btn-primary:hover { filter:brightness(1.08); }
.nl-scope .bench-inline-edit-form .btn-ghost { background:transparent; border-color:var(--nl-line); color:var(--nl-paper); }
.nl-scope .bench-inline-edit-form .btn-ghost:hover { background:var(--nl-surface-hover); }
.nl-scope .bench-add-position-form { border-color:var(--nl-line); }
.nl-scope .bench-add-position-manage-all { color:var(--nl-muted); }

.nl-scope .bench-panel { background:var(--nl-surface); border-color:var(--nl-line); }
.nl-scope .bench-panel-title { color:var(--nl-muted) !important; }
.nl-scope .bench-dim { color:var(--nl-faint) !important; }
.nl-scope .bench-ready-count { color:var(--nl-good); }
.nl-scope .bench-position-head { color:var(--nl-muted); }
.nl-scope .bench-dropzone, .nl-scope .bench-free { border-color:var(--nl-line); color:var(--nl-faint); }
.nl-scope .bench-drop-hover, .nl-scope .bench-position.bench-drop-hover, .nl-scope .bench-panel.bench-drop-hover {
  border-color:var(--nl-info); background:var(--nl-info-soft);
}
.nl-scope .bench-drop-hover .bench-dropzone, .nl-scope .bench-drop-hover .bench-free { border-color:var(--nl-info); color:var(--nl-info); }

.nl-scope .bench-chip-quick { color:var(--nl-faint); }
.nl-scope .bench-chip-quick:hover, .nl-scope .bench-chip-quick:focus-visible { background:var(--nl-surface-hover); color:var(--nl-paper); }
.nl-scope .bench-chip { background:var(--nl-surface-raised); border-color:var(--nl-line); color:var(--nl-paper); }
.nl-scope .bench-chip-sub { color:var(--nl-muted); }
.nl-scope .bench-chip-fault { color:var(--nl-muted); }
.nl-scope .bench-ready { border-left-color:var(--nl-good); }
.nl-scope .bench-aging { border-color:var(--nl-warn); }
.nl-scope .bench-aging .bench-chip-sub { color:var(--nl-warn); }

.nl-scope .bench-loose-head { color:var(--nl-faint); }
.nl-scope .bench-unplaced-hint { color:var(--nl-faint); }

.nl-scope .bench-stat { color:var(--nl-muted); }
.nl-scope .bench-stat .bench-mono { color:var(--nl-paper); }
.nl-scope .bench-stat-aging, .nl-scope .bench-stat-aging .bench-mono { color:var(--nl-warn); }
.nl-scope .bench-stat-ready, .nl-scope .bench-stat-ready .bench-mono { color:var(--nl-good); }

.nl-scope .bench-owner-initials { border-color:var(--nl-line); color:var(--nl-faint); }

.nl-scope .board-lane { background:var(--nl-surface); border-color:var(--nl-line); }
.nl-scope .board-lane-title { color:var(--nl-muted) !important; }
.nl-scope .board-lane-empty { border-color:var(--nl-line); color:var(--nl-faint); }
.nl-scope .board-lane-hover { border-color:var(--nl-info); background:var(--nl-info-soft); }
.nl-scope .board-lane-hover .board-lane-empty { border-color:var(--nl-info); color:var(--nl-info); }
.nl-scope .board-card { background:var(--nl-surface-raised); border-color:var(--nl-line); color:var(--nl-paper); }
.nl-scope .board-card-sub { color:var(--nl-muted); }
.nl-scope .board-card-aging { border-color:var(--nl-warn); }

/* ==========================================================================
   Calendar (calendar/index_new.html). static/js/calendar.js is the
   exact same, unforked file the old-look page loads, and it selects
   the overwhelming majority of this page's classes directly (not just
   via data- attributes), so every cal- prefixed class here keeps its
   old-look name unchanged -- only colour/border/font are overridden,
   scoped under .nl-scope; layout/sizing rules keep coming from
   style.css unchanged. .board-picker family, .file-lightbox shells,
   .modal-close, .contact-icon-btn and .topnav-search-results/-empty
   are the same shared "house style" left bare elsewhere in this
   rollout -- no override here either. "Today" highlighting throughout
   (month cells, day columns, mini-month) uses --nl-brand rather than
   old-look's amber, matching the accent already used for every other
   "this one's active/current" state in this design system. */
/* var(--nl-ink), not var(--nl-surface) -- same "blend into the page
   like Google/Apple Calendar" request as style.css's own .cal-shell
   override (Dave, 2026-09-25); --nl-ink is .nl-scope's own page
   background token, so this makes the calendar flush with it exactly. */
.nl-scope .cal-shell { background:var(--nl-ink); border:1px solid var(--nl-line); }
.nl-scope .cal-toolbar { border-bottom-color:var(--nl-line-soft); }
.nl-scope .cal-range-label { color:var(--nl-paper); }
.nl-scope .cal-view-switch { background:var(--nl-surface-raised); }
.nl-scope .cal-view-switch-btn { color:var(--nl-muted); }
.nl-scope .cal-view-switch-btn:hover { color:var(--nl-paper); }
.nl-scope .cal-view-switch-btn.chip-active { background:var(--nl-surface); color:var(--nl-paper); }
.nl-scope .cal-search-input { background:var(--nl-surface-raised); border-color:var(--nl-line); color:var(--nl-paper); }

.nl-scope .cal-grid-panel { background:transparent; border:none; }

/* Month view */
.nl-scope .cal-month-head { color:var(--nl-faint); }
.nl-scope .cal-month-cell { border-color:var(--nl-line-soft); }
.nl-scope .cal-month-cell:hover { border-color:var(--nl-line); }
.nl-scope .cal-month-cell.cal-today { background:var(--nl-brand-soft); border-color:var(--nl-brand-ring); }
.nl-scope .cal-month-cell.cal-today .cal-month-daynum { background:var(--nl-brand); color:#fff; }
.nl-scope .cal-month-cell.cal-blackout { background:var(--nl-surface-raised); }
.nl-scope .cal-month-daynum { color:var(--nl-muted); }
.nl-scope .cal-month-daynum:hover { color:var(--nl-paper); }
.nl-scope .cal-more { color:var(--nl-faint); }
.nl-scope .cal-more:hover { color:var(--nl-paper); }

/* Event chips (month/week/day) -- colour comes from the --chip-color
   custom property JS sets per-event (chipColor() in calendar.js), so
   only the fallback and structural bits need overriding here. */
.nl-scope .cal-chip { --chip-color:var(--nl-info); color:var(--nl-paper); }
.nl-scope .cal-chip-time { color:var(--nl-muted); }
.nl-scope .cal-chip-cancelled .cal-chip-name { color:var(--nl-faint); }
.nl-scope .cal-chip-noshow { border-left-color:var(--nl-bad) !important; }
.nl-scope .cal-chip-noshow .cal-chip-name::after { color:var(--nl-bad); }

/* Week/day time grid */
.nl-scope .cal-col-heads .cal-col-head:last-child { border-right-color:var(--nl-line); }
.nl-scope .cal-col-head { color:var(--nl-muted); }
.nl-scope .cal-col-head.cal-today { color:var(--nl-brand); }
.nl-scope .cal-col-head-num { color:var(--nl-muted); }
.nl-scope .cal-col-head.cal-today .cal-col-head-num { background:var(--nl-brand); color:#fff; }
.nl-scope .cal-col-count { color:var(--nl-faint); }
.nl-scope .cal-time-tick { color:var(--nl-faint); }
/* var(--nl-ink), matching .nl-scope .cal-shell's own background token
   above -- style.css's own .cal-col rule falls back to plain
   var(--bg), which .nl-scope never redefines, so left alone this
   would have painted the grid body in the base theme's dark/light
   --bg rather than this scope's own --nl-ink, right back to the same
   two-colours-that-almost-but-don't-quite-match seam the first pass
   already fixed for .cal-shell. */
.nl-scope .cal-col { background:var(--nl-ink); border-color:var(--nl-line-soft); }
.nl-scope .cal-cols .cal-col:last-child { border-right-color:var(--nl-line); }
.nl-scope .cal-col.cal-today { background:var(--nl-brand-soft); }
.nl-scope .cal-hour-line { border-top-color:var(--nl-line-soft); }
.nl-scope .cal-now-line { background:var(--nl-bad); }
.nl-scope .cal-now-line::before { background:var(--nl-bad); }
.nl-scope .cal-shade { background:var(--nl-surface-raised); }

/* Drag -- valid/invalid drop feedback and the floating ghost copy */
.nl-scope .cal-drag-ghost { box-shadow:0 10px 24px rgba(0,0,0,.45); }
.nl-scope .cal-drag-invalid { border-left-color:var(--nl-bad) !important; }

/* Peek panel (appointment details slide-out) */
.nl-scope .cal-peek { background:var(--nl-surface); border-color:var(--nl-line); box-shadow:0 12px 40px rgba(0,0,0,.45); }
.nl-scope .cal-peek-when { color:var(--nl-muted); }
.nl-scope .cal-reschedule-form,
.nl-scope .cal-cancel-form { border-top-color:var(--nl-line-soft); }

/* Right-hand rail -- staff panel (moved from the left; the divider
   hairline moves from its right edge to its left edge to match) plus
   the two brand-new panels beside it. */
.nl-cal-side-stack { display:flex; flex-direction:column; gap:16px; flex:0 0 260px; }
.nl-scope .cal-staff-panel { flex:0 0 auto; }
.nl-scope .cal-staff-panel.cal-staff-panel-open {
  flex:0 0 auto; width:100%; border-right-color:transparent; border-left:1px solid var(--nl-line-soft);
  padding:2px 2px 2px 16px;
}
.nl-scope .cal-staff-panel-label { color:var(--nl-faint); }
.nl-scope .cal-staff-panel-bulk-link { color:var(--nl-muted); }
.nl-scope .cal-staff-panel-bulk-link:hover { background:var(--nl-surface-hover); color:var(--nl-paper); }
.nl-scope .cal-staff-panel-row:hover { background:var(--nl-surface-hover); }
.nl-scope .cal-mini-month { border-top-color:var(--nl-line-soft); }
.nl-scope .cal-mini-month-label { color:var(--nl-paper); }
.nl-scope .cal-mini-month-nav-btn { color:var(--nl-muted); }
.nl-scope .cal-mini-month-nav-btn:hover { background:var(--nl-surface-hover); color:var(--nl-paper); }
.nl-scope .cal-mini-month-dow { color:var(--nl-faint); }
.nl-scope .cal-mini-month-day { color:var(--nl-muted); }
.nl-scope .cal-mini-month-day:hover { background:var(--nl-surface-hover); }
.nl-scope .cal-mini-month-day.cal-mini-out-month { color:var(--nl-faint); }
.nl-scope .cal-mini-month-day.cal-mini-today { background:var(--nl-brand); color:#fff; }
.nl-scope .cal-mini-month-day.cal-mini-selected { border-color:var(--nl-brand); }

/* Upcoming appointments -- new, page-local; same row shape as the
   Today screen's .nl-today-row family, its own name since it's a
   plain list-item row rather than that primitive's bigger card look.
   The panel itself is deliberately NOT .nl-panel (no box/background) --
   a hairline top border instead, same treatment .cal-mini-month above
   already gives its own division from the staff list -- so this reads
   as one continuous sidebar, not a second, differently-styled card
   stacked under the first (Dave, 2026-09-25's "poor visual hierarchy"). */
.nl-scope .nl-cal-upcoming-panel { border-top: 1px solid var(--nl-line-soft); padding-top: 14px; }
.nl-cal-upcoming-list { display:flex; flex-direction:column; gap:8px; margin-top:8px; }
.nl-cal-upcoming-row { display:flex; align-items:center; gap:10px; padding:2px 0; }
/* .nl-scope-prefixed to beat .nl-scope a's own specificity -- same
   trap as .nl-btn/.nl-tab/.nl-today-row above. */
.nl-scope .nl-cal-upcoming-row { color:var(--nl-paper); text-decoration:none; }
.nl-scope .nl-cal-upcoming-row:hover { text-decoration:underline; }
.nl-cal-upcoming-when { flex:0 0 60px; font-size:12px; color:var(--nl-muted); font-variant-numeric:tabular-nums; }
.nl-cal-upcoming-main { flex:1; min-width:0; }
.nl-cal-upcoming-main strong { font-weight:600; display:block; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.nl-cal-upcoming-sub { color:var(--nl-faint); font-size:11.5px; display:block; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* calendar.js (the same unforked script the old-look page also loads)
   JS-builds several more bare old-look elements with no override anywhere
   -- unlike .board-picker above, none of these are on the "deliberately
   left bare" list, they were just missed:
   - the drag-move toast's Notify/Undo buttons (.cal-toast .btn*)
   - the waitlist panel's per-row Remove button and row divider
   - the "Find a time" suggestion chips (bare .chip, not .cal-chip)
   - the quick-create popover's category chips (.cal-qc-chip) */
.nl-scope .cal-toast .btn-primary { background:var(--nl-brand); border-color:transparent; color:#fff; }
.nl-scope .cal-toast .btn-primary:hover { filter:brightness(1.08); }
.nl-scope .cal-toast .btn-ghost, .nl-scope .cal-wl-row .btn-ghost { background:transparent; border-color:var(--nl-line); color:var(--nl-paper); }
.nl-scope .cal-toast .btn-ghost:hover, .nl-scope .cal-wl-row .btn-ghost:hover { background:var(--nl-surface-hover); }
.nl-scope .cal-wl-row { border-bottom-color:var(--nl-line-soft); }
.nl-scope [data-cal-suggestions] .chip { background:none; border-color:var(--nl-line); color:var(--nl-muted); }
.nl-scope [data-cal-suggestions] .chip:hover { border-color:var(--nl-brand-ring); color:var(--nl-paper); }
.nl-scope .cal-qc-chip { background:var(--nl-surface-raised); border-color:var(--nl-line); color:var(--nl-paper); }
.nl-scope .cal-qc-chip.selected { border-color:var(--nl-paper); background:var(--nl-surface); }
.nl-scope .cal-qc-chip-new { color:var(--nl-muted); }

@media (max-width:720px) {
  .nl-cal-side-stack { flex:1 1 100%; width:100%; }
  .nl-scope .cal-body { flex-wrap:wrap; }
}

/* ==========================================================================
   Public portal shell (base_public.html; templates/portal/account/*).
   base_public.html is shared by every portal page, converted or not,
   and its own background/brand header/flashes sit directly behind
   {% block content %} -- unlike base.html's nav (identical, JS-driven
   chrome on every staff page regardless of that page's own conversion
   status), recolouring any of that here would visibly affect portal
   pages that haven't been converted yet, since their own content is
   still old-look-classed. So base_public.html itself stays untouched
   beyond conditionally loading this stylesheet (harmless on its own --
   every rule here is scoped under .nl-scope, so it does nothing until
   a page actually uses it); each converted page instead wraps just its
   own {% block content %} output in a local "nl-scope nl-public-scope"
   div. .nl-public-scope makes that wrapper display:contents -- it
   generates no box of its own (none of .nl-scope's own big-dashboard-
   page background/padding/border-radius apply), so .card renders
   exactly as if it were still a direct child of .public-wrap, while
   its descendants still inherit .nl-scope's colour tokens and match
   every .nl-scope-prefixed rule (.nl-btn, .nl-form-stack, .nl-eyebrow,
   .nl-muted...) normally -- the entire existing primitive library
   reused as-is, no duplication. The shell's own bare classes (.card,
   .auth-card, .auth-footnote) keep their old-look names -- nothing
   JS-driven about them, this is the same "recolour, don't rename"
   approach used for every other purely-decorative old-look class left
   bare elsewhere in this rollout. .flash/.flash-* (the shared
   _flashes.html partial, rendered outside this wrapper) stay bare
   too, matching every other already-shipped new-look page. */
.nl-scope.nl-public-scope { display:contents; }
.nl-public-scope .card { background:var(--nl-surface); border-color:var(--nl-line); }
.nl-public-scope .auth-footnote { color:var(--nl-muted); }

/* Timeline -- shared bare component (also used by unconverted
   mail_overview.html, messages/inbox.html, access_requests/inbox.html and
   access_requests/detail.html), left unrenamed, colors overridden here the
   same way as every other confirmed-shared class this rollout. */
.nl-scope .timeline-item { border-left-color:var(--nl-line-soft); }
.nl-scope .timeline-marker { background:var(--nl-surface); border-color:var(--nl-faint); }
.nl-scope .timeline-item-visible .timeline-marker { border-color:var(--nl-info); background:var(--nl-info); }
.nl-scope .timeline-time { color:var(--nl-muted); }
.nl-scope .timeline-author { color:var(--nl-faint); }

/* Anonymous job-status tracker rail -- page-local to portal/status.html,
   not shared with any other template, so it's a real new component
   rather than an override of a bare shared class. */
.nl-tracker-rail { margin:18px 0 4px; }
.nl-tracker-stage { display:flex; gap:14px; }
.nl-tracker-dotcol { display:flex; flex-direction:column; align-items:center; }
.nl-tracker-dot { width:22px; height:22px; border-radius:50%; display:flex; align-items:center; justify-content:center; flex:none; }
.nl-tracker-done .nl-tracker-dot { background:var(--nl-good); color:var(--nl-surface); }
.nl-tracker-current .nl-tracker-dot { background:var(--nl-brand); animation:nl-tracker-pulse 2.2s ease-in-out infinite; }
.nl-tracker-todo .nl-tracker-dot { border:2px solid var(--nl-line); box-sizing:border-box; }
.nl-tracker-line { width:2px; flex:1; min-height:18px; background:var(--nl-line-soft); }
.nl-tracker-done .nl-tracker-line { background:var(--nl-good); }
.nl-tracker-body { padding-bottom:16px; min-width:0; flex:1; }
.nl-tracker-label { font-weight:600; font-size:14px; color:var(--nl-paper); }
.nl-tracker-current .nl-tracker-label { color:var(--nl-brand); }
.nl-tracker-todo .nl-tracker-label, .nl-tracker-todo .nl-tracker-sub { color:var(--nl-faint); }
.nl-tracker-when { font-size:12px; color:var(--nl-muted); }
.nl-tracker-sub { font-size:12.5px; color:var(--nl-muted); margin-top:2px; }
.nl-tracker-note { margin-top:8px; padding:10px 14px; background:var(--nl-surface-raised); border:1px solid var(--nl-line-soft);
  border-radius:0 10px 10px 10px; font-size:13.5px; line-height:1.5; max-width:60ch; }
.nl-tracker-photos { margin-top:14px; }
.nl-tracker-photo-strip { display:flex; gap:10px; overflow-x:auto; }
.nl-tracker-photo-strip a { flex:none; }
.nl-tracker-photo-strip img { height:84px; width:112px; object-fit:cover; border-radius:10px; border:1px solid var(--nl-line-soft); display:block; }
@keyframes nl-tracker-pulse { 0%,100% { box-shadow:0 0 0 0 var(--nl-brand-ring); } 50% { box-shadow:0 0 0 8px rgba(0,0,0,0); } }
