:root {
  --brand-maroon: #732034;
  --brand-bg: #FAF7F7;
  --brand-green: #198754;
  --brand-dark: #2c0d16;
  --brand-amber: #e67e22;
  --brand-red: #c0392b;
}

body { background-color: var(--brand-bg); padding: 10px; font-family: 'Montserrat', sans-serif; font-size: 14px; }
.hidden { display: none !important; }

.auth-card {
  max-width: 400px; margin: 50px auto; background: white;
  padding: 30px; border-radius: 12px; box-shadow: 0 10px 25px rgba(0,0,0,0.15);
  border-top: 6px solid var(--brand-maroon);
}
.auth-head { text-align: center; border-bottom: 3px solid var(--brand-maroon); padding-bottom: 15px; margin-bottom: 20px; }
.auth-title { font-weight: 800; color: var(--brand-maroon); font-size: 1.2rem; letter-spacing: 1px; }
.auth-sub { color: #aaa; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 1px; }

#appNav {
  max-width: 1400px; margin: 0 auto 0 auto; background: var(--brand-maroon);
  border-radius: 10px 10px 0 0; padding: 10px 16px; display: flex; align-items: center; gap: 12px;
}
#appNav .brand-title { color: #fff; font-weight: 800; font-size: 0.85rem; letter-spacing: 1px; }
#appNav .pb-page-title { color: rgba(255,255,255,0.85); font-weight: 700; font-size: 0.8rem; }
#appNav .pb-page-title::before { content: '›'; margin-right: 8px; opacity: 0.6; }
#appNav .nav-user { color: rgba(255,255,255,0.75); font-size: 0.75rem; margin-left: auto; }
#appNav .nav-access-link { color: #fff; font-size: 0.75rem; font-weight: 700; text-decoration: none; opacity: 0.9; }
#appNav .nav-access-link:hover { opacity: 1; text-decoration: underline; }

/* ---------- Home: big hub tiles (Demand / Supply / Distribution / Masters) ---------- */
#pbHomeCards { max-width: 960px; margin: 0 auto; display: grid; gap: 16px;
  grid-template-columns: repeat(4, 1fr); padding: 8px 0 30px; }
.hub-card {
  background: #fff; border: 1px solid #eadfe2; border-radius: 14px; padding: 22px 20px;
  cursor: pointer; transition: transform 0.15s, box-shadow 0.15s; box-shadow: 0 3px 12px rgba(0,0,0,0.06);
  border-left: 5px solid var(--brand-maroon); min-height: 150px; display: flex; flex-direction: column;
}
.hub-card:hover { transform: translateY(-3px); box-shadow: 0 8px 22px rgba(115,32,52,0.15); }
.hub-card .hub-ico { font-size: 2rem; color: var(--brand-maroon); margin-bottom: 8px; }
.hub-card .hub-name { font-size: 1.15rem; font-weight: 800; color: #2c2c2c; }
.hub-card .hub-desc { font-size: 0.8rem; color: #888; margin-top: 4px; line-height: 1.4; flex: 1; }
.hub-card.hub-disabled { cursor: not-allowed; opacity: 0.5; filter: grayscale(1); border-left-color: #bbb; box-shadow: none; transform: none; }
.hub-card.hub-disabled:hover { transform: none; box-shadow: none; }
.hub-card .hub-lock { font-size: 0.72rem; color: #b00; font-weight: 700; margin-top: 8px; }
@media (max-width: 520px) {
  #pbHomeCards { grid-template-columns: repeat(2, 1fr); gap: 8px; }
  .hub-card { min-height: 120px; padding: 16px 14px; }
  .hub-card .hub-ico { font-size: 1.6rem; }
  .hub-card .hub-name { font-size: 1rem; }
  .hub-card .hub-desc { font-size: 0.74rem; }
}

/* ---------- Hub view: breadcrumb + this hub's page tiles ---------- */
.pb-crumb { display: flex; align-items: center; gap: 6px; margin: 0 auto 16px; max-width: 960px; }
.pb-crumb-seg { font-size: 0.8rem; font-weight: 700; color: var(--brand-maroon); }
.pb-crumb-seg.pb-crumb-link { cursor: pointer; }
.pb-crumb-seg.pb-crumb-link:hover { text-decoration: underline; }
.pb-crumb-seg.pb-crumb-current { color: #2c2c2c; font-weight: 800; }
.pb-crumb-sep { opacity: 0.4; font-size: 0.8rem; }
/* B3 — declared breakpoints, never auto-fill. auto-fill derives the column count from the container
   width, so the same hub renders 4 tiles on one laptop and 6 on another and the layout cannot be
   described or tested. #pbHomeCards above is already explicit (repeat(4,1fr)); this matches it.
   1 phone -> 2 tablet -> 4 desk, stated. */
#pbHubPages { max-width: 960px; margin: 0 auto 24px; display: grid; gap: 12px;
  grid-template-columns: 1fr; }
@media (min-width: 600px) { #pbHubPages { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { #pbHubPages { grid-template-columns: repeat(4, 1fr); } }
.pb-page-tile {
  background: #fff; border: 1px solid #eadfe2; border-radius: 10px; padding: 16px;
  cursor: pointer; transition: transform 0.1s, box-shadow 0.1s; box-shadow: 0 2px 8px rgba(0,0,0,0.05);
  font-weight: 700; color: #2c2c2c; display: flex; align-items: center; gap: 10px;
  text-decoration: none;
}
.pb-page-tile:hover { transform: translateY(-2px); box-shadow: 0 6px 16px rgba(115,32,52,0.12); color: #2c2c2c; }
.pb-page-tile i { color: var(--brand-maroon); font-size: 1.2rem; }
.pb-empty-hub { max-width: 960px; margin: 0 auto 24px; text-align: center; color: #999; padding: 30px; }

.app-panel {
  max-width: 1400px; margin: 0 auto; background: white; padding: 20px;
  border-radius: 0 0 10px 10px; box-shadow: 0 5px 20px rgba(0,0,0,0.12); min-height: 500px;
}

.form-label { font-weight: 700; font-size: 0.72rem; text-transform: uppercase; color: #888; margin-bottom: 2px; }
/* ============================================================================================
   CONTROL SIZING — one rule set, so a button and an input of the same Bootstrap size are always the
   same height. Built up over 2026-08-23 after the Norms 1-2-3-4 strip shipped visibly ragged.

   The trap: a bare `padding` declaration in this file outranks Bootstrap's size classes — equal
   single-class specificity, and this file loads AFTER bootstrap.min.css. So `.btn-brand`'s padding
   beat `.btn-sm`, and `.form-control`'s beat `.form-control-sm`, and any row mixing a branded control
   with a plain small one came out unequal. Heights before this block existed:
       sized:    btn-brand 47 · form-control-sm 45 · plain btn-sm 31       (three values)
       unsized:  btn-brand 49 · form-control 45 · plain btn 37             (three values)

   Three rules fix it, and none of them hardcodes a height:
     1. --bs-btn-border-width: 2px  — buttons match the 2px border inputs already carry, so a sized
        button is not 2px shorter than a sized input.
     2. Our padding applies ONLY when no size class is present, so `-sm` / `-lg` get Bootstrap's own
        metrics and there is nothing to remember the next time one is used.
     3. Unsized buttons and unsized inputs share 10px VERTICAL padding — vertical is what sets height.
        Horizontal deliberately differs (16px on buttons) since a label wants more room than a field.

   Result: every sized control is 33px and every unsized control is 45px, across all 17 pages.
   ============================================================================================ */
.btn { --bs-btn-border-width: 2px; }
.btn:not(.btn-sm):not(.btn-lg) { padding: 10px 16px; }
.form-control, .form-select { border: 2px solid #e9ecef; font-weight: 600; }
.form-control:not(.form-control-sm):not(.form-control-lg),
.form-select:not(.form-select-sm):not(.form-select-lg) { padding: 10px; }
.form-control:focus, .form-select:focus { border-color: var(--brand-maroon); box-shadow: none; }
.btn-brand { background-color: var(--brand-maroon); border-color: var(--brand-maroon); color: white; font-weight: 800; text-transform: uppercase; }
.btn-brand:hover { background-color: #5a1828; color: white; }
.btn-brand:disabled { background-color: #bd9ba4; border-color: #bd9ba4; cursor: not-allowed; }

.select2-container .select2-selection--single { height: 38px !important; border: 2px solid #e9ecef; }
.select2-container--bootstrap-5 .select2-selection { font-size: 14px; font-weight: 600; }
.select2-container--bootstrap-5 .select2-selection--single .select2-selection__rendered {
  color: #212529; line-height: 36px; display: flex; align-items: center; height: 100%; padding: 0;
}
/* Multi-select filters (Masters' Category/Sub-Category/Lifecycle/Kitted) used to grow taller as more
   chips got selected, pushing the whole layout down. Cap the box and let the chip list scroll inside
   it instead (2026-08-16). */
.select2-container--bootstrap-5 .select2-selection--multiple {
  max-height: 38px; overflow-y: auto;
}

/* ---------- Planning grid ---------- */
.pb-toolbar { display: flex; gap: 16px; align-items: flex-end; margin-bottom: 18px; flex-wrap: wrap; }
.pb-sku-picker { min-width: 320px; flex: 1; }
.pb-summary-card {
  background: linear-gradient(135deg, var(--brand-dark) 0%, var(--brand-maroon) 100%);
  color: white; border-radius: 10px; padding: 14px 20px; margin-bottom: 16px;
  display: flex; align-items: center; gap: 24px; flex-wrap: wrap;
}
.pb-summary-card .pb-sku-name { font-size: 1rem; font-weight: 800; margin: 0; }
.pb-summary-card .pb-sku-code { font-size: 0.72rem; opacity: 0.75; margin: 0; }
.pb-summary-card .pb-meta { margin-left: auto; display: flex; gap: 20px; text-align: right; }
.pb-summary-card .pb-meta-num { font-size: 1.1rem; font-weight: 800; line-height: 1; }
.pb-summary-card .pb-meta-label { font-size: 0.62rem; opacity: 0.75; text-transform: uppercase; }

.pb-table-wrap { overflow-x: auto; border-radius: 8px; border: 1px solid #eee; }
table.pb-table { border-collapse: separate; border-spacing: 0; font-size: 0.78rem; white-space: nowrap; }
table.pb-table th, table.pb-table td { padding: 6px 10px; border-bottom: 1px solid #f0f0f0; text-align: right; }
table.pb-table thead th {
  background: var(--brand-maroon); color: white; font-weight: 700; font-size: 0.68rem;
  text-transform: uppercase; letter-spacing: 0.4px; position: sticky; top: 0; z-index: 2;
}
table.pb-table thead th.pb-week-past { background: #9c5f6d; }
table.pb-table .pb-row-label {
  text-align: left; font-weight: 700; color: #555; position: sticky; left: 0; background: #fff;
  z-index: 1; border-right: 2px solid #eee; min-width: 150px;
}
table.pb-table thead th.pb-row-label { background: var(--brand-maroon); color: #fff; z-index: 3; }
table.pb-table tbody tr.pb-section-head td { background: #faf7f7; font-weight: 800; color: var(--brand-maroon); font-size: 0.7rem; text-transform: uppercase; text-align: left; }
table.pb-table td.pb-past { color: #999; background: #fbfbfb; }
table.pb-table td.pb-future { color: #222; }
table.pb-table input.pb-input {
  width: 78px; text-align: right; border: 1.5px solid #e9ecef; border-radius: 4px; padding: 4px 6px;
  font-size: 0.78rem; font-weight: 700; color: var(--brand-dark);
}
table.pb-table input.pb-input:focus { border-color: var(--brand-maroon); outline: none; }
table.pb-table input.pb-input.pb-dirty { border-color: var(--brand-amber); background: #fff8ec; }
.pb-devi { font-size: 0.65rem; color: var(--brand-amber); font-weight: 700; }
.pb-pill { display: inline-block; padding: 2px 8px; border-radius: 12px; font-weight: 800; font-size: 0.72rem; }
.pb-pill.pb-ok { background: #e8f5e9; color: #1b5e20; }
.pb-pill.pb-warn { background: #fff3e0; color: #a65b00; }
.pb-pill.pb-bad { background: #ffeef0; color: var(--brand-red); }

.pb-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 14px; }

/* ---------- Planning Masters ---------- */
.masters-filters {
  display: grid; grid-template-columns: repeat(5, minmax(150px, 1fr)) auto;
  gap: 10px; margin-bottom: 14px; align-items: end;
}
@media (max-width: 900px) { .masters-filters { grid-template-columns: 1fr 1fr; } }
.masters-add-form {
  background: #faf7f7; border: 1px solid #eee; border-left: 4px solid var(--brand-maroon);
  border-radius: 8px; padding: 14px 16px; margin-bottom: 16px;
}
table.masters-table th, table.masters-table td { text-align: left; }
table.masters-table input, table.masters-table select {
  border: 1.5px solid #e9ecef; border-radius: 4px; padding: 3px 6px;
  font-size: 0.75rem; font-weight: 600; color: var(--brand-dark); background: #fff;
  min-width: 90px; max-width: 160px;
}
table.masters-table input:focus, table.masters-table select:focus { border-color: var(--brand-maroon); outline: none; }
table.masters-table input.pb-dirty, table.masters-table select.pb-dirty { border-color: var(--brand-amber); background: #fff8ec; }
table.masters-table input:disabled { background: #f4f4f4; color: #888; border-style: dashed; }
table.masters-table td.m-locked { position: relative; }
.access-role-chk { display: inline-flex; align-items: center; gap: 5px; font-size: 0.8rem; font-weight: 700; color: #555; margin: 0; }
.pb-legend { font-size: 0.68rem; color: #999; margin-top: 8px; }
.pb-legend span { display: inline-flex; align-items: center; gap: 4px; margin-right: 14px; }
.pb-legend .sw { width: 10px; height: 10px; border-radius: 2px; display: inline-block; }

/* ---------- Norms & Sourcing (STR build, Phase 1) ---------- */
.norms-subtab button.active { background-color: var(--brand-maroon); border-color: var(--brand-maroon); color: #fff; }
/* .norm-csv-report was the bespoke styling for eleven hand-rolled CSV validation previews. All
   twelve instances across the eight pages that carried them now use PBCommon.csvReport (.csvr),
   and no page references this class any more — verified by grep before deleting. csvReport also
   strips the class at runtime, so a page copy-pasted from an old one cannot resurrect it. */
table.masters-table input[type="date"] { min-width: 130px; }

/* ---------- SKU Master Bulk Upload staging grid (2026-08-16) ---------- */
table.masters-table tr.row-invalid { background: #ffeef0; }
table.masters-table .err-list { color: var(--brand-red); font-size: 0.7rem; }

/* ==========================================================================================
   Filter ergonomics (2026-08-23). See pb-common.js for why.
   The multi-select field is pinned to ONE line so it can never grow and reflow the toolbar; the
   selection count moves into the field's label, and the full selection is listed in the chips row
   below the toolbar.
   Scoped to --multiple so single-selects are untouched.
   ========================================================================================== */
.select2-container--bootstrap-5 .select2-selection--multiple,
.select2-container .select2-selection--multiple {
  min-height: 31px !important;
  max-height: 31px !important;
  overflow: hidden !important;
}
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered,
.select2-container .select2-selection--multiple .select2-selection__rendered {
  display: flex !important;
  flex-wrap: nowrap !important;
  overflow: hidden !important;
  max-height: 29px;
}
/* Each chip stays on ONE line and truncates. Without this the chip TEXT wraps inside the pinned
   31px box and gets sliced mid-word ("Raisin Dark", "Cl"), which looks broken even though the layout
   is now stable. The full, untruncated value is in the chips row below, so truncating here loses
   nothing. */
.select2-container .select2-selection--multiple .select2-selection__choice {
  white-space: nowrap !important;
  overflow: hidden !important;
  text-overflow: ellipsis !important;
  max-width: 15ch;
  display: inline-flex !important;
  align-items: center;
  line-height: 1.2;
}
.select2-container .select2-selection--multiple .select2-selection__choice__display {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* the chips row: the full selection, always visible, never part of the control layout */
.pb-filter-chips {
  display: flex; flex-wrap: wrap; gap: .35rem .75rem;
  margin: .4rem 0 .6rem 0; align-items: center;
}
.pb-chip-group { display: inline-flex; flex-wrap: wrap; gap: .25rem; align-items: center; }
.pb-chip-label {
  font-size: .72rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .02em; color: #6c757d; margin-right: .15rem;
}
.pb-chip {
  display: inline-flex; align-items: center; gap: .25rem;
  background: #f1f1f4; border: 1px solid #dcdce2; border-radius: 999px;
  padding: .08rem .5rem; font-size: .78rem; cursor: pointer; max-width: 320px;
}
.pb-chip:hover { background: #e6e6ec; }
.pb-chip i { font-size: .72rem; opacity: .55; }
.pb-chip:hover i { opacity: 1; }
.pb-chip-clear { font-size: .72rem; margin-left: .15rem; text-decoration: none; opacity: .7; }
.pb-chip-clear:hover { opacity: 1; text-decoration: underline; }

/* ---------- G7 — respect prefers-reduced-motion (WCAG 2.2 AA, 2.3.3) ----------
   The hub tiles and page tiles lift on hover (transform + shadow). For a user with a vestibular
   disorder that movement is not decoration, and the OS-level setting is how they say so. The hover
   STATE stays — only the motion goes — so the affordance is not lost, which is why this kills the
   transition and the translate rather than the whole :hover rule. */
@media (prefers-reduced-motion: reduce) {
  .hub-card, .pb-page-tile { transition: none; }
  .hub-card:hover, .pb-page-tile:hover { transform: none; }
}

/* ============================================================================================
   TABLE STANDARD — twt-sc-design group C. Added 2026-09-12 (Stage 2).

   The sticky header (C20) and the frozen identity column (.pb-row-label, C23) already existed in
   this file and were never the gap; the scanner said otherwise for a while because grep is
   line-based and those rules span three lines. What was genuinely missing across the portal:
   tabular figures, a loading state, sortable header affordances, and pagination.
   ============================================================================================ */

/* C25a — tabular figures. Proportional digits make a right-aligned numeric column ragged: 1,111 is
   visibly narrower than 8,888, so a column of quantities will not scan. Applied to every cell, not
   just numeric ones — it is a no-op on text and one rule is cheaper to keep true than a class. */
table.pb-table td, table.pb-table th,
table.masters-table td, table.masters-table th { font-variant-numeric: tabular-nums; }

/* C6 — a sortable header is a real <button>: keyboard reachable and announced as a control, which a
   th with an onclick is not. The arrow is ALWAYS visible (a hover-only affordance is undiscoverable
   and dead on touch), faint until the column is the active sort. Inherits the maroon header. */
table.pb-table thead th.pb-sortable { padding: 0; }
table.pb-table thead th.pb-sortable > button {
  width: 100%; background: none; border: 0; border-radius: 0; padding: 6px 10px; font: inherit;
  color: inherit; font-weight: 700; text-transform: uppercase; letter-spacing: 0.4px;
  font-size: 0.68rem; cursor: pointer; display: flex; align-items: center; gap: 5px;
  justify-content: flex-end;
}
table.masters-table thead th.pb-sortable > button,
table.pb-table thead th.pb-sortable.pb-left > button { justify-content: flex-start; }
table.pb-table thead th.pb-sortable > button:hover { background: rgba(255,255,255,0.12); }

/* Column alignment (tableView opts.align). These come AFTER .pb-left deliberately: they carry the
   same specificity, so source order is what lets an explicit alignment override the blanket
   left-push. One class drives the header button's justification AND the cell's text-align, which is
   what makes a header and its column incapable of disagreeing. */
table.pb-table th.pb-a-left,   table.pb-table td.pb-a-left   { text-align: left; }
table.pb-table th.pb-a-right,  table.pb-table td.pb-a-right  { text-align: right; }
table.pb-table th.pb-a-center, table.pb-table td.pb-a-center { text-align: center; }
table.pb-table thead th.pb-sortable.pb-a-left   > button { justify-content: flex-start; }
table.pb-table thead th.pb-sortable.pb-a-right  > button { justify-content: flex-end; }
table.pb-table thead th.pb-sortable.pb-a-center > button { justify-content: center; }

/* Totals row (tableView setTotal). Sticky to the bottom of the scroll box so the total stays
   readable while the rows scroll under it. */
table.pb-table tfoot tr.pb-total td {
  position: sticky; bottom: 0; background: #fbf9f9; font-weight: 700;
  border-top: 2px solid #e3dcde; z-index: 2;
}
table.pb-table thead th.pb-sortable .pb-arw { font-size: 0.6rem; opacity: 0.4; }
table.pb-table thead th.pb-sortable[aria-sort="ascending"] .pb-arw,
table.pb-table thead th.pb-sortable[aria-sort="descending"] .pb-arw { opacity: 1; }

/* C32 — loading is skeleton rows at the REAL column count, so the header and the column widths hold
   still. A spinner that replaces the table jumps the layout and takes away the headings the user was
   reading. Motion is opt-out per G7. */
.pb-skel { display: block; height: 0.6rem; border-radius: 4px; background: #e9e3e5; }
table.pb-table tbody tr.pb-skel-row td { padding: 7px 10px; }
@media (prefers-reduced-motion: no-preference) {
  .pb-skel { background: linear-gradient(90deg, #e9e3e5 25%, #f6f2f3 37%, #e9e3e5 63%);
    background-size: 400% 100%; animation: pbSkel 1.4s ease-in-out infinite; }
  @keyframes pbSkel { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
}

/* C13-C17 — the pager. C15's count line is mandatory and is never hidden: "Showing 51-100 of 1,204"
   is how a planner knows the 50 rows on screen are not the whole answer. */
.pb-pager { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 7px 10px;
  border-top: 1px solid #eee; background: #fbfafa; font-size: 0.72rem; color: #777; }
.pb-pager .pb-count { font-weight: 800; color: #444; }
.pb-pager .pb-grow { flex: 1; }
.pb-pager button { padding: 2px 9px; font-size: 0.72rem; }
.pb-pager select { padding: 1px 4px; font-size: 0.72rem; border-width: 1px; }

/* Editable cells owned by tableView. .pb-dirty already means "changed, not yet saved" everywhere in
   this app (masters/norms/sourcing all used it on their own inputs) — the contract reuses that
   vocabulary rather than inventing a second one, so the amber means the same thing on every screen. */
table.pb-table td > .pb-cell-edit,
table.masters-table td > .pb-cell-edit {
  width: 100%; border: 1px solid #e9ecef; border-radius: 4px; padding: 2px 5px;
  font: inherit; font-size: 0.78rem; background: #fff; color: #212529;
}
table.pb-table td > .pb-cell-edit:focus,
table.masters-table td > .pb-cell-edit:focus { border-color: var(--brand-maroon); outline: none; }
table.pb-table td > .pb-cell-edit.pb-dirty,
table.masters-table td > .pb-cell-edit.pb-dirty { border-color: var(--brand-amber); background: #fff8ec; }

/* ---------- MSEL — the multi-value filter control (twt-sc-design D1, 2026-09-13) ----------
   Values copied from normreport.html / productionreport.html, which already ship this control, so
   a converted page looks identical to the two that were already on it. The native <select multiple>
   it enhances stays in the DOM as the value store and is only hidden — see PBCommon.msel(). */
select.msel-native { display: none !important; }
.msel { position: relative; }
.msel-btn { width: 100%; text-align: left; padding: 6px 10px; border-radius: 6px; border: 1px solid #ced4da; background: #fff; font-weight: 600; font-size: .8rem; cursor: pointer; display: flex; justify-content: space-between; align-items: center; color: var(--brand-dark); min-height: 34px; }
.msel.open .msel-btn { border-color: var(--brand-maroon); }
.msel-btn:focus-visible { outline: 2px solid var(--brand-maroon); outline-offset: 1px; }
.msel-btn .ph { color: #8a8a8a; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msel-btn .ph.chosen { color: var(--brand-dark); font-weight: 600; }
.msel-count { background: var(--brand-maroon); color: #fff; font-size: .62rem; font-weight: 800; padding: 1px 7px; border-radius: 999px; margin-left: 8px; flex: none; }
.msel-pop { position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 60; background: #fff; border: 1px solid #eadfe2; border-radius: 8px; box-shadow: 0 10px 34px rgba(44,13,22,.18); max-height: 260px; display: flex; flex-direction: column; min-width: 200px; }
.msel-pop.hidden { display: none; }
.msel-search { margin: 8px; padding: 6px 8px; border: 1px solid #eadfe2; border-radius: 6px; font-size: .78rem; }
.msel-search:focus { border-color: var(--brand-maroon); outline: none; }
.msel-list { overflow-y: auto; padding: 0 4px 6px; }
/* D1 — options are semi-bold at .85rem in the ink colour. Never default-weight or muted grey: a
   muted option list reads as disabled, which is exactly what it must not read as. */
.msel-opt { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 6px; font-size: .85rem; font-weight: 600; color: var(--brand-dark); cursor: pointer; }
.msel-opt:hover { background: var(--brand-bg); }
.msel-opt input:focus-visible { outline: 2px solid var(--brand-maroon); outline-offset: 1px; }
.msel-empty { padding: 10px; color: #8a8a8a; font-size: .76rem; text-align: center; }
.msel-actions { border-top: 1px solid #eadfe2; padding: 6px 8px; text-align: right; }
.msel-actions button { background: none; border: none; color: var(--brand-maroon); font-weight: 700; font-size: .72rem; cursor: pointer; padding: 4px 6px; }
.msel-actions button:focus-visible { outline: 2px solid var(--brand-maroon); outline-offset: 1px; }

/* ---------- csvReport — the shared upload validation preview (2026-09-13) ----------
   Replaces eleven hand-rolled copies. The scroll box is the INNER .csvr-scroll, never the host:
   tableView anchors its pager to the nearest .pb-table-wrap, so a max-height on the host would
   scroll the pager out of sight exactly when the row count makes it necessary. */
.csvr-summary { font-size: 0.78rem; margin-bottom: 4px; }
.csvr-scroll { max-height: 320px; overflow-y: auto; }
/* Kept compact: a validation preview is scanned, not read, and these tables run to hundreds of
   rows. Everything else — the maroon sort header, the row striping — is the house table. */
.csvr table.pb-table { font-size: 0.74rem; }
.csvr table.pb-table th, .csvr table.pb-table td { padding: 4px 8px; }
.csvr table.masters-table tr.row-invalid { background: #ffeef0; }
.csvr .err-list { color: var(--brand-red); font-size: 0.72rem; }
