/* =====================================================================
   BoatDB2 shared components — UI overhaul Phase 0
   (Documents/UI_Overhaul_Implementation_Plan_v1.md)

   Every class consumes design tokens (--bd-*) only, so each component is
   automatically correct in both themes. Namespaced .bd- to coexist with
   legacy styles during the module-by-module migration; legacy classes
   are deleted per module in Phase 3.

   Living examples: /web/styleguide
   ===================================================================== */

/* ---------- base ---------- */
.bd-app {
  font-family: var(--bd-font);
  font-size: var(--bd-fs-body);
  color: var(--bd-text);
  line-height: 1.45;
  /* On a migrated page (body.bd-app) the backdrop follows the theme too, so
     dark mode is fully dark — not dark cards on a light-locked --color-bg.
     Non-migrated pages lack .bd-app and keep the light legacy background. */
  background: var(--bd-bg);
}
.bd-app *, .bd-app *::before, .bd-app *::after { box-sizing: border-box; }

/* ---------- badge: the one status system ---------- */
.bd-badge {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 11px; font-weight: 600; line-height: 1;
  padding: 4px 9px; border-radius: var(--bd-r-pill);
  white-space: nowrap;
}
.bd-badge::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.bd-badge--good    { color: var(--bd-good);    background: var(--bd-good-bg); }
.bd-badge--info    { color: var(--bd-info);    background: var(--bd-info-bg); }
.bd-badge--warn    { color: var(--bd-warn);    background: var(--bd-warn-bg); }
.bd-badge--crit    { color: var(--bd-crit);    background: var(--bd-crit-bg); }
.bd-badge--neutral { color: var(--bd-neutral); background: var(--bd-neutral-bg); }

/* ---------- buttons ---------- */
.bd-btn {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--bd-font); font-size: 12.5px; font-weight: 600;
  color: var(--bd-text); background: var(--bd-surface);
  border: 1px solid var(--bd-border); border-radius: var(--bd-r-sm);
  padding: 7px 13px; cursor: pointer; text-decoration: none;
  /* No colour transitions on token-driven components: Chromium keeps painting
     the OLD colour when a custom property flips on an ancestor (theme switch),
     so animated hover colour would break dark/light switching. Hover feedback
     is instant instead — correct in both themes. */
}
.bd-btn:hover { background: var(--bd-surface-2); border-color: var(--bd-muted); }
.bd-btn:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: 2px; }
.bd-btn--primary { background: var(--bd-accent); border-color: var(--bd-accent); color: #fff; }
.bd-btn--primary:hover { background: var(--bd-accent-strong); border-color: var(--bd-accent-strong); }
.bd-btn--danger { color: var(--bd-crit); border-color: var(--bd-crit); background: var(--bd-surface); }
.bd-btn--danger:hover { background: var(--bd-crit-bg); }
.bd-btn--small { padding: 4px 9px; font-size: 11.5px; }
.bd-btn[disabled], .bd-btn.is-disabled { opacity: 0.45; pointer-events: none; }

/* ---------- filter chips ---------- */
.bd-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.bd-chip {
  font-family: var(--bd-font); font-size: 11.5px; font-weight: 600;
  color: var(--bd-muted); background: var(--bd-surface);
  border: 1px solid var(--bd-border); border-radius: var(--bd-r-pill);
  padding: 5px 12px; cursor: pointer;
}
.bd-chip:hover { color: var(--bd-text); }
.bd-chip:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: 2px; }
.bd-chip.is-active { color: var(--bd-accent); background: var(--bd-accent-soft); border-color: transparent; }
.bd-chip__count { opacity: 0.65; font-weight: 500; margin-left: 3px; }

/* ---------- table (compact-first) ---------- */
.bd-table-wrap {
  overflow-x: auto;
  background: var(--bd-surface);
  border: 1px solid var(--bd-border); border-radius: var(--bd-r-md);
}
.bd-table { width: 100%; border-collapse: collapse; font-size: var(--bd-fs-body); }
.bd-table thead th {
  text-align: left; font-size: var(--bd-fs-micro); font-weight: 700;
  letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--bd-muted); background: var(--bd-surface-2);
  padding: 8px 12px; border-bottom: 1px solid var(--bd-border);
  white-space: nowrap;
}
.bd-table td {
  padding: 0 12px; height: var(--bd-row-h);
  border-bottom: 1px solid var(--bd-border); white-space: nowrap;
}
.bd-table tbody tr:last-child td { border-bottom: none; }
.bd-table tbody tr:hover { background: var(--bd-surface-2); }
/* A row that opens its record: whole row is the link (Enter/Space too). */
.bd-table tbody tr.is-link { cursor: pointer; }
.bd-table tbody tr.is-link:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: -2px; }
.bd-table .num { font-variant-numeric: tabular-nums; text-align: right; }
.bd-table .dim { color: var(--bd-muted); }
.bd-table .id { color: var(--bd-accent); font-weight: 600; }
/* A table whose cells hold inline controls (the boat's engines): controls
   fill their column, the size classes set the column, and a cell flagged
   has-error reddens its control. The row's error text sits under the name. */
.bd-table--controls td { padding-top: 6px; padding-bottom: 6px; }
.bd-table--controls .bd-input, .bd-table--controls .bd-select { width: 100%; box-sizing: border-box; }
.bd-table--controls .bd-col-sm { width: 96px; min-width: 96px; }
.bd-table--controls .bd-col-md { width: 170px; min-width: 140px; }
.bd-table--controls .bd-col-lg { width: 24%; min-width: 180px; }
.bd-table--controls .bd-col-act { width: 1%; text-align: right; }
.bd-table--controls td.has-error .bd-input, .bd-table--controls td.has-error .bd-select { border-color: var(--bd-crit); }
.bd-table--controls .bd-row__error { white-space: normal; }

/* ---------- engine kind tint — a category, NOT status ----------
   A row (or a legend swatch) says what kind of unit it is: inboard,
   outboard, drive / gearbox, generator. Faint ground + a 3px edge on the
   first cell; anything unclassified stays plain. Never a status signal. */
.bd-kind--inboard   { --bd-kind: var(--bd-kind-inboard);   --bd-kind-bg: var(--bd-kind-inboard-bg); }
.bd-kind--outboard  { --bd-kind: var(--bd-kind-outboard);  --bd-kind-bg: var(--bd-kind-outboard-bg); }
.bd-kind--drive     { --bd-kind: var(--bd-kind-drive);     --bd-kind-bg: var(--bd-kind-drive-bg); }
.bd-kind--generator { --bd-kind: var(--bd-kind-generator); --bd-kind-bg: var(--bd-kind-generator-bg); }
.bd-table tbody tr[class*="bd-kind--"] > td { background: var(--bd-kind-bg); }
.bd-table tbody tr[class*="bd-kind--"] > td:first-child { box-shadow: inset 3px 0 0 0 var(--bd-kind); }
.bd-kindkey { display: flex; flex-wrap: wrap; gap: 6px 14px; font-size: var(--bd-fs-caption); color: var(--bd-muted); }
.bd-kindkey__item { display: inline-flex; align-items: center; gap: 6px; }
.bd-kindkey__item::before {
  content: ""; width: 12px; height: 12px; border-radius: 3px;
  background: var(--bd-kind-bg); box-shadow: inset 3px 0 0 0 var(--bd-kind);
  border: 1px solid var(--bd-border);
}
/* A drag handle that is also a button, so rows move by keyboard
   (arrow up / down) as well as by drag. */
button.bd-drag-handle { background: none; border: 0; padding: 4px 0; font: inherit; }
button.bd-drag-handle:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: 1px; border-radius: 3px; }
/* wide tables (financial): first column stays put while the rest scroll */
.bd-table--first-sticky th:first-child,
.bd-table--first-sticky td:first-child {
  position: sticky; left: 0; background: var(--bd-surface); z-index: 1;
  box-shadow: 1px 0 0 var(--bd-border);
}
.bd-table--first-sticky thead th:first-child { background: var(--bd-surface-2); z-index: 2; }

/* ---------- card ---------- */
.bd-card {
  background: var(--bd-surface);
  /* its own ink with its own surface, so a card dropped into a legacy page
     (no body.bd-app) stays readable in dark mode */
  color: var(--bd-text);
  border: 1px solid var(--bd-border); border-radius: var(--bd-r-md);
  overflow: hidden;
}
.bd-card__head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--bd-sp-2);
  padding: 10px 14px; border-bottom: 1px solid var(--bd-border);
}
.bd-card__title {
  font-size: var(--bd-fs-micro); font-weight: 700;
  letter-spacing: 0.07em; text-transform: uppercase; color: var(--bd-muted);
}
.bd-card__body { padding: var(--bd-sp-4); }
.bd-card__body--flush { padding: 0; }
/* --inset: a card nested inside another card's body (a summary panel above
   the controls that change it). Sits on surface-2 so it reads as recessed
   rather than as a second top-level card. Stays in normal flow. */
.bd-card--inset { background: var(--bd-surface-2); }
.bd-card--inset > .bd-card__head { background: var(--bd-surface); }

/* ---------- key/value rows (summary rails) ---------- */
.bd-kv { display: flex; justify-content: space-between; gap: var(--bd-sp-3); padding: 7px 14px; border-bottom: 1px solid var(--bd-border); font-size: var(--bd-fs-caption); }
.bd-kv:last-child { border-bottom: none; }
.bd-kv__k { color: var(--bd-muted); }
.bd-kv__v { font-weight: 600; font-variant-numeric: tabular-nums; text-align: right; }

/* ---------- forms ---------- */
.bd-form { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--bd-sp-4); }
.bd-field { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.bd-field--span2 { grid-column: span 2; }
.bd-label { font-size: var(--bd-fs-caption); font-weight: 600; color: var(--bd-muted); }
.bd-label .req { color: var(--bd-crit); }
.bd-input, .bd-select, .bd-textarea {
  font-family: var(--bd-font); font-size: var(--bd-fs-body);
  color: var(--bd-text); background: var(--bd-surface);
  border: 1px solid var(--bd-border); border-radius: var(--bd-r-sm);
  padding: 7px 10px; width: 100%;
}
.bd-textarea { min-height: 74px; resize: vertical; }
.bd-input:focus, .bd-select:focus, .bd-textarea:focus {
  outline: none; border-color: var(--bd-accent);
  box-shadow: 0 0 0 3px var(--bd-accent-soft);
}
.bd-input::placeholder { color: var(--bd-muted); opacity: 0.8; }
.bd-field.has-error .bd-input, .bd-field.has-error .bd-select, .bd-field.has-error .bd-textarea { border-color: var(--bd-crit); }

/* Combo box — a lookup that behaves like a <select> (click the chevron for the
   full list) while staying typeable to filter long lists. */
.bd-combo { position: relative; flex: 1; min-width: 0; }
.bd-combo .bd-input { padding-right: 30px; }
.bd-combo__toggle {
  position: absolute; top: 0; right: 0; height: 100%; width: 28px;
  display: flex; align-items: center; justify-content: center;
  border: 0; background: none; cursor: pointer; padding: 0;
  color: var(--bd-muted); font-size: 11px; line-height: 1;
}
.bd-combo__toggle:hover { color: var(--bd-text); }
.bd-combo__toggle:disabled { cursor: not-allowed; opacity: 0.5; }
.bd-combo__panel {
  position: absolute; z-index: 40; top: calc(100% + 3px); left: 0; right: 0;
  max-height: 232px; overflow-y: auto;
  background: var(--bd-surface); border: 1px solid var(--bd-border);
  border-radius: var(--bd-r-sm); box-shadow: var(--bd-shadow-2);
  padding: 4px;
}
.bd-combo__panel.hidden { display: none; }
.bd-combo__option {
  padding: 6px 9px; border-radius: var(--bd-r-sm); cursor: pointer;
  font-size: var(--bd-fs-body); color: var(--bd-text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.bd-combo__option:hover, .bd-combo__option.is-active { background: var(--bd-accent-soft); }
.bd-combo__option[aria-selected="true"] { font-weight: 600; }
.bd-combo__empty { padding: 6px 9px; font-size: var(--bd-fs-caption); color: var(--bd-muted); }

/* Sortable table header — a button so it is keyboard reachable, styled to read
   as a plain header until hovered or active. */
.bd-sort {
  display: inline-flex; align-items: center; gap: 4px;
  border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; color: inherit; text-align: inherit;
}
.bd-sort--num { flex-direction: row-reverse; }
.bd-sort::after {
  content: '\2195';                       /* up-down arrow: sortable, unsorted */
  font-size: 10px; color: var(--bd-muted); opacity: 0;
}
.bd-sort:hover::after, .bd-sort:focus-visible::after { opacity: 0.6; }
[aria-sort="ascending"] .bd-sort::after { content: '\25B2'; opacity: 1; color: var(--bd-accent); }
[aria-sort="descending"] .bd-sort::after { content: '\25BC'; opacity: 1; color: var(--bd-accent); }
[aria-sort] .bd-sort { color: var(--bd-text); font-weight: 700; }

/* Pager — page-size selector, range readout and page buttons under a table. */
.bd-pager {
  display: flex; align-items: center; gap: var(--bd-sp-4); flex-wrap: wrap;
  padding: var(--bd-sp-3) 0 0;
}
.bd-pager[hidden] { display: none; }
.bd-pager__size { display: flex; align-items: center; gap: var(--bd-sp-2); }
.bd-pager__size .bd-select { width: auto; min-width: 72px; }
.bd-pager__info { font-size: var(--bd-fs-caption); color: var(--bd-muted); margin-right: auto; }
.bd-pager__nav { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.bd-pager__pages { display: flex; align-items: center; gap: 3px; }
.bd-pager__page {
  min-width: 30px; padding: 4px 7px; border-radius: var(--bd-r-sm);
  border: 1px solid var(--bd-border); background: var(--bd-surface);
  color: var(--bd-text); font-size: var(--bd-fs-caption); cursor: pointer;
}
.bd-pager__page:hover { border-color: var(--bd-accent); }
.bd-pager__page.is-current { background: var(--bd-accent); border-color: var(--bd-accent); color: #fff; font-weight: 600; }
.bd-pager__gap { padding: 0 2px; color: var(--bd-muted); font-size: var(--bd-fs-caption); }
.bd-pager .bd-btn[disabled] { opacity: 0.45; cursor: not-allowed; }
.bd-field__error { font-size: 11px; color: var(--bd-crit); }
.bd-form__actions { grid-column: 1 / -1; display: flex; justify-content: flex-end; gap: var(--bd-sp-2); }
/* Full-width name row: narrow title + two equal name fields */
.bd-form__namerow { grid-column: span 2; display: grid; grid-template-columns: 110px 1fr 1fr; gap: var(--bd-sp-4); }
/* Column stack: several fields occupying ONE cell of the form grid (left
   contact column vs right address column); top-aligned so uneven stacks
   don't stretch each other's fields apart. */
.bd-colstack { display: flex; flex-direction: column; gap: var(--bd-sp-4); min-width: 0; align-self: start; }
/* Phone input: dial-code select + national number */
.bd-phone { display: grid; grid-template-columns: 132px 1fr; gap: var(--bd-sp-2); }
/* Defined field-group box: a bordered container that groups related fields
   inside a form (e.g. contact details vs address on the account form). Sits
   in ONE cell of the .bd-form grid, like .bd-colstack but visually framed. */
.bd-fieldbox {
  border: 1px solid var(--bd-border); border-radius: var(--bd-r-md);
  padding: var(--bd-sp-4); display: flex; flex-direction: column;
  gap: var(--bd-sp-4); min-width: 0; align-self: start;
}
.bd-fieldbox__title {
  font-size: var(--bd-fs-caption); font-weight: 600; color: var(--bd-muted);
  text-transform: uppercase; letter-spacing: 0.04em;
}
/* Input with trailing button (e.g. postcode + Find address) */
.bd-input-group { display: flex; gap: var(--bd-sp-2); }
.bd-input-group .bd-input { flex: 1 1 auto; min-width: 0; }
/* A machine string to copy or compare: a token, a connector URL, a code.
   Usually readonly beside a Copy button in a .bd-input-group. */
.bd-input--mono { font-family: var(--bd-font-mono); font-size: var(--bd-fs-caption); letter-spacing: 0; }
/* One readable column for settings-style pages (My Profile): forms stop
   stretching across a wide screen. */
.bd-readable { max-width: 760px; }
.bd-field__hint { font-size: 11px; color: var(--bd-muted); }
@media (max-width: 700px) {
  .bd-form { grid-template-columns: 1fr; }
  .bd-field--span2 { grid-column: auto; }
  .bd-form__namerow { grid-column: auto; grid-template-columns: 1fr; }
}

/* ---------- page header ---------- */
.bd-pagehead {
  display: flex; align-items: center; gap: var(--bd-sp-3); flex-wrap: wrap;
  padding: var(--bd-sp-3) 0; margin-bottom: var(--bd-sp-3);
}
.bd-pagehead__title { font-size: var(--bd-fs-display); font-weight: 650; margin: 0; }
.bd-pagehead__spacer { flex: 1; }

/* ---------- record header (customer / boat / supplier pages) ---------- */
.bd-rechead {
  display: flex; align-items: center; gap: var(--bd-sp-3);
  padding: var(--bd-sp-4) var(--bd-sp-5);
  background: var(--bd-surface); border-bottom: 1px solid var(--bd-border);
}
.bd-rechead__avatar {
  width: 38px; height: 38px; border-radius: 50%; flex-shrink: 0;
  background: var(--bd-accent); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 700;
}
.bd-rechead__name { font-size: var(--bd-fs-title); font-weight: 650; }
.bd-rechead__meta {
  display: flex; gap: var(--bd-sp-2); align-items: center; flex-wrap: wrap;
  color: var(--bd-muted); font-size: var(--bd-fs-caption); margin-top: 2px;
}
.bd-rechead__actions { margin-left: auto; display: flex; gap: var(--bd-sp-2); }
.bd-recstats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--bd-sp-3); }
.bd-recstat { background: var(--bd-surface); border: 1px solid var(--bd-border); border-radius: var(--bd-r-md); padding: 11px 14px; }
.bd-recstat__value { font-size: 17px; font-weight: 700; font-variant-numeric: tabular-nums; }
.bd-recstat__value--crit { color: var(--bd-crit); }
.bd-recstat__label { font-size: var(--bd-fs-micro); font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase; color: var(--bd-muted); margin-top: 2px; }

/* ---------- tabs ---------- */
.bd-tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--bd-border); background: var(--bd-surface); padding: 0 var(--bd-sp-5); }
.bd-tab {
  font-family: var(--bd-font); font-size: 12.5px; font-weight: 600;
  color: var(--bd-muted); background: none; border: none; cursor: pointer;
  padding: 9px 12px; border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.bd-tab:hover { color: var(--bd-text); }
.bd-tab:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: -2px; }
.bd-tab.is-active { color: var(--bd-accent); border-bottom-color: var(--bd-accent); }

/* ---------- empty state ---------- */
.bd-empty {
  padding: 34px 20px; text-align: center;
  border: 1px dashed var(--bd-border); border-radius: var(--bd-r-md);
  color: var(--bd-muted); font-size: var(--bd-fs-body);
}
.bd-empty__title { font-weight: 650; color: var(--bd-text); margin-bottom: 4px; }

/* ---------- modal ---------- */
/* z-index must clear the fixed shell chrome (#bd-shell, 1100, 102px tall) —
   below it the workspace/module bars paint over the modal head and clip the
   title, because the veil's 9vh top padding lands inside those bars. */
.bd-modal-veil {
  position: fixed; inset: 0; z-index: 1200;
  background: rgba(10, 16, 23, 0.5);
  display: none; align-items: flex-start; justify-content: center;
  padding: 9vh 16px 16px;
}
.bd-modal-veil.is-open { display: flex; }
.bd-modal {
  width: 100%; max-width: 520px; max-height: 82vh;
  display: flex; flex-direction: column;
  background: var(--bd-surface); color: var(--bd-text);
  border: 1px solid var(--bd-border); border-radius: var(--bd-r-lg);
  box-shadow: var(--bd-shadow-2);
  font-family: var(--bd-font); font-size: var(--bd-fs-body);
}
.bd-modal--wide { max-width: 760px; }
/* Line-by-line work tables (receiving a delivery): nearly the whole screen,
   so every column fits without scrolling sideways. */
.bd-modal--xl { max-width: min(1280px, 96vw); max-height: 94vh; }
.bd-modal__head { display: flex; align-items: center; gap: var(--bd-sp-2); padding: 13px 18px; border-bottom: 1px solid var(--bd-border); }
.bd-modal__title { font-size: var(--bd-fs-title); font-weight: 650; margin: 0; flex: 1; }
.bd-modal__close {
  background: none; border: none; cursor: pointer; color: var(--bd-muted);
  font-size: 16px; line-height: 1; padding: 4px 6px; border-radius: var(--bd-r-sm);
}
.bd-modal__close:hover { color: var(--bd-text); background: var(--bd-surface-2); }
.bd-modal__body { padding: var(--bd-sp-4) 18px; overflow-y: auto; }
.bd-modal__foot { display: flex; justify-content: flex-end; gap: var(--bd-sp-2); padding: 12px 18px; border-top: 1px solid var(--bd-border); }

/* ---------- record QR code (BD.qr / [data-qr-src]) ----------
   A record's own QR code — customer account, boat. Always dark on white
   paper, in both themes: scanners need the contrast, and it is what prints. */
.bd-qr { display: flex; flex-direction: column; align-items: center; gap: var(--bd-sp-3); text-align: center; }
.bd-qr__code {
  width: 240px; max-width: 100%; height: auto;
  background: var(--bd-paper); padding: 12px;
  border: 1px solid var(--bd-border); border-radius: var(--bd-r-md);
}
.bd-qr__name { font-weight: 650; }
.bd-qr__hint { color: var(--bd-muted); }
/* Nothing in a QR modal is wider than the body, but at a fractional modal
   width Chromium can still paint an empty horizontal scrollbar under it. */
.bd-modal__body:has(> .bd-qr) { overflow-x: hidden; }

/* ---------- image viewer ----------
   A picture worth opening: a photo that can be enlarged sits in .bd-zoomable,
   which only signals that clicking does something. The viewer itself is an
   ordinary .bd-modal--wide; its body is a fixed-height frame the image is
   moved around inside, so zooming never resizes the dialog. The page sets
   `transform` on .bd-viewer__img as the viewer zooms and pans — scale and
   translate only, which the compositor handles without a repaint. No
   transition on it: at 4x a tweened zoom reads as lag, and a colour-carrying
   transition would not survive a theme flip anyway. */
.bd-zoomable { cursor: zoom-in; }
.bd-viewer__frame {
  position: relative; overflow: hidden;
  /* The dialog is capped at 82vh and the head, toolbar and padding take their
     share — keep the frame under that so the zoom controls stay in view on a
     short screen instead of pushing the body into a scrollbar. */
  height: min(58vh, 560px);
  background: var(--bd-surface-2);
  border-radius: var(--bd-r-md);
  display: flex; align-items: center; justify-content: center;
  touch-action: none;           /* drag pans the image, not the page */
}
.bd-viewer__img {
  max-width: 100%; max-height: 100%;
  transform-origin: center center;
  user-select: none; -webkit-user-drag: none;
  will-change: transform;
}
.bd-viewer__frame.is-zoomed { cursor: grab; }
.bd-viewer__frame.is-panning { cursor: grabbing; }
.bd-viewer__bar {
  display: flex; align-items: center; gap: var(--bd-sp-2);
  padding-top: var(--bd-sp-3);
}
.bd-viewer__level {
  font-variant-numeric: tabular-nums; color: var(--bd-muted);
  font-size: var(--bd-fs-caption); min-width: 48px;
}
.bd-viewer__hint { color: var(--bd-muted); font-size: var(--bd-fs-caption); margin-left: auto; }

/* ---------- photo gallery (bd_gallery.js) ----------
   Comment photos open full screen on a dark ground (dark in both themes — a
   photo reads best on black), with ‹ › and ← → stepping through every photo
   in the same thread and a thumbnail strip underneath. Sits above modals
   (1200) and below toasts (1400), so it opens over the job chat popup. */
.bd-gallery {
  position: fixed; inset: 0; z-index: 1300;
  background: rgba(6, 10, 15, 0.92);
  display: none; flex-direction: column;
  font-family: var(--bd-font); color: var(--bd-chrome-strong);
}
.bd-gallery.is-open { display: flex; }
.bd-gallery__top {
  flex: 0 0 auto; display: flex; align-items: center; gap: var(--bd-sp-3);
  padding: 12px 16px;
}
.bd-gallery__count { flex: 1 1 auto; font-size: var(--bd-fs-body); font-weight: 600; font-variant-numeric: tabular-nums; }
.bd-gallery__open { color: var(--bd-chrome-text); font-size: var(--bd-fs-caption); text-decoration: none; }
.bd-gallery__open:hover { color: var(--bd-chrome-strong); text-decoration: underline; }
.bd-gallery__close,
.bd-gallery__nav {
  flex: 0 0 auto; border: none; cursor: pointer; font-family: inherit;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--bd-chrome-strong); background: rgba(255, 255, 255, 0.12);
  border-radius: 50%;
}
.bd-gallery__close { width: 36px; height: 36px; font-size: 22px; line-height: 1; }
.bd-gallery__close:hover,
.bd-gallery__nav:hover { background: rgba(255, 255, 255, 0.24); }
.bd-gallery__close:focus-visible,
.bd-gallery__nav:focus-visible,
.bd-gallery__thumb:focus-visible { outline: 2px solid var(--bd-chrome-accent); outline-offset: 2px; }
.bd-gallery__stage {
  flex: 1 1 auto; min-height: 0; position: relative;
  display: flex; align-items: center; justify-content: center;
  padding: 0 72px;
}
.bd-gallery__img {
  max-width: 100%; max-height: 100%; object-fit: contain;
  border-radius: var(--bd-r-sm); user-select: none; -webkit-user-drag: none;
}
.bd-gallery__nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 48px; height: 48px; font-size: 30px; line-height: 1;
}
.bd-gallery__nav--prev { left: 14px; }
.bd-gallery__nav--next { right: 14px; }
.bd-gallery__nav.hidden { display: none; }
.bd-gallery__caption {
  flex: 0 0 auto; text-align: center; padding: 10px 16px 4px;
  font-size: var(--bd-fs-caption); color: var(--bd-chrome-text);
  min-height: 1.4em;
}
.bd-gallery__strip {
  flex: 0 0 auto; display: flex; gap: 6px; justify-content: safe center;
  overflow-x: auto; padding: 8px 16px 14px;
}
.bd-gallery__strip.hidden { display: none; }
.bd-gallery__thumb {
  flex: 0 0 auto; width: 56px; height: 56px; padding: 0; cursor: pointer;
  border: 2px solid transparent; border-radius: var(--bd-r-sm); overflow: hidden;
  background: rgba(255, 255, 255, 0.08); opacity: 0.55;
}
.bd-gallery__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.bd-gallery__thumb:hover { opacity: 0.85; }
.bd-gallery__thumb.is-active { opacity: 1; border-color: var(--bd-chrome-strong); }
@media (max-width: 640px) {
  .bd-gallery__stage { padding: 0 8px; }
  .bd-gallery__nav { width: 40px; height: 40px; font-size: 26px; background: rgba(0, 0, 0, 0.45); }
  .bd-gallery__nav--prev { left: 6px; }
  .bd-gallery__nav--next { right: 6px; }
}

/* ---------- toasts ---------- */
.bd-toasts {
  position: fixed; right: 16px; bottom: 16px; z-index: 1400;
  display: flex; flex-direction: column; gap: 8px; max-width: 360px;
}
.bd-toast {
  display: flex; align-items: center; gap: 10px;
  background: var(--bd-surface); color: var(--bd-text);
  border: 1px solid var(--bd-border); border-left: 3px solid var(--bd-neutral);
  border-radius: var(--bd-r-md); box-shadow: var(--bd-shadow-1);
  font-family: var(--bd-font); font-size: var(--bd-fs-body);
  padding: 10px 14px;
  animation: bd-toast-in 0.18s ease;
}
.bd-toast--good { border-left-color: var(--bd-good); }
.bd-toast--info { border-left-color: var(--bd-info); }
.bd-toast--warn { border-left-color: var(--bd-warn); }
.bd-toast--crit { border-left-color: var(--bd-crit); }
@keyframes bd-toast-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .bd-toast { animation: none; }
}

/* ---------- home / dashboard layout (Today page, module dashboards) ---------- */
.bd-home { max-width: 1200px; margin: 0 auto; }
.bd-home-greet { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin: 6px 0 4px; }
.bd-home-greet h1 { font-size: var(--bd-fs-display); font-weight: 650; margin: 0; }
.bd-home-greet .d { color: var(--bd-muted); font-size: var(--bd-fs-caption); }
.bd-kpis { display: flex; gap: 8px; flex-wrap: wrap; margin: 12px 0 18px; }
.bd-kpi {
  display: inline-flex; align-items: center; gap: 8px;
  background: var(--bd-surface); border: 1px solid var(--bd-border);
  border-radius: var(--bd-r-md); padding: 9px 14px;
  font-size: var(--bd-fs-caption); font-weight: 600; color: var(--bd-muted);
  text-decoration: none;
}
.bd-kpi:hover { border-color: var(--bd-muted); }
.bd-kpi .v { font-size: 17px; font-weight: 700; color: var(--bd-text); font-variant-numeric: tabular-nums; }
.bd-kpi--warn .v { color: var(--bd-warn); }
.bd-kpi--crit .v { color: var(--bd-crit); }
.bd-kpi--ok .v { color: var(--bd-accent); }
.bd-home-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 14px; align-items: start; }
/* Wide widgets (team board, clock-ins) span two grid tracks where there's room. */
.bd-card--span2 { grid-column: span 2; }
@media (max-width: 720px) { .bd-card--span2 { grid-column: auto; } }
.bd-listrow {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 14px; border-bottom: 1px solid var(--bd-border);
  font-size: var(--bd-fs-body); color: inherit; text-decoration: none;
}
.bd-listrow:last-child { border-bottom: none; }
a.bd-listrow:hover { background: var(--bd-surface-2); }
/* A whole row that is itself the action (picker results) — strip the native
   button chrome so it reads as a list row, not a control. */
button.bd-listrow {
  width: 100%; background: none; border: 0;
  border-bottom: 1px solid var(--bd-border); text-align: left; font: inherit;
}
button.bd-listrow:last-child { border-bottom: none; }
.bd-listrow .t { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-listrow .t b { font-weight: 650; }
.bd-listrow .meta { color: var(--bd-muted); font-size: 11.5px; white-space: nowrap; font-variant-numeric: tabular-nums; }
/* The record open in the editor beside a list (bd-split--list). */
.bd-listrow.is-selected { background: var(--bd-surface-2); box-shadow: inset 3px 0 0 var(--bd-accent); }
.bd-listrow .sub { display: block; color: var(--bd-muted); font-size: 11.5px; font-weight: 400; }
/* Settings rules (plan settings): one rule = a titled fieldset whose fields
   save as they change; a status tag says whether the system acts on it yet. */
.bd-rule { border-top: 1px solid var(--bd-border); padding: var(--bd-sp-3) 0; }
.bd-rule:first-child { border-top: 0; padding-top: 0; }
.bd-rule__head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: var(--bd-sp-2); }
.bd-rule__title { font-weight: 650; color: var(--bd-text); }
.bd-rule__help { color: var(--bd-muted); font-size: var(--bd-fs-caption); margin: 0 0 var(--bd-sp-2); }
.bd-rule__item { border: 1px solid var(--bd-border); border-radius: var(--bd-r-sm); padding: var(--bd-sp-3); margin-bottom: var(--bd-sp-2); }
.bd-checks { display: flex; flex-wrap: wrap; gap: 4px 14px; }
.bd-checks label { display: inline-flex; align-items: center; gap: 6px; font-size: var(--bd-fs-body); color: var(--bd-text); }
.bd-stripe { width: 3px; align-self: stretch; border-radius: 2px; flex-shrink: 0; }
.bd-listrow__actions { display: flex; gap: 6px; flex-shrink: 0; }

/* ---------- hovercard (rich hover detail) ----------
   One fixed-position card per page, filled and placed by JS on anchor hover
   (fixed so it escapes .bd-card overflow clipping). Body = .bd-kv rows.
   pointer-events none so the anchor keeps hover + click. Toggle .hidden. */
.bd-hovercard {
  position: fixed; z-index: 1200;
  min-width: 240px; max-width: 340px;
  background: var(--bd-surface);
  border: 1px solid var(--bd-border); border-radius: var(--bd-r-md);
  box-shadow: var(--bd-shadow-2);
  pointer-events: none;
}
.bd-hovercard__title {
  padding: 8px 14px 7px; border-bottom: 1px solid var(--bd-border);
  font-weight: 650; font-size: var(--bd-fs-body);
}
.bd-hovercard__title .meta { color: var(--bd-muted); font-weight: 400; }
/* Styleguide/demo only: render in flow instead of floating. */
.bd-hovercard--demo { position: static; box-shadow: var(--bd-shadow-1); }

/* Conditions widget: springs/neaps one-way cycle bar + attribution footer.
   The dot always travels left→right (from-phase → to-phase, labels swap at
   each peak); ticks mark the days of the ~7.4-day leg. */
.bd-cycle {
  display: flex; align-items: center; gap: var(--bd-sp-2);
  padding: var(--bd-sp-2) 0; font-size: var(--bd-fs-caption);
  color: var(--bd-muted);
}
.bd-cycle__track {
  position: relative; flex: 1; height: 4px;
  background: var(--bd-surface-2); border-radius: var(--bd-r-pill);
}
.bd-cycle__tick {
  position: absolute; top: 50%; transform: translate(-50%, -50%);
  width: 1px; height: 9px; background: var(--bd-border);
}
.bd-cycle__dot {
  position: absolute; top: 50%; transform: translate(-50%, -50%);
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--bd-info); border: 2px solid var(--bd-surface);
  transition: left 0.6s ease; /* position only — never colour (theme gotcha) */
}
/* ---------- weather icons + forecast popup (Conditions widget) ----------
   Icons are inline SVG composed of a sun/moon disc, a cloud and a precip
   motif; every part takes its colour from a --bd-wx-* token so both themes
   work. These are illustration, never status. */
.bd-wxicon { width: 26px; height: 26px; flex-shrink: 0; }
.bd-wxicon--lg { width: 34px; height: 34px; }
.bd-wxicon--sm { width: 20px; height: 20px; }
.bd-wx-sun { fill: var(--bd-wx-sun); }
.bd-wx-moon { fill: var(--bd-wx-moon); }
/* Unlit half of the moon-phase glyph — a disc the lit part is drawn over. */
.bd-wx-moondark { fill: var(--bd-surface-2); stroke: var(--bd-border); stroke-width: 1; }
.bd-cycle__moon { display: inline-flex; align-items: center; flex-shrink: 0; }
.bd-wx-cloud { fill: var(--bd-wx-cloud); }
.bd-wx-snow { fill: var(--bd-wx-snow); }
.bd-wx-bolt { fill: var(--bd-wx-bolt); }
.bd-wx-ray { fill: none; stroke: var(--bd-wx-sun); stroke-width: 2.2; stroke-linecap: round; }
.bd-wx-rain { fill: none; stroke: var(--bd-wx-rain); stroke-width: 2.2; stroke-linecap: round; }
.bd-wx-fog { fill: none; stroke: var(--bd-wx-cloud); stroke-width: 2.2; stroke-linecap: round; }

/* Card head: the day's icon stands in for the title. --stack puts the heading
   on its own line across the whole top with the actions grouped beneath it,
   so neither ever squeezes the other. */
.bd-card__actions { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.bd-card__head--stack { flex-direction: column; align-items: stretch; gap: var(--bd-sp-2); }
.bd-wxhead {
  display: inline-flex; align-items: center; gap: var(--bd-sp-2); align-self: flex-start;
  flex-wrap: wrap; text-align: left;
  padding: 0; border: none; background: none; cursor: pointer;
  font-family: var(--bd-font); color: var(--bd-text);
}
.bd-wxhead__lab { font-size: var(--bd-fs-caption); font-weight: 600; white-space: nowrap; }
.bd-wxhead:hover .bd-wxhead__lab { color: var(--bd-accent); }
/* Headline readouts beside the icon: temp · wind · rain, middot-separated. */
.bd-wxhead__stats {
  display: inline-flex; align-items: center; flex-wrap: wrap; gap: 6px;
  font-size: var(--bd-fs-caption); color: var(--bd-muted);
}
.bd-wxhead__stat { white-space: nowrap; font-variant-numeric: tabular-nums; }
.bd-wxhead__stat b { font-weight: 700; color: var(--bd-text); }
.bd-wxhead__stat + .bd-wxhead__stat::before { content: "·"; margin-right: 6px; }

.bd-wxsec {
  font-size: var(--bd-fs-micro); font-weight: 700; letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--bd-muted);
  margin: var(--bd-sp-3) 0 var(--bd-sp-2);
}
.bd-wxsec:first-child { margin-top: 0; }
/* Hour strip scrolls sideways; the page itself never does. */
.bd-wxhours {
  display: flex; align-items: stretch; gap: 2px; overflow-x: auto;
  border: 1px solid var(--bd-border); border-radius: var(--bd-r-sm);
  padding: var(--bd-sp-2);
}
.bd-wxhours__day {
  display: flex; align-items: center; flex-shrink: 0;
  padding: 0 var(--bd-sp-2); margin-right: var(--bd-sp-1);
  border-left: 2px solid var(--bd-border);
  font-size: var(--bd-fs-micro); font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--bd-muted); writing-mode: vertical-rl;
}
.bd-wxhours__day:first-child { border-left: none; }
.bd-wxhour {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  flex-shrink: 0; min-width: 62px; padding: var(--bd-sp-2) var(--bd-sp-1);
  border-radius: var(--bd-r-sm); font-size: var(--bd-fs-caption);
}
.bd-wxhour:hover { background: var(--bd-surface-2); }
.bd-wxhour__t { color: var(--bd-muted); font-variant-numeric: tabular-nums; }
.bd-wxhour__temp { font-weight: 700; font-variant-numeric: tabular-nums; }
.bd-wxhour__rain { color: var(--bd-wx-rain); font-size: var(--bd-fs-micro); font-variant-numeric: tabular-nums; }
.bd-wxhour__wind { color: var(--bd-muted); font-size: var(--bd-fs-micro); white-space: nowrap; }
.bd-wxhour__rain.is-dry, .bd-wxday__rain.is-dry { color: var(--bd-muted); }

.bd-wxdays { border: 1px solid var(--bd-border); border-radius: var(--bd-r-sm); }
.bd-wxday {
  display: grid; align-items: center; gap: var(--bd-sp-2);
  grid-template-columns: 8.5rem 26px 1fr auto 4.5rem 6rem;
  padding: 6px var(--bd-sp-3); border-bottom: 1px solid var(--bd-border);
  font-size: var(--bd-fs-caption);
}
.bd-wxday:last-child { border-bottom: none; }
.bd-wxday__name { font-weight: 600; }
.bd-wxday__lab { color: var(--bd-muted); }
.bd-wxday__temp { font-variant-numeric: tabular-nums; white-space: nowrap; }
.bd-wxday__rain { color: var(--bd-wx-rain); font-variant-numeric: tabular-nums; text-align: right; }
.bd-wxday__wind { color: var(--bd-muted); text-align: right; white-space: nowrap; }
@media (max-width: 640px) {
  .bd-wxday { grid-template-columns: 6.5rem 24px 1fr auto; }
  .bd-wxday__rain, .bd-wxday__wind { display: none; }
}

/* Synoptic chart modal: the proxied Met Éireann analysis PNG */
.bd-synoptic__img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--bd-border); border-radius: 6px;
  background: #fff; /* chart is drawn for paper — keep it legible in dark mode */
}
.bd-card__foot {
  margin-top: var(--bd-sp-2); padding-top: var(--bd-sp-2);
  border-top: 1px solid var(--bd-border);
  font-size: var(--bd-fs-micro); color: var(--bd-muted);
}
.bd-listrow--click { cursor: pointer; }
.bd-listrow--click:hover { background: var(--bd-surface-2); }
.bd-modal__toolbar {
  display: flex; justify-content: flex-end; margin-bottom: var(--bd-sp-2);
}

/* Today "Edit view": drag widgets to arrange. The grid container shows a
   faint alignment lattice; cards get dashed outlines and grab cursors, and
   their contents stop swallowing pointer events so the card itself drags. */
.bd-home-grid.is-editing {
  padding: var(--bd-sp-2);
  border-radius: var(--bd-r-md);
  background-image:
    repeating-linear-gradient(to right, var(--bd-surface-2) 0 1px, transparent 1px 24px),
    repeating-linear-gradient(to bottom, var(--bd-surface-2) 0 1px, transparent 1px 24px);
}
.bd-home-grid.is-editing .bd-card {
  outline: 2px dashed var(--bd-accent);
  outline-offset: 2px;
  cursor: grab;
  touch-action: none;
  user-select: none;
}
.bd-home-grid.is-editing .bd-card > * { pointer-events: none; }
/* The in-grid original while dragging: a dimmed slot showing where the
   widget will land. The ghost (below) is what follows the pointer. */
.bd-home-grid.is-editing .bd-card.is-dragging {
  opacity: 0.35;
  outline-style: solid;
  cursor: grabbing;
  pointer-events: none;
}
\* The card that follows the pointer while dragging. Drag machinery only —
   it is position:fixed, so putting it on a card in normal flow lifts that
   card out of the page and lands it on top of whatever is underneath. */
.bd-card--drag-ghost {
  position: fixed;
  z-index: 1000;
  margin: 0;
  overflow: hidden;
  pointer-events: none;
  opacity: 0.92;
  outline: 2px solid var(--bd-accent);
  box-shadow: var(--bd-shadow-2);
  cursor: grabbing;
}
/* Target-cell indicator while dragging: sized by its unit span to show
   exactly the footprint the widget will occupy. */
.bd-drop-slot {
  min-height: 24px;
  border: 2px dashed var(--bd-accent);
  border-radius: var(--bd-r-md);
  background: var(--bd-accent-soft);
  pointer-events: none;
}
.bd-home-edit-hint {
  font-size: var(--bd-fs-caption);
  color: var(--bd-muted);
}
/* Loading placeholders inside a widget frame while its data is in flight. */
.bd-skel {
  display: inline-block;
  height: 11px;
  width: 70%;
  border-radius: var(--bd-r-pill);
  background: var(--bd-surface-2);
  animation: bd-skel-pulse 1.2s ease-in-out infinite;
}
.bd-skel--short { width: 45%; }
@keyframes bd-skel-pulse {
  0%, 100% { opacity: 0.55; }
  50% { opacity: 1; }
}
.bd-seafcst {
  margin-top: var(--bd-sp-2); padding-top: var(--bd-sp-2);
  border-top: 1px solid var(--bd-border);
  font-size: var(--bd-fs-body);
}
.bd-seafcst__area { font-weight: 600; margin-bottom: var(--bd-sp-1); }
.bd-stripe--good { background: var(--bd-good); }
.bd-stripe--info { background: var(--bd-info); }
.bd-stripe--warn { background: var(--bd-warn); }
.bd-stripe--crit { background: var(--bd-crit); }

/* text-colour utilities (replace ad-hoc inline colour styles) */
.bd-muted { color: var(--bd-muted); }
.bd-text-crit { color: var(--bd-crit); }

/* Today customiser: right-aligned action + modal widget list */
.bd-home-customise { margin-left: auto; }
.bd-customise__row { display: flex; align-items: center; gap: 12px; padding: 7px 0; border-bottom: 1px solid var(--bd-border); }
.bd-customise__row:last-child { border-bottom: none; }
.bd-customise__label { flex: 1; display: flex; align-items: center; gap: 8px; font-size: var(--bd-fs-body); cursor: pointer; }
.bd-customise__moves { display: flex; gap: 6px; }
.bd-customise__pin { font-size: var(--bd-fs-micro); letter-spacing: 0.06em; text-transform: uppercase; color: var(--bd-muted); border: 1px solid var(--bd-border); border-radius: var(--bd-r-pill); padding: 1px 7px; }
.bd-customise__note { font-size: var(--bd-fs-caption); margin: 0 0 10px; }
/* Column picker (BD.columns): the same row idiom, made draggable. Scrolls
   inside the modal so a list page with twenty columns doesn't push the
   footer buttons off-screen. */
.bd-customise__list { max-height: 46vh; overflow-y: auto; }
.bd-customise__row[draggable="true"] { cursor: grab; }
.bd-customise__row.is-dragging { opacity: .45; }
.bd-customise__row.is-drop-above { box-shadow: inset 0 2px 0 0 var(--bd-accent); }
.bd-customise__row.is-drop-below { box-shadow: inset 0 -2px 0 0 var(--bd-accent); }
.bd-customise__row .bd-drag-handle { color: var(--bd-muted); }

/* ---------- filter bar (list pages) ---------- */
.bd-filterbar { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin: 0 0 12px; }
.bd-input--inline, .bd-select--inline { width: auto; min-width: 150px; }
/* Sized with width, not flex-basis: inside a COLUMN flex container a
   flex-basis is the main axis — i.e. the height — which turned this into a
   340px-tall box. width sizes the cross axis there and still resolves to the
   same flex-basis in the filterbar rows this is normally used in. */
.bd-input--search { width: 340px; min-width: 280px; max-width: 100%; }

/* ---------- type-ahead (search field with a result dropdown) ----------
   Wrap the input and the list in .bd-typeahead; the list floats over what
   follows instead of pushing the form around as results arrive. */
.bd-typeahead { position: relative; }
.bd-typeahead__list {
  position: absolute; left: 0; right: 0; top: 100%; z-index: 50;
  margin-top: 3px; padding: 4px; max-height: 220px; overflow-y: auto;
  background: var(--bd-surface); border: 1px solid var(--bd-border);
  border-radius: var(--bd-r-md); box-shadow: var(--bd-shadow-1);
}
.bd-typeahead__item {
  display: block; width: 100%; text-align: left; font: inherit;
  padding: 7px 12px; border: 0; background: none; color: var(--bd-text);
  border-radius: var(--bd-r-sm); cursor: pointer;
}
.bd-typeahead__item:hover, .bd-typeahead__item:focus { background: var(--bd-surface-2); }
.bd-typeahead__item .meta { color: var(--bd-muted); }

/* ---------- pick row (a whole search result is the button) ----------
   An inline search list where clicking anywhere on the row picks it — the
   trailing __cta pill is a cue, not the only target. --new is the
   "Add 'x' as a new …" row: dashed, accent ink. */
.bd-pickrow {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; text-align: left; font: inherit; color: var(--bd-text);
  padding: 9px 12px; border: 1px solid var(--bd-border); border-radius: var(--bd-r-sm);
  background: var(--bd-surface); box-shadow: var(--bd-shadow-1); cursor: pointer;
}
.bd-pickrow:hover, .bd-pickrow:focus-visible {
  border-color: var(--bd-accent); background: var(--bd-accent-soft); outline: none;
}
.bd-pickrow:active { transform: translateY(1px); }
.bd-pickrow__main { min-width: 0; flex: 1; }
.bd-pickrow__title { display: block; font-weight: 700; }
.bd-pickrow__meta { color: var(--bd-muted); font-size: var(--bd-fs-caption); }
.bd-pickrow__cta {
  flex: none; padding: 3px 12px; border-radius: 999px; font-size: var(--bd-fs-caption);
  font-weight: 600; color: var(--bd-accent); border: 1px solid var(--bd-accent);
}
.bd-pickrow:hover .bd-pickrow__cta, .bd-pickrow:focus-visible .bd-pickrow__cta {
  background: var(--bd-surface); border-color: var(--bd-accent-strong);
}
.bd-pickrow--new { border-style: dashed; color: var(--bd-accent); box-shadow: none; }

/* ---------- info note (muted callout) + legend swatch ---------- */
.bd-note {
  padding: 10px 13px; border-radius: var(--bd-r-sm);
  background: var(--bd-accent-soft); border: 1px solid var(--bd-border);
  font-size: var(--bd-fs-caption); color: var(--bd-muted); line-height: 1.6;
}
.bd-note strong { color: var(--bd-text); }
.bd-swatch {
  display: inline-block; width: 10px; height: 10px; border-radius: 2px;
  margin-right: 5px; vertical-align: middle;
}

/* ---------- vertical stack (replaces per-element margins between cards) ---------- */
.bd-stack { display: flex; flex-direction: column; gap: var(--bd-sp-4); }
/* Inside a stack the gap is the only spacing — a filter bar's own trailing
   margin would otherwise stack on top of it, whether the bar is a stack
   child or ends one of the stack's sections. */
.bd-stack > .bd-filterbar,
.bd-stack > * > .bd-filterbar:last-child { margin-bottom: 0; }
.bd-stack--tight { gap: var(--bd-sp-3); }

/* Inline action row — buttons with adjacent status text (e.g. Users →
   Mobile tab app-package uploader). Pair with .muted spans for the text. */
.bd-inline-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }

/* =====================================================================
   Schedule picker — technician availability calendar (schedule_picker.js)
   A reusable modal that lets you allocate a task's minutes across techs
   and days by clicking colour-coded availability cells. Per-tech identity
   colour is passed in via the --sp-tech custom property. All structural
   styling here; JS only sets --sp-tech + state-modifier classes.
   ===================================================================== */
.sp-overlay { position: fixed; inset: 0; background: rgba(0,0,10,.52); z-index: 1190; display: flex; align-items: flex-start; justify-content: center; overflow-y: auto; padding: 24px 12px; }
.sp-overlay.hidden { display: none; }
.sp-box { background: var(--bd-surface); border: 1px solid var(--bd-border); border-radius: 20px; padding: 18px 20px; width: min(680px,96vw); max-height: calc(100vh - 48px); margin: auto; overflow-y: auto; box-shadow: 0 16px 56px rgba(0,0,40,.22); display: flex; flex-direction: column; gap: 12px; }
.sp-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.sp-title { font-size: 1.05rem; font-weight: 800; color: var(--bd-text); margin: 0; }
.sp-sub { font-size: .8rem; color: var(--bd-muted); margin-top: 2px; }
.sp-close { appearance: none; border: 1px solid var(--bd-border); background: var(--bd-surface-2); color: var(--bd-muted); border-radius: 9px; padding: 4px 11px; cursor: pointer; font-size: .95rem; line-height: 1.4; flex-shrink: 0; }
.sp-close:hover { color: var(--bd-text); }

.sp-budget { display: flex; align-items: center; gap: 10px; }
.sp-budget-bar { flex: 1; height: 8px; border-radius: 999px; background: var(--bd-surface-2); overflow: hidden; display: flex; }
.sp-budget-fill { height: 100%; background: var(--bd-accent); }
.sp-budget-text { font-size: .78rem; color: var(--bd-muted); white-space: nowrap; }
.sp-budget-text b { color: var(--bd-text); font-weight: 700; }
.sp-budget--done .sp-budget-text, .sp-budget--done .sp-budget-text b { color: var(--bd-good); }
.sp-budget--done .sp-budget-fill { background: var(--bd-good); }
.sp-budget--over .sp-budget-text, .sp-budget--over .sp-budget-text b { color: var(--bd-warn); }
.sp-budget--over .sp-budget-fill { background: var(--bd-warn); }

.sp-techs { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sp-tech-chip { display: inline-flex; align-items: center; gap: 6px; font-size: .8rem; padding: 4px 10px; border-radius: 999px; border: 1px solid var(--bd-border); background: var(--bd-surface); color: var(--bd-muted); cursor: pointer; user-select: none; }
.sp-tech-chip .sp-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--sp-tech,#888); flex-shrink: 0; }
.sp-tech-chip.active { border-color: var(--sp-tech,var(--bd-accent)); color: var(--bd-text); background: var(--bd-surface-2); }

.sp-nav { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.sp-nav-center { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; justify-content: center; }
.sp-nav-label { font-size: .82rem; font-weight: 700; color: var(--bd-text); }
.sp-nav-avail { appearance: none; border: 1px solid var(--bd-border); background: var(--bd-surface); color: var(--bd-accent); border-radius: 8px; padding: 4px 10px; font-size: .76rem; font-weight: 700; cursor: pointer; white-space: nowrap; }
.sp-nav-avail:hover { border-color: var(--bd-accent); background: var(--bd-accent-soft); }
.sp-nav-avail:disabled { opacity: .6; cursor: default; }
.sp-nav-btn { appearance: none; border: 1px solid var(--bd-border); background: var(--bd-surface); color: var(--bd-muted); border-radius: 8px; width: 28px; height: 28px; cursor: pointer; font-size: .9rem; line-height: 1; flex-shrink: 0; }
.sp-nav-btn:hover { border-color: var(--bd-accent); color: var(--bd-text); }

.sp-hint { font-size: .74rem; color: var(--bd-muted); }
.sp-hint b { color: var(--bd-text); font-weight: 700; }

.sp-dow { display: grid; grid-template-columns: repeat(5,1fr); gap: 6px; font-size: .66rem; color: var(--bd-muted); text-transform: uppercase; letter-spacing: .04em; }
.sp-grid { display: grid; grid-template-columns: repeat(5,1fr); gap: 6px; }
.sp-week-gap { grid-column: 1 / -1; height: 2px; }

.sp-cell { position: relative; height: 58px; border-radius: 8px; border: 1px solid var(--bd-border); background: var(--bd-surface); padding: 5px 7px; overflow: hidden; font-size: .78rem; }
.sp-cell-num { position: relative; font-weight: 700; color: var(--bd-text); display: flex; align-items: center; justify-content: space-between; line-height: 1.1; }
.sp-cell-free { position: relative; margin-top: 15px; font-size: .7rem; color: var(--bd-muted); }
.sp-tri { position: absolute; inset: 0; background: var(--sp-tech,#888); clip-path: polygon(100% 0,100% 100%,0 100%); opacity: .85; }
.sp-cell--selectable { cursor: pointer; }
.sp-cell--selectable:hover { border-color: var(--bd-accent); }
.sp-cell--full { background: var(--sp-tech,#888); border-color: transparent; cursor: not-allowed; }
.sp-cell--full .sp-cell-num, .sp-cell--full .sp-cell-free { color: #fff; }
.sp-cell--off { background: repeating-linear-gradient(45deg, var(--bd-surface-2), var(--bd-surface-2) 4px, var(--bd-surface) 4px, var(--bd-surface) 8px); cursor: not-allowed; }
.sp-cell--off .sp-cell-num { color: var(--bd-muted); font-weight: 500; }
.sp-cell--selected { border: 2px solid var(--bd-accent); }
.sp-cell--thistask { box-shadow: inset 0 0 0 2px var(--bd-warn); }
.sp-cell[draggable="true"] { cursor: grab; }
.sp-cell[draggable="true"]:active { cursor: grabbing; }
.sp-cell--dragover { border-color: var(--bd-accent) !important; box-shadow: inset 0 0 0 2px var(--bd-accent); }
.sp-cell-anydot { color: var(--bd-warn); font-size: .8rem; line-height: 1; }

/* Compact variant for the multi-tech compare grid */
.sp-cmp { display: grid; gap: 4px; align-items: center; }
.sp-cmp-corner { }
.sp-cmp-dow { text-align: center; font-size: .6rem; color: var(--bd-muted); line-height: 1.25; }
.sp-cmp-dow.sp-common { color: var(--bd-good); font-weight: 700; }
.sp-cmp-name { font-size: .72rem; font-weight: 700; display: flex; align-items: center; gap: 5px; color: var(--sp-tech,var(--bd-text)); }
.sp-cmp-name .sp-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--sp-tech,#888); flex-shrink: 0; }
.sp-cell--compact { height: 32px; padding: 0; display: flex; align-items: center; justify-content: center; border-radius: 5px; }
.sp-cell--compact .sp-cmp-h { position: relative; font-size: .64rem; font-weight: 700; color: var(--bd-text); }
.sp-cell--compact.sp-cell--full .sp-cmp-h { color: #fff; }

.sp-legend { display: flex; gap: 12px; flex-wrap: wrap; font-size: .66rem; color: var(--bd-muted); align-items: center; }
.sp-legend-item { display: inline-flex; align-items: center; gap: 5px; }
.sp-swatch { width: 13px; height: 13px; border-radius: 3px; border: 1px solid var(--bd-border); flex-shrink: 0; }
.sp-swatch--free { background: var(--bd-surface); }
.sp-swatch--part { background: linear-gradient(135deg, var(--bd-muted) 50%, transparent 50%); }
.sp-swatch--full { background: var(--bd-muted); border-color: transparent; }
.sp-swatch--off { background: repeating-linear-gradient(45deg, var(--bd-surface-2), var(--bd-surface-2) 3px, var(--bd-surface) 3px, var(--bd-surface) 6px); }
.sp-swatch--task { background: var(--bd-surface); box-shadow: inset 0 0 0 2px var(--bd-warn); }

.sp-alloc { border-top: 1px solid var(--bd-border); padding-top: 12px; display: flex; flex-direction: column; gap: 8px; }
.sp-alloc.hidden { display: none; }
.sp-alloc-title { font-size: .85rem; font-weight: 700; color: var(--bd-text); }
.sp-alloc-title .sp-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; background: var(--sp-tech,var(--bd-accent)); margin-right: 5px; vertical-align: middle; }
.sp-alloc-sub { font-size: .76rem; color: var(--bd-muted); }
.sp-alloc-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.sp-alloc-row label { font-size: .74rem; color: var(--bd-muted); }
.sp-alloc-row .input { width: 88px; }
.sp-flag { font-size: .74rem; padding: 3px 8px; border-radius: 6px; }
.sp-flag--good { color: var(--bd-good); background: var(--bd-good-bg); }
.sp-flag--warn { color: var(--bd-warn); background: var(--bd-warn-bg); }
.sp-flag--crit { color: var(--bd-crit); background: var(--bd-crit-bg); }
.sp-alloc-actions { display: flex; gap: 8px; margin-top: 2px; align-items: center; }

.sp-section-label { font-size: .7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--bd-muted); }
.sp-placed { display: flex; flex-direction: column; gap: 5px; }
.sp-placed-row { display: flex; align-items: center; gap: 8px; font-size: .78rem; background: var(--bd-surface-2); border: 1px solid var(--bd-border); border-radius: 8px; padding: 5px 9px; }
.sp-placed-row--clickable { cursor: pointer; }
.sp-placed-row--clickable:hover { border-color: var(--bd-accent); }
.sp-placed-row--active { border-color: var(--bd-accent); background: var(--bd-accent-soft); }
.sp-placed-row .sp-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--sp-tech,#888); flex-shrink: 0; }
.sp-placed-when { flex: 1; color: var(--bd-text); }
.sp-placed-remove { appearance: none; border: 0; background: transparent; color: var(--bd-muted); cursor: pointer; font-size: .85rem; border-radius: 4px; padding: 0 5px; }
.sp-placed-remove:hover { color: var(--bd-crit); background: var(--bd-crit-bg); }
.sp-empty { font-size: .8rem; color: var(--bd-muted); font-style: italic; padding: 8px 0; }

/* --- Hub sections (Administration / Settings landing pages) ---------------
   Collapsible department/area groups built on native <details>. Summary is
   the section heading; body holds .bd-listrow links. hub.js handles hash
   deep-links (#section-id opens + scrolls) and remembered open state. */
.bd-hub { display: flex; flex-direction: column; gap: 12px; max-width: 900px; }
/* Full page width, for grouped work lists (the workshop orders view). */
.bd-hub--full { max-width: none; }
.bd-hub-section {
  border: 1px solid var(--bd-border); border-radius: 12px;
  background: var(--bd-surface); overflow: hidden;
}
.bd-hub-section > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: flex; align-items: center; gap: 12px; padding: 14px 18px;
}
.bd-hub-section > summary::-webkit-details-marker { display: none; }
.bd-hub-section > summary:hover { background: var(--bd-surface-2); }
.bd-hub-section > summary:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: -2px; }
.bd-hub-section__chev {
  color: var(--bd-muted); font-size: 13px; line-height: 1; flex-shrink: 0;
  transition: transform .15s ease;
}
.bd-hub-section[open] > summary .bd-hub-section__chev { transform: rotate(90deg); }
.bd-hub-section__title { font-size: var(--bd-fs-title); font-weight: 650; color: var(--bd-text); }
.bd-hub-section__desc {
  flex: 1; min-width: 0; color: var(--bd-muted); font-size: var(--bd-fs-body);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bd-hub-section__body { border-top: 1px solid var(--bd-border); }
.bd-hub-section__body .bd-listrow { padding-left: 18px; padding-right: 18px; }
/* Not-yet-built entries: same row shape, muted, non-interactive. */
.bd-listrow--soon { color: var(--bd-muted); cursor: default; }
.bd-listrow--soon .t b { font-weight: 600; color: var(--bd-muted); }

/* ---------- engine service check sheet (task popup fill-in + builder) ----------
   Marks use the five semantic slots only: done=good, see-job-card=warn, N/A=neutral. */
.bd-checksheet-head {
  display: flex; flex-wrap: wrap; gap: 4px 14px; align-items: baseline;
  font-size: 12px; color: var(--bd-muted);
  padding: 6px 0 8px; border-bottom: 1px solid var(--bd-border);
}
.bd-checksheet-head__engine { font-weight: 650; color: var(--bd-text); font-size: 13px; }
.bd-checksheet-item {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 8px 0; border-bottom: 1px solid var(--bd-border);
}
.bd-checksheet-item__code {
  flex-shrink: 0; width: 22px; height: 22px; margin-top: 1px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 700; color: var(--bd-muted);
  border: 1px solid var(--bd-border); border-radius: var(--bd-r-sm);
  background: var(--bd-surface-2);
}
.bd-checksheet-item__body { flex: 1; min-width: 0; }
.bd-checksheet-item__desc { font-size: 13px; color: var(--bd-text); }
.bd-checksheet-item.is-done .bd-checksheet-item__desc { text-decoration: line-through; opacity: .55; }
.bd-checksheet-item__rule {
  font-size: 11px; color: var(--bd-warn); font-weight: 600;
  text-transform: uppercase; letter-spacing: .02em; margin-top: 2px;
}
.bd-checksheet-item__meta { font-size: 11px; color: var(--bd-muted); margin-top: 2px; }
.bd-checksheet-marks { display: flex; gap: 4px; flex-shrink: 0; }
.bd-checksheet-mark {
  min-width: 30px; height: 26px; padding: 0 7px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 700; cursor: pointer;
  color: var(--bd-muted); background: var(--bd-surface);
  border: 1px solid var(--bd-border); border-radius: var(--bd-r-sm);
}
.bd-checksheet-mark:hover { background: var(--bd-surface-2); }
.bd-checksheet-mark:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: 1px; }
.bd-checksheet-mark.is-active--done { color: var(--bd-good); background: var(--bd-good-bg); border-color: transparent; }
.bd-checksheet-mark.is-active--see  { color: var(--bd-warn); background: var(--bd-warn-bg); border-color: transparent; }
.bd-checksheet-mark.is-active--na   { color: var(--bd-neutral); background: var(--bd-neutral-bg); border-color: transparent; }
.bd-checksheet-comment { margin-top: 6px; }
.bd-checksheet-comment textarea {
  width: 100%; min-height: 34px; resize: vertical;
  font: inherit; font-size: 12px; color: var(--bd-text);
  background: var(--bd-surface); border: 1px solid var(--bd-border);
  border-radius: var(--bd-r-sm); padding: 5px 8px;
}
.bd-checksheet-comment textarea:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: 0; }
/* Engine details block heading the sheet: make/model, serial, hours, confirm. */
.bd-checksheet-engine {
  display: flex; flex-direction: column; gap: var(--bd-sp-2);
  margin: 8px 0 4px; padding: var(--bd-sp-3);
  border: 1px solid var(--bd-border); border-radius: var(--bd-r-md);
  background: var(--bd-surface-2);
}
.bd-checksheet-engine__title {
  font-size: var(--bd-fs-caption); font-weight: 600; color: var(--bd-muted);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.bd-checksheet-engine__grid {
  display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--bd-sp-3); align-items: start;
}
.bd-checksheet-engine__value { font-size: 13px; font-weight: 650; color: var(--bd-text); padding-top: 7px; }
.bd-checksheet-engine__warn { color: var(--bd-warn); }
.bd-checksheet-engine__confirm {
  display: flex; align-items: center; gap: 8px;
  font-size: 13px; color: var(--bd-text); cursor: pointer;
}
@media (max-width: 700px) {
  .bd-checksheet-engine__grid { grid-template-columns: 1fr; }
}

/* ---------- check-sheet template builder grid (engines settings) ----------
   Columns = template intervals, rows = service operations. Cell states:
   dash = not applicable at that interval, R/I/T = action code, dot = present
   with no code (record-only). */
.bd-sheetgrid-wrap { overflow-x: auto; }
.bd-sheetgrid { border-collapse: collapse; width: 100%; font-size: 13px; }
.bd-sheetgrid th, .bd-sheetgrid td {
  border: 1px solid var(--bd-border); padding: 4px 6px;
}
.bd-sheetgrid th {
  background: var(--bd-surface-2); color: var(--bd-muted);
  font-size: 11px; font-weight: 650; text-transform: uppercase;
  letter-spacing: .02em; text-align: center; white-space: nowrap;
}
.bd-sheetgrid th.op-head { text-align: left; }
.bd-sheetgrid td.op { color: var(--bd-text); }
.bd-sheetgrid td.cell { text-align: center; width: 56px; padding: 2px; }
.bd-sheetgrid td.rowactions { white-space: nowrap; width: 1%; text-align: center; }
.bd-sheetgrid-cellbtn {
  width: 100%; min-width: 44px; height: 26px; cursor: pointer;
  font-size: 12px; font-weight: 700;
  color: var(--bd-text); background: transparent;
  border: 0; border-radius: var(--bd-r-sm);
}
.bd-sheetgrid-cellbtn:hover { background: var(--bd-accent-soft); }
.bd-sheetgrid-cellbtn:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: -2px; }
.bd-sheetgrid-cellbtn.is-absent { color: var(--bd-muted); font-weight: 400; }
.bd-sheetgrid-cellbtn.is-code-R { color: var(--bd-crit); }
.bd-sheetgrid-cellbtn.is-code-I { color: var(--bd-info); }
.bd-sheetgrid-cellbtn.is-code-T { color: var(--bd-warn); }
.bd-sheetgrid__rule {
  display: block; font-size: 10px; color: var(--bd-warn);
  font-weight: 600; text-transform: uppercase; letter-spacing: .02em;
}
.bd-sheetgrid__colmeta { display: block; font-weight: 400; text-transform: none; }
.bd-sheetgrid-iconbtn {
  border: 0; background: transparent; cursor: pointer;
  color: var(--bd-muted); font-size: 13px; line-height: 1;
  padding: 3px 5px; border-radius: var(--bd-r-sm);
}
.bd-sheetgrid-iconbtn:hover { color: var(--bd-text); background: var(--bd-surface-2); }
.bd-sheetgrid-iconbtn--danger:hover { color: var(--bd-crit); }
.bd-sheetgrid-addrow { display: flex; gap: 6px; margin-top: 8px; align-items: center; }
.bd-sheetgrid-key { font-size: 11px; color: var(--bd-muted); margin-top: 6px; }

/* =====================================================================
   Record page — boat detail (shares the customer-record pattern)
   ===================================================================== */

/* ---------- split columns: editable left, reference right ---------- */
.bd-split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--bd-sp-4); align-items: start; }
/* --aside: a wider working column with a reference/sidebar column beside it
   (project detail = details + budget | job sheets). */
.bd-split--aside { grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); }
/* --list: a narrow list of records beside the wide editor for the one picked
   (Contract plans). Collapses to one column with the split. */
.bd-split--list { grid-template-columns: minmax(220px, 300px) minmax(0, 1fr); }
@media (max-width: 1080px) { .bd-split { grid-template-columns: 1fr; } }

/* ---------- drag-to-reorder rows ----------
   The handle alone is draggable so inputs and selects inside the row stay
   usable; the row is the drop target. Insertion point is drawn with an inset
   shadow rather than a border so nothing shifts as it moves. */
.bd-drag-handle {
  width: 26px; text-align: center; cursor: grab;
  color: var(--bd-muted); user-select: none; line-height: 1;
}
.bd-drag-handle:active { cursor: grabbing; }
.bd-draggable-row.is-dragging { opacity: .45; }
/* A card or row acting as a drop target for a dragged item. */
.bd-droptarget.is-drop-target { outline: 2px dashed var(--bd-accent); outline-offset: -2px; }
.bd-dropzone {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 14px; margin-bottom: 12px;
  border: 1px dashed var(--bd-border); border-radius: var(--bd-r-md);
  color: var(--bd-muted); font-size: var(--bd-fs-caption); text-align: center;
}
.bd-dropzone.is-drop-target { border-color: var(--bd-accent); color: var(--bd-accent); }
.bd-draggable-row.is-drop-above > td { box-shadow: inset 0 2px 0 0 var(--bd-accent); }
.bd-draggable-row.is-drop-below > td { box-shadow: inset 0 -2px 0 0 var(--bd-accent); }

/* ---------- card body that follows a header/rechead in the same card ---------- */
.bd-card__body--divided { border-top: 1px solid var(--bd-border); }

/* ---------- item rows: search results and linked engines / parts ----------
   A card body lists these; each row is one linkable or linked record with
   its actions on the right. Deliberately not .bd-table — rows carry an
   inline control (serial number, quantity) that a data grid can't hold. */
.bd-rows { display: flex; flex-direction: column; gap: var(--bd-sp-2); }
.bd-row {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--bd-sp-3);
  padding: 10px 12px; border: 1px solid var(--bd-border);
  border-radius: var(--bd-r-md); background: var(--bd-surface);
}
.bd-row__main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.bd-row__title { font-weight: 650; font-size: var(--bd-fs-body); color: var(--bd-text); }
.bd-row__title a { color: var(--bd-accent); }
.bd-row__meta { display: flex; flex-wrap: wrap; gap: 6px; color: var(--bd-muted); font-size: var(--bd-fs-caption); }
.bd-row__actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
/* One horizontal band inside .bd-row__main: the record's title sitting with
   the inline fields that identify it (an engine's location, serial, year).
   Wraps rather than squeezing the inputs on a narrow screen. */
.bd-row__line { display: flex; align-items: center; flex-wrap: wrap; gap: 6px var(--bd-sp-3); }
.bd-row__field { display: flex; align-items: center; gap: 6px; }
.bd-row__field .bd-label { flex-shrink: 0; }
.bd-row__field .bd-input, .bd-row__field .bd-select { max-width: 240px; }
/* Field sized to its content: a serial number, then a four-digit year. */
.bd-row__field--md .bd-input, .bd-row__field--md .bd-select { width: 170px; }
.bd-row__field--sm .bd-input, .bd-row__field--sm .bd-select { width: 92px; }
/* A row that saves as you type reports its trouble in its own line, never in
   a dialog — a modal over an autosaving field traps whoever is mid-word. */
.bd-row__field.has-error .bd-input, .bd-row__field.has-error .bd-select { border-color: var(--bd-crit); }
.bd-row__error { color: var(--bd-crit); font-size: var(--bd-fs-caption); }
/* Clickable row: the whole row IS the action (search-result pickers).
   Render as <button class="bd-row bd-row--click"> with no .bd-row__actions. */
button.bd-row--click { width: 100%; font: inherit; text-align: left; cursor: pointer; }
.bd-row--click:hover { border-color: var(--bd-accent); background: var(--bd-accent-soft); }
.bd-row--click:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: 1px; }

/* Small neutral pill for row metadata (group, supplier, class…). Never a
   status — statuses use .bd-badge and its five semantic slots. */
.bd-tag {
  display: inline-block; padding: 1px 7px; border-radius: var(--bd-r-pill);
  border: 1px solid var(--bd-border); background: var(--bd-surface-2);
  font-size: 11px; color: var(--bd-muted); white-space: nowrap;
}

/* ---------- sub-heading inside a card body ---------- */
.bd-subhead { display: flex; align-items: center; gap: var(--bd-sp-2); margin: 0 0 8px; }
.bd-subhead__title {
  font-size: var(--bd-fs-micro); font-weight: 700; letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--bd-muted);
}
.bd-subhead__meta {
  margin-left: auto; font-size: var(--bd-fs-caption); color: var(--bd-muted);
}

/* ---------- period navigator: prev / label / next over a span of dates ----
   The label carries the period being shown (a week, a month) and the arrows
   step it. Buttons disable at the ends of the available range. Use the
   --sticky modifier inside a scrolling panel (a modal body): it cancels the
   panel's top padding so the strip sits flush against the top edge. */
.bd-periodnav {
  display: flex; align-items: center; gap: var(--bd-sp-2);
  padding-bottom: var(--bd-sp-2);
}
.bd-periodnav__label {
  flex: 1; text-align: center; font-weight: 650;
  font-size: var(--bd-fs-body); color: var(--bd-text);
}
.bd-periodnav .bd-btn[disabled] { opacity: 0.45; cursor: not-allowed; }
/* ---------- tide week grid: one column per day, events stacked ----------
   Reads across the week the way a printed tide table does. Seven columns
   need more width than a phone has, so the grid scrolls inside
   .bd-tidegrid-wrap and the page/modal itself never side-scrolls. */
.bd-tidegrid-wrap { overflow-x: auto; }
.bd-tidegrid {
  display: grid; grid-template-columns: repeat(7, minmax(88px, 1fr));
  min-width: 616px;
  border: 1px solid var(--bd-border); border-radius: var(--bd-r-md);
  overflow: hidden;
}
.bd-tideday { min-width: 0; }
.bd-tideday + .bd-tideday { border-left: 1px solid var(--bd-border); }
.bd-tideday__head {
  padding: 7px 9px 8px; border-bottom: 1px solid var(--bd-border);
  background: var(--bd-surface-2); white-space: nowrap;
}
.bd-tideday__day {
  display: block; font-size: var(--bd-fs-caption); font-weight: 700;
  color: var(--bd-text);
}
.bd-tideday__date {
  display: block; font-size: var(--bd-fs-caption); color: var(--bd-muted);
}
.bd-tideday__cycle {
  display: block; margin-top: 3px;
  font-size: var(--bd-fs-micro); font-weight: 700; letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--bd-muted);
}
/* Today is marked by a rule under its heading, not a colour wash - the
   columns either side must stay equally readable. */
.bd-tideday.is-today .bd-tideday__head { box-shadow: inset 0 -2px 0 var(--bd-accent); }
.bd-tideday.is-today .bd-tideday__day { color: var(--bd-accent); }
.bd-tideday__body { padding: 0 9px; }
.bd-tideday__none {
  padding: 10px 0; margin: 0;
  font-size: var(--bd-fs-caption); color: var(--bd-muted);
}
.bd-tide { padding: 8px 0; }
.bd-tide + .bd-tide { border-top: 1px solid var(--bd-border); }
.bd-tide__type {
  display: block; font-size: var(--bd-fs-caption); font-weight: 650;
  color: var(--bd-text);
}
.bd-tide__time {
  display: block; font-weight: 650; color: var(--bd-accent);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.bd-tide__height {
  display: block; font-size: var(--bd-fs-caption); color: var(--bd-muted);
  font-variant-numeric: tabular-nums;
}

.bd-periodnav--sticky {
  position: sticky; top: calc(var(--bd-sp-4) * -1); z-index: 1;
  margin-top: calc(var(--bd-sp-4) * -1);
  padding-top: var(--bd-sp-4);
  background: var(--bd-surface);
}

/* ---------- compact empty state (inside a card, not a whole panel) ---------- */
.bd-empty--inline { padding: 14px; font-size: var(--bd-fs-caption); }

/* ---------- inline alerts (expiring documents, blocking notices) ----------
   Same five semantic slots as .bd-badge; the alert is the full-width form. */
.bd-alerts { display: flex; flex-direction: column; gap: 6px; }
.bd-alert {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 13px; border-radius: var(--bd-r-sm);
  font-size: var(--bd-fs-caption); border: 1px solid transparent;
}
.bd-alert__label { font-weight: 650; flex-shrink: 0; }
.bd-alert__end { margin-left: auto; white-space: nowrap; font-weight: 600; }
.bd-alert--good { color: var(--bd-good); background: var(--bd-good-bg); }
.bd-alert--info { color: var(--bd-info); background: var(--bd-info-bg); }
.bd-alert--warn { color: var(--bd-warn); background: var(--bd-warn-bg); }
/* An alert that carries buttons: they wrap under the text on a narrow panel
   instead of running off its edge. */
.bd-alert--wrap { flex-wrap: wrap; }
.bd-alert--crit { color: var(--bd-crit); background: var(--bd-crit-bg); }

/* ---------- closed record (a processed / invoiced job sheet) ----------
   The content steps back — greyed, dimmed, on a sunken ground — so a
   finished record can't be mistaken for a live one. Children marked
   .bd-closed__keep (the banner saying why, its buttons) stay full strength. */
.bd-closed { background: var(--bd-surface-2); }
.bd-closed > :not(.bd-closed__keep) { filter: grayscale(1); opacity: .65; }
/* One finished item in a list (a row, card or sidebar entry). Table rows
   grey their cells — a filter on the <tr> itself is unreliable. */
.bd-closed-item:not(tr), tr.bd-closed-item > td { filter: grayscale(1); opacity: .6; }

/* ---------- populate-from-catalogue button (sits inside a field label) ---------- */
.bd-popbtn {
  border: 0; background: var(--bd-accent-soft); color: var(--bd-accent);
  border-radius: var(--bd-r-sm); cursor: pointer;
  font-size: 11px; line-height: 1; padding: 3px 6px; margin-left: 4px;
}
.bd-popbtn:hover:not(:disabled) { background: var(--bd-accent); color: #fff; }
.bd-popbtn:disabled { opacity: 0.35; cursor: default; }

/* ---------- dual-unit field (metres/feet, kg/lbs) ---------- */
.bd-dual { display: grid; grid-template-columns: 1fr 1fr; gap: var(--bd-sp-2); }
@media (max-width: 680px) { .bd-dual { grid-template-columns: 1fr; } }
.bd-dual__unit { display: flex; flex-direction: column; gap: 4px; }
.bd-dual__unit > span { font-size: 11px; font-weight: 600; color: var(--bd-muted); }

/* ---------- searchable dropdown (BoatCataloguePanel) on migrated pages ----------
   Re-points the shared .csd-* markup at the bd tokens so it is correct in
   dark mode; unmigrated pages (no components.css) keep the legacy look. */
.bd-csd-dropdown {
  background: var(--bd-surface); border-color: var(--bd-border);
  border-radius: var(--bd-r-md); box-shadow: var(--bd-shadow-1);
}
.bd-csd-dropdown .csd-option { font-size: var(--bd-fs-body); color: var(--bd-text); }
.bd-csd-dropdown .csd-option:hover, .bd-csd-dropdown .csd-option.csd-focused { background: var(--bd-accent-soft); }
.bd-csd-dropdown .csd-option.csd-empty { color: var(--bd-muted); }

/* Read-only catalogue spec fields. The legacy `.readonly-field .input[readonly]`
   rule is a light-locked literal at equal specificity, so it is re-pointed
   here rather than left to win on a migrated page. */
.bd-input[readonly], .readonly-field .bd-input[readonly] {
  background: var(--bd-surface-2); color: var(--bd-muted);
  border-color: var(--bd-border); cursor: default;
}

/* ---------- sign-in / reset-password card (reversed brand panel) ----------
   The one screen every user sees, so it is a deliberate deep-navy brand
   panel rather than a normal surface. It stays navy in both themes — the
   theme bootstrap does run here, but --bd-chrome is navy either way, so
   the card only deepens slightly rather than inverting.

   Everything inside is scoped to the --bd-chrome-* reversed inks. It has
   to be: the ground tokens and the legacy --color-* variables are all
   light-surface values with no dark override, so the card previously
   inherited near-black --color-text onto navy — labels were dark-on-dark
   and typed input text was very nearly invisible. */
.bd-signin-body {
  display: flex; align-items: center; justify-content: center;
  min-height: 100vh;
}
.bd-signin-wrap { width: 100%; max-width: 400px; padding: var(--bd-sp-5); }
.bd-signin-card {
  background: var(--bd-chrome);
  border: 1px solid var(--bd-chrome-line);
  border-radius: var(--bd-r-lg);
  box-shadow: var(--bd-shadow-2);
  padding: 40px 36px;
  color: var(--bd-chrome-strong);
}
.bd-signin-head { margin-bottom: 28px; }
.bd-signin-head h1 { margin: 0 0 var(--bd-sp-1) 0; font-size: 1.5rem; font-weight: 700; }
.bd-signin-sub { margin: 0; font-size: var(--bd-fs-body); color: var(--bd-chrome-text); }
/* Step intros ("Your account uses a temporary password…") */
.bd-signin-note {
  margin: 0 0 var(--bd-sp-5) 0;
  font-size: var(--bd-fs-body); color: var(--bd-chrome-text);
}
.bd-signin-hint {
  margin: var(--bd-sp-3) 0 0 0;
  font-size: var(--bd-fs-caption); color: var(--bd-chrome-text);
}

.bd-signin-field { margin-bottom: var(--bd-sp-4); }
.bd-signin-field label {
  display: block; margin-bottom: 6px;
  font-size: var(--bd-fs-caption); color: var(--bd-chrome-text);
}
.bd-signin-input {
  width: 100%; padding: 12px 14px; box-sizing: border-box;
  background: var(--bd-chrome-active);
  border: 1px solid var(--bd-chrome-line);
  border-radius: var(--bd-r-sm);
  color: var(--bd-chrome-strong);
  font-size: 0.95rem; font-family: inherit;
  outline: none; transition: border-color 0.15s;
}
.bd-signin-input::placeholder { color: var(--bd-chrome-text); opacity: 0.75; }
.bd-signin-input:focus { border-color: var(--bd-chrome-accent); }
/* 2FA code entry — wide-tracked, larger digits */
.bd-signin-input--code { letter-spacing: 0.2em; font-size: 1.2rem; }

/* Cyan fill takes NAVY ink, not white — see the token block in design.css. */
.bd-signin-btn {
  display: block; width: 100%; padding: 13px; margin-top: var(--bd-sp-2);
  background: var(--bd-chrome-accent); color: var(--bd-chrome);
  border: none; border-radius: var(--bd-r-sm);
  font-size: 1rem; font-weight: 600; font-family: inherit;
  text-align: center; text-decoration: none; cursor: pointer;
  transition: background 0.15s;
}
.bd-signin-btn:hover { background: var(--bd-chrome-accent-strong); }
.bd-signin-btn:disabled { opacity: 0.6; cursor: default; }

.bd-signin-msg {
  margin-top: var(--bd-sp-3); min-height: 20px;
  font-size: var(--bd-fs-caption); color: var(--bd-chrome-crit);
}
.bd-signin-msg--ok { color: var(--bd-chrome-good); }
.bd-signin-linkrow { margin-top: 14px; text-align: center; }
.bd-signin-link {
  color: var(--bd-chrome-accent);
  font-size: var(--bd-fs-caption); text-decoration: none;
}
.bd-signin-link:hover { text-decoration: underline; }
.bd-signin-rule {
  border: none; border-top: 1px solid var(--bd-chrome-line);
  margin: var(--bd-sp-6) 0;
}

/* --- Search-result snippet -------------------------------------------------
   A line of matched document text under a result row, with the search terms
   highlighted. Used by the Library, where most documents are scanned manuals
   and the title alone rarely tells you whether the hit is the one you want. */
.bd-search-snippet {
  margin-top: 3px;
  font-size: var(--bd-fs-caption);
  line-height: 1.4;
  color: var(--bd-muted);
}
.bd-search-snippet mark {
  background: var(--bd-highlight-bg);
  color: var(--bd-highlight-ink);
  border-radius: 2px;
  padding: 0 1px;
}

/* --- Facet bar -------------------------------------------------------------
   A row of narrowing controls under a list toolbar: facet selects followed by
   the chips for whatever is currently applied. Wraps rather than scrolls, so
   a long chip set pushes the table down instead of hiding filters off-screen. */
.bd-facet-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--bd-sp-2);
  margin-bottom: var(--bd-sp-3);
}
.bd-facet-bar .bd-chips { flex: 1 1 auto; }
/* .bd-input / .select are width:100% for form layouts. In a wrapping filter
   row that makes every control claim the full width and stack one per line,
   so they are given a real basis here instead. */
.bd-facet-bar > select,
.bd-facet-bar > input {
  width: auto;
  flex: 0 1 190px;
  min-width: 132px;
}

/* A chip standing for an applied filter: click anywhere on it to remove.
   The × is decorative — the whole chip is the button. */
.bd-chip--applied { color: var(--bd-accent); background: var(--bd-accent-soft); border-color: transparent; }
.bd-chip--applied::after { content: ' \00D7'; font-weight: 700; opacity: 0.7; }
.bd-chip__kind { opacity: 0.6; font-weight: 500; margin-right: 4px; text-transform: lowercase; }
/* The search box leads a facet bar and takes the slack. */
.bd-facet-bar > .bd-facet-bar__search { flex: 1 1 240px; min-width: 200px; }

/* --- Drop strip --------------------------------------------------------------
   A one-line drop target for files: drag onto it or click it. The slim
   sibling of a full drop zone, for pages where the drop is a shortcut and the
   content below is the point (the Library). */
.bd-dropstrip {
  display: flex; align-items: center; gap: var(--bd-sp-2); flex-wrap: wrap;
  padding: 8px 14px; margin-bottom: var(--bd-sp-3);
  border: 1.5px dashed var(--bd-border); border-radius: var(--bd-r-md);
  background: var(--bd-surface); color: var(--bd-muted);
  font-size: var(--bd-fs-caption); cursor: pointer;
}
.bd-dropstrip:hover, .bd-dropstrip:focus-visible { border-color: var(--bd-accent); outline: none; }
.bd-dropstrip.is-dragover { border-color: var(--bd-accent); background: var(--bd-accent-soft); color: var(--bd-text); }
.bd-dropstrip__icon { font-size: 1.1rem; line-height: 1; }

/* --- Library layout: contents beside the list --------------------------------
   Two columns on a desktop; on a narrow screen the contents stacks above the
   list and can be folded away. */
.bd-libgrid { display: grid; grid-template-columns: 270px minmax(0, 1fr); gap: var(--bd-sp-3); align-items: start; }
.bd-libgrid__main { min-width: 0; }
.bd-libgrid__main > .panel-title { justify-content: flex-start; flex-wrap: wrap; }
.bd-libgrid__view { margin-left: auto; }
/* The list keeps its title readable beside the contents panel: the title
   column never collapses, and the least-used columns (document date, pages,
   date added)
   step aside until the screen is wide enough for them. */
.bd-libgrid .bd-libcol-title { min-width: 200px; }
@media (max-width: 1500px) { .bd-libgrid .bd-libcol-opt { display: none; } }
.bd-libgrid__chips:empty { display: none; }
.bd-libgrid__chips { margin-bottom: var(--bd-sp-3); }
@media (max-width: 900px) { .bd-libgrid { grid-template-columns: minmax(0, 1fr); } }

/* --- Contents tree (Library) -------------------------------------------------
   Category → Type → Sub-type with counts. Each row is a button: clicking it
   filters the list, the caret beside it only opens or closes the branch. */
.bd-libcontents {
  background: var(--bd-surface); border: 1px solid var(--bd-border);
  border-radius: var(--bd-r-md); padding: var(--bd-sp-3);
  position: sticky; top: calc(var(--bd-shell-h, 0px) + var(--bd-sp-3));
  max-height: calc(100vh - var(--bd-shell-h, 0px) - 2 * var(--bd-sp-3));
  overflow-y: auto;
}
@media (max-width: 900px) { .bd-libcontents { position: static; max-height: none; } }
.bd-libcontents__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--bd-sp-2); }
.bd-libcontents__head h3 { margin: 0; font-size: var(--bd-fs-body); }
.bd-libcontents__fold {
  display: none; background: none; border: none; cursor: pointer; padding: 2px 4px;
  color: var(--bd-accent); font-size: var(--bd-fs-caption); font-weight: 600;
}
@media (max-width: 900px) { .bd-libcontents__fold { display: inline-block; } }
.bd-libcontents.is-folded .bd-libcontents__body { display: none; }
.bd-libcontents__body > .bd-select { margin: 4px 0 var(--bd-sp-3); }
.bd-libcontents__tree { display: flex; flex-direction: column; gap: 1px; }
.bd-libcontents__empty { color: var(--bd-muted); font-size: var(--bd-fs-caption); padding: 6px 2px; }
.bd-libcontents__row { display: flex; align-items: center; gap: 2px; }
.bd-libcontents__caret {
  flex: 0 0 22px; height: 26px; background: none; border: none; cursor: pointer; padding: 0;
  color: var(--bd-muted); font-size: 0.7rem; line-height: 1;
}
.bd-libcontents__caret:hover { color: var(--bd-text); }
.bd-libcontents__caret.is-open { transform: rotate(90deg); }
.bd-libcontents__caret--none { visibility: hidden; cursor: default; }
.bd-libcontents__item {
  flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 8px;
  padding: 5px 8px; border: none; border-radius: var(--bd-r-sm);
  background: none; color: var(--bd-text); cursor: pointer; text-align: left;
  font: inherit; font-size: var(--bd-fs-caption);
}
.bd-libcontents__item:hover { background: var(--bd-surface-2); }
.bd-libcontents__item:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: -2px; }
.bd-libcontents__item.is-active { background: var(--bd-accent-soft); color: var(--bd-accent); font-weight: 650; }
.bd-libcontents__name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-libcontents__count { flex: 0 0 auto; color: var(--bd-muted); font-variant-numeric: tabular-nums; font-weight: 500; }
.bd-libcontents__item.is-active .bd-libcontents__count { color: var(--bd-accent); }
.bd-libcontents__item--all { font-weight: 650; }
.bd-libcontents__item--cat { font-weight: 650; }
.bd-libcontents__item--none { color: var(--bd-muted); font-style: italic; }
.bd-libcontents__children { padding-left: 16px; display: flex; flex-direction: column; gap: 1px; }

/* --- Workload calendar ------------------------------------------------------
   Workshop → Workload: a per-person/team day-column calendar of schedule
   blocks with per-day capacity and an unscheduled side panel. Cards carry
   only workload-management facts: queue rank, estimated minutes, parts
   hold, urgency. Urgent cards sort first and take the crit slot. */
.bd-wl-toolbar {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--bd-sp-2); margin-bottom: var(--bd-sp-3);
}
.bd-wl-toolbar .bd-select { min-width: 220px; }
.bd-wl-range { font-weight: 650; font-size: var(--bd-fs-body); color: var(--bd-text); margin: 0 var(--bd-sp-2); white-space: nowrap; }
.bd-wl-seg { display: inline-flex; border: 1px solid var(--bd-border); border-radius: var(--bd-r-sm); overflow: hidden; }
.bd-wl-seg .bd-wl-seg__btn {
  font-family: var(--bd-font); font-size: var(--bd-fs-caption); font-weight: 600;
  color: var(--bd-muted); background: var(--bd-surface); border: none; cursor: pointer;
  padding: 6px 12px;
}
.bd-wl-seg .bd-wl-seg__btn + .bd-wl-seg__btn { border-left: 1px solid var(--bd-border); }
.bd-wl-seg .bd-wl-seg__btn.is-active { color: var(--bd-accent); background: var(--bd-accent-soft); }
.bd-wl-layout { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: var(--bd-sp-4); align-items: start; }
@media (max-width: 1020px) { .bd-wl-layout { grid-template-columns: 1fr; } }
.bd-wl-grid { display: grid; gap: var(--bd-sp-2); }
.bd-wl-grid--1 { grid-template-columns: 1fr; }
.bd-wl-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.bd-wl-grid--7 { grid-template-columns: repeat(7, minmax(0, 1fr)); }
@media (max-width: 860px) { .bd-wl-grid--3, .bd-wl-grid--7 { grid-template-columns: 1fr; } }
.bd-wl-day {
  background: var(--bd-surface-2); border: 1px solid var(--bd-border);
  border-radius: var(--bd-r-md); padding: var(--bd-sp-2); min-height: 110px;
}
.bd-wl-day--today { border-color: var(--bd-accent); }
.bd-wl-day--off { opacity: 0.65; }
.bd-wl-day__head { display: flex; align-items: baseline; gap: var(--bd-sp-2); }
.bd-wl-day__date { font-weight: 700; font-size: var(--bd-fs-caption); }
.bd-wl-day__cap { margin-left: auto; font-size: var(--bd-fs-micro); color: var(--bd-muted); white-space: nowrap; }
.bd-wl-day__cap--full { color: var(--bd-warn); }
.bd-wl-day__cap--over { color: var(--bd-crit); font-weight: 700; }
.bd-wl-member { font-size: var(--bd-fs-micro); font-weight: 700; text-transform: uppercase; letter-spacing: 0.07em; color: var(--bd-muted); margin: var(--bd-sp-2) 0 2px; }
.bd-wl-card {
  background: var(--bd-surface); border: 1px solid var(--bd-border);
  border-left: 3px solid var(--bd-neutral); border-radius: var(--bd-r-sm);
  padding: 6px 8px; margin-top: 6px; font-size: var(--bd-fs-caption);
}
.bd-wl-card--hold { border-left-color: var(--bd-warn); }
.bd-wl-card--urgent { border-left-color: var(--bd-crit); }
.bd-wl-card__row { display: flex; align-items: center; gap: 6px; min-width: 0; }
.bd-wl-card__job { font-weight: 700; white-space: nowrap; }
.bd-wl-card__title { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-wl-card__meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; margin-top: 3px; color: var(--bd-muted); font-size: var(--bd-fs-micro); }
.bd-wl-rank {
  display: inline-flex; align-items: center; font-weight: 700;
  font-size: var(--bd-fs-micro); color: var(--bd-muted);
  background: var(--bd-neutral-bg); border: 1px solid var(--bd-neutral);
  border-radius: var(--bd-r-pill); padding: 0 7px;
}
.bd-wl-urgent { color: var(--bd-crit); font-weight: 800; }
.bd-wl-hold { color: var(--bd-warn); font-weight: 700; white-space: nowrap; }
.bd-wl-unsched-list { display: flex; flex-direction: column; }

/* Workload drag interactions (slice 3): cards drag between days / member
   zones to re-schedule or re-assign; dropping on the unscheduled panel
   removes the block. Zones highlight with the accent while a card hovers. */
.bd-wl-card[draggable="true"] { cursor: grab; }
.bd-wl-card.is-dragging { opacity: 0.5; }
.bd-wl-drop.is-drag-over { outline: 2px dashed var(--bd-accent); outline-offset: -2px; border-radius: var(--bd-r-md); }
.bd-wl-member-zone { border-radius: var(--bd-r-sm); }
.bd-wl-zone-empty { color: var(--bd-muted); font-size: var(--bd-fs-micro); padding: 2px 0 6px; }

/* ── File drop zone + upload queue ──────────────────────────────────────
   Attaching documents is a batch job, not a one-file-at-a-time job: a
   model arrives with an operator manual, a workshop manual and three
   bulletins together. The zone takes a drop or opens the file picker for
   a multi-select; the queue below reports each file's fate individually,
   because "already in the Library" is a normal outcome, not a failure. */
.bd-dropzone {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--bd-sp-1);
  border: 2px dashed var(--bd-border); border-radius: var(--bd-r-md);
  background: var(--bd-surface-2);
  padding: var(--bd-sp-5) var(--bd-sp-4);
  text-align: center; cursor: pointer;
}
.bd-dropzone:hover { border-color: var(--bd-accent); }
.bd-dropzone:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: 2px; }
.bd-dropzone.is-drag-over { border-color: var(--bd-accent); background: var(--bd-accent-soft); }
.bd-dropzone__icon { font-size: 26px; line-height: 1; }
.bd-dropzone__label { font-size: var(--bd-fs-body); font-weight: 650; color: var(--bd-text); }
.bd-dropzone__hint { font-size: var(--bd-fs-caption); color: var(--bd-muted); }
.bd-dropzone__input { display: none; }

.bd-uploads { display: flex; flex-direction: column; gap: 2px; }
.bd-uploads__row {
  display: flex; align-items: center; gap: var(--bd-sp-2);
  padding: 6px var(--bd-sp-2); border-radius: var(--bd-r-sm);
  background: var(--bd-surface-2); font-size: var(--bd-fs-caption);
}
.bd-uploads__name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-uploads__size { color: var(--bd-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.bd-uploads__state { white-space: nowrap; font-weight: 700; color: var(--bd-muted); }
.bd-uploads__state--busy { color: var(--bd-info); }
.bd-uploads__state--done { color: var(--bd-good); }
.bd-uploads__state--dupe { color: var(--bd-warn); }
.bd-uploads__state--fail { color: var(--bd-crit); }

/* The box the zone, its options and its queue live in. Kept separate from
   the zone itself so a page can drop the zone into an existing form. */
.bd-uploadbox { display: grid; gap: var(--bd-sp-3); }
.bd-uploadbox.hidden { display: none; }
.bd-uploadbox__fields { display: grid; grid-template-columns: 1fr 1fr; gap: var(--bd-sp-3); }
@media (max-width: 720px) { .bd-uploadbox__fields { grid-template-columns: 1fr; } }
.bd-uploadbox__actions { display: flex; align-items: center; gap: var(--bd-sp-2); flex-wrap: wrap; }

/* Inline status line beside an action bar — "3 uploaded · 1 failed". */
.bd-inline-status { font-size: var(--bd-fs-caption); color: var(--bd-muted); }
.bd-inline-status--error { color: var(--bd-crit); }

/* ── Smart materials picker (bd-material) ─────────────────────────────────
   A material rule names the quantity, the tech names the product: "10 litres
   of engine oil — which grade?". So each row pairs a computed, editable
   quantity with a product picker, rather than the flat checkbox a plain
   suggested part gets. Used by the job sheet's parts popup and task detail. */
.bd-materials { display: flex; flex-direction: column; gap: var(--bd-sp-2); }

.bd-material {
  display: grid;
  grid-template-columns: auto 1fr; gap: var(--bd-sp-1) var(--bd-sp-2);
  align-items: center;
  padding: var(--bd-sp-2); border-radius: var(--bd-r-sm);
  background: var(--bd-surface-2);
  border: 1px solid var(--bd-border);
}
.bd-material--done { opacity: .68; }
.bd-material--blocked { background: transparent; }

.bd-material__check { width: 16px; height: 16px; cursor: pointer; }
.bd-material__head { display: flex; align-items: baseline; gap: var(--bd-sp-2); flex-wrap: wrap; min-width: 0; }
.bd-material__label { font-size: var(--bd-fs-body); font-weight: 650; color: var(--bd-text); }
.bd-material__qty-hint { font-size: var(--bd-fs-caption); color: var(--bd-muted); font-variant-numeric: tabular-nums; }
.bd-material__why { font-size: var(--bd-fs-caption); color: var(--bd-muted); }

/* Second row lines up under the label, clear of the checkbox column. */
.bd-material__pick {
  grid-column: 2;
  display: flex; align-items: center; gap: var(--bd-sp-2); flex-wrap: wrap;
}
.bd-material__product { flex: 1 1 220px; min-width: 0; }
.bd-material__qty { width: 5.5rem; flex: 0 0 auto; text-align: right; font-variant-numeric: tabular-nums; }
.bd-material__unit { font-size: var(--bd-fs-caption); color: var(--bd-muted); white-space: nowrap; }

/* --- Protected document viewer ---------------------------------------------
   Paged image reader for view-only documents. Its whole reason to exist is
   that there is no source file behind it: each page is a server-rendered,
   watermarked image. The affordance stripping below (no selection, no drag,
   no context menu) is a deterrent for the casual copy, not the protection —
   the protection is that the original never reaches the browser. */
.bd-docview {
  display: flex;
  flex-direction: column;
  gap: var(--bd-sp-3);
  background: var(--bd-surface-2);
  border-radius: var(--bd-r-md);
  padding: var(--bd-sp-3);
  max-height: 78vh;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  /* Blocks drag-to-desktop and the selection handles that make a page feel
     copyable. */
  user-select: none;
  -webkit-user-select: none;
}
.bd-docview__page {
  display: block;
  width: 100%;
  height: auto;
  margin: 0 auto;
  background: var(--bd-surface);
  border: 1px solid var(--bd-border);
  border-radius: var(--bd-r-sm);
  -webkit-user-drag: none;
  pointer-events: none;   /* no long-press "save image" on tablets */
}
/* Reserves the page's height before its image arrives, so lazy loading does
   not make the scrollbar jump under the reader's thumb. */
.bd-docview__slot {
  position: relative;
  min-height: 60vh;
  display: flex;
  align-items: center;
  justify-content: center;
}
.bd-docview__slot--pending::after {
  content: attr(data-label);
  font-size: var(--bd-fs-caption);
  color: var(--bd-muted);
}
.bd-docview__bar {
  display: flex;
  align-items: center;
  gap: var(--bd-sp-2);
  flex-wrap: wrap;
  font-size: var(--bd-fs-caption);
  color: var(--bd-muted);
}
.bd-docview__notice {
  display: flex;
  align-items: center;
  gap: var(--bd-sp-2);
  padding: var(--bd-sp-2) var(--bd-sp-3);
  border-radius: var(--bd-r-sm);
  background: var(--bd-warn-bg);
  color: var(--bd-warn);
  font-size: var(--bd-fs-caption);
}
/* Viewer toolbar: where you are, going to a page, and your bookmarks. Sits
   above both the page reader (view-only) and the embedded PDF (standard). */
.bd-docview__tools {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--bd-sp-2);
  padding: var(--bd-sp-2) 0;
  font-size: var(--bd-fs-caption);
  color: var(--bd-muted);
}
.bd-docview__goto { display: inline-flex; align-items: center; gap: var(--bd-sp-1); white-space: nowrap; }
.bd-docview__goto .bd-input { width: 4.75rem; padding: 4px 6px; text-align: center; font-variant-numeric: tabular-nums; }
.bd-docview__markbtn.is-marked { color: var(--bd-accent); border-color: var(--bd-accent); }
/* Search inside a view-only document: the box, "2 of 12 · p. 212" and the
   step buttons on one line; the note (still reading, no matches, slowed
   down) takes a line of its own beneath the toolbar row. */
.bd-docview__search { display: inline-flex; align-items: center; gap: var(--bd-sp-1); flex: 0 1 26rem; min-width: 0; margin: 0; }
.bd-docview__search .bd-input { flex: 1 1 10rem; min-width: 8rem; padding: 4px 8px; }
/* Muted ink, weighted up, like the toolbar's own labels: the viewer sits on
   the legacy .card, which stays light in dark mode, so --bd-text (light in
   dark mode) would vanish on it. */
.bd-docview__search-count { white-space: nowrap; font-variant-numeric: tabular-nums; font-weight: 600; }
.bd-docview__search-note { flex: 1 0 100%; order: 99; color: var(--bd-muted); }
/* This document's bookmarks as chips: one click jumps, and the chip for the
   page being read is lit (.is-active). */
.bd-docview__marks { display: flex; flex-wrap: wrap; gap: 6px; flex: 1 1 12rem; min-width: 0; }
.bd-docview__mark { max-width: 15rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-docview__mark-page { font-variant-numeric: tabular-nums; opacity: 0.7; margin-right: 4px; }
/* "All bookmarks": one bordered list per document. Each row's text is the
   jump (a button stripped to read as text); its buttons manage it. */
.bd-docview__marklist { border: 1px solid var(--bd-border); border-radius: var(--bd-r-md); margin-bottom: var(--bd-sp-4); }
.bd-docview__markjump,
.bd-docview__markedit {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 10px;
}
.bd-docview__markjump {
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.bd-docview__markjump:hover .t { color: var(--bd-accent); }
.bd-docview__markjump:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: 2px; border-radius: var(--bd-r-sm); }

/* --- Link picker results ---------------------------------------------------
   Search results in the Library's "Link to…" dialog. A fixed-height scroller
   so the dialog does not resize under the cursor as results arrive. */
.bd-linkpicker { max-height: 46vh; overflow-y: auto; margin-top: var(--bd-sp-2); }
/* A result row: name and hint on the left, the add/remove control on the
   right. The whole row is the click target — the button is the affordance,
   not the only place that works. */
.bd-linkpicker__item {
  display: flex; align-items: center; gap: var(--bd-sp-2);
  width: 100%; text-align: left; cursor: pointer;
  background: none; border: none; border-bottom: 1px solid var(--bd-border);
  padding: 9px 6px; color: inherit; font-family: var(--bd-font);
}
.bd-linkpicker__item:hover { background: var(--bd-surface-2); }
.bd-linkpicker__item:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: -2px; }
.bd-linkpicker__text  { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.bd-linkpicker__label { font-size: var(--bd-fs-body); font-weight: 600; }
.bd-linkpicker__hint  { font-size: var(--bd-fs-caption); color: var(--bd-muted); }
.bd-linkpicker__empty { padding: var(--bd-sp-3); color: var(--bd-muted); font-size: var(--bd-fs-caption); }
/* The + / ✓ toggle. It stays put as you work down a list of matches, which is
   the whole point of the dialog not closing after each link. */
.bd-linkpicker__add {
  flex: 0 0 auto; width: 26px; height: 26px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--bd-border); border-radius: var(--bd-r-pill);
  background: var(--bd-surface); color: var(--bd-muted);
  font-size: 15px; font-family: var(--bd-font); cursor: pointer;
}
.bd-linkpicker__item:hover .bd-linkpicker__add { color: var(--bd-accent); border-color: var(--bd-accent); }
.bd-linkpicker__add.is-linked,
.bd-linkpicker__item.is-linked:hover .bd-linkpicker__add {
  background: var(--bd-good-bg); border-color: var(--bd-good); color: var(--bd-good);
}
.bd-linkpicker__item.is-linked .bd-linkpicker__label { color: var(--bd-muted); }
/* Running count plus the bulk action, between the search box and the results. */
.bd-linkpicker__bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--bd-sp-2); margin-top: var(--bd-sp-2);
}
.bd-linkpicker__count { font-size: var(--bd-fs-caption); color: var(--bd-muted); }

/* One existing link, with its remove control. */
.bd-linkrow {
  display: flex; align-items: center; gap: var(--bd-sp-2);
  padding: 7px 0; border-bottom: 1px solid var(--bd-border);
  font-size: var(--bd-fs-caption);
}
.bd-linkrow__name { flex: 1; color: var(--bd-text); }
.bd-linkrow__type { color: var(--bd-muted); }
/* A link whose target row has since been deleted. */
.bd-linkrow--missing .bd-linkrow__name { color: var(--bd-muted); font-style: italic; }

/* --- "Linked to" list cell -------------------------------------------------
   A document can be tied to hundreds of engine variants, so the cell shows the
   first couple and a +N counter; the counter's title lists the rest on hover.
   A title attribute rather than a CSS popover on purpose: the table scrolls
   inside .table-wrap, and a positioned bubble would be clipped by it. */
/* One line, not a stack: the table already carries ten columns, and letting
   the chips wrap turned every row into three lines of it. */
.bd-linkcell { display: flex; flex-wrap: nowrap; gap: 4px; align-items: center; max-width: 240px; }
.col-links { min-width: 172px; }
.bd-linkcell__chip {
  flex: 0 1 auto; min-width: 40px;
  display: inline-block; max-width: 150px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
  padding: 1px 7px; border-radius: var(--bd-r-pill);
  background: var(--bd-surface-2); color: var(--bd-text);
  font-size: var(--bd-fs-caption); line-height: 1.5;
}
.bd-linkcell__more {
  flex: 0 0 auto;
  padding: 1px 7px; border-radius: var(--bd-r-pill);
  background: var(--bd-accent-soft); color: var(--bd-accent);
  font-size: var(--bd-fs-caption); font-weight: 600; line-height: 1.5; cursor: help;
}
.bd-linkcell__none { color: var(--bd-muted); }

/* --- Boat manufacturer page ------------------------------------------------
   The make is where a document is held for a whole range, so its Documents
   tab needs a little more room than a stock table row: an explanatory line,
   a search panel, and two actions per row (re-role and unlink). */
.make-docs-note { font-size: var(--bd-fs-caption); margin: 0 0 var(--bd-sp-3); max-width: 68ch; }
.make-docs-toolbar {
  display: flex; align-items: center; gap: var(--bd-sp-3);
  margin-bottom: var(--bd-sp-3); flex-wrap: wrap;
}
.make-docs-search {
  display: flex; gap: var(--bd-sp-2); align-items: center;
  margin-bottom: var(--bd-sp-3);
}
.make-docs-search input { flex: 1 1 240px; min-width: 180px; }
.make-docs-actions { display: flex; gap: var(--bd-sp-2); align-items: center; white-space: nowrap; }
/* A failed action reported next to the control that failed. */
.muted.is-error { color: var(--bd-crit); }
/* Scoped to this page on purpose: .stacked-list has no rule of its own and
   several older pages render it as a plain block. Giving it one globally would
   quietly restyle them. */
#catalogue-make-root .stacked-list { display: flex; flex-direction: column; gap: var(--bd-sp-2); }
#catalogue-make-root .stacked-list .list-item,
#catalogue-make-root #doc-link-results .list-item {
  display: flex; align-items: center; justify-content: space-between; gap: var(--bd-sp-3);
}

/* --- Document tiles --------------------------------------------------------
   Card grid alternative to the document table. Thumbnails make a manual
   recognisable at a glance in a way a row of text never does — the same reason
   paperless-ngx offers both. */
.bd-doctiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
  gap: var(--bd-sp-3);
}
.bd-doctile {
  display: flex; flex-direction: column;
  border: 1px solid var(--bd-border);
  border-radius: var(--bd-r-md);
  background: var(--bd-surface);
  overflow: hidden;
  text-align: left;
  cursor: pointer;
  font-family: var(--bd-font);
  color: inherit;
  padding: 0;
}
.bd-doctile:hover { border-color: var(--bd-accent); }
.bd-doctile:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: 2px; }
.bd-doctile__thumb {
  aspect-ratio: 3 / 4;
  width: 100%;
  object-fit: cover;
  object-position: top center;
  background: var(--bd-surface-2);
  border-bottom: 1px solid var(--bd-border);
  display: block;
}
/* Stand-in when a document has no renderable first page (.docx, .xlsx). */
.bd-doctile__icon {
  aspect-ratio: 3 / 4;
  display: flex; align-items: center; justify-content: center;
  font-size: 2.4rem;
  background: var(--bd-surface-2);
  border-bottom: 1px solid var(--bd-border);
}
.bd-doctile__body { padding: 8px 10px 10px; display: flex; flex-direction: column; gap: 3px; }
.bd-doctile__title {
  font-size: var(--bd-fs-caption); font-weight: 650; color: var(--bd-text);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.bd-doctile__meta { font-size: var(--bd-fs-micro); color: var(--bd-muted); }

/* View switch (list / tiles). */
.bd-viewtoggle { display: inline-flex; border: 1px solid var(--bd-border); border-radius: var(--bd-r-sm); overflow: hidden; }
.bd-viewtoggle__btn {
  border: none; background: none; cursor: pointer;
  font-family: var(--bd-font); font-size: var(--bd-fs-caption);
  color: var(--bd-muted); padding: 6px 12px;
}
.bd-viewtoggle__btn.is-active { background: var(--bd-accent-soft); color: var(--bd-accent); font-weight: 650; }

/* --- Change history ---------------------------------------------------------
   One entry per edit burst: who, when, and each field that moved. Autosaving
   folds a burst of saves into a single entry, so this reads as edits rather
   than as keystrokes. */
.bd-history__entry { padding: 10px 0; border-bottom: 1px solid var(--bd-border); }
.bd-history__entry:last-child { border-bottom: none; }
.bd-history__head {
  display: flex; align-items: baseline; gap: var(--bd-sp-2); flex-wrap: wrap;
  font-size: var(--bd-fs-caption);
}
.bd-history__actor { font-weight: 650; color: var(--bd-text); }
.bd-history__when { color: var(--bd-muted); }
.bd-history__action {
  font-size: var(--bd-fs-micro); text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--bd-muted);
}
.bd-history__change {
  font-size: var(--bd-fs-caption); color: var(--bd-muted);
  margin-top: 4px; padding-left: 2px;
}
.bd-history__field { color: var(--bd-text); font-weight: 600; }
/* The previous value, shown struck through rather than labelled "from". */
.bd-history__was { text-decoration: line-through; opacity: 0.75; }

/* --- Quick look ------------------------------------------------------------
   A glance at a document before committing to opening it. Read-only about the
   document itself; tags and notes are the two things worth capturing in the
   moment you are looking at it. */
.bd-quick__facts {
  display: grid; grid-template-columns: auto 1fr;
  gap: 4px var(--bd-sp-3); margin: 0 0 var(--bd-sp-4);
  font-size: var(--bd-fs-caption);
}
.bd-quick__facts dt { color: var(--bd-muted); }
.bd-quick__facts dd { margin: 0; color: var(--bd-text); }
.bd-quick__add { display: flex; gap: var(--bd-sp-2); margin-top: var(--bd-sp-2); }
.bd-quick__add .bd-input { flex: 1 1 auto; }

/* A card arrived at by deep link, briefly outlined so the eye lands on it. */
.is-flagged { box-shadow: 0 0 0 2px var(--bd-accent); transition: box-shadow 0.2s; }

/* ── Logo preview (Administration → Company Details) ─────────────────────
   A fixed-size well that shows an uploaded mark on the ground it will
   actually print on — paper white for the full-colour logo, the document
   header band for the reversed (white) one. Both are theme-independent
   tokens on purpose: the preview is of the page, not of the app. */
.bd-logo-preview {
  flex: 0 0 auto; width: 132px; height: 56px; margin-right: var(--bd-sp-3);
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--bd-paper); border: 1px solid var(--bd-border);
  border-radius: var(--bd-radius-sm, 6px); overflow: hidden;
}
.bd-logo-preview img { max-width: 120px; max-height: 44px; width: auto; height: auto; object-fit: contain; }
.bd-logo-preview--dark { background: var(--bd-paper-band); border-color: var(--bd-paper-band); }

/* ── Document preview (Administration → Document Templates) ──────────────
   Sample-data renders of a document type. Paper white on purpose — it is a
   printed page, not app surface — and side by side with the email engine
   when asked, so the two channels can be seen to match. */
.bd-preview-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--bd-sp-4); }
.bd-preview-grid--single { grid-template-columns: 1fr; }
@media (max-width: 1080px) { .bd-preview-grid { grid-template-columns: 1fr; } }
.bd-preview-frame {
  width: 100%; height: 760px; border: 1px solid var(--bd-border);
  border-radius: var(--bd-radius-sm, 6px); background: var(--bd-paper); display: block;
}
.bd-table tr.is-selected > td { background: var(--bd-surface-2); }

/* --- Shell Inbox -------------------------------------------------------------
   Notifications + job-sheet chats behind the speech-bubble button in the
   shell chrome (shell.js markup, shell_inbox.js behaviour). Tokens only, so
   it follows the theme; the panel is the help-panel dropdown widened into a
   two-tab list with a chat thread view. */
.bd-inbox-wrap { position: relative; }
.bd-inbox-btn { position: relative; }
.bd-inbox-btn.has-unread { color: var(--bd-chrome-strong); }
.bd-inbox-badge {
  position: absolute; top: -4px; right: -4px;
  min-width: 18px; height: 18px; padding: 0 5px; box-sizing: border-box;
  border-radius: var(--bd-r-pill);
  background: var(--bd-brand-buoy); color: var(--bd-chrome-strong);
  font-size: var(--bd-fs-micro); font-weight: 700; line-height: 18px; text-align: center;
  border: 2px solid var(--bd-chrome);
  pointer-events: none;
}
.bd-inbox-badge.hidden { display: none; }
#bd-shell .bd-inbox-panel {
  width: min(440px, 94vw); padding: 0; gap: 0; overflow: hidden;
  background: var(--bd-surface); border-color: var(--bd-border);
  border-radius: 14px; box-shadow: var(--bd-shadow-2); color: var(--bd-text);
  font-family: var(--bd-font);
}
.bd-inbox { display: flex; flex-direction: column; max-height: min(72vh, 680px); min-height: 280px; }
.bd-inbox--thread { height: min(72vh, 680px); }
.bd-inbox__head {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 16px 10px;
}
.bd-inbox--thread .bd-inbox__head { border-bottom: 1px solid var(--bd-border); padding: 12px 14px 10px; }
.bd-inbox__link {
  flex: 0 0 auto; background: none; border: none; cursor: pointer; font-family: inherit;
  font-size: var(--bd-fs-caption); font-weight: 600; color: var(--bd-accent);
  padding: 5px 8px; border-radius: var(--bd-r-sm);
}
.bd-inbox__link:hover { background: var(--bd-accent-soft); }
.bd-inbox__link.hidden { display: none; }
.bd-inbox__title { font-size: var(--bd-fs-title); font-weight: 650; color: var(--bd-text); flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-inbox__headtext { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; }
.bd-inbox__headtext .bd-inbox__title { flex: 0 0 auto; }
.bd-inbox__sub { font-size: var(--bd-fs-caption); color: var(--bd-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-inbox__back {
  flex: 0 0 auto; width: 30px; height: 30px; border-radius: 50%;
  background: var(--bd-surface-2); border: 1px solid var(--bd-border); color: var(--bd-text);
  font-size: 18px; line-height: 1; cursor: pointer; font-family: inherit;
}
.bd-inbox__back:hover { background: var(--bd-accent-soft); color: var(--bd-accent); }
.bd-inbox__tabs {
  flex: 0 0 auto; display: flex; gap: 3px; margin: 0 12px 10px; padding: 3px;
  background: var(--bd-surface-2); border-radius: var(--bd-r-pill);
}
.bd-inbox__tab {
  flex: 1 1 0; display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 7px 10px; border-radius: var(--bd-r-pill); border: none; background: transparent;
  font-family: inherit; font-size: 12.5px; font-weight: 600; color: var(--bd-muted); cursor: pointer;
}
.bd-inbox__tab:hover { color: var(--bd-text); }
.bd-inbox__tab:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: -2px; }
.bd-inbox__tab.is-active { background: var(--bd-surface); color: var(--bd-text); box-shadow: var(--bd-shadow-1); }
.bd-inbox__count {
  display: inline-block; min-width: 18px; padding: 1px 6px;
  border-radius: var(--bd-r-pill); background: var(--bd-accent); color: var(--bd-chrome-strong);
  font-size: var(--bd-fs-micro); font-weight: 700; line-height: 16px; text-align: center;
}
.bd-inbox__count.hidden { display: none; }
.bd-inbox__chips { padding: 0 14px 8px; flex: 0 0 auto; gap: 5px; }
.bd-inbox__chips .bd-chip { padding: 4px 10px; font-size: 11px; }
.bd-inbox__chips.hidden { display: none; }
.bd-inbox__list {
  overflow-y: auto; padding: 4px 8px 10px; display: flex; flex-direction: column; gap: 3px;
  border-top: 1px solid var(--bd-border);
}
.bd-inbox__list .bd-empty { margin: 8px 6px; }
.bd-inbox__empty {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 40px 24px 44px; text-align: center; color: var(--bd-muted); font-size: var(--bd-fs-caption);
}
.bd-inbox__emptyglyph {
  width: 48px; height: 48px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center;
  background: var(--bd-surface-2); font-size: 22px; margin-bottom: 6px;
}
.bd-inbox__emptytitle { font-size: var(--bd-fs-body); font-weight: 650; color: var(--bd-text); }
.bd-inbox__emptyline { max-width: 280px; line-height: 1.45; }
.bd-inbox__row {
  display: flex; align-items: flex-start; gap: 10px; width: 100%; text-align: left;
  padding: 9px 10px; border-radius: var(--bd-r-md); border: none; background: transparent;
  color: var(--bd-text); cursor: pointer; font-family: inherit; font-size: var(--bd-fs-body);
}
.bd-inbox__row:hover { background: var(--bd-surface-2); }
.bd-inbox__row:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: -2px; }
.bd-inbox__row.is-unread { background: var(--bd-accent-soft); box-shadow: inset 3px 0 0 var(--bd-accent); }
.bd-inbox__row.is-unread:hover { background: var(--bd-surface-2); }
.bd-inbox__glyph {
  flex: 0 0 auto; width: 28px; height: 28px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 13px;
  color: var(--bd-neutral); background: var(--bd-neutral-bg);
}
.bd-inbox__glyph--mention { color: var(--bd-info); background: var(--bd-info-bg); }
.bd-inbox__glyph--task { color: var(--bd-good); background: var(--bd-good-bg); }
.bd-inbox__glyph--request { color: var(--bd-warn); background: var(--bd-warn-bg); }
.bd-inbox__avatar {
  flex: 0 0 auto; width: 34px; height: 34px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 12px; letter-spacing: .03em;
  color: var(--bd-accent); background: var(--bd-accent-soft);
}
.bd-inbox__rowmain { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.bd-inbox__rowtitle { font-weight: 600; color: var(--bd-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-inbox__row.is-unread .bd-inbox__rowtitle { font-weight: 700; }
.bd-inbox__rowsub { font-size: var(--bd-fs-caption); color: var(--bd-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-inbox__rowbody {
  font-size: var(--bd-fs-caption); color: var(--bd-muted);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.bd-inbox__row.is-unread .bd-inbox__rowbody { color: var(--bd-text); }
.bd-inbox__rowend { flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-end; gap: 5px; }
.bd-inbox__when { font-size: var(--bd-fs-caption); color: var(--bd-muted); white-space: nowrap; }
.bd-inbox__dot { width: 9px; height: 9px; border-radius: 50%; background: var(--bd-accent); }
.bd-inbox__unread { padding: 2px 7px; }
.bd-inbox__unread::before { display: none; }
.bd-inbox__scope {
  display: inline-block; padding: 0 6px; margin-right: 4px; border-radius: var(--bd-r-pill);
  background: var(--bd-surface-2); color: var(--bd-muted); font-size: var(--bd-fs-micro); font-weight: 700;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: middle;
}

/* thread */
.bd-inbox__messages {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 12px 14px;
  display: flex; flex-direction: column; gap: 8px; background: var(--bd-bg);
}
.bd-inbox__messages .bd-inbox__empty { margin: auto; padding: 24px; }
.bd-inbox__msg { display: flex; }
.bd-inbox__msg.is-own { justify-content: flex-end; }
.bd-inbox__bubble {
  max-width: 86%; padding: 8px 11px; border-radius: 14px;
  background: var(--bd-surface); border: 1px solid var(--bd-border);
  font-size: var(--bd-fs-body); color: var(--bd-text);
  display: flex; flex-direction: column; gap: 4px;
}
.bd-inbox__msg.is-own .bd-inbox__bubble { background: var(--bd-accent-soft); border-color: transparent; }
.bd-inbox__author { font-size: var(--bd-fs-caption); font-weight: 700; color: var(--bd-accent); }
/* The task a comment was made on — a small heading atop the bubble. */
.bd-inbox__task {
  display: flex; align-items: center; gap: 5px; min-width: 0;
  padding-bottom: 4px; border-bottom: 1px solid var(--bd-border);
  font-size: var(--bd-fs-micro); font-weight: 700; letter-spacing: .02em;
  color: var(--bd-muted); text-transform: uppercase;
}
.bd-inbox__taskicon { flex: 0 0 auto; }
.bd-inbox__tasktitle { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-inbox__bubble .bd-note-media { margin-top: 2px; }
.bd-inbox__msgmeta { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }
.bd-inbox__msgmeta .bd-badge { padding: 2px 7px; }
.bd-inbox__msgmeta .bd-badge::before { display: none; }
.bd-inbox__text { white-space: pre-wrap; word-break: break-word; line-height: 1.4; }
.bd-inbox__stamp { font-size: var(--bd-fs-micro); color: var(--bd-muted); align-self: flex-end; }
.bd-inbox__docs { display: flex; flex-direction: column; gap: 4px; }
.bd-inbox__doc {
  display: flex; align-items: center; gap: 6px;
  padding: 5px 8px; border-radius: var(--bd-r-sm);
  background: var(--bd-surface-2); border: 1px solid var(--bd-border);
  color: var(--bd-accent); text-decoration: none; font-size: var(--bd-fs-caption); font-weight: 600;
  min-width: 0;
}
.bd-inbox__doc:hover { border-color: var(--bd-accent); }
.bd-inbox__doc.is-archived { color: var(--bd-muted); text-decoration: line-through; }
.bd-inbox__docicon { flex: 0 0 auto; }
.bd-inbox__doctitle { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-inbox__doctype { flex: 0 0 auto; font-weight: 500; color: var(--bd-muted); }

/* compose */
.bd-inbox__compose {
  position: relative; flex: 0 0 auto; padding: 10px 12px 12px;
  border-top: 1px solid var(--bd-border); display: flex; flex-direction: column; gap: 8px;
  background: var(--bd-surface);
}
/* Files dragged over the compose box (photos go on the message). */
.bd-inbox__compose.is-drag-over { background: var(--bd-accent-soft); box-shadow: inset 0 0 0 2px var(--bd-accent); }
.bd-inbox__nopost { padding: 12px 14px; border-top: 1px solid var(--bd-border); font-size: var(--bd-fs-caption); }
.bd-inbox__attached { display: flex; flex-wrap: wrap; gap: 6px; }
.bd-inbox__attached.hidden { display: none; }
.bd-inbox__input { min-height: 44px; font-size: var(--bd-fs-body); }
.bd-inbox__toolbar { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; }
.bd-inbox__tools { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; min-width: 0; }
.bd-inbox__select { height: 30px; padding: 0 8px; font-size: var(--bd-fs-caption); max-width: 170px; }
.bd-inbox__pop {
  position: absolute; left: 12px; right: 12px; bottom: calc(100% + 6px);
  background: var(--bd-surface); border: 1px solid var(--bd-border); border-radius: var(--bd-r-md);
  box-shadow: var(--bd-shadow-2); padding: 10px; display: flex; flex-direction: column; gap: 8px;
  z-index: 3;
}
.bd-inbox__pop.hidden { display: none; }
.bd-inbox__pophead { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: var(--bd-fs-caption); font-weight: 700; color: var(--bd-text); }
.bd-inbox__popresults { max-height: 220px; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; }
.bd-inbox__popitem {
  display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%;
  padding: 7px 9px; border-radius: var(--bd-r-sm); border: none; background: transparent;
  color: var(--bd-text); text-align: left; cursor: pointer; font-family: inherit; font-size: var(--bd-fs-body);
}
.bd-inbox__popitem:hover { background: var(--bd-surface-2); }
.bd-inbox__popitem.is-on { background: var(--bd-accent-soft); color: var(--bd-accent); }
.bd-inbox__popmain { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.bd-inbox__popmain > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-inbox__popmeta { font-size: var(--bd-fs-micro); color: var(--bd-muted); font-weight: 600; text-transform: uppercase; letter-spacing: .04em; }
.bd-inbox__tick { flex: 0 0 auto; width: 22px; height: 22px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; background: var(--bd-surface-2); font-weight: 700; font-size: 12px; }
.bd-inbox__popitem.is-on .bd-inbox__tick { background: var(--bd-accent); color: var(--bd-chrome-strong); }
.bd-inbox__popmore { padding: 6px 9px 2px; font-size: var(--bd-fs-micro); }

/* --- Job chat popup -----------------------------------------------------------
   The job sheet's 💬 Chat button: a large modal with the job's tasks down the
   left and the selected conversation on the right. "Whole job" (the default)
   is every message on the job, each task comment headed by its task; picking
   a task narrows to that task's thread and posts onto it. The conversation
   pane reuses the Inbox thread markup (.bd-inbox__messages / __compose). */
.bd-jobchat { max-width: min(1080px, 96vw); height: min(84vh, 820px); max-height: 84vh; overflow: hidden; }
.bd-jobchat__head .bd-inbox__headtext { flex: 1 1 auto; }
.bd-jobchat__body { flex: 1 1 auto; min-height: 0; display: flex; }
.bd-jobchat__side {
  flex: 0 0 280px; min-width: 0; display: flex; flex-direction: column;
  border-right: 1px solid var(--bd-border); background: var(--bd-surface);
}
.bd-jobchat__sidehead {
  flex: 0 0 auto; padding: 12px 14px 6px;
  font-size: var(--bd-fs-micro); font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--bd-muted);
}
.bd-jobchat__list { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 0 8px 10px; display: flex; flex-direction: column; gap: 2px; }
.bd-jobchat__item {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  padding: 8px 10px; border-radius: var(--bd-r-md); border: none; background: transparent;
  color: var(--bd-text); cursor: pointer; font-family: inherit; font-size: var(--bd-fs-body);
}
.bd-jobchat__item:hover { background: var(--bd-surface-2); }
.bd-jobchat__item:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: -2px; }
.bd-jobchat__item.is-active { background: var(--bd-accent-soft); box-shadow: inset 3px 0 0 var(--bd-accent); }
.bd-jobchat__item--sub { padding-left: 26px; }
.bd-jobchat__item--job { font-weight: 650; margin-bottom: 6px; }
.bd-jobchat__num { flex: 0 0 auto; min-width: 28px; font-size: var(--bd-fs-caption); font-weight: 700; color: var(--bd-muted); font-variant-numeric: tabular-nums; }
.bd-jobchat__label { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.bd-jobchat__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-jobchat__last { font-size: var(--bd-fs-caption); color: var(--bd-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-jobchat__end { flex: 0 0 auto; display: flex; align-items: center; gap: 5px; }
.bd-jobchat__n {
  min-width: 20px; padding: 1px 6px; border-radius: var(--bd-r-pill); text-align: center;
  background: var(--bd-surface-2); color: var(--bd-muted);
  font-size: var(--bd-fs-micro); font-weight: 700; line-height: 16px;
}
.bd-jobchat__n.has-photos::after { content: " 📷"; }
.bd-jobchat__item.is-active .bd-jobchat__n { background: var(--bd-surface); color: var(--bd-accent); }
.bd-jobchat__main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.bd-jobchat__mainhead {
  flex: 0 0 auto; display: flex; align-items: center; gap: 8px;
  padding: 10px 16px; border-bottom: 1px solid var(--bd-border);
}
.bd-jobchat__maintitle { flex: 1 1 auto; min-width: 0; font-weight: 650; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-jobchat__main .bd-inbox__bubble { max-width: 72%; }
.bd-jobchat__main .bd-note-media__thumb { width: 96px; height: 96px; }
.bd-jobchat__sidetoggle { display: none; }
@media (max-width: 760px) {
  .bd-jobchat { height: 92vh; max-height: 92vh; }
  .bd-jobchat__side { display: none; flex-basis: auto; width: 100%; border-right: none; }
  .bd-jobchat.is-picking .bd-jobchat__side { display: flex; }
  .bd-jobchat.is-picking .bd-jobchat__main { display: none; }
  .bd-jobchat__sidetoggle { display: inline-flex; }
  .bd-jobchat__main .bd-inbox__bubble { max-width: 88%; }
}

/* Job-sheet Notes panel: documents attached from the Inbox chat. */
.jqv-note-docs { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }

/* --- Job window Gantt (bd-gantt) --------------------------------------------
   The scheduler's Overview: one row per technician, one bar per job sheet
   spanning its start → due window. Day columns are set inline (the count
   varies with the period); bars take grid-row lanes so overlapping jobs stack.
   Bar tone comes from the five semantic slots only. */
.bd-gantt { display: flex; flex-direction: column; min-width: 0; background: var(--bd-surface); color: var(--bd-text); }
.bd-gantt__head,
.bd-gantt__row { display: grid; grid-template-columns: 156px minmax(0, 1fr); border-bottom: 1px solid var(--bd-border); }
.bd-gantt__head { position: sticky; top: 0; z-index: 4; background: var(--bd-surface); }
.bd-gantt__corner,
.bd-gantt__label {
  display: flex; flex-direction: column; justify-content: center; gap: 2px;
  padding: 8px 10px; border-right: 1px solid var(--bd-border); min-width: 0;
}
.bd-gantt__corner strong,
.bd-gantt__label strong { font-size: var(--bd-fs-body); color: var(--bd-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bd-gantt__corner span,
.bd-gantt__label span { font-size: var(--bd-fs-caption); color: var(--bd-muted); }
.bd-gantt__days { display: grid; }
/* Seven day columns unless the page sets its own count inline. */
.bd-gantt__days, .bd-gantt__grid, .bd-gantt__track { grid-template-columns: repeat(7, minmax(0, 1fr)); }
.bd-gantt__day {
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 6px 0; border-left: 1px solid var(--bd-border);
  font-size: var(--bd-fs-micro); color: var(--bd-muted); min-width: 0; overflow: hidden;
}
.bd-gantt__day strong { font-size: var(--bd-fs-caption); color: var(--bd-text); }
.bd-gantt__day.is-weekend { background: var(--bd-surface-2); }
.bd-gantt__day.is-today { background: var(--bd-accent-soft); }
.bd-gantt__day.is-today strong { color: var(--bd-accent); }
.bd-gantt__lane { position: relative; min-width: 0; min-height: 46px; }
.bd-gantt__grid { position: absolute; inset: 0; display: grid; }
.bd-gantt__cell { border-left: 1px solid var(--bd-border); }
.bd-gantt__cell.is-weekend { background: var(--bd-surface-2); }
.bd-gantt__cell.is-today { background: var(--bd-accent-soft); }
.bd-gantt__cell.is-absent {
  background: repeating-linear-gradient(135deg, var(--bd-neutral-bg) 0 6px, transparent 6px 12px);
}
.bd-gantt__track { position: relative; display: grid; grid-auto-rows: 28px; row-gap: 4px; padding: 8px 0; }
.bd-gantt__bar {
  display: flex; align-items: center; gap: 6px; min-width: 0; margin: 0 3px;
  padding: 0 8px; border: 1px solid var(--bd-info); border-left-width: 4px;
  border-radius: var(--bd-r-sm); background: var(--bd-info-bg); color: var(--bd-text);
  font-family: inherit; font-size: var(--bd-fs-caption); text-align: left;
  white-space: nowrap; overflow: hidden; cursor: pointer;
}
.bd-gantt__bar:hover { box-shadow: var(--bd-shadow-1); }
.bd-gantt__bar:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: 1px; }
.bd-gantt__bar strong { flex: 0 0 auto; font-weight: 700; }
.bd-gantt__bar-text { overflow: hidden; text-overflow: ellipsis; color: var(--bd-muted); }
.bd-gantt__bar--good { border-color: var(--bd-good); background: var(--bd-good-bg); }
.bd-gantt__bar--warn { border-color: var(--bd-warn); background: var(--bd-warn-bg); }
.bd-gantt__bar--crit { border-color: var(--bd-crit); background: var(--bd-crit-bg); }
.bd-gantt__bar--neutral { border-color: var(--bd-neutral); background: var(--bd-neutral-bg); border-style: dashed; border-left-style: solid; }
/* The window runs on past the edge of the period shown. */
.bd-gantt__bar.is-clipped-start { border-left-style: dashed; border-top-left-radius: 0; border-bottom-left-radius: 0; margin-left: 0; }
.bd-gantt__bar.is-clipped-end { border-right-style: dashed; border-top-right-radius: 0; border-bottom-right-radius: 0; margin-right: 0; }
.bd-gantt__flag { flex: 0 0 auto; color: var(--bd-crit); font-weight: 700; }

/* Scheduler Overview toggle — sits after "Next free day", filled so it reads
   as a mode switch rather than another navigation step. */
.sched-overview-btn { flex: 1 1 auto; justify-content: center; white-space: nowrap; font-size: .78rem; font-weight: 700; }
.sched-overview-btn[aria-pressed="true"] { background: var(--bd-accent-strong); border-color: var(--bd-accent-strong); }

/* A task block outside its job's start → due window: saved, but flagged. */
.scheduler-v2-booking.is-out-of-window { outline: 2px dashed var(--bd-crit); outline-offset: -2px; }
/* Job not ready to start (Workshop Job Management slice 3) — booked, but
   flagged in the warn slot; the window flag (crit) wins when both apply. */
.scheduler-v2-booking.is-not-ready:not(.is-out-of-window) { outline: 2px dashed var(--bd-warn); outline-offset: -2px; }
.scheduler-v2-booking .card-ready-flag { color: var(--bd-warn); }
.scheduler-v2-booking .card-window-flag {
  position: absolute; top: 2px; right: 4px; z-index: 2;
  font-size: var(--bd-fs-caption); font-weight: 700; color: var(--bd-crit); line-height: 1;
}

/* ── Skills matrix (Administration → Skills; scheduler tech picker) ───────
   bd-skillcell: one matrix cell — a button that cycles a technician's level
   in a trade (none → learning → competent → lead). Levels map onto the
   semantic slots: learning = warn, competent = good, lead = info, none =
   neutral. bd-skillmark: the compact ✓ / ~ / — glyph beside a technician's
   name where a task needs a trade. No colour transitions (theme repaint). */
.bd-skillcell {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 92px; padding: 5px 10px;
  font-family: var(--bd-font); font-size: var(--bd-fs-caption); font-weight: 600; line-height: 1;
  border: 1px solid transparent; border-radius: var(--bd-r-pill);
  cursor: pointer; white-space: nowrap;
}
.bd-skillcell:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: 2px; }
.bd-skillcell:disabled { cursor: default; }
.bd-skillcell--none      { color: var(--bd-muted);   background: transparent; border-color: var(--bd-border); border-style: dashed; }
.bd-skillcell--learning  { color: var(--bd-warn);    background: var(--bd-warn-bg); }
.bd-skillcell--competent { color: var(--bd-good);    background: var(--bd-good-bg); }
.bd-skillcell--lead      { color: var(--bd-info);    background: var(--bd-info-bg); border-color: var(--bd-info); }
.bd-skillcell.is-saving  { opacity: .55; }

.bd-skillmark {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; flex: none;
  font-size: var(--bd-fs-caption); font-weight: 800; line-height: 1;
  border-radius: 50%;
}
.bd-skillmark--good    { color: var(--bd-good);    background: var(--bd-good-bg); }
.bd-skillmark--warn    { color: var(--bd-warn);    background: var(--bd-warn-bg); }
.bd-skillmark--neutral { color: var(--bd-neutral); background: var(--bd-neutral-bg); }
.bd-skillneed { font-size: var(--bd-fs-caption); color: var(--bd-muted); }

/* ── Row strip — a note under a list row (task awaiting the customer, a
   declined item). Slot modifiers only; the warn strip matches the amber
   part-hold strip on the job sheet. ─────────────────────────────────── */
.bd-rowstrip {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 6px 12px; border-top: 1px solid var(--bd-border);
  font-size: var(--bd-fs-caption); color: var(--bd-muted);
}
.bd-rowstrip--warn    { background: var(--bd-warn-bg);    border-top-color: var(--bd-warn); }
.bd-rowstrip--info    { background: var(--bd-info-bg);    border-top-color: var(--bd-info); }
.bd-rowstrip--neutral { background: var(--bd-neutral-bg); }

/* ── Photo thumbnails (findings, reports) — click opens the full image ── */
.bd-thumbs { display: flex; gap: 6px; flex-wrap: wrap; }
.bd-thumb {
  display: block; width: 64px; height: 64px; flex: none;
  border-radius: var(--bd-r-sm); border: 1px solid var(--bd-border);
  background: var(--bd-surface-2); object-fit: cover;
}
.bd-thumb:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: 2px; }
/* A photo you tick to include (customer pack): the thumbnail is the label,
   the box sits in its corner, a ticked photo gets the accent ring. */
.bd-thumbpick { position: relative; display: block; cursor: pointer; }
.bd-thumbpick .bd-thumb { width: 96px; height: 96px; }
.bd-thumbpick input { position: absolute; top: 4px; left: 4px; margin: 0; accent-color: var(--bd-accent); }
.bd-thumbpick:has(input:checked) .bd-thumb { outline: 2px solid var(--bd-accent); outline-offset: 1px; }
.bd-thumbpick__cap {
  display: block; width: 96px; margin-top: 2px; font-size: 11px; color: var(--bd-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Space above a card that follows another card in a legacy page column. */
.bd-card--gap { margin-top: var(--bd-sp-4); }

/* ── Comments: composer with drag-and-drop attachments + attachment strip ──
   One comment thread shared by the web job sheet and the app (task
   Comments tab). The whole composer is the drop target — dropping a photo
   or PDF on the box is the fast path; the Attach button opens the picker
   for the same queue. Pending files show as removable chips until posted.
   Posted attachments: image thumbnails that open full size, voice notes as
   an inline player, anything else as a file chip that downloads. */
.bd-comment-composer {
  display: flex; flex-direction: column; gap: var(--bd-sp-2);
  padding: var(--bd-sp-2); border: 1px dashed transparent; border-radius: var(--bd-r-md);
}
.bd-comment-composer.is-drag-over { border-color: var(--bd-accent); background: var(--bd-accent-soft); }
.bd-comment-composer__input { resize: vertical; min-height: 56px; font-size: var(--bd-fs-body); }
.bd-comment-composer__row { display: flex; align-items: center; gap: var(--bd-sp-2); flex-wrap: wrap; }
.bd-comment-composer__row .bd-comment-composer__spacer { flex: 1 1 auto; }
.bd-comment-composer__row .bd-select { width: auto; max-width: 220px; flex: 0 1 auto; }
.bd-comment-composer__hint { font-size: var(--bd-fs-caption); color: var(--bd-muted); }
.bd-comment-composer__file { display: none; }
.bd-comment-composer__queue { display: flex; flex-wrap: wrap; gap: 6px; }
.bd-comment-composer__queue:empty { display: none; }
.bd-attach-chip {
  display: inline-flex; align-items: center; gap: 6px; max-width: 220px;
  padding: 3px 4px 3px 8px; border-radius: var(--bd-r-pill);
  background: var(--bd-surface-2); border: 1px solid var(--bd-border);
  font-size: var(--bd-fs-caption); color: var(--bd-text);
}
.bd-attach-chip__name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A queued image shows what it is before it is sent (Inbox / job chat). */
.bd-attach-chip__thumb { flex: 0 0 auto; width: 22px; height: 22px; margin-left: -4px; border-radius: 50%; object-fit: cover; }
.bd-attach-chip__remove {
  border: 0; background: transparent; color: var(--bd-muted); cursor: pointer;
  width: 20px; height: 20px; border-radius: var(--bd-r-pill); line-height: 1;
}
.bd-attach-chip__remove:hover { color: var(--bd-text); background: var(--bd-border); }

.bd-note-media { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.bd-note-media__thumb {
  display: block; width: 72px; height: 72px; border-radius: var(--bd-r-sm);
  overflow: hidden; border: 1px solid var(--bd-border); background: var(--bd-surface-2);
}
.bd-note-media__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.bd-note-media__thumb:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: 2px; }
.bd-note-media__audio { height: 32px; max-width: 260px; }
.bd-note-media__file {
  display: inline-flex; align-items: center; gap: 6px; max-width: 260px;
  padding: 5px 8px; border-radius: var(--bd-r-sm);
  background: var(--bd-surface-2); border: 1px solid var(--bd-border);
  color: var(--bd-accent); text-decoration: none; font-size: var(--bd-fs-caption); font-weight: 600;
}
.bd-note-media__file:hover { border-color: var(--bd-accent); }
.bd-note-media__filename { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A photo with its "share with owner" chip underneath (owner app, C0.3). */
.bd-note-media__item { display: inline-flex; flex-direction: column; align-items: center; gap: 4px; }
.bd-note-media__share { padding: 2px 8px; font-size: 10.5px; }

/* The selected row in a pick-list of bd-listrow buttons. */
.bd-listrow.is-active { background: var(--bd-surface-2); box-shadow: inset 3px 0 0 var(--bd-accent); }

/* ---------- engine link panel — link an engine to a boat without leaving a
   flow (job sheet engine-service popup). Location + serial + catalogue
   search; results are ordinary .bd-row items with a Link button. ---------- */
.bd-enginelink {
  display: flex; flex-direction: column; gap: var(--bd-sp-3);
  padding: 12px; border: 1px solid var(--bd-border);
  border-radius: var(--bd-r-md); background: var(--bd-surface-2);
}
.bd-enginelink__form {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--bd-sp-2);
}
.bd-enginelink__form > .bd-field--span2 { grid-column: 1 / -1; }
.bd-enginelink .bd-input, .bd-enginelink .bd-select { width: 100%; box-sizing: border-box; }
.bd-enginelink__results { max-height: 240px; overflow-y: auto; }
@media (max-width: 560px) { .bd-enginelink__form { grid-template-columns: 1fr; } }

/* ---------- disclosure — secondary options folded behind a native <details>
   (e.g. the other standard service sheets behind the Default one). ---------- */
.bd-disclosure > summary {
  cursor: pointer; padding: 6px 0;
  font-size: var(--bd-fs-caption); font-weight: 600; color: var(--bd-muted);
}
.bd-disclosure > summary:hover { color: var(--bd-text); }
.bd-disclosure[open] > summary { margin-bottom: 6px; }

/* Label designer (Stock settings → Labels). The stage is the label at
   screen scale on paper white in both themes; each item is the printer's
   own bitmap for that item, so what moves is what prints. */
.bd-labeldesigner { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 14px; align-items: start; }
@media (max-width: 900px) { .bd-labeldesigner { grid-template-columns: minmax(0, 1fr); } }
.bd-labeldesigner__toolbar { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 10px; }
.bd-labeldesigner__toolbar .bd-select, .bd-labeldesigner__toolbar .bd-input { width: auto; }
.bd-labelstage-wrap {
  display: flex; justify-content: center; align-items: flex-start; overflow: auto;
  min-height: 220px; padding: 28px 20px;
  background: var(--bd-surface-2); border: 1px solid var(--bd-border); border-radius: var(--bd-r-sm);
}
.bd-labelstage {
  position: relative; flex: none; background: var(--bd-paper); border-radius: 3px;
  box-shadow: var(--bd-shadow-1); touch-action: none; user-select: none;
}
.bd-labelstage:focus { outline: none; }
.bd-labelstage:focus-visible { outline: 2px solid var(--bd-accent); outline-offset: 4px; }
.bd-labelitem { position: absolute; cursor: move; }
.bd-labelitem img { display: block; width: 100%; height: 100%; image-rendering: pixelated; pointer-events: none; }
.bd-labelitem:hover { outline: 1px dashed var(--bd-paper-ink); }
.bd-labelitem.is-warn { outline: 1px dashed var(--bd-warn); }
.bd-labelitem.is-selected { outline: 1.5px solid var(--bd-accent); z-index: 2; }
.bd-labelitem__empty {
  display: flex; align-items: center; justify-content: center; width: 100%; height: 100%;
  overflow: hidden; border: 1px dashed var(--bd-paper-ink);
  color: var(--bd-paper-ink); font-size: 10px; line-height: 1.1; text-align: center;
}
.bd-labelitem__handle {
  position: absolute; width: 9px; height: 9px; transform: translate(-50%, -50%);
  background: var(--bd-paper); border: 1.5px solid var(--bd-accent); border-radius: 2px;
}
.bd-labelitem__handle--nw { left: 0; top: 0; cursor: nwse-resize; }
.bd-labelitem__handle--n  { left: 50%; top: 0; cursor: ns-resize; }
.bd-labelitem__handle--ne { left: 100%; top: 0; cursor: nesw-resize; }
.bd-labelitem__handle--e  { left: 100%; top: 50%; cursor: ew-resize; }
.bd-labelitem__handle--se { left: 100%; top: 100%; cursor: nwse-resize; }
.bd-labelitem__handle--s  { left: 50%; top: 100%; cursor: ns-resize; }
.bd-labelitem__handle--sw { left: 0; top: 100%; cursor: nesw-resize; }
.bd-labelitem__handle--w  { left: 0; top: 50%; cursor: ew-resize; }
.bd-labelguide { position: absolute; pointer-events: none; background: var(--bd-accent); z-index: 3; }
.bd-labelguide--v { top: 0; bottom: 0; width: 1px; }
.bd-labelguide--h { left: 0; right: 0; height: 1px; }
.bd-checklist { display: flex; flex-direction: column; }
.bd-checklist__group {
  margin: 10px 0 2px; font-size: var(--bd-fs-caption); font-weight: 650; color: var(--bd-muted);
}
.bd-checklist__group:first-child { margin-top: 0; }
.bd-checklist__row { display: flex; align-items: center; gap: 8px; padding: 4px 2px; cursor: pointer; color: var(--bd-text); }
.bd-checklist__row input { accent-color: var(--bd-accent); margin: 0; }
.bd-checklist__row .bd-checklist__name { flex: 1; min-width: 0; }
.bd-checklist__adds { display: flex; flex-wrap: wrap; gap: 6px; padding-top: 4px; }
.bd-labelprops__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.bd-labelprops__actions { display: flex; flex-wrap: wrap; gap: 6px; }


/* ── Menu — a button that drops a short list of actions (bd-menu) ──────────
   <div class="bd-menu"><button class="bd-btn" data-bd-menu>More ▾</button>
     <div class="bd-menu__list" role="menu"><button class="bd-menu__item">…</button></div></div>
   BD.menu (components.js) opens/closes it; Esc and a click elsewhere close it.
   Keep lists short — a handful of actions that don't earn a toolbar slot. */
.bd-menu { position: relative; display: inline-flex; }
.bd-menu__list {
  display: none; position: absolute; right: 0; top: 100%; z-index: 60;
  margin-top: 4px; min-width: 200px; padding: 4px;
  flex-direction: column; gap: 2px;
  background: var(--bd-surface); border: 1px solid var(--bd-border);
  border-radius: var(--bd-r-md); box-shadow: var(--bd-shadow-2);
}
.bd-menu.is-open .bd-menu__list { display: flex; }
/* A card clips its content to its rounded corners; a row menu opened inside
   one would be cut off at the card's edge, so the card lets go while it is open. */
.bd-card:has(.bd-menu.is-open) { overflow: visible; z-index: 2; }
.bd-menu__item {
  display: flex; align-items: center; gap: 8px; width: 100%;
  text-align: left; font: inherit; white-space: nowrap;
  padding: 7px 12px; border: 0; background: none; color: var(--bd-text);
  border-radius: var(--bd-r-sm); cursor: pointer;
}
.bd-menu__item:hover, .bd-menu__item:focus-visible { background: var(--bd-surface-2); outline: none; }
.bd-menu__item.is-active { background: var(--bd-accent-soft); color: var(--bd-accent); font-weight: 600; }
/* A destructive action (Delete) — last in the list, below a separator. */
.bd-menu__item--danger { color: var(--bd-crit); }
.bd-menu__item--danger:hover, .bd-menu__item--danger:focus-visible { background: var(--bd-crit-bg); }
.bd-menu__sep { height: 1px; margin: 4px 2px; background: var(--bd-border); }


/* ── Readable page — a converted document drawn word by word (bd-readpage) ──
   <div class="bd-readpage"><div class="bd-readpage__sheet">
     <button class="bd-readpage__word">4262060</button> …</div></div>
   Used by the supplier invoice review: the converter gives every word with its
   position, JS places each one (left/top/width/font-size set from the page's
   own geometry), and clicking a word fills the field the reviewer has picked.
   Words are buttons so the page is keyboard-reachable. The sheet is a paper-
   coloured surface in both themes; words inherit --bd-text. */
.bd-readpage {
  background: var(--bd-surface-2);
  border-radius: var(--bd-r-md);
  padding: var(--bd-sp-3);
  max-height: 80vh;
  overflow: auto;
}
.bd-readpage__sheet {
  position: relative;
  margin: 0 auto;
  background: var(--bd-surface);
  border: 1px solid var(--bd-border);
  border-radius: var(--bd-r-sm);
  box-shadow: var(--bd-shadow-1);
}
.bd-readpage__word {
  position: absolute;
  padding: 0;
  margin: 0;
  border: 0;
  border-radius: 2px;
  background: none;
  color: var(--bd-text);
  font-family: var(--bd-font);
  line-height: 1;
  white-space: nowrap;
  overflow: visible;
  cursor: pointer;
  text-align: left;
}
.bd-readpage__word:hover,
.bd-readpage__word:focus-visible { background: var(--bd-accent-soft); color: var(--bd-accent); outline: none; }
.bd-readpage__word.is-used { background: var(--bd-good-bg); }
.bd-readpage__word.is-picked { background: var(--bd-accent-soft); outline: 1px solid var(--bd-accent); }
.bd-readpage.is-readonly .bd-readpage__word { cursor: default; }
.bd-readpage__empty {
  padding: var(--bd-sp-5);
  text-align: center;
  color: var(--bd-muted);
  font-size: var(--bd-fs-caption);
}
/* The form field the next clicked word will fill. */
.bd-input.is-fill-target, .bd-select.is-fill-target {
  border-color: var(--bd-accent);
  box-shadow: 0 0 0 2px var(--bd-accent-soft);
}
/* The original file, for documents the converter could not read. */
.bd-readpage__frame { display: block; width: 100%; height: 75vh; border: 0; border-radius: var(--bd-r-sm); background: var(--bd-surface); }

/* ── Editable line grid — a table of small inputs (bd-linegrid) ────────────
   Invoice lines on the supplier invoice review: every cell is an input, so the
   inputs shrink to their column instead of their default form width. Column
   width classes keep code/description wide and the numbers narrow. */
.bd-linegrid { width: 100%; min-width: 44rem; border-collapse: collapse; table-layout: fixed; font-size: var(--bd-fs-caption); }
.bd-linegrid th { text-align: left; font-weight: 600; color: var(--bd-muted); padding: 4px 3px; border-bottom: 1px solid var(--bd-border); }
.bd-linegrid td { padding: 3px; vertical-align: top; }
.bd-linegrid .bd-input, .bd-linegrid .bd-select { width: 100%; min-width: 0; padding: 4px 6px; font-size: var(--bd-fs-caption); }
.bd-linegrid .num .bd-input { text-align: right; font-variant-numeric: tabular-nums; }
.bd-linegrid__type { width: 5.5rem; }
.bd-linegrid__code { width: 7.5rem; }
.bd-linegrid__qty { width: 3.75rem; }
.bd-linegrid__money { width: 5.25rem; }
.bd-linegrid__rate { width: 3.5rem; }
.bd-linegrid__act { width: 2rem; }
.bd-linegrid tr.is-off td .bd-input { border-color: var(--bd-warn); }
.bd-checklist { display: flex; flex-direction: column; gap: 4px; margin: 0; padding: 0; list-style: none; font-size: var(--bd-fs-caption); }

/* Berth timeline (Marina → Timeline, map Slice M10): spaces as rows, stays and
   bookings as bars in the five map meanings. Drawn as SVG; every colour is a
   class here so the theme flips it. */
.bd-timeline { overflow-x: auto; border: 1px solid var(--bd-border); border-radius: var(--bd-radius); background: var(--bd-surface); }
.bd-timeline svg { display: block; font-family: inherit; }
.bd-tl-grid { stroke: var(--bd-border); stroke-width: 1; }
.bd-tl-weekend { fill: var(--bd-surface-2); }
.bd-tl-today { stroke: var(--bd-accent); stroke-width: 2; }
.bd-tl-head { fill: var(--bd-muted); font-size: 11px; }
.bd-tl-head--month { fill: var(--bd-text); font-weight: 700; }
.bd-tl-label { fill: var(--bd-text); font-size: 12px; font-weight: 600; cursor: pointer; }
.bd-tl-group { fill: var(--bd-muted); font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; }
.bd-tl-bar { stroke-width: 1.5; cursor: pointer; }
.bd-tl-bar--neutral { fill: var(--bd-map-neutral-fill); stroke: var(--bd-map-neutral-line); }
.bd-tl-bar--info { fill: var(--bd-map-info-fill); stroke: var(--bd-map-info-line); stroke-dasharray: 5 3; }
.bd-tl-bar--good { fill: var(--bd-map-good-fill); stroke: var(--bd-map-good-line); }
.bd-tl-bar--warn { fill: var(--bd-map-warn-fill); stroke: var(--bd-map-warn-line); stroke-width: 2.5; }
.bd-tl-bar--crit { fill: var(--bd-map-crit-fill); stroke: var(--bd-map-crit-line); stroke-width: 2.5; }
.bd-tl-bar--hatch { fill: url(#bd-tl-hatch); }
.bd-tl-hatch-line { stroke: var(--bd-map-crit-line); stroke-width: 2; }
.bd-tl-bartext { fill: var(--bd-map-ink); font-size: 11px; pointer-events: none; }
.bd-tl-open { fill: var(--bd-map-ink); font-size: 11px; pointer-events: none; }

/* Signature pad (marina contracts Slice 11): a drawn signature on paper —
   white with dark ink in both themes (tokens --bd-sigpad-*). */
.bd-sigpad { display: block; width: 100%; max-width: 560px; height: 180px; background: var(--bd-sigpad-bg);
  border: 1px solid var(--bd-border); border-radius: var(--bd-radius); touch-action: none; cursor: crosshair; }
.bd-sigpad__actions { display: flex; gap: var(--bd-sp-3); margin-top: var(--bd-sp-2); }
.bd-docframe { width: 100%; height: 520px; border: 1px solid var(--bd-border); border-radius: var(--bd-radius);
  background: var(--bd-sigpad-bg); }

