/*
  BurgiTech Portal — design system
  Dark product UI derived from the RB Console dashboard study
  (Revolut Business measured styles), re-tokened for the Burgi brand:
  black canvas, teal accent/actions, Inter interface type with
  Satoshi reserved for brand/display moments.
  Shared by index.html (technician app) and admin.html.

  Two themes. Dark is the original and the default; every colour role below is
  its dark value. `:root[data-theme="light"]` (further down) re-points the same
  roles at a light palette built from the same Burgi hues — teal accent, ink,
  and the four status colours are unchanged, only lightness and the -text shades
  move so text stays legible on white. personalization.js sets data-theme from
  the signed-in account's `theme` preference; nothing here follows the OS.
  Keep the light block and apps/portal-app/src/ui/theme.ts's LightColors in sync.
*/

:root {
  color-scheme: dark;

  /* Colour roles */
  --bt-canvas: #000;
  --bt-surface: rgba(255, 255, 255, 0.12);
  --bt-surface-nested: rgba(255, 255, 255, 0.1);
  --bt-surface-raised: rgba(255, 255, 255, 0.06);
  --bt-surface-field: rgba(255, 255, 255, 0.16);
  /* The edge of anything you type into or pick from. Fields sit on the canvas,
     on panels and on sheets, and no one fill separates from all three. */
  --bt-field-line: rgba(255, 255, 255, 0.45);
  --bt-surface-interactive: rgba(255, 255, 255, 0.26);
  --bt-surface-popover: #1e3238;
  --bt-dock: rgba(9, 30, 35, 0.92);
  --bt-text-primary: #fff;
  --bt-text-secondary: rgba(255, 255, 255, 0.76);
  --bt-text-disabled: rgba(255, 255, 255, 0.38);
  --bt-ink: #001318;
  --bt-accent: #18bfcc;
  --bt-accent-deep: #139fab;
  --bt-accent-soft: rgba(24, 191, 204, 0.16);
  --bt-accent-text: #63d8e2;
  --bt-info: #4ca6ff;
  --bt-info-soft: rgba(76, 166, 255, 0.16);
  --bt-info-text: #8fc4ff;
  --bt-neutral: rgba(255, 255, 255, 0.34);
  /* Not necessary: settled on purpose. Violet, so it never reads as the grey
     of a step still to do or as any other status hue. */
  --bt-skip: #a48bf5;
  --bt-skip-soft: rgba(164, 139, 245, 0.16);
  --bt-skip-text: #c3b3fa;
  --bt-positive: #1ac097;
  --bt-positive-soft: rgba(26, 192, 151, 0.14);
  --bt-positive-text: #62dabd;
  --bt-warning: #ff9f0a;
  --bt-warning-soft: rgba(255, 159, 10, 0.14);
  --bt-warning-text: #ffc15c;
  --bt-danger: #ff5363;
  --bt-danger-soft: rgba(255, 83, 99, 0.14);
  --bt-danger-text: #ff8a96;
  --bt-line: rgba(255, 255, 255, 0.12);
  --bt-reflective-line: rgba(255, 255, 255, 0.08);
  /* Translucent-overlay roles, so the same rule works on either canvas: white
     films over black in dark, dark films over white in light. */
  --bt-hairline: rgba(255, 255, 255, 0.16);
  --bt-scrim: rgba(0, 0, 0, 0.6);
  --bt-scrim-soft: rgba(0, 0, 0, 0.45);
  /* Laid over a control's own fill on hover, so every fill lifts by the same
     visible step instead of each colour needing its own hover shade. */
  --bt-hover-wash: rgba(255, 255, 255, 0.1);
  --bt-track: rgba(255, 255, 255, 0.14);
  --bt-thumb: #fff;
  --bt-scrollbar: rgba(255, 255, 255, 0.14);
  --bt-scrollbar-hover: rgba(255, 255, 255, 0.24);
  --bt-scrollbar-thin: rgba(255, 255, 255, 0.18);
  --bt-preview-overlay: rgba(0, 0, 0, 0.58);
  --bt-select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,0.7)' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  /* The image-background overlay: personalization.js sets --bt-background-overlay
     (opacity); this is the colour it tints with — black in dark, white in light,
     so the same gradient rule dims a photo toward either canvas. */
  --bt-background-scrim-rgb: 0, 0, 0;
  --bt-background-canvas: #000;

  /* Typography */
  --bt-font-interface: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
  --bt-font-display: "Satoshi", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;

  /* Geometry */
  --bt-radius-sm: 8px;
  --bt-radius-cell: 10px;
  --bt-radius-panel: 14px;
  --bt-radius-sheet: 22px;
  --bt-radius-round: 9999px;
  --bt-control-xs: 28px;
  --bt-control-sm: 32px;
  --bt-control-md: 44px;
  --bt-topbar-height: 60px;
  --bt-sidebar-width: 96px;
  --bt-shell-gap: 16px;
  --bt-rail-width: 360px;
  --bt-content-pad: 24px;
  --bt-wide-width: 1080px;
  --bt-narrow-width: 760px;
  --bt-profile-width: 560px;
  --bt-background-overlay: 0.805;

  /* Motion and elevation */
  --bt-duration-sm: 200ms;
  --bt-duration-md: 300ms;
  --bt-ease: cubic-bezier(0.15, 0.5, 0.5, 1);
  --bt-shadow-inset: 0 0 0 1px var(--bt-reflective-line) inset;
  --bt-shadow-popover: 0 4px 4px 4px rgba(0, 0, 0, 0.12), 0 16px 48px rgba(0, 0, 0, 0.28);
}

/* ---------------------------------------------------------------- light theme
   Same roles, same Burgi hues. The teal accent, ink, and the four status hues
   are unchanged; only the canvas, the translucent surfaces/lines (white films
   become dark films), the primary/secondary text, and the -text status shades
   move — the light -text shades are darkened to keep AA contrast on white.
   Geometry, motion and typography are theme-independent and stay in :root. */
:root[data-theme="light"] {
  color-scheme: light;

  /* Solid greys rather than translucent films: on a light ground the dark-canvas
     trick (a white film that lifts #000 toward grey) has nowhere to go, so the
     hierarchy is spelled out — a soft neutral ground, near-white panels that sit
     above it, recessed greys inside them. A literally pure-white canvas would
     make every panel edge vanish, so "Solid white" is this soft neutral. */
  --bt-canvas: #e2e5ea;
  --bt-surface: #ffffff;
  --bt-surface-nested: #d6dae1;
  --bt-surface-raised: #ffffff;
  --bt-surface-field: #f7f8fa;
  --bt-field-line: #7a838b;
  --bt-surface-interactive: #c7ccd5;
  --bt-surface-popover: #ffffff;
  --bt-dock: rgba(255, 255, 255, 0.94);
  --bt-text-primary: #0d1416;
  --bt-text-secondary: rgba(13, 20, 22, 0.76);
  --bt-text-disabled: rgba(13, 20, 22, 0.4);
  --bt-ink: #ffffff;
  --bt-accent: #0f97a3;
  --bt-accent-deep: #0b767f;
  --bt-accent-soft: rgba(15, 151, 163, 0.12);
  --bt-accent-text: #0a6b74;
  --bt-info: #2f6fd0;
  --bt-info-soft: rgba(47, 111, 208, 0.12);
  --bt-info-text: #1f57a8;
  --bt-neutral: rgba(13, 20, 22, 0.42);
  --bt-skip: #7457d4;
  --bt-skip-soft: rgba(116, 87, 212, 0.12);
  --bt-skip-text: #5a3fb3;
  --bt-positive: #0f9b74;
  --bt-positive-soft: rgba(15, 155, 116, 0.14);
  --bt-positive-text: #0a7757;
  --bt-warning: #a86a00;
  --bt-warning-soft: rgba(255, 159, 10, 0.18);
  --bt-warning-text: #82500a;
  --bt-danger: #d83a4a;
  --bt-danger-soft: rgba(216, 58, 74, 0.12);
  --bt-danger-text: #b62f3d;
  --bt-line: rgba(13, 20, 22, 0.16);
  --bt-reflective-line: rgba(13, 20, 22, 0.12);
  --bt-hairline: rgba(13, 20, 22, 0.18);
  --bt-scrim: rgba(9, 17, 20, 0.5);
  --bt-scrim-soft: rgba(9, 17, 20, 0.38);
  --bt-hover-wash: rgba(13, 20, 22, 0.06);
  --bt-track: rgba(13, 20, 22, 0.16);
  --bt-thumb: #ffffff;
  --bt-scrollbar: rgba(13, 20, 22, 0.22);
  --bt-scrollbar-hover: rgba(13, 20, 22, 0.4);
  --bt-scrollbar-thin: rgba(13, 20, 22, 0.3);
  /* Matches the live light overlay at the default brightness (personalization.js:
     0.25 + 50 * 0.004), so a swatch previews what picking it actually looks like. */
  --bt-preview-overlay: rgba(255, 255, 255, 0.45);
  --bt-select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='rgba(13,20,22,0.64)' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  --bt-background-scrim-rgb: 255, 255, 255;
  --bt-background-canvas: #ffffff;

  --bt-shadow-inset: 0 0 0 1px var(--bt-reflective-line) inset;
  --bt-shadow-popover: 0 2px 8px rgba(9, 17, 20, 0.08), 0 16px 40px rgba(9, 17, 20, 0.16);
}

/* The few places a token swap isn't enough: elements whose dark value is a
   baked-in dark gradient rather than a role. */
:root[data-theme="light"] .bt-integration-card__logo {
  border-color: var(--bt-line);
  background: var(--bt-surface-nested);
}
:root[data-theme="light"] .bt-avatar--neutral {
  background: linear-gradient(150deg, #8aa0a8, #61787f);
}

/* ---------------------------------------------------------------- reset */
*,
*::before,
*::after { box-sizing: border-box; }

html {
  min-width: 320px;
  /* Keep the shell width stable as page content starts or stops scrolling. */
  scrollbar-gutter: stable;
  background: var(--bt-canvas);
}

body {
  min-height: 100vh;
  margin: 0;
  overflow-x: hidden;
  background: var(--bt-canvas);
  color: var(--bt-text-primary);
  font-family: var(--bt-font-interface);
  font-size: 15px;
  font-weight: 400;
  line-height: 20px;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}

body.bt-has-background {
  background-color: var(--bt-background-canvas);
  background-image:
    linear-gradient(
      rgba(var(--bt-background-scrim-rgb), var(--bt-background-overlay)),
      rgba(var(--bt-background-scrim-rgb), var(--bt-background-overlay))
    ),
    var(--bt-background-image);
  background-position: center;
  background-repeat: no-repeat;
  background-size: cover;
  background-attachment: fixed;
}

button, input, select, textarea { color: inherit; font: inherit; }
button:not(:disabled), [role="button"] { cursor: pointer; }
[hidden] { display: none !important; }
a { color: inherit; text-decoration: none; }
img, svg { display: block; max-width: 100%; }
h1, h2, h3, h4, p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }

::selection { background: var(--bt-accent); color: var(--bt-ink); }

:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 2px; }

* {
  scrollbar-width: thin;
  scrollbar-color: var(--bt-scrollbar-thin) transparent;
}
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-thumb { border-radius: 999px; background: var(--bt-scrollbar); }
*::-webkit-scrollbar-thumb:hover { background: var(--bt-scrollbar-hover); }
*::-webkit-scrollbar-track { background: transparent; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}

/* ---------------------------------------------------------------- typography */
.bt-heading-1 { font-size: 24px; font-weight: 700; line-height: 30px; }
.bt-heading-2 { font-size: 21px; font-weight: 700; line-height: 26px; }
.bt-heading-3 { font-size: 18px; font-weight: 700; line-height: 24px; }
.bt-subheader { font-size: 16px; font-weight: 500; line-height: 22px; }
.bt-body-1 { font-size: 15px; font-weight: 400; line-height: 20px; }
.bt-body-2 { font-size: 14px; font-weight: 400; line-height: 20px; }
.bt-body-3 { font-size: 12px; font-weight: 400; line-height: 18px; }
.bt-emphasis-1 { font-size: 15px; font-weight: 500; line-height: 20px; }
.bt-emphasis-2 { font-size: 14px; font-weight: 500; line-height: 20px; }
.bt-emphasis-3 { font-size: 12px; font-weight: 600; line-height: 18px; }
.bt-caption { font-size: 11px; font-weight: 600; line-height: 14px; }

.bt-overline {
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  line-height: 14px;
  text-transform: uppercase;
}

.bt-display { font-family: var(--bt-font-display); }

.bt-text-secondary { color: var(--bt-text-secondary); }
.bt-text-disabled { color: var(--bt-text-disabled); }
.bt-text-accent { color: var(--bt-accent-text); }
.bt-text-positive { color: var(--bt-positive-text); }
.bt-text-warning { color: var(--bt-warning-text); }
.bt-text-danger { color: var(--bt-danger-text); }
.bt-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Read by a screen reader, seen by nobody. For the places where a heading has
   to exist to be announced but would be noise on screen — a table column of
   icon buttons, say. Clipped rather than hidden, because display:none and
   visibility:hidden take it out of the accessibility tree too. */
.bt-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------------------------------------------------------------- icons & avatars */
.bt-icon { width: 20px; height: 20px; flex: 0 0 auto; color: currentColor; }
.bt-icon--16 { width: 16px; height: 16px; }
.bt-icon--18 { width: 18px; height: 18px; }
.bt-icon--24 { width: 24px; height: 24px; }

.bt-avatar {
  display: inline-grid;
  width: 32px;
  height: 32px;
  flex: 0 0 auto;
  place-items: center;
  overflow: hidden;
  border-radius: var(--bt-radius-round);
  background: linear-gradient(150deg, #17aab6, #0c6b74);
  box-shadow: var(--bt-shadow-inset);
  color: #fff;
  font-size: 12px;
  font-weight: 600;
  line-height: 1;
}

.bt-avatar--24 { width: 24px; height: 24px; font-size: 10px; }
.bt-avatar--28 { width: 28px; height: 28px; font-size: 11px; }
.bt-avatar--36 { width: 36px; height: 36px; }
.bt-avatar--72 { width: 72px; height: 72px; font-size: 26px; }
.bt-avatar--96 { width: 96px; height: 96px; font-size: 34px; }
.bt-avatar--neutral { background: linear-gradient(150deg, #46606a, #26363d); }

.bt-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ---------------------------------------------------------------- layout primitives */
.bt-stack { display: flex; flex-direction: column; }
.bt-stack--4 { gap: 4px; }
.bt-stack--8 { gap: 8px; }
.bt-stack--12 { gap: 12px; }
.bt-stack--16 { gap: 16px; }
.bt-stack--24 { gap: 24px; }

.bt-cluster { display: flex; align-items: center; gap: 8px; }
.bt-cluster--6 { gap: 6px; }
.bt-cluster--12 { gap: 12px; }
.bt-cluster--wrap { flex-wrap: wrap; }
.bt-cluster--between { justify-content: space-between; }
/* For a slot that gains and loses a small button — a Save that only exists while
   there is something to save. Holding the button's height means the panel body
   doesn't step down the moment it appears, which is otherwise felt as the field
   you are typing in moving under the cursor. */
.bt-cluster--control { min-height: var(--bt-control-xs); }

.bt-spacer { flex: 1 1 auto; }

/* ---------------------------------------------------------------- surfaces */
.bt-panel {
  border-radius: var(--bt-radius-panel);
  background: var(--bt-surface);
}

.bt-panel__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 16px 16px 4px;
}
/* A head carrying period controls as well as a title runs out of room on a
   phone; let those wrap under the title rather than clipping off the edge. */
.bt-panel__head--controls { flex-wrap: wrap; }
.bt-panel__head--controls > .bt-cluster { flex-wrap: wrap; }

.bt-panel__body { padding: 8px; }
.bt-panel__body--pad { padding: 16px; }

/* A second titled group inside one panel body: a hairline, then an overline
   label. For a panel that holds two related things which still need naming
   separately — the notification switches and the browser they arrive on. */
.bt-panel__subhead {
  margin: 24px 0 6px;
  padding: 16px 16px 0;
  border-top: 1px solid var(--bt-hairline);
}

.bt-panel__lede {
  padding: 2px 16px 0;
  color: var(--bt-text-secondary);
  font-size: 14px;
  line-height: 20px;
}

.bt-panel__foot {
  display: flex;
  min-height: 44px;
  align-items: center;
  justify-content: center;
  padding: 4px 8px 8px;
}

.bt-divider { height: 1px; border: 0; margin: 0; background: var(--bt-line); }

/* ---------------------------------------------------------------- buttons */
.bt-button,
.bt-icon-button {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  transition:
    background-color var(--bt-duration-sm) var(--bt-ease),
    color var(--bt-duration-sm) var(--bt-ease),
    opacity var(--bt-duration-sm) var(--bt-ease),
    transform var(--bt-duration-sm) var(--bt-ease),
    filter var(--bt-duration-sm) var(--bt-ease),
    box-shadow var(--bt-duration-sm) var(--bt-ease);
  user-select: none;
}

.bt-button {
  min-height: var(--bt-control-sm);
  gap: 6px;
  padding: 0 14px;
  border-radius: var(--bt-radius-round);
  font-size: 14px;
  font-weight: 500;
  line-height: 20px;
  white-space: nowrap;
}

.bt-button--primary {
  background: var(--bt-accent);
  color: var(--bt-ink);
  font-weight: 600;
}

.bt-button--secondary {
  background: var(--bt-surface-interactive);
  color: var(--bt-text-primary);
}

.bt-button--subtle {
  background: var(--bt-surface);
  color: var(--bt-text-primary);
}

.bt-button--warning { background: var(--bt-warning-soft); color: var(--bt-warning-text); }
.bt-button--danger { background: var(--bt-danger-soft); color: var(--bt-danger-text); }

.bt-button--text {
  min-height: auto;
  padding: 0;
  border-radius: var(--bt-radius-cell);
  background: transparent;
  color: var(--bt-text-secondary);
}
.bt-button--text:hover { color: var(--bt-text-primary); }
/* A text button can still be the destructive one in its row. Both modifiers set
   colour and --text is declared last, so without this pair the danger tint is
   silently dropped and the one button you should read twice looks like the rest. */
.bt-button--text.bt-button--danger,
.bt-button--text.bt-button--danger:hover { color: var(--bt-danger-text); }

.bt-button--xs { min-height: var(--bt-control-xs); padding-inline: 10px; font-size: 12px; line-height: 18px; }
.bt-button--md { min-height: var(--bt-control-md); padding-inline: 20px; font-size: 15px; }
.bt-button--full { width: 100%; }

.bt-icon-button {
  width: var(--bt-control-sm);
  height: var(--bt-control-sm);
  flex: 0 0 auto;
  padding: 6px;
  border-radius: var(--bt-radius-round);
  background: transparent;
  color: var(--bt-text-secondary);
}

.bt-icon-button:hover { color: var(--bt-text-primary); background: var(--bt-surface); }
.bt-icon-button--filled { background: var(--bt-surface-interactive); color: var(--bt-text-primary); }
.bt-icon-button--danger:hover { color: var(--bt-danger-text); background: var(--bt-danger-soft); }

/* A filled button lifts by the shared wash. A 10% brightness filter used to be
   the whole effect, which on the translucent secondary fill barely showed. */
.bt-button:not(.bt-button--text):not(:disabled):hover { box-shadow: inset 0 0 0 999px var(--bt-hover-wash); }
.bt-button:active,
.bt-icon-button:active { transform: scale(0.97); }

.bt-button:disabled,
.bt-icon-button:disabled {
  cursor: not-allowed;
  opacity: 0.38;
  transform: none;
}

/* ---------------------------------------------------------------- fields */
.bt-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }

.bt-label {
  color: var(--bt-text-secondary);
  font-size: 12px;
  font-weight: 600;
  line-height: 18px;
}
.bt-label em { font-style: normal; color: var(--bt-danger-text); }
/* The counterpart to that asterisk, for a field where leaving it empty is a real
   choice rather than an omission. Quieter than the label it follows — it is
   permission, not instruction. */
.bt-label__note { font-weight: 500; color: var(--bt-text-disabled); }

.bt-input-shell {
  display: flex;
  min-height: var(--bt-control-md);
  align-items: center;
  gap: 10px;
  padding: 0 14px;
  border: 1px solid var(--bt-field-line);
  border-radius: 12px;
  background: var(--bt-surface-field);
  color: var(--bt-text-secondary);
  transition: border-color var(--bt-duration-sm) var(--bt-ease), background var(--bt-duration-sm) var(--bt-ease);
}

.bt-input-shell:focus-within {
  border-color: var(--bt-accent);
  background: var(--bt-surface);
}

.bt-input-shell--area { align-items: stretch; padding: 11px 14px; }
.bt-input-shell--round { border-radius: var(--bt-radius-round); }

/* A field the portal fills in rather than the person: it looks like a field so
   the value reads as part of the form, but flatter and without the focus ring,
   so it never invites a click that would do nothing. Used by the payroll form's
   Bank, which comes from the routing number. */
.bt-input-shell--locked {
  border-color: transparent;
  background: var(--bt-surface-nested);
  cursor: default;
}
.bt-input-shell--locked:focus-within { border-color: transparent; background: var(--bt-surface-nested); }
.bt-input-shell--locked .bt-input { color: var(--bt-text-primary); cursor: default; }
.bt-input-shell--locked .bt-input::placeholder { color: var(--bt-text-secondary); }

/* New job's benchmark: the booker confirms the number from inside the field.
   Confirmed reads as done, not as another thing to press. */
.bt-benchmark-shell { padding-right: 6px; }
.bt-benchmark-confirm { flex: 0 0 auto; gap: 4px; }
.bt-benchmark-confirm.is-confirmed,
.bt-benchmark-confirm.is-confirmed:hover { background: var(--bt-positive-soft); color: var(--bt-positive-text); border-color: transparent; }

/* Address-suggestion dropdown (map.js BTMap.attachAutocomplete). Anchored to
   the field that holds the input, not the input itself — the shell needs
   room to sit above it. */
.btmap-suggest-host { position: relative; }
.btmap-suggest {
  position: absolute;
  z-index: 1000;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  display: flex;
  flex-direction: column;
  gap: 0;
  max-height: min(300px, 45vh);
  overflow-y: auto;
  padding: 4px;
  border: 1px solid var(--bt-hairline);
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-popover);
  box-shadow: var(--bt-shadow-popover);
  color: var(--bt-text-primary);
  isolation: isolate;
}
.btmap-suggest[hidden] { display: none; }
.btmap-suggest__opt {
  display: flex;
  flex: 0 0 auto;
  flex-direction: column;
  align-items: stretch;
  gap: 2px;
  width: 100%;
  min-height: 44px;
  padding: 7px 10px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--bt-text-primary);
  font-family: inherit;
  font-size: 13px;
  line-height: 18px;
  text-align: left;
  white-space: normal;
  overflow-wrap: anywhere;
  cursor: pointer;
}
.btmap-suggest__primary { color: var(--bt-text-primary); font-weight: 600; }
.btmap-suggest__secondary { color: var(--bt-text-secondary); font-size: 12px; }
.btmap-suggest__opt:hover { background: var(--bt-surface-interactive); }

.bt-input {
  width: 100%;
  min-width: 0;
  padding: 0;
  border: 0;
  outline: 0;
  background: transparent;
  color: var(--bt-text-primary);
  font-size: 15px;
}
.bt-input::placeholder { color: var(--bt-text-disabled); }
.bt-input:disabled { color: var(--bt-text-disabled); }
/* For a value whose punctuation is the point — a job number pattern, where
   {SEQ} has to be told apart from SEQ at a glance. Slightly wider tracking
   because braces and hyphens crowd in a proportional face. */
.bt-input--mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 14px;
  letter-spacing: 0.02em;
}

textarea.bt-input {
  min-height: 76px;
  padding: 10px 0 12px;
  resize: vertical;
  line-height: 20px;
}

.bt-select {
  width: 100%;
  min-height: var(--bt-control-md);
  padding: 0 38px 0 14px;
  border: 1px solid var(--bt-field-line);
  border-radius: 12px;
  outline: 0;
  appearance: none;
  background-color: var(--bt-surface-field);
  background-image: var(--bt-select-chevron);
  background-position: right 12px center;
  background-repeat: no-repeat;
  background-size: 16px;
  color: var(--bt-text-primary);
}
.bt-select:focus { border-color: var(--bt-accent); }
.bt-select option { background: var(--bt-surface-popover); color: var(--bt-text-primary); }
/* A select that sits beside other controls in a row (e.g. asset type editor's
   "available in" picker) rather than filling its own column. */
.bt-select--kind { width: 150px; flex: 0 0 150px; }

.bt-select-enhanced {
  position: relative;
  display: flex;
  align-items: center;
  font-family: var(--bt-font-interface);
  font-weight: 500;
  line-height: 20px;
  cursor: pointer;
  transition:
    border-color var(--bt-duration-sm) var(--bt-ease),
    background-color var(--bt-duration-sm) var(--bt-ease),
    box-shadow var(--bt-duration-sm) var(--bt-ease);
}
.bt-select-enhanced:hover { background-color: var(--bt-surface-interactive); }
.bt-select-enhanced:focus-within,
.bt-select-enhanced.is-open {
  border-color: var(--bt-accent);
  background-color: var(--bt-surface);
  box-shadow: 0 0 0 3px var(--bt-accent-soft);
}
.bt-select-enhanced.is-disabled { cursor: default; opacity: 0.5; }
.bt-select-enhanced.is-disabled:hover { background-color: var(--bt-surface-field); }
.bt-select-native {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  min-height: 0 !important;
  overflow: hidden !important;
  padding: 0 !important;
  border: 0 !important;
  opacity: 0 !important;
  pointer-events: none !important;
}
.bt-select__trigger {
  display: flex;
  min-width: 0;
  min-height: inherit;
  flex: 1 1 auto;
  align-items: center;
  padding: 0;
  border: 0;
  outline: 0;
  overflow: hidden;
  background: transparent;
  color: inherit;
  font: inherit;
  line-height: inherit;
  text-align: left;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
}
.bt-select__trigger:disabled { cursor: default; }

.bt-select-menu {
  position: fixed;
  z-index: 180;
  display: flex;
  min-height: 44px;
  flex-direction: column;
  padding: 6px;
  border: 1px solid var(--bt-hairline);
  border-radius: var(--bt-radius-panel);
  overflow: hidden;
  background: var(--bt-surface-popover);
  box-shadow: var(--bt-shadow-popover);
  color: var(--bt-text-primary);
  font-family: var(--bt-font-interface);
  font-size: 13px;
  line-height: 18px;
  animation: bt-select-in var(--bt-duration-sm) var(--bt-ease);
}
@keyframes bt-select-in {
  from { opacity: 0; transform: translateY(-4px) scale(0.985); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
.bt-select-menu__search {
  display: flex;
  min-height: 38px;
  flex: 0 0 auto;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
  padding: 0 10px;
  border: 1px solid var(--bt-field-line);
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-field);
  color: var(--bt-text-secondary);
}
.bt-select-menu__search:focus-within {
  border-color: var(--bt-accent);
  color: var(--bt-text-primary);
}
.bt-select-menu__search input {
  width: 100%;
  min-width: 0;
  padding: 0;
  border: 0;
  outline: 0;
  background: transparent;
  color: var(--bt-text-primary);
  font: inherit;
}
.bt-select-menu__search input::placeholder { color: var(--bt-text-secondary); }
.bt-select-menu__list {
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
}
.bt-select-menu__group {
  padding: 10px 10px 4px;
  color: var(--bt-text-disabled);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  line-height: 14px;
  text-transform: uppercase;
}
.bt-select-menu__option {
  display: flex;
  width: 100%;
  min-height: 38px;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 0;
  border-radius: var(--bt-radius-sm);
  outline: 0;
  background: transparent;
  color: var(--bt-text-secondary);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.bt-select-menu__option:hover,
.bt-select-menu__option:focus-visible {
  background: var(--bt-surface-interactive);
  color: var(--bt-text-primary);
}
.bt-select-menu__option.is-selected {
  background: var(--bt-accent-soft);
  color: var(--bt-accent-text);
  font-weight: 600;
}
.bt-select-menu__option:disabled {
  color: var(--bt-text-disabled);
  cursor: default;
  opacity: 0.5;
}
.bt-select-menu__option-text {
  min-width: 0;
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* A part of an option (one of a client's sites), offered when a search names
   it: the part on the first line, what it belongs to beneath. */
.bt-select-menu__option--sub .bt-select-menu__option-text { white-space: normal; }
.bt-select-menu__option-hint {
  display: block;
  overflow: hidden;
  color: var(--bt-text-secondary);
  font-size: 12px;
  font-weight: 400;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bt-select-menu__empty {
  padding: 20px 12px;
  color: var(--bt-text-disabled);
  text-align: center;
}

/* Compact variant for selects that sit inside a list row alongside buttons. */
.bt-select--inline {
  width: auto;
  min-height: var(--bt-control-xs);
  padding: 0 28px 0 10px;
  border-radius: var(--bt-radius-sm);
  background-position: right 8px center;
  background-size: 14px;
  font-size: 12px;
  font-weight: 500;
}

/* Read-only job facts. Most of a job's detail is set when it's booked, so it
   reads as plain text; the fields a technician may correct carry a pencil that
   swaps in an input. */
.bt-label-row {
  display: flex;
  min-height: 18px;
  align-items: center;
  gap: 6px;
}
/* See above: a 22px dot in an 18px row, so a field with an explanation lines up
   with one without. */
.bt-label-row > .bt-info-dot { margin-block: -2px; }

.bt-readonly {
  min-height: 22px;
  font-size: 14px;
  font-weight: 500;
  line-height: 22px;
  overflow-wrap: anywhere;
}
.bt-readonly:empty::before { content: "—"; color: var(--bt-text-disabled); }

.bt-inline-edit {
  display: grid;
  width: 22px;
  height: 22px;
  flex: 0 0 auto;
  place-items: center;
  border: 0;
  /* A button carries 6px of side padding from the browser until told otherwise.
     With border-box sizing that left this 22px control a 10px content box, and
     the icon inside — capped by the reset's `max-width: 100%` — was squashed to
     10px wide in a 16px slot everywhere this button is used. */
  padding: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--bt-text-disabled);
  transition: background var(--bt-duration-sm) var(--bt-ease), color var(--bt-duration-sm) var(--bt-ease);
}
.bt-inline-edit:hover { background: var(--bt-surface-interactive); color: var(--bt-text-primary); }
.bt-inline-edit:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 1px; }
/* Held for a beat after a copy so the click has a visible answer. */
.bt-inline-edit.is-copied,
.bt-inline-edit.is-copied:hover { color: var(--bt-positive-text); }
/* Pressed state for the ones that toggle an editor open rather than fire once. */
.bt-inline-edit.is-active,
.bt-inline-edit.is-active:hover { background: var(--bt-accent-soft); color: var(--bt-accent-text); }

.bt-inline-editor { display: flex; align-items: center; gap: 6px; }
.bt-inline-editor .bt-input-shell { flex: 1 1 auto; min-width: 0; }
.bt-inline-editor .bt-select-enhanced { flex: 1 1 auto; min-width: 0; }

.bt-hint { font-size: 12px; line-height: 18px; color: var(--bt-text-disabled); }
.bt-field-err { display: none; font-size: 12px; line-height: 18px; font-weight: 600; color: var(--bt-danger-text); }

.bt-form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
}
.bt-form-grid > .bt-field--full { grid-column: 1 / -1; }
.bt-schedule-inputs {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 132px;
  gap: 8px;
}
.bt-inline-editor .bt-schedule-inputs { flex: 1 1 auto; min-width: 0; }
/* The technician detail pane is deliberately narrow. In its inline schedule
   editor, stacking the controls protects the date from being squeezed down to
   a calendar icon by the time field and the two action buttons. */
.bt-inline-editor--schedule { align-items: flex-end; }
.bt-inline-editor--schedule .bt-schedule-inputs { grid-template-columns: minmax(0, 1fr); }

/* choice chips (e.g. the other assignees on a job) */
.bt-chip {
  display: inline-flex;
  min-height: var(--bt-control-sm);
  align-items: center;
  gap: 6px;
  padding: 0 14px;
  border: 0;
  border-radius: var(--bt-radius-round);
  background: var(--bt-surface-nested);
  color: var(--bt-text-secondary);
  font-size: 13px;
  font-weight: 500;
  transition: background var(--bt-duration-sm) var(--bt-ease), color var(--bt-duration-sm) var(--bt-ease);
}
.bt-chip:hover { color: var(--bt-text-primary); }
.bt-chip.is-active { background: var(--bt-accent); color: var(--bt-ink); font-weight: 600; }
.bt-chip:disabled { cursor: default; opacity: 0.45; }
.bt-chip:disabled.is-active { opacity: 0.75; }

.bt-static-pill {
  display: inline-flex;
  min-height: var(--bt-control-sm);
  align-items: center;
  gap: 8px;
  padding: 0 14px 0 4px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-surface);
  color: var(--bt-text-primary);
  font-size: 13px;
  font-weight: 500;
}

/* ---------------------------------------------------------------- badges */
.bt-badge {
  display: inline-flex;
  min-height: 22px;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-surface);
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 600;
  line-height: 16px;
  white-space: nowrap;
}

.bt-badge--accent { background: var(--bt-accent-soft); color: var(--bt-accent-text); }
/* "New" — a job you've been put on but haven't opened yet. Solid, not soft, so
   it reads as an alert rather than another status chip. */
.bt-badge--new {
  background: var(--bt-accent);
  color: var(--bt-ink);
  letter-spacing: 0.06em;
}
.bt-badge--positive { background: var(--bt-positive-soft); color: var(--bt-positive-text); }
.bt-badge--warning { background: var(--bt-warning-soft); color: var(--bt-warning-text); }
.bt-badge--danger { background: var(--bt-danger-soft); color: var(--bt-danger-text); }

/* A job's status, where it can be changed in place: a picker that carries the
   same colours as the badge it replaces, so a board scanned from across the
   room reads the same whether or not you happen to be able to edit it. Its
   fill marks it out, so it goes without the field edge until focused. */
.bt-status-select { border-color: transparent; background-color: var(--bt-surface-nested); }
.bt-status-select--in_progress { background-color: var(--bt-accent-soft); color: var(--bt-accent-text); }
.bt-status-select--on_hold { background-color: var(--bt-warning-soft); color: var(--bt-warning-text); }
.bt-status-select--completed { background-color: var(--bt-positive-soft); color: var(--bt-positive-text); }
.bt-status-select { transition: box-shadow var(--bt-duration-sm) var(--bt-ease); }
.bt-status-select:not(:disabled):hover { box-shadow: inset 0 0 0 999px var(--bt-hover-wash); }

/* ---------------------------------------------------------------- segmented filters */
/* Filters on the left (scrolling sideways when there are many), view controls
   pinned on the right. The two are separate boxes because the scroller would
   otherwise clip the columns popover. */
.bt-boardbar { display: flex; align-items: center; gap: 8px; }
.bt-boardbar__actions { display: flex; flex: 0 0 auto; align-items: center; gap: 8px; }

.bt-filterbar {
  display: flex;
  min-width: 0;
  flex: 1 1 auto;
  align-items: center;
  gap: 8px;
  overflow-x: auto;
  padding-bottom: 2px;
  scrollbar-width: none;
}
.bt-filterbar::-webkit-scrollbar { display: none; }
/* A filter with several answers becomes a list when its button row will not
   fit, so the bar stays one line on a laptop. Shaped like the segmented
   controls beside it (34px pill on the same surface), and teal once it
   narrows the board. */
.bt-filter-select {
  width: auto;
  flex: 0 0 auto;
  height: 34px;
  min-height: 34px;
  padding-right: 34px;
  border-radius: var(--bt-radius-round);
  background-color: var(--bt-surface);
  background-position: right 11px center;
  font-size: 13px;
  white-space: nowrap;
}
.bt-filter-select.is-set { color: var(--bt-accent-text); }
/* Each such filter is drawn both ways; the bar shows the buttons while they
   fit and the list once they do not (fitFilterBar adds is-compact). The rule
   reaches the enhanced select's wrapper too, which copies the select's
   classes. */
.bt-filterbar:not(.is-compact) .bt-filter-select,
.bt-filterbar.is-compact .bt-filter-wide { display: none; }

.bt-segmented {
  display: inline-flex;
  flex: 0 0 auto;
  flex-wrap: nowrap;
  gap: 2px;
  padding: 3px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-surface);
  box-shadow: inset 0 0 0 1px var(--bt-line);
}

.bt-segmented__item {
  flex: 0 0 auto;
  min-height: 28px;
  padding: 0 12px;
  border: 0;
  border-radius: var(--bt-radius-round);
  background: transparent;
  color: var(--bt-text-secondary);
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
  transition: background var(--bt-duration-sm) var(--bt-ease), color var(--bt-duration-sm) var(--bt-ease);
}
.bt-segmented__item:hover { color: var(--bt-text-primary); }
.bt-segmented__item.is-active {
  background: var(--bt-surface-interactive);
  color: var(--bt-text-primary);
}
/* The list/calendar label is text-only, so each segment centres its label
   rather than inheriting the visual weight of the removed calendar icon. */
.bt-inv-counts { cursor: pointer; white-space: nowrap; }
.bt-inv-counts[hidden] { display: none; }
.bt-segmented__num { margin-left: 6px; font-variant-numeric: tabular-nums; opacity: 0.7; }
.bt-segmented__num:empty { display: none; }
#job-view-mode .bt-segmented__item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}

/* ---------------------------------------------------------------- progress */
/* Segmented, multi-status bar: each checklist status paints its own slice, so
   the colour mix is the progress. Order left→right: done, not-necessary,
   in-progress, on-hold; the remainder is the empty (to-do) track. */
.bt-segbar {
  display: flex;
  width: 100%;
  height: 6px;
  overflow: hidden;
  border-radius: var(--bt-radius-round);
  background: var(--bt-track);
}

.bt-segbar__seg {
  height: 100%;
  min-width: 0;
  transition: width var(--bt-duration-md) var(--bt-ease);
}
.bt-segbar__seg--done { background: var(--bt-positive); }
.bt-segbar__seg--not_necessary { background: var(--bt-skip); }
.bt-segbar__seg--in_progress { background: var(--bt-info); }
.bt-segbar__seg--on_hold { background: var(--bt-warning); }

.bt-segbar--flush { height: 3px; border-radius: 0; background: var(--bt-line); }

/* Compact key so the bar colours are legible at a glance. */
.bt-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
}
.bt-legend__item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--bt-text-secondary);
  font-size: 12px;
  line-height: 16px;
}
.bt-legend__dot {
  width: 8px;
  height: 8px;
  flex: 0 0 auto;
  border-radius: 50%;
}
.bt-legend__dot--done { background: var(--bt-positive); }
.bt-legend__dot--not_necessary { background: var(--bt-skip); }
.bt-legend__dot--in_progress { background: var(--bt-info); }
.bt-legend__dot--on_hold { background: var(--bt-warning); }
.bt-legend__item b { color: var(--bt-text-primary); font-weight: 600; }

/* ---------------------------------------------------------------- list rows */
.bt-list { display: flex; flex-direction: column; }

.bt-list-row {
  display: grid;
  min-height: 56px;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 8px;
  border: 0;
  border-radius: var(--bt-radius-cell);
  background: transparent;
  color: inherit;
  text-align: left;
  transition: background var(--bt-duration-sm) var(--bt-ease);
}

.bt-list-row:hover { background: var(--bt-surface-nested); }
.bt-list-row--static:hover { background: transparent; }
.bt-list-row--static + .bt-list-row--static { box-shadow: 0 -1px 0 var(--bt-line); }
/* Team mixes --static and --open-row (self/off-limits accounts aren't
   clickable) — the rule above only fires between two --static rows, so it
   misses any pair touching a clickable one. Scoped to the roster only. */
#user-list > .bt-list-row + .bt-list-row { box-shadow: 0 -1px 0 var(--bt-line); }
.bt-list-row__content { min-width: 0; }
/* The roster's standing tags: Microsoft, Timesheet, Payroll, Location (and, for
   an administrator, the app build), each in
   a column of its own between the person and their role. An empty cell keeps
   its column, so every kind lines up down the list. */
/* Without room for four columns the tags follow the person on a line of
   their own, the empty cells dropped. */
.bt-account-tags { display: flex; flex-wrap: wrap; gap: 6px; grid-column: 2 / -1; grid-row: 2; }
.bt-account-tags > :empty { display: none; }
.bt-account-tags:not(:has(.bt-badge)) { display: none; }
@media (min-width: 1101px) {
  /* One grid for the whole roster, each row on its columns, so a wider role
     picker on one row cannot push that row's tags out of line. */
  #user-list:has(> .bt-list-row) { display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto; }
  #user-list > .bt-list-row { grid-column: 1 / -1; grid-template-columns: subgrid; }
  .bt-account-tags {
    display: grid;
    grid-template-columns: 96px 98px 82px 200px;
    align-items: center;
    grid-column: auto;
    grid-row: auto;
  }
  /* An administrator's roster has a fifth column: the app build on each phone. */
  #user-list.bt-roster--app .bt-account-tags { grid-template-columns: 96px 98px 82px 200px 150px; }
  .bt-account-tags:not(:has(.bt-badge)) { display: grid; }
  .bt-account-tags > :empty { display: block; }
  .bt-account-tags > .bt-badge { justify-self: start; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
}
.bt-list-row__side { display: flex; align-items: center; gap: 6px; justify-self: end; }

/* Shared contract for list rows that open another view. Keep the rounded
   shape, hover/focus affordance and trailing chevron together whenever a new
   row is introduced. */
.bt-open-row {
  cursor: pointer;
  border-radius: var(--bt-radius-cell);
  transition: background var(--bt-duration-sm) var(--bt-ease);
}
.bt-open-row:hover { background: var(--bt-surface-nested); }
.bt-open-row:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: -2px; }
.bt-row-chevron {
  flex: 0 0 auto;
  color: var(--bt-text-secondary);
  opacity: 0.8;
}

.bt-tile-icon {
  display: grid;
  width: 40px;
  height: 40px;
  flex: 0 0 auto;
  place-items: center;
  border-radius: 12px;
  background: var(--bt-accent-soft);
  color: var(--bt-accent-text);
}

/* ---------------------------------------------------------------- metrics */
.bt-metric-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
}

/* The jobs page and the team Ratings page have five summary metrics; keep
   them on one row on wide screens while leaving the other metric groups on
   their four-column grid. */
@media (min-width: 1181px) {
  #stats.bt-metric-grid,
  #perf-stats.bt-metric-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

/* Below that the five will not fit on a line, and the four-column default
   leaves the fifth stranded on a row of its own — which reads as a tile that
   failed to load rather than a deliberate 4 + 1. Three and two is the same
   five metrics with no orphan. */
@media (max-width: 1180px) {
  #stats.bt-metric-grid,
  #perf-stats.bt-metric-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Two columns on a phone — stated here rather than left to .bt-metric-grid's
   own phone rule, which this selector's id would out-specify. The same odd one
   out then takes the width instead of half of it. */
@media (max-width: 760px) {
  #stats.bt-metric-grid,
  #perf-stats.bt-metric-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  #stats.bt-metric-grid > :last-child:nth-child(odd),
  #perf-stats.bt-metric-grid > :last-child:nth-child(odd) { grid-column: 1 / -1; }
}

.bt-metric {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 14px;
  border-radius: var(--bt-radius-panel);
  background: var(--bt-surface);
}

.bt-metric__value {
  font-family: var(--bt-font-display);
  font-size: 26px;
  font-weight: 700;
  line-height: 32px;
}

.bt-metric__label { color: var(--bt-text-secondary); font-size: 12px; font-weight: 500; line-height: 18px; }

/* Totals above a table they sum up: as many as there are across the row, at a
   size that leaves the table the first thing on screen. */
.bt-metric-grid--compact { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; }
.bt-metric-grid--compact .bt-metric { padding: 10px 14px; }
.bt-metric-grid--compact .bt-metric__value { font-size: 20px; line-height: 26px; }

/* A count that is also a filter. The band above a board counts rows the board
   can show, so each tile opens it narrowed to exactly those — drawn as a real
   button for the keyboard and the screen reader, with the layout class above
   still doing the layout. Same contract for the rail's own stat rows. */
button.bt-metric,
button.bt-stat-row {
  width: 100%;
  border: 1px solid transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background var(--bt-duration-sm) var(--bt-ease);
}
button.bt-stat-row { background: transparent; }
button.bt-metric:hover,
button.bt-stat-row:hover { background: var(--bt-surface-nested); }
button.bt-metric:focus-visible,
button.bt-stat-row:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 1px; }
button.bt-metric.is-active {
  border-color: var(--bt-line-strong);
  background: var(--bt-surface-nested);
}

/* ---------------------------------------------------------------- alerts */
.bt-alert {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  gap: 10px;
  padding: 12px 14px;
  border-radius: var(--bt-radius-panel);
  background: var(--bt-surface);
  font-size: 14px;
  font-weight: 500;
  line-height: 20px;
}

.bt-alert .bt-icon { margin-top: 0; }
/* Under the message, lined up with it, rather than in the icon's column. */
.bt-alert__action { grid-column: 2; justify-self: start; }
/* Inline literals — a URL fragment, a filename — inside explanatory copy. */
code {
  padding: 1px 5px;
  border-radius: 5px;
  background: var(--bt-surface-nested);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.9em;
}

.bt-alert--positive { background: var(--bt-positive-soft); color: var(--bt-positive-text); }
.bt-alert--warning { background: var(--bt-warning-soft); color: var(--bt-warning-text); }
.bt-alert--danger { background: var(--bt-danger-soft); color: var(--bt-danger-text); }

/* ---------------------------------------------------------------- empty state */
.bt-empty {
  padding: 32px 16px;
  border: 1px dashed var(--bt-hairline);
  border-radius: var(--bt-radius-panel);
  color: var(--bt-text-secondary);
  font-size: 14px;
  line-height: 20px;
  text-align: center;
}

/* ---------------------------------------------------------------- app shell */
.bt-shell {
  display: grid;
  min-height: 100vh;
  grid-template-columns: var(--bt-sidebar-width) minmax(0, 1fr) var(--bt-rail-width);
  /* The bar grows past its height only for a job whose client's name takes
     more than one line (see jobhead.js). */
  grid-template-rows: minmax(var(--bt-topbar-height), auto) minmax(0, 1fr);
}

/* Focused task views (jobs, forms, checklist, profile) drop the right rail. */
.bt-shell.is-focused {
  grid-template-columns: var(--bt-sidebar-width) minmax(0, 1fr);
}
.bt-shell.is-focused .bt-rail { display: none; }

.bt-sidebar {
  position: sticky;
  z-index: 30;
  top: 0;
  display: flex;
  height: 100vh;
  grid-column: 1;
  grid-row: 1 / -1;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 10px 8px 12px;
}

.bt-sidebar__brand {
  display: grid;
  width: 44px;
  height: 44px;
  flex: 0 0 auto;
  place-items: center;
  overflow: hidden;
  margin-bottom: 8px;
  border-radius: 50%;
  box-shadow: 0 0 0 1px var(--bt-line);
}
.bt-sidebar__brand img,
.bt-sidebar__brand svg { width: 100%; height: 100%; object-fit: cover; }

.bt-nav {
  display: flex;
  width: 72px;
  flex-direction: column;
}
/* The first highlight lines up with the top of the page content (the topbar
   plus .bt-main's 20px) rather than sitting wherever the logo leaves it: the
   sidebar's 10px padding, the 44px logo, its 8px margin and the 2px gap put the
   nav at 64px, and the pill starts 6px into its item. Only the rail moves; the
   logo and the spacing between items stay as they are. */
@media (min-width: 761px) {
  .bt-nav { margin-top: calc(var(--bt-topbar-height) + 20px - 70px); }
}

/* Icon over label, with the hover/active highlight wrapped around the icon
   only — so the rail reads as a column of buttons rather than a column of
   blocks. The four numbers here are coupled; see the pill below before changing
   any of them. */
.bt-nav-item {
  position: relative;
  display: flex;
  width: 72px;
  height: 68px;
  flex: 0 0 auto;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* Has to clear the pill's lower edge (6px past the icon) with room to spare,
     or the label sits tucked inside the highlight. */
  gap: 10px;
  border: 0;
  background: transparent;
  border-radius: 12px;
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 600;
  line-height: 14px;
  transition: color var(--bt-duration-sm) var(--bt-ease);
}

/* The highlight. Sized to hug the 20px icon with 6px all round, which centred
   content in a 68px item places at y=12–32 — so the pill runs 6–38 and its
   bottom inset is 68 − 38 = 30. That leaves 4px of clear space above the
   label, which starts at y=42. */
.bt-nav-item::before {
  position: absolute;
  inset: 6px 14px 30px;
  border-radius: 12px;
  background: transparent;
  content: "";
  transition: background var(--bt-duration-sm) var(--bt-ease);
}

.bt-nav-item > * { position: relative; z-index: 1; }
.bt-nav-item:hover { color: var(--bt-text-primary); }
.bt-nav-item:hover::before { background: var(--bt-surface-nested); }
.bt-nav-item.is-active { color: var(--bt-text-primary); }
.bt-nav-item.is-active::before { background: var(--bt-surface); }

.bt-topbar {
  position: sticky;
  z-index: 20;
  top: 0;
  display: flex;
  min-width: 0;
  min-height: var(--bt-topbar-height);
  grid-column: 2 / -1;
  grid-row: 1;
  align-items: center;
  gap: 8px;
  padding: 0 var(--bt-content-pad) 0 var(--bt-shell-gap);
  isolation: isolate;
  background: transparent;
}

/* The backdrop behind the sticky bar. It stays fully transparent so content
   and wallpaper retain their true colour while scrolling beneath it; the
   backdrop blur supplies the separation needed for the title and controls.
   The last 14px fade out, dissolving the bar into page content rather than
   drawing a hard bottom edge. z-index:-1 keeps it behind bar controls and the
   sidebar. */
.bt-topbar::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0;
  left: calc(-1 * var(--bt-sidebar-width));
  background: transparent;
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 14px), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 14px), transparent);
  pointer-events: none;
}
.bt-shell.is-focused .bt-topbar { grid-column: 2; }

/* Back, Forward and Up, as a file browser has them. Tight together so they
   read as one control; each keeps a full-size hit area. */
.bt-topbar__nav { display: flex; flex: 0 0 auto; align-items: center; gap: 2px; margin-left: -6px; }
.bt-topbar__nav .bt-icon-button:disabled { opacity: 0.3; }

.bt-topbar__heading {
  display: flex;
  min-width: 0;
  flex-direction: column;
  justify-content: center;
}
.bt-topbar__jobline { display:flex; align-items:center; gap:10px; min-width:0; }
.bt-topbar__jobline .bt-topbar__title { flex:0 1 auto; }
/* A job's topbar (jobhead.js): client, site and start on one line at one
   size; the number is in the details pane. What does not fit moves to the
   details pane, so nothing here ever truncates. */
.bt-topbar__heading.is-job { flex: 1 1 auto; }
.bt-topbar__heading.is-job > :first-child { flex-wrap: nowrap; align-items: center; gap: 10px; }
.bt-topbar__place {
  flex: none;
  color: var(--bt-text-secondary);
  font-family: var(--bt-font-display);
  font-size: 22px;
  font-weight: 500;
  line-height: 30px;
  white-space: nowrap;
}
.bt-topbar__when {
  display: flex;
  flex: none;
  align-items: baseline;
  gap: 8px;
  padding-left: 12px;
  border-left: 1px solid var(--bt-line);
  white-space: nowrap;
}
.bt-topbar__when b {
  font-family: var(--bt-font-display);
  font-size: 22px;
  font-weight: 700;
  line-height: 30px;
  font-variant-numeric: tabular-nums;
}
.bt-topbar__when small { color: var(--bt-text-secondary); font-size: 13px; }
.bt-topbar__title.is-wrapping { padding: 6px 0; white-space: normal; line-height: 22px; text-overflow: clip; }
#job-work-actions { display:flex; align-items:center; gap:6px; flex:0 0 auto; }
#job-work-actions .bt-button { white-space:nowrap; }
.bt-jobgrid__main.is-work-locked { opacity:.62; }

.bt-topbar__title {
  min-width: 0;
  overflow: hidden;
  font-family: var(--bt-font-display);
  font-size: 22px;
  font-weight: 700;
  line-height: 30px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bt-topbar__sub {
  overflow: hidden;
  color: var(--bt-text-secondary);
  font-size: 13px;
  line-height: 18px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bt-topbar__actions {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 6px;
  margin-left: auto;
}

.bt-topbar__progress {
  position: absolute;
  right: 0;
  bottom: -3px;
  left: 0;
  height: 3px;
  overflow: hidden;
  background: var(--bt-line);
}
.bt-topbar__progress .bt-progress__bar { border-radius: 0; }

.bt-user-pill {
  display: inline-flex;
  height: 36px;
  flex: 0 0 auto;
  align-items: center;
  gap: 8px;
  padding: 0 14px 0 4px;
  border: 0;
  border-radius: var(--bt-radius-round);
  background: var(--bt-surface);
  color: var(--bt-text-primary);
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
  transition: filter var(--bt-duration-sm) var(--bt-ease), background var(--bt-duration-sm) var(--bt-ease);
}
.bt-user-pill:hover { background: var(--bt-surface-interactive); }
.bt-user-pill:active { transform: scale(0.98); }
.bt-user-pill__chevron {
  width: 14px;
  height: 14px;
  margin-left: -2px;
  color: var(--bt-text-secondary);
}

/* The mobile profile control joins the floating navigation dock. */
.bt-mobile-profile { display: none; }

.bt-main {
  min-width: 0;
  grid-column: 2;
  grid-row: 2;
  padding: 20px var(--bt-content-pad) 56px var(--bt-shell-gap);
}

.bt-rail {
  min-width: 0;
  grid-column: 3;
  grid-row: 2;
  padding: 20px var(--bt-content-pad) 56px 0;
}

.bt-rail__inner {
  position: sticky;
  top: calc(var(--bt-topbar-height) + 20px);
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.bt-rail__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 2px 2px;
}

/* Content column: left-aligned under the topbar title, capped for readability. */
.bt-page {
  display: flex;
  width: 100%;
  max-width: var(--bt-wide-width);
  flex-direction: column;
  gap: 20px;
}

.bt-page--narrow { max-width: var(--bt-narrow-width); }
/* Full-width task with readable job details and room for dispatch planning. */
.bt-page--create { max-width: none; }
.bt-create-job-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 24px;
  align-items: start;
}
.bt-create-job-grid > * { min-width: 0; }
.bt-create-job-draft { grid-column: 1 / -1; }
#c-draft-banner { padding: 16px; border-radius: var(--bt-radius-cell); background: var(--bt-surface-nested); }
#c-draft-banner .bt-button { min-height: 44px; }
.bt-create-job-context { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.bt-create-job-header { grid-column: 1 / -1; display: flex; align-items: center; flex-wrap: wrap; gap: 8px 16px; }
.bt-create-job-header > h2 { margin: 0; }
/* New job's tabs: the jobs being created together, then Add job. */
.bt-create-batch { display: flex; min-width: 0; flex: 1 1 auto; align-items: center; flex-wrap: wrap; gap: 8px; }
.bt-create-batch[hidden] { display: none; }
.bt-create-batch__tabs { max-width: 100%; overflow-x: auto; scrollbar-width: none; }
.bt-create-batch__tabs::-webkit-scrollbar { display: none; }
.bt-create-batch__tabs .bt-segmented__item { display: inline-flex; max-width: 220px; align-items: center; gap: 6px; }
.bt-create-batch__name { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.bt-create-batch__num { flex: 0 0 auto; font-variant-numeric: tabular-nums; opacity: 0.7; }
.bt-create-dispatch-toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 8px 12px;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.bt-create-dispatch-toggle:hover { background: var(--bt-surface-interactive); }
.bt-create-dispatch-toggle:has(:checked) { border-color: var(--bt-accent); }
.bt-create-dispatch-toggle:has(:focus-visible) { outline: 2px solid var(--bt-accent); outline-offset: 2px; }
.bt-create-dispatch-toggle:has(:disabled) { opacity: .45; cursor: not-allowed; }
.bt-create-dispatch-toggle input { margin: 0; flex-shrink: 0; }
.bt-create-job-grid:has(#c-dispatch-toggle:not(:checked)) .bt-create-job-grid__dispatch { display: none; }
.bt-create-job-grid__footer { grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px; }
.bt-create-job-grid__submit { margin-left: auto; width: auto; min-width: 200px; }
/* Delete pairs with Save changes on the right rather than sitting apart. */
.bt-create-job-grid__delete { margin-left: auto; }
.bt-create-job-grid__delete:not([hidden]) + .bt-create-job-grid__submit { margin-left: 0; }
/* The visit's fields sit on the form itself, not in a panel of their own. */
.bt-create-job-grid__dispatch .bt-create-dispatch { margin-top: 0; padding: 0; gap: 24px; border: 0; background: none; }
.bt-create-job-grid__dispatch .bt-form-grid { grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr)); }
/* Empty location slots must not consume a cell in the details grid. */
#c-location-slot-job:empty { display: none; }
#c-location-slot-job:has(> [hidden]) { display: none; }
/* Number, purpose and benchmark swap places with the dispatch toggle; their
   slots lay the field out as if it sat there directly, and add no gap empty. */
#c-number-slot-job, #c-number-slot-dispatch, #c-title-slot-job, #c-title-slot-dispatch,
#c-benchmark-slot-job, #c-benchmark-slot-dispatch { display: contents; }
/* Shared job details keep their geometry; only the planning pane changes. */
.bt-create-job-planning {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 24px;
  padding: 16px;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
}
.bt-create-job-grid:has(#c-dispatch-toggle:checked) .bt-create-job-planning { display: none; }
@media (max-width: 760px) {
  .bt-create-job-planning { grid-template-columns: minmax(0, 1fr); }
}
@media (min-width: 1200px) {
  /* Three equal columns: the job details, then the visit's own two (an inner
     grid, .bt-create-dispatch, with a 24px gap of its own). 1fr / 2fr would
     leave the first 12px wider, since the visit's share loses that inner gap
     before it is halved; a third of what is left after both gaps is exact. */
  .bt-create-job-grid { grid-template-columns: minmax(300px, calc((100% - 48px) / 3)) minmax(0, 1fr); }
  .bt-create-job-grid__alert { grid-column: 1 / -1; }
  /* Side by side, the job details usually run longer than the visit beside
     them. The visit panel stretches to match, and the map takes the extra
     height, rather than leaving an empty band above Create job. */
  .bt-create-job-grid__dispatch { align-self: stretch; display: flex; flex-direction: column; }
  .bt-create-job-grid__dispatch .bt-create-dispatch { flex: 1 1 auto; }
  .bt-create-job-grid__dispatch #c-dispatch-map-wrap { flex: 1 1 auto; display: flex; flex-direction: column; }
  .bt-create-job-grid__dispatch #c-dispatch-map { flex: 1 1 auto; height: auto; min-height: 260px; }
}
@media (max-width: 599px) {
  .bt-create-job-grid__submit { width: 100%; min-width: 0; }
  .bt-create-job-grid__dispatch .bt-create-dispatch { gap: 16px; }
}
.bt-checklist-picker {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.bt-checklist-picker__row {
  display: flex;
  min-width: 0;
  align-items: center;
  gap: 10px;
  padding: 9px 10px;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
}
.bt-checklist-picker__order {
  display: grid;
  width: 24px;
  height: 24px;
  flex: 0 0 24px;
  place-items: center;
  border-radius: 50%;
  background: var(--bt-surface-interactive);
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 700;
}
.bt-checklist-picker__copy {
  display: flex;
  min-width: 0;
  flex: 1 1 auto;
  flex-direction: column;
  gap: 2px;
}
/* Customized follows the name, on its line. */
.bt-checklist-picker__title { display: flex; min-width: 0; align-items: center; gap: 6px; }
.bt-checklist-picker__title > .bt-badge { flex: 0 0 auto; }
.bt-checklist-picker__actions { display: flex; flex: 0 0 auto; gap: 2px; }
.bt-checklist-picker__actions .bt-icon-button { width: 30px; height: 30px; }
.bt-checklist-picker__add { padding-top: 2px; }
.bt-create-dispatch {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px;
  margin-top: 14px;
  padding: 14px;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
}
.bt-create-dispatch__column {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 14px;
}
/* The job page's Dispatch section (onsite.js renderSummary): the visit's
   essentials, a still map and the live line, one fact per line. The whole card
   opens the visit in full; for a reader who cannot, it is a plain block. */
.bt-dispatch-sum {
  display: flex;
  width: 100%;
  min-width: 0;
  flex-direction: column;
  gap: 4px;
  padding: 10px 12px;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
  color: inherit;
  font: inherit;
  text-align: left;
}
button.bt-dispatch-sum { cursor: pointer; transition: background var(--bt-duration-sm) var(--bt-ease), border-color var(--bt-duration-sm) var(--bt-ease); }
button.bt-dispatch-sum:hover { border-color: var(--bt-hairline); background: var(--bt-hover-wash); }
button.bt-dispatch-sum:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 2px; }
.bt-dispatch-sum__head { display: flex; min-width: 0; align-items: center; justify-content: space-between; gap: 8px; }
/* The line beside the badge wraps under itself, and its first line shares
   the badge's baseline rather than centering on two. */
.bt-dispatch-sum .bt-onsite-live-row { margin-top: 2px; align-items: baseline; }
.bt-dispatch-sum .bt-onsite-live-row .bt-badge { flex-shrink: 0; }
@media (max-width: 760px) {
  .bt-create-dispatch { grid-template-columns: minmax(0, 1fr); }
}
.bt-page--profile { max-width: var(--bt-profile-width); }
/* A readable column of panels, plus the section rail and its gap, so a panel is
   the same width on a page that has a rail as on one that doesn't.
   --bt-profile-width is that column; the 212px is the 188px rail and the 24px
   gap beside it. */
.bt-page--settings { max-width: calc(var(--bt-profile-width) + 212px); }

/* ---------------------------------------------------------------- integrations
   The grid is one card per service; a card opens that service's own
   full-width page (view-integration), where its settings body is shown. */
.bt-integrations-intro {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 18px;
}
.bt-integrations-intro p { margin: 4px 0 0; }
/* Cards share a width and a height floor. */
.bt-integration-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
  align-items: start;
}
.bt-integration-card {
  display: flex;
  flex-direction: column;
  min-width: 0;
  border: 1px solid var(--bt-line);
  border-radius: 20px;
  background:
    radial-gradient(circle at 8% 0%, rgba(24, 191, 204, 0.11), transparent 36%),
    var(--bt-surface);
  box-shadow: var(--bt-shadow-inset), 0 16px 42px rgba(0, 0, 0, 0.12);
}
/* A floor, not a fixed size, so a card whose description wraps grows instead
   of spilling past the rounded corner. */
.bt-integration-card { min-height: 128px; }
.bt-integration-card__summary {
  flex: 1;
  width: 100%;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  display: grid;
  grid-template-columns: 116px minmax(0, 1fr) auto;
  align-items: center;
  align-content: center;
  gap: 14px;
  min-height: 120px;
  padding: 16px;
  /* The card's 20px corner less the 1px border this sits inside, so the hover
     highlight traces it. */
  border-radius: 19px;
  cursor: pointer;
}
.bt-integration-card__summary:hover { background: var(--bt-surface-raised); }
.bt-integration-card__summary:focus-visible {
  outline: 2px solid var(--bt-accent);
  outline-offset: 2px;
}
.bt-integration-card__logo {
  display: grid;
  width: 116px;
  height: 72px;
  place-items: center;
  padding: 14px;
  border: 1px solid rgba(139, 237, 231, 0.18);
  border-radius: 16px;
  background: linear-gradient(145deg, #102d43, #252044);
}
/* For a vendor whose only full logo is dark ink meant for a white page (Wave).
   After the light-theme override above, so it wins in both themes. */
.bt-integration-card__logo--light,
:root[data-theme="light"] .bt-integration-card__logo--light {
  border-color: var(--bt-line);
  background: #fff;
}
.bt-integration-card__logo img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 44px;
  object-fit: contain;
}
.bt-integration-card__identity {
  display: flex;
  min-width: 0;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
}
.bt-integration-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 600;
}
.bt-integration-status__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--bt-text-disabled);
  box-shadow: 0 0 0 3px var(--bt-surface-raised);
}
.bt-integration-status.is-positive { color: var(--bt-positive); }
.bt-integration-status.is-positive .bt-integration-status__dot {
  background: var(--bt-positive);
  box-shadow: 0 0 0 3px rgba(26, 192, 151, 0.16);
}
.bt-integration-status.is-danger { color: var(--bt-danger-text); }
.bt-integration-status.is-danger .bt-integration-status__dot {
  background: var(--bt-danger);
  box-shadow: 0 0 0 3px rgba(255, 83, 99, 0.16);
}
.bt-integration-card__manage {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--bt-text-secondary);
  font-size: 12px;
  font-weight: 600;
}
.bt-integration-card__manage .bt-icon { transition: transform var(--bt-duration-sm) var(--bt-ease); }
.bt-integration-card__summary:hover .bt-integration-card__manage { color: var(--bt-text-primary); }
.bt-integration-card__summary:hover .bt-integration-card__manage .bt-icon { transform: translateX(2px); }

/* One integration's page. Fields sit side by side as width allows; everything
   else in the body (headings, notes, results, tables, buttons) takes a row of
   its own, so the reading order is the markup order. */
.bt-integration-detail__head {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 18px;
}
.bt-integration-detail__head .bt-integration-card__logo { flex: 0 0 auto; }
.bt-integration-detail__identity { min-width: 0; }
.bt-integration-detail__identity h2,
.bt-integration-detail__identity p { margin: 0; }
.bt-integration-detail__identity p { margin-top: 4px; }
.bt-integration-card__body {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 340px), 1fr));
  align-items: start;
  gap: 16px 24px;
  padding: 24px;
  border: 1px solid var(--bt-line);
  border-radius: 20px;
  background:
    radial-gradient(circle at 4% 0%, rgba(24, 191, 204, 0.08), transparent 30%),
    var(--bt-surface);
  box-shadow: var(--bt-shadow-inset), 0 16px 42px rgba(0, 0, 0, 0.12);
}
.bt-integration-card__body > * { grid-column: 1 / -1; min-width: 0; }
.bt-integration-card__body > .bt-field { grid-column: auto; }
.bt-integration-card__body > .bt-integration-card__section-head:not(:first-child) { margin-top: 8px; }
.bt-integration-card__section-head {
  display: flex;
  min-height: 24px;
  align-items: center;
  gap: 6px;
}
.bt-integration-card__section-head #syncro-saved { margin-left: auto; }
/* The Syncro error log: one card per problem, open ones first. */
.bt-incidents { display: grid; gap: 10px; }
.bt-pagination.bt-incidents__pager { display: flex; justify-content: flex-end; margin-top: 10px; }
/* The Jobs board hides these on a phone to share its toolbar; this pager has a
   line of its own, so it keeps them. */
@media (max-width: 760px) {
  .bt-incidents__pager .bt-pagination__summary,
  .bt-incidents__pager .bt-pagination__pages { display: inline-flex; }
}
.bt-incident {
  display: grid;
  gap: 6px;
  padding: 12px 14px;
  border: 1px solid var(--bt-line);
  border-radius: 14px;
  background: var(--bt-surface-nested);
}
.bt-incident.is-open { border-color: var(--bt-danger-soft); }
.bt-incident__head { display: flex; align-items: center; gap: 8px; }
.bt-incident__kind { color: var(--bt-text-secondary); font-size: 12px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; }
.bt-incident__title { font-size: 14px; line-height: 1.4; }
.bt-incident__when { color: var(--bt-text-secondary); font-size: 12.5px; font-variant-numeric: tabular-nums; }
/* Syncro's own words, which are often a JSON fragment or a long run of text. */
.bt-incident__detail {
  padding: 8px 10px;
  border-radius: 10px;
  background: var(--bt-surface);
  color: var(--bt-text-secondary);
  font-size: 12.5px;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.bt-incident__emails { display: grid; gap: 2px; margin: 0; padding: 0; list-style: none; }
.bt-incident__email { color: var(--bt-text-secondary); font-size: 12px; overflow-wrap: anywhere; }
.bt-incident__email.is-bad { color: var(--bt-warning-text); }
.bt-integration-label {
  display: flex;
  min-height: 24px;
  align-items: center;
  justify-content: flex-start;
  gap: 6px;
}
.bt-integration-label .bt-label { margin: 0; }
.bt-integration-help { position: relative; flex: 0 0 auto; }
.bt-integration-help::after {
  position: absolute;
  z-index: 50;
  top: calc(100% + 7px);
  left: 0;
  width: max-content;
  max-width: 260px;
  padding: 8px 10px;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-sm);
  background: var(--bt-surface-popover);
  box-shadow: var(--bt-shadow-popover);
  color: var(--bt-text-primary);
  content: attr(data-tooltip);
  font-size: 12px;
  font-weight: 400;
  line-height: 17px;
  opacity: 0;
  pointer-events: none;
  transform: translateY(-4px);
  transition: opacity var(--bt-duration-sm) var(--bt-ease), transform var(--bt-duration-sm) var(--bt-ease);
}
.bt-integration-help:hover::after,
.bt-integration-help:focus::after { opacity: 1; transform: translateY(0); }

@media (max-width: 1599px) {
  .bt-integration-card { min-height: 160px; }
  .bt-integration-card__summary {
    grid-template-columns: 88px minmax(0, 1fr);
    min-height: 136px;
  }
  .bt-integration-card__logo { width: 88px; height: 62px; padding: 10px; }
  .bt-integration-card__manage { grid-column: 2; justify-self: start; }
}
@media (max-width: 1180px) {
  .bt-integration-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 760px) {
  .bt-integration-grid { grid-template-columns: minmax(0, 1fr); }
  .bt-integration-card { min-height: 184px; }
  .bt-integration-card__body { padding: 16px; }
  .bt-integration-detail__head .bt-integration-card__logo { width: 88px; height: 62px; padding: 10px; }
  .bt-integration-card__summary { min-height: 136px; }
}
@media (max-width: 560px) {
  .bt-integration-help::after { max-width: min(260px, calc(100vw - 52px)); }
  .bt-integration-card__section-head .bt-integration-help::after { right: 0; left: auto; }
}
.bt-page--fill { max-width: none; }
/* The jobs board is a table — it gets whatever width the window offers, so a
   job's columns fit on one line instead of wrapping. */
.bt-page--board { max-width: none; }

.bt-view { display: none; }
.bt-view.is-active { display: block; }

/* ---------------------------------------------------------------- progressive account creation */
.bt-add-account {
  display: flex;
  width: 100%;
  min-height: 68px;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border: 0;
  border-radius: var(--bt-radius-panel);
  background: var(--bt-surface);
  color: var(--bt-text-primary);
  text-align: left;
  transition: background var(--bt-duration-sm) var(--bt-ease), transform var(--bt-duration-sm) var(--bt-ease);
}
.bt-add-account:hover { background: var(--bt-surface-nested); }
.bt-add-account:active { transform: scale(0.99); }
.bt-add-account__icon {
  display: grid;
  width: 36px;
  height: 36px;
  flex: 0 0 auto;
  place-items: center;
  border-radius: 50%;
  background: var(--bt-accent-soft);
  color: var(--bt-accent-text);
}
.bt-add-account__copy {
  display: flex;
  min-width: 0;
  flex: 1 1 auto;
  flex-direction: column;
}
.bt-add-account__chevron {
  width: 18px;
  height: 18px;
  color: var(--bt-text-secondary);
}

/* The Team roster keeps account management behind one compact overflow button.
   Fixed positioning lets the menu sit above panel overflow and mobile rows. */
.bt-account-menu {
  position: fixed;
  z-index: 75;
  width: 220px;
  max-width: calc(100vw - 24px);
  padding: 6px;
  border: 1px solid var(--bt-hairline);
  border-radius: var(--bt-radius-panel);
  background: var(--bt-surface-popover);
  box-shadow: var(--bt-shadow-popover);
  opacity: 0;
  pointer-events: none;
  transform: translateY(-5px) scale(0.98);
  transform-origin: top right;
  transition: opacity var(--bt-duration-sm) var(--bt-ease), transform var(--bt-duration-sm) var(--bt-ease);
}
.bt-account-menu.is-open { opacity: 1; pointer-events: auto; transform: none; }
.bt-account-menu.is-above { transform-origin: bottom right; }
.bt-account-more,
.bt-client-more { background: transparent; color: var(--bt-text-secondary); }
.bt-account-more:focus-visible,
.bt-client-more:focus-visible,
.bt-account-more[aria-expanded="true"],
.bt-client-more[aria-expanded="true"] {
  background: var(--bt-surface-interactive);
  color: var(--bt-text-primary);
}
.bt-account-menu__note {
  padding: 7px 10px 5px;
  color: var(--bt-text-disabled);
  font-size: 11px;
  line-height: 16px;
}
.bt-client-row { border-radius: var(--bt-radius-cell); }
/* A client's Syncro asset counts: one link out to its asset list in Syncro,
   split into active (green), inactive (yellow, only when there are any) and
   not monitored (plain). Beside the row's menu, and on the client itself. */
.bt-asset-pill {
  position: relative;
  display: inline-flex;
  min-height: var(--bt-control-sm);
  overflow: hidden;
  border-radius: var(--bt-radius-round);
  background: var(--bt-surface);
  box-shadow: inset 0 0 0 1px var(--bt-line);
  color: var(--bt-text-secondary);
  font-size: 13px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-decoration: none;
  white-space: nowrap;
  transition: filter var(--bt-duration-sm) var(--bt-ease);
}
.bt-asset-pill:hover { filter: brightness(1.18); }
.bt-asset-pill:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 2px; }
/* 44px to tap, on a 32px pill. */
.bt-asset-pill::after { content: ''; position: absolute; inset: -6px 0; }
/* Above the tap area, so each number's tooltip still shows on hover. */
.bt-asset-pill__seg { position: relative; z-index: 1; display: inline-flex; align-items: center; gap: 4px; padding: 0 10px; }
.bt-asset-pill__seg + .bt-asset-pill__seg { box-shadow: inset 1px 0 0 var(--bt-line); }
.bt-asset-pill__seg.is-positive { background: var(--bt-positive-soft); color: var(--bt-positive-text); }
.bt-asset-pill__seg.is-warning { background: var(--bt-warning-soft); color: var(--bt-warning-text); }
.bt-client-row__assets { display: inline-flex; gap: 6px; }
.bt-client-row__slot { display: inline-flex; }
.bt-client-row__slot--cycle { min-width: 200px; }
.bt-client-row__slot--counts { min-width: 132px; }
.bt-client-row__export-gap { flex: none; width: var(--bt-control-sm); }
.bt-client-row__assets--narrow { display: none; flex-wrap: wrap; margin-top: 8px; }
@media (max-width: 480px) {
  .bt-client-row__assets--narrow { display: flex; }
  /* The billing-cycle pill has to fit beside the row's actions on a phone. */
  .bt-client-row__assets--narrow .bt-asset-pill--cycle .bt-asset-pill__seg { padding: 0 8px; }
  .bt-client-row__assets--narrow .bt-asset-pill--cycle .bt-icon { display: none; }
  .bt-client-row__assets--wide { display: none; }
}
.bt-client-detail {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 16px;
}
.bt-client-detail__facts {
  overflow: hidden;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-cell);
}
.bt-client-detail__facts .bt-info-row__value { max-width: min(520px, 70%); }
.bt-client-detail__location { position: relative; min-width: 0; display: flex; flex-direction: column; }
.bt-client-detail__location .btmap { flex: 1 1 auto; min-height: 240px; }
.bt-client-detail__location .bt-button { margin-top: 10px; flex: none; align-self: flex-start; }
@media (min-width: 760px) {
  .bt-client-detail { grid-template-columns: minmax(0, 1fr) minmax(280px, 0.75fr); }
}
.bt-menu-item--warning { color: var(--bt-warning-text); }
.bt-menu-item--warning .bt-icon { color: currentColor; }
/* The shape every "are you sure" wears: a badge, a heading, the consequence in
   plain words, and the two ways out. It was named for accounts because they were
   the first thing worth asking about; the layout never had anything to do with
   accounts, so the old name is kept only so existing markup goes on working. */
.bt-confirm-account,
.bt-confirm { padding: 4px 10px 10px; text-align: center; }
.bt-confirm-account__icon,
.bt-confirm__icon {
  display: grid;
  width: 44px;
  height: 44px;
  margin: 2px auto 14px;
  place-items: center;
  border-radius: 50%;
  background: var(--bt-warning-soft);
  color: var(--bt-warning-text);
}
.bt-confirm-account__icon .bt-icon,
.bt-confirm__icon .bt-icon { width: 22px; height: 22px; }
.bt-confirm-account__icon.is-danger,
.bt-confirm__icon.is-danger { background: var(--bt-danger-soft); color: var(--bt-danger-text); }
.bt-confirm-account .bt-heading-3,
.bt-confirm .bt-heading-3 { margin-bottom: 8px; }
.bt-confirm-account .bt-alert,
.bt-confirm .bt-alert { margin-top: 14px; text-align: left; }
.bt-confirm-account .bt-sheet__actions,
.bt-confirm .bt-sheet__actions { margin-top: 20px; }

/* The typed check. Left-aligned inside a centred dialog on purpose: it is a form
   field, and a centred label above a centred box reads as more decoration rather
   than as the one thing still being asked for. */
/* BTDialog (dialogs.js): the portal's confirm / alert / prompt. Above every
   other sheet and menu, since it is usually asked from inside one. */
/* Two classes, so the plain .bt-scrim rule further down cannot pull a question
   back level with the dialog it was asked from, where whichever was added to
   the page last wins. */
.bt-scrim.bt-scrim--dialog { z-index: 95; }
.bt-confirm__icon.is-info { background: var(--bt-accent-soft); color: var(--bt-accent-text); }
.bt-dialog__copy { margin: 0 0 8px; }
.bt-dialog__copy:last-child { margin-bottom: 0; }
.bt-dialog__field { margin-top: 12px; text-align: left; }
.bt-dialog__field .bt-label { display: block; margin-bottom: 6px; }
.bt-dialog__field textarea.bt-input { min-height: 74px; resize: vertical; }
.bt-dialog__err { margin-top: 12px; }
.bt-confirm__gate { margin-top: 16px; text-align: left; }
.bt-confirm__gate .bt-label { display: block; margin-bottom: 6px; }
.bt-confirm__gate .bt-label strong { color: var(--bt-text-primary); font-weight: 700; }

/* Field help stays available on touch screens: tapping the icon focuses it and
   opens the same compact tooltip that pointer users get on hover. */
.bt-field-help { position: relative; }
.bt-field-help::after {
  position: absolute;
  z-index: 90;
  top: calc(100% + 7px);
  left: 0;
  width: max-content;
  max-width: min(260px, calc(100vw - 64px));
  padding: 8px 10px;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-sm);
  background: var(--bt-surface-popover);
  box-shadow: var(--bt-shadow-popover);
  color: var(--bt-text-primary);
  content: attr(data-tooltip);
  font-size: 12px;
  font-weight: 400;
  line-height: 17px;
  opacity: 0;
  pointer-events: none;
  transform: translateY(-4px);
  transition: opacity var(--bt-duration-sm) var(--bt-ease), transform var(--bt-duration-sm) var(--bt-ease);
}
.bt-field-help:hover::after,
.bt-field-help:focus::after { opacity: 1; transform: none; }

/* Both account-dialog actions live in the same track so their edges, height,
   and the space between them stay identical at every sheet width. */
.bt-sheet.bt-sheet--account,
.bt-account-form {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 12px;
}
.bt-account-form .btmap { height: 210px; }

/* The Microsoft 365 picker in Add an account.
   A searchable scrolling list rather than a <select>, for two reasons: the
   directory holds shared mailboxes and resource accounts beside people, so
   finding somebody is a search rather than a scroll, and a native picker can
   show neither two lines per entry nor an address under a name. */
.bt-directory {
  max-height: 216px;
  overflow-y: auto;
  margin-top: 6px;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-sm);
  background: var(--bt-surface);
}
.bt-directory__item {
  display: block;
  width: 100%;
  padding: 8px 12px;
  border: 0;
  background: transparent;
  color: var(--bt-text-primary);
  text-align: left;
  cursor: pointer;
}
.bt-directory__item + .bt-directory__item { box-shadow: 0 -1px 0 var(--bt-line); }
.bt-directory__item:hover,
.bt-directory__item:focus-visible { background: var(--bt-surface-nested); }
.bt-directory__name { display: block; font-size: 14px; font-weight: 600; }
.bt-directory__meta {
  display: block;
  font-size: 13px;
  color: var(--bt-text-secondary);
  overflow-wrap: anywhere;
}
.bt-directory__note { padding: 12px; font-size: 13px; color: var(--bt-text-secondary); }
/* Once somebody is picked, their name and address stop being fields. They are
   the directory's answer, not this form's, and showing them as inputs would
   invite an edit that Microsoft would only contradict. */
.bt-directory-picked {
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-sm);
  padding: 2px 12px;
  background: var(--bt-surface);
}
.bt-account-form > .bt-alert,
.bt-account-form__wide { grid-column: 1 / -1; }
.bt-edit-account__actions {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}
.bt-edit-account__actions .bt-sheet__cancel { width: 100%; margin-top: 0; }

@media (min-width: 680px) {
  .bt-account-form { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px 16px; }
  .bt-account-form .btmap,
  .bt-sheet--client .btmap { height: 230px; }
}

/* tooltips (pointer devices only) */
@media (hover: hover) {
  .bt-tooltip { position: relative; }
  .bt-tooltip::after {
    position: absolute;
    z-index: 40;
    top: calc(100% + 8px);
    left: 50%;
    max-width: 220px;
    padding: 6px 8px;
    border-radius: var(--bt-radius-sm);
    background: var(--bt-surface-popover);
    box-shadow: var(--bt-shadow-popover);
    color: var(--bt-text-primary);
    content: attr(data-tooltip);
    font-size: 12px;
    font-weight: 400;
    line-height: 18px;
    opacity: 0;
    pointer-events: none;
    transform: translate(-50%, -4px);
    transition: opacity var(--bt-duration-sm) var(--bt-ease), transform var(--bt-duration-sm) var(--bt-ease);
    white-space: nowrap;
  }
  .bt-tooltip:hover::after,
  .bt-tooltip:focus-visible::after { opacity: 1; transform: translate(-50%, 0); }

  /* A modal sheet scrolls vertically, which forces its horizontal overflow to
     clip too (an implicit CSS rule neither axis can opt out of once the other
     isn't "visible"). A tooltip centered under an icon near the sheet's left
     edge loses its left half to that clip, so anchor it to the icon instead
     of centering — it only ever needs room to grow rightward. */
  .bt-sheet .bt-tooltip::after { left: 0; transform: translate(0, -4px); }
  .bt-sheet .bt-tooltip:hover::after,
  .bt-sheet .bt-tooltip:focus-visible::after { transform: translate(0, 0); }

  /* Everything above is the fallback for a page with no script. Where the
     tooltip script *has* run it draws the message at the document root instead,
     and the pseudo-element becomes a box that is never seen and still takes up
     room — a couple of hundred pixels of nowrap text hanging off the last column
     of a grid, which is where the settings tables were getting a horizontal
     scrollbar from. `content: none` removes it rather than hiding it; hidden is
     not the same as gone, and only gone stops it counting. */
  .bt-tooltip-js .bt-tooltip::after { content: none; }
}

/* The same removal for the two help dots, which carry their own ::after rather
   than .bt-tooltip's and were therefore missed by the rule above. Both are
   tabindex="0" and tooltip.js shows the portal message on focus as well as
   hover, so it covers them on touch too — which makes the pseudo-element pure
   dead layout, and `width: max-content` worth of it. Left at 260px hanging off
   a help dot near the right edge, it was pushing the Integrations page 43px
   wider than the phone it was on. */
.bt-tooltip-js .bt-field-help::after,
.bt-tooltip-js .bt-integration-help::after { content: none; }

/* The shared tooltip script moves active messages to document.body so they
   cannot be cut off by a table, card, modal scroller, or the viewport edge. */
.bt-tooltip-portal {
  position: fixed;
  z-index: 2000;
  max-width: min(260px, calc(100vw - 16px));
  padding: 6px 8px;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-sm);
  background: var(--bt-surface-popover);
  box-shadow: var(--bt-shadow-popover);
  color: var(--bt-text-primary);
  font-size: 12px;
  font-weight: 400;
  line-height: 18px;
  overflow-wrap: anywhere;
  white-space: pre-line;
  pointer-events: none;
}
.bt-tooltip--portaled::after { display: none !important; }

/* ---------------------------------------------------------------- login */
.bt-login {
  display: flex;
  min-height: 100vh;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 40px 20px;
}

.bt-login__card {
  width: 100%;
  max-width: 400px;
  padding: 28px 24px;
  border-radius: 20px;
  background: var(--bt-surface);
  box-shadow: var(--bt-shadow-inset);
}

/* The completion form is a sign-in card with a map in it, so it needs the room
   a map needs — and it scrolls, because on a phone this is taller than a
   viewport and the button at the bottom is the whole point of the screen.
   Opaque and over everything: it is mounted on top of whichever application
   loaded it, and half-covering an admin shell nobody can use yet would read as
   a rendering fault rather than as a step. */
#bt-onboarding {
  position: fixed;
  inset: 0;
  z-index: 60;
  overflow-y: auto;
  background: var(--bt-canvas);
}
.bt-onboarding__card { max-width: 560px; }
.bt-onboarding__card .btmap { height: 220px; }
#bt-onboarding .bt-login { min-height: 100dvh; }

.bt-login__logo { display: block; margin: 0 auto 22px; }

/* Sign-in, invitation and setup cards: the logo and each heading with its line
   of copy sit on the centre line, as do notes under the card's buttons. Fields,
   labels, alerts and the hints inside a form stay left-aligned, where the eye
   reads them. */
.bt-login__card--centered :is(h1, h2, h1 + p, h2 + p, .bt-hint:not(form .bt-hint)) { text-align: center; }

.bt-login__foot {
  margin-top: 20px;
  color: var(--bt-text-disabled);
  font-size: 12px;
  line-height: 18px;
  text-align: center;
}

/* ---------------------------------------------------------------- customer ratings */
/* The Ratings page's Customer ratings panel (admin.html). The survey's faces,
   small, in the status tokens as moods: the same faces customers chose from. */
.bt-icon--12 { width: 12px; height: 12px; }

.bt-cr-face { flex: none; }
.bt-cr-face[data-mood="danger"]   { --cr-mood: var(--bt-danger);   --cr-text: var(--bt-danger-text); }
.bt-cr-face[data-mood="warning"]  { --cr-mood: var(--bt-info);  --cr-text: var(--bt-info-text); }
.bt-cr-face[data-mood="neutral"]  { --cr-mood: var(--bt-accent);     --cr-text: var(--bt-accent-text); }
.bt-cr-face[data-mood="positive"] { --cr-mood: var(--bt-positive); --cr-text: var(--bt-positive-text); }
.bt-cr-face[data-mood="love"]     { --cr-mood: var(--bt-warning);   --cr-text: var(--bt-warning-text); }
.bt-cr-face { fill: var(--cr-text); }
.bt-cr-face__disc { fill: color-mix(in srgb, var(--cr-mood) 22%, transparent); stroke: color-mix(in srgb, var(--cr-mood) 45%, transparent); stroke-width: 1.5; }
.bt-cr-face__line { fill: none; stroke: var(--cr-text); stroke-width: 4; stroke-linecap: round; }
.bt-cr-face__heart { fill: var(--bt-danger); }

.bt-cr-subhead {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.bt-cr-who { gap: 4px; padding-right: 4px; }
.bt-cr-who__clear {
  display: inline-grid;
  width: 22px;
  height: 22px;
  place-items: center;
  border: 0;
  border-radius: var(--bt-radius-round);
  background: transparent;
  color: inherit;
}
.bt-cr-who__clear:hover { background: var(--bt-hover-wash); }

/* A person: picking one narrows the responses below to theirs. */
.bt-cr-person.is-picked { background: var(--bt-accent-soft); }
.bt-cr-counts { display: flex; gap: 10px; }
.bt-cr-count {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 13px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.bt-cr-count.is-zero { opacity: 0.35; }
.bt-cr-bonus-cell {
  display: flex;
  min-width: 96px;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  font-variant-numeric: tabular-nums;
}

/* A response: the face, what was said about which ticket, who it went to,
   then when and where. Fixed side columns so every row lines up. */
.bt-cr-response {
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr) 96px 220px;
  align-items: start;
  gap: 4px 16px;
  padding: 14px 8px;
}
.bt-cr-response + .bt-cr-response { box-shadow: 0 -1px 0 var(--bt-line); }
.bt-cr-response__main { display: grid; gap: 4px; min-width: 0; }
.bt-cr-response__top { display: flex; align-items: center; gap: 10px; min-width: 0; min-height: 36px; }
.bt-cr-response__top .bt-cr-mood { flex: none; }
/* The job and the ticket side by side, the ticket being the job page's number pill. */
.bt-cr-response__where { display: flex; align-items: center; justify-content: flex-end; gap: 8px; max-width: 100%; }
.bt-cr-response__where .bt-cr-job { flex: none; }
.bt-cr-response .bt-cr-ticket { flex: none; align-self: center; text-decoration: none; }
a.bt-cr-ticket:hover { filter: brightness(1.2); }
.bt-cr-response__people { display: flex; align-items: center; min-height: 36px; }
.bt-cr-people { display: flex; flex-wrap: wrap; gap: 4px 0; }
.bt-cr-people .bt-avatar { position: relative; box-shadow: 0 0 0 2px var(--bt-surface); cursor: default; }
.bt-cr-people .bt-avatar + .bt-avatar { margin-left: -6px; }
.bt-cr-people .bt-avatar:hover, .bt-cr-people .bt-avatar:focus-visible { z-index: 20 !important; }
.bt-cr-response[data-mood="danger"]   .bt-cr-mood { color: var(--bt-danger-text); }
.bt-cr-response[data-mood="warning"]  .bt-cr-mood { color: var(--bt-info-text); }
.bt-cr-response[data-mood="neutral"]  .bt-cr-mood { color: var(--bt-accent-text); }
.bt-cr-response[data-mood="positive"] .bt-cr-mood { color: var(--bt-positive-text); }
.bt-cr-response[data-mood="love"]     .bt-cr-mood { color: var(--bt-warning-text); }
.bt-cr-comment {
  margin: 4px 0 2px;
  padding: 8px 12px;
  border-left: 3px solid var(--bt-line);
  border-radius: 0 var(--bt-radius-sm) var(--bt-radius-sm) 0;
  background: var(--bt-surface-nested);
  font-size: 14px;
  line-height: 20px;
  white-space: pre-line;
  overflow-wrap: anywhere;
}
.bt-cr-response__side { display: flex; flex-direction: column; align-items: flex-end; justify-content: center; gap: 6px; min-height: 36px; text-align: right; }
.bt-cr-response__tags { justify-content: flex-end; flex-wrap: wrap; }
.bt-cr-job { display: inline-flex; align-items: center; gap: 4px; border: 0; cursor: pointer; }
.bt-cr-job:hover { filter: brightness(1.15); }

/* A job's Customer rating section, on both job pages (customerrating.js). */
.bt-cr-jobrating__head { margin-bottom: 10px; }
.bt-cr-jobrating { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: start; gap: 12px; }
.bt-cr-jobrating__main { display: grid; gap: 2px; min-width: 0; }
.bt-cr-jobrating__main .bt-cr-comment { margin: 6px 0 4px; }
.bt-cr-jobrating[data-mood="danger"]   .bt-cr-mood { color: var(--bt-danger-text); }
.bt-cr-jobrating[data-mood="warning"]  .bt-cr-mood { color: var(--bt-info-text); }
.bt-cr-jobrating[data-mood="neutral"]  .bt-cr-mood { color: var(--bt-accent-text); }
.bt-cr-jobrating[data-mood="positive"] .bt-cr-mood { color: var(--bt-positive-text); }
.bt-cr-jobrating[data-mood="love"]     .bt-cr-mood { color: var(--bt-warning-text); }


@media (max-width: 760px) {
  .bt-cr-counts { display: none; }
  .bt-cr-response { grid-template-columns: 36px minmax(0, 1fr); gap: 8px 12px; }
  .bt-cr-response__people { grid-column: 2; min-height: 0; }
  .bt-cr-response__side { grid-column: 2; align-items: flex-start; min-height: 0; text-align: left; }
  .bt-cr-response__where { justify-content: flex-start; flex-wrap: wrap; }
}

/* ---------------------------------------------------------------- survey */
/* survey.html, the customer satisfaction page a resolved ticket's email opens.
   A sign-in card on the portal's wallpaper, with five faces that take on the
   status tokens as a mood: the chosen face fills with it, the label under the
   row picks it up, and a soft glow behind the card shifts to it. */
.bt-survey { --bt-background-image: url("portal-bg-aurora.jpg"); --sv-mood: var(--bt-accent); }
:root[data-theme="light"] .bt-survey { --bt-background-image: url("portal-bg-dawn.jpg"); }
.bt-survey[data-mood="danger"]   { --sv-mood: var(--bt-danger);   --sv-mood-text: var(--bt-danger-text); }
.bt-survey[data-mood="warning"]  { --sv-mood: var(--bt-info);  --sv-mood-text: var(--bt-info-text); }
.bt-survey[data-mood="neutral"]  { --sv-mood: var(--bt-accent);     --sv-mood-text: var(--bt-accent-text); }
.bt-survey[data-mood="positive"] { --sv-mood: var(--bt-positive); --sv-mood-text: var(--bt-positive-text); }
.bt-survey[data-mood="love"]     { --sv-mood: var(--bt-warning);   --sv-mood-text: var(--bt-warning-text); }

.bt-survey .bt-login { position: relative; z-index: 1; min-height: 100dvh; }

.bt-survey__glow {
  position: fixed;
  top: 50%;
  left: 50%;
  z-index: 0;
  width: min(900px, 120vmax);
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--sv-mood);
  opacity: 0.1;
  filter: blur(120px);
  transform: translate(-50%, -55%);
  pointer-events: none;
  transition: background-color 700ms var(--bt-ease), opacity 700ms var(--bt-ease);
}
.bt-survey[data-mood] .bt-survey__glow { opacity: 0.26; }
:root[data-theme="light"] .bt-survey__glow { opacity: 0.08; }
:root[data-theme="light"] .bt-survey[data-mood] .bt-survey__glow { opacity: 0.2; }

.bt-survey__card {
  position: relative;
  max-width: 520px;
  padding: 32px 32px 28px;
  border-radius: var(--bt-radius-sheet);
  -webkit-backdrop-filter: blur(24px) saturate(1.2);
  backdrop-filter: blur(24px) saturate(1.2);
  animation: bt-survey-rise 500ms var(--bt-ease) both;
}
.bt-survey__card .bt-login__logo { margin-bottom: 18px; }

.bt-survey__ticket {
  display: block;
  margin-bottom: 8px;
  color: var(--bt-accent-text);
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.bt-survey__title {
  margin-bottom: 6px;
  font-size: 32px;
  font-weight: 900;
  line-height: 38px;
  letter-spacing: -0.01em;
  text-align: center;
}

/* The row of faces. Every face is a radio; the chosen one grows and fills with
   the mood, and once one is chosen the others step back. */
.bt-survey__faces {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 8px;
  margin-top: 28px;
}
.bt-survey__face {
  display: grid;
  min-height: 44px;
  aspect-ratio: 1;
  place-items: center;
  padding: 6px;
  border: 0;
  border-radius: var(--bt-radius-round);
  background: transparent;
  color: var(--bt-text-primary);
  animation: bt-survey-face-in 420ms var(--bt-ease) both;
  animation-delay: calc(120ms + var(--i) * 60ms);
  transition: transform var(--bt-duration-md) var(--bt-ease), opacity var(--bt-duration-md) var(--bt-ease);
}
.bt-survey__face .bt-face { width: 100%; max-width: 68px; overflow: visible; }
.bt-face__disc {
  fill: var(--bt-surface-interactive);
  stroke: var(--bt-hairline);
  stroke-width: 1;
  transition: fill var(--bt-duration-md) var(--bt-ease);
}
.bt-face__eye { fill: currentColor; transition: fill var(--bt-duration-md) var(--bt-ease); }
.bt-face__line {
  fill: none;
  stroke: currentColor;
  stroke-width: 3.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: stroke var(--bt-duration-md) var(--bt-ease);
}
.bt-face__mouth { fill: currentColor; transition: fill var(--bt-duration-md) var(--bt-ease); }
.bt-face__heart { fill: var(--bt-danger); transform-box: fill-box; transform-origin: center; }

.bt-survey__face[data-mood="danger"]   { --face: var(--bt-danger); }
.bt-survey__face[data-mood="warning"]  { --face: var(--bt-info); }
.bt-survey__face[data-mood="neutral"]  { --face: var(--bt-accent); }
.bt-survey__face[data-mood="positive"] { --face: var(--bt-positive); }
.bt-survey__face[data-mood="love"]     { --face: var(--bt-warning); }
/* At rest each face carries a little of its own mood, so the row reads as a
   scale from red to teal before anything is chosen. */
.bt-survey__face .bt-face__disc { fill: color-mix(in srgb, var(--face) 22%, var(--bt-surface-interactive)); }

@media (hover: hover) {
  .bt-survey__face:hover { transform: translateY(-4px) scale(1.06); }
  .bt-survey__face:hover .bt-face__disc { fill: color-mix(in srgb, var(--face) 38%, var(--bt-surface-interactive)); }
  .bt-survey__face[data-mood="love"]:hover .bt-face__heart { animation: bt-survey-beat 900ms var(--bt-ease) infinite; }
}
.bt-survey__faces.has-choice .bt-survey__face:not(.is-chosen) { opacity: 0.55; }
.bt-survey__faces.has-choice .bt-survey__face:not(.is-chosen):hover { opacity: 1; }
.bt-survey__face.is-chosen { transform: scale(1.14); }
.bt-survey__face.is-chosen .bt-face__disc { fill: var(--face); stroke: transparent; }
.bt-survey__face.is-chosen .bt-face__eye,
.bt-survey__face.is-chosen .bt-face__mouth { fill: var(--bt-ink); }
.bt-survey__face.is-chosen .bt-face__line { stroke: var(--bt-ink); }
.bt-survey__face.is-pop .bt-face { animation: bt-survey-pop 520ms var(--bt-ease); }
.bt-survey__face.is-chosen[data-mood="love"] .bt-face__heart { animation: bt-survey-beat 1100ms var(--bt-ease) infinite; }
.bt-survey__face:focus-visible { outline-offset: 0; }

.bt-survey__face-label {
  min-height: 20px;
  margin-top: 10px;
  color: var(--bt-text-secondary);
  font-size: 13px;
  font-weight: 600;
  line-height: 20px;
  text-align: center;
  transition: color var(--bt-duration-sm) var(--bt-ease);
}
.bt-survey__face-label[data-mood]:not([data-mood=""]) { color: var(--sv-mood-text, var(--bt-text-secondary)); }

/* The follow-up opens under the faces: its height grows from nothing on the
   first choice, and its copy cross-fades when the choice changes. */
.bt-survey__follow {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 420ms var(--bt-ease);
}
.bt-survey__follow.is-open,
.bt-survey__follow.is-swap { grid-template-rows: 1fr; }
.bt-survey__follow-inner {
  display: grid;
  gap: 12px;
  min-height: 0;
  overflow: hidden;
  padding-top: 20px;
}
.bt-survey__follow.is-open .bt-survey__follow-inner { animation: bt-survey-fade-up 420ms var(--bt-ease) both; }
.bt-survey__follow.is-swap .bt-survey__follow-inner > :is(h2, p) { animation: bt-survey-fade-up 300ms var(--bt-ease) both; }
.bt-survey__follow h2 { text-align: center; }
.bt-survey__follow h2 + p { margin-top: -6px; text-align: center; }
.bt-survey__comment { margin-top: 4px; text-align: left; }
.bt-survey__comment textarea { min-height: 96px; resize: vertical; line-height: 20px; }
.bt-survey__follow .bt-button { margin-top: 4px; }

/* Sent: a seal draws itself, and the top face throws hearts. */
#sv-done { position: relative; text-align: center; }
.bt-survey__seal { width: 76px; margin: 4px auto 18px; color: var(--sv-mood); }
.bt-survey__seal svg { overflow: visible; }
.bt-survey__seal-ring {
  fill: color-mix(in srgb, currentColor 16%, transparent);
  stroke: currentColor;
  stroke-width: 3;
  stroke-dasharray: 176;
  stroke-dashoffset: 176;
  animation: bt-survey-draw 700ms var(--bt-ease) 80ms forwards;
}
.bt-survey__seal-tick {
  fill: none;
  stroke: currentColor;
  stroke-width: 4;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 40;
  stroke-dashoffset: 40;
  animation: bt-survey-draw 420ms var(--bt-ease) 560ms forwards;
}
#sv-done .bt-survey__title { animation: bt-survey-fade-up 420ms var(--bt-ease) 300ms both; }
#sv-done-body { animation: bt-survey-fade-up 420ms var(--bt-ease) 380ms both; }

.bt-survey__burst { position: absolute; top: 42px; left: 50%; width: 0; height: 0; pointer-events: none; }
.bt-survey__bit {
  position: absolute;
  width: 8px;
  height: 8px;
  margin: -4px;
  border-radius: 2px;
  background: var(--bt-accent);
  opacity: 0;
  animation: bt-survey-fly 1100ms cubic-bezier(0.2, 0.7, 0.3, 1) var(--delay) forwards;
}
.bt-survey__bit:nth-child(3n + 2) { background: var(--bt-warning); border-radius: 50%; }
.bt-survey__bit--heart {
  width: 12px;
  height: 12px;
  margin: -6px;
  background: var(--bt-danger);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 21C5 16.4 2 12.9 2 9.3A4.8 4.8 0 0 1 12 6.5a4.8 4.8 0 0 1 10 2.8c0 3.6-3 7.1-10 11.7z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 21C5 16.4 2 12.9 2 9.3A4.8 4.8 0 0 1 12 6.5a4.8 4.8 0 0 1 10 2.8c0 3.6-3 7.1-10 11.7z'/%3E%3C/svg%3E") center / contain no-repeat;
}

.bt-survey__onward { display: grid; gap: 10px; margin-top: 22px; animation: bt-survey-fade-up 420ms var(--bt-ease) 520ms both; }
.bt-survey__countdown { height: 4px; overflow: hidden; border-radius: var(--bt-radius-round); background: var(--bt-track); }
.bt-survey__countdown span {
  display: block;
  width: 100%;
  height: 100%;
  background: var(--bt-accent);
  transform: scaleX(0);
  transform-origin: left;
  transition-property: transform;
  transition-timing-function: linear;
}
.bt-survey__countdown span.is-running { transform: scaleX(1); }
.bt-survey__onward .bt-hint { font-variant-numeric: tabular-nums; }

@keyframes bt-survey-rise { from { opacity: 0; transform: translateY(16px) scale(0.98); } }
@keyframes bt-survey-face-in { from { opacity: 0; transform: translateY(14px) scale(0.8); } }
@keyframes bt-survey-fade-up { from { opacity: 0; transform: translateY(8px); } }
@keyframes bt-survey-pop {
  0% { transform: scale(1) rotate(0); }
  35% { transform: scale(1.18) rotate(-6deg); }
  65% { transform: scale(0.96) rotate(3deg); }
  100% { transform: scale(1) rotate(0); }
}
@keyframes bt-survey-beat { 0%, 100% { transform: scale(1); } 30% { transform: scale(1.22); } 55% { transform: scale(0.96); } }
@keyframes bt-survey-draw { to { stroke-dashoffset: 0; } }
@keyframes bt-survey-fly {
  0% { opacity: 1; transform: translate(0, 0) rotate(0) scale(0.4); }
  70% { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--dx), var(--dy)) rotate(var(--rot)) scale(1); }
}

@media (max-width: 480px) {
  .bt-survey .bt-login { padding: 24px 16px; }
  .bt-survey__card { padding: 26px 18px 22px; }
  .bt-survey__title { font-size: 27px; line-height: 32px; }
  .bt-survey__faces { gap: 4px; margin-top: 22px; }
  .bt-survey__face { padding: 3px; }
}
@media (prefers-reduced-motion: reduce) {
  .bt-survey__bit { display: none; }
  .bt-survey__face-label, .bt-survey__glow { transition: none; }
}

/* ---------------------------------------------------------------- job board */
/* The board is a table of chosen columns. --job-cols is written by the client
   from the visible column set and shared by the header and every row, which is
   what keeps a job on one line and the columns aligned.

   A column set can also want more width than the window has — twelve columns
   are offered and the window is whatever the window is. The board scrolls
   inside its own panel rather than pushing the page sideways, which is the
   answer the timesheet and the permission matrix already give: the columns you
   chose are the ones you get, and the topbar, metrics and filter bar stay put
   instead of sliding off with them. --job-min is the width the current set
   needs, written beside --job-cols by renderJobHead; the head and the list
   carry it together or they scroll out of step. */
.bt-table {
  --job-cols: minmax(0, 1fr) auto;
  overflow-x: auto;
}
.bt-table__head,
.bt-joblist { min-width: var(--job-min, 0); }

.bt-table__head {
  display: grid;
  grid-template-columns: var(--job-cols);
  align-items: center;
  gap: 12px;
  /* Matches .bt-job-row's inline padding exactly, or the columns would not line
     up with their headings. */
  padding: 0 10px 8px;
  border-bottom: 1px solid var(--bt-line);
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.bt-job-sort {
  display: inline-flex;
  min-width: 0;
  align-items: center;
  gap: 4px;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
}
.bt-job-sort:hover,
.bt-job-sort:focus-visible { color: var(--bt-text-primary); }
.bt-job-sort:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 3px; border-radius: 2px; }
.bt-job-sort__mark { color: var(--bt-accent-text); font-size: 13px; line-height: 1; }

/* Inside Admin's board --job-cols is set per view; the technician app
   uses the same row as a two-part card, which is what the fallback describes. */
.bt-job-row {
  display: grid;
  grid-template-columns: var(--job-cols, minmax(0, 1fr) auto);
  align-items: center;
  gap: 12px;
  width: 100%;
  /* Fixed so the row never changes height with its contents: without it a row
     is as tall as its tallest cell, so entering select mode (which swaps the
     32px action buttons for a checkbox) would visibly shrink every row, and
     rows with avatars would sit taller than rows without. 32px of content
     plus the 10px padding either side. */
  min-height: 52px;
  padding: 10px;
  border: 0;
  border-radius: var(--bt-radius-cell);
  background: transparent;
  color: inherit;
  text-align: left;
  transition: background var(--bt-duration-sm) var(--bt-ease);
}

/* One cell: single line, clipped rather than wrapped. */
.bt-cell {
  display: flex;
  min-width: 0;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  line-height: 18px;
}
.bt-cell--job { font-weight: 600; }
/* The job number is the row's identity, so it is never what gives way when the
   badges (jobflags.js) sit beside "New" and the column is at its floor. The
   badges wrap under the number rather than clipping: a half-shown "To rate"
   or "RTO" reads as a different word, or as nothing. Only a row that needs
   the room grows; every other row stays on one line. */
.bt-cell--job { flex-wrap: wrap; row-gap: 4px; }
.bt-cell--job > .bt-cell__num { flex: 0 0 auto; max-width: 100%; }
/* The Job column turned off: its cell is still drawn for the phone's stacked
   card (the max-width: 900px block below shows it again) but takes no track
   on the board. */
.bt-cell--offboard { display: none; }
/* The row's badges lead its first column, whichever that is (boardCells).
   They come before the cell's own content and never truncate; a text value
   gives way to them down to a few characters, and only then wraps under
   them — the job cell's rule. The job cell's own copy is for the phone's
   stacked card, so off a phone it shows only when Job is the first column. */
.bt-cell--lead { flex-wrap: wrap; row-gap: 4px; }
.bt-cell__badges { display: inline-flex; flex: 0 0 auto; max-width: 100%; flex-wrap: wrap; align-items: center; gap: 6px; }
.bt-cell--lead > .bt-truncate { flex: 1 1 0; min-width: 56px; }
.bt-cell__badges--phone { display: none; }
.bt-cell__num { font-size: 14px; font-weight: 600; }
.bt-cell__muted { color: var(--bt-text-disabled); }
.bt-cell__pct {
  flex: 0 0 auto;
  min-width: 34px;
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 600;
  text-align: right;
}
/* The phone-only summary under the job number. */
.bt-cell__sub { display: none; }
.bt-segbar--cell { min-width: 0; flex: 1 1 auto; }

/* Overlapping avatars for everyone on the job; the name is on hover.
   The job owner is first AND on top — the stack reads left to right, so
   the one that matters most must not be the one partly covered. Each avatar is
   given a descending z-index by the client (see assigneeStackHtml). */
.bt-avstack { display: flex; align-items: center; }
.bt-avstack > * { position: relative; margin-left: -6px; box-shadow: 0 0 0 2px var(--bt-surface); }
.bt-avstack > *:first-child { margin-left: 0; }
.bt-avstack__more {
  background: var(--bt-surface-interactive);
  color: var(--bt-text-secondary);
  font-size: 10px;
  font-weight: 600;
}
.bt-job-row:hover .bt-avstack > * { box-shadow: 0 0 0 2px var(--bt-surface-nested); }
.bt-job-row:hover { background: var(--bt-surface-nested); }
.bt-job-row + .bt-job-row { box-shadow: 0 -1px 0 var(--bt-line); }

/* The technician app stacks a job's detail into a card rather than columns:
   headline badges, a meta line, then the progress bar. Admin's board
   doesn't use these — its cells are .bt-cell — but both share .bt-job-row. */
.bt-job-row__content { min-width: 0; padding: 2px 0; }
.bt-job-row__head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.bt-job-row__num { font-size: 15px; font-weight: 600; line-height: 20px; }
.bt-job-row__meta {
  margin-top: 2px;
  color: var(--bt-text-secondary);
  font-size: 12px;
  line-height: 18px;
}
.bt-job-row__progress {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 8px;
  max-width: 420px;
}
.bt-job-row__pct {
  flex: 0 0 auto;
  min-width: 66px;
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 600;
  line-height: 14px;
  text-align: right;
}

.bt-job-row__side {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 2px;
  color: var(--bt-text-disabled);
}
.bt-job-row .bt-icon-button { opacity: 0; }
.bt-job-row:hover .bt-icon-button,
.bt-job-row .bt-icon-button:focus-visible { opacity: 1; }
@media (hover: none) {
  .bt-job-row .bt-icon-button { opacity: 1; }
}

/* Pagination lives with the board controls, immediately before Columns. It is
   intentionally quiet and compact so it does not compete with the filters. */
.bt-pagination {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 8px;
  min-height: 0;
  padding: 0;
  color: var(--bt-text-secondary);
  font-size: 12px;
  line-height: 18px;
}
.bt-pagination[hidden] { display: none; }
.bt-pagination__size {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
}
.bt-pagination__size .bt-select { min-width: 60px; }
.bt-pagination__summary { white-space: nowrap; }
.bt-pagination__nav,
.bt-pagination__pages {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.bt-pagination__arrow,
.bt-pagination__page {
  display: inline-flex;
  width: 30px;
  height: 30px;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--bt-radius-sm);
  background: transparent;
  color: var(--bt-text-secondary);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.bt-pagination__arrow:hover:not(:disabled),
.bt-pagination__page:hover { background: var(--bt-surface-interactive); color: var(--bt-text-primary); }
.bt-pagination__page.is-active {
  background: var(--bt-surface-nested);
  color: var(--bt-text-primary);
}
.bt-pagination__arrow:focus-visible,
.bt-pagination__page:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 1px; }
.bt-pagination__arrow:disabled { opacity: 0.35; cursor: default; }
.bt-pagination__ellipsis {
  display: inline-flex;
  width: 18px;
  justify-content: center;
  color: var(--bt-text-disabled);
}

/* Below tablet width there is no room for columns, so the board folds back into
   a stacked card: the job cell keeps its summary line, everything else drops
   away, and the row actions stay on the right. The header goes with them —
   column labels are meaningless once the columns are gone. */
@media (max-width: 900px) {
  .bt-table__head { display: none; }
  /* The columns are gone here, so the width they needed goes with them — a
     stacked card must not inherit the table's scroll. */
  .bt-table { overflow-x: visible; }
  .bt-table__head,
  .bt-joblist { min-width: 0; }
  .bt-job-row {
    grid-template-columns: minmax(0, 1fr) auto !important;
    align-items: flex-start;
    padding: 12px 10px;
  }
  .bt-job-row > .bt-cell { display: none; }
  .bt-job-row > .bt-cell--job,
  .bt-job-row > .bt-job-row__side,
  .bt-job-row > .bt-job-select { display: flex; }
  .bt-job-row > .bt-cell--job {
    flex-wrap: wrap;
    align-items: center;
    grid-column: 1;
  }
  .bt-job-row > .bt-job-row__side { grid-column: 2; align-items: center; }
  /* In selection mode the checkbox takes the first column and the job cell
     moves along one, so the row still reads left to right. */
  .bt-job-srow { grid-template-columns: auto minmax(0, 1fr) auto !important; }
  .bt-cell__badges--phone { display: inline-flex; }
  .bt-cell__sub {
    display: block;
    width: 100%;
    margin-top: 2px;
    color: var(--bt-text-secondary);
    font-size: 12px;
    font-weight: 400;
    line-height: 18px;
  }
}

/* ---------------------------------------------------------------- job calendar */
/* A second reading of the same filtered job list: the rows the board would
   show, placed on a Week or Month grid by their start date/time. Purely a
   presentation layer — role visibility, filters and search all come from the
   list query untouched. Token-only, so the light theme needs no fix-ups. */
.bt-cal { --cal-hour: 44px; }
.bt-cal .bt-panel__body { padding: 0; }
.bt-cal__toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 12px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--bt-hairline);
}
.bt-cal__nav { display: inline-flex; align-items: center; gap: 6px; }
.bt-cal__period {
  margin-left: 4px;
  font-size: 15px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.bt-cal__body { overflow-x: auto; padding: 12px 16px 16px; }
.bt-cal__empty { padding: 40px 16px; text-align: center; color: var(--bt-text-secondary); }

/* Month ------------------------------------------------------------------- */
.bt-cal-month { display: flex; flex-direction: column; min-width: 560px; }
.bt-cal-weekhead {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
}
.bt-cal-weekhead span {
  padding: 4px 6px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--bt-text-secondary);
}
.bt-cal-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  border: 1px solid var(--bt-hairline);
  border-radius: var(--bt-radius-cell);
  overflow: hidden;
}
.bt-cal-day {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-height: 112px;
  padding: 6px;
  border-top: 1px solid var(--bt-hairline);
  border-left: 1px solid var(--bt-hairline);
}
.bt-cal-day:nth-child(7n + 1) { border-left: 0; }
.bt-cal-day:nth-child(-n + 7) { border-top: 0; }
.bt-cal-day--muted { background: var(--bt-surface-nested); }
.bt-cal-day--muted .bt-cal-day__num { color: var(--bt-text-disabled); }
.bt-cal-day__num {
  align-self: flex-start;
  min-width: 22px;
  padding: 1px 5px;
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: center;
  border-radius: var(--bt-radius-round);
}
.bt-cal-day--today .bt-cal-day__num { background: var(--bt-accent); color: var(--bt-ink); }

.bt-cal-chip {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  padding: 2px 6px;
  border: 0;
  border-radius: var(--bt-radius-sm);
  background: var(--bt-surface-nested);
  color: var(--bt-text-primary);
  font: inherit;
  font-size: 12px;
  line-height: 18px;
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
}
.bt-cal-chip:hover { background: var(--bt-surface-interactive); }
.bt-cal-chip:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 1px; }
.bt-cal-chip__dot {
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-text-secondary);
}
.bt-cal-chip__dot--todo { background: var(--bt-text-secondary); }
.bt-cal-chip__dot--in_progress { background: var(--bt-info); }
.bt-cal-chip__dot--on_hold { background: var(--bt-warning); }
.bt-cal-chip__dot--completed { background: var(--bt-positive); }
.bt-cal-chip__time { color: var(--bt-text-secondary); font-variant-numeric: tabular-nums; }
.bt-cal-chip__label { overflow: hidden; text-overflow: ellipsis; }
.bt-cal-chip--done .bt-cal-chip__label { color: var(--bt-text-secondary); }
.bt-cal-more {
  align-self: flex-start;
  padding: 1px 5px;
  border: 0;
  background: transparent;
  color: var(--bt-text-secondary);
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  cursor: pointer;
}
.bt-cal-more:hover { color: var(--bt-text-primary); }

/* Week ------------------------------------------------------------------- */
.bt-cal-week {
  /* A 64px time rail keeps 12-hour labels (for example, "10:00 AM") and
     the all-day label on one clean line. */
  --cal-gutter: 64px;
  display: flex;
  flex-direction: column;
  min-width: 640px;
  border: 1px solid var(--bt-hairline);
  border-radius: var(--bt-radius-cell);
  overflow: hidden;
}
.bt-cal-week__head,
.bt-cal-allday {
  display: grid;
  grid-template-columns: var(--cal-gutter) repeat(7, minmax(0, 1fr));
}
.bt-cal-week__head { border-bottom: 1px solid var(--bt-hairline); }
.bt-cal-week__head span {
  padding: 6px 4px;
  border-left: 1px solid var(--bt-hairline);
  font-size: 11px;
  font-weight: 600;
  text-align: center;
  color: var(--bt-text-secondary);
}
.bt-cal-week__head span:first-child { border-left: 0; }
.bt-cal-week__head b {
  display: block;
  font-size: 14px;
  color: var(--bt-text-primary);
  font-variant-numeric: tabular-nums;
}
.bt-cal-week__head .is-today b { color: var(--bt-accent-text); }
.bt-cal-allday { border-bottom: 1px solid var(--bt-hairline); }
.bt-cal-allday__label {
  padding: 4px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--bt-text-disabled);
  text-align: right;
  white-space: nowrap;
}
.bt-cal-allday__cell {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 4px;
  border-left: 1px solid var(--bt-hairline);
}
.bt-cal-timegrid {
  position: relative;
  display: grid;
  grid-template-columns: var(--cal-gutter) repeat(7, minmax(0, 1fr));
}
.bt-cal-hours { position: relative; }
.bt-cal-hour {
  height: var(--cal-hour);
  padding-right: 6px;
  font-size: 10px;
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--bt-text-disabled);
  white-space: nowrap;
  transform: translateY(-6px);
}
.bt-cal-col {
  position: relative;
  border-left: 1px solid var(--bt-hairline);
  background-image: repeating-linear-gradient(
    var(--bt-hairline) 0 1px,
    transparent 1px var(--cal-hour)
  );
}
.bt-cal-col--today { background-color: var(--bt-accent-soft); }
.bt-cal-event {
  position: absolute;
  overflow: hidden;
  padding: 2px 5px;
  border-radius: var(--bt-radius-sm);
  border-left: 3px solid var(--bt-text-secondary);
  background: var(--bt-surface-interactive);
  font-size: 11px;
  line-height: 14px;
  text-align: left;
  cursor: pointer;
}
.bt-cal-event:hover { filter: brightness(1.12); }
.bt-cal-event:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 1px; }
.bt-cal-event--todo { border-left-color: var(--bt-text-secondary); }
.bt-cal-event--in_progress { border-left-color: var(--bt-info); }
.bt-cal-event--on_hold { border-left-color: var(--bt-warning); }
.bt-cal-event--completed { border-left-color: var(--bt-positive); }
.bt-cal-event b { font-weight: 600; }
.bt-cal-event span { display: block; color: var(--bt-text-secondary); }

@media (max-width: 760px) {
  .bt-cal__body { padding: 8px; }
  .bt-cal-month { min-width: 0; }
  .bt-cal-day { min-height: 62px; padding: 4px; }
  .bt-cal-chip { padding: 2px; gap: 0; background: transparent; }
  .bt-cal-chip:hover { background: transparent; }
  .bt-cal-chip__time,
  .bt-cal-chip__label { display: none; }
  .bt-cal-chip__dot { width: 8px; height: 8px; }
}

/* Cells you can edit in place. They stay flat until hovered so the board still
   reads as a table rather than a wall of buttons. */
.bt-assign-cell,
.bt-tpl-cell {
  display: flex;
  min-width: 0;
  max-width: 100%;
  align-items: center;
  gap: 4px;
  padding: 3px 4px;
  /* Cancels the inline padding again. The padding is only there to give the
     hover chip room around the content, and without this it also pushes the
     content 4px right of the column edge — so an editable cell sat 4px off
     from its own column heading, and off from any cell in the same column that
     isn't editable (an unassigned job's dash).
     Bleeding the chip outwards instead keeps every cell in a column starting at
     the same x. The row's 12px grid gap absorbs the overhang. */
  margin: 0 -4px;
  border: 0;
  border-radius: var(--bt-radius-sm);
  background: transparent;
  color: inherit;
  text-align: left;
  transition: background var(--bt-duration-sm) var(--bt-ease);
}
.bt-assign-cell:hover,
.bt-tpl-cell:hover { background: var(--bt-surface-interactive); }
.bt-checklist-cell {
  display: flex;
  min-width: 0;
  max-width: 100%;
  align-items: center;
}
.bt-assign-cell__add {
  display: grid;
  width: 24px;
  height: 24px;
  place-items: center;
  border: 1.5px dashed var(--bt-neutral);
  border-radius: 50%;
  color: var(--bt-text-secondary);
}
.bt-assign-cell:hover .bt-assign-cell__add { border-color: var(--bt-accent); color: var(--bt-accent-text); }
.bt-tpl-cell__caret { flex: 0 0 auto; color: var(--bt-text-disabled); opacity: 0; transition: opacity var(--bt-duration-sm) var(--bt-ease); }
.bt-tpl-cell:hover .bt-tpl-cell__caret { opacity: 1; }
@media (hover: none) {
  .bt-tpl-cell__caret { opacity: 1; }
}

/* The floating editor a board cell opens. */
.bt-rowpop {
  position: fixed;
  z-index: 70;
  width: 248px;
  max-height: 60vh;
  overflow-y: auto;
  padding: 6px;
  border: 1px solid var(--bt-hairline);
  border-radius: var(--bt-radius-panel);
  background: var(--bt-surface-popover);
  box-shadow: var(--bt-shadow-popover);
  color: var(--bt-text-primary);
  font-family: var(--bt-font-interface);
  font-size: 13px;
  line-height: 18px;
  opacity: 0;
  pointer-events: none;
  transform: translateY(-4px);
  transition: opacity var(--bt-duration-sm) var(--bt-ease), transform var(--bt-duration-sm) var(--bt-ease);
}
.bt-rowpop.is-open { opacity: 1; pointer-events: auto; transform: none; }
.bt-rowpop--assignees { width: min(520px, calc(100vw - 16px)); overflow-x: hidden; }
.bt-rowpop__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 2px 6px 8px;
}
.bt-rowpop__opt {
  display: flex;
  width: 100%;
  min-height: 38px;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border: 0;
  border-radius: var(--bt-radius-sm);
  outline: 0;
  background: transparent;
  color: var(--bt-text-secondary);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.bt-rowpop__opt:hover,
.bt-rowpop__opt:focus-visible {
  background: var(--bt-surface-interactive);
  color: var(--bt-text-primary);
}
.bt-rowpop__opt.is-on {
  background: var(--bt-accent-soft);
  color: var(--bt-accent-text);
  font-weight: 600;
}
/* Same row, nothing to press: the read-only assignee list for a role that may
   see who is on a job but not change it. */
.bt-rowpop__opt--static { cursor: default; }
.bt-rowpop__opt--static:hover { background: transparent; }
.bt-rowpop__opt > .bt-icon:last-child { margin-left: auto; flex: 0 0 auto; }
.bt-rowpop__name { display: flex; min-width: 0; flex: 1 1 auto; align-items: center; gap: 6px; }
.bt-rowpop__tag {
  flex: 0 0 auto;
  padding: 0 6px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-accent-soft);
  color: var(--bt-accent-text);
  font-size: 10px;
  font-weight: 600;
}
.bt-rowpop__foot {
  padding: 8px 6px 2px;
  color: var(--bt-text-secondary);
  font-size: 11px;
  line-height: 16px;
}

/* Which columns the board shows. */
.bt-colmenu-wrap { position: relative; }
.bt-colmenu {
  position: absolute;
  z-index: 45;
  top: calc(100% + 6px);
  right: 0;
  width: 216px;
  max-height: min(70vh, 480px);
  padding: 6px;
  border: 1px solid var(--bt-hairline);
  border-radius: var(--bt-radius-panel);
  overflow: hidden;
  background: var(--bt-surface-popover);
  box-shadow: var(--bt-shadow-popover);
  color: var(--bt-text-primary);
  font-family: var(--bt-font-interface);
  font-size: 13px;
  line-height: 18px;
  opacity: 0;
  pointer-events: none;
  transform: translateY(-4px);
  transition: opacity var(--bt-duration-sm) var(--bt-ease), transform var(--bt-duration-sm) var(--bt-ease);
}
.bt-colmenu.is-open { opacity: 1; pointer-events: auto; transform: none; }
.bt-colmenu__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 4px 10px 8px;
}
.bt-colmenu__list {
  max-height: min(60vh, 410px);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
}
.bt-colmenu__row {
  display: flex;
  min-height: 38px;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--bt-radius-sm);
  color: var(--bt-text-primary);
  cursor: pointer;
  font: inherit;
  user-select: none;
  transition:
    background-color var(--bt-duration-sm) var(--bt-ease),
    color var(--bt-duration-sm) var(--bt-ease);
}
.bt-colmenu__row:hover,
.bt-colmenu__row:focus-within { background: var(--bt-surface-interactive); }
.bt-colmenu__row.is-locked { cursor: default; color: var(--bt-text-secondary); }
.bt-colmenu__row.is-locked:hover { background: transparent; }
/* The same row, as a button rather than a label — a single-choice filter
   menu picks one value and closes, rather than staying open for several
   checkboxes, so its rows have no input to click and are the control
   themselves. */
button.bt-colmenu__row { width: 100%; background: none; border: 0; text-align: left; }
.bt-colmenu__row.is-active { color: var(--bt-accent-text); }
.bt-colmenu__label {
  display: inline-flex;
  min-width: 0;
  align-items: center;
  gap: 6px;
}
.bt-colmenu__lock {
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
  color: var(--bt-text-disabled);
}
/* The handle a column row is dragged by (or moved with the arrow keys). The
   glyph is small; the button keeps the row's full height to aim at. */
.bt-colmenu__grip {
  display: inline-flex;
  width: 28px;
  min-height: 38px;
  margin: -8px -10px -8px auto;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: var(--bt-radius-sm);
  background: none;
  color: var(--bt-text-disabled);
  cursor: grab;
  touch-action: none;
}
.bt-colmenu__grip .bt-icon { width: 16px; height: 16px; }
.bt-colmenu__row:hover .bt-colmenu__grip,
.bt-colmenu__grip:focus-visible { color: var(--bt-text-secondary); }
.bt-colmenu__row.is-dragging {
  background: var(--bt-surface-interactive);
  box-shadow: var(--bt-shadow-popover);
}
.bt-colmenu__row.is-dragging .bt-colmenu__grip { cursor: grabbing; }

.bt-row-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 10px; }

/* ---------------------------------------------------------------- checklist */
.bt-cl-section { overflow: hidden; }

.bt-cl-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 16px 16px 6px;
}
.bt-cl-head__num { color: var(--bt-accent-text); font-size: 11px; font-weight: 600; line-height: 14px; letter-spacing: 0.06em; }
.bt-cl-head h3 { flex: 1; font-size: 16px; font-weight: 700; line-height: 22px; }
.bt-cl-head__count { color: var(--bt-text-secondary); font-size: 12px; font-weight: 600; line-height: 18px; }
/* A section folds from anywhere on its header (jobpanes.js); folded, it keeps
   its number, title and progress, and the progress turns green once done. */
.bt-cl-section[data-jp-section] > .bt-cl-head { align-items: center; cursor: pointer; padding-block: 10px 2px; }
.bt-cl-section.is-folded > .bt-cl-head { padding-bottom: 10px; }
.bt-cl-section.is-folded > .bt-panel__body { display: none; }
.bt-cl-section.is-folded .bt-cl-head__count.is-done { color: var(--bt-positive-text); }
.bt-cl-toggle { margin-right: -8px; }
.bt-cl-all { flex: 0 0 auto; padding-inline: 8px; }

.bt-cl-item {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  width: 100%;
  padding: 11px 10px;
  border: 0;
  border-radius: var(--bt-radius-cell);
  background: transparent;
  color: inherit;
  text-align: left;
  transition: background var(--bt-duration-sm) var(--bt-ease);
}
.bt-cl-item[role="button"]:hover { background: var(--bt-surface-nested); }

.bt-cl-item__body { flex: 1; min-width: 0; }
.bt-cl-item__label { font-size: 14px; font-weight: 500; line-height: 20px; }
.bt-cl-item__hint { margin-top: 1px; color: var(--bt-text-secondary); font-size: 12px; line-height: 18px; }

.bt-st-icon {
  display: flex;
  width: 24px;
  height: 24px;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  margin-top: -1px;
  border: 2px solid var(--bt-neutral);
  border-radius: 50%;
  color: transparent;
  transition: background var(--bt-duration-sm) var(--bt-ease), border-color var(--bt-duration-sm) var(--bt-ease);
}
.bt-st-icon .bt-icon { width: 13px; height: 13px; }

.st-done .bt-st-icon { background: var(--bt-positive); border-color: var(--bt-positive); color: var(--bt-ink); }
.st-prog .bt-st-icon { border-color: var(--bt-info); color: var(--bt-info); }
.st-hold .bt-st-icon { border-color: var(--bt-warning); color: var(--bt-warning-text); }
.st-nn .bt-st-icon { border-style: dashed; border-color: var(--bt-skip); color: var(--bt-skip-text); }

.st-done .bt-cl-item__label {
  color: var(--bt-text-secondary);
  text-decoration: line-through;
  text-decoration-color: var(--bt-accent-deep);
}
.st-nn .bt-cl-item__label { color: var(--bt-text-disabled); }

.bt-st-chip {
  flex: 0 0 auto;
  align-self: flex-start;
  margin-top: 1px;
  padding: 3px 10px;
  border: 1px solid var(--bt-hairline);
  border-radius: var(--bt-radius-round);
  background: transparent;
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 600;
  line-height: 16px;
  white-space: nowrap;
  transition: background var(--bt-duration-sm) var(--bt-ease), color var(--bt-duration-sm) var(--bt-ease), border-color var(--bt-duration-sm) var(--bt-ease);
}
button.bt-st-chip:hover { border-color: var(--bt-neutral); color: var(--bt-text-primary); }

.st-done .bt-st-chip { background: var(--bt-positive-soft); border-color: transparent; color: var(--bt-positive-text); }
.st-prog .bt-st-chip { background: var(--bt-info-soft); border-color: transparent; color: var(--bt-info-text); }
.st-hold .bt-st-chip { background: var(--bt-warning-soft); border-color: transparent; color: var(--bt-warning-text); }
.st-nn .bt-st-chip { background: var(--bt-skip-soft); border-color: transparent; color: var(--bt-skip-text); }

/* Checklist items that ask a question rather than being ticked off. The row
   carries its own control, so it is never a button and never strikes itself
   through: an answered question is still worth reading back. */
.bt-cl-item--entry { align-items: flex-start; }
.bt-cl-item--entry .bt-cl-item__body { display: flex; flex-direction: column; gap: 6px; }
.bt-cl-item--entry .bt-label-row { margin: 0; }
.bt-cl-item--entry.st-done .bt-cl-item__label { color: inherit; text-decoration: none; }
.bt-cl-item--entry .bt-cl-item__hint { margin-top: 0; }
.bt-cl-item--entry .bt-input-shell,
.bt-cl-item--entry .bt-select-enhanced { width: 100%; }

/* Standing instruction text between the steps: something to read, with nothing
   to record against it, so it is set apart rather than dressed as an item. */
.bt-cl-item--info {
  gap: 10px;
  border-left: 2px solid var(--bt-accent-deep);
  border-radius: 0 var(--bt-radius-cell) var(--bt-radius-cell) 0;
  background: var(--bt-surface-nested);
}
.bt-cl-item--info .bt-cl-item__icon {
  display: flex;
  flex: 0 0 auto;
  color: var(--bt-accent-text);
}
.bt-cl-item--info .bt-cl-item__label { font-weight: 500; color: var(--bt-text-secondary); }
.bt-cl-item--info .bt-st-chip { align-self: center; }

/* A single tick box, for the items that just want confirming. */
.bt-tick {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 6px 12px 6px 10px;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-round);
  cursor: pointer;
}
.bt-tick:hover { border-color: var(--bt-neutral); }
/* The row stacks its parts, so the pill has to say it doesn't want the width. */
.bt-cl-item--entry .bt-tick { align-self: flex-start; }
.bt-tick input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.bt-tick__label { font-size: 13px; font-weight: 600; line-height: 18px; color: var(--bt-text-secondary); }
.bt-tick input:checked ~ .bt-tick__label { color: var(--bt-accent-text); }
.bt-tick input:focus-visible + .bt-check__box { outline: 2px solid var(--bt-accent); outline-offset: 2px; }
.bt-tick input:disabled { cursor: default; }
.bt-tick:has(input:disabled) { cursor: default; opacity: 0.6; }

/* Pick one, or pick any: the same pill either way, so a checklist reads as one
   thing however its items were set up. The mark tells them apart — a dot for a
   choice you can only make once, a tick for one you can make alongside others. */
.bt-choices { display: flex; flex-wrap: wrap; gap: 8px; }
.bt-choice {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 14px 7px 10px;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-round);
  background: transparent;
  cursor: pointer;
  transition: background var(--bt-duration-sm) var(--bt-ease), border-color var(--bt-duration-sm) var(--bt-ease);
}
.bt-choice:hover { border-color: var(--bt-neutral); background: var(--bt-surface-nested); }
.bt-choice input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
.bt-choice__body {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 600;
  line-height: 18px;
  color: var(--bt-text-secondary);
}
.bt-choice__body::before {
  content: "";
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  border: 1.8px solid var(--bt-neutral);
  border-radius: 50%;
  transition: background var(--bt-duration-sm) var(--bt-ease), border-color var(--bt-duration-sm) var(--bt-ease);
}
.bt-choices--multi .bt-choice__body::before { border-radius: 5px; }
.bt-choice input:checked ~ .bt-choice__body { color: var(--bt-accent-text); }
.bt-choice input:checked ~ .bt-choice__body::before {
  border-color: var(--bt-accent);
  background: var(--bt-accent) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23001318' stroke-width='3.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center / 12px no-repeat;
}
.bt-choice:has(input:checked) { border-color: transparent; background: var(--bt-accent-soft); }
.bt-choice:has(input:focus-visible) { outline: 2px solid var(--bt-accent); outline-offset: 2px; }
/* One choice per line, as wide as the column, for options too long to be a
   pill: a ticket's number over its full title, which wraps rather than being
   cut, because the title is how somebody tells two tickets apart. */
.bt-choices--stack { flex-direction: column; flex-wrap: nowrap; }
.bt-choices--stack .bt-choice { display: flex; border-radius: var(--bt-radius-cell); padding: 8px 16px 8px 8px; }
.bt-choices--stack .bt-choice__body { align-items: flex-start; min-width: 0; }
.bt-choices--stack .bt-choice__body::before { margin-top: 1px; }
.bt-choice__lines { display: grid; gap: 2px; min-width: 0; }
.bt-choice__sub { font-weight: 400; color: var(--bt-text-primary); overflow-wrap: anywhere; }
.bt-choice:has(input:disabled) { cursor: default; opacity: 0.6; }
.bt-choice:has(input:disabled):hover { border-color: var(--bt-line); background: transparent; }

/* collapsible details card */
.bt-collapse__head {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 14px 16px;
  border: 0;
  background: transparent;
  color: inherit;
  text-align: left;
}
.bt-collapse__head .bt-icon { color: var(--bt-text-secondary); transition: transform var(--bt-duration-sm) var(--bt-ease); }
.bt-collapse.is-open .bt-collapse__head .bt-icon { transform: rotate(180deg); }
.bt-collapse__body { display: none; padding: 2px 16px 16px; }
.bt-collapse.is-open .bt-collapse__body { display: block; }

/* meta grid (admin job detail) */
.bt-meta-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px 16px;
  padding: 16px;
}
.bt-meta-item b {
  display: block;
  margin-bottom: 2px;
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.05em;
  line-height: 14px;
  text-transform: uppercase;
}
.bt-meta-item div { font-size: 14px; font-weight: 500; line-height: 20px; overflow-wrap: anywhere; }

/* ---------------------------------------------------------------- job workspace (3-pane) */
/* A header strip above the workspace holds status badges + the job-level actions. */
.bt-jobhead {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px 16px;
}
.bt-jobhead__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }

/* The technician summary uses the same compact tag/action header as the
   admin job detail, then a key/value list rather than a tall field stack. */
.bt-job-detail__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.bt-job-detail__head > #d-tags { min-width: 0; }
.bt-job-detail .bt-kv__v > .bt-input-shell,
.bt-job-detail .bt-kv__v > .bt-select,
.bt-job-detail .bt-kv__v > .bt-schedule-inputs { width: 100%; }
/* The admin job editor lives in the narrow job-detail pane. Stack its
   schedule controls so the date field never collapses beside the fixed-width
   time control. Keep the assignee role tabs evenly distributed as well. */
.bt-job-detail .bt-schedule-inputs {
  grid-template-columns: minmax(0, 1fr);
}
.bt-job-detail .bt-picker__roles {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 4px;
  padding: 4px;
  border-radius: 14px;
}
.bt-job-detail .bt-picker__roles--dispatch {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.bt-job-detail .bt-picker__roles .bt-segmented__item {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-width: 0;
  min-height: 28px;
  padding: 0 8px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.bt-job-detail .bt-kv__v .bt-cluster { margin-top: 6px; }
.bt-job-detail .bt-kv__v--copy {
  display: flex;
  min-width: 0;
  align-items: flex-start;
  gap: 6px;
  white-space: nowrap;
}
.bt-job-detail .bt-kv__v--copy .bt-kv__value {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bt-job-detail .bt-kv__v--copy .bt-inline-edit {
  flex: 0 0 auto;
  margin-left: auto;
}

/* info · checklist · activity · conversation. The grid fills the window so the
   conversation sits hard against the right edge; below 1440 the activity log
   folds under the pair, and below 1240 the conversation joins it.
   Every child pins its own row: the conversation comes before the log in the
   DOM (so it stacks first on narrow screens), and without an explicit row the
   sparse auto-placement algorithm would push the log down a row rather than
   step back to column 3. */
.bt-jobgrid {
  --bt-job-pane-bottom: 24px;
  display: grid;
  width: 100%;
  margin-top: var(--bt-jobhead-space, 0);
  /* A folded side pane narrows its own track to a strip; see jobpanes.js. */
  --jg-info: 300px;
  --jg-log: 320px;
  --jg-conv: 360px;
  grid-template-columns: minmax(0, var(--jg-info)) minmax(0, 1fr) minmax(0, var(--jg-log)) minmax(0, var(--jg-conv));
  transition: grid-template-columns var(--bt-duration-sm) var(--bt-ease);
  gap: 16px;
  align-items: start;
}
.bt-jobgrid__info  { grid-column: 1; grid-row: 1; }
.bt-jobgrid__main  { grid-column: 2; grid-row: 1; display: flex; min-width: 0; flex-direction: column; gap: 16px; }
.bt-jobgrid__log   { grid-column: 3; grid-row: 1; }
.bt-jobgrid__conv  { grid-column: 4; grid-row: 1; }
.bt-jobgrid__info,
.bt-jobgrid__log,
.bt-jobgrid__conv { position: sticky; top: calc(var(--bt-topbar-height) + 20px); min-width: 0; }

/* Keep a tall information rail inside the same usable viewport as the
   conversation. Otherwise the grid's lower edge pushes both sticky panes up
   behind the top bar when the checklist reaches its end. */
@media (min-width: 1241px) {
  .bt-jobgrid__info {
    display: flex;
    height: calc(100vh - var(--bt-job-pane-top, calc(var(--bt-topbar-height) + 20px)) - var(--bt-job-pane-bottom));
    height: calc(100dvh - var(--bt-job-pane-top, calc(var(--bt-topbar-height) + 20px)) - var(--bt-job-pane-bottom));
    flex-direction: column;
    overflow: hidden;
  }
  .bt-jobgrid__info > .bt-panel:first-child {
    min-height: 0;
    flex: 1 1 auto;
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
  }
}

/* Compact assignee: avatar next to the name, so one person fits on one line.
   Everywhere it is used now it is static text — changing who is on a job is the
   picker's job (below). */
.bt-assignee {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: var(--bt-control-md);
  padding: 0 8px;
  border: 1px solid transparent;
  border-radius: 12px;
  background: var(--bt-surface-nested);
}
.bt-assignee--static { background: transparent; padding: 0; }
/* Marks the job owner, and only worth showing when there are others. */
.bt-assignee__tag {
  flex: 0 0 auto;
  padding: 0 6px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-accent-soft);
  color: var(--bt-accent-text);
  font-size: 10px;
  font-weight: 600;
}
.bt-assignee__name {
  min-width: 0;
  overflow: hidden;
  font-size: 14px;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bt-assignee__name.is-empty { color: var(--bt-text-secondary); font-weight: 500; }

/* ---------------------------------------------------------------- settings */
/* Settings is three unrelated concerns — where you are in the world, what the
   portal tells you about, and what it looks like — so it gets a rail of its own
   rather than stacking all three into one long scroll. The rail is the same
   idea as the app sidebar, one level in: a list on the left, one section's
   panels on the right. */
.bt-settings {
  display: grid;
  align-items: start;
  gap: 24px;
  grid-template-columns: 188px minmax(0, 1fr);
}
/* Sticks while the panels scroll, so switching section never means scrolling
   back up to find the rail. */
.bt-settings__nav {
  position: sticky;
  top: calc(var(--bt-topbar-height) + 16px);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.bt-settings__link {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  border: 0;
  border-radius: 10px;
  background: transparent;
  color: var(--bt-text-secondary);
  font-size: 14px;
  font-weight: 500;
  text-align: left;
  transition: background var(--bt-duration-sm) var(--bt-ease), color var(--bt-duration-sm) var(--bt-ease);
}
.bt-settings__link > .bt-icon { flex: 0 0 auto; }
.bt-settings__link:hover { background: var(--bt-surface-nested); color: var(--bt-text-primary); }
.bt-settings__link.is-active { background: var(--bt-surface); color: var(--bt-text-primary); font-weight: 600; }
.bt-settings__link:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 2px; }

.bt-settings__body { display: flex; flex-direction: column; gap: 20px; }
.bt-settings__section { display: flex; flex-direction: column; gap: 20px; }
/* `hidden` has to beat the display above, or the sections all render at once. */
.bt-settings__section[hidden] { display: none; }

/* General settings reads across the page rather than down it: where you are in
   the world (Time zone, Operations time zone) on the left, how a job is
   numbered and scored (Job number format, Rating scale) in the middle,
   Automatic timesheets on the right. Every column is the same width. A column
   whose panels the signed-in role can't see folds away and the others take its
   place, so a role that only gets Time zone still reads as a single panel on a
   readable measure — not a third of a wide page. Below the row breakpoint the
   columns stack in that same order. */
.bt-settings-columns { display: flex; flex-direction: column; gap: 20px; }
.bt-settings-columns__col { display: flex; flex-direction: column; gap: 20px; min-width: 0; }
.bt-settings-columns__col:not(:has(> .bt-panel:not([hidden]))) { display: none; }

/* Three columns only once each is wide enough to hold a form comfortably;
   below that they stack in the same order rather than squeezing. */
@media (min-width: 1360px) {
  .bt-settings-columns { flex-direction: row; align-items: flex-start; }
  .bt-settings-columns__col { flex: 1 1 0; }
  /* A column is a third of the page, so its forms want one field per row. */
  .bt-settings-columns .bt-form-grid { grid-template-columns: minmax(0, 1fr); }
  /* The rating scale is built for a full-width panel; in a third-width column
     the "% of target" / "of 5" units won't fit beside the number, so they move
     up into the column headings. */
  .bt-settings-columns .bt-scale__row .bt-scale__unit { display: none; }
  .bt-settings-columns .bt-scale__head > span:first-child::after { content: " (%)"; }
  .bt-settings-columns .bt-scale--lateness .bt-scale__head > span:first-child::after { content: " (min)"; }
  .bt-settings-columns .bt-scale__head > span:nth-child(2)::after { content: " (of 5)"; }
  /* Two or more columns of panels need the whole page; a lone Time zone panel
     keeps the narrow measure the other settings sections use. */
  .bt-page--settings:has(#settings-general:not([hidden]) .bt-settings-columns__col:first-child ~ .bt-settings-columns__col > .bt-panel:not([hidden])) {
    max-width: none;
  }
}

.bt-policy-form {
  display: grid;
  grid-template-columns: minmax(0, 280px) auto;
  align-items: start;
  gap: 12px;
}
.bt-policy-form > .bt-button { justify-self: start; margin-top: 24px; }
.bt-policy-form > .bt-alert { grid-column: 1 / -1; }

/* No room for a rail on a phone, so it lies down above the panels and scrolls
   sideways — the same three keys, still one row. */
@media (max-width: 760px) {
  .bt-settings { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .bt-settings__nav {
    position: static;
    flex-direction: row;
    gap: 4px;
    overflow-x: auto;
    padding-bottom: 2px;
    scrollbar-width: none;
  }
  .bt-settings__nav::-webkit-scrollbar { display: none; }
  .bt-settings__link { flex: 0 0 auto; }
  .bt-policy-form { grid-template-columns: minmax(0, 1fr); }
  .bt-policy-form > .bt-button { margin-top: 0; }
}

/* ---------------------------------------------------------------- syncro tickets */
/* A linked ticket reads as one line: its number, what it's about, and where it
   stands. The number leads because that is what gets said out loud and typed
   into every other system. */
.bt-tickets { display: flex; flex-direction: column; gap: 10px; }
.bt-tickets__chosen { display: flex; flex-direction: column; gap: 6px; }

.bt-ticket {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  padding: 7px 8px;
  border-radius: 10px;
  background: var(--bt-surface-nested);
  color: inherit;
  text-decoration: none;
}
.bt-ticket--link:hover { background: var(--bt-surface-interactive); }
.bt-ticket--empty { background: transparent; padding: 0; color: var(--bt-text-secondary); font-size: 13px; }
.bt-ticket__num {
  flex: 0 0 auto;
  align-self: flex-start;
  padding: 1px 7px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-accent-soft);
  color: var(--bt-accent-text);
  font-size: 11px;
  font-weight: 700;
  line-height: 18px;
}
.bt-ticket__body { display: flex; min-width: 0; flex: 1 1 auto; flex-direction: column; }
.bt-ticket__subject { font-size: 13px; font-weight: 500; line-height: 18px; }
/* The whole title, for someone who reads it to know which ticket they are on. */
.bt-ticket__subject--full { overflow-wrap: anywhere; }
.bt-ticket__meta { color: var(--bt-text-secondary); font-size: 11px; line-height: 16px; }
.bt-ticket > .bt-icon { flex: 0 0 auto; color: var(--bt-text-disabled); }
.bt-ticket--link:hover > .bt-icon { color: var(--bt-accent-text); }

/* The Orders page's Ticket number, when it resolved to a real Syncro ticket.
   Plain text with an arrow rather than the card above — a table row is
   already the unit of attention, so this only has to read as clickable. */
.bt-orders-link { display: inline-flex; align-items: center; gap: 4px; color: inherit; text-decoration: none; }
.bt-orders-link:hover { color: var(--bt-accent-text); }
.bt-orders-link .bt-icon { color: var(--bt-text-disabled); }
.bt-orders-link:hover .bt-icon { color: var(--bt-accent-text); }
/* The Client name cell reuses the same look on a <button> rather than an <a>
   — it opens the client detail pane in-page, not a new tab — so the browser's
   own button chrome is stripped back to plain text first. */
button.bt-orders-link { border: 0; background: none; padding: 0; font: inherit; cursor: pointer; }

/* The board's Tickets column. Numbers only — the column is ~96px, and the
   number is what gets read off a board and typed into something else. The
   subjects are in the tooltip. */
/* The cell is its own container, so what fits is measured against the column
   as it is drawn rather than the window: the board's columns flex, and a
   laptop-width board holds the Tickets column at its 96px floor. */
.bt-tickcell { flex: 1 1 auto; min-width: 0; container-type: inline-size; }
.bt-tickcell__row { display: flex; min-width: 0; align-items: center; gap: 4px; }
.bt-tickcell__more--narrow { display: none; }
@container (max-width: 119px) {
  .bt-tickcell__num--second,
  .bt-tickcell__more--wide { display: none; }
  .bt-tickcell__more--narrow { display: inline; }
}
/* Two numbers and a "+N" after them need a little more. */
@container (max-width: 139px) {
  .bt-tickcell__row--more .bt-tickcell__num--second,
  .bt-tickcell__row--more .bt-tickcell__more--wide { display: none; }
  .bt-tickcell__row--more .bt-tickcell__more--narrow { display: inline; }
}
.bt-tickcell__num {
  flex: 0 0 auto;
  padding: 1px 6px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-accent-soft);
  color: var(--bt-accent-text);
  font-size: 11px;
  font-weight: 700;
  line-height: 16px;
}
/* The same number on an inventory row, where it opens the ticket. The target is
   bigger than the pill it draws. */
.bt-invticket { position: relative; display: inline-block; text-decoration: none; }
.bt-invticket::after { content: ''; position: absolute; inset: -12px -4px; }
.bt-invticket:hover { background: var(--bt-accent); color: var(--bt-ink); }
.bt-invticket:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 2px; }
.bt-tickcell__more {
  flex: 0 0 auto;
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 600;
}

/* Search results, in the same shape as the linked rows above them so picking
   one is visibly the same object arriving. */
.bt-tickets__results {
  display: grid;
  max-height: 260px;
  gap: 2px;
  overflow-y: auto;
  padding: 4px;
  border-radius: 12px;
  background: var(--bt-surface-nested);
}
.bt-tickets__opt {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 10px;
  padding: 6px;
  border: 0;
  border-radius: var(--bt-radius-sm);
  background: transparent;
  color: inherit;
  text-align: left;
}
.bt-tickets__opt:hover { background: var(--bt-surface-nested); }
.bt-tickets__opt:disabled { cursor: default; opacity: 0.55; }
.bt-tickets__opt:disabled:hover { background: transparent; }
.bt-tickets__more { display: flex; justify-content: center; padding: 6px 0 2px; }
/* A count from a minimum to a maximum: minus, the number (typeable), plus, in
   the field's own edge and fill (the phone's ui/quantity-stepper.tsx). */
.bt-stepper {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: stretch;
  height: var(--bt-control-sm);
  overflow: hidden;
  border: 1px solid var(--bt-field-line);
  border-radius: var(--bt-radius-sm);
  background: var(--bt-surface-field);
}
.bt-stepper:focus-within { border-color: var(--bt-accent); }
.bt-stepper__step {
  display: inline-flex;
  width: var(--bt-control-sm);
  align-items: center;
  justify-content: center;
  border: 0;
  background: transparent;
  color: var(--bt-text-primary);
  cursor: pointer;
}
.bt-stepper__step:hover:not(:disabled) { background: var(--bt-surface-interactive); }
.bt-stepper__step:disabled { cursor: not-allowed; opacity: 0.38; }
.bt-stepper__value {
  width: 36px;
  min-width: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--bt-text-primary);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: center;
  -moz-appearance: textfield;
  appearance: textfield;
}
.bt-stepper__value:focus { outline: none; }
.bt-stepper__value::-webkit-outer-spin-button,
.bt-stepper__value::-webkit-inner-spin-button { margin: 0; -webkit-appearance: none; }
/* The job form's results, as a menu over the fields below (the same surface as
   .bt-select-menu), so opening them never moves the form. */
.bt-tickets__search { position: relative; }
.bt-tickets__search .bt-tickets__results {
  position: absolute;
  z-index: 180;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  max-height: 360px;
  padding: 6px;
  border: 1px solid var(--bt-hairline);
  border-radius: var(--bt-radius-panel);
  overscroll-behavior: contain;
  scrollbar-width: thin;
  background: var(--bt-surface-popover);
  box-shadow: var(--bt-shadow-popover);
  animation: bt-select-in var(--bt-duration-sm) var(--bt-ease);
}
.bt-tickets__search .bt-tickets__results[hidden] { display: none; }
.bt-tickets__search .bt-tickets__opt:hover:not(:disabled) { background: var(--bt-surface-interactive); }
.bt-ticket__on {
  flex: 0 0 auto;
  color: var(--bt-accent-text);
  font-size: 11px;
  font-weight: 600;
}

/* ---------------------------------------------------------------- assignee picker */
/* One control for the whole of "who is on this job": whoever is already on it
   at the top — job owner first — then a role filter over everyone who can
   be assigned. The booking form and the job pane share it, so assignment reads
   the same way wherever you happen to be doing it. The chosen list is the only
   part a read-only view renders, which is also all the job pane shows until
   the pencil is pressed. */
.bt-picker { display: flex; min-width: 0; flex-direction: column; gap: 10px; }
.bt-picker__chosen { display: flex; flex-direction: column; gap: 8px; }
.bt-picked { display: flex; align-items: center; gap: 10px; }
.bt-picked__body { display: flex; min-width: 0; flex: 1 1 auto; flex-direction: column; }
.bt-picked__name {
  min-width: 0;
  overflow: hidden;
  font-size: 14px;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bt-picked__name.is-empty { color: var(--bt-text-secondary); font-weight: 500; }
.bt-picked__role { color: var(--bt-text-secondary); font-size: 11px; line-height: 16px; }

/* The assignee editor is wide enough to keep all role tabs visible together. */
.bt-picker__roles {
  display: flex;
  flex-wrap: wrap;
  width: 100%;
  min-width: 0;
  max-width: 100%;
  box-sizing: border-box;
  overflow: visible;
}
.bt-picker__roles .bt-segmented__item { flex: 0 0 auto; }

.bt-picker__list {
  display: grid;
  min-width: 0;
  max-height: 216px;
  gap: 2px;
  overflow-x: hidden;
  overflow-y: auto;
  padding: 4px;
  border-radius: 12px;
  background: var(--bt-surface-nested);
}
.bt-picker__row { display: flex; align-items: center; gap: 2px; border-radius: var(--bt-radius-sm); }
.bt-picker__row:hover { background: var(--bt-surface-nested); }
.bt-picker__opt {
  display: flex;
  min-width: 0;
  flex: 1 1 auto;
  align-items: center;
  gap: 8px;
  padding: 6px;
  border: 0;
  border-radius: var(--bt-radius-sm);
  background: transparent;
  color: inherit;
  font-size: 13px;
  text-align: left;
}
.bt-picker__row.is-on .bt-picker__opt { color: var(--bt-accent-text); }
.bt-picker__opt > .bt-icon:last-child { flex: 0 0 auto; margin-left: auto; }
.bt-picker__name { min-width: 0; flex: 1 1 auto; font-weight: 500; }
.bt-picker__route { flex: 0 1 auto; color: var(--bt-text-secondary); font-size: 11px; line-height: 16px; white-space: nowrap; }
/* What the distance was measured from: a pin for where they are now, a house
   for their home address. */
.bt-picker__route { display: inline-flex; align-items: center; gap: 2px; }
.bt-picker__route > .bt-icon { flex: 0 0 auto; }
.bt-picker__route.is-live { color: var(--bt-positive-text); }
/* Only offered for someone already on the job — promoting a person who isn't on
   it yet is just picking them, which the row itself does. */
.bt-picker__main {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  margin-right: 4px;
  border: 0;
  border-radius: var(--bt-radius-sm);
  background: transparent;
  color: var(--bt-text-disabled);
}
.bt-picker__main:hover { background: var(--bt-surface-interactive); color: var(--bt-text-primary); }
.bt-picker__main.is-main { color: var(--bt-accent-text); }
.bt-picker__main.is-main:hover { background: transparent; color: var(--bt-accent-text); }
.bt-picker__main.is-route-active { background: var(--bt-accent-soft); color: var(--bt-accent-text); }
.bt-picker__main.is-route-active:hover { background: var(--bt-accent-soft); color: var(--bt-accent-text); }
.bt-picker__empty { padding: 10px 6px; color: var(--bt-text-secondary); font-size: 12px; }

/* Compact key/value details list (replaces the roomier meta grid in the pane). */
.bt-kv-list { display: grid; gap: 10px; }
.bt-kv { display: grid; grid-template-columns: 92px minmax(0, 1fr); gap: 12px; align-items: baseline; }
.bt-kv__k { color: var(--bt-text-secondary); font-size: 12px; font-weight: 500; line-height: 18px; }
/* A label that is an abbreviation (RTO, CTO), with its meaning in an info dot. */
.bt-kv__k-term { display: inline-flex; align-items: center; gap: 2px; }
.bt-kv__k-term > .bt-info-dot { margin-block: -2px; }
.bt-kv__v { font-size: 14px; font-weight: 500; line-height: 18px; overflow-wrap: anywhere; }
/* The job's checklists, one tag per template: a count when it was added more
   than once, a sliders icon when it was customized on the job (admin.html's
   checklistTagsHtml). */
/* Plain text like every other value in the list, one template per line; the
   marks trail the name and wrap with it rather than boxing it in. */
.bt-checklist-tags { display: grid; gap: 4px; justify-items: start; }
.bt-checklist-tag { color: var(--bt-text-primary); }
.bt-checklist-tag__count {
  margin-left: 6px;
  color: var(--bt-text-secondary);
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.bt-checklist-tag__count { white-space: nowrap; }
.bt-checklist-tag__edited {
  display: inline-block;
  margin-left: 6px;
  vertical-align: -2px;
  color: var(--bt-text-secondary);
}
.bt-checklist-tag__edited.bt-icon--14 { width: 13px; height: 13px; }
/* A second line under a value that needs one — a site's street address beneath
   its name. Quieter than the value itself, since it is the detail rather than
   the answer. */
.bt-kv__sub { margin-top: 2px; font-size: 12px; font-weight: 400; line-height: 17px; }
/* The job pane's facts: ~270px across, so a narrower label column and a
   slightly smaller value keep a date and time on one line. */
.bt-kv-list--tight { gap: 8px; }
.bt-kv-list--tight .bt-kv { grid-template-columns: 72px minmax(0, 1fr); gap: 10px; }
.bt-kv-list--tight .bt-kv__v { font-size: 13px; }
/* A job's times (jobtimes.js) read as figures. */
.bt-kv--time .bt-kv__v { font-variant-numeric: tabular-nums; }
/* A finished job's way to its timesheet, beside the bell and shaped like it. */
.bt-jobsheet { position: relative; display: inline-flex; }
.bt-jobsheet[hidden] { display: none; }
.bt-watchbell__btn.bt-jobsheet__btn { border-color: var(--bt-accent); background: var(--bt-accent-soft); color: var(--bt-accent-text); font-size: 12px; font-weight: 600; white-space: nowrap; }
/* Hover deepens the fill only; the label and icon keep their color, so the
   clock never dims and relights under the pointer. */
.bt-watchbell__btn.bt-jobsheet__btn:hover { background: color-mix(in srgb, var(--bt-accent) 30%, transparent); color: var(--bt-accent-text); }
.bt-jobsheet .bt-menu-pop { top: calc(100% + 6px); right: auto; left: 0; width: 240px; }
.bt-jobsheet--new { animation: bt-activity-arrive var(--bt-duration-md) var(--bt-ease) both; }
@media (prefers-reduced-motion: reduce) { .bt-jobsheet--new { animation: none; } }

/* Activity log — a reverse-chronological timeline, shared by techs and admins. */
.bt-log__body {
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - var(--bt-topbar-height) - 132px);
  padding: 6px 16px 14px;
  overflow-y: auto;
}
.bt-event {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 10px;
  padding: 7px 0;
}
.bt-event--new { animation: bt-activity-arrive var(--bt-duration-md) var(--bt-ease) 150ms both; }
@keyframes bt-activity-arrive {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) { .bt-event--new { animation: none; } }
.bt-event:not(:last-child)::before {
  content: "";
  position: absolute;
  top: 19px;
  bottom: -7px;
  left: 5px;
  width: 2px;
  background: var(--bt-line);
}
.bt-event__dot {
  width: 12px;
  height: 12px;
  margin-top: 4px;
  flex: 0 0 auto;
  border-radius: 50%;
  background: var(--bt-neutral);
}
/* Each dot is the color of the badge that state wears elsewhere: Done and
   Completed are green, In progress blue (the step chip), On hold amber,
   Not necessary violet, To-do neutral. A new job is teal like the "New" badge.
   apps/portal-app/src/domain/job.ts keeps the same map for the phone. */
.bt-event__dot--done,
.bt-event__dot--completed { background: var(--bt-positive); }
.bt-event__dot--in_progress,
.bt-event__dot--offer,
.bt-event__dot--arrive,
.bt-event__dot--work,
.bt-event__dot--drive,
.bt-event__dot--home,
.bt-event__dot--started,
.bt-event__dot--resumed,
.bt-event__dot--assigned,
.bt-event__dot--helper,
.bt-event__dot--reopened,
.bt-event__dot--edit,
.bt-event__dot--dispatch,
/* archived/unarchived are retired event types, kept so log entries written
   before archiving was removed still get their colour. */
.bt-event__dot--unarchived { background: var(--bt-info); }
.bt-event__dot--on_hold,
.bt-event__dot--break,
.bt-event__dot--archived { background: var(--bt-warning); }
.bt-event__dot--created { background: var(--bt-accent); }
.bt-event__dot--not_necessary { background: var(--bt-skip); }
/* The job paused, started or resumed: a pause or play glyph in place of the
   dot, in the same On hold amber and In progress blue. The phone draws the
   same glyphs (apps/portal-app/src/app/jobs/activity/[id].tsx). */
.bt-event__dot--paused,
.bt-event__dot--started,
.bt-event__dot--resumed {
  border-radius: 0;
  -webkit-mask: var(--bt-event-glyph) center / 12px 12px no-repeat;
          mask: var(--bt-event-glyph) center / 12px 12px no-repeat;
}
.bt-event__dot--paused {
  background: var(--bt-warning);
  --bt-event-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Crect x='2' y='1' width='3' height='10' rx='1'/%3E%3Crect x='7' y='1' width='3' height='10' rx='1'/%3E%3C/svg%3E");
}
.bt-event__dot--started,
.bt-event__dot--resumed {
  background: var(--bt-info);
  --bt-event-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M3 1.6v8.8a.8.8 0 0 0 1.2.7l6.9-4.4a.8.8 0 0 0 0-1.4L4.2.9A.8.8 0 0 0 3 1.6z'/%3E%3C/svg%3E");
}
.bt-event__text { color: var(--bt-text-primary); font-size: 13px; line-height: 18px; overflow-wrap: anywhere; }
.bt-event__text b { font-weight: 600; }
.bt-event__time { margin-top: 2px; color: var(--bt-text-disabled); font-size: 11px; line-height: 16px; }

/* Under 1600 there isn't room for four panes without squeezing the checklist,
   so the activity log drops to a full-width strip below. The conversation stays
   the right-hand column either way. */
@media (max-width: 1599px) {
  .bt-jobgrid { grid-template-columns: minmax(0, var(--jg-info)) minmax(0, 1fr) minmax(0, var(--jg-conv)); }
  .bt-jobgrid__conv { grid-column: 3; grid-row: 1; }
  .bt-jobgrid__log { position: static; grid-column: 1 / -1; grid-row: 2; }
  .bt-jobgrid__log .bt-log__body { max-height: 380px; }
}
/* While the details and the conversation are fixed to the viewport either side,
   a full-width row would run underneath them; the log goes under the checklist
   instead, where nothing covers it however short the checklist is. */
@media (min-width: 1241px) and (max-width: 1599px) {
  .bt-jobgrid__log { grid-column: 2; }
}
/* Below 1240 both feeds become full-width strips under the info+checklist pair,
   conversation first. */
@media (max-width: 1240px) {
  .bt-jobgrid { grid-template-columns: minmax(0, var(--jg-info)) minmax(0, 1fr); }
  .bt-jobgrid__conv { position: static; grid-column: 1 / -1; grid-row: 2; }
  .bt-jobgrid__log { grid-row: 3; }
}
@media (max-width: 760px) {
  .bt-jobgrid { grid-template-columns: minmax(0, 1fr); gap: 14px; }
  .bt-jobgrid__info { position: static; }
  /* One column: hand every pane back to auto-placement so they stack in DOM
     order (info · checklist · conversation · activity) instead of the pinned
     columns/rows creating phantom tracks. */
  .bt-jobgrid > * { grid-column: auto; grid-row: auto; }
}

/* Above the stacked layout, only the checklist scrolls with the document.
   JavaScript resolves the live grid tracks into viewport coordinates so the
   information, activity and conversation panes remain genuinely fixed rather
   than being pulled upward by a sticky containing block at the page end. */
@media (min-width: 1241px) {
  .bt-jobhead--fixed {
    position: fixed;
    z-index: 12;
    top: var(--bt-fixed-top);
    left: var(--bt-fixed-left);
    width: var(--bt-fixed-width);
  }
  .bt-jobgrid__pane--fixed {
    position: fixed;
    z-index: 10;
    top: var(--bt-fixed-top);
    left: var(--bt-fixed-left);
    width: var(--bt-fixed-width);
    margin: 0;
  }
  .bt-jobgrid__info.bt-jobgrid__pane--fixed,
  .bt-jobgrid__log.bt-jobgrid__pane--fixed,
  .bt-jobgrid__conv.bt-jobgrid__pane--fixed {
    height: var(--bt-fixed-height);
  }
  .bt-jobgrid__conv.bt-jobgrid__pane--fixed .bt-conv { height: 100%; }
  .bt-jobgrid__log.bt-jobgrid__pane--fixed .bt-log {
    display: flex;
    max-height: 100%;
    flex-direction: column;
    overflow: hidden;
  }
  .bt-jobgrid__log.bt-jobgrid__pane--fixed .bt-log__body {
    min-height: 0;
    flex: 1 1 auto;
    max-height: none;
    overflow-y: auto;
  }
}

/* Folding a job's panes (jobpanes.js). A side pane with a column of its own
   folds sideways into a strip; once the layout has stacked it under the
   checklist it folds up into its header instead. The widths below are the
   ones jobpanes.js mirrors in WIDE. */
.bt-jobgrid.is-jp-instant,
.bt-jobgrid.is-jp-instant .bt-icon { transition: none; }

.bt-pane-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: -8px -8px -4px 0;
}
.bt-pane-head .bt-overline { margin: 0; }
/* An open Details pane is plainly the details, so its name is only said when
   folded. Open, the header gives its row back and the chevron sits beside the
   first section's label; the heading stays for screen readers. */
.bt-jobgrid:not(.is-info-folded) .bt-jobgrid__info .bt-pane-head { justify-content: flex-end; margin-bottom: -36px; }
.bt-jobgrid:not(.is-info-folded) .bt-jobgrid__info .bt-pane-head > .bt-overline {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.bt-jobgrid__info .bt-pane-head > .bt-pane-toggle { position: relative; z-index: 1; }
.bt-pane-head,
.bt-jobgrid .bt-panel__head:has(> .bt-pane-toggle) { cursor: pointer; }
/* A header that folds lights its chevron wherever it is hovered, since the
   chevron is what the click does. */
.bt-pane-head:hover > .bt-pane-toggle,
.bt-panel__head:hover > .bt-pane-toggle,
.bt-cl-section[data-jp-section] > .bt-cl-head:hover .bt-cl-toggle { background: var(--bt-surface); color: var(--bt-text-primary); }
.bt-panel__head > .bt-pane-toggle { margin-right: -6px; }

.bt-pane-toggle .bt-icon,
.bt-cl-toggle .bt-icon,
.bt-pane-rail__chev .bt-icon { transition: transform var(--bt-duration-sm) var(--bt-ease); }
/* Stacked: up to fold, down to unfold. */
.bt-pane-toggle .bt-icon,
.bt-cl-toggle .bt-icon { transform: rotate(180deg); }
.bt-pane-toggle[aria-expanded="false"] .bt-icon,
.bt-cl-toggle[aria-expanded="false"] .bt-icon { transform: none; }

.bt-pane-rail {
  display: none;
  width: 100%;
  height: 100%;
  min-height: 240px;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 12px 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--bt-text-secondary);
  cursor: pointer;
  transition: color var(--bt-duration-sm) var(--bt-ease), box-shadow var(--bt-duration-sm) var(--bt-ease);
}
/* A folded pane is a line on the checklist side of its strip, not a box. */
.bt-jobgrid__info > .bt-pane-rail { box-shadow: inset -1px 0 0 var(--bt-hairline); }
.bt-jobgrid__log > .bt-pane-rail,
.bt-jobgrid__conv > .bt-pane-rail { box-shadow: inset 1px 0 0 var(--bt-hairline); }
.bt-pane-rail:hover { color: var(--bt-text-primary); }
.bt-jobgrid__info > .bt-pane-rail:hover { box-shadow: inset -1px 0 0 var(--bt-accent); }
.bt-jobgrid__log > .bt-pane-rail:hover,
.bt-jobgrid__conv > .bt-pane-rail:hover { box-shadow: inset 1px 0 0 var(--bt-accent); }
.bt-pane-rail:focus-visible { outline-offset: -2px; }
.bt-pane-rail__chev { display: inline-flex; width: 32px; height: 32px; align-items: center; justify-content: center; }
.bt-pane-rail__label {
  color: var(--bt-text-primary);
  font-size: 13px;
  font-weight: 600;
  line-height: 18px;
  writing-mode: vertical-rl;
}
.bt-pane-rail__count {
  min-width: 22px;
  padding: 2px 6px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-accent-soft);
  color: var(--bt-accent-text);
  font-size: 11px;
  font-weight: 600;
  line-height: 18px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.bt-pane-head__count { display: none; margin-left: auto; margin-right: -4px; }
.bt-jobgrid.is-conversation-folded .bt-jobgrid__conv .bt-pane-head__count:not([hidden]) { display: inline-block; }
/* The strip points the way the pane will open. */
.bt-pane-rail[data-jp-pane="info"] .bt-pane-rail__chev .bt-icon { transform: rotate(-90deg); }
/* A side strip stands as tall as the conversation it replaces, whether or not
   layout.js has fixed it to the viewport yet. */
.bt-jobgrid__log > .bt-pane-rail,
.bt-jobgrid__conv > .bt-pane-rail {
  height: calc(100vh - var(--bt-job-pane-top, calc(var(--bt-topbar-height) + 20px)) - var(--bt-job-pane-bottom, 24px));
  height: calc(100dvh - var(--bt-job-pane-top, calc(var(--bt-topbar-height) + 20px)) - var(--bt-job-pane-bottom, 24px));
}
.bt-jobgrid__pane--fixed > .bt-pane-rail { height: 100%; }
.bt-pane-rail[data-jp-pane="activity"] .bt-pane-rail__chev .bt-icon,
.bt-pane-rail[data-jp-pane="conversation"] .bt-pane-rail__chev .bt-icon { transform: rotate(90deg); }

/* Details: its own column from 761px up. */
@media (min-width: 761px) {
  .bt-pane-toggle[data-jp-pane="info"] .bt-icon { transform: rotate(90deg); }
  .bt-jobgrid.is-info-folded { --jg-info: 36px; }
  .bt-jobgrid.is-info-folded .bt-jobgrid__info > :not(.bt-pane-rail) { display: none; }
  .bt-jobgrid.is-info-folded .bt-jobgrid__info > .bt-pane-rail { display: flex; }
}
@media (max-width: 760px) {
  .bt-jobgrid.is-info-folded .bt-jobgrid__info > :not(:first-child) { display: none; }
  .bt-jobgrid.is-info-folded .bt-jobgrid__info > .bt-panel:first-child > :not(.bt-pane-head) { display: none; }
  .bt-jobgrid.is-info-folded .bt-pane-head { margin-bottom: -8px; }
}
/* Conversation: its own column from 1241px up. */
@media (min-width: 1241px) {
  .bt-pane-toggle[data-jp-pane="conversation"] .bt-icon { transform: rotate(-90deg); }
  .bt-jobgrid.is-conversation-folded { --jg-conv: 36px; }
  .bt-jobgrid.is-conversation-folded .bt-jobgrid__conv > .bt-conv { display: none; }
  .bt-jobgrid.is-conversation-folded .bt-jobgrid__conv > .bt-pane-rail { display: flex; }
}
@media (max-width: 1240px) {
  .bt-jobgrid.is-conversation-folded .bt-conv { height: auto; }
  .bt-jobgrid.is-conversation-folded .bt-conv > :not(.bt-panel__head) { display: none; }
  .bt-jobgrid.is-conversation-folded .bt-conv > .bt-panel__head { padding-bottom: 16px; }
}
/* Activity: its own column from 1600px up. */
@media (min-width: 1600px) {
  .bt-pane-toggle[data-jp-pane="activity"] .bt-icon { transform: rotate(-90deg); }
  .bt-jobgrid.is-activity-folded { --jg-log: 36px; }
  .bt-jobgrid.is-activity-folded .bt-jobgrid__log > .bt-log { display: none; }
  .bt-jobgrid.is-activity-folded .bt-jobgrid__log > .bt-pane-rail { display: flex; }
}
@media (max-width: 1599px) {
  .bt-jobgrid.is-activity-folded .bt-log > :not(.bt-panel__head) { display: none; }
  .bt-jobgrid.is-activity-folded .bt-log > .bt-panel__head { padding-bottom: 16px; }
}

/* ---------------------------------------------------------------- sheet / dialog */
.bt-scrim {
  position: fixed;
  z-index: 60;
  inset: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 0;
  background: rgba(0, 8, 10, 0.62);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--bt-duration-sm) var(--bt-ease);
}
.bt-scrim.is-open { opacity: 1; pointer-events: auto; }

.bt-sheet {
  width: 100%;
  max-width: 460px;
  /* Status options plus a note can outgrow a short screen (or one with the
     keyboard up), so the sheet scrolls rather than pushing Save off-screen. */
  max-height: 100%;
  overflow-y: auto;
  padding: 16px 14px calc(18px + env(safe-area-inset-bottom));
  border-radius: var(--bt-radius-sheet) var(--bt-radius-sheet) 0 0;
  background: var(--bt-surface-popover);
  box-shadow: var(--bt-shadow-popover);
  transform: translateY(24px);
  transition: transform var(--bt-duration-sm) var(--bt-ease);
}
.bt-scrim.is-open .bt-sheet { transform: none; }

/* Manage fields: one grid per type, so the kind, name, the two requirements,
   the count and the action each keep a column. The add row under it shares
   the first two. */
.bt-invtype-row,
.bt-invtype-add {
  display: grid;
  grid-template-columns: minmax(140px, 1fr) 112px 112px 64px 150px;
  align-items: center;
  gap: 8px;
}
.bt-invtype-row { padding: 8px 0; border-bottom: 1px solid var(--bt-line); }
.bt-invtype-row > :last-child { justify-self: end; }
.bt-invtype-row .bt-select,
.bt-invtype-add .bt-select { width: 100%; }
.bt-invtype-row__name { display: flex; align-items: center; gap: 8px; min-width: 0; }
.bt-invtype-row__name .bt-input-shell { flex: 1; min-width: 0; }
/* Up and down, before the name; a retired row keeps the space so names line up. */
.bt-invtype-row__move { display: flex; flex: none; gap: 2px; width: 66px; }
.bt-invtype-add { grid-template-columns: minmax(0, 1fr) auto; }
@media (max-width: 720px) {
  .bt-invtype-row { grid-template-columns: 1fr 1fr; }
  .bt-invtype-row__name { grid-column: 1 / -1; }
}

.bt-sheet__title {
  padding: 0 10px 10px;
  color: var(--bt-text-secondary);
  font-size: 13px;
  font-weight: 600;
  line-height: 18px;
}

/* The work-clock prompt is operational copy, so it reads as one left-aligned
   instruction instead of the centred confirmation/reveal dialogs elsewhere. */
#work-overlay .bt-sheet { text-align: left; }
#work-dialog-title,
#work-dialog-copy { text-align: left; }
#work-dialog-copy { margin: 0; padding: 0 10px; }

.bt-sheet__opt {
  display: flex;
  width: 100%;
  min-height: 48px;
  align-items: center;
  gap: 12px;
  padding: 8px 10px;
  border: 0;
  border-radius: var(--bt-radius-cell);
  background: transparent;
  color: var(--bt-text-primary);
  font-size: 15px;
  font-weight: 500;
  text-align: left;
  transition: background var(--bt-duration-sm) var(--bt-ease);
}
.bt-sheet__opt:hover { background: var(--bt-surface-nested); }
.bt-sheet__opt.is-selected { background: var(--bt-accent-soft); }
.bt-sheet__opt .bt-st-icon { margin-top: 0; }
.bt-sheet__opt__check { margin-left: auto; color: var(--bt-accent-text); }

/* The note lives with the status options, so an item is set in one place. */
.bt-sheet__note { padding: 12px 10px 4px; }
.bt-sheet__note .bt-label { display: block; margin-bottom: 6px; }
.bt-sheet__note textarea.bt-input { min-height: 74px; resize: vertical; }

.bt-sheet__actions {
  display: flex;
  gap: 8px;
  margin-top: 12px;
}
.bt-sheet__actions .bt-button { flex: 1 1 0; }

/* Record-save actions are a matched pair rather than two unrelated full-width
   controls. This keeps their size and the space between them consistent. */
/* "(required)" beside a field's label, marked before the save is refused. */
.bt-required { color: var(--bt-danger-text); font-weight: 600; }
/* A record's Details and History tabs, with Close at the far end on History. */
.bt-record-tabs { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin: 0 8px 8px; }
/* Manage fields: only the list scrolls; Cancel and Save sit at the window's foot. */
.bt-sheet.bt-invfields-sheet { display: flex; flex-direction: column; overflow: hidden; }
.bt-invfields-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.bt-invfields-foot { flex: none; margin-top: 0; padding: 12px 8px 0; border-top: 1px solid var(--bt-line); }
/* An inventory list fits its panel: long models and serials from an imported
   sheet wrap in their cell rather than pushing Quantity out of sight.
   Each column's width is its share of the panel (fitInvCols in admin.html),
   and it never scrolls sideways. */
#inv-body .bt-tsheet { table-layout: fixed; min-width: 100%; }
#inv-body .bt-tsheet-scroll { overflow-x: hidden; }
/* One line in every cell: a value too long for its column ends in "…" (the
   whole of it shows on hover) rather than wrapping. */
#inv-body .bt-tsheet td { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#inv-body .bt-tsheet td .bt-tsheet__title { flex-wrap: nowrap; }
#inv-body .bt-tsheet td .bt-tsheet__clients { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#inv-body .bt-tsheet td .bt-tsheet__summary { max-width: none; }
#inv-body .bt-tsheet td .bt-tickcell__num { white-space: nowrap; }
/* A date stays on one line; a column too narrow for it ends in an ellipsis. */
.bt-invdate { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Quantity's frames sit centered under the heading. */
#inv-body .bt-tsheet .bt-tsheet__num, #inv-body .bt-tsheet thead th.bt-tsheet__num { text-align: center; }
#inv-body .bt-tsheet .bt-invcheck { padding-right: 0; text-align: center; vertical-align: middle; }
/* A header: its label (a sort button where the column sorts) and, on its right
   edge, the handle that sets its width. */
.bt-invth { position: relative; overflow: hidden; text-overflow: ellipsis; }
.bt-invsort {
  display: inline-flex;
  max-width: 100%;
  align-items: center;
  gap: 2px;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  cursor: pointer;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
}
.bt-invsort .bt-icon { flex: none; opacity: 0.5; }
.bt-invsort:hover, .bt-invsort.is-on { color: var(--bt-text-primary); }
.bt-invsort.is-on .bt-icon { opacity: 1; }
.bt-invsort:focus-visible { outline: 2px solid var(--bt-focus, var(--bt-accent)); outline-offset: 2px; border-radius: 4px; }
/* A column's filter, beside its name and no taller than it, so the heading
   row keeps its height. Accent while a value is chosen. */
/* A heading's words, its sort arrow and its filter on one line. The words give
   way to "…"; the icons never do, and stay clear of the drag handle. */
.bt-invth__in { display: inline-flex; height: 16px; max-width: 100%; min-width: 0; align-items: center; gap: 2px; vertical-align: middle; }
.bt-invth__text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bt-invsort { min-width: 0; }
.bt-invfilter {
  display: inline-flex;
  min-width: 0;
  max-width: 100%;
  height: 16px;
  align-items: center;
  gap: 2px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 4px;
  background: none;
  color: inherit;
  cursor: pointer;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
}
.bt-invfilter .bt-icon { flex: none; opacity: 0.55; }
.bt-invfilter:hover { color: var(--bt-text-primary); }
.bt-invfilter:hover .bt-icon { opacity: 1; }
.bt-invfilter.is-on .bt-icon { opacity: 1; color: var(--bt-accent-text); }
.bt-invfilter:focus-visible { outline: 2px solid var(--bt-focus, var(--bt-accent)); outline-offset: 1px; }
.bt-invgrid__main { position: relative; }
.bt-invfilter-menu { right: auto; }
.bt-invfilter-menu .bt-colmenu__row { justify-content: space-between; }
.bt-invth__grip {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 9px;
  cursor: col-resize;
  touch-action: none;
}
.bt-invth__grip::after {
  content: "";
  position: absolute;
  top: 2px;
  right: 3px;
  bottom: 10px;
  width: 2px;
  border-radius: 1px;
  background: var(--bt-line);
}
.bt-invth__grip:hover::after, .bt-invth__grip.is-dragging::after, .bt-invth__grip:focus-visible::after { background: var(--bt-accent); }
.bt-invth__grip:focus-visible { outline: none; }
/* The sheet's tools: search, the two filters and Columns; Activity's person
   and days. They wrap on a narrow screen. */
.bt-invfilters { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 12px; }
.bt-invfilters[hidden] { display: none; }
.bt-invfilters__search { flex: 1 1 260px; }
.bt-invfilters .bt-select--inline { max-width: 220px; min-height: var(--bt-control-sm, 32px); }
/* The site, beside the panel's heading. */
.bt-inv-site { max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bt-inv-site[hidden] { display: none; }
/* Activity takes the whole width: with the type rail put away, its column goes too. */
.bt-invgrid:has(> .bt-invgrid__rail[hidden]) { grid-template-columns: minmax(0, 1fr); }
/* Activity: one line per thing that happened, who and what on the left and
   when on the right, in a framed list; Show more under it. */
.bt-invlog {
  display: flex;
  flex-direction: column;
  padding: 6px 18px;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-panel);
  background: var(--bt-surface-raised);
}
.bt-invlog .bt-event { padding: 10px 0; }
.bt-invlog .bt-event:not(:last-child)::before { top: 23px; bottom: -11px; }
.bt-invlog .bt-event__dot { margin-top: 3px; }
.bt-invlog .bt-event > div {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  column-gap: 20px;
}
.bt-invlog .bt-event__text { font-size: 13px; line-height: 19px; }
.bt-invlog .bt-event__time { margin-top: 0; white-space: nowrap; font-size: 12px; }
@media (max-width: 640px) {
  .bt-invlog { padding: 4px 12px; }
  .bt-invlog .bt-event > div { grid-template-columns: minmax(0, 1fr); }
  .bt-invlog .bt-event__time { margin-top: 2px; }
}
/* An asset named in the log, opening it. */
.bt-invlink { color: var(--bt-accent-text); font-weight: 600; text-decoration: none; }
.bt-invlink:hover { text-decoration: underline; }
.bt-invlink:focus-visible { outline: 2px solid var(--bt-focus, var(--bt-accent)); outline-offset: 2px; border-radius: 3px; }
.bt-invlog__more { display: flex; justify-content: center; padding: 16px 0 4px; }
/* A record's status, and what can be done about it, on one line. */
.bt-record-status { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px; }
.bt-sheet__actions.bt-record-actions {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  margin-top: 4px;
}
.bt-record-actions .bt-button,
.bt-record-actions .bt-sheet__cancel { width: 100%; margin-top: 0; justify-content: center; }

.bt-sheet__cancel {
  width: 100%;
  min-height: var(--bt-control-md);
  margin-top: 10px;
  border: 0;
  border-radius: var(--bt-radius-round);
  background: var(--bt-surface-interactive);
  color: var(--bt-text-primary);
  font-size: 14px;
  font-weight: 500;
}

@media (min-width: 761px) {
  .bt-scrim { align-items: center; padding: 24px; }
  .bt-sheet { max-width: 400px; border-radius: 18px; padding: 16px 14px 14px; }
}

/* ---------------------------------------------------------------- save pill */
.bt-save-pill {
  flex: 0 0 auto;
  color: var(--bt-text-secondary);
  font-size: 12px;
  font-weight: 600;
  line-height: 18px;
  white-space: nowrap;
}
.bt-save-pill.ok { color: var(--bt-positive-text); }
.bt-save-pill.bad { color: var(--bt-danger-text); }

/* ---------------------------------------------------------------- rail widgets */
.bt-widget {
  border-radius: var(--bt-radius-panel);
  background: var(--bt-surface);
}

.bt-widget__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 14px 16px 8px;
}

.bt-widget__body { padding: 0 12px 12px; }

.bt-stat-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  min-height: 48px;
  padding: 6px 8px;
  border-radius: var(--bt-radius-cell);
}
.bt-stat-row + .bt-stat-row { box-shadow: 0 -1px 0 var(--bt-line); }
.bt-stat-row__value { font-family: var(--bt-font-display); font-size: 20px; font-weight: 700; line-height: 1; }

.bt-dot {
  width: 9px;
  height: 9px;
  flex: 0 0 auto;
  border-radius: 50%;
  background: currentColor;
}

/* quick-action rows inside a rail widget */
.bt-quick {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 12px;
  padding: 10px 8px;
  border: 0;
  border-radius: var(--bt-radius-cell);
  background: transparent;
  color: inherit;
  text-align: left;
  transition: background var(--bt-duration-sm) var(--bt-ease);
}
.bt-quick:hover { background: var(--bt-surface-nested); }

/* ---------------------------------------------------------------- profile */
.bt-profile-head {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 20px 16px;
}

.bt-profile-photo {
  position: relative;
  flex: 0 0 auto;
}
.bt-profile-photo__edit {
  position: absolute;
  right: -2px;
  bottom: -2px;
  display: grid;
  width: 30px;
  height: 30px;
  place-items: center;
  border: 3px solid var(--bt-surface-popover);
  border-radius: 50%;
  background: var(--bt-accent);
  color: var(--bt-ink);
}
.bt-profile-photo__edit .bt-icon { width: 14px; height: 14px; }
.bt-profile-photo__edit:hover { filter: brightness(1.08); }
.bt-profile-photo__edit:active { transform: scale(0.94); }

.bt-info-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: 48px;
  padding: 10px 16px;
}
.bt-info-row + .bt-info-row { box-shadow: 0 -1px 0 var(--bt-line); }
.bt-info-row__label { color: var(--bt-text-secondary); font-size: 13px; }
.bt-info-row__value { font-size: 14px; font-weight: 500; text-align: right; overflow-wrap: anywhere; }

/* ---------------------------------------------------------------- star rating */
/* Two identical rows of stars stacked on top of each other; the upper one is
   clipped to --fill, so any fraction of a star reads correctly. */
.bt-stars {
  position: relative;
  display: inline-block;
  flex: 0 0 auto;
  line-height: 0;
}

.bt-stars__row {
  display: flex;
  gap: 3px;
  white-space: nowrap;
}

.bt-stars__row .bt-icon {
  width: var(--bt-star-size, 18px);
  height: var(--bt-star-size, 18px);
}

.bt-stars__base { color: var(--bt-track); }

.bt-stars__fill {
  position: absolute;
  top: 0;
  left: 0;
  overflow: hidden;
  width: var(--bt-star-fill, 0%);
  color: var(--bt-accent);
  transition: width 900ms var(--bt-ease);
}
.bt-stars__fill .bt-stars__row { width: max-content; }

.bt-stars--sm { --bt-star-size: 13px; }
.bt-stars--lg { --bt-star-size: 24px; }
.bt-stars--xl { --bt-star-size: 34px; }
.bt-stars--xl .bt-stars__row { gap: 6px; }

/* Scores below the benchmark shade warm rather than teal, so a weak result
   never reads as a good one at a glance. */
.bt-stars.is-low .bt-stars__fill { color: var(--bt-warning); }
.bt-stars.is-poor .bt-stars__fill { color: var(--bt-danger); }

.bt-score {
  display: inline-flex;
  align-items: baseline;
  gap: 2px;
  font-family: var(--bt-font-display);
  font-weight: 700;
}
.bt-score__value { font-size: 1em; line-height: 1; }
.bt-score__max { color: var(--bt-text-disabled); font-size: 0.5em; font-weight: 500; }

/* ---------------------------------------------------------------- performance */
.bt-perf-head {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 18px 16px 4px;
}

.bt-perf-head__score {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}
.bt-perf-head__score .bt-score { font-size: 40px; }

/* The grid closes out its panel, so its opaque cells would otherwise square off
   the panel's bottom corners. It carries the radius and clips them itself. */
.bt-perf-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  overflow: hidden;
  margin-top: 14px;
  background: var(--bt-line);
  border-top: 1px solid var(--bt-line);
  border-radius: 0 0 var(--bt-radius-panel) var(--bt-radius-panel);
}

/* Four facts: two across on a phone, so no cell is left on a row alone. */
.bt-perf-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 560px) {
  .bt-perf-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.bt-perf-cell {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 12px 14px;
  background: var(--bt-surface-nested);
}
.bt-perf-cell__value { font-family: var(--bt-font-display); font-size: 19px; font-weight: 700; line-height: 26px; }
.bt-perf-cell__label { color: var(--bt-text-secondary); font-size: 11px; font-weight: 500; line-height: 16px; }

/* Measured time against the benchmark (paceGeometry). The bar fills to the end
   at the benchmark; the marker is drawn only past it, where it shows how far
   back along the time worked the benchmark fell.
   The track is block-level in its own right: it is used as a <span> inside the
   technician record's job rows, and an inline box would collapse to nothing and
   leave the absolutely-positioned marker floating on its own. */
.bt-pace {
  display: block;
  position: relative;
  height: 6px;
  overflow: hidden;
  border-radius: var(--bt-radius-round);
  background: var(--bt-track);
}

.bt-pace__fill {
  height: 100%;
  border-radius: inherit;
  background: var(--bt-accent);
  transition: width var(--bt-duration-md) var(--bt-ease);
}
.bt-pace__fill--over { background: var(--bt-warning); }
.bt-pace__fill--late { background: var(--bt-danger); }

.bt-pace__mark {
  position: absolute;
  top: -2px;
  bottom: -2px;
  width: 2px;
  background: var(--bt-text-primary);
  opacity: 0.6;
}

.bt-perf-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 12px 8px;
  border: 0;
  border-radius: var(--bt-radius-cell);
  background: transparent;
  color: inherit;
  text-align: left;
  transition: background var(--bt-duration-sm) var(--bt-ease);
}
.bt-perf-row + .bt-perf-row { box-shadow: 0 -1px 0 var(--bt-line); }
.bt-perf-row:hover { background: var(--bt-surface-nested); }

.bt-perf-row__side {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
}
/* A list of jobs with time bars: the score column is one width on every row,
   so the bars beside it all end at the same point. Its line wraps rather than
   pushing the bar. */
.bt-perf-row--bars { grid-template-columns: auto minmax(0, 1fr) 210px auto; }
.bt-perf-row--bars .bt-perf-row__side { text-align: right; }
@media (max-width: 760px) {
  .bt-perf-row--bars { grid-template-columns: auto minmax(0, 1fr) 132px auto; }
}

/* The key to those bars, beside the list's heading. */
.bt-pace-key {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--bt-text-secondary);
  font-size: 12px;
  line-height: 18px;
}
.bt-pace-key__fill { width: 16px; height: 6px; border-radius: var(--bt-radius-round); background: var(--bt-accent); }
.bt-pace-key__mark { width: 2px; height: 12px; margin-left: 10px; background: var(--bt-text-primary); opacity: 0.6; }

/* What a figure counts, in a line under its label. */
.bt-perf-cell__hint { color: var(--bt-text-disabled); font-size: 11px; line-height: 15px; }

/* ---------------------------------------------------------------- rating reveal */
.bt-reveal {
  width: 100%;
  max-width: 420px;
  padding: 28px 24px 20px;
  border-radius: var(--bt-radius-sheet);
  background: var(--bt-surface-popover);
  box-shadow: var(--bt-shadow-popover);
  text-align: center;
  transform: translateY(24px) scale(0.97);
  transition: transform var(--bt-duration-md) var(--bt-ease);
}
.bt-scrim.is-open .bt-reveal { transform: none; }

.bt-reveal__glow {
  position: relative;
  display: flex;
  justify-content: center;
  padding: 6px 0 14px;
}
.bt-reveal__glow::before {
  position: absolute;
  z-index: 0;
  top: 50%;
  left: 50%;
  width: 240px;
  height: 240px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--bt-accent-soft), transparent 62%);
  content: "";
  transform: translate(-50%, -50%);
}
.bt-reveal__glow > * { position: relative; z-index: 1; }

.bt-reveal__score {
  font-family: var(--bt-font-display);
  font-size: 56px;
  font-weight: 900;
  line-height: 1;
}

.bt-reveal__verdict {
  margin-top: 10px;
  font-family: var(--bt-font-display);
  font-size: 20px;
  font-weight: 700;
  line-height: 26px;
}

.bt-reveal__lede {
  margin-top: 6px;
  color: var(--bt-text-secondary);
  font-size: 14px;
  line-height: 20px;
}

.bt-reveal__times {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  margin-top: 18px;
  text-align: left;
}

.bt-reveal__time {
  padding: 10px 12px;
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
}
.bt-reveal__time b { display: block; font-family: var(--bt-font-display); font-size: 18px; line-height: 24px; }
.bt-reveal__time span { color: var(--bt-text-secondary); font-size: 11px; font-weight: 500; }

/* ---------------------------------------------------------------- permission matrix */
/* On a wide screen the table scrolls with the page and its header sticks under
   the topbar. A sticky header cannot stick to the page from inside a sideways
   scroller, so only a narrow screen (see below) makes this box the scroller,
   in both directions, with the header stuck to the box's top instead. */
.bt-matrix-scroll {
  border-radius: var(--bt-radius-panel);
  -webkit-overflow-scrolling: touch;
}

.bt-matrix {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: 13px;
}

.bt-matrix th,
.bt-matrix td {
  padding: 10px 12px;
  border-bottom: 1px solid var(--bt-line);
  text-align: center;
  vertical-align: middle;
}

/* The header row sticks under the topbar while the page scrolls, like the
   notification grids' headings. At rest it is plain labels on the panel; once
   stuck (is-stuck, set by layout.js) a frosted layer slides in behind it and
   reaches up behind the see-through topbar, so the two read as one bar and no
   rows show in the strip between them. On a narrow screen the table scrolls in
   its own box instead (below), where the heading is always frosted. */
.bt-matrix thead {
  position: sticky;
  z-index: 3;
  top: var(--bt-topbar-height);
}
.bt-matrix thead::before {
  position: absolute;
  z-index: -1;
  inset: 0;
  background: color-mix(in srgb, var(--bt-canvas) 55%, transparent);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  backdrop-filter: blur(16px) saturate(140%);
  content: '';
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--bt-duration-sm) var(--bt-ease);
}
.bt-matrix thead.is-stuck::before {
  /* Out over the panel body's 8px padding, so the band meets the panel edges. */
  inset: calc(-1 * var(--bt-topbar-height)) -8px 0;
  opacity: 1;
}
.bt-matrix thead th {
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}
/* is-stacked is set by layout.js when the table is wider than the panel can
   show. Rather than scroll sideways (where no header can stick to the page),
   each permission restacks: its name on a line of its own, then a switch per
   role with the role's name above it. The header row has nothing left to
   label, so it goes. */
.bt-matrix-scroll.is-stacked thead { display: none; }
.bt-matrix-scroll.is-stacked .bt-matrix,
.bt-matrix-scroll.is-stacked tbody,
.bt-matrix-scroll.is-stacked .bt-matrix__group:not([hidden]),
.bt-matrix-scroll.is-stacked .bt-matrix__group td { display: block; }
.bt-matrix-scroll.is-stacked tr[data-group-of]:not([hidden]) {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 4px;
  padding: 12px;
  border-bottom: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-sm);
}
.bt-matrix-scroll.is-stacked tr[data-group-of] td {
  display: flex;
  min-width: 76px;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 0;
  border: 0;
}
.bt-matrix-scroll.is-stacked tr[data-group-of] td::before {
  color: var(--bt-text-secondary);
  content: attr(data-col);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.bt-matrix-scroll.is-stacked tr[data-group-of] td.bt-matrix__row-head {
  display: block;
  position: static;
  min-width: 0;
  flex-basis: 100%;
  align-items: flex-start;
  text-align: left;
}
.bt-matrix-scroll.is-stacked tbody tr:hover td { background: transparent; }
.bt-matrix-scroll.is-stacked tr[data-group-of]:hover { background: var(--bt-surface-nested); }

/* The permission name column stays put while the roles scroll past it. */
.bt-matrix th.bt-matrix__row-head,
.bt-matrix td.bt-matrix__row-head {
  position: sticky;
  z-index: 1;
  left: 0;
  min-width: 240px;
  background: transparent;
  font-weight: 500;
  text-align: left;
}
.bt-matrix thead th.bt-matrix__row-head { z-index: 2; }

.bt-matrix__group td {
  padding-top: 16px;
  background: transparent;
  color: var(--bt-accent-text);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-align: left;
  text-transform: uppercase;
}

.bt-matrix__locked { color: var(--bt-text-disabled); }

.bt-matrix tbody tr:hover td { background: var(--bt-surface-nested); }
.bt-matrix tbody tr:hover td.bt-matrix__row-head { background: var(--bt-surface-nested); }

/* A checkbox-backed toggle: the input stays focusable for keyboard users. */
.bt-toggle {
  position: relative;
  display: inline-flex;
  width: 38px;
  height: 22px;
  flex: 0 0 auto;
  align-items: center;
  cursor: pointer;
}
.bt-toggle input {
  position: absolute;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
  opacity: 0;
}
.bt-toggle__track {
  width: 100%;
  height: 100%;
  border-radius: var(--bt-radius-round);
  background: var(--bt-surface-interactive);
  transition: background var(--bt-duration-sm) var(--bt-ease);
}
.bt-toggle__track::after {
  display: block;
  width: 16px;
  height: 16px;
  margin: 3px;
  border-radius: 50%;
  background: var(--bt-text-primary);
  content: "";
  transition: transform var(--bt-duration-sm) var(--bt-ease);
}
.bt-toggle input:checked + .bt-toggle__track { background: var(--bt-accent); }
.bt-toggle input:checked + .bt-toggle__track::after { background: var(--bt-ink); transform: translateX(16px); }
.bt-toggle input:focus-visible + .bt-toggle__track { outline: 2px solid var(--bt-accent); outline-offset: 2px; }
.bt-toggle input:disabled { cursor: default; }
.bt-toggle input:disabled + .bt-toggle__track { opacity: 0.45; }

/* ---------------------------------------------------------------- benchmark editor */
.bt-bench-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 82px 132px;
  align-items: center;
  gap: 12px;
  padding: 12px 8px;
}
.bt-bench-row + .bt-bench-row { box-shadow: 0 -1px 0 var(--bt-line); }
.bt-bench-row__hours {
  justify-self: end;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.bt-bench-row__minutes {
  width: 132px;
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------- rating scale editor */
/* A row reads as one sentence — "finished in 60% of the target, earns 5.0" — so
   the two numbers sit under headings instead of carrying labels of their own.
   The last column is held open whether or not a remove button is offered in it,
   or the fields shuffle sideways as rows come and go. */
.bt-scale__head,
.bt-scale__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) var(--bt-control-sm);
  align-items: center;
  gap: 12px;
}
.bt-scale__head {
  padding: 0 2px 8px;
  border-bottom: 1px solid var(--bt-line);
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.bt-scale__row { padding: 8px 2px; }
/* Rates by date: From and the rate stacked, each field naming itself, with
   the remove button beside the first. Each rate runs until the next starts,
   so there is no end date to choose. */
.bt-scale--rates .bt-scale__row {
  grid-template-columns: minmax(0, 1fr) var(--bt-control-sm);
  row-gap: 8px;
}
.bt-scale--rates .bt-scale__row > .bt-input-shell { grid-column: 1; }
.bt-scale--rates .bt-scale__row > button { grid-column: 2; grid-row: 1; }
.bt-scale--rates .bt-scale__row .bt-scale__unit:first-child,
.bt-scale--splits .bt-scale__row .bt-scale__unit:first-child { min-width: 36px; }
/* With the room, a rate is one line: From, rate, remove. */
.bt-scale--rates { container-type: inline-size; }
@container (min-width: 480px) {
  .bt-scale--rates .bt-scale__row { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) var(--bt-control-sm); }
  .bt-scale--rates .bt-scale__row > .bt-input-shell,
  .bt-scale--rates .bt-scale__row > button { grid-column: auto; grid-row: auto; }
}
/* Splits by date: From beside the remove button, then the three shares in
   columns, each named above its field the way any form field is, so a field
   holds only its number. One column when the panel is narrow. */
.bt-scale--splits { container-type: inline-size; }
.bt-scale--splits .bt-scale__row {
  grid-template-columns: minmax(0, 1fr) var(--bt-control-sm);
  row-gap: 12px;
}
.bt-scale__split-parts {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}
@container (max-width: 360px) {
  .bt-scale__split-parts { grid-template-columns: minmax(0, 1fr); }
}
/* The settings columns drop a scale's units for its column headings; rates
   and splits have no headings, so each field keeps its word. */
.bt-settings-columns .bt-scale--rates .bt-scale__row .bt-scale__unit,
.bt-settings-columns .bt-scale--splits .bt-scale__row .bt-scale__unit { display: inline; }
.bt-scale__row + .bt-scale__row { box-shadow: 0 -1px 0 var(--bt-line); }
.bt-scale__row .bt-input { font-variant-numeric: tabular-nums; }
/* The unit belongs to the number it follows, not to a hint under the field. */
.bt-scale__unit { flex: 0 0 auto; color: var(--bt-text-disabled); font-size: 13px; }

/* A checkbox that belongs to the button beside it rather than to the panel: it
   qualifies one save and appears only while that save is on offer, so it is
   sized to sit in a panel head next to an xs button instead of taking a row. */
.bt-check-inline {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: var(--bt-control-xs);
  padding-inline: 4px;
  color: var(--bt-text-secondary);
  font-size: 12px;
  font-weight: 500;
  line-height: 18px;
  white-space: nowrap;
  cursor: pointer;
}
.bt-check-inline:hover { color: var(--bt-text-primary); }
.bt-check-inline input[type="checkbox"] {
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
  margin: 0;
  accent-color: var(--bt-accent);
}
.bt-check-inline:has(input:checked) { color: var(--bt-accent-text); }

/* What the scale actually pays out, read off the curve the server returned
   rather than worked out a second time here. On-target is the number people
   come to this panel to argue about, so it is the one that is marked. */
.bt-scale__samples { display: flex; flex-wrap: wrap; gap: 6px; }
.bt-scale__sample {
  display: flex;
  gap: 6px;
  padding: 4px 9px;
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
  color: var(--bt-text-secondary);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.bt-scale__sample b { color: var(--bt-text-primary); font-weight: 600; }
.bt-scale__sample--target { background: var(--bt-accent-soft); color: var(--bt-accent-text); }
.bt-scale__sample--target b { color: var(--bt-accent-text); }

/* What each part of the score is worth: pace, timeliness and the review, as
   three shares side by side under the curves they weigh. It sits inside the
   same panel because it is the other half of the same question. */
.bt-scale__weights {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 12px;
  border-top: 1px solid var(--bt-line);
}
.bt-scale__weights .bt-input { font-variant-numeric: tabular-nums; }
/* A part's label and its info dot read as one line, the same arrangement a
   panel head uses for its heading. Ruled when it starts a new part. */
.bt-scale__split-copy {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.bt-scale__split-copy--ruled { padding-top: 12px; border-top: 1px solid var(--bt-line); }
.bt-scale__split-label { font-size: 13px; font-weight: 500; }

/* ---------------------------------------------------------------- review */
/* Picking a mark out of five. Five buttons rather than a slider or a number
   field: a review is a judgement somebody makes in one gesture, and the stars
   are what the score is shown as everywhere else, so choosing one should look
   like the thing it produces.

   Whole stars only, which is why nothing here fills a fraction of one — that is
   bt-stars' job, for scores the arithmetic produced. */
.bt-starpick {
  display: flex;
  gap: 4px;
}
.bt-starpick__star {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: var(--bt-radius-cell);
  background: transparent;
  color: var(--bt-track);
  cursor: pointer;
  transition: color 120ms var(--bt-ease), background 120ms var(--bt-ease);
}
.bt-starpick__star .bt-icon { width: 28px; height: 28px; }
.bt-starpick__star:hover { background: var(--bt-surface-nested); }
/* is-on is set by the script for every star up to the one chosen, and again on
   hover for the one under the pointer, so the row previews the mark before it
   is committed. */
.bt-starpick__star.is-on { color: var(--bt-accent); }
.bt-starpick.is-low .bt-starpick__star.is-on { color: var(--bt-warning); }
.bt-starpick.is-poor .bt-starpick__star.is-on { color: var(--bt-danger); }

/* The review as it reads back on the job: the mark and who left it on one line,
   the reason under it. Nested surface rather than a panel of its own — it
   belongs to the score above it. */
.bt-review {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
}
.bt-review__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
}
.bt-review__who {
  min-width: 0;
  color: var(--bt-text-secondary);
  font-size: 12px;
  overflow-wrap: anywhere;
}
.bt-review__comment {
  font-size: 13px;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
/* A job's score (jobscore.js): the number, a bar split into what each part
   adds to it, and a card per part. Each part keeps one hue everywhere it
   appears, borrowed from the existing roles rather than new colors. A part
   that does not count (excused, or missing) gets a dashed edge. */
.bt-jobscore {
  --bt-part-pace: var(--bt-accent);
  --bt-part-punctuality: var(--bt-info);
  --bt-part-review: var(--bt-warning-text);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.bt-jobscore__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 8px;
}
.bt-jobscore__total {
  font-family: var(--bt-font-display);
  font-size: 32px;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.bt-jobscore__max {
  margin-left: 2px;
  color: var(--bt-text-secondary);
  font-family: var(--bt-font-interface);
  font-size: 0.45em;
  font-weight: 500;
}
.bt-jobscore__bar {
  display: flex;
  height: 10px;
  overflow: hidden;
  border-radius: var(--bt-radius-round);
  background: var(--bt-track);
}
.bt-jobscore__seg { height: 100%; transition: width var(--bt-duration-md) var(--bt-ease); }
.bt-jobscore__seg + .bt-jobscore__seg { box-shadow: inset 2px 0 0 var(--bt-canvas); }
.bt-jobscore__seg:focus-visible { outline: 2px solid var(--bt-text-primary); outline-offset: -2px; }
.bt-jobscore__seg--pace, .bt-jobscore__dot--pace { background: var(--bt-part-pace); }
.bt-jobscore__seg--punctuality, .bt-jobscore__dot--punctuality { background: var(--bt-part-punctuality); }
.bt-jobscore__seg--review, .bt-jobscore__dot--review { background: var(--bt-part-review); }
.bt-jobscore__ticks {
  display: flex;
  justify-content: space-between;
  margin-top: -4px;
  color: var(--bt-text-disabled);
  font-size: 10px;
  font-variant-numeric: tabular-nums;
}
.bt-jobscore__parts { display: none; flex-direction: column; gap: 8px; }
.bt-jobscore.is-open .bt-jobscore__parts { display: flex; }
/* Folded, the parts are a row of chips (color, name, score); the row is the
   button that opens the cards. */
.bt-jobscore__summary {
  display: flex;
  min-height: var(--bt-control-md);
  align-items: center;
  gap: 8px;
  margin: 0 -8px;
  padding: 4px 8px;
  border: 0;
  border-radius: var(--bt-radius-sm);
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.bt-jobscore__summary:hover { background: var(--bt-hover-wash); }
.bt-jobscore__summary:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 0; }
/* One line, however narrow the pane: a name that runs out of room is cut
   short; its score never is. */
.bt-jobscore__chips { display: flex; min-width: 0; flex: 1; gap: 8px; }
.bt-jobscore__chip {
  display: inline-flex;
  min-width: 0;
  align-items: center;
  gap: 4px;
  color: var(--bt-text-secondary);
  font-size: 11px;
  white-space: nowrap;
}
.bt-jobscore__chip-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* The score sits inside its part's color, in the ink that reads on a solid
   fill in either theme. */
.bt-jobscore__chip-n {
  flex: 0 0 auto;
  min-width: 26px;
  padding: 2px 4px;
  border-radius: 5px;
  color: var(--bt-ink);
  font-size: 11px;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.bt-jobscore__chip.is-out .bt-jobscore__chip-n { opacity: 0.45; text-decoration: line-through; }
.bt-jobscore__hide { display: none; margin-left: auto; color: var(--bt-text-secondary); font-size: 12px; }
.bt-jobscore.is-open .bt-jobscore__chips { display: none; }
.bt-jobscore.is-open .bt-jobscore__hide { display: inline; }
.bt-jobscore__chevron { flex: 0 0 auto; color: var(--bt-text-secondary); transition: transform var(--bt-duration-sm) var(--bt-ease); }
.bt-jobscore.is-open .bt-jobscore__chevron { transform: rotate(180deg); }
.bt-jobscore__part {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  border: 1px solid var(--bt-hairline);
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
}
.bt-jobscore__part.is-out { border-style: dashed; background: transparent; }
.bt-jobscore__part.is-out .bt-jobscore__n { color: var(--bt-text-disabled); text-decoration: line-through; }
/* The color sits beside the title line, not centered on the score and its
   share stacked opposite. */
.bt-jobscore__part-head { display: flex; align-items: flex-start; gap: 8px; }
.bt-jobscore__dot { flex: 0 0 auto; width: 10px; height: 10px; margin-top: 5px; border-radius: 3px; }
.bt-jobscore__label { min-width: 0; font-size: 13px; font-weight: 600; line-height: 20px; }
.bt-jobscore__fig {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  margin-left: auto;
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.bt-jobscore__n { font-size: 15px; font-weight: 700; }
.bt-jobscore__worth { color: var(--bt-text-secondary); font-size: 11px; }
.bt-jobscore__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex-wrap: wrap;
}
.bt-jobscore__tag {
  padding: 2px 6px;
  border-radius: 4px;
  background: var(--bt-surface-nested);
  color: var(--bt-text-secondary);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
@media (prefers-reduced-motion: reduce) { .bt-jobscore__seg, .bt-jobscore__chevron { transition: none; } }

/* ---------------------------------------------------------------- conversation */
.bt-jobgrid__conv.is-file-drag .bt-conv { outline: 2px dashed var(--bt-accent); outline-offset: -4px; }
.bt-conv__drop-hint {
  display: none;
  color: var(--bt-accent-text);
  font-size: 13px;
  white-space: nowrap;
}
.bt-jobgrid__conv.is-file-drag .bt-conv__title {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.bt-jobgrid__conv.is-file-drag .bt-conv__drop-hint { display: inline; }
.bt-conv {
  display: flex;
  height: calc(100vh - var(--bt-job-pane-top, calc(var(--bt-topbar-height) + 20px)) - var(--bt-job-pane-bottom, 24px));
  height: calc(100dvh - var(--bt-job-pane-top, calc(var(--bt-topbar-height) + 20px)) - var(--bt-job-pane-bottom, 24px));
  flex-direction: column;
  overflow: hidden;
}
.bt-conv__body {
  display: flex;
  flex: 1 1 auto;
  min-height: 120px;
  flex-direction: column;
  gap: 14px;
  padding: 12px 14px;
  overflow-y: auto;
}

/* Messages arrived while the reader was scrolled up. It floats over the foot of
   the thread, just above the composer, rather than taking a row of its own. */
.bt-conv__jump {
  position: relative;
  z-index: 2;
  align-self: center;
  margin-top: calc(-1 * var(--bt-control-sm) - 12px);
  margin-bottom: 12px;
  border-radius: 999px;
  box-shadow: var(--bt-shadow-popover);
}
.bt-conv__jump[hidden] { display: none; }
.bt-comment { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 10px; }
.bt-comment__avatar { width: 30px; height: 30px; flex: 0 0 auto; }
.bt-comment__main { min-width: 0; }
.bt-comment__head { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
.bt-comment__author { font-size: 13px; font-weight: 600; }
.bt-comment__time { color: var(--bt-text-disabled); font-size: 11px; }
/* A job message's byline: the name and time take the width and wrap within it,
   and the actions beside them are one group that cannot wrap — so the trash can
   never drop to a line of its own under the smiley. */
.bt-comment__who { display: flex; flex: 1 1 0; min-width: 0; flex-wrap: wrap; align-items: baseline; gap: 0 6px; }
.bt-comment__acts { display: flex; flex: none; align-items: center; gap: 2px; align-self: flex-start; margin-left: auto; }
.bt-comment__acts:empty { display: none; }
/* Edit and delete sit at the end of the byline and stay out of the way until
   the message is hovered — a conversation shouldn't read as a row of buttons. */
.bt-comment__act {
  padding: 2px;
  border: 0;
  background: transparent;
  color: var(--bt-text-disabled);
  opacity: 0;
  cursor: pointer;
  transition: opacity var(--bt-duration-sm) var(--bt-ease), color var(--bt-duration-sm) var(--bt-ease);
}
.bt-comment:hover .bt-comment__act,
.bt-comment__act:focus-visible { opacity: 1; }
.bt-comment__act:hover { color: var(--bt-text-primary); }
.bt-comment__act--danger:hover { color: var(--bt-danger-text); }
@media (hover: none) {
  .bt-comment__act { opacity: 1; }
}
/* With a mouse the actions float over the byline's right edge while the message
   is hovered, or one of them has focus, rather than holding a place in it: a
   long name and stamp keep their one line, and nothing can wrap. Touch has no
   hover, so there they stay in the line, as above. */
@media (hover: hover) {
  .bt-comment__main { position: relative; }
  .bt-comment__acts {
    position: absolute;
    top: 0;
    right: 0;
    padding: 0 3px;
    border-radius: var(--bt-radius-sm);
    background: var(--bt-surface-popover);
    box-shadow: 0 0 0 1px var(--bt-hairline);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--bt-duration-sm) var(--bt-ease);
  }
  .bt-comment:hover .bt-comment__acts,
  .bt-comment__acts:focus-within { opacity: 1; pointer-events: auto; }
  .bt-comment__act { opacity: 1; }
}
.bt-comment__editor { margin-top: 6px; }
.bt-comment__editor textarea.bt-input { min-height: 54px; resize: vertical; }
.bt-comment__reply, .bt-reply-compose {
  border-left: 3px solid var(--bt-accent);
  background: var(--bt-surface-interactive);
  border-radius: 8px;
  padding: 8px 12px;
  margin-bottom: 8px;
  font-size: 12px;
}
button.bt-comment__reply {
  display: block; width: 100%; text-align: left; color: inherit; font: inherit; font-size: 12px;
  border-top: 0; border-right: 0; border-bottom: 0; cursor: pointer;
}
button.bt-comment__reply:hover { border-left-color: var(--bt-text-primary); }
.bt-comment.is-quoted { animation: bt-quoted 1.6s var(--bt-ease) both; border-radius: 8px; }
@keyframes bt-quoted { 0%, 40% { background: var(--bt-accent-soft); } 100% { background: transparent; } }
@media (prefers-reduced-motion: reduce) { .bt-comment.is-quoted { animation: none; outline: 2px solid var(--bt-accent); } }
.bt-comment__reply strong, .bt-comment__reply span, .bt-reply__text strong, .bt-reply__text span { display: block; }
.bt-comment__reply span, .bt-reply__text span { color: var(--bt-text-secondary); overflow-wrap: anywhere; }
.bt-reply-compose { display: flex; align-items: center; gap: 8px; }
.bt-reply-compose[hidden] { display: none; }
.bt-reply__text { flex: 1; min-width: 0; }
.bt-reply__cancel { border: 0; background: transparent; color: var(--bt-text-secondary); min-width: 44px; min-height: 44px; font-size: 20px; cursor: pointer; }
.bt-comment__body {
  margin-top: 2px;
  font-size: 13.5px;
  line-height: 19px;
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}
.bt-comment__to {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-bottom: 4px;
  padding: 1px 8px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-accent-soft);
  color: var(--bt-accent-text);
  font-size: 11px;
  font-weight: 600;
}
/* A name inside a message. Tinted rather than boxed: it sits mid-sentence, and a
   chip with its own padding and border would break the line it is part of. */
.bt-mention {
  padding: 0 2px;
  border-radius: 4px;
  background: var(--bt-accent-soft);
  color: var(--bt-accent-text);
  font-weight: 600;
}

/* ---- reactions (reactions.js) ----
   Chips under a message, one per emoji, yours tinted. */
.bt-reacts { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.bt-react {
  display: inline-flex;
  min-height: 28px;
  align-items: center;
  gap: 5px;
  padding: 0 9px;
  border: 1px solid var(--bt-hairline);
  border-radius: var(--bt-radius-round);
  background: var(--bt-surface);
  color: var(--bt-text-secondary);
  font: inherit;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
button.bt-react { cursor: pointer; transition: background var(--bt-duration-sm) var(--bt-ease), border-color var(--bt-duration-sm) var(--bt-ease); }
button.bt-react:hover { background: var(--bt-surface-interactive); }
.bt-react__emoji { font-size: 14px; }
.bt-react.is-mine {
  border-color: var(--bt-accent);
  background: var(--bt-accent-soft);
  color: var(--bt-accent-text);
}

/* The picker: a pill opened by right-clicking a message (or its smiley), holding
   the six Messages offers and a chevron to the rest. Fixed to the viewport and
   appended to the body, because the conversation clips whatever overflows it. */
.bt-reactbar {
  position: fixed;
  z-index: 80;
  max-width: calc(100vw - 16px);
  overflow: hidden;
  border: 1px solid var(--bt-hairline);
  border-radius: 28px;
  background: var(--bt-surface-popover);
  box-shadow: var(--bt-shadow-popover);
  animation: bt-reactbar-in var(--bt-duration-sm) var(--bt-ease);
}
.bt-reactbar.is-expanded { border-radius: var(--bt-radius-sheet); }
.bt-reactbar__row { display: flex; align-items: center; gap: 2px; padding: 6px; }
.bt-reactbar__btn,
.bt-reactbar__more {
  display: grid;
  width: 40px;
  height: 40px;
  flex: 0 0 auto;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--bt-text-secondary);
  cursor: pointer;
  font: inherit;
  font-size: 22px;
  line-height: 1;
  transition: background var(--bt-duration-sm) var(--bt-ease), transform var(--bt-duration-sm) var(--bt-ease);
}
.bt-reactbar__btn:hover,
.bt-reactbar__more:hover { background: var(--bt-surface-interactive); }
.bt-reactbar__btn:hover { transform: scale(1.12); }
.bt-reactbar__btn.is-mine { background: var(--bt-accent-soft); box-shadow: inset 0 0 0 1.5px var(--bt-accent); }
.bt-reactbar__more:hover { color: var(--bt-text-primary); }
.bt-reactbar.is-expanded .bt-reactbar__more .bt-icon { transform: rotate(180deg); }
.bt-reactbar__more .bt-icon { transition: transform var(--bt-duration-sm) var(--bt-ease); }
.bt-reactbar__grid {
  display: grid;
  grid-template-columns: repeat(8, 36px);
  gap: 2px;
  max-height: 188px;
  overflow-y: auto;
  padding: 6px 8px 8px;
  border-top: 1px solid var(--bt-hairline);
  overscroll-behavior: contain;
  scrollbar-width: thin;
}
.bt-reactbar__grid[hidden] { display: none; }
.bt-reactbar__grid .bt-reactbar__btn { width: 36px; height: 36px; font-size: 20px; }
@keyframes bt-reactbar-in {
  from { opacity: 0; transform: translateY(4px) scale(0.97); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .bt-reactbar { animation: none; }
  .bt-reactbar__btn:hover { transform: none; }
}

/* ---- the mention picker ----
   Anchored in the composer's own flow rather than positioned over the page: it
   only ever opens directly above the box, and a panel that shares the composer's
   width cannot be knocked out of place by the conversation scrolling underneath
   it. */
.bt-mentionpop {
  max-height: 216px;
  overflow-y: auto;
  margin-bottom: 6px;
  padding: 4px;
  border: 1px solid var(--bt-hairline);
  border-radius: var(--bt-radius-panel);
  background: var(--bt-surface-popover);
  box-shadow: var(--bt-shadow-popover);
  overscroll-behavior: contain;
  scrollbar-width: thin;
}
.bt-mentionpop__row {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 8px;
  padding: 6px;
  border: 0;
  border-radius: var(--bt-radius-sm);
  background: transparent;
  color: var(--bt-text-secondary);
  cursor: pointer;
  font-family: inherit;
  text-align: left;
}
/* Hover and the keyboard's highlight are one state: two different-looking
   "selected" rows in one list is two answers to which one Enter takes. */
.bt-mentionpop__row:hover,
.bt-mentionpop__row.is-active {
  background: var(--bt-accent-soft);
  color: var(--bt-accent-text);
}
.bt-mentionpop__name { min-width: 0; flex: 1 1 auto; font-size: 13px; font-weight: 600; }
.bt-mentionpop__meta {
  display: block;
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 400;
}
.bt-mentionpop__row:hover .bt-mentionpop__meta,
.bt-mentionpop__row.is-active .bt-mentionpop__meta { color: inherit; opacity: 0.75; }

/* Attachments: image thumbnails render inline, other files as a chip. */
.bt-attachments { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.bt-attach-imgbtn {
  display: block;
  padding: 0;
  border: 0;
  border-radius: var(--bt-radius-cell);
  background: transparent;
  line-height: 0;
  transition: opacity var(--bt-duration-sm) var(--bt-ease);
}
.bt-attach-imgbtn:hover { opacity: 0.85; }
.bt-attach-img {
  display: block;
  max-width: 160px;
  max-height: 160px;
  border-radius: var(--bt-radius-cell);
  border: 1px solid var(--bt-line);
  object-fit: cover;
}

/* An attachment you may remove carries its own × in the corner. It only shows
   on hover so a thread you're only reading stays clean, and stays put on touch
   where there is no hover to reveal it. */
.bt-attach { position: relative; display: inline-flex; }
.bt-attach__x {
  position: absolute;
  top: 4px;
  right: 4px;
  display: grid;
  width: 22px;
  height: 22px;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--bt-duration-sm) var(--bt-ease), background var(--bt-duration-sm) var(--bt-ease);
}
.bt-attach:hover .bt-attach__x,
.bt-attach__x:focus-visible { opacity: 1; }
.bt-attach__x:hover { background: var(--bt-danger); }
@media (hover: none) {
  .bt-attach__x { opacity: 1; }
}
/* A file chip is one short row, so the × sits at the end of it rather than on
   top of the name and size. */
.bt-attach--file .bt-attach-file { padding-right: 32px; }
.bt-attach--file .bt-attach__x {
  top: 50%;
  background: transparent;
  color: var(--bt-text-secondary);
  transform: translateY(-50%);
}
.bt-attach--file .bt-attach__x:hover { background: var(--bt-danger-soft); color: var(--bt-danger-text); }

/* Full-size viewer for an attached image, over the page rather than a new tab
   so the conversation is still there when it closes. */
.bt-lightbox {
  position: fixed;
  z-index: 90;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 16px;
  background: rgba(0, 6, 8, 0.88);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--bt-duration-sm) var(--bt-ease);
}
.bt-lightbox.is-open { opacity: 1; pointer-events: auto; }
.bt-lightbox__bar {
  display: flex;
  width: 100%;
  max-width: 900px;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
}
.bt-lightbox__name {
  min-width: 0;
  flex: 1 1 auto;
  color: var(--bt-text-secondary);
  font-size: 13px;
}
.bt-lightbox__count {
  flex: 0 0 auto;
  color: var(--bt-text-disabled);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
/* The stage carries the arrows either side of whatever is being shown, so a
   message's attachments can be walked through without closing the viewer. */
.bt-lightbox__stage {
  position: relative;
  display: flex;
  width: 100%;
  min-height: 0;
  max-width: 900px;
  flex: 1 1 auto;
  align-items: center;
  justify-content: center;
}
.bt-lightbox__img {
  max-width: 100%;
  /* Leaves room for the bar above it whatever the window height. */
  max-height: calc(100vh - 96px);
  border-radius: var(--bt-radius-cell);
  object-fit: contain;
}
/* Not every attachment is a picture. A file gets a card in the same slot so
   the sequence doesn't break where a PDF happens to sit. */
.bt-lightbox__file {
  display: flex;
  max-width: min(360px, 100%);
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 28px 32px;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-panel);
  background: var(--bt-surface);
  text-align: center;
}
.bt-lightbox__file-name {
  max-width: 100%;
  font-size: 14px;
  font-weight: 600;
  overflow-wrap: anywhere;
}
.bt-lightbox__file-size { color: var(--bt-text-disabled); font-size: 12px; }
.bt-lightbox__nav {
  position: absolute;
  top: 50%;
  display: grid;
  width: 40px;
  height: 40px;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.45);
  color: var(--bt-text-primary);
  cursor: pointer;
  transform: translateY(-50%);
  transition: background var(--bt-duration-sm) var(--bt-ease);
}
.bt-lightbox__nav:hover { background: rgba(0, 0, 0, 0.7); }
.bt-lightbox__nav[hidden] { display: none; }
.bt-lightbox__nav--prev { left: -4px; }
.bt-lightbox__nav--next { right: -4px; }
@media (min-width: 1000px) {
  .bt-lightbox__nav--prev { left: -52px; }
  .bt-lightbox__nav--next { right: -52px; }
}
.bt-attach-file {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  max-width: 220px;
  padding: 8px 12px;
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
  color: var(--bt-text-primary);
  font-size: 12px;
  font-weight: 500;
  text-decoration: none;
}
.bt-attach-file:hover { background: var(--bt-surface-interactive); }
.bt-attach-file__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bt-attach-file__size { flex: 0 0 auto; color: var(--bt-text-disabled); }

/* A voice message: one row of play, progress, length and the transcript's
   fold, with anything to read underneath. Sized like a short chat bubble so
   it sits among the other attachments without stretching across the pane. */
.bt-attach--voice { flex: 0 1 340px; min-width: 0; max-width: 100%; }
.bt-voice {
  width: 100%;
  min-width: 0;
  border-radius: var(--bt-radius-panel);
  background: var(--bt-surface-nested);
  color: var(--bt-text-primary);
  font-size: 12px;
}
.bt-voice__row { display: flex; align-items: center; gap: 10px; min-height: 48px; padding: 8px 8px 8px 8px; }
.bt-voice__play {
  position: relative;
  display: grid;
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: var(--bt-accent);
  color: var(--bt-ink);
  cursor: pointer;
  transition: filter var(--bt-duration-sm) var(--bt-ease);
}
/* A 44px target around the 32px disc. */
.bt-voice__play::after { content: ""; position: absolute; inset: -6px; }
.bt-voice__play:hover { filter: brightness(1.08); }
.bt-voice__play--off { background: var(--bt-surface-interactive); color: var(--bt-text-secondary); cursor: default; }
.bt-voice__play--off::after { content: none; }
.bt-voice.is-loading .bt-voice__play { opacity: 0.6; cursor: progress; }
.bt-voice__track {
  position: relative;
  flex: 1 1 auto;
  min-width: 48px;
  height: 4px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-line);
  cursor: pointer;
  touch-action: none;
}
/* The bar is 4px to look at and 28px to grab. */
.bt-voice__track::before { content: ""; position: absolute; inset: -12px 0; }
.bt-voice__track:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 8px; }
.bt-voice__fill {
  position: absolute;
  inset: 0 auto 0 0;
  border-radius: inherit;
  background: var(--bt-accent);
}
/* Where it has got to: a dot riding the end of the fill. */
.bt-voice__fill::after {
  content: "";
  position: absolute;
  top: 50%;
  right: -5px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--bt-accent);
  transform: translateY(-50%);
  transition: width var(--bt-duration-sm) var(--bt-ease), height var(--bt-duration-sm) var(--bt-ease), right var(--bt-duration-sm) var(--bt-ease);
}
.bt-voice__track:hover .bt-voice__fill::after,
.bt-voice.is-seeking .bt-voice__fill::after { right: -7px; width: 14px; height: 14px; }
@media (prefers-reduced-motion: reduce) { .bt-voice__fill::after { transition: none; } }
.bt-voice__time { flex: 0 0 auto; min-width: 30px; color: var(--bt-text-secondary); font-variant-numeric: tabular-nums; }
.bt-voice__gone { flex: 1 1 auto; min-width: 0; color: var(--bt-text-secondary); }
.bt-voice__toggle {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 2px;
  min-height: 28px;
  padding: 0 4px 0 8px;
  border: 0;
  border-radius: var(--bt-radius-round);
  background: transparent;
  color: var(--bt-text-secondary);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--bt-duration-sm) var(--bt-ease), color var(--bt-duration-sm) var(--bt-ease);
}
.bt-voice__toggle:hover,
.bt-voice__toggle[aria-expanded="true"] { background: var(--bt-surface-interactive); color: var(--bt-text-primary); }
.bt-voice__body { display: flex; flex-direction: column; gap: 6px; padding: 10px 12px 12px; border-top: 1px solid var(--bt-line); }
.bt-voice__note { color: var(--bt-text-secondary); }
.bt-voice__transcript { font-size: 13px; line-height: 19px; white-space: pre-wrap; overflow-wrap: anywhere; }
.bt-voice__from { color: var(--bt-text-disabled); }
.bt-voice__link {
  padding: 0;
  border: 0;
  background: none;
  color: var(--bt-accent-text);
  font: inherit;
  cursor: pointer;
}
.bt-voice__link:hover { text-decoration: underline; }

/* Composer pinned under the thread. */
.bt-composer {
  flex: 0 0 auto;
  padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--bt-line);
  background: var(--bt-surface);
}
.bt-composer__staging { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 8px; }
.bt-stage {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 180px;
  padding: 6px 26px 6px 10px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-surface-nested);
  font-size: 12px;
}
.bt-stage img { width: 22px; height: 22px; border-radius: 5px; object-fit: cover; }
.bt-stage__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bt-stage__x {
  position: absolute;
  right: 4px;
  display: grid;
  width: 18px;
  height: 18px;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--bt-text-secondary);
  cursor: pointer;
}
.bt-stage__x:hover { background: var(--bt-surface-interactive); color: var(--bt-text-primary); }

.bt-composer__to {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 8px;
  font-size: 12px;
  color: var(--bt-text-secondary);
}
/* Text first, tools underneath. Sharing one row with four buttons cost the
   textarea ~150px of its width and wrapped ordinary sentences after a handful
   of words; stacking gives every line the full pane. */
.bt-composer__field {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 2px;
  padding: 8px 8px 6px;
  border: 1px solid var(--bt-field-line);
  border-radius: var(--bt-radius-panel);
  background: var(--bt-surface-field);
}
.bt-composer__field:focus-within { border-color: var(--bt-accent); }
.bt-composer__input {
  width: 100%;
  min-width: 0;
  min-height: 20px;
  max-height: 160px;
  overflow-x: hidden;
  overflow-y: hidden;
  padding: 2px 4px;
  border: 0;
  outline: 0;
  background: transparent;
  color: var(--bt-text-primary);
  font: inherit;
  font-size: 14px;
  line-height: 20px;
  resize: none;
}
.bt-composer__tools { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }
.bt-composer__tool {
  display: grid;
  width: 34px;
  height: 34px;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--bt-text-secondary);
  cursor: pointer;
  transition: background var(--bt-duration-sm) var(--bt-ease), color var(--bt-duration-sm) var(--bt-ease);
}
.bt-composer__tool:hover { background: var(--bt-surface-interactive); color: var(--bt-text-primary); }
.bt-composer__tool.is-active { color: var(--bt-accent-text); }
/* Send sits at the end of the tool row and reads as one of them until there is
   something to send — an empty composer shouldn't advertise a filled button
   that does nothing. The moment there's text or an attachment it takes the
   accent and becomes the obvious next move. */
.bt-composer__send {
  display: grid;
  width: 34px;
  height: 34px;
  margin-left: auto;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--bt-text-disabled);
  cursor: default;
  transition: background var(--bt-duration-sm) var(--bt-ease), color var(--bt-duration-sm) var(--bt-ease);
}
.bt-composer__send:not(:disabled) {
  background: var(--bt-accent);
  color: var(--bt-ink);
  cursor: pointer;
}
.bt-composer__send:not(:disabled):hover { filter: brightness(1.08); }

/* Recording a voice message: the box becomes discard, a pulsing dot with the
   time, and send. What was typed waits underneath for afterwards. */
.bt-composer__field.is-recording > .bt-composer__input,
.bt-composer__field.is-recording > .bt-composer__tools { display: none; }
.bt-composer__rec { display: flex; align-items: center; gap: 8px; min-height: 34px; }
.bt-composer__rec[hidden] { display: none; }
.bt-composer__rec-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--bt-danger);
  animation: bt-rec-pulse 1.2s var(--bt-ease) infinite;
}
.bt-composer__rec-time { color: var(--bt-text-primary); font-size: 14px; font-variant-numeric: tabular-nums; }
.bt-composer__rec-hint { overflow: hidden; color: var(--bt-text-secondary); font-size: 12px; text-overflow: ellipsis; white-space: nowrap; }
.bt-composer__rec .bt-composer__send { margin-left: auto; }
.bt-composer__rec .bt-composer__rec-stop { margin-left: auto; color: var(--bt-text-primary); }
.bt-composer__rec .bt-composer__rec-stop + .bt-composer__send { margin-left: 0; }
/* Stopped: discard, the clip to listen back to, and send. */
.bt-composer__rec.is-review > .bt-composer__rec-dot,
.bt-composer__rec.is-review > .bt-composer__rec-time,
.bt-composer__rec.is-review > .bt-composer__rec-hint,
.bt-composer__rec.is-review > .bt-composer__rec-stop { display: none; }
.bt-composer__rec-review { flex: 1 1 auto; min-width: 0; }
.bt-composer__rec-review[hidden] { display: none; }
.bt-composer__rec-review .bt-voice { background: none; }
.bt-composer__rec-review .bt-voice__row { min-height: 34px; padding: 0 4px; }
.bt-composer__rec .bt-composer__rec-done { margin-left: auto; }
/* Dictating keeps the box: the words arrive in it. Only the tools give way. */
.bt-composer__field.is-dictating > .bt-composer__tools { display: none; }
.bt-composer__rec-dot--live { background: var(--bt-accent); }
@keyframes bt-rec-pulse { 50% { opacity: 0.35; } }
@media (prefers-reduced-motion: reduce) { .bt-composer__rec-dot { animation: none; } }

/* Emoji picker popover, anchored above the composer. */
/* Anchored to the composer field it sits inside, so it always opens directly
   above the box however tall the textarea has grown. */
.bt-emoji-pop {
  position: absolute;
  z-index: 20;
  right: 0;
  bottom: calc(100% + 8px);
  display: none;
  grid-template-columns: repeat(8, 1fr);
  gap: 2px;
  width: 296px;
  max-height: 200px;
  overflow-y: auto;
  padding: 8px;
  border-radius: var(--bt-radius-panel);
  background: var(--bt-surface-popover);
  box-shadow: var(--bt-shadow-popover);
}
.bt-emoji-pop.is-open { display: grid; }
.bt-emoji-pop button {
  height: 32px;
  border: 0;
  border-radius: var(--bt-radius-sm);
  background: transparent;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}
.bt-emoji-pop button:hover { background: var(--bt-surface-interactive); }

/* An assign-comment mini menu reuses the sheet options at the bottom. */
.bt-conv__empty {
  display: flex;
  max-width: 240px;
  margin: auto;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 24px 12px;
  text-align: center;
}
.bt-conv__empty-icon {
  display: grid;
  width: 48px;
  height: 48px;
  margin-bottom: 8px;
  place-items: center;
  border: 1px solid rgba(31, 194, 207, 0.22);
  border-radius: 50%;
  background:
    radial-gradient(circle at 32% 24%, rgba(31, 194, 207, 0.2), transparent 58%),
    var(--bt-accent-soft);
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.2), var(--bt-shadow-inset);
  color: var(--bt-accent-text);
}
.bt-conv__empty-icon .bt-icon { width: 22px; height: 22px; }
.bt-conv__empty-title {
  color: var(--bt-text-primary);
  font-size: 13px;
  font-weight: 600;
  line-height: 18px;
}
.bt-conv__empty-copy {
  color: var(--bt-text-secondary);
  font-size: 12px;
  line-height: 17px;
}

/* ---------------------------------------------------------------- notifications */
.bt-bell {
  position: relative;
  display: grid;
  width: var(--bt-control-md);
  height: var(--bt-control-md);
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--bt-text-secondary);
  cursor: pointer;
  transition: background var(--bt-duration-sm) var(--bt-ease), color var(--bt-duration-sm) var(--bt-ease);
}
.bt-bell:hover { background: var(--bt-surface); color: var(--bt-text-primary); }
.bt-bell__badge {
  position: absolute;
  top: 6px;
  right: 6px;
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-danger);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 17px;
  text-align: center;
}

.bt-notif-pop {
  position: absolute;
  z-index: 70;
  top: calc(var(--bt-topbar-height) - 6px);
  right: 12px;
  width: 380px;
  max-width: calc(100vw - 24px);
  border: 1px solid var(--bt-hairline);
  border-radius: var(--bt-radius-panel);
  background: var(--bt-surface-popover);
  box-shadow: var(--bt-shadow-popover);
  color: var(--bt-text-primary);
  font-family: var(--bt-font-interface);
  opacity: 0;
  transform: translateY(-6px);
  pointer-events: none;
  transition: opacity var(--bt-duration-sm) var(--bt-ease), transform var(--bt-duration-sm) var(--bt-ease);
}
.bt-notif-pop.is-open { opacity: 1; transform: none; pointer-events: auto; }
.bt-notif-pop__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--bt-line);
}
.bt-notif-pop__body { max-height: min(70vh, 460px); overflow-y: auto; padding: 6px; }
.bt-notif {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 10px;
  width: 100%;
  min-height: 38px;
  padding: 10px;
  border: 0;
  border-radius: var(--bt-radius-sm);
  outline: 0;
  background: transparent;
  color: inherit;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}
.bt-notif:hover,
.bt-notif:focus-visible { background: var(--bt-surface-interactive); }
.bt-notif__dot {
  width: 8px;
  height: 8px;
  margin-top: 6px;
  flex: 0 0 auto;
  border-radius: 50%;
  background: var(--bt-accent);
}
.bt-notif.is-read .bt-notif__dot { background: transparent; box-shadow: inset 0 0 0 1.5px var(--bt-neutral); }
.bt-notif__title { font-size: 13px; font-weight: 600; line-height: 18px; overflow-wrap: anywhere; }
.bt-notif.is-read .bt-notif__title { font-weight: 500; color: var(--bt-text-secondary); }
.bt-notif__body { color: var(--bt-text-secondary); font-size: 12px; line-height: 17px; overflow-wrap: anywhere; }
.bt-notif__time { margin-top: 2px; color: var(--bt-text-disabled); font-size: 11px; }
.bt-notif-pop__empty { padding: 30px 16px; color: var(--bt-text-secondary); font-size: 13px; text-align: center; }

/* ---------------------------------------------------------------- profile menu */
.bt-menu-pop {
  position: absolute;
  z-index: 70;
  top: calc(var(--bt-topbar-height) - 6px);
  right: 12px;
  width: 260px;
  max-width: calc(100vw - 24px);
  padding: 6px;
  border: 1px solid var(--bt-hairline);
  border-radius: var(--bt-radius-panel);
  background: var(--bt-surface-popover);
  box-shadow: var(--bt-shadow-popover);
  color: var(--bt-text-primary);
  font-family: var(--bt-font-interface);
  opacity: 0;
  transform: translateY(-6px);
  pointer-events: none;
  transition: opacity var(--bt-duration-sm) var(--bt-ease), transform var(--bt-duration-sm) var(--bt-ease);
}
.bt-menu-pop.is-open { opacity: 1; transform: none; pointer-events: auto; }

.bt-menu-pop__head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 10px 12px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--bt-line);
}
.bt-menu-pop__name { font-size: 14px; font-weight: 600; line-height: 18px; }
.bt-menu-pop__role { color: var(--bt-text-secondary); font-size: 12px; line-height: 16px; }

.bt-menu-item {
  display: flex;
  width: 100%;
  min-height: 38px;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 0;
  border-radius: var(--bt-radius-sm);
  outline: 0;
  background: transparent;
  color: var(--bt-text-primary);
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  text-align: left;
  text-decoration: none;
  transition: background var(--bt-duration-sm) var(--bt-ease);
}
.bt-menu-item:hover,
.bt-menu-item:focus-visible { background: var(--bt-surface-interactive); }
.bt-menu-item .bt-icon { color: var(--bt-text-secondary); }
.bt-menu-item--danger { color: var(--bt-danger-text); }
.bt-menu-item--danger .bt-icon { color: currentColor; }
.bt-menu-item__count {
  margin-left: auto;
  color: var(--bt-text-disabled);
  font-size: 12px;
  font-weight: 600;
}
.bt-menu-sep { height: 1px; margin: 4px 0; background: var(--bt-line); }

/* ---------------------------------------------------------------- trash */
.bt-trash-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  padding: 12px 8px;
}
.bt-trash-row + .bt-trash-row { box-shadow: 0 -1px 0 var(--bt-line); }
.bt-trash-row__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.bt-trash-row__meta { color: var(--bt-text-secondary); font-size: 12px; line-height: 18px; }
.bt-trash-row--selectable {
  grid-template-columns: auto minmax(0, 1fr);
  border-radius: var(--bt-radius-cell);
  cursor: pointer;
  transition: background-color var(--bt-duration-sm) var(--bt-ease);
}
.bt-trash-row--selectable:hover,
.bt-trash-row--selectable:focus-visible { background: var(--bt-surface-nested); }
.bt-trash-row--selectable:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: -2px; }
.bt-trash-row--selectable.is-selected { background: var(--bt-accent-soft); }

/* ---------------------------------------------------------------- checklist item notes */
/* The saved note, shown under the item it belongs to. */
.bt-item-note {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-top: 8px;
  padding: 8px 10px;
  border-left: 2px solid var(--bt-accent);
  border-radius: 0 var(--bt-radius-sm) var(--bt-radius-sm) 0;
  background: var(--bt-surface-nested);
  color: var(--bt-text-secondary);
  font-size: 12.5px;
  line-height: 18px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.bt-item-note .bt-icon { margin-top: 1px; flex: 0 0 auto; color: var(--bt-accent-text); }

/* ---------------------------------------------------------------- form jobs */
/* Fields stack down the middle pane, each with room to type. */
.bt-form-field + .bt-form-field { margin-top: 4px; }
.bt-form-field textarea.bt-input { min-height: 76px; resize: vertical; }

/* A recorded answer, shown read-only in Admin. */
.bt-answer {
  margin-top: 4px;
  color: var(--bt-text-primary);
  font-size: 13.5px;
  line-height: 20px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.bt-answer--empty { color: var(--bt-text-disabled); font-style: italic; }

/* An explanation that only matters once, parked next to the heading it
   explains. Focusable so the tooltip is reachable without a pointer. */
.bt-info-dot {
  display: grid;
  width: 22px;
  height: 22px;
  flex: 0 0 auto;
  place-items: center;
  border-radius: 50%;
  color: var(--bt-text-disabled);
  transition: background var(--bt-duration-sm) var(--bt-ease), color var(--bt-duration-sm) var(--bt-ease);
}
.bt-info-dot:hover,
.bt-info-dot:focus-visible { background: var(--bt-surface-interactive); color: var(--bt-text-primary); }

/* Tooltips on this one wrap: it's a sentence, not a label. */
.bt-tooltip--wrap::after { white-space: normal; width: max-content; max-width: 260px; }

/* Templates are grouped by shape in the library. */
.bt-typegroup + .bt-typegroup { margin-top: 18px; }
.bt-typegroup__head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 8px 8px;
  color: var(--bt-text-secondary);
}
.bt-typegroup__count {
  padding: 1px 7px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-surface-interactive);
  font-size: 11px;
  font-weight: 600;
}

/* ---------------------------------------------------------------- job type editor */
/* Sections and items are edited in place, so each row is a little form: the
   number, the inputs, and the move/remove controls. */
.bt-te-section {
  margin-bottom: 14px;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
}
.bt-te-section:last-child { margin-bottom: 0; }
.bt-te-section__head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--bt-line);
}
.bt-te-section__head .bt-input-shell { flex: 1 1 auto; min-width: 0; }
.bt-te-section__body { padding: 10px 12px 12px; }

.bt-te-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 8px 0;
}
.bt-te-item + .bt-te-item { box-shadow: 0 -1px 0 var(--bt-line); }
.bt-te-item__n {
  min-width: 22px;
  padding-top: 10px;
  color: var(--bt-text-secondary);
  font-size: 12px;
  font-weight: 600;
  text-align: right;
}
.bt-te-item__fields {
  display: flex;
  min-width: 0;
  flex: 1 1 auto;
  flex-direction: column;
  gap: 6px;
}
.bt-te-item__row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.bt-te-item__row .bt-select { flex: 0 1 180px; }
.bt-te-item__controls { display: flex; align-items: center; gap: 2px; padding-top: 4px; }
.bt-te-required { display: flex; align-items: center; gap: 8px; }

/* Picking the shape of a new job type: two big, obviously-exclusive choices. */
.bt-kindpick { display: flex; flex-direction: column; gap: 8px; }
.bt-kindpick__opt {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px;
  border: 1.5px solid var(--bt-line);
  border-radius: var(--bt-radius-cell);
  background: transparent;
  color: inherit;
  text-align: left;
  transition: border-color var(--bt-duration-sm) var(--bt-ease), background var(--bt-duration-sm) var(--bt-ease);
}
.bt-kindpick__opt:hover { background: var(--bt-surface-nested); }
.bt-kindpick__opt.is-selected { border-color: var(--bt-accent); background: var(--bt-accent-soft); }
.bt-kindpick__opt .bt-icon { margin-top: 2px; flex: 0 0 auto; color: var(--bt-text-secondary); }
.bt-kindpick__opt.is-selected .bt-icon { color: var(--bt-accent-text); }
.bt-kindpick__copy { display: flex; min-width: 0; flex-direction: column; }

@media (max-width: 760px) {
  .bt-te-item { flex-wrap: wrap; }
  .bt-te-item__controls { padding-top: 0; margin-left: auto; }
}

/* ---------------------------------------------------------------- settings: personalization + notification prefs */
.bt-background-picker {
  min-width: 0;
  padding: 0;
  border: 0;
  margin: 0;
}

.bt-background-picker > .bt-label { margin-bottom: 8px; }

.bt-background-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}

.bt-background-option {
  position: relative;
  display: block;
  min-width: 0;
}

.bt-background-option > input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.bt-background-option__card {
  display: flex;
  overflow: hidden;
  min-width: 0;
  flex-direction: column;
  gap: 8px;
  padding: 7px 7px 9px;
  border: 1.5px solid var(--bt-line);
  border-radius: var(--bt-radius-panel);
  background: var(--bt-surface-nested);
  transition:
    border-color var(--bt-duration-sm) var(--bt-ease),
    background var(--bt-duration-sm) var(--bt-ease),
    transform var(--bt-duration-sm) var(--bt-ease);
}

.bt-background-option:hover .bt-background-option__card {
  border-color: var(--bt-neutral);
  background: var(--bt-surface);
  transform: translateY(-1px);
}

.bt-background-option > input:checked + .bt-background-option__card {
  border-color: var(--bt-accent);
  background: var(--bt-accent-soft);
  box-shadow: 0 0 0 1px rgba(24, 191, 204, 0.12);
}

.bt-background-option > input:focus-visible + .bt-background-option__card {
  outline: 2px solid var(--bt-accent);
  outline-offset: 2px;
}

.bt-background-option__preview {
  position: relative;
  display: block;
  overflow: hidden;
  aspect-ratio: 16 / 9;
  border-radius: var(--bt-radius-cell);
  background-color: var(--bt-canvas);
  background-position: center;
  background-size: cover;
  box-shadow: 0 0 0 1px var(--bt-line) inset;
}

/* Every image swatch carries the same overlay the live background does, so the
   preview shows what picking it actually looks like under the interface — dark
   film for the dark-theme images, light film for the light-theme ones (the
   active theme sets --bt-preview-overlay). The two solid options have nothing
   to dim. */
.bt-background-option__preview:not(.bt-background-option__preview--black):not(.bt-background-option__preview--white)::after {
  position: absolute;
  inset: 0;
  background: var(--bt-preview-overlay);
  content: "";
}

.bt-background-option__preview--black {
  background: #000;
}

.bt-background-option__preview--white {
  background: #fff;
}

.bt-background-option__preview--aurora {
  background-image: url("portal-bg-aurora.jpg");
}

.bt-background-option__preview--prism {
  background-image: url("portal-bg-prism.jpg");
}

.bt-background-option__preview--amber {
  background-image: url("portal-bg-amber.jpg");
}

.bt-background-option__preview--dawn {
  background-image: url("portal-bg-dawn.jpg");
}

.bt-background-option__preview--mist {
  background-image: url("portal-bg-mist.jpg");
}

.bt-background-option__preview--sand {
  background-image: url("portal-bg-sand.jpg");
}

.bt-background-option__name {
  overflow: hidden;
  padding: 0 3px;
  font-size: 13px;
  font-weight: 600;
  line-height: 18px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bt-background-brightness {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 18px;
  transition: opacity var(--bt-duration-sm) var(--bt-ease);
}

.bt-background-brightness__value {
  color: var(--bt-accent-text);
  font-size: 12px;
  font-weight: 600;
}

.bt-range {
  width: 100%;
  height: 6px;
  padding: 0;
  border: 0;
  border-radius: var(--bt-radius-round);
  appearance: none;
  -webkit-appearance: none;
  background:
    linear-gradient(
      to right,
      var(--bt-accent) 0,
      var(--bt-accent) var(--bt-range-fill, 50%),
      var(--bt-track) var(--bt-range-fill, 50%),
      var(--bt-track) 100%
    );
  cursor: pointer;
}

.bt-range::-webkit-slider-thumb {
  width: 18px;
  height: 18px;
  border: 3px solid var(--bt-accent);
  border-radius: 50%;
  appearance: none;
  -webkit-appearance: none;
  background: var(--bt-thumb);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
}

.bt-range::-moz-range-thumb {
  width: 12px;
  height: 12px;
  border: 3px solid var(--bt-accent);
  border-radius: 50%;
  background: var(--bt-thumb);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
}

.bt-range:disabled {
  cursor: not-allowed;
  opacity: 0.38;
}

.bt-range-labels {
  display: flex;
  justify-content: space-between;
  color: var(--bt-text-disabled);
  font-size: 11px;
  line-height: 14px;
}

.bt-panel.is-solid .bt-background-brightness {
  opacity: 0.58;
}


/* ---------------------------------------------------------------- bulk select */
/* A selectable row is the ordinary row with a checkbox track in front: the
   columns read identically, they just shift right by the width of the box.
   The track itself is part of --job-cols (see renderJobHead), so this must not
   redeclare grid-template-columns or the row would fall out of the table. */
.bt-job-select {
  display: grid;
  place-items: center;
  align-self: center;
}
.bt-check {
  position: relative;
  display: inline-grid;
  width: 20px;
  height: 20px;
  place-items: center;
  cursor: pointer;
}
.bt-check input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
.bt-check__box {
  width: 20px;
  height: 20px;
  border: 1.8px solid var(--bt-neutral);
  border-radius: 6px;
  transition: background var(--bt-duration-sm) var(--bt-ease), border-color var(--bt-duration-sm) var(--bt-ease);
}
.bt-check input:checked + .bt-check__box,
.bt-tick input:checked + .bt-check__box {
  border-color: var(--bt-accent);
  background: var(--bt-accent) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23001318' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center / 14px no-repeat;
}
/* Half-ticked: some of what this box governs is selected, not all of it. Left
   unstyled it is pixel-for-pixel the empty state, and a master box that reports
   "nothing selected" while three rows are is worse than having no master box.
   Declared after :checked so a box that is somehow both shows the dash. */
.bt-check input:indeterminate + .bt-check__box {
  border-color: var(--bt-accent);
  background: var(--bt-accent) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23001318' stroke-width='3.4' stroke-linecap='round'%3E%3Cpath d='M6 12h12'/%3E%3C/svg%3E") center / 14px no-repeat;
}
.bt-check input:focus-visible + .bt-check__box { outline: 2px solid var(--bt-accent); outline-offset: 2px; }

/* Rows in selection mode reveal their checkbox; the whole board dims the swipe. */
.bt-joblist.is-selecting .bt-job-row { cursor: default; }

/* Floating action bar for the current selection. */
.bt-bulkbar {
  position: fixed;
  z-index: 55;
  left: 50%;
  bottom: 24px;
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: calc(100vw - 32px);
  padding: 8px 8px 8px 16px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-surface-popover);
  box-shadow: var(--bt-shadow-inset), var(--bt-shadow-popover);
  /* Parked well clear of the bottom edge — a percentage of its own height left
     the top few pixels (and their inset highlight) peeking above the fold, and
     visibility keeps it out of the way for good once the slide has finished. */
  transform: translate(-50%, calc(100% + 40px));
  visibility: hidden;
  transition: transform var(--bt-duration-md) var(--bt-ease),
              visibility 0s linear var(--bt-duration-md);
}
.bt-bulkbar.is-open {
  transform: translate(-50%, 0);
  visibility: visible;
  transition: transform var(--bt-duration-md) var(--bt-ease);
}
.bt-bulkbar__count { font-size: 14px; font-weight: 600; white-space: nowrap; }
.bt-bulkbar__actions { display: flex; align-items: center; gap: 6px; }
.bt-bulkbar__sep { width: 1px; height: 24px; background: var(--bt-line); }
/* More actions than a phone's width holds: they wrap, inside a rounder-cornered bar. */
@media (max-width: 760px) {
  .bt-bulkbar--wrap { flex-wrap: wrap; width: calc(100vw - 32px); border-radius: var(--bt-radius-sheet); }
  .bt-bulkbar--wrap .bt-bulkbar__sep { display: none; }
  .bt-bulkbar--wrap .bt-bulkbar__count { flex: 1; }
  .bt-bulkbar--wrap .bt-bulkbar__actions { order: 3; flex: 1 0 100%; flex-wrap: wrap; }
}

/* ---------------------------------------------------------------- job notes */
/* Notes are usually a sentence and occasionally an essay, and the info pane is
   ~300px wide. So the pane shows the opening two lines and a button; the whole
   thing is read and written in the dialog, where there's room for it. */
.bt-notes-block { display: flex; flex-direction: column; gap: 8px; }
/* The clamp sits on an inner element, not on this one: `overflow: hidden`
   clips at the padding edge, so a padded box lets the third line bleed into
   the bottom padding instead of being cut off. The padding lives out here and
   the clipping happens in there, flush against the text. */
.bt-notes-preview {
  padding: 8px 10px;
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
  color: var(--bt-text-secondary);
  font-size: 14px;
  line-height: 20px;
}
.bt-notes-preview__text {
  display: -webkit-box;
  overflow: hidden;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
}
.bt-notes-actions { display: flex; align-items: center; gap: 8px; }
/* Nothing to show more of: no empty row under the preview. */
.bt-notes-actions:has(> [hidden]:only-child) { display: none; }
/* The heading and its Add/Edit button share one line. */
.bt-notes-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.bt-notes-head + .bt-notes-block { margin-top: 6px; }
.bt-field > .bt-notes-head + .bt-notes-block { margin-top: 0; }
/* The dialog's own copy of the note, when it's being read rather than edited. */
.bt-notes-full {
  max-height: 46vh;
  overflow-y: auto;
  font-size: 14px;
  line-height: 21px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.bt-sheet__note textarea.bt-input.bt-notes-input { min-height: 180px; }

/* ---------------------------------------------------------------- map */
/* Tiles are positioned absolutely inside a clipped box (see map.js). Nothing
   here assumes a library — the classes are all ours. */
.btmap {
  position: relative;
  overflow: hidden;
  height: 300px;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
}
/* Google's Map draws into this div itself, so it just needs to fill the
   shell — no tile/pin drawing of our own any more. */
.btmap__canvas { position: absolute; inset: 0; }
/* What happened at a spot on a trip, in Google's own info bubble. Literal
   colours: the bubble is white in both themes, and the portal's dark theme
   would otherwise hand its close button white-on-white (button colour is
   inherited site-wide). */
.btmap .gm-style .gm-style-iw-c { padding: 0 14px 14px !important; border-radius: 12px; box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18); }
.btmap .gm-style .gm-style-iw-d { overflow: auto !important; padding: 0 !important; }
.btmap .gm-style .gm-style-iw-chr { align-items: center; padding-top: 6px; }
.btmap .gm-style .gm-ui-hover-effect { color: #3a3a3c; opacity: 0.7; }
.btmap .gm-style .gm-ui-hover-effect:hover { opacity: 1; }
.btmap .gm-style .gm-ui-hover-effect > span { background-color: #3a3a3c !important; }
.btmap-bubble__head { display: flex; min-width: 0; align-items: center; gap: 8px; padding-top: 2px; }
.btmap-bubble__title { color: #1c1c1e; font-size: 14px; line-height: 20px; font-weight: 600; }
.btmap-bubble__glyph {
  display: inline-flex; flex: 0 0 auto; width: 22px; height: 22px; align-items: center; justify-content: center;
  border-radius: 50%; background: #1c1c1e; color: #ffffff; font-size: 11px; font-weight: 700;
}
.btmap-bubble__glyph--auto { background: #ffffff; color: #3a3a3c; box-shadow: inset 0 0 0 2px #3a3a3c; }
.btmap-bubble { display: flex; max-width: 230px; flex-direction: column; align-items: flex-start; gap: 4px; color: #3a3a3c; font-size: 13px; line-height: 18px; }
.btmap-bubble__badge {
  padding: 1px 8px; border-radius: 999px; background: #1c1c1e; color: #ffffff;
  font-size: 11px; line-height: 16px; font-weight: 600;
}
.btmap-bubble__badge--auto { background: #ffffff; color: #3a3a3c; box-shadow: inset 0 0 0 1px #8e8e93; }
.btmap-bubble__meta { color: #1c1c1e; }
.btmap-bubble__detail { color: #6e6e73; }
.btmap__status {
  position: absolute;
  top: 50%;
  left: 50%;
  width: min(280px, 84%);
  padding: 10px 12px;
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-popover);
  box-shadow: var(--bt-shadow-popover);
  color: var(--bt-text-secondary);
  font-size: 12px;
  line-height: 17px;
  text-align: center;
  transform: translate(-50%, -50%);
  z-index: 1;
}
.btmap__traffic-legend {
  position: absolute;
  top: 10px;
  left: 10px;
  display: flex;
  gap: 8px;
  align-items: center;
  padding: 5px 8px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: var(--bt-radius-round);
  background: rgba(9, 30, 35, 0.86);
  color: #fff;
  font-size: 10px;
  line-height: 14px;
  pointer-events: none;
  z-index: 1;
}
.btmap__traffic-legend span { display: inline-flex; gap: 4px; align-items: center; }
.btmap__traffic-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; }
.btmap__traffic-dot--normal { background: #18bfcc; }
.btmap__traffic-dot--slow { background: #f5a623; }
.btmap__traffic-dot--jam { background: #e25555; }
.btmap--tall { height: 460px; }
.btmap--flat { border-radius: 0; border-width: 0; }

/* A client's logo. Letterboxed, never cropped — a logo cropped to a circle
   stops being the logo. */
.bt-logo {
  display: grid;
  overflow: hidden;
  width: 40px;
  height: 40px;
  flex: 0 0 auto;
  place-items: center;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
  color: var(--bt-text-secondary);
  font-size: 13px;
  font-weight: 700;
}
.bt-logo img { max-width: 100%; max-height: 100%; object-fit: contain; }
.bt-logo--64 { width: 64px; height: 64px; font-size: 18px; }
.bt-logo--24 { width: 24px; height: 24px; border-radius: 6px; font-size: 10px; }
/* Logo beside the name, labels on one line, boxes starting together. */
.bt-idrow { display: flex; align-items: flex-start; gap: 14px; }

/* The camera badge sits on the tile, the way the profile photo editor does —
   so the control is on the thing it changes rather than in a button row. */
.bt-logo-edit { position: relative; flex: 0 0 auto; }
.bt-logo-edit__btn,
.bt-logo-edit__x {
  position: absolute;
  display: grid;
  width: 26px;
  height: 26px;
  place-items: center;
  border: 3px solid var(--bt-surface-popover);
  border-radius: 50%;
  cursor: pointer;
}
.bt-logo-edit__btn {
  right: -8px;
  bottom: -8px;
  background: var(--bt-accent);
  color: var(--bt-ink);
}
.bt-logo-edit__x {
  right: -8px;
  top: -8px;
  background: var(--bt-danger-soft);
  color: var(--bt-danger-text);
}
.bt-logo-edit__btn .bt-icon { width: 13px; height: 13px; }
.bt-logo-edit__btn:hover { filter: brightness(1.08); }
.bt-logo-edit__x:hover { background: var(--bt-danger); color: #fff; }

/* ---------------------------------------------------------------- dispatch schedule */
.bt-onsite-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}
/* The Jobs board's Dispatch view keeps only the switch between readings: its
   schedule has its own periods, and the job filters, search, columns and
   pages say nothing about it. */
#view-jobs.is-dispatch #filter-chips > .bt-segmented:not(#job-view-mode),
#view-jobs.is-dispatch .bt-input-shell:has(#search-input),
#view-jobs.is-dispatch .bt-boardbar__actions { display: none; }
.bt-onsite-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
  gap: 16px;
}
/* As tall as the screen leaves room for, not a fixed 650px that left the
   bottom of a tall window empty and scrolled after eight visits. */
.bt-onsite-list {
  display: flex;
  min-width: 0;
  max-height: max(420px, calc(100dvh - var(--bt-topbar-height) - 140px));
  flex-direction: column;
  gap: 4px;
  overflow-y: auto;
}
.bt-onsite-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 72px;
  padding: 8px;
  border: 1px solid transparent;
  border-radius: var(--bt-radius-cell);
  background: transparent;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.bt-onsite-row:hover { background: var(--bt-surface-nested); }
.bt-onsite-row.is-active {
  border-color: var(--bt-line-strong);
  background: var(--bt-surface-nested);
}
.bt-onsite-row:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 1px; }
.bt-onsite-row__body { display: flex; min-width: 0; flex-direction: column; gap: 2px; }
.bt-onsite-date {
  display: grid;
  width: 46px;
  height: 50px;
  flex: 0 0 auto;
  place-content: center;
  border-radius: 12px;
  background: var(--bt-surface-nested);
  text-align: center;
}
.bt-onsite-date small {
  color: var(--bt-text-secondary);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  line-height: 13px;
  text-transform: uppercase;
}
.bt-onsite-date b { font-size: 19px; line-height: 22px; }
.bt-onsite-avatars { display: flex; align-items: center; padding-left: 6px; }
.bt-onsite-avatars > * { position: relative; margin-left: -7px; }
.bt-onsite-avatars > :first-child { z-index: 2; margin-left: 0; }
.bt-onsite-avatars .bt-avatar { box-shadow: 0 0 0 2px var(--bt-surface); }
.bt-onsite-row:hover .bt-onsite-avatars .bt-avatar,
.bt-onsite-row.is-active .bt-onsite-avatars .bt-avatar { box-shadow: 0 0 0 2px var(--bt-surface-nested); }
.bt-onsite-avatar-more { background: var(--bt-surface-interactive); color: var(--bt-text-secondary); font-size: 10px; }

/* The Live tab and what it adds to a row: a state badge over the row's own
   meta line, and the crew block under Assigned people. The dot is the only
   thing on the board that moves, and the global reduced-motion rule above
   holds it still. */
.bt-onsite-live-tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.bt-onsite-live-dot {
  display: block;
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--bt-accent);
  animation: bt-onsite-live-pulse 2s ease-in-out infinite;
}
@keyframes bt-onsite-live-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}
.bt-onsite-live-row {
  display: flex;
  min-width: 0;
  align-items: center;
  gap: 6px;
  margin-top: 2px;
}
.bt-onsite-live {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.bt-onsite-live[hidden] { display: none; }
.bt-onsite-live-person {
  display: flex;
  min-width: 0;
  align-items: center;
  gap: 8px;
  padding: 6px 10px 6px 6px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-surface-nested);
}
.bt-onsite-live-person__body { display: flex; min-width: 0; flex-direction: column; }
.bt-onsite-live-person strong { font-size: 13px; line-height: 17px; }
.bt-onsite-live-person small { font-size: 11px; line-height: 15px; }

/* The road a visit actually took. One row per recorded leg, each carrying the
   colour of its own line on the map so the two read as one thing. */
.bt-onsite-trail {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.bt-onsite-trail[hidden] { display: none; }
/* The route's key and the rings' key share the row above the map: route on
   the left, rings on the right, stacking when the panel is too narrow. */
.bt-onsite-keys { display: flex; flex-wrap: wrap; gap: 8px 24px; }
.bt-onsite-keys > .bt-onsite-trail { flex: 1 1 240px; min-width: 0; }
.bt-onsite-keys > .bt-onsite-keys__rings { flex: 0 1 auto; margin-left: auto; }
.bt-onsite-keys:not(:has(> :not([hidden]))) { display: none; }
/* How to read the map (pins, dashes, rings), folded under the legs until
   asked for. Not a destination, so a plain <details>. */
.bt-onsite-mapkey > summary {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--bt-text-secondary);
  cursor: pointer;
  font-size: 12px;
  font-weight: 600;
  line-height: 18px;
  list-style: none;
}
.bt-onsite-mapkey > summary::-webkit-details-marker { display: none; }
.bt-onsite-mapkey > summary:hover { color: var(--bt-text-primary); }
.bt-onsite-mapkey > summary .bt-icon { transform: rotate(-90deg); transition: transform var(--bt-duration-sm) var(--bt-ease); }
.bt-onsite-mapkey[open] > summary .bt-icon { transform: none; }
.bt-onsite-mapkey > .bt-onsite-keys,
.bt-onsite-mapkey__body { margin-top: 8px; }
.bt-onsite-mapkey .bt-onsite-keys > .bt-onsite-keys__rings { flex: 1 1 240px; margin-left: 0; }
.bt-onsite-trail-row {
  display: flex;
  min-width: 0;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  line-height: 18px;
  color: var(--bt-text-secondary);
}
.bt-onsite-trail-key {
  flex: 0 0 auto;
  width: 14px;
  height: 3px;
  border-radius: 2px;
  background: var(--bt-accent);
}
/* The literal line colour rather than --bt-warning, which differs between the
   two themes: Google's tiles do not, so the key would stop matching the line. */
.bt-onsite-trail-key--return { background: #ff9f0a; }
.bt-onsite-trail-key--break { background: #bf5af2; }
.bt-onsite-trail-key--away { background: #ff453a; }
/* The drive so far while there is still a road ahead, at the line's own
   faded strength; and the road ahead, thick and outlined like the line. */
.bt-onsite-trail-key--faded { opacity: 0.4; }
.bt-onsite-trail-key--ahead { height: 6px; border-radius: 3px; box-shadow: 0 0 0 2px #0b4c54; }
.bt-onsite-trail-row strong { color: var(--bt-text-primary); font-weight: 600; }
/* A ring round the site pin, keyed as the ring it is. */
/* Pins for what happened on a trip: filled for a tap, hollow for a time
   worked out from the GPS. Lines: solid after a tap, dashed when worked out. */
.bt-onsite-mark-key {
  flex: 0 0 auto;
  width: 12px;
  height: 12px;
  border: 2px solid #3a3a3c;
  border-radius: 50%;
  box-sizing: border-box;
  background: #ffffff;
}
.bt-onsite-mark-key--tap { background: #1c1c1e; border-color: #ffffff; box-shadow: 0 0 0 1px #3a3a3c; }
/* A person's avatar, keyed with the badge the map puts on it (map.js
   avatarIcon): the accent ring and a green dot where they are now, a grey
   ring and a clock where their phone last reported. */
.bt-onsite-person-key {
  position: relative;
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  border: 2px solid #18bfcc;
  border-radius: 50%;
  box-sizing: border-box;
  background: #3a3a3c;
}
.bt-onsite-person-key__badge {
  position: absolute;
  right: -4px;
  bottom: -4px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 9px;
  height: 9px;
  border: 1.5px solid #ffffff;
  border-radius: 50%;
  box-sizing: content-box;
  background: #30d158;
  color: #ffffff;
}
.bt-onsite-person-key__badge svg { width: 7px; height: 7px; }
.bt-onsite-person-key--stale { border-color: #8e8e93; }
.bt-onsite-person-key--stale .bt-onsite-person-key__badge { background: #636366; }
/* Worked out from the GPS rather than after a tap: its own brown, matching the line. */
.bt-onsite-trail-key.bt-onsite-trail-key--dashed { background: repeating-linear-gradient(90deg, #ac8e68 0 4px, transparent 4px 7px); }
/* A calculated drive home: the drive home's amber, dashed, because it was worked out rather than driven. */
.bt-onsite-trail-key--return.bt-onsite-trail-key--dashed { background: repeating-linear-gradient(90deg, #ff9f0a 0 4px, transparent 4px 7px); }
.bt-onsite-zone-key {
  flex: 0 0 auto;
  width: 12px;
  height: 12px;
  border: 2px solid;
  border-radius: 50%;
  box-sizing: border-box;
}

/* One visit in full, as a pop-out over the job page or the Jobs board's
   Dispatch view. Wider than a decision sheet: the map is most of it. As tall
   as the window allows inside the scrim's padding, so a visit with a live
   drive opens without scrolling on most screens. */
.bt-sheet.bt-onsite-popout {
  max-width: min(760px, calc(100vw - 32px));
  max-height: calc(100vh - 48px);
  max-height: calc(100dvh - 48px);
  overflow-y: auto;
  text-align: left;
}
.bt-onsite-preview {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 14px;
  padding: 4px 6px;
}
.bt-onsite-popout .btmap--onsite { height: min(420px, 48vh); }
.bt-onsite-preview__head { display: flex; min-width: 0; align-items: center; gap: 12px; }
/* Where the visit is, above its title: client and site in full, wrapping
   rather than cut. */
.bt-onsite-preview__where { display: block; color: var(--bt-text-secondary); font-size: 13px; font-weight: 500; line-height: 18px; }
.bt-onsite-preview__meta { display: flex; flex-direction: column; gap: 7px; color: var(--bt-text-secondary); font-size: 13px; }
.bt-onsite-preview__meta > span { display: flex; min-width: 0; align-items: flex-start; gap: 8px; }
.bt-onsite-preview__meta .bt-icon { flex: 0 0 auto; margin-top: 1px; }
.bt-onsite-preview__people { display: flex; flex-wrap: wrap; gap: 8px; }
.bt-onsite-preview__people > .bt-overline { width: 100%; }
.bt-onsite-person-chip {
  display: inline-flex;
  min-width: 0;
  align-items: center;
  gap: 7px;
  padding: 5px 14px 5px 5px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-surface-nested);
}
.bt-onsite-person-chip strong,
.bt-onsite-person-chip small { display: block; line-height: 15px; }
.bt-onsite-person-chip strong { font-size: 12px; }
.bt-onsite-person-chip small { color: var(--bt-text-secondary); font-size: 10px; }
.bt-onsite-preview__actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 6px; }
.btmap--onsite { height: 250px; }
.btmap--editor { height: 260px; }
/* The job page's Dispatch section: a picture of the place, not a map to work. */
.btmap--mini { flex: 0 0 auto; height: 112px; margin: 4px 0 2px; border-radius: var(--bt-radius-sm); pointer-events: none; }
.btmap--mini .btmap__status { font-size: 11px; }

.bt-sheet.bt-onsite-editor {
  max-width: min(960px, calc(100vw - 32px));
  max-height: min(90vh, 880px);
  overflow-y: auto;
}
.bt-onsite-editor-form {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 12px;
  padding: 4px 10px 10px;
}
.bt-onsite-editor-form > * { min-width: 0; }
.bt-onsite-editor__column {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 12px;
}
.bt-onsite-editor__actions {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}
.bt-onsite-editor__actions .bt-sheet__cancel { width: 100%; margin-top: 0; }
@media (min-width: 760px) {
  .bt-onsite-editor-form {
    grid-template-areas:
      "error error"
      "left right"
      "actions actions";
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-items: start;
    gap: 14px 18px;
  }
  .bt-onsite-editor__error { grid-area: error; }
  .bt-onsite-editor__column--left { grid-area: left; }
  .bt-onsite-editor__column--right { grid-area: right; }
  .bt-onsite-editor__actions { grid-area: actions; }
  .bt-onsite-editor__column { gap: 14px; }
}
.bt-onsite-people-control { gap: 6px; }
.bt-onsite-people-control .bt-picker__list {
  max-height: 216px;
  overscroll-behavior: contain;
}
/* On the job and visit forms, what is chosen has to read at a glance across a
   busy three-column page: the same teal outline as the kind picker's choice. */
#create-job-form .bt-segmented__item.is-active,
.bt-onsite-editor-form .bt-segmented__item.is-active {
  background: var(--bt-accent-soft);
  box-shadow: inset 0 0 0 1px var(--bt-accent);
  color: var(--bt-accent-text);
}
#create-job-form .bt-picker__row.is-on,
.bt-onsite-editor-form .bt-picker__row.is-on {
  background: var(--bt-accent-soft);
  box-shadow: inset 0 0 0 1px var(--bt-accent);
}
.bt-onsite-client-card {
  display: flex;
  min-height: 56px;
  align-items: center;
  gap: 10px;
  margin-top: 8px;
  padding: 8px;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
}
/* "Use a different address" under the client card: a quiet text action,
   left-aligned with the card, with the 44px hit area of the row it sits in. */
.bt-create-dispatch__own {
  align-self: flex-start;
  min-height: 32px;
  margin-top: 4px;
  gap: 6px;
}
@media (max-width: 900px) {
  .bt-onsite-list { max-height: none; }
}
@media (max-width: 560px) {
  .bt-onsite-row { grid-template-columns: auto minmax(0, 1fr) auto; }
  .bt-onsite-row > .bt-onsite-avatars { grid-column: 2; justify-self: start; padding-left: 0; }
  .bt-onsite-row > .bt-row-chevron { grid-column: 3; grid-row: 1 / span 2; }
  .bt-onsite-preview__actions > * { flex: 1 1 auto; justify-content: center; }
}


/* ---------------------------------------------------------------- inventory */
/* Type rail beside the grid, the way the app this replaces works: the list of
   what's in here doubles as the filter. */
.bt-invgrid { display: grid; grid-template-columns: 190px minmax(0, 1fr); gap: 18px; }
.bt-invgrid__rail { display: flex; flex-direction: column; gap: 2px; }
.bt-invgrid__rail[hidden] { display: none; }
.bt-invgrid__main { min-width: 0; }
.bt-invtype {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border: 0;
  border-radius: var(--bt-radius-sm);
  background: transparent;
  color: var(--bt-text-secondary);
  font-size: 13px;
  text-align: left;
  cursor: pointer;
}
.bt-invtype:hover { background: var(--bt-surface-nested); color: var(--bt-text-primary); }
.bt-invtype.is-active { background: var(--bt-accent-soft); color: var(--bt-accent-text); font-weight: 600; }
.bt-invtype__n { margin-left: auto; flex: 0 0 auto; font-variant-numeric: tabular-nums; }
@media (max-width: 860px) {
  /* No room for a rail — it becomes a scrolling row of chips above the grid. */
  .bt-invgrid { grid-template-columns: minmax(0, 1fr); gap: 10px; }
  .bt-invgrid__rail { flex-direction: row; overflow-x: auto; padding-bottom: 4px; }
  .bt-invgrid__rail .bt-overline { display: none; }
  .bt-invtype { width: auto; flex: 0 0 auto; border-radius: var(--bt-radius-round); }
}

/* A record's photos and paperwork. Thumbnails open the same viewer the job
   conversations use, so there is one way to look at a picture in this app. */
.bt-gallery { display: flex; flex-wrap: wrap; gap: 8px; }
.bt-gallery:empty { display: none; }
.bt-gallery__item { position: relative; display: inline-flex; }
.bt-gallery__item--claim { flex-direction: column; width: 84px; }
.bt-gallery__open {
  display: block;
  overflow: hidden;
  width: 84px;
  height: 84px;
  padding: 0;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
  cursor: pointer;
  line-height: 0;
}
.bt-gallery__open:hover { border-color: var(--bt-accent); }
.bt-gallery__open img { width: 100%; height: 100%; object-fit: cover; }
/* Anything that isn't a picture still needs to be a tile, so the row of files
   reads as one set rather than pictures plus a list. */
.bt-gallery__doc {
  display: flex;
  width: 100%;
  height: 100%;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 6px;
  color: var(--bt-text-secondary);
  font-size: 10px;
  line-height: 13px;
}
.bt-gallery__doc .bt-truncate { max-width: 100%; }

/* ---------------------------------------------------------------- timesheet */
/* The week as a table. Narrow screens scroll it sideways rather than reflowing
   into cards: a timesheet is read by scanning a column, and columns are the
   one thing cards throw away. */
/* Named apart from .bt-sheet — that's the bottom-sheet dialog, and a table
   sharing its class inherits a 400px cap, a popover background and a scroll
   box. */
.bt-tsheet-scroll { overflow-x: auto; }
.bt-tsheet {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  white-space: nowrap;
}
.bt-tsheet thead th {
  padding: 0 10px 8px;
  border-bottom: 1px solid var(--bt-line);
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-align: left;
  text-transform: uppercase;
}
.bt-tsheet__num { text-align: right; font-variant-numeric: tabular-nums; }
/* `.bt-tsheet thead th` outranks a bare class, so a numeric header would keep
   the left alignment while its column of numbers sat right — the heading has
   to travel with its column. */
.bt-tsheet thead th.bt-tsheet__num { text-align: right; }

/* Two report tables with identical columns should line up with each other.
   Auto table layout sizes every table independently from its own content, so
   "By person" and "By client" ended up with different column widths — fixed
   layout gives them one set of widths to agree on. The min-width keeps the
   number columns readable on a narrow screen; the scroller handles the rest. */
.bt-tsheet--report { table-layout: fixed; min-width: 600px; }
.bt-tsheet--report th:not(:first-child),
.bt-tsheet--report td:not(:first-child) { width: 104px; }
.bt-tsheet--report th.bt-tsheet__chev,
.bt-tsheet--report td.bt-tsheet__chev { width: 40px; padding-inline: 4px 10px; text-align: right; }
/* Fixed layout means a long name has to be told what to do instead of pushing
   its column wider. */
.bt-tsheet--report td:first-child > span {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* A person on the Timesheets screen: face, name, role — the row opens their
   week, so it wears the same pointer and lift as an entry row. */
.bt-tsheet--report td:first-child > .bt-tsheet__person {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.bt-tsheet__who { min-width: 0; }
.bt-tsheet__who > span { display: block; overflow: hidden; text-overflow: ellipsis; }
.bt-tsheet__row[data-person] td { vertical-align: middle; }

/* People · Clients · To review: what the Timesheets screen is about, set
   above its panel with the page's own gap below and a little air on top. */
.bt-ts-modes { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; justify-content: space-between; padding-top: 4px; }

/* The calendar. A week by the hour: a time gutter, then one column per day,
   each entry a block from arrival to departure. Scrolls sideways inside its
   panel on a narrow screen rather than squeezing seven days into it. */
.bt-tscal-scroll { overflow-x: auto; }
.bt-tscal {
  display: grid;
  grid-template-columns: 64px repeat(var(--cal-days), minmax(120px, 1fr));
  min-width: calc(64px + var(--cal-days) * 120px);
}
.bt-tscal__dayhead {
  display: flex;
  height: 40px;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  padding: 0 8px;
  border-bottom: 1px solid var(--bt-line);
  font-size: 12px;
}
.bt-tscal__dayname { font-weight: 700; }
.bt-tscal__col.is-today .bt-tscal__dayname { color: var(--bt-accent-text); }
.bt-tscal__daytotal { color: var(--bt-text-secondary); font-size: 12px; font-variant-numeric: tabular-nums; }
.bt-tscal__gutter .bt-tscal__hour { position: relative; }
.bt-tscal__gutter .bt-tscal__hour span {
  position: absolute; top: -7px; right: 8px;
  color: var(--bt-text-secondary); font-size: 11px; white-space: nowrap;
}
.bt-tscal__gutter .bt-tscal__hour:first-of-type span { top: 2px; }
.bt-tscal__col { border-left: 1px solid var(--bt-line); }
.bt-tscal__lane {
  position: relative;
  background-image: repeating-linear-gradient(to bottom, transparent 0, transparent 43px, var(--bt-line) 43px, var(--bt-line) 44px);
}
.bt-tscal__block {
  position: absolute;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  gap: 1px;
  padding: 4px 6px;
  border: 0;
  border-left: 3px solid var(--bt-accent);
  border-radius: 6px;
  background: var(--bt-accent-soft);
  color: var(--bt-text-primary);
  font: inherit;
  font-size: 12px;
  line-height: 15px;
  text-align: left;
  cursor: pointer;
}
.bt-tscal__block:hover { filter: brightness(1.15); }
.bt-tscal__block:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 1px; }
.bt-tscal__block.is-flagged { border-left-color: var(--bt-warning); background: var(--bt-warning-soft); }
.bt-tscal__block > span { flex-shrink: 0; }
.bt-tscal__first { font-weight: 700; }
.bt-tscal__second, .bt-tscal__time { color: var(--bt-text-secondary); font-size: 11px; }
/* A short entry has no room for three lines: one line holds who and when,
   and the rest is in its tooltip. */
.bt-tscal__block.is-short { flex-direction: row; align-items: center; gap: 6px; padding-top: 0; padding-bottom: 0; }
.bt-tscal__block.is-short > .bt-tscal__first { max-width: 100%; }
.bt-tscal__block.is-short > .bt-tscal__time { flex: 1 1 0; min-width: 0; }
.bt-tscal__block.is-short > .bt-tscal__second { display: none; }
.bt-tscal__block.is-medium > .bt-tscal__second { display: none; }

/* Longer than two weeks: a month grid, Monday first. */
.bt-tscal-month {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  border-top: 1px solid var(--bt-line);
  border-left: 1px solid var(--bt-line);
}
.bt-tscal-month__head {
  padding: 8px; border-right: 1px solid var(--bt-line); border-bottom: 1px solid var(--bt-line);
  color: var(--bt-text-secondary); font-size: 11px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;
}
.bt-tscal-month__day {
  display: flex; flex-direction: column; gap: 3px; min-width: 0; min-height: 104px; padding: 4px;
  border-right: 1px solid var(--bt-line); border-bottom: 1px solid var(--bt-line);
}
.bt-tscal-month__day.is-outside { opacity: 0.45; }
.bt-tscal-month__date,
.bt-tscal-month__item,
.bt-tscal-month__more {
  display: flex; min-width: 0; align-items: center; justify-content: space-between; gap: 6px;
  padding: 2px 6px; border: 0; border-radius: 6px; background: transparent; color: inherit;
  font: inherit; font-size: 12px; text-align: left; cursor: pointer;
}
.bt-tscal-month__date { font-weight: 700; }
.bt-tscal-month__day.is-today .bt-tscal-month__date > span:first-child { color: var(--bt-accent-text); }
.bt-tscal-month__item { background: var(--bt-accent-soft); }
.bt-tscal-month__item.is-flagged { background: var(--bt-warning-soft); }
.bt-tscal-month__more { color: var(--bt-text-secondary); }
.bt-tscal-month__date:hover, .bt-tscal-month__more:hover { background: var(--bt-surface-nested); }
.bt-tscal-month__item:hover { filter: brightness(1.15); }
.bt-tscal-month__date:focus-visible, .bt-tscal-month__item:focus-visible, .bt-tscal-month__more:focus-visible {
  outline: 2px solid var(--bt-accent); outline-offset: 1px;
}
@media (max-width: 760px) {
  .bt-tscal-month__day { min-height: 72px; }
  .bt-tscal-month__item > span:last-child { display: none; }
}
/* The review queue: a list of entries across people and weeks, so the date and
   the person lead and the reason sits under the customer it qualifies. */
.bt-tsheet--queue { table-layout: fixed; min-width: 900px; }
.bt-tsheet--queue th:nth-child(1), .bt-tsheet--queue td:nth-child(1) { width: 112px; }
.bt-tsheet--queue th:nth-child(2), .bt-tsheet--queue td:nth-child(2) { width: 150px; }
.bt-tsheet--queue th:nth-child(4), .bt-tsheet--queue td:nth-child(4) { width: 164px; white-space: nowrap; }
.bt-tsheet--queue th:nth-child(5), .bt-tsheet--queue td:nth-child(5) { width: 92px; }
/* Wide enough for a full job number and Mark reviewed side by side. */
.bt-tsheet--queue th:nth-child(6), .bt-tsheet--queue td:nth-child(6) { width: 284px; }
.bt-tsheet--queue .bt-tsheet__actions .bt-cluster { flex-wrap: nowrap; }
.bt-tsheet--queue td { overflow: hidden; }
.bt-tsheet--queue .bt-tsheet__summary { max-width: none; overflow: hidden; text-overflow: ellipsis; }
.bt-tsheet--queue .bt-tsheet__actions .bt-cluster { justify-content: flex-end; }
/* A custom range: two dates at the height of the controls beside them. */
.bt-ts-range { align-items: center; }
/* The export dialog's period: the five presets across the dialog, then the
   period itself between the arrows that step it. */
.bt-tx-period { display: flex; width: 100%; }
.bt-tx-period .bt-segmented__item { flex: 1 1 auto; padding: 0 8px; }
.bt-tx-step { display: flex; align-items: center; gap: 6px; }
.bt-tx-step__label { flex: 1 1 auto; min-width: 0; font-weight: 600; text-align: center; font-variant-numeric: tabular-nums; }
.bt-ts-range .bt-input-shell { min-height: var(--bt-control-xs); padding: 0 10px; border-radius: var(--bt-radius-round); }
.bt-ts-range .bt-input { min-height: 0; padding: 0; font-size: 12px; }
@media (max-width: 760px) { .bt-ts-range .bt-input { font-size: 16px; } }
/* One client's entries: when and who lead, the job and what was said about it
   take what is left, and the figures sit at fixed widths. */
.bt-tsheet--client { table-layout: fixed; min-width: 760px; }
.bt-tsheet--client th:nth-child(1), .bt-tsheet--client td:nth-child(1) { width: 112px; }
.bt-tsheet--client th:nth-child(2), .bt-tsheet--client td:nth-child(2) { width: 150px; }
.bt-tsheet--client th:nth-child(4), .bt-tsheet--client td:nth-child(4) { width: 120px; }
.bt-tsheet--client th:nth-child(n+5), .bt-tsheet--client td:nth-child(n+5) { width: 96px; }
.bt-tsheet--client td { overflow: hidden; }
.bt-tsheet--client .bt-tsheet__summary { max-width: none; overflow: hidden; text-overflow: ellipsis; }
.bt-ts-queue__bar { align-items: center; margin: 16px 0 12px; }
.bt-ts-queue__bar .bt-label { margin: 0; }
.bt-ts-queue__bar .bt-select { width: auto; min-width: 200px; }

/* The week: six columns at fixed widths, the customer taking what is left.
   Auto layout let a long summary push the review flag off the side of a
   laptop screen; below the min-width the scroller takes over. */
.bt-tsheet--week { table-layout: fixed; min-width: 752px; }
.bt-tsheet--week.has-actions { min-width: 952px; }
.bt-tsheet--week th:nth-child(2), .bt-tsheet--week td:nth-child(2) { width: 112px; }
.bt-tsheet--week th:nth-child(n+3), .bt-tsheet--week td:nth-child(n+3) { width: 92px; }
.bt-tsheet--week.has-actions th:last-child,
.bt-tsheet--week.has-actions td:last-child { width: 136px; }
/* The day band's own cells keep their colspans; the widths above are read from
   the header row, so these only need to stay out of the way. */
.bt-tsheet--week .bt-tsheet__day > * { width: auto; }
.bt-tsheet--week td:first-child { overflow: hidden; }
.bt-tsheet--week .bt-tsheet__summary { max-width: none; overflow: hidden; text-overflow: ellipsis; }
/* What a figure is made of, under it: the miles under what they are worth,
   "Meals" under an expense. Secondary, so the column still reads as money. */
.bt-tsheet__sub {
  display: block;
  margin-top: 2px;
  color: var(--bt-text-secondary);
  font-size: 12px;
  font-weight: 500;
}
/* Flagged before it is paid: the whole row says so, not a badge at one end. */
.bt-tsheet__row.is-flagged td,
.bt-tsheet__row.is-flagged:hover td { background: var(--bt-warning-soft); }
/* The count above the week, with the way to it at the end of the line. */
.bt-tsheet-review { grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; margin-bottom: 16px; }

/* The week, a block per day. Each day is its own <tbody>: its band on top,
   carrying the day's totals, and its entries joined under it by hairlines.
   The space is between days, never inside one, so what belongs to a day is
   read at a glance and the band is plainly that day's total. */
.bt-tsheet--week { border-collapse: separate; border-spacing: 0; }
.bt-tsheet--week .bt-tsheet__group + .bt-tsheet__group::before,
.bt-tsheet--week tfoot::before { content: ''; display: table-row; height: 16px; }
/* A run of days with nothing on them stays a run of thin lines. */
.bt-tsheet--week .bt-tsheet__group:has(> .is-empty) + .bt-tsheet__group:has(> .is-empty)::before { height: 0; }
.bt-tsheet--week .bt-tsheet__row td { padding: 12px; }
.bt-tsheet--week .bt-tsheet__day th,
.bt-tsheet--week .bt-tsheet__day td { padding-top: 18px; padding-inline: 12px; }
.bt-tsheet--week .bt-tsheet__day.is-empty th,
.bt-tsheet--week .bt-tsheet__day.is-empty td { padding-block: 10px; }
/* An entry is a part of its day, not a card of its own: square, on the day's
   lighter shade, and only the day's last entry rounds the block off. */
.bt-tsheet--week .bt-tsheet__group .bt-tsheet__row td { background: var(--bt-surface-nested); border-radius: 0; }
.bt-tsheet--week .bt-tsheet__group .bt-tsheet__row.is-flagged td { background: var(--bt-warning-soft); }
.bt-tsheet--week .bt-tsheet__group .bt-tsheet__row[data-entry]:hover td { background: var(--bt-surface-nested); }
.bt-tsheet--week .bt-tsheet__group .bt-tsheet__row:last-child td:first-child { border-radius: 0 0 0 var(--bt-radius-cell); }
.bt-tsheet--week .bt-tsheet__group .bt-tsheet__row:last-child td:last-child { border-radius: 0 0 var(--bt-radius-cell) 0; }
.bt-tsheet--week thead th { padding-inline: 12px; }
/* The week's totals above it, a little apart from each other and the week. */
#ts-tiles { gap: 12px; margin-bottom: 4px; }
#ts-tiles .bt-metric { padding: 12px 16px; }
/* The day band. Every day of the week gets one, worked or not. */
.bt-tsheet__day td {
  padding: 14px 10px 6px;
  color: var(--bt-text-secondary);
  font-size: 12px;
  font-weight: 600;
}
/* A day with nothing under it is a thin line, not a heading over nothing. */
.bt-tsheet__day.is-empty th,
.bt-tsheet__day.is-empty td { padding-top: 6px; padding-bottom: 6px; border-top: 1px solid var(--bt-line); }
.bt-tsheet__day.is-empty th { color: var(--bt-text-secondary); font-weight: 500; }
.bt-tsheet__day.is-empty .bt-tsheet__dayname { font-weight: 500; }
.bt-tsheet__day th {
  padding: 14px 10px 6px;
  color: var(--bt-text-primary);
  font-size: 12px;
  font-weight: 600;
  text-align: left;
}
.bt-tsheet__day.is-today th { color: var(--bt-accent-text); }
.bt-tsheet__dayname { font-weight: 700; }
.bt-tsheet__daydate { color: var(--bt-text-secondary); font-weight: 500; }
/* Adjusted sits just left of the day's hours it qualifies, centered on the
   figure's line. */
.bt-tsheet__day .bt-badge { vertical-align: middle; }
.bt-tsheet__day .bt-badge + .bt-tsheet__dayhours { margin-left: 8px; vertical-align: middle; }
/* A day with time on it heads its block: a stronger shade than its entries,
   and its totals in the entries' size but bold, so they read as the sum of
   the rows under them rather than as a quieter row of their own. */
.bt-tsheet--week .bt-tsheet__day:not(.is-empty) > * {
  padding-block: 12px;
  background: var(--bt-surface-interactive);
  color: var(--bt-text-primary);
  font-size: 13px;
  font-weight: 700;
}
.bt-tsheet--week .bt-tsheet__day:not(.is-empty) > :first-child {
  border-radius: var(--bt-radius-cell) 0 0 0;
  font-size: 14px;
}
.bt-tsheet--week .bt-tsheet__day:not(.is-empty) > :last-child {
  border-radius: 0 var(--bt-radius-cell) 0 0;
}
.bt-tsheet--week .bt-tsheet__day.is-today:not(.is-empty) > th { color: var(--bt-accent-text); }
.bt-tsheet__row td {
  padding: 8px 10px;
  border-top: 1px solid var(--bt-line);
  vertical-align: top;
}
/* A row that opens something says so: the pointer and a lift in the
   background, with the same rounded ends every other openable row in the
   application has. NO ACCENT EDGE: a 2px bar down the left of a hovered row
   exists nowhere else here, so it read as this table's own invention rather
   than as "this one". Covers both grids — the timesheet's entries and the
   inventory's assets and stock. */
.bt-tsheet__row[data-entry],
.bt-tsheet__row[data-rec],
.bt-tsheet__row[data-person],
.bt-tsheet__row[data-ts-client],
.bt-tsheet__row[data-queue-entry],
.bt-tsheet__row[data-tsc-entry] { cursor: pointer; }
.bt-tsheet__row[data-entry] td:first-child,
.bt-tsheet__row[data-rec] td:first-child,
.bt-tsheet__row[data-person] td:first-child {
  border-radius: var(--bt-radius-cell) 0 0 var(--bt-radius-cell);
}
.bt-tsheet__row[data-entry] td:last-child,
.bt-tsheet__row[data-rec] td:last-child,
.bt-tsheet__row[data-person] td:last-child {
  border-radius: 0 var(--bt-radius-cell) var(--bt-radius-cell) 0;
}
.bt-tsheet__row[data-entry]:hover td,
.bt-tsheet__row[data-rec]:hover td,
.bt-tsheet__row[data-person]:hover td,
.bt-tsheet__row[data-ts-client]:hover td,
.bt-tsheet__row[data-queue-entry]:hover td,
.bt-tsheet__row[data-tsc-entry]:hover td { background: var(--bt-surface-nested); }
.bt-tsheet__row[data-ts-client]:focus-visible,
.bt-tsheet__row[data-queue-entry]:focus-visible,
.bt-tsheet__row[data-tsc-entry]:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: -2px; }
.bt-tsheet__row[data-person]:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: -2px; }
/* The name and whatever qualifies it — Auto, Needs review — on one line, so a
   generated entry is two lines like every other row instead of three. */
.bt-tsheet__title {
  display: flex;
  flex-wrap: wrap;
  min-width: 0;
  align-items: center;
  gap: 6px;
}
.bt-tsheet__clients { min-width: 0; font-weight: 600; }
/* Whatever a row offers, right-aligned on one baseline at one height. A badge,
   a button and a file count used to sit here at three different sizes. */
.bt-tsheet__actions { text-align: right; }
.bt-tsheet__actions .bt-cluster { align-items: center; justify-content: flex-end; }
/* One height for both. A 22px badge beside a 28px button is two controls that
   never quite line up however they are aligned. */
.bt-tsheet__actions .bt-badge { min-height: var(--bt-control-xs); padding-inline: 10px; }
/* A heading over a table sits above its own first column, not flush to the
   panel: every cell under it carries 10px of its own. */
.bt-tsheet__caption { margin: 0 0 10px; padding-left: 10px; }

/* By person and By client are two answers to two questions, not one table that
   changes its mind halfway down — which is how they read when identical
   columns ran straight on from each other. Each gets its own ground, its own
   air, and a rule between them. */
.bt-report-section { margin-top: 24px; }
.bt-report-section + .bt-report-section {
  margin-top: 32px;
  padding-top: 28px;
  border-top: 1px solid var(--bt-line);
}
.bt-tsheet__summary {
  display: block;
  max-width: 320px;
  margin-top: 2px;
  color: var(--bt-text-secondary);
  font-size: 12px;
}
.bt-tsheet__files {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  color: var(--bt-text-secondary);
  font-size: 12px;
}
/* A quantity is the status badge at one width, so a column of them lines up. */
.bt-qty { min-width: 32px; justify-content: center; font-variant-numeric: tabular-nums; }
/* A selected row, and a box with a 44px target though it only draws 20px. */
.bt-tsheet__row.is-selected td,
.bt-tsheet__row.is-selected:hover td { background: var(--bt-accent-soft); }
.bt-invcheck .bt-check::after { content: ''; position: absolute; inset: -12px; }
.bt-tsheet tfoot td {
  padding: 12px 10px 4px;
  border-top: 2px solid var(--bt-line);
  font-weight: 700;
}
/* The week's total is a band of its own, tinted apart from the grey day bands
   above it, so the figures somebody is paid from are found at a glance. */
.bt-tsheet--week tfoot td {
  padding-block: 14px;
  border-top: 0;
  background: var(--bt-accent-soft);
}
.bt-tsheet--week tfoot td:first-child {
  border-radius: var(--bt-radius-sm) 0 0 var(--bt-radius-sm);
  /* "Week total · 6 entries" in full, not cut off at the Customer column. */
  overflow: visible;
  white-space: nowrap;
  text-overflow: clip;
}
.bt-tsheet--week tfoot td:last-child { border-radius: 0 var(--bt-radius-sm) var(--bt-radius-sm) 0; }

/* ------------------------------------------------------------ entry dialog */
/* One visit: what somebody types on the left, what the portal already knows on
   the right. It used to be a 620px column with a 150px right-aligned label
   gutter and its own paddings — a second form system, on its own rhythm,
   beside every other form in the application. This is .bt-field/.bt-form-grid
   like the rest, on the 8/16/24 spacing the rest keeps. */
.bt-sheet--wide { max-width: 620px; }
.bt-sheet--entry { max-width: 720px; }
/* Wide only when there is something on the right worth the width: a generated
   entry carries figures it was written from and the road it was driven on. A
   typed one is a form, and a form does not get wider by having nothing beside
   it. */
.bt-sheet--entry.is-wide { max-width: 1040px; }
/* Wider again once What happened gets its own column beside the rest of the
   rail rather than stacking under it — the extra width is exactly what that
   column and the gap beside it need. */
.bt-sheet--entry.is-wide:has(.bt-entry-story-panel:not([hidden])) { max-width: 1376px; }

.bt-entry-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 24px;
  align-items: start;
  padding: 8px 10px;
}
.bt-sheet--entry.is-wide .bt-entry-body { grid-template-columns: minmax(0, 1fr) 320px; }
/* The rail widens to hold two columns of its own — the usual panels, and What
   happened beside them — only once there is a visit log to show there. */
.bt-sheet--entry.is-wide .bt-entry-body:has(.bt-entry-story-panel:not([hidden])) {
  grid-template-columns: minmax(0, 1fr) 656px;
}
.bt-entry-form { gap: 16px; align-content: start; }
/* Date and time side by side while each has room for its whole value (the
   year, and AM/PM), stacked when the column is narrower than that. A fixed
   split cut off one or the other at some width. */
.bt-entry-stamp { display: grid; grid-template-columns: repeat(auto-fit, minmax(136px, 1fr)); gap: 8px; }
.bt-entry-form .bt-gallery { margin-top: 2px; }
/* Open job is a way out of the dialog, so it looks like a button, beside the
   Job label, with room above and below it rather than squeezed into the row. */
.bt-entry-open-job { margin-block: 4px; }
/* A job the entry cannot change: the dropdown's shape, but a button to the
   job, its arrow pointing the way out. */
#tme-job-field.is-locked > .bt-select,
#tme-job-field.is-locked > .bt-select-enhanced { display: none; }
.bt-entry-job-link {
  display: flex;
  width: 100%;
  min-height: var(--bt-control-md);
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 0 12px 0 14px;
  border: 1px solid var(--bt-field-line);
  border-radius: 12px;
  background: var(--bt-surface-field);
  color: var(--bt-text-primary);
  font: inherit;
  font-weight: 500;
  text-align: left;
  cursor: pointer;
  transition: background-color var(--bt-duration-sm) var(--bt-ease);
}
.bt-entry-job-link:hover { background: var(--bt-surface-interactive); }
.bt-entry-job-link:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 1px; }
.bt-entry-job-link > .bt-icon { flex: 0 0 auto; color: var(--bt-text-secondary); }

/* The right-hand column: each thing the portal knows in its own panel, so the
   clock, the measured figures and the map are three answers rather than one
   run-on list. */
.bt-entry-rail { display: grid; gap: 16px; align-content: start; }
.bt-entry-rail__info { display: grid; gap: 16px; align-content: start; min-width: 0; }
.bt-entry-rail__info > .bt-entry-panel { min-width: 0; }
/* What happened, as its own column rather than a fourth panel stacked under
   the route: it starts level with Time on site and — because it stretches to
   the height its sibling column's own panels add up to, then scrolls the
   rest of itself internally — never reaches past the bottom of Route
   recorded. */
.bt-entry-rail:has(.bt-entry-story-panel:not([hidden])) {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: stretch;
}
.bt-entry-story-panel { min-width: 0; min-height: 0; grid-template-rows: auto minmax(0, 1fr); }
/* Side by side, the rail runs the full height of the form on its left and no
   further. `contain: size` stops the timeline's own length from sizing the
   row (min-height alone does not, so a long visit used to stretch the column
   down the dialog); it takes the row's height instead and scrolls inside it. */
.bt-sheet--entry.is-wide .bt-entry-rail:has(.bt-entry-story-panel:not([hidden])) {
  align-self: stretch;
  align-content: stretch;
}
.bt-sheet--entry.is-wide .bt-entry-story-panel { contain: size; }
.bt-entry-panel {
  display: grid;
  gap: 10px;
  align-content: start;
  padding: 16px;
  border-radius: var(--bt-radius-panel);
  background: var(--bt-surface-nested);
}

/* Reimbursements claimed on the finish-visit sheet (jobtravel.js): one card
   per receipt, meal or other. */
.bt-claims { display: grid; gap: 8px; }
.bt-claims:empty { display: none; }
.bt-claims__add { display: flex; flex-wrap: wrap; gap: 8px; }
.bt-claim {
  display: grid;
  gap: 8px;
  padding: 16px;
  border-radius: var(--bt-radius-panel);
  background: var(--bt-surface-nested);
}
.bt-claim__head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.bt-claim__rules { font-size: 13px; line-height: 20px; color: var(--bt-text-secondary); }
.bt-claim__rules p { margin: 0 0 8px; }
.bt-claim__rules p:last-child { margin-bottom: 0; }
.bt-claim__rules strong { color: var(--bt-text-primary); }
.bt-claim__rules ul { margin: 0 0 8px; padding-left: 20px; list-style: disc; }
.bt-claim__receipt { display: flex; align-items: center; gap: 8px; min-width: 0; }
.bt-claim__file { display: inline-flex; align-items: center; gap: 4px; min-width: 0; }
/* What a receipt on a saved entry was claimed for, under its tile. */
.bt-gallery__claim {
  display: block;
  margin-top: 4px;
  font-size: 12px;
  line-height: 16px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--bt-text-secondary);
}
.bt-entry-total {
  font-family: var(--bt-font-display);
  font-size: 28px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 32px;
}
/* Stamps that cannot be a visit are a correction to make, not a figure to
   read, so this stops being a display number while it says so. */
.bt-entry-total__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.bt-entry-total.is-bad {
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  line-height: 18px;
  color: var(--bt-danger-text);
}
/* The recorded figures get the panel's own width for their labels rather than
   .bt-kv's default 92px — "Miles driven (there and home)" over three lines
   beside a one-line value reads as damage. */
.bt-entry-rail .bt-kv { grid-template-columns: minmax(0, 1fr) auto; gap: 12px; }
.bt-entry-rail .bt-kv__v { text-align: right; font-variant-numeric: tabular-nums; }
.bt-entry-rail__err {
  margin-top: -4px;
  color: var(--bt-danger-text);
  font-size: 12px;
  font-weight: 600;
  line-height: 17px;
}
.bt-entry-map { height: 240px; overflow: hidden; border-radius: var(--bt-radius-cell); }

/* The two drives, side by side. They are two different facts about a visit —
   how long it took to reach the customer, and how long it took to get home —
   and whether the second one is paid for is a settings question, so a single
   line could never have reported both. */
.bt-entry-drives { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.bt-entry-drive {
  display: grid;
  gap: 2px;
  padding: 10px 12px;
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
}
.bt-entry-drive__label {
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.bt-entry-drive__value {
  font-size: 18px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 24px;
}
.bt-entry-drive__sub {
  color: var(--bt-text-secondary);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  line-height: 16px;
}

/* The customer on an entry: their mark, their name, and where they are. A
   search box lived here permanently for a field that is almost always already
   right, because the visit came off a job that named them. */
.bt-entry-clients { display: grid; gap: 6px; }
.bt-entry-client {
  display: flex;
  gap: 10px;
  align-items: center;
  min-height: 52px;
  padding: 6px 8px 6px 6px;
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
}
.bt-entry-client__text { display: grid; min-width: 0; flex: 1 1 auto; }
.bt-entry-client__name { font-size: 14px; font-weight: 600; line-height: 19px; }
.bt-entry-client__where {
  color: var(--bt-text-secondary);
  font-size: 12px;
  line-height: 16px;
}
/* Breaks on a generated entry: one row per break, styled like the customer
   row above it rather than like a bare form field — this is a measurement
   being corrected, not a fresh field being filled in. */
.bt-entry-breaks { display: grid; gap: 6px; }
.bt-entry-break {
  display: flex;
  gap: 8px;
  align-items: center;
  padding: 6px 8px 6px 10px;
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
}
.bt-entry-break .bt-input-shell { flex: 1 1 0; min-width: 0; }
.bt-entry-break__to { flex: 0 0 auto; color: var(--bt-text-secondary); font-size: 12px; }
/* What happened on the visit: the job log's own timeline (.bt-event), in the
   entry rail. It scrolls inside its panel rather than growing the dialog —
   a long afternoon must not push Save off the bottom of the sheet. */
.bt-entry-story { max-height: 320px; min-height: 0; padding: 0; }
/* Stretched to the height of the info column beside it, this scrolls on its
   own account rather than the fixed cap the stacked/mobile layout uses. */
.bt-entry-rail:has(.bt-entry-story-panel:not([hidden])) .bt-entry-story { max-height: none; }
.bt-event__detail {
  margin-top: 1px;
  color: var(--bt-text-secondary);
  font-size: 12px;
  line-height: 17px;
  overflow-wrap: anywhere;
}

.bt-entry-legend { display: grid; gap: 6px; }
/* A drive's facts wrap rather than hold the panel open to one line's width:
   unwrapped, they pushed Route recorded over the What happened column. */
.bt-entry-legend__text { min-width: 0; }
.bt-entry-legend__row {
  display: flex;
  gap: 8px;
  align-items: center;
  color: var(--bt-text-secondary);
  font-size: 12px;
  line-height: 17px;
}

/* The question the delete button asks, in the dialog it belongs to. */
.bt-entry-confirm {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  margin-top: 12px;
  padding: 12px 14px;
  border-radius: var(--bt-radius-cell);
  background: var(--bt-danger-soft);
  color: var(--bt-danger-text);
  font-size: 13px;
  line-height: 18px;
}
.bt-entry-confirm > span { flex: 1 1 220px; }

@media (max-width: 900px) {
  /* No room for two columns: the form comes first and what the portal knows
     follows it, which is the order the same screen keeps on a phone. */
  .bt-sheet--entry.is-wide .bt-entry-body,
  .bt-sheet--entry.is-wide .bt-entry-body:has(.bt-entry-story-panel:not([hidden])) {
    grid-template-columns: minmax(0, 1fr);
  }
  /* And within the rail itself: What happened goes back to stacking under the
     route, its own scroll cap restored, rather than sitting in a column with
     no room left beside it. */
  .bt-entry-rail:has(.bt-entry-story-panel:not([hidden])) {
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
  }
  .bt-entry-story-panel { grid-template-rows: none; }
  .bt-sheet--entry.is-wide .bt-entry-story-panel { contain: none; }
  .bt-sheet--entry.is-wide .bt-entry-rail:has(.bt-entry-story-panel:not([hidden])) { align-self: start; }
  .bt-entry-rail:has(.bt-entry-story-panel:not([hidden])) .bt-entry-story { max-height: 320px; }
}
@media (max-width: 560px) {
  .bt-entry-form { grid-template-columns: minmax(0, 1fr); }
  .bt-entry-body { padding: 8px 2px; gap: 16px; }
}

/* A chip you can take back off — the customers on an entry. */
.bt-chip__x {
  display: inline-grid;
  width: 16px;
  height: 16px;
  margin-left: 4px;
  margin-right: -4px;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: inherit;
  cursor: pointer;
  vertical-align: -3px;
}
.bt-chip__x:hover { background: var(--bt-surface-interactive); }

.bt-sheet__cancel--danger { background: var(--bt-danger-soft); color: var(--bt-danger-text); }

/* A count that is also a way of getting to what it counts — so it is sized
   like something you press rather than like a label that happens to be
   clickable. A square hit area whatever the number, the figure centred in it,
   and the same 28px the controls beside it keep. */
button.bt-badge--button {
  display: inline-grid;
  width: var(--bt-control-xs);
  height: var(--bt-control-xs);
  padding: 0;
  place-items: center;
  border: 0;
  border-radius: var(--bt-radius-cell);
  cursor: pointer;
  font: inherit;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  line-height: 1;
}
button.bt-badge--button:hover { background: var(--bt-warning); color: var(--bt-canvas); }
button.bt-badge--button:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 2px; }

/* What an entry is, said above the fields that describe it: measured rather
   than typed, closed, or flagged for somebody to look at. */
.bt-entry-notes { display: grid; gap: 6px; padding: 4px 10px 0; }
.bt-entry-note__action { margin-left: auto; flex: 0 0 auto; }

/* An unread count: the bell's red pill, sized to sit inline in a tab or a row. */
.bt-unread {
  display: inline-block;
  min-width: 17px;
  height: 17px;
  margin-left: 6px;
  padding: 0 5px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-danger);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 17px;
  text-align: center;
  vertical-align: 1px;
}
.bt-unread[hidden] { display: none; }
.bt-thread-badge { display: inline-flex; align-items: center; gap: 4px; }

/* DETAILS | CONVERSATION on a time entry. */
.bt-entry-tabs { margin: 0 10px 4px; align-self: flex-start; }
.bt-entry-conv { display: flex; flex-direction: column; gap: 10px; padding: 4px 10px 0; min-height: 320px; }
.bt-entry-conv[hidden] { display: none; }
.bt-entry-body[hidden] { display: none; }
.bt-entry-conv__status { display: flex; align-items: center; gap: 8px; color: var(--bt-text-secondary); font-size: 13px; }
.bt-entry-conv__list {
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-height: min(52vh, 520px);
  min-height: 160px;
  padding: 12px;
  overflow-y: auto;
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
}
.bt-entry-conv__event { align-self: center; color: var(--bt-text-disabled); font-size: 12px; }
.bt-entry-conv__decision {
  padding: 10px 12px;
  border-radius: var(--bt-radius-cell);
  background: var(--bt-positive-soft);
  color: var(--bt-text-primary);
}
.bt-entry-conv__decision-head {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--bt-positive-text);
  font-size: 12px;
  font-weight: 600;
}
.bt-entry-conv__composer { display: grid; gap: 8px; }
.bt-entry-conv__change {
  padding: 10px 12px;
  border-radius: var(--bt-radius-cell);
  background: var(--bt-warning-soft);
  color: var(--bt-text-primary);
}
.bt-entry-conv__change .bt-entry-conv__decision-head { color: var(--bt-warning-text); }
.bt-entry-conv__confirm {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 10px 12px;
  border-radius: var(--bt-radius-cell);
  background: var(--bt-warning-soft);
  color: var(--bt-warning-text);
  font-size: 13px;
}
.bt-entry-conv__confirm[hidden] { display: none; }
/* AUTO ADJUST, under the form's files. The heading carries the button; the
   reasons are a short list; each change is a cell with the figure now struck
   through and the corrected one under it, three across where there is room. */
.bt-entry-adjust { gap: 12px; border: 1px solid var(--bt-warning-soft); }
.bt-entry-adjust__head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.bt-entry-adjust__head .bt-overline { margin: 0; }
.bt-entry-adjust__why {
  display: grid;
  gap: 4px;
  margin: 0;
  padding-left: 18px;
  color: var(--bt-text-secondary);
  font-size: 13px;
  line-height: 19px;
}
.bt-entry-adjust__changes { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; }
.bt-entry-adjust__change {
  display: grid;
  gap: 2px;
  padding: 8px 10px;
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
  font-variant-numeric: tabular-nums;
}
.bt-entry-adjust__label { color: var(--bt-text-secondary); font-size: 11px; font-weight: 600; line-height: 16px; letter-spacing: 0.04em; text-transform: uppercase; }
.bt-entry-adjust__from { color: var(--bt-text-secondary); font-size: 12px; line-height: 17px; text-decoration: line-through; }
.bt-entry-adjust__to { color: var(--bt-text-primary); font-size: 14px; font-weight: 600; line-height: 20px; }
.bt-entry-conv__composer[hidden] { display: none; }
.bt-entry-conv__send { justify-content: flex-end; }
.bt-entry-note {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  padding: 8px 10px;
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
  color: var(--bt-text-secondary);
  font-size: 13px;
  line-height: 18px;
}
.bt-entry-note .bt-icon { flex: 0 0 auto; margin-top: 1px; color: var(--bt-text-secondary); }
/* A note with a button at its end ("Open conversation") is one line whose
   button is taller than the text: centre them, or the text sits at the top
   of the bar. A note without one keeps its icon at the first line. */
.bt-entry-note:has(> .bt-entry-note__action) { align-items: center; padding-block: 6px; }
.bt-entry-note:has(> .bt-entry-note__action) .bt-icon { margin-top: 0; }
.bt-entry-note--warning { background: var(--bt-warning-soft); color: var(--bt-warning-text); }
.bt-entry-note--warning .bt-icon { color: currentColor; }

/* Delete at the far end of the row from Save, and sized like what it is: one
   rare action, not a second commit. `.bt-sheet__actions .bt-button` stretches
   everything in this row, so this has to opt out by name. */
.bt-sheet__actions.bt-entry-actions { flex-wrap: wrap; align-items: center; justify-content: flex-end; }
/* Standard-size buttons at the right, not three stretched bars across the
   dialog. Delete keeps the far left (below). */
.bt-sheet__actions.bt-entry-actions .bt-button { flex: 0 0 auto; }
.bt-sheet__actions .bt-button.bt-entry-actions__delete { flex: 0 0 auto; margin-right: auto; }
@media (max-width: 480px) {
  /* Stacked, the delete would sit under Save again — put it first, where the
     hand going for the primary action never passes through it. It keeps its
     own small width: the margin is what pushes the pair onto the next line,
     rather than stretching this into the full-width red bar it replaced. */
  .bt-sheet__actions .bt-button.bt-entry-actions__delete { order: -1; margin-right: 100%; }
  /* On a phone-width screen the commits share the row again. */
  .bt-sheet__actions.bt-entry-actions .bt-button:not(.bt-entry-actions__delete) { flex: 1 1 0; }
}

/* A switch with its explanation beside it — the account editor's Timesheet. */
.bt-toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 10px 12px;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-cell);
  cursor: pointer;
}
.bt-toggle-row:hover { background: var(--bt-surface-nested); }
/* A switch for something this account can never have — Payroll on an applicant.
   Dimmed and out of reach rather than removed: a row that vanishes leaves somebody
   hunting for a setting they remember, and the tooltip on the row says why. */
.bt-toggle-row.is-disabled { opacity: .45; cursor: not-allowed; }
.bt-toggle-row.is-disabled:hover { background: transparent; }
.bt-toggle-row__label { display: block; font-size: 14px; font-weight: 600; }
.bt-toggle-row input[type="checkbox"] {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  accent-color: var(--bt-accent);
}

/* ---------------------------------------------------------------- notes save status */
.bt-notes-status {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
  min-height: 16px;
  color: var(--bt-text-disabled);
  font-size: 12px;
  font-weight: 500;
}
.bt-notes-status::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
}
.bt-notes-status.is-saving { color: var(--bt-text-secondary); }
.bt-notes-status.is-saved { color: var(--bt-positive-text); }
.bt-notes-status.is-dirty { color: var(--bt-warning-text); }
.bt-notes-status.is-error { color: var(--bt-danger-text); }

/* ---------------------------------------------------------------- responsive */
@media (max-width: 1180px) {
  /* Rail drops below the main content and spreads its widgets into a row. */
  .bt-shell {
    grid-template-columns: var(--bt-sidebar-width) minmax(0, 1fr);
    grid-template-rows: minmax(var(--bt-topbar-height), auto) auto auto;
  }
  .bt-topbar { grid-column: 2; }
  .bt-main { grid-column: 2; grid-row: 2; }
  .bt-rail { grid-column: 2; grid-row: 3; padding: 0 var(--bt-content-pad) 48px var(--bt-shell-gap); }
  /* Below the rail breakpoint it drops under the main column, full-width. */
  .bt-rail__inner { position: static; }
}

@media (max-width: 760px) {
  :root { --bt-topbar-height: 56px; }

  body { padding-bottom: 92px; }

  .bt-shell,
  .bt-shell.is-focused {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(var(--bt-topbar-height), auto) auto auto;
  }

  .bt-sidebar {
    position: fixed;
    z-index: 50;
    top: auto;
    right: auto;
    bottom: calc(10px + env(safe-area-inset-bottom));
    left: 50%;
    max-width: calc(100vw - 20px);
    flex-direction: row;
    align-items: center;
    height: auto;
    padding: 5px;
    border-radius: var(--bt-radius-round);
    background: var(--bt-dock);
    box-shadow: var(--bt-shadow-inset), var(--bt-shadow-popover);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    transform: translateX(-50%);
  }

  .bt-sidebar__brand { display: none; }
  /* An administrator's dock carries ten destinations and a phone is 375px
     wide, so the row cannot be made to fit — it has to be reachable instead.
     The nav scrolls sideways inside the dock; the profile button is its
     sibling rather than part of the scroller, so Account, Settings, Trash and
     Log out never scroll away from under the thumb. Without this the trailing
     destinations were simply clipped off the end, with nothing to say they
     were there. */
  .bt-nav {
    width: auto;
    min-width: 0;
    flex: 0 1 auto;
    flex-direction: row;
    gap: 2px;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .bt-nav::-webkit-scrollbar { display: none; }
  .bt-nav-item { scroll-snap-align: center; }
  /* As a bottom bar the highlight wraps the whole item, so there is no pill for
     the label to collide with — the gap here is only about not crowding the
     icon, and the height is what a thumb bar can afford. */
  .bt-nav-item { width: 70px; height: 56px; gap: 5px; border-radius: var(--bt-radius-round); }
  .bt-nav-item::before { inset: 0; border-radius: var(--bt-radius-round); }
  .bt-nav-item.is-active::before { background: var(--bt-surface-interactive); }
  .bt-nav-item .bt-icon { width: 19px; height: 19px; }

  .bt-mobile-profile {
    position: relative;
    display: grid;
    width: 54px;
    height: 54px;
    flex: 0 0 auto;
    place-items: center;
    margin-left: 2px;
    padding: 0;
    border: 0;
    border-radius: var(--bt-radius-round);
    background: transparent;
    color: var(--bt-text-secondary);
  }
  .bt-mobile-profile::before {
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: transparent;
    content: "";
  }
  .bt-mobile-profile:hover::before,
  .bt-mobile-profile.is-active::before { background: var(--bt-surface); }
  .bt-mobile-profile .bt-avatar { position: relative; width: 32px; height: 32px; }

  .bt-topbar,
  .bt-shell.is-focused .bt-topbar { grid-column: 1; grid-row: 1; padding: 0 14px; }
  /* The bar is full width here — there is no sidebar column to reach back
     under, so the backdrop starts at the edge. */
  .bt-topbar::before { left: 0; }
  .bt-topbar__heading { flex: 1 1 auto; }
  .bt-topbar__title,
  .bt-topbar__place,
  .bt-topbar__when b { font-size: 19px; }
  /* No room for arrows that do nothing: the phone has its own back gesture,
     so only the ones that lead somewhere stay. */
  .bt-topbar__nav .bt-icon-button:disabled { display: none; }
  .bt-topbar__nav { margin-left: -4px; }
  .bt-topbar .bt-user-pill { display: none; }

  .bt-main { grid-column: 1; grid-row: 2; padding: 14px 14px 20px; }
  .bt-rail { grid-column: 1; grid-row: 3; padding: 0 14px 24px; }
  .bt-page { gap: 14px; }

  /* Avoid iOS form-focus zoom, which otherwise makes the narrow layout jump. */
  .bt-input,
  .bt-select { font-size: 16px; }

  /* Give descriptive text room to breathe instead of forcing single-line
     ellipses into the narrow content column. */
  .bt-list-row__content > .bt-body-2.bt-truncate,
  .bt-job-row__meta.bt-truncate {
    display: -webkit-box !important;
    overflow: hidden;
    white-space: normal;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
  }
  .bt-job-row { gap: 8px; }
  .bt-job-row__progress { gap: 8px; max-width: none; }
  .bt-job-row__pct { min-width: 60px; }
  /* Chips bleed to the screen edges so the last one isn't clipped mid-scroll;
     the view controls sit under them rather than competing for the same row. */
  .bt-boardbar { flex-wrap: wrap; }
  .bt-filterbar {
    order: 2;
    margin-inline: -14px;
    padding-inline: 14px;
    scroll-padding-inline: 14px;
  }
  .bt-boardbar__actions { order: 1; flex-wrap: wrap; justify-content: flex-end; margin-left: auto; }
  .bt-pagination { gap: 6px; }
  .bt-pagination__size > span,
  .bt-pagination__summary,
  .bt-pagination__pages { display: none; }

  .bt-form-grid { grid-template-columns: minmax(0, 1fr); }
  .bt-schedule-inputs { grid-template-columns: minmax(0, 1fr) 120px; }
  .bt-picker__roles {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 2px;
  }
  .bt-picker__roles .bt-segmented__item {
    width: 100%;
    min-width: 0;
    padding-inline: 6px;
  }
  .bt-metric-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  .bt-perf-head { gap: 12px; padding: 16px 14px 4px; }
  .bt-perf-head__score .bt-score { font-size: 34px; }
  .bt-bench-row {
    grid-template-columns: minmax(0, 1fr) 66px 110px;
    gap: 8px;
  }
  .bt-bench-row__minutes { width: 110px; }
  .bt-matrix th.bt-matrix__row-head,
  .bt-matrix td.bt-matrix__row-head { min-width: 190px; }
  .bt-reveal { max-width: none; border-radius: var(--bt-radius-sheet) var(--bt-radius-sheet) 0 0; }
  .bt-reveal__score { font-size: 46px; }
  .bt-meta-grid { grid-template-columns: minmax(0, 1fr); }

  .bt-notif-pop { position: fixed; top: auto; bottom: calc(78px + env(safe-area-inset-bottom)); left: 12px; right: 12px; width: auto; }
  /* Stacked, the thread has a scroll cap of its own, so the pane is as tall
     as what it holds. Left at the screen's height it kept the space the
     cap took away, and the composer floated above an empty band. */
  .bt-conv { height: auto; max-height: none; }
  .bt-conv__body { max-height: 460px; }
  .bt-bulkbar { bottom: calc(78px + env(safe-area-inset-bottom)); }
}

@media (max-width: 480px) {
  .bt-panel__head { padding: 14px 14px 4px; }
  .bt-panel__lede { padding-inline: 14px; }
  .bt-panel__body { padding: 6px; }
  .bt-panel__body--pad { padding: 14px; }

  /* Inline admin actions are all available in the detail view; keeping only
     the disclosure chevron prevents them from crushing job information. */
  .bt-job-row__side .bt-icon-button { display: none; }

  .bt-list-row--static {
    grid-template-columns: auto minmax(0, 1fr);
    align-items: start;
  }
  .bt-list-row--static > .bt-list-row__side {
    grid-column: 1 / -1;
    justify-self: stretch;
    padding-left: 44px;
  }
  .bt-list-row--static > .bt-list-row__side .bt-button { flex: 1 1 auto; }

  .bt-cl-item {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 8px 10px;
  }
  .bt-cl-item .bt-st-chip {
    grid-column: 2;
    justify-self: start;
    margin-top: 0;
  }

  .bt-info-row { align-items: flex-start; }
}

@media (max-width: 360px) {
  .bt-nav-item { width: 62px; }
  .bt-mobile-profile { width: 50px; }
}

/* ---------------------------------------------------------------- followers
   Following a job sits beside its status as a small bell with a count: it is
   set once and then left alone, so it does not earn standing space in a pane
   that is read all day. Everything else lives in the popover it opens. */
/* Status and bell stay together on the left; the header's space-between then
   pushes only the actions to the right. */
.bt-jobhead__status { --bt-jobhead-control-height: 26px; display: flex; min-width: 0; flex-wrap: wrap; align-items: center; gap: 8px; }
.bt-jobhead__status :is(#dt-pause, #job-work-actions) > .bt-button { min-height: var(--bt-jobhead-control-height); }

.bt-watchbell { display: inline-flex; }
.bt-watchbell__btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: var(--bt-jobhead-control-height, 26px);
  padding: 0 8px;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-round);
  background: transparent;
  color: var(--bt-text-secondary);
  cursor: pointer;
  transition: background var(--bt-duration-sm) var(--bt-ease), color var(--bt-duration-sm) var(--bt-ease),
              border-color var(--bt-duration-sm) var(--bt-ease);
}
.bt-watchbell__btn:hover { background: var(--bt-surface); color: var(--bt-text-primary); }
/* Following is the state worth showing at a glance, so it is the accented one. */
.bt-watchbell__btn.is-following {
  border-color: var(--bt-accent);
  background: var(--bt-accent-soft);
  color: var(--bt-accent-text);
}
.bt-watchbell__count { font-size: 11px; font-weight: 700; line-height: 1; }

/* Fixed, not absolute, and positioned from JavaScript against the bell's own
   rectangle. The info pane is a scrolling column inside an overflow:hidden
   rail, so an absolutely-positioned panel is clipped twice over — the same
   reason the Team roster's account menu is fixed. */
.bt-watchpop {
  position: fixed;
  z-index: 75;
  width: 320px;
  max-width: calc(100vw - 24px);
  padding: 8px;
  border: 1px solid var(--bt-hairline);
  border-radius: var(--bt-radius-panel);
  background: var(--bt-surface-popover);
  box-shadow: var(--bt-shadow-popover);
  color: var(--bt-text-primary);
  font-family: var(--bt-font-interface);
  text-align: left;
  /* A column so the follower list takes whatever height is left after the
     choices and the search box, and scrolls inside the panel rather than
     running off the bottom of the window. max-height is set from JavaScript,
     against the room actually available below (or above) the bell. */
  display: flex;
  min-height: 0;
  flex-direction: column;
}
.bt-watchpop__choices { display: grid; flex: none; gap: 2px; }
.bt-watchpop__choice {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  width: 100%;
  padding: 8px;
  border: 0;
  border-radius: var(--bt-radius-sm);
  background: transparent;
  color: var(--bt-text-secondary);
  cursor: pointer;
  text-align: left;
}
.bt-watchpop__choice:hover { background: var(--bt-surface); color: var(--bt-text-primary); }
.bt-watchpop__choice.is-on { background: var(--bt-accent-soft); color: var(--bt-accent-text); }
.bt-watchpop__choicebody { flex: 1; min-width: 0; }
.bt-watchpop__choicetitle { display: block; font-size: 13px; font-weight: 600; }
.bt-watchpop__choicehint {
  display: block;
  margin-top: 1px;
  color: var(--bt-text-secondary);
  font-size: 11px;
  line-height: 1.35;
}
.bt-watchpop__choice.is-on .bt-watchpop__choicehint { color: inherit; opacity: 0.8; }
.bt-watchpop__tick { flex: none; margin-top: 2px; }

/* A note that belongs to both choices above it rather than to either one, so it
   sits outside them and is quieter than either. */
.bt-watchpop__note {
  margin: 2px 0 0;
  padding: 0 8px;
  color: var(--bt-text-secondary);
  font-size: 10.5px;
  line-height: 1.4;
}

/* .bt-input-shell supplies the rounded field, background and focus ring; this
   only has to size it for a compact popover and space it from the choices. */
.bt-watchpop__search {
  min-height: var(--bt-control-sm);
  flex: none;
  gap: 8px;
  margin: 10px 0 4px;
  padding: 0 12px;
}
.bt-watchpop__search .bt-icon { flex: none; }
.bt-watchpop__search .bt-input { font-size: 12.5px; }

.bt-watchpop__list { min-height: 0; flex: 1 1 auto; overflow-y: auto; }
.bt-watchpop__label {
  padding: 8px 6px 4px;
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.bt-watchpop__row {
  display: flex;
  gap: 8px;
  align-items: center;
  width: 100%;
  padding: 5px 6px;
  border: 0;
  border-radius: var(--bt-radius-sm);
  background: transparent;
  color: inherit;
  font-family: inherit;
  text-align: left;
}
.bt-watchpop__row--add { cursor: pointer; }
.bt-watchpop__row--add:hover, .bt-watchpop__row:hover { background: var(--bt-surface); }
.bt-watchpop__name { flex: 1; min-width: 0; font-size: 13px; font-weight: 500; }
.bt-watchpop__meta {
  display: block;
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 400;
}

/* ------------------------------------------------------- notification centre
   The bell's popover is a peek; this is the page you come to when there is
   more than a peek's worth. Rail of questions on the left, list on the right. */
.bt-notifcentre { display: grid; gap: 16px; grid-template-columns: minmax(0, 180px) minmax(0, 1fr); align-items: start; }
.bt-notifcentre__rail { position: sticky; top: calc(var(--bt-topbar-height) + 20px); display: grid; gap: 2px; }
.bt-notifcentre__railhead {
  margin-top: 10px;
  padding: 6px 10px 2px;
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.bt-notifcentre__filter {
  padding: 8px 10px;
  border: 0;
  border-radius: var(--bt-radius-sm);
  background: transparent;
  color: var(--bt-text-secondary);
  cursor: pointer;
  font-family: inherit;
  font-size: 13px;
  text-align: left;
}
.bt-notifcentre__filter:hover { background: var(--bt-surface); color: var(--bt-text-primary); }
.bt-notifcentre__filter.is-active { background: var(--bt-accent-soft); color: var(--bt-accent-text); font-weight: 600; }

.bt-notifcentre__main { min-width: 0; border-radius: var(--bt-radius-panel); background: var(--bt-surface); }
.bt-notifcentre__bar {
  display: flex;
  min-height: 44px;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  padding: 6px 12px;
  border-bottom: 1px solid var(--bt-line);
}
/* The select-all box and the count it explains travel together on the left; the
   actions stay pinned right. The 2px nudge puts the box on the same vertical
   line as the row checkboxes below it, which is the only thing that makes it
   read as their master rather than as one more control in the bar. */
.bt-notifcentre__barleft {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-right: auto;
  padding-left: 2px;
}
.bt-notifcentre__count { color: var(--bt-text-secondary); font-size: 12px; }
.bt-notifcentre__day {
  padding: 12px 14px 6px;
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.bt-notifcentre__more { padding: 12px; text-align: center; }

.bt-notifrow {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 10px 14px;
  border-bottom: 1px solid var(--bt-line);
}
.bt-notifrow:last-child { border-bottom: 0; }
.bt-notifrow:hover { background: var(--bt-surface-raised); }
.bt-notifrow.is-selected { background: var(--bt-accent-soft); }
.bt-notifrow__check { flex: none; margin-top: 6px; }
/* Unread is carried by a dot and a weight, not a background: a list where most
   rows are unread should not read as one solid block of colour. */
.bt-notifrow__dot {
  width: 7px;
  height: 7px;
  flex: none;
  margin-top: 13px;
  border-radius: 50%;
  background: var(--bt-accent);
}
.bt-notifrow.is-read .bt-notifrow__dot { background: transparent; }
.bt-notifrow.is-read .bt-notifrow__title { font-weight: 500; color: var(--bt-text-secondary); }
.bt-notifrow__body { display: grid; min-width: 0; gap: 2px; flex: 1; }
.bt-notifrow__title { font-size: 13px; font-weight: 600; }
.bt-notifrow__preview {
  overflow: hidden;
  color: var(--bt-text-secondary);
  font-size: 12px;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bt-notifrow__meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.bt-notifrow__job { color: var(--bt-text-secondary); font-size: 11px; font-weight: 600; }
.bt-notifrow__chip {
  padding: 1px 7px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-surface-raised);
  color: var(--bt-text-secondary);
  font-size: 10px;
  font-weight: 600;
}
.bt-notifrow__time { color: var(--bt-text-secondary); font-size: 11px; }
.bt-notifrow__actions { display: flex; flex: none; align-items: center; gap: 4px; }
/* Actions appear on hover on a pointer device; on touch they are always there,
   because there is no hover to reveal them with. */
@media (hover: hover) {
  .bt-notifrow__actions { opacity: 0; transition: opacity var(--bt-duration-sm) var(--bt-ease); }
  .bt-notifrow:hover .bt-notifrow__actions,
  .bt-notifrow:focus-within .bt-notifrow__actions { opacity: 1; }
}

@media (max-width: 900px) {
  .bt-notifcentre { grid-template-columns: minmax(0, 1fr); }
  /* The rail lies down into a scrolling row above the list, the same way the
     settings rail does. */
  .bt-notifcentre__rail {
    position: static;
    display: flex;
    overflow-x: auto;
    gap: 6px;
    scrollbar-width: none;
  }
  .bt-notifcentre__rail::-webkit-scrollbar { display: none; }
  .bt-notifcentre__filter { flex: none; white-space: nowrap; }
  .bt-notifcentre__railhead { display: none; }
  .bt-notifrow__preview { white-space: normal; }
}

/* The bell popover's way through to the full page. */
.bt-notif-pop__foot {
  padding: 6px;
  border-top: 1px solid var(--bt-line);
  text-align: center;
}


/* ===================================================== notification settings
   Settings → Notifications carries four related screens: the person's own
   grid, and — for whoever may edit them — the company's role policy, the
   auto-follow rules and the delivery log. They sit behind sub-tabs rather than
   in a separate corner of Admin, because "what do I get" and "what does my
   company give people" are one subject asked from two sides.

   All four notification tables use the full available page width. The settings
   section switcher applies is-wide only here; other settings keep their form width. */
.bt-page--settings.is-wide { max-width: none; }
/* One gutter for all four tabs. Every table in here already carries 4px of its
   own inside each row, so the body gives up the rest and the rows land on the
   same 16px line as the heading and the sub-tab labels above them. */
.bt-panel__body--wide { padding: 0 12px 16px; }
.bt-panel--flush > .bt-panel__body--wide { padding-top: 4px; }
/* The tab's opening sentence is part of the table it introduces, not a caption
   floating above it: same left edge, and enough air under the tab strip to read
   as a new screen rather than a stray line. An ID, because the panel-wide lede
   rule sets a gutter of its own and re-sets it again on a phone. */
#prefs-list > .bt-panel__lede {
  margin: 0 0 4px;
  padding: 16px 4px 0;
}
/* The heading and its explanation are one block; the badge sits opposite. */
.bt-panel__head--stack { align-items: flex-start; gap: 16px; }
.bt-panel__head--stack .bt-panel__lede { margin: 4px 0 0; }

.bt-subtabs {
  display: flex;
  overflow-x: auto;
  gap: 2px;
  /* Four short of the panel's own gutter, because the label inside carries
     twelve of its own: what has to line up with the heading above and the table
     below is the word, not the button drawn around it. */
  padding: 0 4px;
  border-bottom: 1px solid var(--bt-line);
  scrollbar-width: none;
}
.bt-subtabs::-webkit-scrollbar { display: none; }
.bt-subtabs__item {
  position: relative;
  padding: 10px 12px;
  border: 0;
  background: transparent;
  color: var(--bt-text-secondary);
  cursor: pointer;
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
}
.bt-subtabs__item:hover { color: var(--bt-text-primary); }
.bt-subtabs__item.is-active { color: var(--bt-text-primary); font-weight: 600; }
.bt-subtabs__item.is-active::after {
  position: absolute;
  right: 12px;
  bottom: -1px;
  left: 12px;
  height: 2px;
  border-radius: 2px 2px 0 0;
  background: var(--bt-accent);
  content: '';
}

/* ---- the person's sets of switches ---- */
.bt-prefsets { margin: 14px 0 6px; }
.bt-prefsets__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  justify-content: space-between;
}
.bt-prefsets__pick { display: flex; min-width: 0; align-items: center; gap: 8px; }
.bt-prefsets__picklabel {
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
/* Not the hidden native select the enhancer leaves behind: it is absolutely
   positioned, so a width given to it hangs off the side of the control it is
   standing in for and widens the bar for nothing. */
.bt-prefsets__pick .bt-select:not(.bt-select-native) { min-width: 160px; max-width: 260px; }
.bt-prefsets__actions { display: flex; align-items: center; gap: 6px; }
.bt-prefsets__actions .bt-button { display: inline-flex; align-items: center; gap: 5px; }
.bt-prefsets__actions .bt-inline-edit:disabled { cursor: default; opacity: 0.35; }
/* The naming form appears in place under the bar rather than in a dialog: it is
   one field, and a dialog for one field is a bigger interruption than the thing
   it asks for. */
.bt-prefsets__form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  padding: 10px;
  border: 1px solid var(--bt-line);
  /* Wider than the field it holds: an outer corner tighter than its inner one
     reads as a mistake. */
  border-radius: var(--bt-radius-panel);
  background: var(--bt-surface-nested);
}
.bt-prefsets__formlabel { color: var(--bt-text-secondary); font-size: 12px; font-weight: 600; }
.bt-prefsets__name { min-width: 0; flex: 1 1 200px; }
.bt-prefsets__name.bt-input-shell { min-height: var(--bt-control-sm); padding: 0 12px; }
.bt-prefsets__name .bt-input { font-size: 12.5px; }
.bt-prefsets__hint {
  margin: 8px 0 0;
  color: var(--bt-text-secondary);
  font-size: 11.5px;
  line-height: 1.5;
}
.bt-prefsets__hint strong { color: var(--bt-text-primary); font-weight: 600; }

/* ---- the person's own grid ---- */
/* No sideways scroll, at any width: the channel columns give up their room to
   the label first, and below the breakpoint further down the grid stops being a
   table altogether rather than being dragged off the side of the screen. */
.bt-prefgrid { display: grid; min-width: 0; }
.bt-prefgrid__row,
.bt-prefgrid__group {
  display: grid;
  align-items: center;
  gap: 12px;
  /* The label column takes the slack; channel columns stay a comfortable,
     equal width so the toggles line up down the page — and are allowed to
     shrink rather than overflow when there is less room than they would like. */
  grid-template-columns: minmax(0, 1fr) repeat(var(--bt-pref-cols, 4), minmax(0, 108px));
}
.bt-prefgrid__row { padding: 14px 4px; border-top: 1px solid var(--bt-line); }
.bt-prefgrid__row--head {
  /* Bottom-aligned, so a heading that wraps to two lines still sits on the
     same line as the rest instead of floating above them. */
  align-items: end;
  padding: 0 4px 10px;
  border-top: 0;
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
/* A group heading, with its "all" switches sitting in the channel columns they
   belong to rather than on a row of their own. */
.bt-prefgrid__group { display: block; padding: 22px 4px 6px; border-top: 1px solid var(--bt-line); }
.bt-prefgrid__group:first-of-type { border-top: 0; }
.bt-prefgrid__groupname { font-size: 12px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; }
/* A flex column, not a grid: an auto grid track sizes itself to the widest thing
   inside it and then hangs over the edge of the cell, which is what put a
   sideways scrollbar under this whole table. Every child is held to the width it
   is given instead. */
.bt-prefgrid__ch {
  display: flex;
  min-width: 0;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  text-align: center;
}
.bt-prefgrid__ch > * { max-width: 100%; min-width: 0; }
/* Transparent to the row's grid, so the cells sit in the channel columns exactly
   as they did before this wrapper existed. It becomes a real box only in the
   narrow layout further down. */
.bt-prefgrid__cells { display: contents; }
.bt-prefgrid__chname { overflow-wrap: anywhere; }
/* An unplugged lead, until the channel can actually deliver. It replaces the
   line of small print that used to sit under every switched-on toggle in the
   column saying the same sentence. */
.bt-prefgrid__chwarn { display: inline-grid; flex: none; color: var(--bt-text-disabled); place-items: center; }
.bt-prefgrid__chwarn:hover, .bt-prefgrid__chwarn:focus-visible { color: var(--bt-text-primary); }
/* The channel's name repeated inside its own cell. Only wanted once the grid has
   reflowed into stacked rows and the headings are no longer above it. */
.bt-prefgrid__chlabel { display: none; }
.bt-prefgrid__label { min-width: 0; font-size: 13px; font-weight: 600; }
.bt-prefgrid__ch--locked { color: var(--bt-text-secondary); }
.bt-prefgrid__lockstate { font-size: 10px; font-weight: 600; }

/* People tab: a switch changed away from the role carries a small reset under it. */
.bt-prefgrid__reset {
  display: inline-grid;
  width: 22px;
  height: 22px;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: var(--bt-radius-round);
  background: var(--bt-accent-soft);
  color: var(--bt-accent-text);
  cursor: pointer;
}
.bt-prefgrid__reset:hover { background: var(--bt-surface-nested); color: var(--bt-text-primary); }
.bt-prefgrid__reset:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 2px; }
.bt-people-notif__toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin: 14px 0 6px; }
.bt-people-notif__toolbar .bt-select { flex: 0 1 360px; min-width: 0; }
.bt-people-notif__summary { margin: 10px 0 4px; color: var(--bt-text-secondary); }

/* ---- the company's role policy ---- */
.bt-rolepick {
  display: flex;
  overflow-x: auto;
  gap: 4px;
  margin: 14px 0 6px;
  padding: 4px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-surface-nested);
  scrollbar-width: none;
}
.bt-rolepick::-webkit-scrollbar { display: none; }
.bt-rolepick__item {
  padding: 7px 14px;
  border: 0;
  border-radius: var(--bt-radius-round);
  background: transparent;
  color: var(--bt-text-secondary);
  cursor: pointer;
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
}
.bt-rolepick__item:hover { color: var(--bt-text-primary); }
.bt-rolepick__item.is-active { background: var(--bt-accent); color: var(--bt-ink); font-weight: 600; }

/* Sideways at any width rather than at a breakpoint: what this table needs is a
   number — a label column wide enough for the longest notification's name, and
   four columns wide enough for a switch and a lock — and a window either has it
   or it does not. Guessing at it with a media query got it wrong in the middle,
   where the settings rail is still taking 212px and the grid has stopped
   scrolling. */
.bt-policy { display: grid; }
/* The switch and its lock are 78px however wide the window is, so the channel
   columns are held near that and every pixel of the slack goes to the label —
   the only column here whose content is a sentence. Four equal fractions used to
   split the slack five ways and wrap "A job you follow is completed" onto three
   lines while the dropdowns beside it sat half empty. */
.bt-policy__row {
  display: grid;
  align-items: center;
  gap: 12px;
  min-width: 640px;
  padding: 8px 4px;
  border-top: 1px solid var(--bt-line);
  grid-template-columns: minmax(260px, 1fr) repeat(var(--bt-policy-cols, 4), minmax(80px, 124px));
}
.bt-policy__row--head {
  /* Bottom-aligned, so a heading that wraps still sits on the same line as the
     rest — the same rule the person's own grid follows. */
  align-items: end;
  padding: 0 4px 10px;
  border-top: 0;
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.bt-policy__row--head > span + span { text-align: center; }
.bt-policy__group {
  min-width: 640px;
  padding: 22px 4px 6px;
  border-top: 1px solid var(--bt-line);
  color: var(--bt-text-primary);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.bt-policy__group:first-of-type { border-top: 0; }
.bt-policy__type { min-width: 0; font-size: 13px; font-weight: 500; }
/* Switch and lock side by side — one decision with two parts, not two rows —
   centred under the channel they answer for, so a column reads down as cleanly
   as a row reads across. */
.bt-policy__cell {
  display: flex;
  min-width: 0;
  align-items: center;
  justify-content: center;
  gap: 8px;
}
.bt-policy__switch { flex: none; }
.bt-policy__lock {
  display: grid;
  width: var(--bt-control-sm);
  height: var(--bt-control-sm);
  flex: none;
  border: 1px solid var(--bt-line);
  border-radius: 9px;
  background: transparent;
  color: var(--bt-text-disabled);
  place-items: center;
  transition: color var(--bt-duration-sm) var(--bt-ease),
    border-color var(--bt-duration-sm) var(--bt-ease),
    background var(--bt-duration-sm) var(--bt-ease);
}
/* Locked is the state worth seeing from across the grid, so it is the accented
   one; an open lock keeps to the quietest ink there is, because on most rows it
   is saying nothing has been decided. */
.bt-policy__lock.is-on { border-color: var(--bt-accent); background: var(--bt-accent-soft); color: var(--bt-accent-text); }
/* Only the button is a button. The system's own lock is drawn with the same
   shape and takes none of the behaviour — a hover state that leads nowhere is a
   worse promise than the greyed-out control it replaced. */
button.bt-policy__lock { cursor: pointer; }
button.bt-policy__lock:hover { background: var(--bt-surface-nested); color: var(--bt-text-primary); }
button.bt-policy__lock:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 2px; }
.bt-policy__cell--system,
.bt-policy__cell--system .bt-toggle,
.bt-policy__cell--system .bt-toggle input { cursor: default; }
.bt-policy__cell--system .bt-toggle input:disabled + .bt-toggle__track { opacity: 0.55; }
.bt-policy__cell--system .bt-policy__lock { opacity: 0.75; }
.bt-policy__cell--note {
  color: var(--bt-text-disabled);
  font-size: 11.5px;
  gap: 5px;
  text-align: center;
}

/* All three notification grids (Yours, People, Role policy): the channel
   headings stay under the topbar while the rows scroll past, and the row under
   the pointer is tinted so a switch is easy to read back to its name.
   The heading is frosted glass like the topbar, not a solid bar: a solid one
   reads as a grey slab over a wallpaper the panel itself lets through. */
.bt-prefgrid__row--head,
.bt-policy__row--head {
  position: sticky;
  z-index: 3;
  top: var(--bt-topbar-height);
  padding-top: 10px;
}
/* The glass is a layer under the heading's text rather than the heading's own
   background, so it can reach up behind the topbar once stuck (is-stuck, set
   by notifications.js). Without that, rows show through the see-through
   topbar in the strip just above the stuck heading. */
.bt-prefgrid__row--head::before,
.bt-policy__row--head::before {
  position: absolute;
  z-index: -1;
  inset: 0;
  border-bottom: 1px solid var(--bt-line);
  background: color-mix(in srgb, var(--bt-canvas) 55%, transparent);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  backdrop-filter: blur(16px) saturate(140%);
  content: '';
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--bt-duration-sm) var(--bt-ease);
}
.bt-prefgrid__row--head.is-stuck::before,
.bt-policy__row--head.is-stuck::before {
  top: calc(-1 * var(--bt-topbar-height));
  opacity: 1;
}
.bt-prefgrid__row:not(.bt-prefgrid__row--head),
.bt-policy__row:not(.bt-policy__row--head) {
  border-radius: var(--bt-radius-sm);
  transition: background var(--bt-duration-sm) var(--bt-ease);
}
.bt-prefgrid__row:not(.bt-prefgrid__row--head):hover,
.bt-policy__row:not(.bt-policy__row--head):hover { background: var(--bt-surface-nested); }
/* When its 640px rows are wider than the panel (is-stacked, set by layout.js)
   the policy grid restacks rather than scroll sideways, where no heading could
   stick to the page: the type on a line of its own, then each channel's switch
   and lock with the channel's name above them. The heading row goes. */
.bt-policy.is-stacked .bt-policy__row--head { display: none; }
.bt-policy.is-stacked .bt-policy__group { min-width: 0; }
.bt-policy.is-stacked .bt-policy__row:not(.bt-policy__row--head) {
  display: flex;
  min-width: 0;
  flex-wrap: wrap;
  gap: 10px 4px;
  padding: 12px 4px;
}
.bt-policy.is-stacked .bt-policy__type { flex-basis: 100%; }
.bt-policy.is-stacked .bt-policy__cell {
  width: 92px;
  flex-wrap: wrap;
  row-gap: 6px;
}
.bt-policy.is-stacked .bt-policy__cell::before {
  flex-basis: 100%;
  color: var(--bt-text-secondary);
  content: attr(data-col);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-align: center;
}
/* Edge to edge of the panel, so the stuck heading is a band across it
   rather than a box floating inside it. The padding keeps the columns put. */
.bt-prefgrid__row--head,
.bt-policy__row--head { margin-inline: -16px; padding-inline: 20px; }
/* The info dot beside a type's name: what it means and an example. */
.bt-typeinfo {
  display: inline-grid;
  width: 18px;
  height: 18px;
  margin-left: 4px;
  vertical-align: -4px;
}

/* ---- follower rules ---- */
.bt-rules { display: grid; margin-top: 14px; }
.bt-rules__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 4px;
  border-top: 1px solid var(--bt-line);
}
.bt-rules__row:first-child { border-top: 0; }
.bt-rules__label { font-size: 13px; font-weight: 500; }

/* ---- delivery log ---- */
.bt-outbox { display: grid; overflow-x: auto; }
.bt-outbox__row {
  display: grid;
  gap: 12px;
  align-items: center;
  padding: 10px 4px;
  border-top: 1px solid var(--bt-line);
  /* Every column but the state badge takes a share of the slack. Capping the
     destination at 260px and the reason at 200px left both of them wrapping to
     three lines on a wide window while the middle of the row sat empty. The
     recipient is a name, so it takes a narrower share. */
  grid-template-columns: 84px 148px minmax(170px, 1.4fr) minmax(110px, 0.7fr) 132px minmax(100px, 1fr) 64px;
}
.bt-outbox__time { font-variant-numeric: tabular-nums; white-space: nowrap; }
.bt-outbox__row--head {
  padding: 0 4px 10px;
  border-top: 0;
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
/* A masked address and a push service's error are both unbreakable runs of
   characters; they wrap inside their own column rather than widening it. */
.bt-outbox__meta {
  color: var(--bt-text-secondary);
  font-size: 11.5px;
  line-height: 1.45;
  overflow-wrap: anywhere;
}
/* The connected-channel badges belong to the sentence above them, with the
   table's own breathing room underneath. Four of them do not fit across a phone,
   and a badge hanging off the side of the panel is worse than a second line. */
.bt-outbox__channels { flex-wrap: wrap; margin: 12px 0 16px; }
/* Search and the three filters on one line, wrapping on a narrow panel; the
   pager sits under them, above the table, so it never scrolls away. */
.bt-outbox__toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 12px; }
.bt-outbox__search { flex: 1 1 240px; min-width: 0; }
.bt-outbox__toolbar .bt-select { flex: 0 1 auto; min-width: 150px; width: auto; }
.bt-outbox__pager { display: flex; flex-wrap: wrap; justify-content: flex-end; margin-bottom: 8px; }
/* The retention limit sits at the end of the filters: a number, labelled in
   words, saved when it changes. */
.bt-outbox__limit { display: inline-flex; flex: 0 0 auto; align-items: center; gap: 8px; color: var(--bt-text-secondary); font-size: 13px; }
.bt-outbox__limit .bt-input-shell { width: 104px; }
.bt-outbox__limit .bt-input { font-variant-numeric: tabular-nums; }
.bt-outbox__limit-status { font-size: 12px; }
.bt-outbox__limit-status:empty { display: none; }
.bt-outbox__limit-status.is-error { color: var(--bt-danger-text); }
#outbox-results[aria-busy="true"] { opacity: 0.55; transition: opacity var(--bt-duration-sm) var(--bt-ease); }

/* A row's channels: one icon per outbound channel, always in the same place.
   Lit when it went out that way, red when it failed, dim when skipped or not
   used. The hover names where each one went. */
.bt-outbox__channels-icons { display: flex; align-items: center; gap: 6px; }
.bt-outbox__chicon {
  position: relative;
  display: inline-grid;
  width: 26px;
  height: 26px;
  flex: none;
  place-items: center;
  border-radius: var(--bt-radius-round);
  color: var(--bt-text-disabled);
}
.bt-outbox__chicon.is-none { opacity: 0.45; }
.bt-outbox__chicon.is-sent { background: var(--bt-positive-soft); color: var(--bt-positive-text); }
.bt-outbox__chicon.is-failed { background: var(--bt-danger-soft); color: var(--bt-danger-text); }
.bt-outbox__chicon.is-pending { background: var(--bt-accent-soft); color: var(--bt-accent-text); }
.bt-outbox__chicon.is-skipped { background: var(--bt-surface-nested); color: var(--bt-text-secondary); }
.bt-outbox__chicon:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 2px; }
.bt-outbox__chcount {
  position: absolute;
  top: -4px;
  right: -5px;
  min-width: 14px;
  padding: 0 3px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-surface-popover);
  color: var(--bt-text-primary);
  font-size: 9.5px;
  font-weight: 700;
  line-height: 14px;
  text-align: center;
}
/* The message dialog's channel tabs; each carries its channel's icon. */
.bt-outbox-message__tabs { flex-wrap: wrap; margin: 0 10px 12px; }
.bt-outbox-message__tabs .bt-subtabs__item { display: inline-flex; align-items: center; gap: 6px; }
.bt-outbox-message__tabs .bt-outbox__chicon { width: 20px; height: 20px; }

/* One delivery's message. An SMS or a push keeps its own line breaks; an email
   is its own HTML in a sandboxed frame, white like the mail app that shows it. */
.bt-outbox-message { padding: 4px 10px 8px; }
.bt-outbox-message__text {
  padding: 12px 14px;
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
  font-size: 14px;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.bt-outbox-message__email {
  display: block;
  width: 100%;
  height: 420px;
  max-height: 60vh;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-cell);
  background: #fff;
}

/* The delivery log scrolls sideways inside its own container rather than
   crushing its columns — it is a dense table read by scanning a row, and there
   is no honest way to stack one. The breakpoint is the width its five columns
   actually stop fitting at rather than a round number, because above it they
   share every spare pixel out between them. The policy grid does the same. */
@media (max-width: 760px) {
  .bt-outbox__toolbar .bt-select { flex: 1 1 140px; }
  .bt-outbox__pager { justify-content: flex-start; }
  /* The Jobs board hides these to share a line with its controls; this pager
     has a line of its own, so it keeps them. */
  .bt-outbox__pager .bt-pagination__summary,
  .bt-outbox__pager .bt-pagination__pages,
  .bt-outbox__pager .bt-pagination__size > span { display: inline-flex; }
  .bt-outbox { display: block; }
  .bt-outbox__row { min-width: 980px; }
  .bt-outbox__limit { flex: 1 1 100%; }
}

@media (max-width: 480px) {
  /* The phone's narrower panel gutter, kept in step with the head above it. */
  .bt-panel__body--wide { padding: 0 10px 14px; }
  #prefs-list > .bt-panel__lede { padding: 14px 4px 0; }
}

/* The person's own grid stops being a table once its room is too narrow for
   the columns (is-stacked, set by layout.js; on a phone, or beside the
   settings rail in a narrow window): the channels become a wrapping row of
   named switches underneath the thing they are about, which is the same
   information read down rather than across. */
.bt-prefgrid.is-stacked .bt-prefgrid__row,
.bt-prefgrid.is-stacked .bt-prefgrid__group { display: block; }
.bt-prefgrid.is-stacked .bt-prefgrid__row { padding: 14px 4px 16px; }
/* The headings have nothing left to head once the columns are gone, and each
   cell now carries its own name. */
.bt-prefgrid.is-stacked .bt-prefgrid__row--head { display: none; }
.bt-prefgrid.is-stacked .bt-prefgrid__cells {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 18px;
  margin-top: 12px;
}
.bt-prefgrid.is-stacked .bt-prefgrid__ch {
  min-height: 0;
  flex: 0 0 auto;
  align-items: flex-start;
  gap: 5px;
  text-align: left;
}
.bt-prefgrid.is-stacked .bt-prefgrid__chlabel {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--bt-text-secondary);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}


/* --------------------------------------------------------- small control set
   The dense tables above need controls a size below the form default. These
   follow the existing --xs / --md naming rather than inventing a scale. */
.bt-select--sm,
.bt-select--sm.bt-select-enhanced {
  min-height: var(--bt-control-sm);
  padding-right: 26px;
  padding-left: 9px;
  border-radius: 9px;
  font-size: 12px;
}
.bt-select--sm { background-position: right 7px center; background-size: 14px; }
.bt-button--sm { min-height: var(--bt-control-sm); padding-inline: 14px; font-size: 13px; }
/* The watcher popover's search field sits inside its own shell, so this only
   needs to set the height it lives at. */
.bt-input--sm { min-height: var(--bt-control-sm); font-size: 12.5px; }

/* The panel that holds the notification tabs: the head, the tab strip and the
   body each own their padding so the tabs can run the full width of the card. */
.bt-panel--flush { padding: 0; }
.bt-panel--flush > .bt-panel__head { padding: 16px 16px 0; }
.bt-panel--flush > .bt-panel__body { padding: 0 16px 16px; }

/* A size below the 16px icon, for the notes that sit inside a heading. */
.bt-icon--14 { width: 14px; height: 14px; }

/* The panel head spreads its children apart to push the badge right; the title
   and the dot that explains it have to travel together. */
.bt-panel__title { display: flex; min-width: 0; align-items: center; gap: 6px; }

/* ---------------------------------------------------------------- account sections */
/* In the technician app the Account page is two pages — Profile and Payroll —
   so it takes the rail Settings uses rather than inventing a second way of
   splitting a page. Same markup, same width arithmetic, and the same collapse
   to a scrolling row of links on a phone; only the entries differ. */
.bt-page--account { max-width: calc(var(--bt-profile-width) + 212px); }
/* An administrator in Admin has only Profile here — their payroll screen is a
   sidebar destination — and a rail with one link on it is not a rail. The page
   drops the empty first column and takes back the width one column wants. */
.bt-page--account.is-single { max-width: var(--bt-profile-width); }
/* Paystubs is a list with a button on every row, so it takes the width Settings
   takes for the notification grid rather than the form column. */
.bt-page--account.is-wide { max-width: none; }
.bt-page--account.is-single .bt-settings { grid-template-columns: minmax(0, 1fr); }

/* ---------------------------------------------------------------- payroll */
/* The section stacks its own panels, because it is mounted in two different
   places — a page of its own in Admin, a rail section in the technician app —
   and only one of those hands it a gap. */
.bt-payroll { display: flex; flex-direction: column; gap: 20px; }
/* The form is a single column of fields and reads badly stretched across a full
   page; the collection queue is a five-column table and wants the room. The
   module says which one it is drawing, so the same mount point suits both. */
.bt-payroll:not(.bt-payroll--managing) { max-width: var(--bt-narrow-width); }

/* Account and routing numbers are read a digit at a time, out loud, against a
   cheque — so they get tabular figures, where a 1 occupies the same width as an
   8 and two numbers of the same length line up under each other. */
.bt-mono {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  letter-spacing: 0.02em;
}

/* What the server is doing with your bank details, as three statements rather
   than a paragraph. Each one is a promise the rest of this feature keeps. */
.bt-assurance {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 4px 0;
  border-top: 1px solid var(--bt-line);
}
.bt-assurance__item {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 10px 0;
}
.bt-assurance__item + .bt-assurance__item { box-shadow: 0 -1px 0 var(--bt-line); }
.bt-assurance__icon {
  display: inline-grid;
  width: 28px;
  height: 28px;
  flex: 0 0 auto;
  place-items: center;
  border-radius: var(--bt-radius-sm);
  background: var(--bt-accent-soft);
  color: var(--bt-accent-text);
}
.bt-assurance__text { display: flex; min-width: 0; flex-direction: column; gap: 2px; }
.bt-assurance__title { font-size: 13px; font-weight: 600; line-height: 18px; }
.bt-assurance__blurb { color: var(--bt-text-secondary); font-size: 12px; line-height: 18px; }

/* The rows describing one submission, boxed so they read as one record rather
   than as a continuation of the panel around them. */
.bt-payroll-card {
  overflow: hidden;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
}
.bt-payroll-card .bt-info-row { padding: 10px 14px; }

/* The nothing-yet state carries the action, because on this page "add your bank
   details" is the only thing there is to do and hiding it in a panel head would
   put it further from the sentence that explains it. */
.bt-payroll-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 28px 20px;
}
.bt-payroll-empty__icon {
  display: inline-grid;
  width: 44px;
  height: 44px;
  place-items: center;
  border-radius: var(--bt-radius-round);
  background: var(--bt-surface);
  color: var(--bt-text-secondary);
}
.bt-payroll-empty__title { color: var(--bt-text-primary); font-size: 15px; font-weight: 600; }
.bt-payroll-empty .bt-button { margin-top: 8px; }

.bt-payroll-history { display: flex; flex-direction: column; }
.bt-payroll-history__row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 10px;
  padding: 10px 0;
}
.bt-payroll-history__row + .bt-payroll-history__row { box-shadow: 0 -1px 0 var(--bt-line); }
/* The date is the part that wraps; the account and its outcome stay together. */
.bt-payroll-history__row > .bt-body-3 { flex: 1 1 200px; }

.bt-payroll-person { display: flex; min-width: 0; align-items: center; gap: 10px; }
.bt-payroll-person__name { display: flex; min-width: 0; flex-direction: column; }

/* Person, account, action. The action column is sized to its button so the
   buttons line up down the list however long the names are. */
.bt-payroll-queue { display: flex; flex-direction: column; }
.bt-payroll-queue__row {
  display: grid;
  align-items: center;
  gap: 10px 16px;
  padding: 12px 0;
  /* The action column is the same width on every row, whichever action it
     holds, so the middle column starts at the same place down the list. */
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) minmax(128px, auto);
}
.bt-payroll-queue__row > :last-child { justify-self: end; }
.bt-payroll-queue__row + .bt-payroll-queue__row { box-shadow: 0 -1px 0 var(--bt-line); }
.bt-payroll-queue__meta { display: flex; min-width: 0; flex-direction: column; gap: 2px; }

/* Columns of dates, names and figures. The table gets the width it needs and
   scrolls inside its own box rather than making the page scroll sideways.
   Named for the job rather than for one feature: payroll's collection queue and
   Connected apps' Amazon report are the same table, and a second copy of these
   rules under a second name is how the two would drift apart. */
.bt-data-table-scroll { overflow-x: auto; }
.bt-data-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.bt-data-table th,
.bt-data-table td {
  padding: 10px 14px 10px 0;
  text-align: left;
  vertical-align: middle;
  white-space: nowrap;
}
/* Column headings match the board and the timesheet — the same uppercase
   micro-caps every other data table in the portal uses. */
.bt-data-table th {
  color: var(--bt-text-secondary);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.bt-data-table thead th { box-shadow: 0 -1px 0 var(--bt-line) inset; }
.bt-data-table tbody tr { box-shadow: 0 -1px 0 var(--bt-line); }
/* The clear button sits at the end of the row in a column just wide enough to
   hold it, so the columns that carry words keep theirs. */
.bt-data-table .bt-data-table__actions { width: 44px; padding-right: 0; text-align: right; }
.bt-data-table__actions > .bt-inline-edit { margin-left: auto; }
/* A column of figures is read down, not across: right-aligned so the digits
   share an edge, and tabular so they do not shuffle sideways between rows. */
.bt-data-table__num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
/* Prose in a table cell — an API's error, a GraphQL query — is the one thing
   here that must be allowed to wrap rather than push the table wider than the
   card. */
.bt-data-table td:last-child { white-space: normal; min-width: 220px; }

/* One revealed number: what it is, what it says, and a way to take it without
   retyping it. The value is the widest thing here and the one being read, so it
   takes the free space. */
.bt-payroll-reveal {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 14px;
  padding: 12px 14px;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
}
.bt-payroll-reveal__label {
  flex: 0 0 auto;
  color: var(--bt-text-secondary);
  font-size: 12px;
  font-weight: 600;
}
.bt-payroll-reveal__value {
  flex: 1 1 160px;
  font-size: 18px;
  font-weight: 600;
  overflow-wrap: anywhere;
}

/* The pill is sized to its words everywhere else it is used; here it is the
   gate on a destructive button directly below it, so it takes the full measure
   and reads as a step rather than as a chip floating beside one. */
.bt-payroll-confirm { display: flex; justify-content: flex-start; padding: 10px 14px; }
.bt-payroll-confirm .bt-tick__label { font-size: 14px; }

/* The segmented control, made out of real radio inputs so it is a radio group
   to a keyboard and a screen reader rather than only to the eye. */
.bt-segmented--choice .bt-segmented__item {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: 30px;
}
.bt-segmented--choice input {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.bt-segmented--choice input:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 2px; }

@media (max-width: 620px) {
  /* The button drops under the row rather than squeezing the name it belongs
     to down to an initial. */
  .bt-payroll-queue__row { grid-template-columns: minmax(0, 1fr); }
  .bt-payroll-queue__row > .bt-button,
  .bt-payroll-queue__row > .bt-badge { justify-self: start; }
}

/* Paystubs. Full width on either page — nothing here is a form — with one
   stub per row: what it is, how big, when it came, and the one button. */
.bt-paystubs { display: flex; flex-direction: column; gap: 20px; }
.bt-paystub-list { display: flex; flex-direction: column; }
.bt-paystub-row {
  display: grid;
  align-items: center;
  gap: 10px 20px;
  padding: 12px 0;
  grid-template-columns: minmax(0, 1fr) auto auto auto;
}
.bt-paystub-row + .bt-paystub-row { box-shadow: 0 -1px 0 var(--bt-line); }
.bt-paystub-row__what { display: flex; min-width: 0; flex-direction: column; gap: 2px; }
.bt-paystub-row__size,
.bt-paystub-row__when { white-space: nowrap; font-variant-numeric: tabular-nums; }
.bt-paystub-row__size { min-width: 56px; text-align: right; }
@media (max-width: 620px) {
  .bt-paystub-row { grid-template-columns: minmax(0, 1fr) auto; }
  .bt-paystub-row__size { display: none; }
  .bt-paystub-row__when { grid-column: 1; }
  .bt-paystub-row > .bt-button, .bt-paystub-row > .bt-badge { grid-row: 1 / span 2; grid-column: 2; }
}
/* One person per row on the administrator's list, opened for their stubs. */
.bt-paystub-people { display: flex; flex-direction: column; margin-top: 12px; }
.bt-paystub-person + .bt-paystub-person { box-shadow: 0 -1px 0 var(--bt-line); }
.bt-paystub-person__head {
  display: grid;
  align-items: center;
  gap: 10px 16px;
  padding: 12px 0;
  grid-template-columns: minmax(0, 1fr) auto auto;
  cursor: pointer;
  list-style: none;
}
.bt-paystub-person__head::-webkit-details-marker { display: none; }
.bt-paystub-person__head > .bt-icon { color: var(--bt-text-secondary); transition: transform 150ms ease; }
.bt-paystub-person[open] > .bt-paystub-person__head > .bt-icon { transform: rotate(180deg); }
.bt-paystub-person__body { padding: 0 0 8px 46px; }
@media (max-width: 620px) {
  .bt-paystub-person__head { grid-template-columns: minmax(0, 1fr) auto; }
  .bt-paystub-person__head > .bt-cluster { grid-column: 1 / -1; }
  .bt-paystub-person__body { padding-left: 0; }
}

/* ---------------------------------------------------------------- Orders
   The Amazon purchasing table. One order is one row and every column it has is
   on that row, which is wider than most windows — so the table scrolls
   sideways inside its own box, and the Columns menu above it is how somebody
   stops it needing to. Nothing here carries a fill: a row this wide is read
   across, and a band of colour behind the headings or under the cursor is one
   more thing crossing the line the eye is trying to follow. */
.bt-orders-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
}

/* Sideways only. A height of its own would want the headings stuck to the top
   of it, and a stuck heading needs something opaque behind it or the rows are
   read straight through it — so this box scrolls across and the page scrolls
   down. */
.bt-orders-scroll { overflow-x: auto; }

/* The shared rule lets a table's last column wrap as prose and gives it a
   floor to wrap within. Which column is last here is whichever one the reader
   left switched on, so every cell keeps the behaviour its own column asked
   for instead. */
.bt-orders-table td:last-child { min-width: 0; white-space: nowrap; }

.bt-orders-sort {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
  white-space: nowrap;
}
.bt-orders-sort:hover { color: var(--bt-text-primary); }
.bt-orders-sort.is-sorted { color: var(--bt-accent-text); }
.bt-orders-sort:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 2px; }
/* A numeric column's heading sits over the right-hand edge its digits share. */
.bt-data-table__num .bt-orders-sort { flex-direction: row-reverse; }

/* The columns allowed to run long. Clipped to a readable width with the whole
   of them on hover, rather than one order's fourteen line items pushing every
   column after it off the right of the window. */
.bt-orders-clip {
  display: inline-block;
  overflow: hidden;
  max-width: 240px;
  text-overflow: ellipsis;
  vertical-align: bottom;
  white-space: nowrap;
}

/* An address, or a row's tracking numbers, are worth two lines: wrapped
   rather than clipped, because half a postal address is not an address and
   a clipped tracking number is not one you can copy. Placed after the
   last-child rule above so it still wraps when it is the last column left. */
.bt-orders-table td.bt-orders-wrap {
  min-width: 180px;
  max-width: 260px;
  white-space: normal;
}

/* Tracking numbers, each its own copyable chip — a row can carry several,
   one per parcel, and pasting one into a carrier's own site is the next
   thing anyone does with it. */
.bt-tracking-list { display: flex; flex-wrap: wrap; gap: 6px; }
.bt-tracking {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 1px 4px 1px 8px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-surface-interactive);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.bt-tracking__num { font-size: 12px; }
.bt-tracking__copy {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  border-radius: var(--bt-radius-round);
  background: none;
  color: var(--bt-text-secondary);
  cursor: pointer;
}
.bt-tracking__copy:hover { color: var(--bt-accent-text); background: var(--bt-surface-popover); }
.bt-tracking__copy.is-copied { color: var(--bt-accent-text); }

/* A refresh keeps the rows it already has on screen rather than replacing them
   with a spinner. Dimmed so it is clear they are the previous answer. */
#view-orders.is-refreshing .bt-orders-scroll { opacity: 0.45; }
/* An order the last check with Amazon changed. */
.bt-orders-table tbody tr.is-changed td { background: var(--bt-accent-soft); }

@media (max-width: 900px) {
  .bt-orders-clip { max-width: 200px; }
  .bt-orders-table td.bt-orders-wrap { min-width: 150px; max-width: 220px; }
}

/* ---------------------------------------------------------------- trip pane
   jobtravel.js's controls, above the job facts in both applications. Built
   from existing tokens only, on purpose: the pane is a panel with a tint, not
   a new visual language; AUTO-TIMESHEET-PLAN.md §9 keeps this shared.
   The accent tint is what separates "the next thing to do" from the read-only
   facts under it without adding a heading nobody needs. */
.bt-trip {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px;
  border-radius: var(--bt-radius-cell);
  background: var(--bt-accent-soft);
  border: 1px solid var(--bt-line);
}
/* The buttons stack rather than sitting side by side: this pane lives in a
   ~300px column, and two full-width buttons on one row give each about eleven
   characters before the label wraps mid-word. */
.bt-trip .bt-button + .bt-button { margin-top: 2px; }

/* ---------------------------------------------------------------- voice agents
   Admin-only GPT-Live agents (voice-agents.js). Full width: one column on
   narrow screens, two on wide ones. Each tab is a grid of panels and column
   stacks; bt-voice-span runs a panel across both. Built from the shared panel, list,
   toggle-row and sheet primitives; only the pieces with no shared equivalent
   live here. */
.bt-page--voice { max-width: none; }
@media (min-width: 1280px) {
  .bt-voice-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; gap: 16px; }
  .bt-voice-grid[hidden] { display: none; }
  .bt-voice-grid > .bt-voice-span { grid-column: 1 / -1; }
}

/* Fields read as fields: the shared film alone disappears on a panel over the
   wallpaper, so every typing surface here gets an edge, and the bare textareas
   (prompts, knowledge, key terms) get the shell they otherwise lack. */
.bt-page--voice,
#va-sheet { --bt-voice-field-line: var(--bt-field-line); }
:root[data-theme="light"] .bt-page--voice,
:root[data-theme="light"] #va-sheet { --bt-voice-field-line: var(--bt-field-line); }
.bt-page--voice .bt-input-shell,
.bt-page--voice .bt-select,
#va-sheet .bt-input-shell,
#va-sheet .bt-select { border-color: var(--bt-voice-field-line); }
.bt-page--voice textarea.bt-input,
#va-sheet textarea.bt-input {
  display: block; padding: 11px 14px;
  border: 1px solid var(--bt-voice-field-line); border-radius: 12px;
  background: var(--bt-surface-field); line-height: 1.5;
  transition: border-color var(--bt-duration-sm) var(--bt-ease), background var(--bt-duration-sm) var(--bt-ease);
}
.bt-page--voice .bt-input-shell:focus-within,
.bt-page--voice .bt-select:focus,
.bt-page--voice textarea.bt-input:focus,
#va-sheet .bt-input-shell:focus-within,
#va-sheet .bt-select:focus,
#va-sheet textarea.bt-input:focus { border-color: var(--bt-accent); }
.bt-page--voice textarea.bt-input:focus,
#va-sheet textarea.bt-input:focus { background: var(--bt-surface); }
.bt-voice-toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.bt-voice-toolbar__search { flex: 1 1 240px; max-width: 420px; }
.bt-voice-toolbar .bt-voice-push { margin-left: auto; }
.bt-voice-preview { display: flex; align-items: center; gap: 8px; }
.bt-voice-preview__text { flex: 1 1 auto; min-width: 0; }
/* Selects in the toolbar match the round search box and buttons beside them. */
.bt-voice-toolbar .bt-select { width: auto; border-radius: var(--bt-radius-round); }
.bt-voice-toolbar__keep { display: inline-flex; align-items: center; gap: 8px; color: var(--bt-text-secondary); font-size: 13px; white-space: nowrap; }

.bt-voice-agent-head,
.bt-voice-agent-row { display: grid; grid-template-columns: 36px minmax(0, 1.4fr) minmax(0, 1fr) 90px 16px; align-items: center; gap: 12px; }
.bt-voice-agent-head { padding: 4px 8px 8px; color: var(--bt-text-secondary); font-size: 12px; font-weight: 600; }
.bt-voice-agent-head > :first-child { grid-column: 1 / 3; }
.bt-voice-agent-row + .bt-voice-agent-row { box-shadow: 0 -1px 0 var(--bt-line); }
.bt-voice-agent-row__icon {
  display: inline-flex; width: 36px; height: 36px; flex: 0 0 auto; align-items: center; justify-content: center;
  border-radius: var(--bt-radius-cell); background: var(--bt-accent-soft); color: var(--bt-accent-text);
}
.bt-voice-agent-row__main { display: grid; gap: 2px; }
.bt-voice-agent-row__name { display: flex; align-items: center; gap: 8px; min-width: 0; font-size: 14px; font-weight: 600; }
.bt-voice-agent-row__numbers,
.bt-voice-agent-row__updated { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.bt-voice-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.bt-voice-bar__actions { flex-wrap: wrap; justify-content: flex-end; }
.bt-voice-menu { position: relative; }
.bt-voice-menu .bt-menu-pop { top: calc(100% + 6px); right: 0; width: 200px; }
.bt-voice-tabs { align-self: flex-start; max-width: 100%; overflow-x: auto; scrollbar-width: none; }
.bt-voice-tabs::-webkit-scrollbar { display: none; }

.bt-voice-prompt { min-height: 120px; resize: vertical; line-height: 1.5; }
.bt-voice-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.bt-voice-chip.is-off { opacity: .55; text-decoration: line-through; }
.bt-voice-static {
  display: flex; min-height: var(--bt-control-md); align-items: center; padding: 0 12px;
  border-radius: var(--bt-radius-cell); background: var(--bt-surface-nested); color: var(--bt-text-secondary);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 13px;
}
.bt-voice-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.bt-voice-foot { margin: 8px 8px 4px; }
.bt-voice-tool-row { grid-template-columns: auto minmax(0, 1fr) auto; align-items: start; }
.bt-voice-tool-row > .bt-list-row__side { align-self: center; }
.bt-voice-tool-row__summary { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bt-voice-tool-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.bt-voice-tool-tag {
  max-width: 100%; padding: 2px 8px; overflow: hidden; border-radius: var(--bt-radius-round);
  background: var(--bt-surface-nested); color: var(--bt-text-secondary);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; line-height: 18px;
  text-overflow: ellipsis; white-space: nowrap;
}
.bt-voice-field-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.bt-voice-families { align-self: flex-start; max-width: 100%; overflow-x: auto; scrollbar-width: none; }
.bt-voice-families::-webkit-scrollbar { display: none; }
.bt-voice-tool-row__name { display: block; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 13px; font-weight: 600; }
.bt-voice-setting { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.bt-voice-setting > div { display: grid; gap: 2px; min-width: 0; }
.bt-voice-pron { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr) auto; align-items: center; gap: 8px; }
.bt-voice-param { display: grid; grid-template-columns: minmax(0, 1.3fr) repeat(3, minmax(0, .8fr)) auto; gap: 6px; align-items: center; padding: 8px; border: 1px solid var(--bt-line); border-radius: var(--bt-radius-cell); }
.bt-voice-param__desc { grid-column: 1 / -1; }
.bt-voice-method { display: grid; grid-template-columns: 120px minmax(0, 1fr); gap: 12px; }
.bt-voice-grow { flex: 1 1 auto; }
.bt-voice-push { margin-left: auto; }

.bt-voice-steps { display: grid; gap: 8px; margin: 0; padding-left: 20px; color: var(--bt-text-primary); font-size: 14px; line-height: 20px; }
.bt-voice-steps code,
.bt-voice-copy code { font-size: 12px; }
.bt-voice-copy-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.bt-voice-copy {
  display: flex; min-height: var(--bt-control-md); align-items: center; gap: 4px; padding: 0 4px 0 12px;
  border-radius: var(--bt-radius-cell); background: var(--bt-surface-nested);
}
.bt-voice-copy code { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.bt-voice-copy .bt-icon-button.is-done { color: var(--bt-positive-text); }
.bt-voice-checklist { display: grid; gap: 6px; }
.bt-voice-check { display: flex; align-items: center; gap: 8px; color: var(--bt-text-secondary); font-size: 13px; }
.bt-voice-check.is-ready { color: var(--bt-text-primary); }
.bt-voice-check.is-ready .bt-icon { color: var(--bt-positive-text); }
.bt-voice-check > .bt-hint { margin-left: auto; }

.bt-voice-call-head,
.bt-voice-call-row { display: grid; grid-template-columns: 200px 80px minmax(0, 1fr) minmax(0, 180px) 80px 120px; align-items: center; gap: 12px; }
.bt-voice-call-head { padding: 4px 88px 8px 8px; color: var(--bt-text-secondary); font-size: 12px; font-weight: 600; }
.bt-voice-call-item { display: flex; align-items: center; gap: 4px; padding-right: 4px; }
.bt-voice-call-item > .bt-voice-call-row { flex: 1 1 auto; min-width: 0; }
.bt-voice-call-item + .bt-voice-call-item { box-shadow: 0 -1px 0 var(--bt-line); }
/* Each row ends in Call back and, for an administrator, End call or Delete. */
.bt-voice-call-item__spacer { flex: 0 0 var(--bt-control-sm); }
.bt-voice-one-action .bt-voice-call-head { padding-right: 52px; }
.bt-voice-viewer [data-va-admin] { display: none !important; }
/* A viewer sees the agent list with voice_calls.view, Shared knowledge with knowledge.manage. */
.bt-voice-no-calls [data-va-calls], .bt-voice-no-kb [data-va-kb-box] { display: none !important; }
.bt-voice-call-row__time { font-size: 13px; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Room for a seven-digit call number. */
.bt-voice-call-row__id { overflow: hidden; color: var(--bt-text-secondary); font-size: 13px; font-variant-numeric: tabular-nums; white-space: nowrap; text-overflow: ellipsis; }
.bt-voice-call-row__caller { display: grid; gap: 2px; min-width: 0; font-size: 14px; font-weight: 600; }
.bt-voice-call-row__who { margin-left: 6px; color: var(--bt-text-secondary); font-weight: 500; }
.bt-voice-call-row__caller .bt-icon { display: inline; vertical-align: -3px; color: var(--bt-accent-text); }
.bt-voice-num { font-variant-numeric: tabular-nums; font-size: 13px; }

.bt-voice-insight-bar { justify-content: space-between; }
.bt-voice-chart { display: flex; align-items: stretch; gap: 4px; height: 180px; }
.bt-voice-bar-col { display: flex; flex: 1 1 0; min-width: 0; flex-direction: column; align-items: center; justify-content: flex-end; gap: 4px; }
.bt-voice-bar-col__stack { display: flex; width: 100%; max-width: 40px; min-height: 2px; flex-direction: column; overflow: hidden; border-radius: 4px 4px 0 0; }
.bt-voice-bar-col__bar { width: 100%; min-height: 2px; background: var(--bt-accent); }
.bt-voice-bar-col__bar--web { background: var(--bt-accent-soft); }
.bt-voice-legend { display: inline-flex; align-items: center; gap: 6px; color: var(--bt-text-secondary); font-size: 12px; }
.bt-voice-legend__key { width: 10px; height: 10px; margin-left: 6px; border-radius: 2px; background: var(--bt-accent); }
.bt-voice-legend__key--web { background: var(--bt-accent-soft); }
.bt-voice-actions { display: flex; flex-wrap: wrap; gap: 6px; }
.bt-voice-bar-col__n { color: var(--bt-text-secondary); font-size: 11px; font-variant-numeric: tabular-nums; }
.bt-voice-bar-col__label { min-height: 16px; color: var(--bt-text-secondary); font-size: 11px; white-space: nowrap; }
.bt-voice-chart__empty { flex: 1 1 auto; align-self: center; }

.bt-sheet.bt-sheet--voice-wide { max-width: 720px; }
.bt-voice-sheet-body { padding: 4px 10px 0; }
.bt-voice-sheet-error { margin: 0 10px 10px; }
.bt-voice-sheet-danger { margin-right: auto; }
#va-sheet-actions { padding: 0 10px; flex-wrap: wrap; }
#va-sheet-actions .bt-button { flex: 0 0 auto; }
#va-sheet-actions .bt-button--primary { min-width: 120px; }
.bt-sheet__opt:disabled { cursor: default; opacity: .5; }
.bt-sheet__opt:disabled:hover { background: transparent; }

/* The top of a conversation: its ID, the number and, when known, who it is. */
.bt-voice-call-who { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; font-size: 15px; }
.bt-voice-message { display: grid; gap: 4px; padding: 12px; border-radius: var(--bt-radius-cell); background: var(--bt-accent-soft); }
.bt-voice-transcript { display: flex; max-height: 52vh; flex-direction: column; gap: 8px; overflow-y: auto; }
.bt-voice-transcript--live { max-height: 240px; width: 100%; text-align: left; }
.bt-voice-turn { display: grid; gap: 2px; max-width: 80%; padding: 8px 12px; border-radius: var(--bt-radius-cell); background: var(--bt-surface-nested); font-size: 14px; line-height: 20px; }
.bt-voice-turn--caller { align-self: flex-end; background: var(--bt-accent-soft); }
.bt-voice-turn--tool { display: flex; align-items: center; gap: 6px; max-width: 100%; padding: 4px 8px; background: transparent; color: var(--bt-text-secondary); }
.bt-voice-turn--tool code { overflow-wrap: anywhere; font-size: 12px; }

.bt-voice-test { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 12px 0 4px; text-align: center; }
.bt-voice-test__as { width: 100%; text-align: left; }
.bt-voice-setting--sub { margin-left: 20px; }
.bt-voice-recording { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: var(--bt-radius-cell); background: var(--bt-surface-nested); color: var(--bt-accent-text); }
.bt-voice-recording audio { flex: 1 1 auto; min-width: 0; height: 36px; }
.bt-voice-turn--seek { cursor: pointer; transition: box-shadow var(--bt-duration-sm) var(--bt-ease); }
.bt-voice-turn--seek:hover,
.bt-voice-turn--seek:focus-visible { box-shadow: inset 0 0 0 1px var(--bt-accent); outline: none; }
.bt-voice-turn.is-playing { box-shadow: inset 0 0 0 2px var(--bt-accent); }
.bt-voice-orb {
  display: inline-flex; width: 72px; height: 72px; align-items: center; justify-content: center;
  border-radius: 50%; background: var(--bt-accent-soft); color: var(--bt-accent-text);
}
.bt-voice-orb.is-live { background: var(--bt-accent); color: var(--bt-ink); animation: bt-voice-pulse 1.6s var(--bt-ease) infinite; }
@keyframes bt-voice-pulse { 0%, 100% { box-shadow: 0 0 0 0 var(--bt-accent-soft); } 50% { box-shadow: 0 0 0 14px transparent; } }
@media (prefers-reduced-motion: reduce) { .bt-voice-orb.is-live { animation: none; } }

@media (max-width: 760px) {
  .bt-voice-agent-head,
  .bt-voice-call-head { display: none; }
  .bt-voice-agent-row { grid-template-columns: 36px minmax(0, 1fr) 16px; }
  .bt-voice-agent-row__numbers,
  .bt-voice-agent-row__updated { display: none; }
  .bt-voice-call-row { grid-template-columns: minmax(0, 1fr) auto; row-gap: 4px; }
  .bt-voice-call-row__time { color: var(--bt-text-secondary); }
  .bt-voice-call-row__id { justify-self: end; }
  .bt-voice-call-row > :nth-child(4) { display: none; }
  .bt-voice-call-row > :nth-child(6) { grid-column: 1 / -1; }
  .bt-voice-bar { align-items: flex-start; }
  .bt-voice-bar__actions { display: grid; width: 100%; grid-template-columns: auto 1fr 1fr; }
  .bt-voice-bar__actions > #va-save { grid-column: 1 / -1; }
  .bt-voice-copy-grid,
  .bt-voice-method { grid-template-columns: 1fr; }
  .bt-voice-setting { align-items: flex-start; flex-direction: column; gap: 8px; }
  .bt-voice-param { grid-template-columns: repeat(3, minmax(0, 1fr)) auto; }
  .bt-voice-param > :first-child { grid-column: 1 / -1; }
  .bt-voice-toolbar .bt-voice-push { margin-left: 0; }
  .bt-voice-turn { max-width: 92%; }
}

/* ---------------------------------------------------------------- app
   Account → App (apprelease.js): one panel per platform, side by side once
   there is room for two. Each holds its build, how to get it, and the
   administrator's tools for it (the Android upload, the folded Expo command). */
.bt-page--app { max-width: calc(var(--bt-profile-width) * 2 + 20px); }
.bt-app-release { gap: 20px; }
.bt-app-release__platforms { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 380px), 1fr)); gap: 20px; align-items: start; }
.bt-app-release__body { display: grid; gap: 16px; padding: 20px; }
.bt-app-release__hero { display: flex; align-items: center; gap: 14px; min-width: 0; }
.bt-app-release__tile {
  display: inline-flex;
  flex: none;
  width: 52px;
  height: 52px;
  align-items: center;
  justify-content: center;
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
  color: var(--bt-text-primary);
}
/* Android's own green; Apple's mark stays the text colour, as Apple draws it. */
.bt-app-release__tile--android { color: #3ddc84; }
.bt-app-release__title { display: grid; gap: 2px; min-width: 0; }
.bt-app-release__facts { padding: 12px 14px; border-radius: var(--bt-radius-cell); background: var(--bt-surface-nested); }
.bt-app-release__facts .bt-kv { grid-template-columns: 104px minmax(0, 1fr); }
.bt-app-release__actions { display: flex; flex-wrap: wrap; gap: 8px; }
.bt-app-release__hint { margin: 0; }
.bt-app-release__admin { display: grid; gap: 14px; padding-top: 16px; border-top: 1px solid var(--bt-line); }
.bt-app-release__admin-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-width: 0; }
.bt-app-release__admin-title { display: grid; gap: 2px; min-width: 0; }
.bt-app-release__fold { gap: 0; }
.bt-app-release__fold > summary { min-height: 44px; cursor: pointer; list-style: none; border-radius: var(--bt-radius-cell); }
.bt-app-release__fold > summary::-webkit-details-marker { display: none; }
.bt-app-release__fold > summary:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 2px; }
.bt-app-release__chevron { display: inline-flex; flex: none; color: var(--bt-text-secondary); transition: transform var(--bt-duration-sm) var(--bt-ease); }
.bt-app-release__fold > summary:hover .bt-app-release__chevron { color: var(--bt-text-primary); }
.bt-app-release__fold[open] .bt-app-release__chevron { transform: rotate(180deg); }
.bt-app-release__fold-body { display: grid; gap: 12px; padding-top: 12px; }
.bt-app-release__command { display: block; padding: 12px 14px; border-radius: var(--bt-radius-cell); background: var(--bt-surface-nested); overflow-wrap: anywhere; white-space: pre-line; user-select: all; }
.bt-tabular { font-variant-numeric: tabular-nums; }
.bt-app-drop {
  display: flex;
  min-height: 72px;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
  border: 1.5px dashed var(--bt-line);
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
  cursor: pointer;
  transition: border-color var(--bt-duration-sm) var(--bt-ease), background var(--bt-duration-sm) var(--bt-ease);
}
.bt-app-drop:hover,
.bt-app-drop.is-over,
.bt-app-drop:focus-within { border-color: var(--bt-accent); background: var(--bt-accent-soft); }
.bt-app-drop:focus-within { outline: 2px solid var(--bt-accent); outline-offset: 2px; }
.bt-app-drop.is-busy { cursor: default; opacity: 0.7; }
.bt-app-drop.is-busy:hover { border-color: var(--bt-line); background: var(--bt-surface-nested); }
.bt-app-drop__icon {
  display: inline-flex;
  flex: none;
  width: 40px;
  height: 40px;
  align-items: center;
  justify-content: center;
  border-radius: var(--bt-radius-round);
  background: var(--bt-surface);
  color: var(--bt-text-secondary);
}
.bt-app-drop__copy { display: grid; gap: 2px; min-width: 0; }
.bt-app-progress { height: 6px; overflow: hidden; border-radius: var(--bt-radius-round); background: var(--bt-surface-nested); }
.bt-app-progress__bar { display: block; height: 100%; border-radius: inherit; background: var(--bt-accent); transition: width var(--bt-duration-sm) var(--bt-ease); }
.bt-app-progress + .bt-hint { margin: -8px 0 0; }
@media (max-width: 480px) {
  .bt-app-release__body { padding: 16px; }
  .bt-app-release__actions .bt-button { flex: 1 1 auto; justify-content: center; }
}
@media (prefers-reduced-motion: reduce) {
  .bt-app-drop, .bt-app-progress__bar, .bt-app-release__chevron { transition: none; }
}

/* ============================================================
   Billing — which Wave customer each client is invoiced as.
   Two columns: the client, and the one select that links it. The select
   takes the column's width so a long Wave name reads in full. */
.bt-billing-table td { vertical-align: top; }
.bt-billing-table td:first-child { width: 40%; }
.bt-billing-client { font-weight: 600; color: var(--bt-text-primary); }
.bt-billing-select { width: 100%; max-width: 420px; }
.bt-billing-suggest { margin-top: 4px; }
/* A client with locations is a header that folds them, as on the job page. */
.bt-billing-head { display: flex; align-items: center; gap: 8px; cursor: pointer; }
.bt-billing-head > div:first-child { flex: 1 1 auto; min-width: 0; }
.bt-billing-fold__count { flex: 0 0 auto; }
.bt-billing-head:hover > .bt-pane-toggle { background: var(--bt-surface); color: var(--bt-text-primary); }
/* A sub-location sits under its client, set in so the group reads as one. */
.bt-billing-site td:first-child { padding-left: 32px; }
.bt-billing-separate { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; cursor: pointer; font-size: 14px; }
.bt-billing-separate + .bt-billing-select { display: block; }
/* A phone has no room for two columns, so each client stacks over its picker. */
@media (max-width: 760px) {
  .bt-billing-table thead { display: none; }
  .bt-billing-table,
  .bt-billing-table tbody { display: block; }
  .bt-billing-table tr { display: block; padding: 10px 0; }
  .bt-billing-table td,
  .bt-billing-table td:first-child,
  .bt-billing-table td:last-child { display: block; width: auto; min-width: 0; padding: 4px 0; border: 0; }
  .bt-billing-select { max-width: none; }
  .bt-billing-table tr.bt-billing-site { padding-left: 16px; }
}

/* PAID, on a person's week: ticked once payroll has paid the entry. */
.bt-tsheet__paid { text-align: center; }
.bt-tsheet--week th.bt-tsheet__paid, .bt-tsheet--week td.bt-tsheet__paid { width: 108px; }
/* Open is waiting on payroll, Paid is done: the job status picker's colours. */
.bt-status-select--open { background-color: var(--bt-warning-soft); color: var(--bt-warning-text); }
.bt-status-select--paid { background-color: var(--bt-positive-soft); color: var(--bt-positive-text); }

/* THEIR DAY: the order of a technician's visits on one day (dayorder.js). */
.bt-dayorder-host { display: grid; gap: 10px; margin-top: 10px; }
.bt-dayorder-host[hidden] { display: none; }
.bt-dayorder { display: grid; gap: 6px; padding: 10px; border-radius: var(--bt-radius-cell); background: var(--bt-surface-nested); }
.bt-dayorder__list { display: grid; gap: 4px; margin: 0; padding: 0; list-style: none; }
.bt-dayorder__item { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: var(--bt-radius-cell); background: var(--bt-surface); }
.bt-dayorder__item.is-self { background: var(--bt-accent-soft); }
.bt-dayorder__item.is-done { opacity: 0.6; }
.bt-dayorder__num { flex: 0 0 auto; min-width: 26px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--bt-accent-text); }
.bt-dayorder__body { display: grid; min-width: 0; flex: 1 1 auto; font-size: 13px; }
.bt-dayorder__body strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bt-dayorder__body small { color: var(--bt-text-secondary); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bt-dayorder__moves { display: flex; gap: 2px; flex: 0 0 auto; }
.bt-dayorder__moves .bt-icon-button { width: 30px; height: 30px; min-width: 30px; }
.bt-dayorder__moves .bt-icon-button:disabled { opacity: 0.35; }
/* The board: each person's place in their day, and requests waiting on the office. */
/* A small pill on the row's time line, never a line of its own. */
.bt-onsite-order {
  flex: 0 0 auto;
  padding: 0 7px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-accent-soft);
  color: var(--bt-accent-text);
  font-size: 11px;
  font-weight: 600;
  line-height: 18px;
  white-space: nowrap;
}
.bt-onsite-row__when { display: flex; min-width: 0; flex-wrap: wrap; align-items: center; gap: 2px 6px; }
/* The time is never cut short for the pill: on a narrow row the pill goes under it. */
.bt-onsite-row__when > .bt-truncate { flex: 0 1 auto; white-space: normal; }
.bt-skip-requests { display: grid; gap: 8px; margin-bottom: 12px; padding: 12px; border-radius: var(--bt-radius-cell); background: var(--bt-warning-soft); }
.bt-skip-requests[hidden] { display: none; }
.bt-skip-request { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px; padding: 8px 10px; border-radius: var(--bt-radius-cell); background: var(--bt-surface); }
.bt-skip-request__body { display: grid; gap: 2px; min-width: 0; }
/* The technician's side: today's order on the job rows, and the banner on a job that has to wait. */
.bt-order-num { display: inline-block; flex: 0 0 auto; margin-right: 6px; padding: 0 6px; border-radius: var(--bt-radius-round); background: var(--bt-accent-soft); color: var(--bt-accent-text); font-size: 11px; font-weight: 700; line-height: 18px; font-variant-numeric: tabular-nums; }
.bt-order-tag { display: inline-block; padding: 1px 8px; border-radius: var(--bt-radius-round); background: var(--bt-surface-nested); color: var(--bt-text-secondary); font-size: 11px; font-weight: 600; white-space: nowrap; }
.bt-order-tag.is-now { background: var(--bt-accent-soft); color: var(--bt-accent-text); }
.bt-job-row.is-day-now { box-shadow: inset 0 0 0 1px var(--bt-accent); }
.bt-job-row.is-day-done { opacity: 0.6; }
.bt-order-banner { display: grid; gap: 8px; padding: 12px; border-radius: var(--bt-radius-cell); background: var(--bt-warning-soft); color: var(--bt-warning-text); }
.bt-order-banner .bt-cluster { flex-wrap: wrap; }

/* The Edit account dialog's Location label shares its row with the Live
   location / Home address tabs. */
.bt-account-loc-head { justify-content: space-between; flex-wrap: wrap; margin-bottom: 6px; }
.bt-segmented__item:disabled { opacity: .45; cursor: not-allowed; }
.bt-segmented__item:disabled:hover { color: var(--bt-text-secondary); }

/* Edit account: the account's fields on the left, its Location on the right,
   Cancel and Save at the bottom right at a set width. The Dispatch visit
   editor's width and split. Stacked in reading order below 900px. */
.bt-sheet.bt-sheet--edit-account { max-width: min(1040px, calc(100vw - 32px)); }
.bt-edit-account {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 12px;
  padding: 4px 10px 10px;
}
.bt-edit-account > * { min-width: 0; }
@media (min-width: 900px) {
  .bt-edit-account {
    grid-template-areas:
      "error error"
      "contact location"
      "rest location"
      "actions actions";
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: auto auto 1fr auto;
    gap: 14px 24px;
  }
  .bt-edit-account__error { grid-area: error; }
  .bt-edit-account__contact { grid-area: contact; }
  .bt-edit-account__rest { grid-area: rest; align-self: start; }
  /* The map grows to the height of the left column, so the map and the line
     under it end level with the last field there, whatever that column holds. */
  .bt-edit-account__location { grid-area: location; display: flex; flex-direction: column; }
  .bt-edit-account__location .btmap { flex: 1 1 auto; height: auto; min-height: 340px; }
  .bt-edit-account__footer { grid-area: actions; }
}
@media (min-width: 561px) {
  .bt-edit-account__footer {
    display: flex;
    justify-content: flex-end;
    margin-top: 4px;
  }
  .bt-edit-account__footer .bt-sheet__cancel,
  .bt-edit-account__footer .bt-button { width: 160px; flex: 0 0 160px; }
}

/* A panel that folds the way a job page's panes do — Payroll's two lists
   (payroll.js) and Access's Roles: the header's chevron folds it into a line,
   the job page's folded strip laid on its side. */
.bt-fold-head { cursor: pointer; }
.bt-fold-head > .bt-pane-toggle { margin-right: -6px; }
.bt-fold-head__count { margin-left: auto; }
.bt-fold-head:hover > .bt-pane-toggle { background: var(--bt-surface); color: var(--bt-text-primary); }
.bt-pane-rail.bt-pane-rail--row {
  display: flex;
  height: auto;
  min-height: 44px;
  flex-direction: row;
  padding: 0 8px 0 4px;
  gap: 10px;
  box-shadow: inset 0 -1px 0 var(--bt-hairline);
}
.bt-pane-rail--row:hover { box-shadow: inset 0 -1px 0 var(--bt-accent); }
.bt-pane-rail--row .bt-pane-rail__label { writing-mode: horizontal-tb; }

/* Access: each permission group folds its rows behind its own header line. */
.bt-matrix__fold {
  display: inline-flex;
  min-height: 32px;
  align-items: center;
  gap: 8px;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  cursor: pointer;
}
.bt-matrix__fold:hover { color: var(--bt-text-primary); }
.bt-matrix__fold .bt-icon { transition: transform var(--bt-duration-sm) var(--bt-ease); }
.bt-matrix__fold[aria-expanded="true"] .bt-icon { transform: rotate(180deg); }
.bt-matrix__group.is-folded td { padding-bottom: 8px; }
.bt-matrix__fold-count { color: var(--bt-text-secondary); font-weight: 600; letter-spacing: 0; }

/* ---------------------------------------------------------------- what's new */
/* One full-width panel per day the portal changed, newest at the top, each
   running its full length: the page is the only thing that scrolls. Each
   point is a row across the panel: a kind badge, then the point. The badges
   share a width so every point starts on the same line. Show more, at the
   foot, adds older days under these (changelog.js). */
.bt-changelog-days { display: flex; flex-direction: column; gap: 16px; }
.bt-changelog-more { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 4px 0 8px; }
.bt-log-more { display: flex; justify-content: center; padding-top: 16px; }
.bt-bell.is-active { background: var(--bt-surface); color: var(--bt-text-primary); }
.bt-changelog-list { margin: 0; padding: 4px 16px 8px; list-style: none; }
.bt-changelog-item {
  display: grid;
  grid-template-columns: 76px minmax(0, 1fr) auto;
  align-items: start;
  gap: 12px;
  padding: 10px 0;
}
.bt-changelog-item + .bt-changelog-item { box-shadow: 0 -1px 0 var(--bt-line); }
.bt-changelog-kind { justify-self: start; margin-top: 2px; }
.bt-changelog-item__text { color: var(--bt-text-primary); font-size: 14px; line-height: 20px; overflow-wrap: anywhere; }
.bt-changelog-item__tags { margin-top: 6px; }
.bt-changelog-item > .bt-icon-button { margin: -8px -4px -8px 0; }
.bt-changelog-item.is-hidden .bt-changelog-item__text { color: var(--bt-text-secondary); }
.bt-changelog-item.is-editing { display: block; }
.bt-changelog-edit textarea.bt-input { min-height: 74px; resize: vertical; }
.bt-changelog-edit__actions { justify-content: flex-end; }
.bt-changelog-empty { margin-bottom: 4px; color: var(--bt-text-primary); font-weight: 600; }
@media (max-width: 480px) {
  .bt-changelog-list { padding-inline: 12px; }
  .bt-changelog-item { grid-template-columns: minmax(0, 1fr) auto; row-gap: 6px; }
  .bt-changelog-kind { grid-column: 1; grid-row: 1; }
  .bt-changelog-item__text { grid-column: 1 / -1; grid-row: 2; }
  .bt-changelog-item > .bt-icon-button { grid-column: 2; grid-row: 1; }
}

/* ---------------------------------------------------------------- round robin
   A job offered to technicians one at a time (offers.js): the setup on New job
   and in its sheet, the job page's status and queue, and the technician's
   offers card and banner. */
.bt-offer-setup__choice { display: flex; flex-wrap: wrap; max-width: 100%; border-radius: var(--bt-radius-cell); }
.bt-offer-setup__choice .bt-segmented__item { flex: 1 1 auto; white-space: normal; min-height: 32px; border-radius: var(--bt-radius-sm); }
.bt-offer-setup__choice .bt-segmented__item:disabled { opacity: .45; cursor: not-allowed; }
.bt-offer-setup__wait { gap: 8px; align-items: center; }
.bt-offer-setup__custom { width: 120px; gap: 6px; }
.bt-offer-setup__custom .bt-input { min-width: 0; }
.bt-offer-setup__start { min-width: 0; max-width: 100%; }
/* When the offers go out and where distance is measured from share a row,
   and stack where there is no room for both. */
.bt-offer-setup__pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
  gap: 12px 16px;
  align-items: start;
}
/* The distance list, with Auto's "starts within" minutes beside it. */
.bt-offer-setup__basis { display: flex; gap: 8px; align-items: center; }
.bt-offer-setup__basis > .bt-select,
.bt-offer-setup__basis > .bt-select-enhanced { flex: 1 1 auto; min-width: 0; }
.bt-offer-setup__minutes { width: 104px; flex: 0 0 auto; gap: 6px; }
.bt-offer-setup__minutes .bt-input { min-width: 0; }
/* Level with the segmented controls beside them (28px items + 3px padding). */
.bt-offer-setup__basis > .bt-select,
.bt-offer-setup__basis > .bt-select-enhanced,
.bt-offer-setup__minutes,
.bt-offer-setup__start { min-height: 34px; height: 34px; }
/* Roles: a select-looking button that opens a list of checkboxes, so several
   can be on and the field is always one line. The list sits under the button
   and stays open while roles are ticked; it is redrawn on every tick, so it
   only animates as it opens. */
.bt-offer-setup__roles { position: relative; width: min(100%, 260px); }
.bt-offer-setup__roles-btn {
  display: flex;
  height: 34px;
  min-height: 34px;
  font-size: 13px;
  text-align: left;
}
.bt-offer-setup__roles-btn > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bt-offer-setup__roles-menu {
  position: absolute;
  z-index: 40;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  animation: none;
}
.bt-offer-setup__roles-menu.is-entering { animation: bt-select-in var(--bt-duration-sm) var(--bt-ease); }
.bt-offer-setup__roles-menu .bt-select-menu__option[aria-disabled="true"] { cursor: default; }
.bt-offer-setup__box {
  display: inline-flex;
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  border: 1.5px solid var(--bt-text-disabled);
  border-radius: 4px;
}
.is-selected > .bt-offer-setup__box { border-color: var(--bt-accent); background: var(--bt-accent); color: var(--bt-ink); }
.bt-offer-setup__box .bt-icon { width: 12px; height: 12px; }
.bt-offer-queue { display: flex; flex-direction: column; margin: 0; padding: 0; list-style: none; }
.bt-offer-queue--add { max-height: 264px; overflow-y: auto; }
/* Where the row is narrow (a phone, the job page's side column) the controls
   drop to their own line instead of squeezing the name and distance. */
.bt-offer-row {
  display: flex;
  min-height: 48px;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 10px;
  padding: 6px 4px;
}
.bt-offer-row + .bt-offer-row { box-shadow: 0 -1px 0 var(--bt-line); }
.bt-offer-row__num {
  width: 18px;
  flex: 0 0 auto;
  color: var(--bt-text-secondary);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.bt-offer-row__body { display: flex; min-width: 120px; flex: 1 1 120px; flex-direction: column; }
.bt-offer-row__name { overflow: hidden; color: var(--bt-text-primary); font-size: 14px; font-weight: 500; line-height: 20px; text-overflow: ellipsis; white-space: nowrap; }
.bt-offer-row__sub { color: var(--bt-text-secondary); font-size: 12px; line-height: 18px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.bt-offer-row__actions { display: inline-flex; flex: 0 0 auto; align-items: center; gap: 2px; margin-left: auto; }
.bt-offer-row__actions .bt-badge { margin-left: 6px; }
/* 44px to touch even where the glyph is drawn smaller. */
.bt-offer-row__actions .bt-icon-button::after { content: ""; position: absolute; inset: -6px; }
.bt-offer-row__actions .bt-icon-button:disabled { opacity: .35; cursor: default; }
.bt-offer-left { font-variant-numeric: tabular-nums; }
.bt-offer-panel__head { display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 8px; min-height: 32px; margin-bottom: 4px; padding: 0; border: 0; border-radius: var(--bt-radius-sm); background: transparent; color: inherit; text-align: left; cursor: pointer; }
.bt-offer-panel__head:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 2px; }
.bt-offer-panel__head .bt-icon { color: var(--bt-text-secondary); transition: transform var(--bt-duration-sm) var(--bt-ease); }
.bt-offer-panel__head[aria-expanded="true"] .bt-icon { transform: rotate(180deg); }
.bt-offer-panel__status { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; color: var(--bt-text-primary); font-size: 14px; line-height: 20px; }
.bt-offer-sheet__body { padding: 4px 10px 10px; }
.bt-sheet.bt-sheet--offer { max-width: 560px; }

/* The technician's side. */
.bt-offer-mine { display: flex; flex-direction: column; margin: 0; padding: 0; list-style: none; }
.bt-offer-mine__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 12px;
  padding: 8px;
}
.bt-offer-mine__row + .bt-offer-mine__row { box-shadow: 0 -1px 0 var(--bt-line); }
.bt-offer-mine__open {
  display: flex;
  min-width: 0;
  min-height: 44px;
  flex-direction: column;
  justify-content: center;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
/* A job in an offer: its title and place first, its number last. A shared
   round lists every job in it. */
.bt-offer-mine__jobs { display: flex; min-width: 0; flex-direction: column; gap: 8px; }
.bt-offer-mine__count { color: var(--bt-accent-text); font-size: 12px; font-weight: 600; line-height: 18px; }
.bt-offer-mine__title { overflow: hidden; color: var(--bt-text-primary); font-size: 15px; font-weight: 600; line-height: 20px; text-overflow: ellipsis; white-space: nowrap; }
.bt-offer-mine__open:hover .bt-offer-mine__title { color: var(--bt-accent-text); }
.bt-offer-banner__job { display: flex; min-width: 0; flex-direction: column; padding: 8px 12px; border-radius: var(--bt-radius-cell); background: var(--bt-surface-nested); }
.bt-offer-mine__sub { overflow: hidden; color: var(--bt-text-secondary); font-size: 12px; line-height: 18px; text-overflow: ellipsis; white-space: nowrap; }
.bt-offer-mine__actions { display: inline-flex; gap: 8px; }
.bt-offer-mine .bt-badge { font-variant-numeric: tabular-nums; white-space: nowrap; }
@media (max-width: 480px) {
  .bt-offer-mine__row { grid-template-columns: minmax(0, 1fr) auto; }
  .bt-offer-mine__actions { grid-column: 1 / -1; }
  .bt-offer-mine__actions .bt-button { flex: 1 1 0; }
}
/* The answer page (offers.js mountPage): a job waiting for your answer, in
   place of the job until you accept it. One panel: the heading, the facts
   the decision rests on, the rest, then the two answers. */
#view-job.is-offer-preview .bt-job-page { display: none; }
.bt-answer { display: flex; flex-direction: column; gap: 16px; padding: 24px; }
.bt-answer__head { display: flex; flex-direction: column; gap: 6px; }
.bt-answer__head .bt-heading-2 { margin: 4px 0 0; }
.bt-answer__head p { margin: 0; }
.bt-answer .bt-badge { font-variant-numeric: tabular-nums; }
.bt-answer__facts {
  margin: 0;
  padding: 0;
  list-style: none;
  border: 1px solid var(--bt-line);
  border-radius: var(--bt-radius-cell);
}
.bt-answer__fact { display: flex; align-items: flex-start; gap: 12px; padding: 12px 16px; }
.bt-answer__fact + .bt-answer__fact { border-top: 1px solid var(--bt-line); }
.bt-answer__fact > .bt-icon { margin-top: 2px; color: var(--bt-text-secondary); }
.bt-answer__fact-text { display: flex; min-width: 0; flex-direction: column; gap: 2px; }
.bt-answer__value { color: var(--bt-text-primary); font-size: 15px; line-height: 20px; overflow-wrap: anywhere; }
.bt-answer__value.bt-text-secondary { color: var(--bt-text-secondary); }
.bt-answer__block { display: flex; flex-direction: column; gap: 6px; }
.bt-answer__block p { margin: 0; white-space: pre-line; }
.bt-answer__details { display: flex; flex-wrap: wrap; gap: 12px 32px; margin: 0; }
.bt-answer__details div { display: flex; flex-direction: column; gap: 2px; }
.bt-answer__details dd { margin: 0; color: var(--bt-text-primary); font-size: 14px; line-height: 20px; }
.bt-answer__actions { display: flex; gap: 8px; padding-top: 8px; border-top: 1px solid var(--bt-line); }
.bt-answer__actions .bt-button { flex: 1 1 0; }
.bt-offer-banner__job { gap: 2px; }
@media (max-width: 480px) {
  .bt-answer { padding: 16px; }
}

/* ---------------------------------------------------------------- documents
   Account menu → Documents (docsview.js): the library, and one guide open. */
.bt-doc-list { display: flex; flex-direction: column; gap: 8px; margin: 0; padding: 0; list-style: none; }
.bt-doc-card {
  display: flex; width: 100%; align-items: center; gap: 14px; padding: 16px;
  border: 0; border-radius: var(--bt-radius-panel); background: var(--bt-surface);
  color: var(--bt-text-secondary); text-align: left; cursor: pointer;
  transition: background var(--bt-duration-sm) var(--bt-ease);
}
.bt-doc-card:hover { background-image: linear-gradient(var(--bt-hover-wash), var(--bt-hover-wash)); }
.bt-doc-card:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 2px; }
.bt-doc-card__icon {
  display: inline-flex; width: 40px; height: 40px; flex: 0 0 auto; align-items: center; justify-content: center;
  border-radius: 12px; background: var(--bt-accent-soft); color: var(--bt-accent-text);
}
.bt-doc-card__body { display: flex; min-width: 0; flex: 1 1 auto; flex-direction: column; gap: 2px; }
.bt-doc-card__title { color: var(--bt-text-primary); font-size: 16px; font-weight: 600; line-height: 22px; }
.bt-doc-card__summary { font-size: 14px; line-height: 20px; }
.bt-doc-card__meta { font-size: 12px; line-height: 18px; }

.bt-doc { display: flex; flex-direction: column; gap: 24px; padding-bottom: 32px; }
.bt-doc__head { display: flex; flex-direction: column; gap: 6px; }
.bt-doc__title { margin: 0; color: var(--bt-text-primary); font-size: 28px; font-weight: 700; line-height: 34px; }
.bt-doc__summary { margin: 0; color: var(--bt-text-secondary); font-size: 16px; line-height: 24px; }
.bt-doc__meta { margin: 0; color: var(--bt-text-secondary); font-size: 12px; line-height: 18px; }
.bt-doc__body { display: flex; flex-direction: column; gap: 12px; color: var(--bt-text-primary); font-size: 15px; line-height: 24px; }
.bt-doc__body strong { font-weight: 600; }
.bt-doc__h {
  margin: 20px 0 0; padding-top: 20px; border-top: 1px solid var(--bt-line);
  color: var(--bt-text-primary); font-size: 19px; font-weight: 700; line-height: 26px;
}
.bt-doc__p { margin: 0; }
.bt-doc__list { display: flex; flex-direction: column; gap: 8px; margin: 0; padding-left: 22px; list-style: disc; }
.bt-doc__list li { display: list-item; padding-left: 2px; }
.bt-doc__list li::marker { color: var(--bt-accent-text); }
.bt-doc__terms { display: flex; flex-direction: column; margin: 0; border-radius: var(--bt-radius-panel); background: var(--bt-surface); }
.bt-doc__term { display: grid; grid-template-columns: minmax(150px, 30%) 1fr; gap: 4px 16px; padding: 12px 16px; }
.bt-doc__term + .bt-doc__term { box-shadow: 0 -1px 0 var(--bt-line); }
.bt-doc__term dt { color: var(--bt-text-primary); font-weight: 600; }
.bt-doc__term dd { margin: 0; color: var(--bt-text-secondary); }
.bt-doc__tips { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 8px; }
.bt-doc__tip {
  display: flex; gap: 10px; padding: 14px 16px; border-radius: var(--bt-radius-panel);
  background: var(--bt-accent-soft); color: var(--bt-accent-text);
}
.bt-doc__tip .bt-icon { flex: 0 0 auto; margin-top: 3px; }
.bt-doc__tip p { margin: 2px 0 0; color: var(--bt-text-primary); font-size: 14px; line-height: 20px; }
.bt-doc__tip-title { display: block; color: var(--bt-text-primary); font-size: 14px; line-height: 20px; }
@media (max-width: 480px) {
  .bt-doc__title { font-size: 24px; line-height: 30px; }
  .bt-doc__term { grid-template-columns: 1fr; }
}

/* Administrators only: which roles can read the document. */
.bt-doc-access { border-radius: var(--bt-radius-panel); background: var(--bt-surface); }
.bt-doc-access__head {
  display: flex; min-height: 48px; align-items: center; gap: 8px; padding: 0 16px;
  color: var(--bt-text-secondary); cursor: pointer; list-style: none;
}
.bt-doc-access__head::-webkit-details-marker { display: none; }
.bt-doc-access__head:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 2px; border-radius: var(--bt-radius-panel); }
.bt-doc-access__title { color: var(--bt-text-primary); font-size: 15px; font-weight: 600; line-height: 20px; }
.bt-doc-access__now { min-width: 0; flex: 1 1 auto; overflow: hidden; font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
.bt-doc-access__chev { display: inline-flex; transition: transform var(--bt-duration-sm) var(--bt-ease); }
.bt-doc-access[open] .bt-doc-access__chev { transform: rotate(180deg); }
.bt-doc-access__body { display: flex; flex-direction: column; gap: 10px; padding: 0 16px 16px; }
@media (prefers-reduced-motion: reduce) { .bt-doc-access__chev { transition: none; } }
.bt-doc-access__roles { display: flex; flex-wrap: wrap; gap: 8px; }
.bt-doc-access__role {
  display: inline-flex; min-height: 36px; align-items: center; gap: 8px; padding: 0 12px;
  border-radius: 999px; background: var(--bt-surface-nested); color: var(--bt-text-primary); font-size: 14px; cursor: pointer;
}
.bt-doc-access__role input { width: 16px; height: 16px; margin: 0; accent-color: var(--bt-accent); }
.bt-doc-access__role:has(input:checked) { background: var(--bt-accent-soft); color: var(--bt-accent-text); }
.bt-doc-access__role.is-fixed { cursor: default; opacity: .8; }
/* The roles, as a dropdown of checkboxes (the board's column menu). */
.bt-doc-access__pick { max-width: 360px; }
.bt-doc-access__select { display: flex; align-items: center; text-align: left; cursor: pointer; }
.bt-doc-access__menu { left: 0; right: auto; width: 100%; }

/* The phone app's guides on Help: what each is, and under it who sees it. */
.bt-doc-guides { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.bt-doc-guides .bt-overline { margin: 0; }
.bt-doc-guides > p { margin: 0 0 4px; }
.bt-doc-guide { border-radius: var(--bt-radius-panel); background: var(--bt-surface); }
.bt-doc-card--static { cursor: default; background: transparent; }
.bt-doc-card--static:hover { background-image: none; }
.bt-doc-guide .bt-doc-access { border-top: 1px solid var(--bt-line); border-radius: 0 0 var(--bt-radius-panel) var(--bt-radius-panel); background: transparent; }

/* ------------------------------------------------------- job checklist editor */
/* A job's own copy of a checklist, edited with the template editor's rows. */
.bt-jce { padding: 4px 10px 10px; }
.bt-jce .bt-te-section + .bt-te-section { margin-top: 12px; }

/* ------------------------------------------------------------------- how-to */
/* A checklist item's link to its guide (howto.js), right beside the item's
   label. A button of its own inside the row, so reading the guide never opens
   the item's status sheet; the ::after widens its hit area past the pill. */
.bt-howto-chip {
  display: inline-flex;
  min-height: 24px;
  align-items: center;
  gap: 4px;
  margin-left: 8px;
  vertical-align: middle;
  padding: 0 10px 0 8px;
  border: 0;
  border-radius: var(--bt-radius-round);
  background: var(--bt-accent-soft);
  color: var(--bt-accent-text);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  position: relative;
}
.bt-howto-chip::after { content: ""; position: absolute; inset: -10px -4px; }
.bt-howto-chip:hover { box-shadow: inset 0 0 0 100px var(--bt-hover-wash); }
.bt-howto-chip:focus-visible { outline: 2px solid var(--bt-accent); outline-offset: 2px; }

/* The checklist editor's control: outlined until a guide is attached, then
   tinted with the guide's name beside the book. */
.bt-howto-attach {
  display: inline-flex;
  min-height: var(--bt-control-sm);
  max-width: 100%;
  align-items: center;
  gap: 6px;
  padding: 0 10px;
  border: 1px dashed var(--bt-hairline);
  border-radius: var(--bt-radius-sm);
  background: transparent;
  color: var(--bt-text-secondary);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
}
.bt-howto-attach:hover { color: var(--bt-text-primary); background: var(--bt-surface-nested); }
.bt-howto-attach.is-set {
  border-style: solid;
  border-color: transparent;
  background: var(--bt-accent-soft);
  color: var(--bt-accent-text);
}
.bt-howto-attach:disabled { cursor: default; opacity: 0.6; }
.bt-howto-attach__name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Reading a guide: a wide sheet that scrolls, Done always reachable. */
.bt-sheet.bt-howto-sheet { max-width: 680px; padding-left: 20px; padding-right: 20px; }
.bt-howto-sheet #howto-body { display: flex; flex-direction: column; gap: 16px; }
.bt-howto-head { display: flex; flex-direction: column; gap: 4px; }
.bt-howto-head p { margin: 4px 0 0; }
.bt-howto-need { display: flex; flex-direction: column; gap: 8px; }
.bt-howto-steps { display: flex; flex-direction: column; gap: 14px; margin: 0; padding: 0; list-style: none; }
.bt-howto-step { display: flex; gap: 12px; }
.bt-howto-step--warning { flex-direction: column; gap: 8px; }
.bt-howto-step__n {
  display: grid;
  width: 26px;
  height: 26px;
  flex: 0 0 auto;
  place-items: center;
  border-radius: 50%;
  background: var(--bt-accent-soft);
  color: var(--bt-accent-text);
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.bt-howto-step__body { display: flex; min-width: 0; flex: 1; flex-direction: column; gap: 8px; padding-top: 3px; }
.bt-howto-step__body p { margin: 0; font-size: 15px; line-height: 22px; white-space: pre-wrap; }
.bt-howto-photo { display: block; max-width: 420px; }
.bt-howto-photo img { display: block; width: 100%; border-radius: var(--bt-radius-cell); }

/* Picking a guide for one item. */
.bt-howto-pick__list { display: flex; max-height: 50vh; flex-direction: column; gap: 2px; margin-top: 8px; overflow-y: auto; }
.bt-howto-pick__icon { display: grid; color: var(--bt-accent-text); place-items: center; }
.bt-howto-pick__text { display: flex; min-width: 0; flex-direction: column; }

/* Writing one (Admin). */
.bt-howto-edit--warning .bt-te-item__n { color: var(--bt-warning-text); }
.bt-howto-thumb { position: relative; display: inline-block; width: 160px; }
.bt-howto-thumb img { display: block; width: 100%; border-radius: var(--bt-radius-cell); }
.bt-howto-thumb .bt-icon-button { position: absolute; top: 4px; right: 4px; background: var(--bt-scrim); }
.bt-howto-editor__bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
#howto-editor { display: flex; flex-direction: column; gap: 16px; }
#howto-editor .bt-te-item__fields > .bt-button,
#howto-editor .bt-howto-thumb { align-self: flex-start; }
@media (max-width: 760px) {
  .bt-howto-step__body p { font-size: 16px; }
  .bt-howto-editor__bar > span[style] { display: none; }
}

/* ---------------------------------------------------------------- client editor */
/* Add client / Edit client. Wide rather than tall: the client on the left, its
   sites on the right, so the whole client fits a laptop screen without
   scrolling. The sheet is as wide as the window allows, up to 1160px, and its
   height follows the content; under 880px the columns stack and it scrolls. */
#client-overlay .bt-sheet.bt-sheet--client {
  display: flex;
  flex-direction: column;
  max-width: 1160px;
  padding: 0;
}
.bt-ced__head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 14px 14px 24px;
  border-bottom: 1px solid var(--bt-line);
}
.bt-ced__title {
  flex: 0 1 auto;
  min-width: 0;
  font-size: 17px;
  font-weight: 600;
  line-height: 24px;
}
.bt-ced__syncro {
  display: inline-flex;
  min-width: 0;
  max-width: 340px;
  height: 32px;
  align-items: center;
  gap: 6px;
  padding: 0 4px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-surface-nested);
  box-shadow: 0 0 0 1px var(--bt-line) inset;
  color: var(--bt-text-secondary);
  font-size: 12px;
}
.bt-ced__syncro[hidden] { display: none; }
/* Centered in the chip; the ticket rows it comes from pin it to the top. */
.bt-ced__syncro .bt-ticket__num { flex: none; align-self: center; margin-left: 2px; padding: 3px 9px; line-height: 16px; font-variant-numeric: tabular-nums; }
.bt-ced__syncrotext { min-width: 0; }
.bt-ced__close { margin-left: auto; }
.bt-ced__err { margin: 14px 24px 0; }
.bt-ced__body {
  display: grid;
  flex: 1 1 auto;
  min-height: 0;
  grid-template-columns: minmax(300px, 372px) minmax(0, 1fr);
}
.bt-ced__side {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 22px;
  padding: 20px 24px 24px;
  border-right: 1px solid var(--bt-line);
}
.bt-ced__main {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 12px;
  padding: 20px 24px 20px;
}
.bt-ced__group { display: flex; flex-direction: column; gap: 12px; }
.bt-ced__overline {
  display: flex;
  align-items: center;
  gap: 4px;
  color: var(--bt-text-disabled);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  line-height: 22px;
  text-transform: uppercase;
}
.bt-ced__overline::after { content: ""; flex: 1 1 auto; height: 1px; margin-left: 6px; background: var(--bt-line); }
.bt-ced__overline .bt-info-dot { letter-spacing: 0; text-transform: none; }
.bt-ced__pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.bt-ced__time { display: grid; grid-template-columns: minmax(0, 1fr) 136px; gap: 10px; }
#cd-report-date-shell { margin-top: 4px; }

/* Logo: one pencil on the tile opens Upload/Replace and Remove. */
.bt-ced__logomenu {
  position: absolute;
  z-index: 6;
  top: calc(100% + 12px);
  left: 0;
  min-width: 184px;
  padding: 4px;
  border: 1px solid var(--bt-hairline);
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-popover);
  box-shadow: var(--bt-shadow-popover);
}
.bt-ced__logomenu .bt-menu-item { cursor: pointer; white-space: nowrap; }

/* Access: a summary row per list, opening the role-tab picker. */
.bt-ced__acc { position: relative; }
.bt-ced__accrow {
  display: grid;
  width: 100%;
  min-height: 52px;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border: 0;
  border-radius: var(--bt-radius-cell);
  background: var(--bt-surface-nested);
  box-shadow: 0 0 0 1px var(--bt-line) inset;
  color: var(--bt-text-primary);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background var(--bt-duration-sm) var(--bt-ease);
}
.bt-ced__accrow:hover { background: var(--bt-surface); }
.bt-ced__accrow[aria-expanded="true"] { box-shadow: 0 0 0 1px var(--bt-accent) inset; }
.bt-ced__accrow > .bt-icon { color: var(--bt-text-secondary); transition: transform var(--bt-duration-sm) var(--bt-ease); }
.bt-ced__accrow[aria-expanded="true"] > .bt-icon { transform: rotate(180deg); }
.bt-ced__acctext { display: flex; min-width: 0; flex-direction: column; }
.bt-ced__acctitle { font-size: 13px; font-weight: 600; line-height: 18px; }
.bt-ced__accsum {
  overflow: hidden;
  color: var(--bt-text-secondary);
  font-size: 12px;
  line-height: 16px;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bt-ced__accsum.is-empty { color: var(--bt-text-disabled); }
.bt-ced__stack { display: flex; }
.bt-ced__stack .bt-avatar { box-shadow: 0 0 0 2px var(--bt-surface-popover); }
.bt-ced__stack .bt-avatar + .bt-avatar { margin-left: -6px; }
.bt-ced__pop {
  position: absolute;
  z-index: 5;
  top: calc(100% + 6px);
  right: 0;
  left: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px;
  border: 1px solid var(--bt-hairline);
  border-radius: var(--bt-radius-panel);
  background: var(--bt-surface-popover);
  box-shadow: var(--bt-shadow-popover);
}
.bt-ced__pop[hidden] { display: none; }
.bt-ced__pop.is-up { top: auto; bottom: calc(100% + 6px); }
.bt-ced__pophead { display: flex; min-height: 28px; align-items: center; gap: 2px; }
.bt-ced__pop .bt-picker__roles { align-self: flex-start; }
.bt-ced__poplist { max-height: none; }
.bt-ced__rolecount { margin-left: 6px; color: var(--bt-accent-text); font-size: 11px; font-variant-numeric: tabular-nums; }
.bt-ced__box {
  display: grid;
  width: 18px;
  height: 18px;
  flex: none;
  place-items: center;
  margin-left: auto;
  border-radius: 5px;
  box-shadow: 0 0 0 1.5px var(--bt-text-disabled) inset;
  color: var(--bt-ink);
}
.bt-picker__row.is-on .bt-ced__box { background: var(--bt-accent); box-shadow: none; }

/* Locations: a list of sites beside one editor. The list only exists from a
   second site on, and scrolls inside itself rather than growing the sheet. */
.bt-ced__lochead { display: flex; align-items: center; gap: 12px; }
.bt-ced__lochead .bt-ced__overline { flex: 1 1 auto; min-width: 0; }
.bt-ced__locs {
  display: grid;
  flex: 1 1 auto;
  min-height: 0;
  grid-template-columns: 220px minmax(0, 1fr);
  gap: 16px;
}
.bt-ced__locs.is-single { grid-template-columns: minmax(0, 1fr); }
.bt-ced__locs.is-single .bt-ced__loclist { display: none; }
.bt-ced__loclist {
  display: flex;
  height: 0;
  min-height: 100%;
  flex-direction: column;
  gap: 2px;
  overflow-y: auto;
}
.bt-ced__locitem {
  display: flex;
  width: 100%;
  min-height: 40px;
  flex: none;
  align-items: center;
  gap: 6px;
  padding: 8px 10px;
  border: 0;
  border-radius: var(--bt-radius-cell);
  background: transparent;
  color: var(--bt-text-primary);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.bt-ced__locitem > .bt-truncate { min-width: 0; flex: 0 1 auto; }
.bt-ced__locitem:hover { background: var(--bt-surface-nested); }
.bt-ced__locitem.is-active { background: var(--bt-surface); box-shadow: 0 0 0 1px var(--bt-line) inset; color: var(--bt-accent-text); }
.bt-ced__tag {
  flex: none;
  padding: 1px 6px;
  border-radius: var(--bt-radius-round);
  background: var(--bt-accent-soft);
  color: var(--bt-accent-text);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.04em;
  line-height: 14px;
  text-transform: uppercase;
}
.bt-ced__tag--warn { background: var(--bt-warning-soft); color: var(--bt-warning-text); }
.bt-ced__tag--muted { background: var(--bt-surface-nested); color: var(--bt-text-secondary); }
.bt-ced__locedit { display: flex; min-width: 0; flex-direction: column; gap: 12px; }
.bt-sheet--client .btmap.bt-ced__map { height: auto; min-height: 260px; flex: 1 1 auto; }
.bt-ced__locmeta { display: flex; min-height: 28px; flex-wrap: wrap; align-items: center; gap: 4px 10px; }
.bt-ced__locmeta > .bt-hint:empty { display: none; }
.bt-ced__spacer { flex: 1 1 auto; }
.bt-ced__remove:hover { color: var(--bt-danger-text); }

.bt-ced__foot {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding: 14px 24px calc(14px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--bt-line);
}
/* Cancel and the commit action are one size. */
.bt-ced__foot .bt-button { width: 148px; }

@media (max-width: 880px) {
  .bt-ced__body { grid-template-columns: minmax(0, 1fr); }
  .bt-ced__side { border-right: 0; border-bottom: 1px solid var(--bt-line); }
  .bt-ced__locs { grid-template-columns: minmax(0, 1fr); }
  .bt-ced__loclist { height: auto; min-height: 0; max-height: 200px; }
  .bt-ced__syncrotext { display: none; }
}
@media (max-width: 480px) {
  .bt-ced__head { padding-left: 16px; }
  .bt-ced__side,
  .bt-ced__main { padding-inline: 16px; }
  .bt-ced__pair { grid-template-columns: minmax(0, 1fr); }
  .bt-ced__foot { padding-inline: 16px; }
  .bt-ced__foot .bt-button { width: auto; flex: 1 1 0; }
}
