/* ============================================================================
   SLAM Design System — Components
   ----------------------------------------------------------------------------
   Visual polish for shared Bootstrap components, all driven by tokens.css.
   Loaded AFTER Bootstrap + legacy CSS + base.html inline styles so these win
   the cascade. Uses tokens only (no hardcoded scheme colors) so light/dark and
   any future re-palette flow through automatically.

   Performance notes:
     - Transitions are limited to transform / box-shadow / background / color /
       border-color (compositor- and paint-cheap). No layout-animating props.
     - No backdrop blur on list/table rows.
   ============================================================================ */

/* ----------------------------------------------------------------------------
   Base typography / rhythm
   ---------------------------------------------------------------------------- */
body {
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  letter-spacing: 0.005em;
}

h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
  letter-spacing: -0.015em;
  font-weight: 700;
  color: var(--bs-heading-color);
}

/* Heading hierarchy: the words lead, decorative icons are secondary and never
   larger than the text. (Addresses "icons bigger than the heading".) */
:where(h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6,
  .section-title, .card-title, .slam-page-title) > i,
:where(h1, h2, h3, h4, h5, h6, .section-title, .card-title) > .fa,
:where(h1, h2, h3, h4, h5, h6, .section-title, .card-title) > .fas,
:where(h1, h2, h3, h4, h5, h6, .section-title, .card-title) > .far {
  font-size: 0.78em;
  color: var(--slam-accent);
  opacity: 0.9;
}

/* Even, fluid card spacing that adapts to any viewport (mobile -> ultrawide)
   without fixed Bootstrap column breakpoints. */
.slam-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--slam-space-4);
}
.slam-card-grid.is-wide {
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}
@media (max-width: 480px) {
  .slam-card-grid {
    grid-template-columns: 1fr;
    gap: var(--slam-space-3);
  }
}

a {
  text-decoration: none;
}
a:hover {
  text-decoration: none;
}

/* ----------------------------------------------------------------------------
   Buttons
   Bootstrap compiles .btn-* colors to literal hex via --bs-btn-* locals, so we
   re-point the primary/outline-primary locals to the accent token explicitly.

   Bootswatch Soft hardcodes every .btn (and :hover) to a muted grey fill
   (#d9e3f1 light / #212529 dark). That makes Create / Submit look disabled.
   Re-apply the CSS-variable colors with enough specificity to win the cascade.
   ---------------------------------------------------------------------------- */
.btn,
[data-bs-theme="dark"] .btn {
  --bs-btn-border-radius: var(--slam-radius-md);
  /* Bootswatch Soft is neumorphic: it sets --bs-btn-border-width to 0 and draws
     the button's edge with soft inset/outset shadows instead. We flatten those
     shadows below (box-shadow: none), so unless the border width is restored
     here every .btn-outline-* in the app has no edge at all and renders as bare
     text — that is what made "Compare vs Timesheets", the nav pills and other
     quiet actions look unstyled. The old `var(--bs-btn-border-width, 1px)`
     fallback could never fire: Soft *defines* the variable as 0, so it resolves
     rather than falling back. Setting it (not just the shorthand) also keeps
     Bootstrap's own :hover/:active rules on the same geometry. */
  --bs-btn-border-width: 1px;
  border-radius: var(--slam-radius-md);
  font-weight: 600;
  color: var(--bs-btn-color);
  background-color: var(--bs-btn-bg);
  border: var(--bs-btn-border-width) solid var(--bs-btn-border-color);
  box-shadow: none;
  transition: background-color var(--slam-dur-fast) var(--slam-ease),
    border-color var(--slam-dur-fast) var(--slam-ease),
    color var(--slam-dur-fast) var(--slam-ease),
    box-shadow var(--slam-dur-fast) var(--slam-ease),
    transform 80ms var(--slam-ease);
}

/* Soft also strips the border from buttons joined to a neighbour
   (`.input-group .btn`, `.btn-group .btn`) at two-class specificity, which the
   base .btn rule above cannot reach. Without this, a segmented toggle or the
   Copy button beside an invite-link field has no edge and reads as plain text.
   Bootstrap's own negative margin on the non-first segments still collapses the
   shared edges, so the seam stays a single line. */
.input-group > .btn,
.btn-group > .btn,
.btn-group-vertical > .btn {
  border: var(--bs-btn-border-width) solid var(--bs-btn-border-color);
}

.btn:hover,
[data-bs-theme="dark"] .btn:hover {
  color: var(--bs-btn-hover-color);
  background-color: var(--bs-btn-hover-bg);
  border-color: var(--bs-btn-hover-border-color);
  box-shadow: none;
}

.btn:focus-visible,
.btn.focus-visible,
[data-bs-theme="dark"] .btn:focus-visible {
  color: var(--bs-btn-hover-color);
  background-color: var(--bs-btn-hover-bg);
  border-color: var(--bs-btn-hover-border-color);
  box-shadow: var(--slam-focus-ring);
}

.btn:active,
.btn.active,
[data-bs-theme="dark"] .btn:active,
[data-bs-theme="dark"] .btn.active {
  color: var(--bs-btn-active-color);
  background-color: var(--bs-btn-active-bg);
  border-color: var(--bs-btn-active-border-color);
  transform: translateY(1px);
}

.btn-sm {
  --bs-btn-border-radius: var(--slam-radius-sm);
  border-radius: var(--slam-radius-sm);
}

.btn-lg {
  --bs-btn-border-radius: var(--slam-radius-lg);
  border-radius: var(--slam-radius-lg);
}

/*
 * !important on solid semantic fills is intentional: Bootswatch Soft paints
 * every .btn grey (#d9e3f1 / #212529) at rest/hover/focus. Without this,
 * Create BBO / Submit Inspection / Create Tag look disabled.
 */
.btn-primary,
[data-bs-theme="dark"] .btn-primary {
  --bs-btn-bg: var(--slam-accent);
  --bs-btn-border-color: var(--slam-accent);
  --bs-btn-hover-bg: var(--slam-accent-hover);
  --bs-btn-hover-border-color: var(--slam-accent-hover);
  --bs-btn-active-bg: var(--slam-accent-active);
  --bs-btn-active-border-color: var(--slam-accent-active);
  --bs-btn-disabled-bg: var(--slam-accent);
  --bs-btn-disabled-border-color: var(--slam-accent);
  --bs-btn-color: var(--slam-accent-contrast);
  --bs-btn-hover-color: var(--slam-accent-contrast);
  --bs-btn-active-color: var(--slam-accent-contrast);
  --bs-btn-disabled-color: var(--slam-accent-contrast);
  color: var(--slam-accent-contrast) !important;
  background-color: var(--slam-accent) !important;
  border-color: var(--slam-accent) !important;
}

.btn-primary:hover,
.btn-primary:focus,
.btn-primary:focus-visible,
.btn-primary:active,
.btn-primary.active,
[data-bs-theme="dark"] .btn-primary:hover,
[data-bs-theme="dark"] .btn-primary:focus,
[data-bs-theme="dark"] .btn-primary:focus-visible,
[data-bs-theme="dark"] .btn-primary:active,
[data-bs-theme="dark"] .btn-primary.active {
  color: var(--slam-accent-contrast) !important;
  background-color: var(--slam-accent-hover) !important;
  border-color: var(--slam-accent-hover) !important;
}

.btn-success,
[data-bs-theme="dark"] .btn-success {
  color: #fff !important;
  background-color: var(--bs-success) !important;
  border-color: var(--bs-success) !important;
}

.btn-success:hover,
.btn-success:focus,
.btn-success:focus-visible,
.btn-success:active,
[data-bs-theme="dark"] .btn-success:hover,
[data-bs-theme="dark"] .btn-success:focus,
[data-bs-theme="dark"] .btn-success:focus-visible,
[data-bs-theme="dark"] .btn-success:active {
  color: #fff !important;
  background-color: var(--bs-success) !important;
  border-color: var(--bs-success) !important;
  filter: brightness(0.92);
}

.btn-danger,
[data-bs-theme="dark"] .btn-danger {
  color: #fff !important;
  background-color: var(--bs-danger) !important;
  border-color: var(--bs-danger) !important;
}

.btn-danger:hover,
.btn-danger:focus,
.btn-danger:focus-visible,
.btn-danger:active,
[data-bs-theme="dark"] .btn-danger:hover,
[data-bs-theme="dark"] .btn-danger:focus,
[data-bs-theme="dark"] .btn-danger:focus-visible,
[data-bs-theme="dark"] .btn-danger:active {
  color: #fff !important;
  background-color: var(--bs-danger) !important;
  border-color: var(--bs-danger) !important;
  filter: brightness(0.92);
}

.btn-warning,
[data-bs-theme="dark"] .btn-warning {
  color: #1d2327 !important;
  background-color: var(--bs-warning) !important;
  border-color: var(--bs-warning) !important;
}

.btn-info,
[data-bs-theme="dark"] .btn-info {
  color: #fff !important;
  background-color: var(--bs-info) !important;
  border-color: var(--bs-info) !important;
}

/* Morph bakes its indigo/purple info (#5b62f4) straight into these rules
   instead of using the --bs-info variables, so the token override alone
   can't reach them. Re-point them at the SLAM info blue. */
.btn-outline-info,
.btn-outline-info:hover,
.btn-outline-info:focus,
.btn-outline-info:active,
.btn-outline-info:active:focus,
.btn-check:checked + .btn-info,
.btn-check:active + .btn-info {
  background-color: var(--bs-info) !important;
  color: #fff;
}
.btn-info,
.btn-outline-info {
  --bs-btn-focus-shadow-rgb: var(--bs-info-rgb);
}
.alert-info {
  /* Morph layers a solid rgba(91,98,244,.75) purple over the subtle bg;
     replace it with the theme-aware info-subtle background so the
     emphasis text keeps AA contrast in both modes. */
  background-color: var(--bs-info-bg-subtle) !important;
  color: var(--bs-info-text-emphasis);
  border-color: var(--bs-info-border-subtle);
}
/* Muted helper text inside tinted alerts: Bootstrap's .text-muted grey can
   dip below AA on the tinted backgrounds, so inherit the alert's emphasis
   color slightly softened instead. */
.alert .text-muted {
  color: inherit !important;
  opacity: 0.85;
}
.table-info {
  --bs-table-color: var(--bs-info-text-emphasis);
  --bs-table-bg: var(--bs-info-bg-subtle);
  --bs-table-border-color: var(--bs-info-border-subtle);
  --bs-table-striped-bg: var(--bs-info-bg-subtle);
  --bs-table-striped-color: var(--bs-info-text-emphasis);
  --bs-table-hover-bg: var(--bs-info-border-subtle);
  --bs-table-hover-color: var(--bs-info-text-emphasis);
  --bs-table-active-bg: var(--bs-info-border-subtle);
  --bs-table-active-color: var(--bs-info-text-emphasis);
}

.btn-outline-primary,
[data-bs-theme="dark"] .btn-outline-primary {
  --bs-btn-color: var(--slam-accent);
  --bs-btn-border-color: var(--slam-accent);
  --bs-btn-bg: transparent;
  --bs-btn-hover-bg: var(--slam-accent);
  --bs-btn-hover-border-color: var(--slam-accent);
  --bs-btn-active-bg: var(--slam-accent);
  --bs-btn-active-border-color: var(--slam-accent);
  --bs-btn-hover-color: var(--slam-accent-contrast);
  --bs-btn-active-color: var(--slam-accent-contrast);
  color: var(--bs-btn-color);
  background-color: var(--bs-btn-bg);
  border-color: var(--bs-btn-border-color);
}

.btn-outline-primary:hover,
.btn-outline-primary:focus-visible,
[data-bs-theme="dark"] .btn-outline-primary:hover,
[data-bs-theme="dark"] .btn-outline-primary:focus-visible {
  color: var(--bs-btn-hover-color);
  background-color: var(--bs-btn-hover-bg);
  border-color: var(--bs-btn-hover-border-color);
}

/* ----------------------------------------------------------------------------
   Neutral buttons (secondary / light / dark families)
   Bootswatch Soft compiles these with near-white text (#d9e3f1 / #f0f5fa)
   that only worked on its dark neumorphic fill. On our flat light surfaces
   that renders as grey-on-grey (invisible until dark mode). Re-point every
   variant at the slate/surface tokens so they read clearly in both themes.
   Dark-scoped selectors are included so these also beat the legacy
   [data-bs-theme="dark"] patches in theme-custom.css.
   ---------------------------------------------------------------------------- */

/* Outline secondary — the workhorse "quiet action" button (Refresh, Email,
   Cancel, filters). Slate text + visible border on any surface. */
.btn-outline-secondary,
[data-bs-theme="dark"] .btn-outline-secondary {
  --bs-btn-color: var(--slam-text);
  --bs-btn-bg: transparent;
  --bs-btn-border-color: var(--slam-border-strong);
  --bs-btn-hover-color: var(--slam-text-strong);
  --bs-btn-hover-bg: var(--slam-surface-2);
  --bs-btn-hover-border-color: var(--slam-border-strong);
  --bs-btn-active-color: var(--slam-text-strong);
  --bs-btn-active-bg: var(--slam-surface-3);
  --bs-btn-active-border-color: var(--slam-border-strong);
  --bs-btn-disabled-color: var(--slam-text-muted);
  --bs-btn-disabled-bg: transparent;
  --bs-btn-disabled-border-color: var(--slam-border);
  --bs-btn-focus-shadow-rgb: var(--slam-accent-rgb);
  color: var(--bs-btn-color);
  background-color: var(--bs-btn-bg);
  border-color: var(--bs-btn-border-color);
}

.btn-outline-secondary:hover,
.btn-outline-secondary:focus-visible,
[data-bs-theme="dark"] .btn-outline-secondary:hover,
[data-bs-theme="dark"] .btn-outline-secondary:focus-visible {
  color: var(--bs-btn-hover-color);
  background-color: var(--bs-btn-hover-bg);
  border-color: var(--bs-btn-hover-border-color);
}

.btn-outline-secondary:active,
.btn-outline-secondary.active,
[data-bs-theme="dark"] .btn-outline-secondary:active,
[data-bs-theme="dark"] .btn-outline-secondary.active {
  color: var(--bs-btn-active-color);
  background-color: var(--bs-btn-active-bg);
  border-color: var(--bs-btn-active-border-color);
}

/* Solid secondary — a filled neutral chip that is clearly a button:
   one surface step above the page plus a real border. */
.btn-secondary,
[data-bs-theme="dark"] .btn-secondary {
  --bs-btn-color: var(--slam-text-strong);
  --bs-btn-bg: var(--slam-surface-2);
  --bs-btn-border-color: var(--slam-border-strong);
  --bs-btn-hover-color: var(--slam-text-strong);
  --bs-btn-hover-bg: var(--slam-surface-3);
  --bs-btn-hover-border-color: var(--slam-border-strong);
  --bs-btn-active-color: var(--slam-text-strong);
  --bs-btn-active-bg: var(--slam-surface-3);
  --bs-btn-active-border-color: var(--slam-border-strong);
  --bs-btn-disabled-color: var(--slam-text-muted);
  --bs-btn-disabled-bg: var(--slam-surface-2);
  --bs-btn-disabled-border-color: var(--slam-border);
  color: var(--bs-btn-color);
  background-color: var(--bs-btn-bg);
  border-color: var(--bs-btn-border-color);
}

.btn-secondary:hover,
.btn-secondary:focus-visible,
.btn-secondary:active,
.btn-secondary.active,
[data-bs-theme="dark"] .btn-secondary:hover,
[data-bs-theme="dark"] .btn-secondary:focus-visible,
[data-bs-theme="dark"] .btn-secondary:active,
[data-bs-theme="dark"] .btn-secondary.active {
  color: var(--bs-btn-hover-color);
  background-color: var(--bs-btn-hover-bg);
  border-color: var(--bs-btn-hover-border-color);
}

/* Light — same neutral-surface treatment (Bootswatch shipped it with
   light-on-light text). Still reads as a pale chip on colored headers. */
.btn-light,
[data-bs-theme="dark"] .btn-light {
  --bs-btn-color: var(--slam-text-strong);
  --bs-btn-bg: var(--slam-surface-2);
  --bs-btn-border-color: var(--slam-border);
  --bs-btn-hover-color: var(--slam-text-strong);
  --bs-btn-hover-bg: var(--slam-surface-3);
  --bs-btn-hover-border-color: var(--slam-border-strong);
  --bs-btn-active-color: var(--slam-text-strong);
  --bs-btn-active-bg: var(--slam-surface-3);
  --bs-btn-active-border-color: var(--slam-border-strong);
  --bs-btn-disabled-color: var(--slam-text-muted);
  --bs-btn-disabled-bg: var(--slam-surface-2);
  --bs-btn-disabled-border-color: var(--slam-border);
  color: var(--bs-btn-color);
  background-color: var(--bs-btn-bg);
  border-color: var(--bs-btn-border-color);
}

.btn-light:hover,
.btn-light:focus-visible,
.btn-light:active,
.btn-light.active,
[data-bs-theme="dark"] .btn-light:hover,
[data-bs-theme="dark"] .btn-light:focus-visible,
[data-bs-theme="dark"] .btn-light:active,
[data-bs-theme="dark"] .btn-light.active {
  color: var(--bs-btn-hover-color);
  background-color: var(--bs-btn-hover-bg);
  border-color: var(--bs-btn-hover-border-color);
}

/* Outline light — meant for colored/dark hero headers only. Constant white
   ink in both themes so it never vanishes on an accent background. */
.btn-outline-light,
[data-bs-theme="dark"] .btn-outline-light {
  --bs-btn-color: #f8fafc;
  --bs-btn-bg: transparent;
  --bs-btn-border-color: rgba(248, 250, 252, 0.55);
  --bs-btn-hover-color: #ffffff;
  --bs-btn-hover-bg: rgba(248, 250, 252, 0.18);
  --bs-btn-hover-border-color: rgba(248, 250, 252, 0.75);
  --bs-btn-active-color: #ffffff;
  --bs-btn-active-bg: rgba(248, 250, 252, 0.26);
  --bs-btn-active-border-color: rgba(248, 250, 252, 0.8);
  color: var(--bs-btn-color);
  background-color: var(--bs-btn-bg);
  border-color: var(--bs-btn-border-color);
}

.btn-outline-light:hover,
.btn-outline-light:focus-visible,
.btn-outline-light:active,
.btn-outline-light.active,
[data-bs-theme="dark"] .btn-outline-light:hover,
[data-bs-theme="dark"] .btn-outline-light:focus-visible,
[data-bs-theme="dark"] .btn-outline-light:active,
[data-bs-theme="dark"] .btn-outline-light.active {
  color: var(--bs-btn-hover-color);
  background-color: var(--bs-btn-hover-bg);
  border-color: var(--bs-btn-hover-border-color);
}

/* Dark / outline dark — theme-aware "high emphasis neutral": dark ink on a
   light page, light ink on a dark page (fixes invisible dark-on-dark). */
.btn-dark,
[data-bs-theme="dark"] .btn-dark {
  --bs-btn-color: var(--slam-surface-1);
  --bs-btn-bg: var(--slam-text-strong);
  --bs-btn-border-color: var(--slam-text-strong);
  --bs-btn-hover-color: var(--slam-surface-1);
  --bs-btn-hover-bg: var(--slam-text);
  --bs-btn-hover-border-color: var(--slam-text);
  --bs-btn-active-color: var(--slam-surface-1);
  --bs-btn-active-bg: var(--slam-text);
  --bs-btn-active-border-color: var(--slam-text);
  --bs-btn-disabled-color: var(--slam-surface-1);
  --bs-btn-disabled-bg: var(--slam-text-muted);
  --bs-btn-disabled-border-color: var(--slam-text-muted);
  color: var(--bs-btn-color);
  background-color: var(--bs-btn-bg);
  border-color: var(--bs-btn-border-color);
}

.btn-dark:hover,
.btn-dark:focus-visible,
.btn-dark:active,
.btn-dark.active,
[data-bs-theme="dark"] .btn-dark:hover,
[data-bs-theme="dark"] .btn-dark:focus-visible,
[data-bs-theme="dark"] .btn-dark:active,
[data-bs-theme="dark"] .btn-dark.active {
  color: var(--bs-btn-hover-color);
  background-color: var(--bs-btn-hover-bg);
  border-color: var(--bs-btn-hover-border-color);
}

.btn-outline-dark,
[data-bs-theme="dark"] .btn-outline-dark {
  --bs-btn-color: var(--bs-emphasis-color);
  --bs-btn-bg: transparent;
  --bs-btn-border-color: var(--slam-border-strong);
  --bs-btn-hover-color: var(--bs-emphasis-color);
  --bs-btn-hover-bg: var(--slam-surface-3);
  --bs-btn-hover-border-color: var(--slam-border-strong);
  --bs-btn-active-color: var(--bs-emphasis-color);
  --bs-btn-active-bg: var(--slam-surface-3);
  --bs-btn-active-border-color: var(--slam-border-strong);
  color: var(--bs-btn-color);
  background-color: var(--bs-btn-bg);
  border-color: var(--bs-btn-border-color);
}

.btn-outline-dark:hover,
.btn-outline-dark:focus-visible,
.btn-outline-dark:active,
.btn-outline-dark.active,
[data-bs-theme="dark"] .btn-outline-dark:hover,
[data-bs-theme="dark"] .btn-outline-dark:focus-visible,
[data-bs-theme="dark"] .btn-outline-dark:active,
[data-bs-theme="dark"] .btn-outline-dark.active {
  color: var(--bs-btn-hover-color);
  background-color: var(--bs-btn-hover-bg);
  border-color: var(--bs-btn-hover-border-color);
}

.btn-link {
  --bs-btn-color: var(--bs-link-color);
  --bs-btn-hover-color: var(--bs-link-hover-color);
  --bs-btn-bg: transparent;
  --bs-btn-border-color: transparent;
  --bs-btn-hover-bg: transparent;
  --bs-btn-hover-border-color: transparent;
  background-color: transparent !important;
  border-color: transparent !important;
  box-shadow: none !important;
  color: var(--bs-link-color) !important;
  /* Bootstrap underlines .btn-link, but most of ours wrap a single icon (theme
     toggle, favourite star, kebab menus). The underline lands behind the glyph
     and text-decoration-skip-ink punches holes in it where the strokes cross,
     so it renders as stray dashes rather than a line. Anchors are already
     undecorated above, so this just brings link-buttons into line with them. */
  text-decoration: none;
}

.btn-link:hover,
.btn-link:focus,
.btn-link:focus-visible {
  background-color: transparent !important;
  border-color: transparent !important;
  color: var(--bs-link-hover-color) !important;
  box-shadow: none !important;
  text-decoration: none;
}

/* Keep the header theme toggle visible and high-contrast in both themes. */
.theme-toggle.btn-link,
.top-nav .theme-toggle {
  display: inline-flex !important;
  align-items: center;
  justify-content: center;
  color: var(--bs-emphasis-color, var(--custom-text, inherit)) !important;
  /* Square hit area so the round hover state isn't an oval. Spacing from its
     neighbours is owned by the .nav-right gap, not a margin on this button. */
  min-width: 2.5rem;
  min-height: 2.5rem;
  padding: 0.55rem !important;
  opacity: 1 !important;
  visibility: visible !important;
}

.theme-toggle.btn-link:hover,
.top-nav .theme-toggle:hover {
  color: var(--slam-accent) !important;
  background-color: var(--slam-accent-soft, rgba(128, 128, 128, 0.15)) !important;
}

.theme-toggle .theme-icon-when-light,
.theme-toggle .theme-icon-when-dark {
  font-size: 1.15rem;
}

html[data-theme="light"] .theme-toggle .theme-icon-when-light,
html[data-bs-theme="light"] .theme-toggle .theme-icon-when-light {
  display: inline-block !important;
}

html[data-theme="light"] .theme-toggle .theme-icon-when-dark,
html[data-bs-theme="light"] .theme-toggle .theme-icon-when-dark {
  display: none !important;
}

html[data-theme="dark"] .theme-toggle .theme-icon-when-dark,
html[data-bs-theme="dark"] .theme-toggle .theme-icon-when-dark {
  display: inline-block !important;
}

html[data-theme="dark"] .theme-toggle .theme-icon-when-light,
html[data-bs-theme="dark"] .theme-toggle .theme-icon-when-light {
  display: none !important;
}

.btn:disabled,
.btn.disabled,
fieldset:disabled .btn,
[data-bs-theme="dark"] .btn:disabled,
[data-bs-theme="dark"] .btn.disabled,
[data-bs-theme="dark"] fieldset:disabled .btn {
  color: var(--bs-btn-disabled-color);
  background-color: var(--bs-btn-disabled-bg);
  border-color: var(--bs-btn-disabled-border-color);
  opacity: var(--bs-btn-disabled-opacity, 0.65);
  box-shadow: none;
  pointer-events: none;
}

/* ----------------------------------------------------------------------------
   Cards / panels — real elevation above the app background
   ---------------------------------------------------------------------------- */
.card {
  --bs-card-bg: var(--slam-card-bg);
  --bs-card-border-color: var(--slam-border);
  --bs-card-border-radius: var(--slam-radius-lg);
  --bs-card-inner-border-radius: calc(var(--slam-radius-lg) - 1px);
  background-color: var(--slam-card-bg);
  /* Hairline border + soft elevation + a faint glassy top highlight. We lean
     on elevation and spacing rather than heavy borders to avoid the
     "boxes in boxes" look. */
  border: 1px solid var(--slam-border);
  border-radius: var(--slam-radius-lg);
  box-shadow: var(--slam-shadow-1), var(--slam-glass-highlight);
  transition: box-shadow var(--slam-dur) var(--slam-ease),
    transform var(--slam-dur) var(--slam-ease),
    border-color var(--slam-dur) var(--slam-ease);
}

/* Frameless card chrome: a quiet divider instead of a filled inner box. */
.card-header {
  background-color: transparent;
  border-bottom: 1px solid var(--slam-border);
  font-weight: 600;
  color: var(--bs-emphasis-color);
}

.card-footer {
  background-color: transparent;
  border-top: 1px solid var(--slam-border);
}

/* Nested cards should not stack borders + shadows (kills "boxes in boxes"). */
.card .card {
  box-shadow: none;
  border-color: var(--slam-border);
}

/* Subtle lift only on cards explicitly marked interactive, so dense report
   pages don't jitter on hover. */
.card.is-interactive,
a.card,
.card.card-hover {
  cursor: pointer;
}
.card.is-interactive:hover,
a.card:hover,
.card.card-hover:hover {
  transform: translateY(-2px);
  box-shadow: var(--slam-shadow-2);
  border-color: var(--slam-border-strong);
}

/* Keep the existing slam-* surfaces aligned with the new elevation scale. */
.slam-panel,
.slam-data-panel {
  background: var(--slam-surface-1);
  border-color: var(--slam-border);
  box-shadow: var(--slam-shadow-1);
}
.slam-stat-card {
  background: var(--slam-surface-1);
  border-color: var(--slam-border);
  border-left-color: var(--slam-accent);
  box-shadow: var(--slam-shadow-1);
}
.slam-toolbar {
  background: var(--slam-surface-1);
  border-color: var(--slam-border);
  box-shadow: var(--slam-shadow-1);
}

/* ----------------------------------------------------------------------------
   Forms — clearer inputs, accent focus ring
   ---------------------------------------------------------------------------- */
.form-control,
.form-select,
.input-group-text {
  border-radius: var(--slam-radius-sm);
  border-color: var(--slam-border-strong);
  background-color: var(--slam-surface-1);
  color: var(--bs-body-color);
  transition: border-color var(--slam-dur-fast) var(--slam-ease),
    box-shadow var(--slam-dur-fast) var(--slam-ease),
    background-color var(--slam-dur-fast) var(--slam-ease);
}

.form-control::placeholder {
  color: var(--slam-text-muted);
  opacity: 0.8;
}

.form-control:focus,
.form-select:focus {
  border-color: var(--slam-accent);
  box-shadow: var(--slam-focus-ring);
  background-color: var(--slam-surface-1);
}

.form-control:disabled,
.form-select:disabled {
  background-color: var(--slam-surface-2);
}

.form-label {
  font-weight: 600;
  color: var(--bs-emphasis-color);
  margin-bottom: 0.35rem;
}

.form-check-input:checked {
  background-color: var(--slam-accent);
  border-color: var(--slam-accent);
}
.form-check-input:focus {
  border-color: var(--slam-accent);
  box-shadow: var(--slam-focus-ring);
}
.form-switch .form-check-input:focus {
  box-shadow: var(--slam-focus-ring);
}

.input-group-text {
  background-color: var(--slam-surface-2);
}

/* Select2 alignment with the new input look. */
.select2-container--default .select2-selection,
.select2-container--bootstrap4 .select2-selection,
.select2-container--bootstrap-5 .select2-selection {
  border-radius: var(--slam-radius-sm) !important;
  border-color: var(--slam-border-strong) !important;
  background-color: var(--slam-surface-1) !important;
  min-height: calc(1.5em + 0.75rem + 2px);
}
.select2-container--default.select2-container--focus .select2-selection {
  border-color: var(--slam-accent) !important;
  box-shadow: var(--slam-focus-ring) !important;
}
.select2-dropdown {
  border-color: var(--slam-border-strong) !important;
  background-color: var(--slam-surface-1) !important;
  border-radius: var(--slam-radius-sm) !important;
  box-shadow: var(--slam-shadow-2) !important;
  z-index: 1060;
}
.modal .select2-dropdown {
  z-index: 1060;
}
.select2-container--default .select2-results__option--highlighted[aria-selected] {
  background-color: var(--slam-accent) !important;
}

/* ----------------------------------------------------------------------------
   Tables — report-grade clarity
   ---------------------------------------------------------------------------- */
.table {
  --bs-table-bg: transparent;
  --bs-table-color: var(--bs-body-color);
  --bs-table-border-color: var(--slam-border);
  --bs-table-striped-bg: var(--slam-surface-2);
  --bs-table-hover-bg: var(--slam-accent-soft);
  --bs-table-hover-color: var(--bs-emphasis-color);
  color: var(--bs-body-color);
  border-color: var(--slam-border);
}

.table > thead th {
  background-color: var(--slam-surface-2);
  color: var(--bs-emphasis-color);
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  border-bottom: 1px solid var(--slam-border-strong);
  white-space: nowrap;
}

.table > tbody > tr {
  transition: background-color var(--slam-dur-fast) var(--slam-ease);
}

.table-hover > tbody > tr:hover > * {
  background-color: var(--slam-accent-soft);
}

/* Sticky report header for scrollable tables. */
.table-responsive.slam-table-wrap {
  border-radius: var(--slam-radius-md);
  border-color: var(--slam-border);
}
.table-responsive.slam-table-wrap .table thead th {
  background: var(--slam-surface-2);
  box-shadow: inset 0 -1px 0 var(--slam-border-strong);
}

/* ----------------------------------------------------------------------------
   Modals / offcanvas / dropdowns / popovers
   ---------------------------------------------------------------------------- */
/* Reusable frosted-glass surface (Apple-style). Apply to hero panels/overlays
   where there is content behind to refract. Kept off dense grids/tables. */
.slam-glass {
  background: var(--slam-glass-bg);
  -webkit-backdrop-filter: var(--slam-glass-blur);
  backdrop-filter: var(--slam-glass-blur);
  border: 1px solid var(--slam-glass-border);
  border-radius: var(--slam-radius-lg);
  box-shadow: var(--slam-shadow-2), var(--slam-glass-highlight);
}

.modal-content {
  border: 1px solid var(--slam-glass-border);
  border-radius: var(--slam-radius-lg);
  background-color: var(--slam-glass-bg-strong);
  -webkit-backdrop-filter: var(--slam-glass-blur);
  backdrop-filter: var(--slam-glass-blur);
  box-shadow: var(--slam-shadow-3), var(--slam-glass-highlight);
}
.modal-header,
.modal-footer {
  border-color: var(--slam-glass-hairline);
}
.offcanvas {
  background-color: var(--slam-glass-bg-strong);
  -webkit-backdrop-filter: var(--slam-glass-blur);
  backdrop-filter: var(--slam-glass-blur);
  color: var(--bs-body-color);
  border-color: var(--slam-glass-border);
}

.dropdown-menu {
  --bs-dropdown-bg: transparent;
  --bs-dropdown-border-color: var(--slam-glass-border);
  --bs-dropdown-border-radius: var(--slam-radius-md);
  --bs-dropdown-link-hover-bg: var(--slam-surface-2);
  --bs-dropdown-link-active-bg: var(--slam-accent);
  --bs-dropdown-link-color: var(--bs-body-color);
  background: var(--slam-glass-bg-strong);
  -webkit-backdrop-filter: var(--slam-glass-blur);
  backdrop-filter: var(--slam-glass-blur);
  border: 1px solid var(--slam-glass-border);
  border-radius: var(--slam-radius-md);
  box-shadow: var(--slam-shadow-2), var(--slam-glass-highlight);
  padding: 0.35rem;
}
.dropdown-item {
  border-radius: var(--slam-radius-sm);
  padding: 0.45rem 0.7rem;
  transition: background-color var(--slam-dur-fast) var(--slam-ease),
    color var(--slam-dur-fast) var(--slam-ease);
}

.popover {
  border-color: var(--slam-border);
  border-radius: var(--slam-radius-md);
  box-shadow: var(--slam-shadow-2);
}

/* ----------------------------------------------------------------------------
   Tabs / nav pills
   ---------------------------------------------------------------------------- */
.nav-tabs {
  --bs-nav-tabs-border-color: var(--slam-border);
  border-bottom: 1px solid var(--slam-border);
}
.nav-tabs .nav-link {
  border: none;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  /* Explicit transparent background so page-level light-only tab styles
     (e.g. background-color: #f8f9fa) do not survive into dark mode. This
     loads after page <style> blocks, so it wins the cascade globally. */
  background-color: transparent;
  color: var(--slam-text-muted);
  font-weight: 600;
  transition: color var(--slam-dur-fast) var(--slam-ease),
    border-color var(--slam-dur-fast) var(--slam-ease),
    background-color var(--slam-dur-fast) var(--slam-ease);
}
.nav-tabs .nav-link:hover {
  color: var(--bs-emphasis-color);
  background-color: var(--slam-surface-2);
  border-bottom-color: var(--slam-border-strong);
}
.nav-tabs .nav-link.active {
  color: var(--slam-accent);
  background-color: transparent;
  border-bottom-color: var(--slam-accent);
}

.nav-pills {
  --bs-nav-pills-border-radius: var(--slam-radius-pill);
  --bs-nav-pills-link-active-bg: var(--slam-accent);
}
.nav-pills .nav-link {
  border-radius: var(--slam-radius-pill);
  font-weight: 600;
  color: var(--slam-text-muted);
}
.nav-pills .nav-link.active {
  background-color: var(--slam-accent);
  color: var(--slam-accent-contrast);
}

/* ----------------------------------------------------------------------------
   Badges / alerts / progress / pagination / list-group
   ---------------------------------------------------------------------------- */
.badge {
  font-weight: 600;
  letter-spacing: 0.01em;
  border-radius: var(--slam-radius-sm);
}
.badge.rounded-pill {
  border-radius: var(--slam-radius-pill);
}
.text-bg-primary,
.badge.bg-primary {
  background-color: var(--slam-accent) !important;
  color: var(--slam-accent-contrast) !important;
}

.alert {
  border-radius: var(--slam-radius-md);
  border: 1px solid transparent;
}
.alert-primary {
  --bs-alert-color: var(--slam-accent-active);
  --bs-alert-bg: rgba(var(--slam-accent-rgb), 0.10);
  --bs-alert-border-color: rgba(var(--slam-accent-rgb), 0.30);
}

.progress {
  border-radius: var(--slam-radius-pill);
  background-color: var(--slam-surface-2);
}
.progress-bar {
  background-color: var(--slam-accent);
}

.pagination {
  --bs-pagination-color: var(--bs-body-color);
  --bs-pagination-bg: var(--slam-surface-1);
  --bs-pagination-border-color: var(--slam-border);
  --bs-pagination-hover-bg: var(--slam-surface-2);
  --bs-pagination-hover-color: var(--bs-emphasis-color);
  --bs-pagination-active-bg: var(--slam-accent);
  --bs-pagination-active-border-color: var(--slam-accent);
  --bs-pagination-focus-box-shadow: var(--slam-focus-ring);
  --bs-pagination-border-radius: var(--slam-radius-sm);
}

.list-group {
  --bs-list-group-bg: var(--slam-surface-1);
  --bs-list-group-border-color: var(--slam-border);
  --bs-list-group-action-hover-bg: var(--slam-surface-2);
  --bs-list-group-active-bg: var(--slam-accent);
  --bs-list-group-active-border-color: var(--slam-accent);
  --bs-list-group-border-radius: var(--slam-radius-md);
}

/* ----------------------------------------------------------------------------
   Accent-driven SLAM components (override the few hardcoded blue accents in
   page-chrome.css so they follow the new brand color).
   ---------------------------------------------------------------------------- */
.info-tile.is-primary .info-tile__icon,
.info-tile:not(.is-success):not(.is-danger):not(.is-warning):not(.is-info):not(.is-muted) .info-tile__icon {
  background: var(--slam-accent-soft-strong);
  color: var(--slam-accent);
}
.slam-pill.is-primary {
  background: var(--slam-accent-soft-strong);
  color: var(--slam-accent);
  border-color: rgba(var(--slam-accent-rgb), 0.35);
}
[data-bs-theme="dark"] .slam-pill.is-primary {
  color: var(--slam-accent-active);
}

/* ----------------------------------------------------------------------------
   Scrollbars — quiet, on-brand
   ---------------------------------------------------------------------------- */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--slam-border-strong) transparent;
}
*::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
*::-webkit-scrollbar-thumb {
  background-color: var(--slam-border-strong);
  border-radius: var(--slam-radius-pill);
  border: 2px solid transparent;
  background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover {
  background-color: var(--slam-text-muted);
  background-clip: padding-box;
}

/* ----------------------------------------------------------------------------
   Dark-mode safety net
   Bootstrap's .bg-light / .bg-white / .table-light are fixed light colors that
   do NOT adapt to the theme. When used as surfaces they become bright boxes in
   dark mode. Re-point them to dark surface tokens so pages that rely on these
   helpers stay readable in both modes. Scoped to dark only, so light mode is
   untouched. (Note: base.html already maps .bg-white; this completes the set.)
   ---------------------------------------------------------------------------- */
[data-bs-theme="dark"] .bg-light,
[data-bs-theme="dark"] .bg-white {
  background-color: var(--slam-surface-2) !important;
  color: var(--bs-body-color);
}
[data-bs-theme="dark"] .table-light,
[data-bs-theme="dark"] .table-light > th,
[data-bs-theme="dark"] .table-light > td {
  --bs-table-bg: var(--slam-surface-2);
  --bs-table-color: var(--bs-emphasis-color);
  background-color: var(--slam-surface-2);
  color: var(--bs-emphasis-color);
}

/* ============================================================================
   Consolidated control library (additive)
   Canonical shared components that replace per-template copies. These are
   NEW class names — nothing existing is overridden. Showcased on the
   developer Style Library page (/dev/style-library/).
   ============================================================================ */

/* ---- Modern checks & radios everywhere ----
   1) Baseline: any native checkbox/radio (including ones injected by JS with
      no class, e.g. map filter trees) picks up the brand accent instead of
      the OS default blue.
   2) Bridge: Bootstrap-4-era .custom-control-input (still used by FLHA / PPE
      hazard checklists) gets the full Bootstrap-5 .form-check-input treatment
      — soft rounded box, accent fill, themed borders — so those pages stop
      looking legacy without editing hundreds of inputs. */
input[type="checkbox"],
input[type="radio"] {
  accent-color: var(--slam-accent);
}

.custom-control {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  min-height: 1.5rem;
}

.custom-control-input {
  appearance: none;
  -webkit-appearance: none;
  flex: 0 0 auto;
  width: 1.1em;
  height: 1.1em;
  margin-top: 0.2em;
  vertical-align: top;
  background-color: var(--bs-body-bg);
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  border: 1px solid var(--slam-border-strong);
  border-radius: 0.32em;
  transition: background-color var(--slam-dur-fast) var(--slam-ease),
    border-color var(--slam-dur-fast) var(--slam-ease),
    box-shadow var(--slam-dur-fast) var(--slam-ease);
  cursor: pointer;
}

.custom-control-input[type="radio"] {
  border-radius: 50%;
}

.custom-control-input:checked {
  background-color: var(--slam-accent);
  border-color: var(--slam-accent);
}

.custom-control-input[type="checkbox"]:checked {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
}

.custom-control-input[type="radio"]:checked {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e");
}

.custom-control-input:focus-visible {
  outline: 0;
  box-shadow: var(--slam-focus-ring);
  border-color: var(--slam-accent);
}

.custom-control-input:disabled {
  opacity: 0.5;
  cursor: default;
}

.custom-control-label {
  cursor: pointer;
  margin-bottom: 0;
}

/* ---- Scaffold tag status badge (green / yellow / red) ----
   One canonical badge for tag status everywhere: lists, dashboards, map
   info windows. Solid = the physical tag color; soft = tinted chip for
   dense tables. Colors come from --slam-status-* tokens only. */
/* ---- Yes/No toggle for important booleans ----
   Replaces easy-to-miss small checkboxes on high-consequence toggles
   (e.g. "Final Report" on progress reports). A real <input type=checkbox>
   stays underneath — same name/id/checked semantics, so forms and JS work
   unchanged. The currently-active segment ignores pointer events, which
   gives set-semantics for free: clicking "No" while No can't accidentally
   flip the value to Yes.

   Markup:
     <span class="slam-yesno">
       <input type="checkbox" id="x" name="x">
       <label class="slam-yesno-option is-no" for="x">No</label>
       <label class="slam-yesno-option is-yes" for="x">Yes</label>
     </span>                                                         */
.slam-yesno {
  position: relative;
  display: inline-flex;
  align-items: stretch;
  border-radius: var(--slam-radius-pill);
  border: 1px solid var(--slam-border-strong);
  background: var(--slam-surface-2);
  overflow: hidden;
  vertical-align: middle;
}

.slam-yesno > input[type="checkbox"],
.slam-yesno > input[type="radio"] {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  margin: 0;
  pointer-events: none;
}

.slam-yesno:focus-within {
  box-shadow: var(--slam-focus-ring);
}

.slam-yesno-option {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 76px;
  padding: 0.55rem 1.2rem;
  margin: 0;
  font-size: 0.85rem;
  font-weight: 800;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  line-height: 1.2;
  color: var(--slam-text-muted);
  cursor: pointer;
  user-select: none;
  transition: background-color var(--slam-dur-fast) var(--slam-ease),
    color var(--slam-dur-fast) var(--slam-ease);
}

.slam-yesno-option + .slam-yesno-option {
  border-left: 1px solid var(--slam-border);
}

.slam-yesno input:not(:checked) ~ .slam-yesno-option.is-no {
  background: var(--slam-status-danger);
  color: var(--slam-status-danger-contrast);
  pointer-events: none;
}

.slam-yesno input:checked ~ .slam-yesno-option.is-yes {
  background: var(--slam-status-ok);
  color: var(--slam-status-ok-contrast);
  pointer-events: none;
}

.slam-yesno input:disabled ~ .slam-yesno-option {
  pointer-events: none;
  opacity: 0.55;
  cursor: not-allowed;
}

.slam-yesno.is-sm .slam-yesno-option {
  min-width: 56px;
  padding: 0.3rem 0.8rem;
  font-size: 0.72rem;
}

.slam-yesno.is-lg .slam-yesno-option {
  min-width: 96px;
  padding: 0.75rem 1.5rem;
  font-size: 0.95rem;
}

/* ---- Segmented choice — compact Yes/No and other short option groups ----
   For the many existing `.btn-group` toggles built from `.btn-check` radios
   (work pack create/edit, requisitions). A `w-100` group on a 1700px form
   renders a two-word answer as a page-wide banner, so the field stops reading
   as one question's answer. This sizes the group to its buttons on anything
   wider than a phone, and only goes edge-to-edge on phones, where a
   full-width thumb target is the right call.

   Markup: replace `w-100` with this class on the group.
     <div class="btn-group slam-segmented" role="group" aria-label="...">   */
.slam-segmented {
  max-width: 100%;
}

.slam-segmented > .btn {
  min-width: 6rem;
}

@media (max-width: 575.98px) {
  .slam-segmented {
    display: flex;
    width: 100%;
  }

  .slam-segmented > .btn {
    flex: 1 1 0;
    min-width: 0;
  }
}

/* ---- Narrow field — inputs whose content has a fixed short length ----
   Coordinates, unit counts, short codes. A 12-character number in a 400px
   box reads as an empty field with a stray value in it. Capping the control
   (not the column) keeps it inside a normal grid cell and still collapses
   with the column on small screens. */
.slam-field-narrow {
  max-width: 16rem;
}

.slam-status-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.28em 0.7em;
  border-radius: var(--slam-radius-pill);
  font-size: 0.78rem;
  font-weight: 600;
  line-height: 1.25;
  white-space: nowrap;
  border: 1px solid transparent;
}

.slam-status-badge.is-ok {
  background: var(--slam-status-ok);
  color: var(--slam-status-ok-contrast);
}
.slam-status-badge.is-caution {
  background: var(--slam-status-caution);
  color: var(--slam-status-caution-contrast);
}
.slam-status-badge.is-danger {
  background: var(--slam-status-danger);
  color: var(--slam-status-danger-contrast);
}

.slam-status-badge.is-soft.is-ok {
  background: var(--slam-status-ok-soft);
  color: var(--slam-status-ok-text);
  border-color: rgba(var(--slam-status-ok-rgb), 0.35);
}
.slam-status-badge.is-soft.is-caution {
  background: var(--slam-status-caution-soft);
  color: var(--slam-status-caution-text);
  border-color: rgba(var(--slam-status-caution-rgb), 0.4);
}
.slam-status-badge.is-soft.is-danger {
  background: var(--slam-status-danger-soft);
  color: var(--slam-status-danger-text);
  border-color: rgba(var(--slam-status-danger-rgb), 0.4);
}

/* Status dot for tight spaces (table cells, map legends). */
.slam-status-dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex-shrink: 0;
  vertical-align: middle;
}
.slam-status-dot.is-ok { background: var(--slam-status-ok); }
.slam-status-dot.is-caution { background: var(--slam-status-caution); }
.slam-status-dot.is-danger { background: var(--slam-status-danger); }

/* ---- Avatar / circular icon ----
   Distortion-proof circle: equal box, flex-shrink locked, and object-fit
   baked in for photos. Replaces the per-page .stat-icon / .metric-icon /
   .employee-*-avatar copies (the source of "stretched circles"). */
.slam-avatar {
  --slam-avatar-size: 40px;
  width: var(--slam-avatar-size);
  height: var(--slam-avatar-size);
  min-width: var(--slam-avatar-size);
  border-radius: 50%;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: var(--slam-accent-soft);
  color: var(--slam-accent);
  font-weight: 600;
}
.slam-avatar.is-sm { --slam-avatar-size: 28px; font-size: 0.72rem; }
.slam-avatar.is-lg { --slam-avatar-size: 64px; font-size: 1.4rem; }
.slam-avatar > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ----------------------------------------------------------------------------
   Hazard YES/NO pills (scaffold tag create / edit / update / quick update)
   YES = red (hazard present), NO = green (clear) — matches tag detail display.
   ---------------------------------------------------------------------------- */
.hazard-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.5rem;
  padding: 0.75rem 1rem;
  border-radius: var(--slam-radius-md);
  background-color: var(--section-bg, var(--slam-surface-2));
  border: 1px solid var(--info-border, var(--slam-border));
  color: var(--info-text, var(--bs-body-color));
  transition: background-color var(--slam-dur-fast) var(--slam-ease);
}

.hazard-label {
  font-weight: 600;
  font-size: 1rem;
}

.hazard-yesno {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-shrink: 0;
}

.hazard-choice {
  padding: 0.5rem 1.5rem;
  border-radius: var(--slam-radius-pill);
  min-width: 80px;
  text-align: center;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  border: 2px solid transparent;
  cursor: pointer;
  opacity: 0.45;
  transition: background-color 140ms ease, border-color 140ms ease,
    color 140ms ease, opacity 140ms ease, transform 140ms ease;
  background: transparent;
  line-height: 1.2;
}

.hazard-choice:hover {
  opacity: 0.75;
  transform: translateY(-1px);
}

.hazard-choice:focus-visible {
  outline: none;
  box-shadow: var(--slam-focus-ring);
  opacity: 1;
}

.hazard-choice.yes {
  color: #dc3545;
  border-color: #dc3545;
  background: rgba(220, 53, 69, 0.12);
}

.hazard-choice.no {
  color: #28a745;
  border-color: #28a745;
  background: rgba(40, 167, 69, 0.12);
}

.hazard-choice.yes.active {
  background-color: #dc3545;
  border-color: #dc3545;
  color: #fff;
  opacity: 1;
}

.hazard-choice.no.active {
  background-color: #28a745;
  border-color: #28a745;
  color: #fff;
  opacity: 1;
}

.hazard-yesno--positive .hazard-choice.yes {
  color: #28a745;
  border-color: #28a745;
  background: rgba(40, 167, 69, 0.12);
}

.hazard-yesno--positive .hazard-choice.no {
  color: #dc3545;
  border-color: #dc3545;
  background: rgba(220, 53, 69, 0.12);
}

.hazard-yesno--positive .hazard-choice.yes.active {
  background-color: #28a745;
  border-color: #28a745;
  color: #fff;
}

.hazard-yesno--positive .hazard-choice.no.active {
  background-color: #dc3545;
  border-color: #dc3545;
  color: #fff;
}

@media (max-width: 576px) {
  .hazard-item {
    flex-wrap: wrap;
  }
  .hazard-yesno {
    width: 100%;
    justify-content: flex-end;
  }
}

/* ==========================================================================
   Placement panel — Area / Floor / Elevation
   Three fields that answer one question (where is this work), all three set
   by dropping the marker. They sit in one panel directly above the map on
   the create/edit forms and in the Location section of detail pages, so the
   auto-fill is something the user watches happen rather than discovers.
   ========================================================================== */
.slam-placement-panel {
  border: 1px solid var(--slam-border, var(--bs-border-color));
  border-radius: var(--slam-radius-md, 10px);
  background: var(--slam-surface-2, var(--bs-secondary-bg));
  padding: 0.9rem 1rem 0.35rem;
}

.slam-placement-panel__hint {
  font-size: 0.82rem;
  color: var(--bs-secondary-color, var(--bs-secondary));
  margin-bottom: 0.65rem;
}

/* Unit and reference toggles have to stay usable when the field is a third
   of the row, so they give up padding before they squeeze the value box. */
.slam-placement-panel .btn-group .btn {
  padding-inline: 0.55rem;
}

/* A field the marker just filled in. Without a signal, a value that changed
   while the user was looking at the map reads as one that was always there. */
@keyframes slam-field-flash {
  from {
    border-color: var(--slam-accent, var(--bs-primary));
    box-shadow: 0 0 0 0.2rem rgba(var(--slam-accent-rgb, 13, 110, 253), 0.35);
  }
  to {
    border-color: var(--slam-border, var(--bs-border-color));
    box-shadow: none;
  }
}

.slam-field-flash {
  animation: slam-field-flash 1.3s var(--slam-ease, ease) 1;
}

@media (prefers-reduced-motion: reduce) {
  .slam-field-flash {
    animation: none;
    border-color: var(--slam-accent, var(--bs-primary));
  }
}

/* Read-only variant for detail pages. */
.slam-placement-panel__value {
  font-weight: 600;
  overflow-wrap: anywhere;
}

.slam-placement-panel__label {
  display: block;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--bs-secondary-color, var(--bs-secondary));
  margin-bottom: 0.15rem;
}

@media (max-width: 575.98px) {
  .slam-placement-panel {
    padding: 0.75rem 0.75rem 0.25rem;
  }
}

/* ==========================================================================
   Drawer — generic right slide-over with a tab bar
   Modelled on the onboarding drawer (core/css/onboarding.css) but written
   against SLAM tokens so any feature can use it. Onboarding keeps its own
   copy on purpose: generalising in place would risk regressing the one
   surface every new project owner sees first.

   Anatomy:
     .slam-drawer-overlay  dim layer, click to dismiss
     .slam-drawer          the panel; add .is-open to reveal
       .slam-drawer-header   .slam-drawer-title / -subtitle / -close
       .slam-drawer-tabs     .slam-drawer-tab (+ .active) with -tab-count
       .slam-drawer-body     scrolling content
       .slam-drawer-footer   actions
   ========================================================================== */
:root {
  /* Nav height (56px) plus clearance, so the drawer's title bar is not
     clipped behind the nav's bottom border. */
  --slam-drawer-top-offset: 64px;
  /* The always-on AI chat icon is fixed bottom-right at ~70px wide and sits
     above the drawer, so the footer keeps its buttons clear of it. */
  --slam-drawer-footer-right-clear: 80px;
  --slam-drawer-width: min(460px, 96vw);
}

.slam-drawer-overlay {
  position: fixed;
  inset: var(--slam-drawer-top-offset) 0 0 0;
  background: rgba(15, 23, 42, 0.38);
  z-index: 1080;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--slam-dur) var(--slam-ease);
}
.slam-drawer-overlay.is-open {
  opacity: 1;
  pointer-events: auto;
}

.slam-drawer {
  position: fixed;
  top: var(--slam-drawer-top-offset);
  right: 0;
  bottom: 0;
  width: var(--slam-drawer-width);
  display: flex;
  flex-direction: column;
  background: var(--slam-surface-1);
  color: var(--bs-body-color);
  border-left: 1px solid var(--slam-border);
  border-top: 1px solid var(--slam-border);
  box-shadow: var(--slam-shadow-3);
  z-index: 1090;
  transform: translateX(110%);
  transition: transform var(--slam-dur-slow) var(--slam-ease-out);
}
.slam-drawer.is-open {
  transform: translateX(0);
}
/* Kept in the flow while hidden so the slide-in animates from off-screen. */
.slam-drawer[hidden] {
  display: flex;
}

/* A dialog opened from the drawer has to outrank the drawer.
   Bootstrap stacks a modal at 1055 over a 1050 backdrop, both under the two
   numbers above, so "New message" opened *behind* the drawer's dim layer:
   greyed out, and every click meant for a field landed on the overlay and
   dismissed the drawer instead. Scoped to the open drawer so dialogs everywhere
   else keep Bootstrap's own order, which deliberately puts tooltips and
   popovers above a dialog. */
body.msg-drawer-open .modal-backdrop {
  z-index: 1095;
}
body.msg-drawer-open .modal {
  z-index: 1100;
}

.slam-drawer-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--slam-space-3);
  padding: var(--slam-space-4) var(--slam-space-5);
  border-bottom: 1px solid var(--slam-border);
}
.slam-drawer-title {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--slam-text-strong);
}
.slam-drawer-subtitle {
  margin: var(--slam-space-1) 0 0;
  font-size: 0.82rem;
  color: var(--slam-text-muted);
}
.slam-drawer-close {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--slam-text-muted);
  font-size: 1.1rem;
  line-height: 1;
  padding: var(--slam-space-1) 0.4rem;
  border-radius: var(--slam-radius-sm);
  cursor: pointer;
  transition: background-color var(--slam-dur-fast) var(--slam-ease),
    color var(--slam-dur-fast) var(--slam-ease);
}
.slam-drawer-close:hover {
  background: var(--slam-surface-3);
  color: var(--slam-text-strong);
}
.slam-drawer-close:focus-visible {
  outline: none;
  box-shadow: var(--slam-focus-ring);
}

.slam-drawer-tabs {
  display: flex;
  overflow-x: auto;
  padding: 0 var(--slam-space-2);
  background: var(--slam-surface-2);
  border-bottom: 1px solid var(--slam-border);
  scrollbar-width: none;
}
.slam-drawer-tabs::-webkit-scrollbar {
  display: none;
}
.slam-drawer-tab {
  appearance: none;
  border: 0;
  border-bottom: 2px solid transparent;
  background: transparent;
  color: var(--slam-text-muted);
  font-weight: 600;
  font-size: 0.85rem;
  padding: 0.65rem 0.85rem;
  cursor: pointer;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  transition: color var(--slam-dur-fast) var(--slam-ease),
    border-color var(--slam-dur-fast) var(--slam-ease);
}
.slam-drawer-tab:hover {
  color: var(--slam-text-strong);
}
.slam-drawer-tab.active {
  color: var(--slam-accent);
  border-bottom-color: var(--slam-accent);
}
.slam-drawer-tab:focus-visible {
  outline: none;
  box-shadow: var(--slam-focus-ring);
}
.slam-drawer-tab-count {
  font-size: 0.72rem;
  font-weight: 700;
  padding: 0.1rem 0.4rem;
  border-radius: var(--slam-radius-pill);
  background: var(--slam-surface-3);
  color: var(--slam-text-muted);
}
.slam-drawer-tab.active .slam-drawer-tab-count {
  background: var(--slam-accent-soft-strong);
  color: var(--slam-accent);
}

.slam-drawer-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: var(--slam-space-3);
  background: var(--slam-surface-0);
}

.slam-drawer-footer {
  display: flex;
  gap: var(--slam-space-2);
  justify-content: flex-end;
  padding: var(--slam-space-3) var(--slam-drawer-footer-right-clear)
    var(--slam-space-3) var(--slam-space-4);
  border-top: 1px solid var(--slam-border);
  background: var(--slam-surface-2);
}

/* A settings strip above the footer: one toggle plus a saved/failed note. Sits
   between the thread list and the actions so it reads as "about you", not as
   another action on the conversation you were reading. */
.slam-drawer-prefs {
  display: flex;
  align-items: center;
  gap: var(--slam-space-2);
  flex-wrap: wrap;
  padding: var(--slam-space-2) var(--slam-space-4);
  border-top: 1px solid var(--slam-border);
  background: var(--slam-surface-1);
}
.slam-drawer-prefs-status {
  font-size: 0.73rem;
  color: var(--slam-text-muted);
}
.slam-drawer-prefs-status.is-error {
  color: var(--bs-danger, #dc3545);
}

.slam-drawer.is-wide {
  --slam-drawer-width: min(680px, 96vw);
}

@media (max-width: 575.98px) {
  .slam-drawer {
    --slam-drawer-width: 100vw;
  }
}

@media (prefers-reduced-motion: reduce) {
  .slam-drawer,
  .slam-drawer-overlay {
    transition: none;
  }
}

/* ==========================================================================
   Conversation thread card — collapsed summary that expands in place
   Collapsed it answers "who, about what, how long ago, anything new". Expanded
   it reveals the message log and a composer, so scanning a list and answering
   one message never costs a page load.
   ========================================================================== */
.slam-thread-card {
  background: var(--slam-surface-1);
  border: 1px solid var(--slam-border);
  border-radius: var(--slam-radius-md);
  margin-bottom: var(--slam-space-2);
  overflow: hidden;
  transition: border-color var(--slam-dur-fast) var(--slam-ease),
    box-shadow var(--slam-dur-fast) var(--slam-ease);
}
.slam-thread-card:hover {
  border-color: var(--slam-border-strong);
}
.slam-thread-card.is-open {
  border-color: rgba(var(--slam-accent-rgb), 0.45);
  box-shadow: var(--slam-shadow-1);
}
/* Unread is carried on the left edge as well as the dot, so the eye finds it
   while scrolling a long list rather than having to read each row. */
.slam-thread-card.is-unread {
  border-left: 3px solid var(--slam-accent);
}

.slam-thread-card-head {
  display: flex;
  align-items: flex-start;
  gap: var(--slam-space-3);
  width: 100%;
  padding: var(--slam-space-3);
  background: transparent;
  border: 0;
  text-align: left;
  cursor: pointer;
  color: inherit;
}
.slam-thread-card-head:hover {
  background: var(--slam-surface-2);
}
.slam-thread-card-head:focus-visible {
  outline: none;
  box-shadow: var(--slam-focus-ring);
}

.slam-thread-card-main {
  flex: 1 1 auto;
  min-width: 0;
}
.slam-thread-card-title {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-weight: 600;
  font-size: 0.92rem;
  color: var(--slam-text-strong);
}
.slam-thread-card.is-unread .slam-thread-card-title {
  font-weight: 700;
}
.slam-thread-card-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-top: 0.3rem;
  font-size: 0.75rem;
  color: var(--slam-text-muted);
}
.slam-thread-card-excerpt {
  margin-top: 0.35rem;
  font-size: 0.82rem;
  color: var(--slam-text-muted);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.slam-thread-card-side {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.35rem;
  flex-shrink: 0;
}
.slam-thread-card-time {
  font-size: 0.72rem;
  color: var(--slam-text-muted);
  white-space: nowrap;
}
.slam-thread-card-body {
  border-top: 1px solid var(--slam-border);
  background: var(--slam-surface-0);
}
.slam-thread-card-body[hidden] {
  display: none;
}

/* Row of pills linking a conversation back to the scaffold, requisition, tag or
   corrective action it is about. Sits above the log in the compact (drawer)
   body, where the card head is a button and so cannot hold links itself. */
.slam-thread-refs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--slam-space-2);
  padding: var(--slam-space-3) var(--slam-space-3) 0;
}

/* ==========================================================================
   Message log and bubbles
   Inbound sits left, outbound right, system centered and quiet. The log is its
   own scroll container so the composer stays pinned while history scrolls.
   ========================================================================== */
.slam-thread-log {
  display: flex;
  flex-direction: column;
  gap: var(--slam-space-3);
  padding: var(--slam-space-3);
  max-height: 420px;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.slam-thread-log.is-tall {
  max-height: min(60vh, 640px);
}

.slam-msg {
  display: flex;
  gap: var(--slam-space-2);
  max-width: 92%;
}
.slam-msg.is-inbound {
  align-self: flex-start;
}
.slam-msg.is-outbound {
  align-self: flex-end;
  flex-direction: row-reverse;
}
.slam-msg.is-system {
  align-self: center;
  max-width: 100%;
}

.slam-msg-bubble {
  padding: 0.55rem 0.8rem;
  border-radius: var(--slam-radius-md);
  font-size: 0.87rem;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  background: var(--slam-surface-1);
  border: 1px solid var(--slam-border);
  color: var(--bs-body-color);
}
.slam-msg.is-outbound .slam-msg-bubble {
  background: var(--slam-accent);
  border-color: var(--slam-accent);
  color: var(--slam-accent-contrast);
}
.slam-msg.is-system .slam-msg-bubble {
  background: transparent;
  border: 1px dashed var(--slam-border-strong);
  color: var(--slam-text-muted);
  font-size: 0.8rem;
  text-align: center;
}

.slam-msg-meta {
  margin-top: 0.25rem;
  font-size: 0.7rem;
  color: var(--slam-text-muted);
}
.slam-msg.is-outbound .slam-msg-meta {
  text-align: right;
}

.slam-msg-attachments {
  display: flex;
  flex-wrap: wrap;
  gap: var(--slam-space-2);
  margin-top: var(--slam-space-2);
}
.slam-msg-attachment {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.3rem 0.55rem;
  font-size: 0.75rem;
  border-radius: var(--slam-radius-sm);
  border: 1px solid var(--slam-border);
  background: var(--slam-surface-1);
  color: var(--bs-body-color);
  text-decoration: none;
  max-width: 100%;
}
.slam-msg-attachment:hover {
  border-color: var(--slam-accent);
  color: var(--slam-accent);
}
.slam-msg-attachment-thumb {
  width: 92px;
  height: 92px;
  border-radius: var(--slam-radius-sm);
  border: 1px solid var(--slam-border);
  object-fit: cover;
  display: block;
}

/* ==========================================================================
   Composer
   ========================================================================== */
.slam-composer {
  border-top: 1px solid var(--slam-border);
  padding: var(--slam-space-3);
  background: var(--slam-surface-1);
}
.slam-composer-input {
  width: 100%;
  min-height: 2.6rem;
  max-height: 9rem;
  resize: vertical;
  padding: 0.55rem 0.75rem;
  font-size: 0.87rem;
  border-radius: var(--slam-radius-md);
  border: 1px solid var(--slam-border);
  background: var(--slam-surface-1);
  color: var(--bs-body-color);
}
.slam-composer-input:focus {
  outline: none;
  border-color: var(--slam-accent);
  box-shadow: var(--slam-focus-ring);
}
.slam-composer-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--slam-space-2);
  margin-top: var(--slam-space-2);
}
.slam-composer-attachments {
  display: flex;
  flex-wrap: wrap;
  gap: var(--slam-space-2);
  margin-top: var(--slam-space-2);
}
.slam-composer-hint {
  font-size: 0.73rem;
  color: var(--slam-text-muted);
}

.slam-thread-empty {
  padding: var(--slam-space-6) var(--slam-space-4);
  text-align: center;
  color: var(--slam-text-muted);
  font-size: 0.88rem;
}
.slam-thread-empty i {
  display: block;
  font-size: 1.6rem;
  margin-bottom: var(--slam-space-2);
  opacity: 0.6;
}

/* The "unread on another project" line above a thread list. Deliberately quiet:
   it explains the nav pill's number, it is not itself a message. */
.slam-thread-elsewhere {
  display: flex;
  align-items: center;
  gap: var(--slam-space-2);
  padding: var(--slam-space-3) var(--slam-space-4);
  border-bottom: 1px solid var(--slam-border);
  background: var(--slam-surface-2);
  color: var(--slam-text-muted);
  font-size: 0.82rem;
  text-decoration: none;
}
.slam-thread-elsewhere span {
  flex: 1 1 auto;
}
.slam-thread-elsewhere:hover {
  background: var(--slam-surface-3);
  color: var(--bs-body-color);
}
.slam-thread-elsewhere:focus-visible {
  outline: none;
  box-shadow: var(--slam-focus-ring);
}

/* ==========================================================================
   Toggle chip
   --------------------------------------------------------------------------
   A checkbox that reads as a chip you switch on rather than a box you tick. The
   underlying input is a real checkbox, so names, forms and keyboard handling are
   unchanged — only the presentation differs.

   Two equivalent shapes; use whichever the markup allows:

     input as a sibling, when there is a stable id to point a label at
       <input type="checkbox" class="slam-toggle-input" id="t1" checked>
       <label class="slam-toggle is-danger" for="t1"><i class="fas fa-check"></i>Issues</label>

     input nested, when the markup is cloned and has no unique id to give
       <label class="slam-toggle is-danger">
         <input type="checkbox" class="slam-toggle-input" checked><i class="fas fa-check"></i>Issues
       </label>

   Off state is deliberately loud about being off — dashed border, muted text and
   a faded marker — so an unselected option cannot be mistaken for a selected one
   at a glance. On state matches the .slam-pill tint of the same name, so the
   editor and the read-only summary of a setting look related.
   ========================================================================== */
.slam-toggle-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.slam-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.28rem 0.7rem;
  border-radius: 999px;
  font-size: 0.76rem;
  font-weight: 600;
  line-height: 1.3;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  margin-bottom: 0;
  border: 1px dashed var(--slam-border-strong);
  background: transparent;
  color: var(--slam-text-muted);
  transition: background-color var(--slam-dur-fast, 0.15s) var(--slam-ease, ease),
    border-color var(--slam-dur-fast, 0.15s) var(--slam-ease, ease),
    color var(--slam-dur-fast, 0.15s) var(--slam-ease, ease);
}
.slam-toggle:hover {
  border-color: var(--slam-accent);
  color: var(--bs-body-color);
}

/* The marker. Faded while off, full strength once on — the same glyph either
   way, so the chip does not change width as it is switched. */
.slam-toggle > i {
  font-size: 0.7rem;
  width: 0.75rem;
  text-align: center;
  opacity: 0.28;
}

.slam-toggle-input:checked + .slam-toggle,
.slam-toggle:has(> .slam-toggle-input:checked) {
  border-style: solid;
  border-color: rgba(var(--slam-accent-rgb, 56, 189, 248), 0.45);
  background: var(--slam-accent-soft, rgba(56, 189, 248, 0.12));
  color: var(--slam-accent, #0d6efd);
}
.slam-toggle-input:checked + .slam-toggle > i,
.slam-toggle:has(> .slam-toggle-input:checked) > i {
  opacity: 1;
}
.slam-toggle-input:focus-visible + .slam-toggle,
.slam-toggle:has(> .slam-toggle-input:focus-visible) {
  outline: none;
  box-shadow: var(--slam-focus-ring);
}
.slam-toggle-input:disabled + .slam-toggle,
.slam-toggle:has(> .slam-toggle-input:disabled) {
  opacity: 0.5;
  cursor: not-allowed;
}

/* On-state tints, matched to .slam-pill so a toggled "Issues" and a rendered
   "Issues" pill are recognisably the same thing. */
.slam-toggle-input:checked + .slam-toggle.is-success,
.slam-toggle.is-success:has(> .slam-toggle-input:checked) {
  border-color: rgba(25, 135, 84, 0.45);
  background: rgba(25, 135, 84, 0.14);
  color: #198754;
}
.slam-toggle-input:checked + .slam-toggle.is-warning,
.slam-toggle.is-warning:has(> .slam-toggle-input:checked) {
  border-color: rgba(240, 160, 0, 0.5);
  background: rgba(240, 160, 0, 0.18);
  color: #996200;
}
.slam-toggle-input:checked + .slam-toggle.is-danger,
.slam-toggle.is-danger:has(> .slam-toggle-input:checked) {
  border-color: rgba(220, 53, 69, 0.45);
  background: rgba(220, 53, 69, 0.14);
  color: #dc3545;
}
[data-bs-theme="dark"] .slam-toggle-input:checked + .slam-toggle.is-success,
[data-bs-theme="dark"] .slam-toggle.is-success:has(> .slam-toggle-input:checked) { color: #6dd4a1; }
[data-bs-theme="dark"] .slam-toggle-input:checked + .slam-toggle.is-warning,
[data-bs-theme="dark"] .slam-toggle.is-warning:has(> .slam-toggle-input:checked) { color: #ffc864; }
[data-bs-theme="dark"] .slam-toggle-input:checked + .slam-toggle.is-danger,
[data-bs-theme="dark"] .slam-toggle.is-danger:has(> .slam-toggle-input:checked) { color: #f58a96; }

/* Icon-only, for a selection column in a table where a label would not fit. */
.slam-toggle.is-icon {
  padding: 0;
  width: 1.4rem;
  height: 1.4rem;
  justify-content: center;
  gap: 0;
  border-radius: var(--slam-radius-sm);
}
.slam-toggle.is-icon > i {
  width: auto;
  margin: 0;
}

/* ==========================================================================
   User picker
   --------------------------------------------------------------------------
   Search-and-select for people. Replaces multi-select <select> lists, where
   choosing more than one person means knowing to hold Ctrl and where a long
   roster is unsearchable.

   Selections show as removable chips above the field; the results list appears
   under it while searching. Driven by js/slam-user-picker.js, which owns the
   hidden inputs so the whole thing still submits as a plain form.
   ========================================================================== */
.slam-picker {
  position: relative;
}

.slam-picker-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--slam-space-2);
  margin-bottom: var(--slam-space-2);
}
.slam-picker-chips:empty {
  display: none;
}

.slam-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  max-width: 100%;
  padding: 0.2rem 0.3rem 0.2rem 0.6rem;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  background: var(--slam-accent-soft, rgba(56, 189, 248, 0.12));
  color: var(--slam-accent, #0d6efd);
  border: 1px solid rgba(var(--slam-accent-rgb, 56, 189, 248), 0.35);
}
.slam-chip-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.slam-chip-meta {
  font-weight: 500;
  opacity: 0.75;
}

/* The X. Sized to a comfortable tap target without inflating the chip. */
.slam-chip-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.15rem;
  height: 1.15rem;
  border: 0;
  border-radius: 50%;
  padding: 0;
  font-size: 0.62rem;
  line-height: 1;
  color: inherit;
  background: rgba(var(--slam-accent-rgb, 56, 189, 248), 0.18);
  cursor: pointer;
  transition: background-color var(--slam-dur-fast, 0.15s) var(--slam-ease, ease);
}
.slam-chip-remove:hover,
.slam-chip-remove:focus-visible {
  background: var(--bs-danger, #dc3545);
  color: #fff;
}
.slam-chip-remove:focus-visible {
  outline: none;
  box-shadow: var(--slam-focus-ring);
}

.slam-picker-results {
  position: absolute;
  z-index: 10;
  left: 0;
  right: 0;
  margin-top: 2px;
  max-height: 15rem;
  overflow-y: auto;
  border-radius: var(--slam-radius-md);
  border: 1px solid var(--slam-border-strong);
  background: var(--slam-surface-1);
  box-shadow: var(--slam-shadow-3, 0 12px 28px -12px rgba(15, 23, 42, 0.4));
}
.slam-picker-results[hidden] {
  display: none;
}

.slam-picker-option {
  display: flex;
  align-items: center;
  gap: var(--slam-space-2);
  width: 100%;
  padding: 0.45rem 0.7rem;
  border: 0;
  background: transparent;
  text-align: left;
  font-size: 0.83rem;
  color: var(--bs-body-color);
  cursor: pointer;
}
.slam-picker-option + .slam-picker-option {
  border-top: 1px solid var(--slam-border);
}
.slam-picker-option:hover,
.slam-picker-option.is-active {
  background: var(--slam-surface-2);
}
.slam-picker-option-name {
  font-weight: 600;
}
.slam-picker-option-meta {
  display: block;
  font-size: 0.74rem;
  color: var(--slam-text-muted);
}

.slam-picker-note {
  padding: 0.55rem 0.7rem;
  font-size: 0.8rem;
  color: var(--slam-text-muted);
}

/* ----------------------------------------------------------------------------
   Auto-expanding description textareas
   Start at a single-line input height; JS sets height to scrollHeight on input.
   ---------------------------------------------------------------------------- */
textarea.slam-auto-expand {
  overflow-y: hidden;
  resize: none;
  min-height: calc(1.5em + 0.75rem + 2px);
  line-height: 1.5;
}

/* ----------------------------------------------------------------------------
   Selectable factor tile — Supports & Constraints pickers
   Used by the work pack Supports & Constraints section and the progress report
   actual-conditions block.

   A 16px checkbox is a poor target for a decision this consequential: ticking
   "Confined space" changes the priced hours and the rate the crew is measured
   against. So the whole tile is the hit area and the whole tile changes state,
   which also makes the current selection readable at a glance down a long list.

   The checkbox stays in the markup and stays focusable — it is what submits the
   form, and it is what a keyboard or screen reader operates. It is moved out of
   sight, never removed from the accessibility tree with display:none.

   Markup: a <label> wrapping the input, with no `for` attribute, because a
   nested input plus `for` double-fires the toggle in some browsers.

     <label class="slam-factor-tile" data-factor-kind="constraint">
       <input type="checkbox" class="slam-factor-tile-input" ...>
       <span class="slam-factor-tile-mark" aria-hidden="true"><i class="fas fa-check"></i></span>
       <span class="slam-factor-tile-body"> ... </span>
     </label>
   ---------------------------------------------------------------------------- */
.slam-factor-tile {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--slam-space-2, 0.5rem);
  width: 100%;
  margin: 0;
  padding: 0.6rem 0.7rem;
  border: 1px solid var(--slam-border);
  border-left: 3px solid var(--slam-border);
  border-radius: var(--slam-radius-md);
  background: var(--slam-surface-1);
  cursor: pointer;
  transition:
    border-color var(--slam-dur-fast, 0.15s) var(--slam-ease, ease),
    background-color var(--slam-dur-fast, 0.15s) var(--slam-ease, ease);
}
.slam-factor-tile + .slam-factor-tile {
  margin-top: 0.5rem;
}
/* An author `display: flex` outranks the user agent's `[hidden]` rule, so the
   scope filter's `tile.hidden = true` would otherwise do nothing. */
.slam-factor-tile[hidden] {
  display: none;
}
.slam-factor-tile:hover {
  border-color: var(--slam-border-strong);
  background: var(--slam-surface-2);
}

/* Positioned under the mark rather than hidden, so it stays keyboard-reachable.
   Zero opacity keeps the native focus ring from drawing in the wrong place. */
.slam-factor-tile-input {
  position: absolute;
  top: 0.7rem;
  left: 0.75rem;
  width: 1.15rem;
  height: 1.15rem;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.slam-factor-tile-mark {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.15rem;
  height: 1.15rem;
  margin-top: 0.1rem;
  border: 1px solid var(--slam-border-strong);
  border-radius: var(--slam-radius-sm, 0.25rem);
  background: var(--slam-surface-1);
  color: transparent;
  font-size: 0.65rem;
  transition:
    border-color var(--slam-dur-fast, 0.15s) var(--slam-ease, ease),
    background-color var(--slam-dur-fast, 0.15s) var(--slam-ease, ease),
    color var(--slam-dur-fast, 0.15s) var(--slam-ease, ease);
}

.slam-factor-tile-body {
  flex: 1 1 auto;
  min-width: 0;
}

/* `:has()` drives the ordinary case. `.is-selected` is the same styling applied
   from script, which also covers programmatic ticks — template recall sets a
   batch of boxes without any click happening. */
.slam-factor-tile:has(.slam-factor-tile-input:checked),
.slam-factor-tile.is-selected {
  border-color: var(--bs-primary);
  border-left-color: var(--bs-primary);
  background: var(--bs-primary-bg-subtle);
}
.slam-factor-tile:has(.slam-factor-tile-input:checked) .slam-factor-tile-mark,
.slam-factor-tile.is-selected .slam-factor-tile-mark {
  border-color: var(--bs-primary);
  background: var(--bs-primary);
  color: var(--bs-white);
}

/* A support and a constraint mean opposite things to the estimate, so a selected
   tile says which it is without the reader going back to the badge. */
.slam-factor-tile.is-selected[data-factor-kind="support"],
.slam-factor-tile[data-factor-kind="support"]:has(.slam-factor-tile-input:checked) {
  border-color: var(--bs-success);
  border-left-color: var(--bs-success);
  background: var(--bs-success-bg-subtle);
}
.slam-factor-tile.is-selected[data-factor-kind="support"] .slam-factor-tile-mark,
.slam-factor-tile[data-factor-kind="support"]:has(.slam-factor-tile-input:checked) .slam-factor-tile-mark {
  border-color: var(--bs-success);
  background: var(--bs-success);
}
.slam-factor-tile.is-selected[data-factor-kind="constraint"],
.slam-factor-tile[data-factor-kind="constraint"]:has(.slam-factor-tile-input:checked) {
  border-color: var(--bs-warning);
  border-left-color: var(--bs-warning);
  background: var(--bs-warning-bg-subtle);
}
.slam-factor-tile.is-selected[data-factor-kind="constraint"] .slam-factor-tile-mark,
.slam-factor-tile[data-factor-kind="constraint"]:has(.slam-factor-tile-input:checked) .slam-factor-tile-mark {
  border-color: var(--bs-warning);
  background: var(--bs-warning);
  color: var(--slam-text);
}

.slam-factor-tile-input:focus-visible ~ .slam-factor-tile-mark {
  outline: none;
  box-shadow: var(--slam-focus-ring);
}

.slam-factor-tile-name {
  font-weight: 600;
}
.slam-factor-tile-effect {
  display: block;
  font-size: 0.8rem;
  color: var(--bs-body-color);
}

/* The provenance line: quieter than the effect it explains, but always present,
   because an unattributed percentage is one an estimator cannot argue with. */
.slam-factor-tile-basis {
  display: block;
  font-size: 0.72rem;
  color: var(--slam-text-muted);
}

/* ----------------------------------------------------------------------------
   Supports & Constraints settings editor — one card per factor
   Used by core/templates/core/settings/work_factor_types_modal.html.

   This replaced an eleven-column table. Eleven labelled fields cannot share a
   table row: the field labels duplicate the column headers, the hints stack into
   tall narrow paragraphs, and no two cells in a row end up the same height, so
   nothing lines up. Giving each factor its own card and its own grid fixes the
   alignment at the source instead of fighting it with column widths.
   ---------------------------------------------------------------------------- */
.slam-factor-editor {
  border: 1px solid var(--slam-border);
  border-left: 3px solid var(--bs-primary);
  border-radius: var(--slam-radius-md);
  background: var(--slam-surface-1);
  margin-bottom: var(--slam-space-3, 1rem);
}

/* Built-in factors are the ones the estimating engine looks up by code. Marking
   them visually stops an administrator wondering why one card's effect type is
   a locked read-out and its neighbour's is a select. */
.slam-factor-editor.is-system {
  border-left-color: var(--slam-border-strong);
  background: var(--slam-surface-2);
}
.slam-factor-editor.is-new {
  border-left-style: dashed;
}

/* Marked for deactivation. Struck through rather than removed, because the row
   still has to post so the server knows to deactivate it. */
.slam-factor-editor.is-removed {
  opacity: 0.5;
}
.slam-factor-editor.is-removed .slam-factor-editor-body {
  display: none;
}
.slam-factor-editor.is-removed .slam-factor-editor-head {
  text-decoration: line-through;
}

.slam-factor-editor-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--slam-space-2, 0.5rem);
  padding: 0.5rem 0.85rem;
  border-bottom: 1px solid var(--slam-border);
  background: var(--slam-surface-2);
  border-radius: calc(var(--slam-radius-md) - 1px) calc(var(--slam-radius-md) - 1px) 0 0;
}
.slam-factor-editor.is-system .slam-factor-editor-head {
  background: var(--slam-surface-3, var(--slam-surface-2));
}

.slam-factor-editor-body {
  padding: 0.85rem;
}

/* A numbered heading per group. The numbers are the point: they say these four
   decisions happen in an order, which is the part a table gave no way to say. */
.slam-factor-group-label {
  display: flex;
  align-items: center;
  gap: var(--slam-space-2, 0.5rem);
  margin-bottom: 0.6rem;
  padding-bottom: 0.3rem;
  border-bottom: 1px solid var(--slam-border);
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--slam-text-muted);
}

.slam-factor-group-step {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 50%;
  background: var(--bs-primary);
  color: var(--bs-white);
  font-size: 0.68rem;
  letter-spacing: 0;
}

/* Says what the multiplier means while it is being typed, so nobody has to
   remember which side of 1.00 is faster. */
.slam-factor-readout {
  min-height: calc(1.5em + 0.5rem + 2px);
  padding: 0.25rem 0.5rem;
  border: 1px dashed var(--slam-border-strong);
  border-radius: var(--slam-radius-sm, 0.25rem);
  background: var(--slam-surface-2);
  font-size: 0.8rem;
  color: var(--slam-text-muted);
}
.slam-factor-readout.is-slower {
  border-color: var(--bs-warning);
  color: var(--bs-warning-text-emphasis);
}
.slam-factor-readout.is-faster {
  border-color: var(--bs-success);
  color: var(--bs-success-text-emphasis);
}

/* Crispy emits a label, the widget and a `.form-text` hint per field. Left at
   default spacing that hint is what pushed the rows apart; tightening it is what
   makes a four-across row read as one line of fields. */
.slam-factor-editor-body .form-label {
  margin-bottom: 0.15rem;
  font-size: 0.78rem;
  font-weight: 600;
}
.slam-factor-editor-body .form-text {
  margin-top: 0.15rem;
  font-size: 0.72rem;
  line-height: 1.25;
}
.slam-factor-editor-body .mb-3 {
  margin-bottom: 0 !important;
}
.slam-factor-editor-body select[multiple] {
  min-height: 5.5rem;
}

/* ── Supervisor / crew filter ────────────────────────────────────────────
   The same control is dropped into a dozen filter bars that each have their own
   grid, so hosts pass `field_class` when they have a matching class of their
   own. This is the standalone fallback: a labelled column that sits happily in
   a flex filter row. */
.crew-filter-field {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  min-width: 12rem;
}
.crew-filter-field > label {
  margin-bottom: 0;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--slam-text-muted);
}
.crew-filter-help {
  font-size: 0.7rem;
  line-height: 1.2;
}

/* slam-user-picker.js turns the supervisor <select> into a search field with
   chips. It stacks the chips above the input, which is right in a form but in a
   filter row means the field grows as crews are picked and drags the row's
   baseline with it. Cap it: a dozen crews scroll instead of pushing Apply off
   the bottom of the card. */
.crew-picker .slam-picker-chips {
  max-height: 4.5rem;
  overflow-y: auto;
}

/* Marks the rows a supervisor owns when a list boosts their crew to the top
   instead of filtering everyone else out. */
.crew-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.05rem 0.4rem;
  border: 1px solid var(--slam-accent);
  border-radius: 999px;
  background: var(--slam-surface-2);
  color: var(--slam-accent);
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

/* Section header for a per-supervisor block in a grouped table or printout. */
.crew-group-heading {
  background: var(--slam-surface-2);
  border-top: 2px solid var(--slam-border-strong);
  font-weight: 700;
}

/* ----------------------------------------------------------------------------
   Build Types editor
   Crispy emits label + widget + help in every cell. `align-middle` then
   centres the Name cell (no help) against the tall help columns, and labels
   like "Dismantle factor" wrap to two lines while "Modify factor" stays one,
   so the inputs sit on different baselines. One shared label band and one
   control height keep every field on the same line; column mins follow what
   the user has to type or read instead of shrinking until text clips.
   ---------------------------------------------------------------------------- */
.build-types-table {
  --bt-label-h: 2.6em;
  --bt-label-gap: 0.4rem;
  --bt-control-h: 2.5rem;
  --bt-help-lh: 1.4;
  width: 100%;
  min-width: 78rem;
  margin-bottom: 0;
}

.build-types-table > :not(caption) > * > * {
  padding: 0.75rem 0.8rem;
  vertical-align: top;
}

.build-types-table thead th {
  white-space: nowrap;
  vertical-align: bottom;
}

.build-types-table tbody td > .mb-3,
.build-types-table tbody .bt-field {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  margin-bottom: 0 !important;
}

.build-types-table .form-label,
.build-types-table .bt-col-active .form-check-label {
  min-height: var(--bt-label-h);
  margin-bottom: var(--bt-label-gap);
  line-height: 1.3;
  display: flex;
  align-items: flex-end;
  overflow: visible;
  white-space: normal;
  overflow-wrap: break-word;
}

.build-types-table .form-control,
.build-types-table .form-select {
  width: 100%;
  height: var(--bt-control-h);
  min-height: var(--bt-control-h);
  max-height: var(--bt-control-h);
  flex: 0 0 var(--bt-control-h);
  box-sizing: border-box;
  line-height: 1.25;
  padding-top: 0.4rem;
  padding-bottom: 0.4rem;
  overflow: visible;
  text-overflow: clip;
}

.build-types-table .form-text {
  margin-top: 0.5rem;
  font-size: 0.75rem;
  line-height: var(--bt-help-lh);
  overflow: visible;
  white-space: normal;
  overflow-wrap: break-word;
}

.build-types-table .bt-col-name {
  min-width: 13rem;
  width: 16%;
}

.build-types-table .bt-col-kind {
  min-width: 12rem;
  width: 14%;
}

.build-types-table .bt-col-active {
  min-width: 5rem;
  width: 6%;
}

.build-types-table .bt-col-num {
  min-width: 11rem;
  width: 12%;
}

.build-types-table .bt-col-actions {
  min-width: 3.5rem;
  width: 4%;
}

.build-types-table .bt-col-name .form-control {
  min-width: 11.5rem;
}

.build-types-table .bt-col-kind .form-select {
  min-width: 11rem;
}

.build-types-table .bt-col-num .form-control {
  min-width: 6.5rem;
}

.build-types-table input[type="number"] {
  -moz-appearance: textfield;
  appearance: textfield;
}

.build-types-table input[type="number"]::-webkit-inner-spin-button,
.build-types-table input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.build-types-table .bt-col-active .form-check {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-left: 0;
  min-height: 0;
  margin-bottom: 0;
}

.build-types-table .bt-col-active .form-check-input {
  order: 2;
  float: none;
  position: relative;
  margin: calc((var(--bt-control-h) - 1.15em) / 2) 0;
  width: 1.15em;
  height: 1.15em;
}

.build-types-table .bt-col-active .form-check-label {
  order: 1;
  justify-content: center;
  width: 100%;
  font-weight: 600;
}

.build-types-table .bt-col-actions .bt-field {
  align-items: center;
}

.build-types-table .bt-col-actions .remove-row {
  height: var(--bt-control-h);
  min-width: var(--bt-control-h);
  padding: 0 0.55rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* ============================================================================
   Work pack completion card (.wp-completion)
   ----------------------------------------------------------------------------
   Three bars answering "how far along is this pack?" from three independent
   angles: what the crew reported, hours burned, and material weight moved.
   Rendered by core/templates/core/partials/completion_progress_card.html on
   the work pack detail page, the progress report detail page, and the
   progress report create/edit forms.

   The reported bar is stacked -- one segment per progress report, coloured
   from the categorical --slam-chart-* series so consecutive reports stay
   distinguishable and both themes get contrast-appropriate hues. The measured
   bars reuse the green/red/grey burn-down vocabulary already established by
   the work pack list and trade dashboard, so "red means over the estimate"
   means the same thing on every page.
   ============================================================================ */
.wp-completion {
  display: grid;
  gap: var(--slam-space-4, 1rem);
}

.wp-completion-row {
  display: grid;
  gap: 0.35rem;
}

.wp-completion-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--slam-space-3, 0.75rem);
  flex-wrap: wrap;
}

.wp-completion-title {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--slam-text-muted);
  margin: 0;
}

.wp-completion-value {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--slam-text-strong);
  font-variant-numeric: tabular-nums;
}

.wp-completion-label {
  font-size: 0.78rem;
  color: var(--slam-text-muted);
  font-variant-numeric: tabular-nums;
}

/* The track. `display:flex` is what makes the segments stack horizontally and
   share the width without any per-segment positioning. */
.wp-completion-track {
  display: flex;
  height: 14px;
  border-radius: var(--slam-radius-pill, 999px);
  background: var(--slam-surface-3);
  overflow: hidden;
  border: 1px solid var(--slam-border);
}

.wp-completion-seg {
  height: 100%;
  min-width: 0;
  transition: width var(--slam-dur, 170ms) var(--slam-ease, ease);
}

/* Colour per report, cycling the categorical chart series. */
.wp-completion-seg--s1 { background: var(--slam-chart-1); }
.wp-completion-seg--s2 { background: var(--slam-chart-2); }
.wp-completion-seg--s3 { background: var(--slam-chart-3); }
.wp-completion-seg--s4 { background: var(--slam-chart-4); }
.wp-completion-seg--s5 { background: var(--slam-chart-5); }
.wp-completion-seg--s6 { background: var(--slam-chart-6); }
.wp-completion-seg--s7 { background: var(--slam-chart-7); }
.wp-completion-seg--s8 { background: var(--slam-chart-8); }

/* The segment being typed right now on the create/edit form. Hatched rather
   than solid so an unsaved figure never reads as recorded history. */
.wp-completion-seg--pending {
  background-image: repeating-linear-gradient(
    45deg,
    var(--slam-accent) 0,
    var(--slam-accent) 5px,
    var(--slam-accent-hover) 5px,
    var(--slam-accent-hover) 10px
  );
}

/* Burn-down vocabulary shared with .wp-progress on the work pack list. */
.wp-completion-seg--within { background: var(--bs-success); }
.wp-completion-seg--over { background: var(--bs-danger); }
.wp-completion-seg--remaining { background: transparent; }

.wp-completion-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 0.65rem;
  margin: 0.15rem 0 0;
  padding: 0;
  list-style: none;
}

.wp-completion-legend li {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.74rem;
  color: var(--slam-text-muted);
  font-variant-numeric: tabular-nums;
}

/* The report this page is about, when the card is on a progress report. */
.wp-completion-legend li.is-current {
  font-weight: 700;
  color: var(--slam-text-strong);
}

.wp-completion-swatch {
  flex: 0 0 auto;
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 0.2rem;
  border: 1px solid var(--slam-border-strong);
}

.wp-completion-empty {
  padding: 0.5rem 0;
  font-size: 0.8rem;
  color: var(--slam-text-muted);
}

.wp-completion-note {
  font-size: 0.78rem;
  color: var(--slam-text-muted);
  margin: 0;
}

.wp-completion-note.is-warning {
  color: var(--slam-status-caution-text);
}

.wp-completion-divider {
  height: 1px;
  background: var(--slam-border);
  margin: 0.1rem 0;
}
