/* ─── Palette ─────────────────────────────────────────────────────────
   Light theme matches Moss Utilities' forest-green + charcoal brand.
   Dark theme keeps the same accent for continuity and dims surfaces.

   Every component below reads from these variables — there are no
   hardcoded colors past this block. Adjust the palette by changing
   only the values inside :root / [data-theme="dark"].
   ───────────────────────────────────────────────────────────────────── */
:root {
  /* Native widgets (date pickers, dropdown chevrons, scrollbars)
     follow the active theme instead of the OS default. */
  color-scheme: light;

  /* surfaces + text */
  --fg:           #1f2933;
  --muted:        #6c7681;
  --bg:           #f7f7f5;
  --card:         #ffffff;
  --card-alt:     #f2f3ef;  /* table headers, subtle fills */
  --border:       #e3e3df;
  --border-strong:#cdd0c9;

  /* brand */
  --accent:       #2d5e3e;  /* forest green — primary actions, links */
  --accent-hover: #244a31;
  --accent-soft:  #e5efe8;  /* tinted backgrounds, chips */
  --accent-fg:    #ffffff;  /* foreground when accent is the background */

  /* status (work in both themes) */
  --good:         #1a8d4d;
  --good-soft:    #d6f0d8;
  --warn:         #d4831f;
  --warn-soft:    #fff0c2;
  --bad:          #b3261e;
  --bad-soft:     #ffd6d0;

  /* chart axis hints — used by Chart.js options where needed */
  --grid:         rgba(0, 0, 0, 0.06);
  --tooltip-bg:   rgba(15, 20, 28, 0.92);
  --tooltip-fg:   #ffffff;
}

[data-theme="dark"] {
  color-scheme: dark;

  --fg:           #e8eaed;
  --muted:        #9aa0a6;
  --bg:           #15191c;
  --card:         #1f242a;
  --card-alt:     #262c33;
  --border:       #2a2f35;
  --border-strong:#3a4047;

  --accent:       #4fae6e;  /* lifted green so it stays readable on dark */
  --accent-hover: #5fbe80;
  --accent-soft:  #1f3328;
  --accent-fg:    #0b1612;

  --good:         #4fae6e;
  --good-soft:    #1f3328;
  --warn:         #e8a04a;
  --warn-soft:    #3a2f1a;
  --bad:          #e26d65;
  --bad-soft:     #3a1f1c;

  --grid:         rgba(255, 255, 255, 0.08);
  --tooltip-bg:   rgba(232, 234, 237, 0.96);
  --tooltip-fg:   #15191c;
}

/* System preference — only applied when the user hasn't picked manually. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;

    --fg:           #e8eaed;
    --muted:        #9aa0a6;
    --bg:           #15191c;
    --card:         #1f242a;
    --card-alt:     #262c33;
    --border:       #2a2f35;
    --border-strong:#3a4047;

    --accent:       #4fae6e;
    --accent-hover: #5fbe80;
    --accent-soft:  #1f3328;
    --accent-fg:    #0b1612;

    --good:         #4fae6e;
    --good-soft:    #1f3328;
    --warn:         #e8a04a;
    --warn-soft:    #3a2f1a;
    --bad:          #e26d65;
    --bad-soft:     #3a1f1c;

    --grid:         rgba(255, 255, 255, 0.08);
    --tooltip-bg:   rgba(232, 234, 237, 0.96);
    --tooltip-fg:   #15191c;
  }
}

/* x-cloak hides the element until Alpine has hydrated. */
[x-cloak] { display: none !important; }

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  color: var(--fg);
  background: var(--bg);
  line-height: 1.5;
}

/* Uniform keyboard-focus ring. Elements with their own :focus-visible
   treatment (e.g. .btn-icon) override this by specificity. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[role="button"]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Skip link — visually hidden until focused, then floats over the header. */
.skip-link {
  position: absolute;
  left: -9999px;
  z-index: 100;
  background: var(--accent);
  color: var(--accent-fg);
  padding: 0.5rem 1rem;
  border-radius: 0 0 4px 0;
  text-decoration: none;
}
.skip-link:focus {
  left: 0;
  top: 0;
}

/* Small-text utility (pairs with .muted). */
.small { font-size: 0.85rem; }

/* Trainee "no role yet" landing — deliberately sparse. */
.access-pending {
  max-width: 560px;
  margin: 4rem auto;
  text-align: center;
}

/* Admin-only payroll-conflict banner on the directory page. */
.dir-sync-conflicts-banner {
  margin: 0 0 1rem;
  padding: 0.5rem 0.9rem;
  background: var(--warn-soft);
  color: var(--warn);
  border: 1px solid var(--warn);
  border-radius: 6px;
  font-size: 0.9rem;
}
.dir-sync-conflicts-banner a { color: inherit; font-weight: 600; }

/* Training-area sub-tabs (Sessions / Workers / Certs / SiteDocs links) — the strip that
   bundles the three pages under the single "Training" main-nav tab.
   Styled as anchored folder tabs sitting on a hairline, echoing the
   directory's division tabs. */
.sub-tabs {
  display: flex;
  flex-wrap: wrap; /* four training tabs don't fit a phone on one line */
  gap: 0.25rem;
  margin: 0 0 1.25rem;
  border-bottom: 1px solid var(--border);
}
.sub-tab {
  white-space: nowrap;
  padding: 0.45rem 1rem;
  color: var(--muted);
  text-decoration: none;
  font-size: 0.92rem;
  border: 1px solid transparent;
  border-bottom: none;
  border-radius: 6px 6px 0 0;
}
.sub-tab:hover {
  color: var(--accent);
  background: var(--accent-soft);
}
.sub-tab.active {
  color: var(--fg);
  font-weight: 600;
  background: var(--card);
  border-color: var(--border);
  /* Overlap the strip's hairline so the active tab reads as attached
     to the page below it. */
  margin-bottom: -1px;
}

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

.site-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  padding: 1rem 2rem;
  border-bottom: 1px solid var(--border);
  background: var(--card);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-weight: 600;
  color: var(--fg);
  text-decoration: none;
}
.brand-logo {
  height: 36px;
  width: auto;
  display: block;
}
/* Show the right logo for the active theme. Light variant by default;
   the dark-variant is swapped in via [data-theme="dark"] or the
   `prefers-color-scheme` media query when the user hasn't picked. */
.brand-logo-dark  { display: none; }
.brand-logo-light { display: block; }
[data-theme="dark"] .brand-logo-light { display: none; }
[data-theme="dark"] .brand-logo-dark  { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .brand-logo-light { display: none; }
  :root:not([data-theme="light"]) .brand-logo-dark  { display: block; }
}
.brand-name {
  font-size: 0.92rem;
  color: var(--muted);
  font-weight: 500;
  letter-spacing: 0.02em;
}

.site-header nav { display: flex; gap: 1rem; align-items: center; flex-wrap: wrap; }
.site-header nav a {
  color: var(--fg);
  text-decoration: none;
  padding: 0.3rem 0.55rem;
  border-radius: 4px;
  font-size: 0.92rem;
}
.site-header nav a:hover {
  background: var(--accent-soft);
  color: var(--accent);
}
.site-header .user { color: var(--muted); font-size: 0.88rem; }

.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  padding: 0;
  transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
}
.theme-toggle:hover {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-soft);
}
.theme-toggle svg { width: 16px; height: 16px; display: block; }

/* Settings gear in the nav — same chrome as .theme-toggle, with a
   floating menu of admin-config links. Only rendered for admin. */
.nav-settings { position: relative; }
.nav-settings-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  padding: 0;
  transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
}
.nav-settings-btn:hover,
.nav-settings-btn[aria-expanded="true"] {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-soft);
}
.nav-settings-btn svg { width: 16px; height: 16px; display: block; }

.nav-settings-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  min-width: 160px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.12);
  padding: 0.25rem;
  display: flex;
  flex-direction: column;
  z-index: 30;
}
.nav-settings-menu a {
  display: block;
  padding: 0.5rem 0.75rem;
  border-radius: 4px;
  color: var(--fg);
  font-size: 0.9rem;
  text-decoration: none;
}
.nav-settings-menu a:hover {
  background: var(--accent-soft);
  color: var(--accent);
}
/* Open-item count on a gear entry ("Duplicate workers 12"). */
.nav-settings-menu .nav-count {
  display: inline-block;
  min-width: 1.5em;
  margin-left: 0.35em;
  padding: 0 0.4em;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 0.75rem;
  font-weight: 600;
  text-align: center;
}
/* Show sun in dark mode, moon in light mode (or system-default light). */
:root .theme-toggle .icon-sun  { display: none; }
:root .theme-toggle .icon-moon { display: block; }
[data-theme="dark"] .theme-toggle .icon-sun  { display: block; }
[data-theme="dark"] .theme-toggle .icon-moon { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .icon-sun  { display: block; }
  :root:not([data-theme="light"]) .theme-toggle .icon-moon { display: none; }
}

body {
  background: var(--bg);
}

.container {
  max-width: 960px;
  margin: 0 auto;
  padding: 2rem;
}

a { color: var(--accent); }
a:hover { color: var(--accent-hover); }

.cta {
  display: inline-block;
  padding: 0.6rem 1.2rem;
  background: var(--accent);
  color: var(--accent-fg);
  border-radius: 4px;
  text-decoration: none;
}
.cta:hover { background: var(--accent-hover); color: var(--accent-fg); }

.muted { color: var(--muted); }

.inline { display: inline; margin: 0; }
.link {
  background: none;
  border: 0;
  color: var(--accent);
  cursor: pointer;
  padding: 0;
  font: inherit;
}

.tick-card {
  margin: 2rem 0;
  padding: 1.5rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
}
.tick-label {
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  margin-bottom: 0.5rem;
}
.tick-value {
  font-size: 2rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* --- admin health table ------------------------------------------------- */
table.health {
  width: 100%;
  border-collapse: collapse;
  margin-top: 1.5rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
  font-size: 0.92rem;
}
table.health th,
table.health td {
  padding: 0.6rem 0.8rem;
  text-align: left;
  border-bottom: 1px solid var(--border);
}
table.health tbody tr:last-child td { border-bottom: 0; }
table.health th {
  font-weight: 600;
  background: var(--card-alt);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
table.health td.num { text-align: right; font-variant-numeric: tabular-nums; }
table.health td.err {
  color: var(--bad);
  font-family: ui-monospace, Menlo, monospace;
  font-size: 0.85rem;
}

.badge {
  display: inline-block;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
}
.badge-idle     { background: var(--card-alt); color: var(--muted); }
.badge-ok       { background: var(--good-soft); color: var(--good); }
.badge-degraded { background: var(--warn-soft); color: var(--warn); }
.badge-failing  { background: var(--bad-soft);  color: var(--bad); }

/* --- training board (Phase 7 pull-list) + directory training strip
   + expected-attendees + catalog admin ----------------------------- */

.page-header-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* Directory training-cards strip — clickable chips above the
   division tabs that jump straight to the training board with the
   selected course pre-selected. */
.dir-training-strip {
  margin: 0 0 1rem;
  padding: 0.6rem 0.8rem;
  background: var(--card-alt);
  border: 1px solid var(--border);
  border-radius: 6px;
}
.dir-training-strip-header {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  margin-bottom: 0.5rem;
}
.dir-training-strip-header h2 {
  margin: 0;
  font-size: 0.92rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.dir-training-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
  gap: 0.5rem;
}
.dir-training-card {
  display: block;
  padding: 0.5rem 0.6rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 4px;
  text-decoration: none;
  color: var(--fg);
  transition: border-color 0.08s;
}
.dir-training-card:hover {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.dir-training-card-name {
  font-size: 0.85rem;
  font-weight: 600;
  line-height: 1.2;
}
.dir-training-card-meta {
  font-size: 0.74rem;
  margin: 0.25rem 0;
}
.dir-training-card-bar {
  height: 4px;
  background: var(--border);
  border-radius: 2px;
  overflow: hidden;
}
.dir-training-card-bar > i {
  display: block;
  height: 100%;
  background: var(--good);
}

/* Add-bucket form — share the existing dir-add-panel chrome. The
   only new element is the checkbox-style stacked field. */
.dir-field-checkbox {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.dir-field-checkbox > * { display: inline; }
.dir-field-checkbox input[type="checkbox"] { margin-right: 0.4rem; }
.dir-field-checkbox small { display: block; margin-top: 0.2rem; font-size: 0.75rem; }

/* Training board page. Forest-green palette port of the
   training-coordination-dashboard mockup. */
.training-board { padding-bottom: 3rem; }
.tb-section { margin: 1.2rem 0; }
.tb-section-header {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  flex-wrap: wrap;
  margin-bottom: 0.7rem;
}
.tb-section-header h2 {
  margin: 0;
  font-size: 1rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.tb-mode {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.85rem;
}
.tb-mode .btn-link.active {
  background: var(--accent-soft);
  border-radius: 3px;
  padding: 0.1rem 0.45rem;
  color: var(--accent);
}

/* Course chips */
.tb-courses {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 0.55rem;
}
.tb-course {
  display: block;
  background: var(--card);
  border: 1px solid var(--border);
  border-left: 3px solid var(--border);
  border-radius: 4px;
  padding: 0.65rem 0.8rem;
  text-align: left;
  cursor: pointer;
  color: var(--fg);
  transition: border-color 0.08s, transform 0.08s;
}
.tb-course:hover { transform: translateY(-1px); border-color: var(--accent); }
.tb-course.active {
  border-color: var(--accent);
  border-left-color: var(--accent);
  background: var(--accent-soft);
  box-shadow: 0 0 0 1px var(--accent) inset;
}
.tb-course-name {
  font-weight: 600;
  font-size: 0.92rem;
  line-height: 1.2;
}
.tb-course-meta {
  font-size: 0.78rem;
  margin: 0.35rem 0;
}
.tb-bar {
  height: 4px;
  background: var(--border);
  border-radius: 2px;
  overflow: hidden;
}
.tb-bar > i {
  display: block;
  height: 100%;
  background: var(--good);
}

/* Crews grid */
.tb-search {
  padding: 0.35rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 4px;
  font: inherit;
  font-size: 0.85rem;
  min-width: 160px;
  background: var(--bg);
  color: var(--fg);
}
.tb-crews {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
  gap: 0.7rem;
}
.tb-crew {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 5px;
  overflow: hidden;
}
.tb-crew.allgood { opacity: 0.5; }
.tb-crew-header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.7rem;
  background: var(--card-alt);
  border-bottom: 1px solid var(--border);
  font-size: 0.9rem;
}
.tb-crew-need {
  margin-left: auto;
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 0.72rem;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  background: var(--bad-soft);
  color: var(--bad);
}
.tb-crew-need.all-set { background: var(--good-soft); color: var(--good); }
.tb-crew-body {
  padding: 0.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.tb-member {
  display: block;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 0.4rem 0.55rem;
  text-align: left;
  cursor: pointer;
  color: var(--fg);
}
.tb-member:hover { border-color: var(--accent); }
.tb-member.sel {
  background: var(--good-soft);
  border-color: var(--good);
}
.tb-member.foreman {
  border-left: 3px solid var(--warn);
}
.tb-member-name {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  font-size: 0.88rem;
}
.tb-member-meta { font-size: 0.74rem; }
.tb-crew-empty { padding: 0.4rem 0.6rem; font-size: 0.78rem; }

/* Crew-card controls — "Pull crew" takes everyone on the crew who
   needs the picked course(s); "Pull 1" is the least-disruptive single
   pick (see pickOne in training_board.js). */
.tb-crew-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.7rem 0;
}
.tb-crew-actions .btn-secondary {
  font-size: 0.78rem;
  padding: 0.2rem 0.55rem;
}
.tb-crew-div {
  margin-left: auto;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.tb-crew-noforeman {
  font-size: 0.72rem;
  font-style: italic;
  color: var(--warn);
}
.tb-toggle {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.82rem;
  color: var(--muted);
}
.tb-error {
  margin: 0.6rem 0;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--bad);
  border-radius: 4px;
  background: var(--bad-soft);
  color: var(--bad);
  font-size: 0.86rem;
}

/* Members who already hold the picked course(s): visible, so the card
   shows the whole crew and you can read "6 of 9 need", but inert —
   registering someone who has the cert only pads the roster. */
.tb-member.done {
  opacity: 0.55;
  cursor: default;
  background: transparent;
}
.tb-member.done:hover { border-color: var(--border); }
.tb-member-done { color: var(--good); }
.tb-crew-more { padding: 0.25rem 0.55rem; font-size: 0.78rem; align-self: flex-start; }
.tb-member-tag {
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0.05rem 0.3rem;
  border-radius: 3px;
  background: var(--warn-soft);
  color: var(--warn);
}

/* Pull-list panel — pinned at the top of the crews list so the
   roster stays visible while the operator scrolls through crews
   below. position:sticky on the wrapping <aside> keeps it in view
   without trapping scroll; the page header above scrolls away
   normally. */
.tb-roster {
  margin: 1rem 0 1.25rem;
  padding: 0.8rem 1rem;
  background: var(--card);
  border: 1px solid var(--accent);
  border-radius: 6px;
}
.tb-roster-sticky {
  position: sticky;
  top: 0;
  z-index: 5;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.06);
}
.tb-roster-header {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  flex-wrap: wrap;
  margin-bottom: 0.6rem;
}
.tb-roster-header h2 { margin: 0; font-size: 1rem; }
.tb-roster-cta { margin-left: auto; }
.tb-roster-groups {
  max-height: 34vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.tb-roster-group h3 {
  margin: 0 0 0.25rem;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.tb-roster-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 0.25rem;
}
.tb-roster-warn { font-size: 0.8rem; color: var(--warn); }
.tb-roster-list li {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  padding: 0.3rem 0.5rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 3px;
  font-size: 0.86rem;
}
.tb-roster-list .btn-link { margin-left: auto; font-size: 0.78rem; }

/* Title input on /admin/users — narrow enough to keep the table
   compact, wide enough that "Safety Training Manager" reads cleanly. */
.users-title-input {
  width: 100%;
  max-width: 220px;
  padding: 0.3rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: 3px;
  background: var(--bg);
  color: var(--fg);
  font: inherit;
  font-size: 0.88rem;
}

/* Side-by-side Potential roster ↔ Attendees layout on the
   session-detail page. The split class lights up only when there
   ARE pre-registrations — otherwise the Attendees column takes the
   full width and the panel reads identically to the pre-pull-list
   single-column version. Collapses below 900px so phones don't
   render a 2-column squish. */
.roster-panel.roster-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}
@media (max-width: 900px) {
  .roster-panel.roster-split { grid-template-columns: 1fr; }
}
.roster-col {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 0.75rem 0.9rem;
}
.roster-col-header {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  margin-bottom: 0.5rem;
}
.roster-col-header h2 {
  margin: 0;
  font-size: 1rem;
}
.roster-col-header .muted { font-size: 0.8rem; }
.roster-col-header .roster-live { margin-left: auto; }
.expected-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 0.3rem;
}
.expected-row {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  padding: 0.35rem 0.5rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-left: 3px solid var(--border);
  border-radius: 3px;
  font-size: 0.86rem;
}
.expected-row.signed-in { border-left-color: var(--good); background: var(--good-soft); }
.expected-row.pending   { border-left-color: var(--warn); }
.expected-row .expected-status {
  margin-left: auto;
  font-size: 0.76rem;
  color: var(--muted);
}
.expected-row.signed-in .expected-status { color: var(--good); }

/* --- training: home, detail, forms ------------------------------------ */

.page-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 1rem;
  margin: 0.5rem 0 1.5rem 0;
}
.page-header h1 { margin: 0; }
.page-header p { margin: 0.2rem 0 0; }

.btn-primary {
  display: inline-block;
  padding: 0.45rem 0.9rem;
  background: var(--accent);
  color: var(--accent-fg);
  border: 1px solid var(--accent);
  border-radius: 4px;
  text-decoration: none;
  font: inherit;
  font-size: 0.92rem;
  cursor: pointer;
}
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.btn-primary:disabled { opacity: 0.6; cursor: not-allowed; }

/* Text-style button — used for inline actions (Clear/Any/All, Cancel,
   Restore, Print) across the training board, catalog, and directory. */
.btn-link {
  background: none;
  border: 1px solid transparent;
  color: var(--accent);
  font: inherit;
  font-size: 0.82rem;
  padding: 0.15rem 0.4rem;
  border-radius: 3px;
  cursor: pointer;
}
.btn-link:hover { background: var(--accent-soft); }

/* Inline load-error text (dashboard query failures). */
.explore-error { color: var(--bad); }

.training-block { margin-bottom: 2rem; }
.training-block h2 {
  font-size: 0.92rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  margin: 0 0 0.6rem;
}

/* Past-sessions block: title + date-range form on one row. */
.training-block-header {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.8rem 1.5rem;
  margin-bottom: 0.6rem;
}
.training-block-header h2 { margin: 0; }
.training-range {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.training-range label {
  display: inline-flex;
  flex-direction: column;
  font-size: 0.78rem;
  gap: 0.2rem;
}
.training-range input[type="date"] {
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg);
  color: var(--fg);
  font: inherit;
  font-size: 0.88rem;
}
.training-range-summary { margin-top: 0.6rem; }

/* Past sessions as a list. Tabular layout reads as one continuous
   timeline; cards waste horizontal space on this surface. */
.session-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.92rem;
}
.session-table thead th {
  text-align: left;
  font-weight: 600;
  color: var(--muted);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0.5rem 0.6rem;
  border-bottom: 1px solid var(--border);
}
.session-table tbody td {
  padding: 0.5rem 0.6rem;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
.session-table tbody tr:hover { background: var(--accent-soft); }
.session-table .num { text-align: right; }
.session-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 0.75rem;
}
.session-card {
  display: block;
  text-decoration: none;
  color: var(--fg);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.9rem 1rem;
}
.session-card:hover {
  border-color: var(--accent);
  box-shadow: 0 1px 6px rgba(45, 94, 62, 0.08);
}
.session-card-past { opacity: 0.88; }
.session-card-title {
  font-weight: 600;
  margin-bottom: 0.3rem;
}
.session-card-meta {
  color: var(--muted);
  font-size: 0.85rem;
  display: flex;
  gap: 0.4rem;
  flex-wrap: wrap;
  margin-bottom: 0.4rem;
}
.session-card-foot {
  font-size: 0.85rem;
  display: flex;
  align-items: baseline;
  gap: 0.3rem;
}

/* Stacked-form is shared by /training/new and /t/{token} signin */
.stacked-form {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  max-width: 480px;
}
.stacked-form label {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  font-size: 0.9rem;
  font-weight: 500;
}
.stacked-form input,
.stacked-form textarea,
.stacked-form select {
  font: inherit;
  font-size: 1rem;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--card);
  color: var(--fg);
}
.stacked-form input:focus,
.stacked-form textarea:focus,
.stacked-form select:focus {
  outline: 2px solid var(--accent);
  outline-offset: -1px;
}
.form-actions { margin-top: 0.5rem; }

/* Equipment-type management on the catalog page (0041). */
.eq-type-list {
  list-style: none;
  margin: 0 0 0.8rem;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}
.eq-type-list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.35rem 0.2rem;
  border-bottom: 1px solid var(--border);
  font-size: 0.95rem;
}
.eq-type-add {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}
.eq-type-add input {
  font: inherit;
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--card);
  color: var(--fg);
  flex: 1;
  max-width: 260px;
}

/* Equipment picker on the session form (0041). The fieldset only
   appears for requires_equipment courses; checkbox labels flow inline
   so eight machines don't take eight rows. */
.equipment-picker {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.7rem 0.9rem 0.8rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
/* display:flex above would defeat the hidden attribute (author CSS
   outranks the UA's [hidden]{display:none}), leaving the picker
   visible for courses that don't need it. Re-assert it. */
.equipment-picker[hidden] { display: none; }
.equipment-picker legend {
  font-size: 0.9rem;
  font-weight: 500;
  padding: 0 0.3rem;
}
.equipment-picker .equipment-options {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.1rem;
}
.equipment-picker .equipment-options label {
  flex-direction: row;
  align-items: center;
  gap: 0.4rem;
  font-weight: 400;
  white-space: nowrap;
}
.equipment-picker .equipment-options input[type="checkbox"] {
  padding: 0;
  accent-color: var(--accent);
}

.form-page { max-width: 600px; }

/* Multi-day series (0043): sibling-day strip + Add-day popover. */
.series-days {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 0.35rem;
}
.series-day {
  font-size: 0.82rem;
  padding: 0.15rem 0.55rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  text-decoration: none;
  color: var(--fg);
  background: var(--card);
}
.series-day.current {
  background: var(--accent-soft);
  border-color: var(--accent);
  font-weight: 600;
}
.add-day { position: relative; display: inline-block; }
.add-day summary { list-style: none; cursor: pointer; }
.add-day summary::-webkit-details-marker { display: none; }
.add-day[open] .add-day-form {
  position: absolute;
  right: 0;
  top: calc(100% + 0.4rem);
  z-index: 30;
  min-width: 260px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  padding: 0.9rem;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  text-align: left;
}
.add-day-form label {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  font-size: 0.85rem;
  font-weight: 500;
}
.add-day-form input {
  font: inherit;
  padding: 0.4rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg);
  color: var(--fg);
}
.add-day-warn {
  font-size: 0.82rem;
  color: var(--fg);
  background: var(--warn-soft, rgba(220, 160, 40, 0.15));
  border: 1px solid var(--warn, #c98a1b);
  border-radius: 6px;
  padding: 0.45rem 0.6rem;
  margin: 0;
}

/* Detail page */
.training-detail .session-description {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.8rem 1rem;
  margin-bottom: 1.5rem;
  white-space: pre-wrap;
}
.detail-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.5rem;
}
@media (max-width: 900px) {
  .detail-grid { grid-template-columns: 1fr; }
}
.qr-panel,
.roster-panel {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 1rem 1.25rem;
}
.qr-panel h2,
.roster-header h2 { margin-top: 0; }
.qr-image {
  display: block;
  margin: 0.5rem auto 0.75rem;
  width: 100%;
  max-width: 360px;
  height: auto;
  border: 1px solid var(--border);
  background: var(--card);
}
.qr-link summary { cursor: pointer; color: var(--muted); }
.qr-link input {
  width: 100%;
  margin-top: 0.4rem;
  font-family: ui-monospace, Menlo, monospace;
  font-size: 0.82rem;
  padding: 0.4rem;
  border: 1px solid var(--border);
  border-radius: 3px;
  background: var(--card);
  color: var(--fg);
}

.roster-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}
/* Soft pulse while htmx settles a live SSE-triggered swap, instead of
   an abrupt content pop. */
#roster, #health-table {
  transition: opacity 150ms ease;
}
#roster.htmx-settling, #health-table.htmx-settling {
  opacity: 0.6;
}
.roster-live {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--good);
  background: var(--good-soft);
  padding: 0.12rem 0.4rem;
  border-radius: 999px;
}
.roster-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}
.roster-table th,
.roster-table td {
  text-align: left;
  padding: 0.45rem 0.5rem;
  border-bottom: 1px solid var(--border);
}
.roster-table tbody tr:last-child td { border-bottom: 0; }
.roster-table th {
  font-size: 0.74rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.roster-actions {
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 0.25rem;
}
.roster-actions-head { width: 1%; }

/* Roster action buttons are icon-only — pencil (edit) and trash
   (remove). Square hit target, muted by default, tints on hover. */
.btn-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  color: var(--muted);
  cursor: pointer;
  transition: background 0.08s, color 0.08s, border-color 0.08s;
}
.btn-icon svg { width: 16px; height: 16px; }
.btn-icon:hover {
  color: var(--accent);
  background: var(--accent-soft);
  border-color: var(--accent-soft);
}
.btn-icon:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.btn-icon-danger { color: var(--bad); }
.btn-icon-danger:hover {
  color: #fff;
  background: var(--bad);
  border-color: var(--bad);
}

/* Inline edit row: the <form> wraps individual <td>s, which means
   the row's <tr> can't be the form parent in HTML5 — we make the
   <form> a direct child of <tr> as a workaround, but the spec only
   technically allows table layout when form descends from table.
   Browsers handle this correctly in practice. */
.roster-row-editing { background: var(--accent-soft); }
.roster-row-editing input[type="text"],
.roster-row-editing input[type="email"] {
  width: 100%;
  padding: 0.3rem 0.4rem;
  border: 1px solid var(--border);
  border-radius: 3px;
  background: var(--bg);
  color: var(--fg);
  font: inherit;
  font-size: 0.86rem;
}

/* Inline-edit action buttons. The previous Save/Cancel were bare,
   unstyled buttons — ambiguous in a dense roster. Save is the green
   confirm (matches .btn-primary); Cancel is a neutral outline. Each
   carries an icon + label so the action reads at a glance. Sized
   compactly to sit inside the roster row. */
.roster-edit-actions {
  display: flex;
  gap: 0.4rem;
  align-items: center;
}
.btn-confirm,
.btn-cancel {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.4rem 0.75rem;
  border-radius: 4px;
  border: 1px solid transparent;
  font: inherit;
  font-size: 0.82rem;
  line-height: 1;
  cursor: pointer;
  white-space: nowrap;
}
.btn-confirm svg,
.btn-cancel svg {
  width: 14px;
  height: 14px;
}
.btn-confirm {
  background: var(--accent);
  color: var(--accent-fg);
  border-color: var(--accent);
}
.btn-confirm:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
}
.btn-cancel {
  background: transparent;
  color: var(--muted);
  border-color: var(--border);
}
.btn-cancel:hover {
  color: var(--fg);
  border-color: var(--muted);
  background: var(--accent-soft);
}
.btn-confirm:focus-visible,
.btn-cancel:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* Collision banner — sits in the row above the editing form. */
.roster-collision td { background: var(--warn-soft); padding: 0; }
.roster-collision-card {
  padding: 0.8rem 1rem;
  border-left: 4px solid var(--warn);
}
.roster-collision-card p { margin: 0.3rem 0 0.6rem; font-size: 0.85rem; }
.roster-collision-card form {
  display: flex;
  gap: 0.5rem;
  align-items: center;
}

/* --- public sign-in page ---------------------------------------------- */

body:has(.signin-page) .container { max-width: 480px; padding: 1.5rem 1rem 3rem; }

.signin-header { margin-bottom: 1.5rem; }
.signin-header h1 { font-size: 1.4rem; margin: 0 0 0.2rem; }

.signin-page .stacked-form { max-width: none; }
.signin-page input { font-size: 1.05rem; padding: 0.7rem 0.8rem; }
.signin-submit { width: 100%; padding: 0.8rem; font-size: 1.05rem; }
.signin-hint { font-size: 0.85rem; }

/* Directory autocomplete dropdown on the name field. */
.signin-name-wrap { position: relative; }
.signin-suggest {
  list-style: none;
  margin: 0.35rem 0 0;
  padding: 0.2rem 0;
  background: var(--card);
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.08);
  max-height: 320px;
  overflow-y: auto;
}
.signin-suggest-item {
  padding: 0.65rem 0.85rem;
  cursor: pointer;
  font-size: 0.95rem;
  line-height: 1.3;
}
.signin-suggest-item:hover,
.signin-suggest-item:active { background: var(--accent-soft); }
.signin-no-match {
  margin: 0.45rem 0 0;
  font-size: 0.85rem;
  line-height: 1.4;
}

.signin-success {
  text-align: center;
  padding: 2rem 1rem;
  background: var(--good-soft);
  border: 1px solid var(--good);
  border-radius: 6px;
  color: var(--good);
}
.signin-success h2 { margin: 0 0 0.5rem; font-size: 1.4rem; }

/* Spec §7.2: language toggle, number choices, shape hints, pair check,
   confirm step. */
.signin-header-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.75rem; }
.signin-fields { display: flex; flex-direction: column; gap: 0.9rem; }
.signin-number-wrap,
.signin-company-wrap { display: flex; flex-direction: column; gap: 0.3rem; font-size: 0.9rem; font-weight: 500; }
.signin-choices { border: 0; margin: -0.4rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.4rem 1.25rem; }
.signin-choices-legend { float: left; width: 100%; padding: 0; margin: 0 0 0.1rem; font-size: 0.85rem; color: var(--muted); }
.stacked-form label.signin-choice { flex-direction: row; align-items: center; gap: 0.5rem; font-weight: 400; }
.signin-page .signin-choice input { width: 1.15rem; height: 1.15rem; padding: 0; margin: 0; accent-color: var(--accent); }
.signin-status:empty { display: none; }
.signin-status p { margin: 0; }
.signin-shape-hint,
.signin-check,
.signin-error {
  margin: -0.4rem 0 0;
  padding: 0.55rem 0.7rem;
  border-left: 3px solid var(--warn);
  background: var(--warn-soft);
  color: var(--fg);
  font-size: 0.9rem;
  line-height: 1.4;
}
.signin-error { margin: 0; }
.signin-check--match { border-left-color: var(--good); background: var(--good-soft); display: flex; gap: 0.45rem; align-items: center; }
.signin-check-mark { color: var(--good); font-weight: 700; }
.signin-check-actions,
.signin-confirm-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.5rem; }
.signin-page .btn-secondary {
  font: inherit;
  padding: 0.5rem 0.9rem;
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  background: var(--card);
  color: var(--fg);
  cursor: pointer;
}
.signin-page .btn-secondary:hover { border-color: var(--accent); color: var(--accent); }
.signin-confirm {
  padding: 1.25rem 1rem;
  border: 1px solid var(--border);
  border-left-width: 4px;
  border-radius: 6px;
  background: var(--card);
}
.signin-confirm--match { border-left-color: var(--good); }
.signin-confirm--amber { border-left-color: var(--warn); }
.signin-confirm h2 { margin: 0 0 0.75rem; font-size: 1.2rem; }
.signin-confirm h2:focus { outline: none; }
.signin-confirm-name { margin: 0; font-size: 1.15rem; font-weight: 600; }
.signin-confirm-number { margin: 0.2rem 0 0; font-size: 1.05rem; }
.signin-confirm-actions .btn-primary { flex: 1 1 12rem; padding: 0.8rem; font-size: 1.05rem; }
.signin-confirm-actions .btn-secondary { flex: 0 1 auto; padding: 0.8rem 1.1rem; }

.portfolio-cta {
  text-align: center;
  margin-top: 1.25rem;
  padding: 1.25rem 1rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
}
.portfolio-cta h3 { margin: 0 0 0.3rem; font-size: 1.05rem; }
.portfolio-cta p  { margin: 0.3rem 0; font-size: 0.92rem; }
.portfolio-qr {
  display: block;
  margin: 0.8rem auto;
  width: 180px;
  height: 180px;
  border: 1px solid var(--border);
  background: white;
}

/* The worker portfolio's styles live in portfolio.css (inlined into that
   page only — see inlineCSSPages in web.go). */

/* --- single cert view (printable) ------------------------------------
   Visual layout matches the docx template Miguel signed off on:
   light-blue wave at top, big "CERTIFICATE OF COMPLETION" headline in
   navy serif, recipient block + course/date in body copy, gold ribbon
   + signature row, navy wave at the bottom with the verify/PDF
   actions over it.  Always rendered in light-mode tokens so the
   downloadable PDF and printed copies don't invert in dark mode. */
body:has(.cert-page) .container {
  max-width: 880px;
  padding: 2rem 1rem 3rem;
}

/* The certificate is always rendered on white, so the theme-driven
   logo swap must not apply here — the light-on-dark logo variant
   would vanish on the white page. !important outranks the
   [data-theme]/media-query swap selectors. */
.cert-page .brand-logo-dark  { display: none !important; }
.cert-page .brand-logo-light { display: block !important; }

.cert-page {
  position: relative;
  background: #ffffff;        /* always light — even in dark mode */
  color: #1a2231;
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;            /* clip the wave decoration */
  padding: 0;                  /* waves are flush against the edges */
  text-align: center;
  box-shadow: 0 4px 24px rgba(20, 35, 70, 0.08);
}

/* Decorative waves — pure backgrounds, no DOM content, so they never
   trap focus or shift screen-reader output. The aspect ratio of the
   source PNGs is ~3.2:1 (top) and ~4.6:1 (bottom); using
   padding-bottom percentages keeps them proportional across widths. */
.cert-wave-top {
  width: 100%;
  padding-bottom: 24%;
  background-repeat: no-repeat;
  background-size: 100% 100%;
  background-position: top center;
}
.cert-wave-bottom {
  width: 100%;
  padding-bottom: 14%;
  background-repeat: no-repeat;
  background-size: 100% 100%;
  background-position: bottom center;
  margin-top: 1.5rem;
}

.cert-page-header { margin-top: -3rem; padding: 0 2rem; }
.cert-brand       { height: 56px; margin: 0 auto; }

.cert-body { padding: 1rem 3rem 0; }

.cert-title {
  margin: 1.25rem 0 0.25rem;
  font-family: 'Georgia', 'Times New Roman', serif;
  color: #14264f;             /* deep navy — matches the wave */
  line-height: 1.05;
}
.cert-title-line {
  display: block;
  font-size: clamp(2rem, 6vw, 3.4rem);
  font-weight: 700;
  letter-spacing: 0.04em;
}
.cert-title-line-2 {
  font-size: clamp(1.4rem, 4vw, 2.2rem);
  letter-spacing: 0.18em;
  font-weight: 500;
  margin-top: 0.1rem;
}

.cert-prelude {
  margin: 1.75rem 0 0.6rem;
  font-size: 1rem;
  color: #475061;
}

.cert-name {
  margin: 0;
  font-family: 'Georgia', 'Times New Roman', serif;
  font-size: clamp(1.8rem, 5vw, 2.8rem);
  font-weight: 700;
  color: #14264f;
  border-bottom: 2px solid #c6d4e6;
  padding-bottom: 0.4rem;
  display: inline-block;
  min-width: 60%;
}
.cert-emp { margin: 0.35rem 0 0; font-size: 0.88rem; color: #6a7589; }

.cert-detail {
  margin: 1.4rem auto 0.5rem;
  max-width: 36rem;
  font-size: 1rem;
  line-height: 1.55;
  color: #2a3346;
}
.cert-detail .cert-training {
  color: #14264f;
  font-weight: 700;
}

/* Machines the training covered (0041) — deliberate ink-on-paper
   colors like .cert-detail: the cert page renders as a document,
   not a themed app surface. */
.cert-equipment {
  margin: 0.2rem auto 0.4rem;
  font-size: 0.95rem;
  color: #2a3346;
}
.cert-equipment strong { color: #14264f; }

/* Course topics list (0042) — same ink-on-paper palette. Columns keep
   a 10-15 item list from becoming a tall pillar on the page. */
.cert-topics {
  margin: 0.6rem auto 0.4rem;
  max-width: 34rem;
  text-align: left;
}
.cert-topics-label {
  text-align: center;
  font-size: 0.82rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #6a7589;
  margin-bottom: 0.3rem;
}
.cert-topics ul {
  margin: 0;
  padding-left: 1.2rem;
  columns: 2;
  column-gap: 2.2rem;
  font-size: 0.92rem;
  color: #2a3346;
}
.cert-topics li { break-inside: avoid; margin-bottom: 0.15rem; }

.cert-validity {
  margin: 0.4rem auto 1.4rem;
  font-size: 0.88rem;
  color: #6a7589;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}

/* Sign-off row — name + title on the left, ribbon medallion on the
   right. Centred together on narrow widths. */
.cert-signoff {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 2rem;
  margin: 2rem 0 0;
  padding: 0 1rem;
  flex-wrap: wrap;
}
.cert-signoff-line {
  text-align: left;
  border-top: 2px solid #14264f;
  padding-top: 0.35rem;
  min-width: 240px;
  flex: 1 1 240px;
}
.cert-signoff-name {
  margin: 0;
  font-family: 'Georgia', 'Times New Roman', serif;
  font-size: 1.1rem;
  color: #14264f;
  font-weight: 700;
}
.cert-signoff-title {
  margin: 0.1rem 0 0;
  font-size: 0.85rem;
  color: #6a7589;
}
.cert-ribbon {
  width: 72px;
  height: auto;
  flex: 0 0 auto;
}

.cert-foot {
  margin: -0.5rem 0 0;
  padding: 1.25rem 2rem 0;
  text-align: center;
}
.cert-foot .btn-primary { display: inline-block; }
.cert-verify {
  font-size: 0.76rem;
  margin-top: 0.9rem;
  color: var(--muted);
  word-break: break-all;
}

/* Tighter on narrow screens — keep the cert legible on phones. */
@media (max-width: 640px) {
  .cert-body { padding: 1rem 1.25rem 0; }
  .cert-page-header { padding: 0 1rem; }
  .cert-signoff { justify-content: center; text-align: center; }
  .cert-signoff-line { text-align: center; }
}

/* Print: the cert page IS the printable artifact, so hide everything
   else and let the wave + ribbon ride to the page edges. */
@media print {
  body:has(.cert-page) .container { max-width: none; padding: 0; }
  .cert-page { border: none; box-shadow: none; border-radius: 0; }
  .cert-foot { display: none; }
}

.badge-perpetual { background: var(--accent-soft); color: var(--accent); }
.badge-valid          { background: var(--good-soft); color: var(--good); }
.badge-expiring_soon  { background: var(--warn-soft); color: var(--warn); }
.badge-expired        { background: var(--bad-soft);  color: var(--bad); }

/* --- logged out landing ---------------------------------------------- */
body:has(.logged-out) .container {
  max-width: 480px;
  padding: 4rem 1rem 3rem;
  text-align: center;
}
.logged-out-header { margin-bottom: 1.5rem; }
.logged-out-brand  { height: 40px; margin: 0 auto; }
.logged-out h1     { font-size: 1.5rem; margin: 0 0 0.5rem; }
.logged-out p      { margin: 0.5rem 0 1.5rem; font-size: 0.95rem; }

/* --- admin certs ----------------------------------------------------- */
.cert-filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  align-items: flex-end;
  padding: 0.7rem 0.9rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  margin: 1rem 0;
}
.cert-filter-bar label {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  font-size: 0.82rem;
}
.cert-filter-bar input[type="text"],
.cert-filter-bar select {
  font: inherit;
  font-size: 0.92rem;
  padding: 0.3rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--card);
  color: var(--fg);
  min-width: 180px;
}
.cert-filter-search { flex: 1 1 240px; }
.cert-result-count { font-size: 0.85rem; margin: 0.4rem 0; }

.cert-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
}
.cert-table th,
.cert-table td {
  padding: 0.5rem 0.6rem;
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: middle;
}
.cert-table tbody tr:last-child td { border-bottom: 0; }
.cert-table th {
  background: var(--card-alt);
  font-size: 0.74rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  /* /admin/certs and /admin/workers list everything — keep the header
     visible while scrolling. Opaque background makes this safe. */
  position: sticky;
  top: 0;
}
.cert-table tbody tr:hover { background: var(--card-alt); }
.cert-row-actions { white-space: nowrap; }
.cert-row-actions a + a { margin-left: 0.5rem; }

/* --- safety overview --------------------------------------------------- */

/* The Overview and TRIR pages share one container geometry so the
   dashboards sub-tabs sit in EXACTLY the same spot on both — switching
   tabs must not make the strip jump (the print view's max-width:none
   override further down still wins when printing TRIR). */
body:has(.safety-overview) .container,
body.trir-page .container { max-width: 1400px; padding: 1rem 1.5rem 3rem; }

.safety-overview .page-header { align-items: center; }
.safety-overview .page-header h1 { font-size: 1.5rem; }

/* Hero KPI ─ "Days since last incident" */
.hero-kpi {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 1rem;
  padding: 1.5rem 1.75rem;
  border-radius: 8px;
  margin-bottom: 1.25rem;
  color: white;
  position: relative;
}
.hero-kpi::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 8px;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.18);
  pointer-events: none;
}
.hero-good { background: linear-gradient(135deg, #1a8d4d 0%, #128141 100%); }
.hero-warn { background: linear-gradient(135deg, #d4831f 0%, #b56715 100%); }
.hero-bad  { background: linear-gradient(135deg, #b3261e 0%, #911b15 100%); }

.hero-label {
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  opacity: 0.85;
  grid-column: 1 / -1;
}
.hero-value {
  font-size: 4.5rem;
  font-weight: 700;
  line-height: 1;
  grid-row: 2;
  font-variant-numeric: tabular-nums;
}
.hero-sub {
  font-size: 0.95rem;
  opacity: 0.92;
  grid-row: 2;
  text-align: right;
}

/* Chart/table card chrome — shared by the dashboard, TRIR report, and
   safety-walk detail. Sizing (flex, min-height) is applied per page. */
.viz-card {
  position: relative;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 1rem;
}
.viz-card canvas {
  width: 100% !important;
  max-width: 100%;
}

/* Card grid */
.card-row {
  display: grid;
  gap: 1rem;
  margin-bottom: 1rem;
}
.card-row-2 { grid-template-columns: 1fr 1fr; }
.card-row-3 { grid-template-columns: 1fr 1fr 1fr; }
.card-row-full { display: block; }

.safety-overview .viz-card {
  min-height: 320px;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.card-title {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 600;
}
.card-sub {
  margin: 0 0 0.4rem;
  font-size: 0.82rem;
  color: var(--muted);
}
.safety-overview .viz-card canvas {
  flex: 1;
  min-height: 0;
  max-height: 280px;
}

/* Top-N table */
.ranked-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.92rem;
}
.ranked-table th,
.ranked-table td {
  padding: 0.5rem 0.6rem;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
.ranked-table tbody tr:last-child td { border-bottom: 0; }
.ranked-table th {
  background: var(--card-alt);
  font-size: 0.74rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  text-align: left;
}
.ranked-table td.num,
.ranked-table th.num { text-align: right; font-variant-numeric: tabular-nums; }
.share-cell { width: 30%; }
.share-bar {
  display: block;
  height: 8px;
  background: linear-gradient(90deg, var(--accent) 0%, var(--good) 100%);
  border-radius: 4px;
}

.load-status { margin-top: 1rem; }

@media (max-width: 1100px) {
  .card-row-3 { grid-template-columns: 1fr 1fr; }
  .card-row-3 > :first-child { grid-column: 1 / -1; }
}
@media (max-width: 760px) {
  .card-row-2, .card-row-3 { grid-template-columns: 1fr; }
  .card-row-3 > :first-child { grid-column: auto; }
  .hero-value { font-size: 3rem; }
  .hero-sub { text-align: left; grid-row: 3; grid-column: 1 / -1; }
}

/* --- safety overview: window selector --------------------------------- */
.window-bar {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 0.6rem 0.9rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  margin-bottom: 1rem;
}
.window-presets { display: inline-flex; gap: 0.2rem; }
.window-preset {
  font: inherit;
  font-size: 0.85rem;
  padding: 0.3rem 0.7rem;
  background: var(--card);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
  cursor: pointer;
}
.window-preset:hover { background: var(--card-alt); }
.window-preset.active {
  background: var(--accent);
  color: var(--accent-fg);
  border-color: var(--accent);
}
.window-dates { display: inline-flex; gap: 0.6rem; }
.window-dates label {
  font-size: 0.82rem;
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
}
.window-dates input[type="date"] {
  font: inherit;
  font-size: 0.88rem;
  padding: 0.22rem 0.4rem;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--card);
  color: var(--fg);
}
.window-status { margin-left: auto; font-size: 0.85rem; }

/* --- safety overview: clickable table + chart cursor ----------------- */
.safety-overview .viz-card canvas { cursor: pointer; }
.ranked-table-clickable tbody tr { cursor: pointer; transition: background 0.08s; }
.ranked-table-clickable tbody tr:hover { background: var(--accent-soft); }
.ranked-table-clickable tbody tr.no-drill { cursor: default; }
.ranked-table-clickable tbody tr.no-drill:hover { background: transparent; }

/* --- modal popup ------------------------------------------------------ */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(15, 20, 30, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  padding: 1.5rem;
}
.modal {
  background: var(--card);
  color: var(--fg);
  border-radius: 8px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
  width: 100%;
  max-width: 760px;
  max-height: 88vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
  padding: 1rem 1.25rem 0.6rem;
  border-bottom: 1px solid var(--border);
}
.modal-title { margin: 0; font-size: 1.1rem; }
.modal-subtitle { margin: 0.2rem 0 0; font-size: 0.85rem; }
.modal-close {
  font-size: 1.6rem;
  line-height: 1;
  background: none;
  border: none;
  color: var(--muted);
  cursor: pointer;
  padding: 0 0.4rem;
  border-radius: 4px;
}
.modal-close:hover { color: var(--bad); background: var(--bad-soft); }
.modal-body {
  padding: 0.8rem 1.25rem 1.25rem;
  overflow-y: auto;
}
.modal-summary { margin: 0 0 0.6rem; font-size: 0.85rem; }

.drill-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.92rem;
}
.drill-table th,
.drill-table td {
  padding: 0.45rem 0.55rem;
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: middle;
}
.drill-table tbody tr:last-child td { border-bottom: 0; }
.drill-table th {
  background: var(--card-alt);
  font-size: 0.74rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  position: sticky;
  top: 0;
}
.drill-table th.num,
.drill-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
.drill-table tbody tr:hover { background: var(--card-alt); }

@media (max-width: 760px) {
  .window-bar { gap: 0.6rem; }
  .window-status { margin-left: 0; flex-basis: 100%; }
  .modal { max-width: 100%; max-height: 95vh; }
}

/* --- admin locations -------------------------------------------------- */
.locations-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.92rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
  margin-top: 0.5rem;
}
.locations-table th,
.locations-table td {
  padding: 0.55rem 0.7rem;
  text-align: left;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
.locations-table tbody tr:last-child td { border-bottom: 0; }
.locations-table th {
  background: var(--card-alt);
  font-size: 0.74rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.locations-table td.num,
.locations-table th.num { text-align: right; font-variant-numeric: tabular-nums; }
.loc-id {
  font-family: ui-monospace, Menlo, monospace;
  font-size: 0.82rem;
  cursor: help;
}
.locations-table input[type="text"] {
  width: 100%;
  font: inherit;
  font-size: 0.92rem;
  padding: 0.3rem 0.45rem;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--card);
  color: var(--fg);
}
.loc-actions {
  display: flex;
  gap: 0.4rem;
  align-items: center;
  white-space: nowrap;
}
.loc-err {
  color: var(--bad);
  font-size: 0.8rem;
}
.loc-notes { max-width: 320px; display: inline-block; }

.crew-samples {
  max-width: 360px;
  line-height: 1.7;
}
.sample-chip {
  display: inline-block;
  font-size: 0.78rem;
  padding: 0.1rem 0.5rem;
  margin: 0.1rem 0.2rem 0.1rem 0;
  background: var(--accent-soft);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  color: var(--fg);
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: middle;
}

/* --- recent incident reports card ------------------------------------ */
.incident-card { margin-bottom: 1rem; }
.incident-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
  flex-wrap: wrap;
}
.incident-badge {
  align-self: flex-start;
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  white-space: nowrap;
}
.badge-good { background: var(--good-soft); color: var(--good); }
.badge-warn { background: var(--warn-soft); color: var(--warn); }
.badge-bad  { background: var(--bad-soft);  color: var(--bad); }

.incident-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.92rem;
  margin-top: 0.6rem;
}
.incident-table th,
.incident-table td {
  text-align: left;
  padding: 0.45rem 0.55rem;
  border-bottom: 1px solid var(--border);
}
.incident-table tbody tr:last-child td { border-bottom: 0; }
.incident-table th {
  background: var(--card-alt);
  font-size: 0.74rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.incident-row { cursor: pointer; transition: background 0.08s; }
.incident-row:hover { background: var(--bad-soft); }
.incident-label {
  max-width: 360px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ─── Field Directory (/admin/directory) ──────────────────────────────
   Layout: page header + horizontal filter bar (divisions + buckets +
   search) across the top, then a two-pane body (tree on the left,
   selected-person detail on the right). Wider working area for the
   tree compared to the original three-column layout.
   ───────────────────────────────────────────────────────────────────── */

.dir-shell {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  margin-top: 0.75rem;
}

/* ── page header ────────────────────────────────────────────────── */
.dir-page-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
  flex-wrap: wrap;
}
.dir-page-header h1 { margin: 0 0 0.25rem; }
.dir-page-header p  { margin: 0; max-width: 60ch; font-size: 0.9rem; line-height: 1.4; }
.dir-page-actions {
  display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center;
}

/* ── horizontal filter bar ──────────────────────────────────────── */
.dir-filterbar {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.75rem 0.9rem;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  position: sticky;
  top: 0;
  z-index: 5;
}
.dir-search-input {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--border-strong);
  border-radius: 5px;
  padding: 0.55rem 0.75rem;
  color: var(--fg);
  font: inherit;
  font-size: 0.95rem;
}
.dir-search-input:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}

.dir-tabs {
  display: flex;
  gap: 0.4rem;
  flex-wrap: wrap;
}
.dir-tab {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.4rem 0.9rem;
  color: var(--fg);
  font: inherit;
  font-size: 0.88rem;
  cursor: pointer;
  transition: background 0.08s, border-color 0.08s, color 0.08s;
}
.dir-tab:hover { border-color: var(--accent); color: var(--accent); }
.dir-tab.active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-fg);
  font-weight: 600;
}
.dir-tab.drag-target {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent) inset;
}
.dir-tab-count {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 0.72rem;
  background: var(--card-alt);
  color: var(--muted);
  border-radius: 999px;
  padding: 0.05rem 0.5rem;
  min-width: 2em;
  text-align: center;
}
.dir-tab.active .dir-tab-count {
  background: rgba(255,255,255,0.18);
  color: var(--accent-fg);
}

/* Trailing "+" pill on the division strip — opens the add-division
   panel. Same height as a tab, dashed border so it reads as a
   call-to-action rather than a selectable division.
   `margin-left: auto` pins it to the far right of the flex row
   regardless of how many divisions are present. */
.dir-tab-add,
.dir-bucket-chip-add {
  font-size: 1.05rem;
  font-weight: 600;
  line-height: 1;
  padding: 0.35rem 0.75rem;
  border-style: dashed;
  color: var(--muted);
  margin-left: auto;
}
.dir-tab-add:hover,
.dir-bucket-chip-add:hover {
  border-style: solid;
  color: var(--accent);
  border-color: var(--accent);
  background: transparent;
}

.dir-buckets-row {
  display: flex;
  gap: 0.35rem;
  flex-wrap: wrap;
  padding-top: 0.55rem;
  border-top: 1px dashed var(--border);
}
.dir-bucket-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 999px;
  padding: 0.25rem 0.7rem;
  color: var(--muted);
  font: inherit;
  font-size: 0.78rem;
  cursor: pointer;
}
.dir-bucket-chip:hover { background: var(--accent-soft); color: var(--accent); }
.dir-bucket-chip.active {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}
.dir-bucket-chip.drag-target {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent) inset;
}
.dir-bucket-count {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 0.7rem;
  background: var(--card-alt);
  border-radius: 999px;
  padding: 0.02rem 0.45rem;
}

/* ── inline add-person panel ────────────────────────────────────── */
.dir-add-panel {
  background: var(--card);
  border: 1px solid var(--accent);
  border-radius: 6px;
  padding: 1rem 1.1rem 1.1rem;
  box-shadow: 0 4px 18px rgba(45, 94, 62, 0.08);
}
.dir-add-panel h3 { margin: 0 0 0.7rem; }
.dir-add-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 0.7rem;
  margin-bottom: 0.7rem;
}
.dir-field-wide { grid-column: 1 / -1; }

/* ── body: tree only (detail moved to a modal) ──────────────────── */
.dir-body { display: block; }

/* Full-width opt-in for the directory page. base.html's <main class="container">
   normally clamps the reading width; .page-wide on <body> lifts that cap. */
body.page-wide .container {
  max-width: none;
  width: 100%;
  padding-left: 1.5rem;
  padding-right: 1.5rem;
}

/* Main (tree) pane */
.dir-main {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 1rem 1.1rem 1.4rem;
  min-height: 60vh;
}
.dir-main-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  border-bottom: 1px solid var(--border);
  padding-bottom: 0.75rem;
  margin-bottom: 1rem;
}
.dir-main-header h2 { margin: 0; font-size: 1.1rem; }
.dir-main-sub      { margin: 0.2rem 0 0; font-size: 0.82rem; }
.dir-search-status {
  background: var(--accent-soft);
  border-radius: 5px;
  padding: 0.55rem 0.75rem;
  margin-bottom: 0.7rem;
  font-size: 0.86rem;
}
.dir-write-badge {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  background: var(--warn-soft);
  color: var(--warn);
  border-radius: 12px;
  padding: 0.15rem 0.65rem;
}
.dir-tree { display: flex; flex-direction: column; gap: 1.4rem; }
.dir-error { color: var(--bad); font-size: 0.9rem; }

/* Super block — one per superintendent (or one orphan-foremen block). */
.dir-super-block {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.75rem 0.85rem 0.9rem;
  background: var(--bg);
  transition: background 0.1s, border-color 0.1s;
}
.dir-super-block.drop-target { background: var(--accent-soft); border-color: var(--accent); }
/* Crewless general super — same header anatomy as a super block but
   one line tall, no grid. Grows into a real block automatically when
   someone reports directly to them (see directory.js divisionTree). */
.dir-gs-line {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  flex-wrap: wrap;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.5rem 0.85rem;
  background: var(--bg);
  font-size: 0.94rem;
  transition: background 0.1s, border-color 0.1s;
}
.dir-gs-line.drop-target { background: var(--accent-soft); border-color: var(--accent); }
.dir-super-header {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  flex-wrap: wrap;
  margin-bottom: 0.6rem;
  font-size: 0.94rem;
}
.dir-orphan-block { background: var(--card-alt); }

/* Orphan-worker list — workers with no active foreman to attach to.
   Lays out as a wrapping row of small chips, not a single column, so
   a busy division doesn't grow a tall pillar at the bottom. */
.dir-orphan-worker-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}
.dir-orphan-worker-list .dir-worker {
  width: auto;
  flex: 0 0 auto;
  min-width: 180px;
  background: var(--bg);
  border: 1px solid var(--border);
}

.dir-foremen {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 0.6rem;
}

.dir-foreman-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 0.5rem 0.55rem 0.4rem;
  cursor: grab;
  transition: background 0.1s, border-color 0.1s, transform 0.08s;
}
.dir-foreman-card:active { cursor: grabbing; }
.dir-foreman-card.drop-target { background: var(--accent-soft); border-color: var(--accent); }
.dir-foreman-card.selected    { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent) inset; }

.dir-foreman-header {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.25rem;
  font-size: 0.85rem;
  margin-bottom: 0.3rem;
}
.dir-foreman-name-row,
.dir-foreman-actions {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.45rem;
}
/* Role line carries Crew certs at its right; the count line carries
   Get cards at its right (.dir-crew-link's margin-left:auto). */
.dir-foreman-top,
.dir-foreman-actions { width: 100%; }
.dir-foreman-top { display: flex; align-items: center; gap: 0.45rem; }
.dir-foreman-count { font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: 0.7rem; }

.dir-name-btn {
  background: none;
  border: none;
  padding: 0;
  color: var(--fg);
  font: inherit;
  cursor: pointer;
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
}
.dir-name-btn:hover strong { color: var(--accent); }

.dir-workers { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 0.15rem; }
.dir-worker {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 0.4rem;
  align-items: baseline;
  padding: 0.25rem 0.35rem;
  border-radius: 3px;
  cursor: grab;
  font-size: 0.82rem;
}
.dir-worker:active { cursor: grabbing; }
.dir-worker:hover { background: var(--accent-soft); }
.dir-worker.selected { background: var(--accent-soft); outline: 1px solid var(--accent); }
.dir-worker-id   { font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: 0.72rem; }
.dir-worker-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dir-worker-empty { color: var(--muted); font-style: italic; padding: 0.2rem 0.35rem; }

.dir-bucket-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 0.6rem;
}

/* Bucket strip — when a side bucket (Floaters / Support / New hires /
   Pending move / Out) is chosen, its members appear in a compact
   horizontal strip ABOVE the division tree. The tree stays rendered
   so users can drag a card from the strip onto a foreman in the tree.
*/
.dir-bucket-strip {
  background: var(--accent-soft);
  border: 1px solid var(--accent);
  border-radius: 6px;
  padding: 0.75rem 0.9rem 0.85rem;
  margin-bottom: 1.1rem;
}
.dir-bucket-strip-header {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  flex-wrap: wrap;
  margin-bottom: 0.6rem;
}
.dir-bucket-strip-header strong { color: var(--accent); }
.dir-bucket-strip-header .btn-link { margin-left: auto; }
.dir-bucket-strip-cards {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.dir-bucket-strip-card {
  flex: 0 0 auto;
  min-width: 170px;
  max-width: 260px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 0.55rem 0.7rem;
  cursor: grab;
  transition: border-color 0.08s, box-shadow 0.08s;
}
.dir-bucket-strip-card:active { cursor: grabbing; }
.dir-bucket-strip-card:hover { border-color: var(--accent); }
.dir-bucket-strip-card.selected { box-shadow: 0 0 0 1px var(--accent) inset; }
.dir-bucket-strip-name {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  font-size: 0.9rem;
}
.dir-bucket-strip-meta {
  font-size: 0.75rem;
  margin-top: 0.15rem;
}

/* New-hire highlight — hired within the last 30 days. 3px green rail
   painted via inset box-shadow so it doesn't push the layout. */
.dir-foreman-card.new-hire,
.dir-bucket-strip-card.new-hire,
.dir-worker.new-hire {
  background: var(--good-soft);
  box-shadow: inset 3px 0 0 var(--good);
}

/* Terminated — still in the directory but not active. Red rail + soft
   tint + dimmed text. Rehire by editing status back to active. */
.dir-foreman-card.terminated,
.dir-bucket-strip-card.terminated,
.dir-worker.terminated {
  background: var(--bad-soft);
  box-shadow: inset 3px 0 0 var(--bad);
  opacity: 0.78;
}

/* Off-crew — the person is in this division but on nobody's crew, or is
   flagged (Out / sick, Pending move) while staying on their foreman's
   roster. Amber rail, deliberately quieter than terminated's red: it is
   an operational state, not an ended one. `.terminated` is declared
   AFTER this so it wins when both apply. */
.dir-foreman-card.off-crew,
.dir-bucket-strip-card.off-crew,
.dir-worker.off-crew {
  box-shadow: inset 3px 0 0 var(--warn);
}

/* The status flag on a person row — "Out / sick", "Pending move", or an
   admin-created chip's label. Reads the label the server sent, so a new
   bucket needs no CSS. Carries the state in TEXT, not colour alone. */
.dir-status-flag {
  flex: none;
  padding: 0.05rem 0.4rem;
  font-size: 0.68rem;
  font-weight: 600;
  color: var(--warn);
  background: var(--warn-soft);
  border-radius: 999px;
  white-space: nowrap;
}

/* A crew whose lead is off-crew (terminated, floater, new hire…). The
   card still renders with its people — hiding it would make a real crew
   disappear — so the banner has to carry the whole explanation. */
.dir-foreman-card.lead-off-crew {
  box-shadow: inset 3px 0 0 var(--warn);
}
.dir-lead-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem;
  margin: 0 0 0.4rem;
  padding: 0.4rem 0.5rem;
  font-size: 0.76rem;
  line-height: 1.35;
  color: var(--warn);
  background: var(--warn-soft);
  border-radius: 0.35rem;
}
.dir-lead-banner strong { font-weight: 700; }
.dir-lead-banner span   { color: var(--fg); }
.dir-lead-banner .btn-link { font-size: 0.76rem; }

/* Role pill for the off-crew lane header. */
.dir-role-offcrew {
  color: var(--warn);
  background: var(--warn-soft);
}

/* Role pill + status badge variants that had no rule at all, so a
   management person and every admin-created bucket rendered in the
   default grey — including `terminated`, whose modal badge disagreed
   with its own red card. */
.dir-role-management {
  color: var(--accent);
  background: var(--accent-soft);
}
.badge-status-terminated {
  color: var(--bad);
  background: var(--bad-soft);
}
.badge-status-management {
  color: var(--accent);
  background: var(--accent-soft);
}

/* Note textarea on the detail pane (annotations — no move). */
.dir-note-form {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-bottom: 0.75rem;
  padding: 0.5rem;
  background: var(--panel-soft, var(--accent-soft));
  border: 1px solid var(--border);
  border-radius: 6px;
}
.dir-note-form textarea {
  width: 100%;
  min-height: 60px;
  font: inherit;
  resize: vertical;
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 0.4rem;
  background: var(--card);
  color: var(--fg);
}
.dir-note-form-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.dir-note-form-actions .error { color: var(--bad); font-size: 0.8rem; }

/* Role pills */
.dir-role-pill {
  display: inline-block;
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  border-radius: 10px;
  padding: 0.1rem 0.5rem;
  background: var(--card-alt);
  color: var(--muted);
  font-weight: 600;
  white-space: nowrap;
}
.dir-role-general_super  { background: var(--accent); color: var(--accent-fg); }
.dir-role-superintendent { background: var(--accent-soft); color: var(--accent); }
.dir-role-foreman        { background: var(--warn-soft); color: var(--warn); }
.dir-role-worker         { background: var(--card-alt); color: var(--muted); }
.dir-role-external       { background: var(--bad-soft);  color: var(--bad); }

.dir-detail-link {
  font-size: 0.78rem;
  margin-left: auto;
}

/* Right detail pane */
.dir-detail {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 1rem 1.1rem 1.3rem;
  position: sticky;
  top: 0.75rem;
  max-height: calc(100vh - 1.5rem);
  overflow: auto;
}
.dir-detail-empty { color: var(--muted); padding: 2rem 0; text-align: center; }
.dir-detail-header { display: flex; flex-direction: column; gap: 0.3rem; margin-bottom: 0.9rem; }
.dir-detail-header h3 { margin: 0; font-size: 1.1rem; }

.dir-detail-grid {
  display: grid;
  grid-template-columns: 100px 1fr;
  gap: 0.35rem 0.7rem;
  margin: 0 0 1rem;
  font-size: 0.86rem;
}
.dir-detail-grid dt { color: var(--muted); }
.dir-detail-grid dd { margin: 0; }

.dir-detail-actions h4 {
  margin: 0.8rem 0 0.5rem;
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}
.dir-field {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  margin-bottom: 0.55rem;
  font-size: 0.85rem;
}
.dir-field input, .dir-field select, .dir-field textarea {
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 0.4rem 0.55rem;
  font: inherit;
}
.dir-field textarea { resize: vertical; }

.dir-detail-buttons {
  display: flex; align-items: center; gap: 0.6rem;
  margin-top: 0.6rem; flex-wrap: wrap;
}
.dir-saved { color: var(--good); font-size: 0.82rem; }

/* ── Detail modal ───────────────────────────────────────────────── */
.dir-modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(15, 20, 28, 0.55);
  z-index: 50;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 3vh 1rem;
  overflow-y: auto;
}
.dir-modal {
  background: var(--card);
  color: var(--fg);
  border-radius: 8px;
  border: 1px solid var(--border);
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.25);
  width: min(1100px, 100%);
  max-height: 94vh;
  display: flex;
  flex-direction: column;
}
.dir-modal-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--border);
}
.dir-modal-title h2 { margin: 0; font-size: 1.3rem; }
.dir-modal-subline {
  display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center;
  margin-top: 0.4rem; font-size: 0.88rem;
}
.dir-modal-close {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--muted);
  width: 36px; height: 36px;
  border-radius: 50%;
  font: inherit;
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
}
.dir-modal-close:hover { color: var(--fg); border-color: var(--fg); }

.dir-modal-body {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.25rem;
  padding: 1.1rem 1.25rem 1.25rem;
  overflow-y: auto;
}
@media (max-width: 880px) {
  .dir-modal-body { grid-template-columns: 1fr; }
}
.dir-modal-col { min-width: 0; } /* let flexbox children shrink properly */
.dir-modal-section-spacer { margin-top: 1.25rem; }

/* Training list inside the modal */
.dir-trainings {
  list-style: none;
  padding: 0;
  margin: 0.5rem 0 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.dir-training-item {
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 0.55rem 0.7rem;
  background: var(--bg);
}
.dir-training-title {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.6rem;
  flex-wrap: wrap;
  font-size: 0.95rem;
}
.dir-training-meta {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 0.78rem;
  margin-top: 0.2rem;
}

/* SiteDocs block in the person modal (spec §7.3) */
.dir-sitedocs-state {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin: 0.4rem 0 0;
}

/* Danger zone — soft-remove button, separated from edit/move actions. */
.dir-danger-zone {
  margin-top: 1.4rem;
  padding: 0.85rem 1rem 1rem;
  border: 1px solid var(--bad-soft);
  border-radius: 5px;
  background: var(--bad-soft);
}
.dir-danger-zone h4 { margin: 0 0 0.35rem; color: var(--bad); }
.dir-danger-zone p  { margin: 0 0 0.65rem; font-size: 0.84rem; color: var(--bad); }
.btn-danger {
  background: var(--bad); color: #fff;
  border: 1px solid var(--bad);
  border-radius: 4px;
  padding: 0.45rem 0.9rem;
  font: inherit;
  cursor: pointer;
}
.btn-danger:hover { filter: brightness(0.92); }
.btn-danger:disabled { opacity: 0.6; cursor: not-allowed; }

.dir-history { margin-top: 1rem; }
.dir-history summary {
  cursor: pointer;
  font-size: 0.82rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 0.4rem;
}
.dir-history-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 0.55rem; }
.dir-history-item {
  border-left: 2px solid var(--border);
  padding: 0.05rem 0 0.05rem 0.6rem;
  font-size: 0.82rem;
}
.dir-history-when { display: flex; gap: 0.5rem; justify-content: space-between; align-items: baseline; }
.dir-history-what { color: var(--fg); font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: 0.78rem; }

/* Status badges (reuse .badge from the existing palette) */
.badge-status-active             { background: var(--good-soft); color: var(--good); }
.badge-status-floater            { background: var(--accent-soft); color: var(--accent); }
.badge-status-new_hire           { background: var(--warn-soft); color: var(--warn); }
.badge-status-pending_relocation { background: var(--warn-soft); color: var(--warn); }
.badge-status-out                { background: var(--bad-soft);  color: var(--bad); }

/* Login page fine-print (mirror /logged-out styling). */
.login-fineprint { margin-top: 1.5rem; font-size: 0.85rem; }

/* ─── /admin/users (role management) ───────────────────────────────── */
.users-table .users-role-select {
  background: var(--bg);
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  padding: 0.35rem 0.55rem;
  color: var(--fg);
  font: inherit;
  font-size: 0.88rem;
  min-width: 11rem;
}
.users-table .users-role-select:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}
.users-actions {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  flex-wrap: wrap;
}
.users-self {
  font-size: 0.78rem;
  margin-left: 0.4rem;
}

/* ─── /trainings (trainee browse + pre-register) ───────────────────── */
.trainings-list {
  list-style: none;
  padding: 0;
  margin: 1rem 0 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 1rem;
}
.training-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.95rem 1.05rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}
.training-card.registered {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent) inset;
}
.training-card-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.75rem;
  flex-wrap: wrap;
}
.training-card-head h2 { margin: 0; font-size: 1.1rem; }
.training-when { font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: 0.82rem; }
.training-card-meta { margin: 0; display: flex; flex-direction: column; gap: 0.3rem; font-size: 0.88rem; }
.training-card-meta > div { display: flex; gap: 0.55rem; }
.training-card-meta dt { color: var(--muted); min-width: 5.5em; }
.training-card-meta dd { margin: 0; }
.training-card-actions { display: flex; gap: 0.5rem; margin-top: auto; }
.training-card-actions .btn-primary,
.training-card-actions .btn-secondary { padding: 0.45rem 0.9rem; }
/* ─── Public report form (/report) ─────────────────────────────────────
   Single-column, big tap targets, no auth chrome. Workers open this on
   a phone after scanning a QR sticker — so generous spacing, large
   inputs, and a clear language toggle in the header. */

.report-header {
  /* Header gets a slightly different shape — no nav links, just the
     brand and the language toggle. */
  justify-content: space-between;
}
.lang-toggle {
  font-size: 0.85rem;
  padding: 0.3rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--card);
  color: var(--fg);
}
.lang-toggle:hover { border-color: var(--accent); color: var(--accent); }

.report-shell {
  max-width: 640px;
  margin: 0 auto;
  padding: 1.5rem 0 4rem;
}
.report-intro h1 { margin: 0 0 0.4rem; font-size: 1.6rem; }
.report-intro p  { margin: 0 0 1.5rem; line-height: 1.5; font-size: 0.95rem; }

.report-form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.report-field {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
.report-field-row {
  flex-direction: row;
  align-items: flex-start;
  gap: 0.6rem;
  background: var(--accent-soft);
  padding: 0.75rem 0.85rem;
  border-radius: 5px;
}
.report-field-row input[type="checkbox"] {
  margin-top: 0.18rem;
  width: 1.1rem;
  height: 1.1rem;
}
.report-label {
  font-weight: 600;
  font-size: 0.94rem;
}
.report-form input[type="text"],
.report-form input[type="tel"],
.report-form select,
.report-form textarea {
  background: var(--card);
  border: 1px solid var(--border-strong);
  border-radius: 5px;
  padding: 0.7rem 0.75rem;
  color: var(--fg);
  font: inherit;
  font-size: 1rem; /* keeps mobile from auto-zooming on focus */
}
.report-form textarea { resize: vertical; min-height: 6rem; }
.report-form input:focus, .report-form select:focus, .report-form textarea:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}
.report-err {
  color: var(--bad);
  font-size: 0.82rem;
}
.report-contact {
  border: 1px dashed var(--border-strong);
  border-radius: 5px;
  padding: 0.85rem 1rem 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}
.report-contact legend {
  padding: 0 0.4rem;
  font-weight: 600;
  font-size: 0.9rem;
}
.report-contact p { margin: 0 0 0.4rem; font-size: 0.86rem; line-height: 1.4; }

.report-submit { padding: 0.85rem 1rem; font-size: 1rem; }

.report-thanks h1 { color: var(--accent); }
.report-thanks p  { line-height: 1.55; }
.report-thanks .btn-primary { margin-top: 1rem; }

/* ─── Admin observations list + detail ────────────────────────────── */

.obs-counts {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin: 0.75rem 0 1rem;
}
.obs-counts-spacer { flex: 1; }
/* Anchor variant — chips also work as filter links. */
a.obs-count { text-decoration: none; transition: filter 0.08s; }
a.obs-count:hover { filter: brightness(0.94); }
.obs-count {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 0.78rem;
  padding: 0.25rem 0.7rem;
  border-radius: 12px;
  background: var(--card-alt);
  color: var(--muted);
}
.obs-count-reported  { background: var(--bad-soft);    color: var(--bad); }
.obs-count-pending   { background: var(--warn-soft);   color: var(--warn); }
.obs-count-action    { background: var(--warn-soft);   color: var(--bad); }
.obs-count-resolved  { background: var(--good-soft);   color: var(--good); }
/* Status tabs (2026-10): the enum-named classes the tab loop emits, plus
   All / Open. The active tab gets an outline so it reads as selected. */
.obs-count-pending_decision { background: var(--warn-soft); color: var(--warn); }
.obs-count-action_required  { background: var(--warn-soft); color: var(--bad); }
.obs-count-all, .obs-count-open { background: var(--card-alt); color: var(--fg); }
.obs-count.is-active { box-shadow: inset 0 0 0 2px currentColor; font-weight: 600; }
/* Legacy fallbacks — a lingering row from before migration 0019
   would otherwise be badge-less. Should be 0 rows in prod after
   the migration but harmless to keep. */
.obs-count-new       { background: var(--bad-soft);    color: var(--bad); }
.obs-count-reviewing { background: var(--warn-soft);   color: var(--warn); }
.obs-count-dismissed { background: var(--card-alt);    color: var(--muted); }

.obs-table tbody tr.obs-row { cursor: pointer; transition: background 0.08s; }
.obs-table tbody tr.obs-row:hover { background: var(--accent-soft); }
/* Keyboard-focusable row link — visually identical to the plain cell
   text so the clickable-row affordance stays the same. */
.obs-row-link { color: inherit; text-decoration: none; }
.obs-desc-cell {
  max-width: 340px;
  white-space: normal;
  font-size: 0.86rem;
  line-height: 1.35;
}

.badge-concern-near_miss     { background: var(--warn-soft);   color: var(--warn); }
.badge-concern-good_catch    { background: var(--accent-soft); color: var(--accent); }
.badge-concern-hr_related    { background: var(--bad-soft);    color: var(--bad); }
.badge-concern-other         { background: var(--card-alt);    color: var(--muted); }

.badge-obs-status-reported         { background: var(--bad-soft);    color: var(--bad); }
.badge-obs-status-pending_decision { background: var(--warn-soft);   color: var(--warn); }
.badge-obs-status-action_required  { background: var(--warn-soft);   color: var(--bad); }
.badge-obs-status-resolved         { background: var(--good-soft);   color: var(--good); }
/* Legacy pre-0019 status values — see .obs-count-* fallbacks above. */
.badge-obs-status-new        { background: var(--bad-soft);    color: var(--bad); }
.badge-obs-status-reviewing  { background: var(--warn-soft);   color: var(--warn); }
.badge-obs-status-dismissed  { background: var(--card-alt);    color: var(--muted); }

/* Severity chip — small pill next to the observation id + in the
   list table. Colour ramps 1 (green, minor) → 5 (red, critical),
   matching the mental model workers pick from on the form. */
.obs-sev {
  display: inline-block;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  vertical-align: middle;
}
.obs-sev.sev-1 { background: var(--good-soft); color: var(--good); }
.obs-sev.sev-2 { background: var(--good-soft); color: var(--good); }
.obs-sev.sev-3 { background: var(--warn-soft); color: var(--warn); }
.obs-sev.sev-4 { background: var(--warn-soft); color: var(--bad);  }
.obs-sev.sev-5 { background: var(--bad-soft);  color: var(--bad);  }

/* Photo gallery — auto-fill grid of square thumbnails so the
   layout adapts from 1 photo (giant tile) to 5 (compact grid)
   without a media-query staircase. */
.obs-photo-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 0.75rem;
  margin: 0.5rem 0 1rem;
}
.obs-photo-tile {
  display: block;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  transition: transform 0.08s, border-color 0.08s;
}
.obs-photo-tile:hover {
  transform: scale(1.02);
  border-color: var(--accent);
}
.obs-photo-tile img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Manual-cert form — new / edit share the same layout. Simple
   fieldsets with a legend so screen readers can group trainee vs
   training sections; visually it's just two padded blocks. */
.cert-manual-form { display: flex; flex-direction: column; gap: 0.6rem; }
.cert-manual-form fieldset {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.8rem 1rem 1rem;
  display: flex; flex-direction: column; gap: 0.6rem;
}
.cert-manual-form legend { font-weight: 600; padding: 0 0.4rem; }
.cert-manual-form .dir-field { gap: 0.25rem; }
.cert-manual-form input[type="text"],
.cert-manual-form input[type="tel"],
.cert-manual-form input[type="email"],
.cert-manual-form input[type="date"] {
  background: var(--card);
  border: 1px solid var(--border-strong);
  border-radius: 5px;
  padding: 0.55rem 0.7rem;
  color: var(--fg);
  font: inherit;
  font-size: 1rem;
}

/* External-cert chip on the /admin/certs list — subtle amber, not
   loud red. It's information, not a warning. */
.badge-external {
  background: var(--warn-soft, #fff3cf);
  color:      var(--warn,      #8a6100);
  font-size:  0.72rem;
  padding:    0.1rem 0.45rem;
  border-radius: 999px;
  vertical-align: middle;
}

/* Resolution note stands out with the same left-rail treatment as
   the description block, so at a glance you can tell what the
   admin wrote from what the reporter wrote. */
.obs-resolution-note {
  white-space: pre-wrap;
  line-height: 1.55;
  background: var(--bg);
  border-left: 3px solid var(--good, #2d5e3e);
  padding: 0.6rem 0.9rem;
  border-radius: 0 4px 4px 0;
}

.obs-detail-shell { max-width: 760px; }
.obs-detail {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 1rem 1.2rem 1.4rem;
  margin: 1rem 0;
}
.obs-detail-grid {
  display: grid;
  grid-template-columns: 130px 1fr;
  gap: 0.4rem 0.85rem;
  margin: 0 0 1.1rem;
}
.obs-detail-grid dt { color: var(--muted); font-size: 0.88rem; }
.obs-detail-grid dd { margin: 0; }
.obs-description {
  white-space: pre-wrap;
  line-height: 1.55;
  background: var(--bg);
  border-left: 3px solid var(--accent);
  padding: 0.85rem 1rem;
  border-radius: 0 4px 4px 0;
}

.obs-status-form {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 1rem 1.2rem;
}
.obs-status-form h2 { margin: 0 0 0.6rem; font-size: 1rem; }

/* ─── Report-a-Problem QR sticker ──────────────────────────────────────
   /admin/report-qr — preview + edit on screen, printable as a sticker.
   The sticker artwork itself is a centered square so the on-screen and
   on-paper layouts match. @media print hides everything but .sticker.
   ───────────────────────────────────────────────────────────────────── */
.report-qr-layout {
  display: grid;
  grid-template-columns: minmax(240px, 320px) 1fr;
  gap: 2rem;
  align-items: start;
}
@media (max-width: 800px) {
  .report-qr-layout { grid-template-columns: 1fr; }
}
.report-qr-edit { display: flex; flex-direction: column; gap: 0.5rem; }
.report-qr-edit h2 { margin: 0; font-size: 1rem; }
.report-qr-download { list-style: none; padding: 0; margin: 0.25rem 0 0; }
.report-qr-download li { padding: 0.15rem 0; }

/* The sticker preview is now just a frame around the composited
   PNG — the banner + URL footer are baked into the image so the
   on-screen view matches what the print shop receives. */
.sticker {
  display: flex;
  justify-content: center;
  width: 100%;
  max-width: 560px;
  margin: 0 auto;
  padding: 1rem;
  background: #ffffff;
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
}
.sticker-png {
  display: block;
  width: 100%;
  height: auto;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
}
.report-qr-color-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.report-qr-color-row input[type="color"] {
  width: 42px; height: 32px; padding: 0;
  border: 1px solid var(--border); border-radius: 4px;
  background: transparent; cursor: pointer;
}
.report-qr-color-row input[type="text"] {
  flex: 1;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
/* Slider rows reuse .dir-field — small inline value chip on the
   right of the label keeps the current % visible while dragging. */
.report-qr-edit .dir-field small {
  float: right;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--fg);
}
.report-qr-edit input[type="range"] {
  width: 100%;
  accent-color: var(--accent);
}

/* Print: hide everything except the sticker, scale it to fill the
   sheet centered. .printing-sticker on <body> is toggled by the
   Print-sticker button so the @media print rules know which surface
   to render. */
.report-qr-page .no-print { /* on-screen: show normally */ }
@media print {
  body.printing-sticker > *:not(main),
  body.printing-sticker main > *:not(.report-qr-page),
  body.printing-sticker .report-qr-page > *:not(.report-qr-layout),
  body.printing-sticker .report-qr-edit,
  body.printing-sticker .no-print {
    display: none !important;
  }
  body.printing-sticker .report-qr-layout {
    display: block !important;
    margin: 0;
  }
  body.printing-sticker .sticker {
    border: none;
    box-shadow: none;
    max-width: 100%;
    margin: 0 auto;
    page-break-inside: avoid;
  }
}

/* ─── Report-form photo picker ────────────────────────────────────────
   Big dashed "+" tile lives inside the same grid as any already-picked
   thumbnails, so removing a photo visually shifts the tile back into
   the row. Native file input is offscreen-positioned (still receives
   the click via <label for=…>). */
.photo-picker {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 0.6rem;
  margin-top: 0.25rem;
}
.photo-add {
  aspect-ratio: 1 / 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.25rem;
  border: 2px dashed var(--border-strong);
  border-radius: 8px;
  background: var(--card);
  color: var(--muted);
  cursor: pointer;
  transition: border-color 0.12s, color 0.12s, background 0.12s;
}
.photo-add:hover,
.photo-add:focus-within {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-soft);
}
.photo-plus {
  font-size: 2.4rem;
  line-height: 1;
  font-weight: 300;
}
.photo-add-label {
  font-size: 0.75rem;
  font-weight: 500;
  text-align: center;
}
.photo-thumb {
  position: relative;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  border-radius: 8px;
  background: var(--bg);
  border: 1px solid var(--border);
}
.photo-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.photo-thumb-remove {
  position: absolute;
  top: 4px;
  right: 4px;
  width: 26px;
  height: 26px;
  border: none;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.65);
  color: white;
  font-size: 1.15rem;
  line-height: 1;
  padding: 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.photo-thumb-remove:hover {
  background: var(--bad, #c0392b);
}

/* ── Crew training-cert roster page (/admin/directory/person/{id}/crew-certs) */
.crew-member { margin: 1.4rem 0; }
.crew-member-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.4rem;
  padding-bottom: 0.3rem;
  border-bottom: 1px solid var(--border);
}
.crew-member-head h2 { font-size: 1rem; margin: 0; }
.crew-member-empty { margin: 0.3rem 0 0; }
.status-expired { color: var(--bad); font-weight: 600; }
.status-valid   { color: var(--good); }
.status-none    { color: var(--muted); }

/* Inline "Crew certs" link in the tree foreman header — pushes to the
   right of the header row; jumps straight to the crew-cert list. */
.dir-crew-link {
  margin-left: auto;
  font-size: 0.78rem;
  white-space: nowrap;
  color: var(--accent);
  text-decoration: none;
}
.dir-crew-link:hover { text-decoration: underline; }

/* "Download all certs" button in the person detail modal. */
.dir-download-certs { display: inline-block; margin-top: 0.6rem; }

/* Backfill pool under each super — workers reporting to the super,
   ready to move to a foreman's crew. A standing drop zone: dropping a
   worker on the super block adds them here. */
.dir-backfill {
  margin-top: 0.6rem;
  padding-top: 0.5rem;
  border-top: 1px dashed var(--border);
}
.dir-role-backfill {
  background: transparent;
  color: var(--muted);
  border: 1px dashed var(--muted);
}

/* External-cert attachments (admin cert edit) + the link on the cert page. */
.cert-attachments { margin-top: 1.5rem; padding-top: 1rem; border-top: 1px solid var(--border); }
.cert-attach-list { list-style: none; padding: 0; margin: 0.5rem 0; }
.cert-attach-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--border);
}
.cert-attach-upload { margin-top: 0.8rem; }
/* "View External Certification" link under the completion line. */
.cert-external { text-align: center; margin: 0.25rem 0 0.75rem; }
.cert-external a { color: var(--accent); font-weight: 600; text-decoration: none; }
.cert-external a:hover { text-decoration: underline; }

/* ── TRIR report ─────────────────────────────────────────────────── */
.trir .viz-card { margin-bottom: 1.25rem; }
.trir-explainer .trir-eq {
  font-size: 1.1rem;
  background: var(--accent-soft);
  color: var(--fg);
  padding: 0.65rem 0.9rem;
  border-radius: 0.5rem;
  margin: 0.4rem 0 0.9rem;
}
.trir-notes { margin: 0; padding-left: 1.1rem; line-height: 1.55; }
.trir-notes li { margin-bottom: 0.35rem; }

.trir-table-wrap { overflow-x: auto; }
.trir-table { width: 100%; border-collapse: collapse; }
.trir-table th, .trir-table td {
  padding: 0.55rem 0.7rem;
  border-bottom: 1px solid var(--border);
  text-align: left;
  white-space: nowrap;
}
.trir-table thead th {
  background: var(--card-alt);
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted);
}
.trir-table td.num, .trir-table th.num { text-align: right; font-variant-numeric: tabular-nums; }
.trir-rate { font-weight: 700; font-size: 1.05rem; font-variant-numeric: tabular-nums; }
.trir-calc { color: var(--muted); font-variant-numeric: tabular-nums; }
.trir-partial {
  display: inline-block;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: var(--warn-soft);
  color: var(--warn);
  padding: 0.08rem 0.4rem;
  border-radius: 0.35rem;
  margin-left: 0.4rem;
  vertical-align: middle;
}
.trir-kind {
  display: inline-block;
  font-size: 0.72rem;
  background: var(--accent-soft);
  color: var(--accent);
  padding: 0.08rem 0.45rem;
  border-radius: 0.35rem;
}
.trir-chart-wrap { position: relative; height: 320px; margin-top: 1.25rem; }
.trir-remove { color: var(--bad); }

.trir-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 0.85rem 1rem;
  align-items: start;
}
.trir-form label { display: flex; flex-direction: column; gap: 0.3rem; font-size: 0.88rem; }
.trir-form input {
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--border-strong);
  border-radius: 0.45rem;
  background: var(--card);
  color: var(--fg);
  font: inherit;
}
.trir-form input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
.trir-note-field { grid-column: 1 / -1; }
.trir-field-error { color: var(--bad); font-size: 0.8rem; }
.trir-actions { grid-column: 1 / -1; }

/* The print header is hidden on screen; screen-only chrome is hidden
   in print (below). */
.trir-print-only { display: none; }

@media print {
  body.trir-page .site-header,
  body.trir-page .trir-screen-only { display: none !important; }
  body.trir-page .trir-print-only { display: block; }
  body.trir-page .container { max-width: none; padding: 0; }
  body.trir-page .viz-card {
    box-shadow: none;
    border: 1px solid #ccc;
    break-inside: avoid;
  }
}

/* ── Directory: multi-division supers ────────────────────────────── */
/* "also here" badge on a super whose home division is elsewhere. */
.dir-extra-badge {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: var(--accent-soft);
  color: var(--accent);
  padding: 0.08rem 0.45rem;
  border-radius: 0.35rem;
  margin-left: 0.35rem;
  vertical-align: middle;
}
/* Divisions section in the detail modal. */
.dir-divisions {
  margin: 1rem 0;
  padding-top: 0.75rem;
  border-top: 1px solid var(--border);
}
.dir-divisions h4 { margin: 0 0 0.25rem; }
.dir-division-list { list-style: none; padding: 0; margin: 0.5rem 0; }
.dir-division-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.3rem 0;
  border-bottom: 1px solid var(--border);
}
.dir-division-tag {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.dir-division-remove { color: var(--bad); margin-left: auto; }
.dir-division-add {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  margin-top: 0.5rem;
}
.dir-division-add select { flex: 1; }

/* Inline rename (double-click a division tab / bucket chip). */
.dir-tab-wrap, .dir-bucket-wrap { display: inline-flex; }
.dir-inline-edit {
  display: inline-flex;
  align-items: center;
  gap: 0.1rem;
  background: var(--card);
  border: 1px solid var(--accent);
  border-radius: 0.45rem;
  padding: 0.1rem 0.25rem;
}
.dir-inline-input {
  border: none;
  background: transparent;
  color: var(--fg);
  font: inherit;
  font-size: 0.9rem;
  width: 8.5rem;
  padding: 0.15rem 0.25rem;
  outline: none;
}
.dir-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  padding: 0.2rem;
  border-radius: 0.3rem;
  line-height: 0;
}
.dir-icon-btn:hover { background: var(--card-alt); color: var(--fg); }
.dir-icon-ok:hover { color: var(--accent); }
.dir-icon-danger:hover { color: var(--bad); }
.dir-icon { width: 16px; height: 16px; display: block; }

/* Outline companion to .btn-primary, for the secondary action in a pair.
   Two solid accent blocks side by side read as equally important and make
   the header feel heavy; this keeps the same footprint with less weight. */
.btn-outline {
  display: inline-block;
  padding: 0.5rem 1rem;
  background: transparent;
  color: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 4px;
  font: inherit;
  cursor: pointer;
}
.btn-outline:hover { background: var(--accent-soft); }
.btn-outline:disabled { opacity: 0.6; cursor: not-allowed; }

/* The header action pair must never wrap mid-label. Without these the flex
   row squeezed "+ Add house" into three stacked lines. */
.housing-header-actions { flex: 0 0 auto; }
.housing-header-actions > button { white-space: nowrap; }

/* ─── Collapsible page chrome (/admin/housing) ─────────────────────────
   Title, help text and the jobsite tab strip fold up out of the way so the
   canvas can take the whole window.

   Collapsed uses display:none rather than height:0 + overflow:hidden. The
   canvas height is MEASURED from this element's bottom edge, so a zero-
   height-but-still-in-flow block would leave the canvas short by the
   margins around it. Losing the slide animation is worth an exact fit. */
.housing-chrome.is-collapsed { display: none; }

/* The collapse control sits with the header actions, not floating over the
   page — it belongs to the panel it hides. */
.housing-chrome-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 4px;
  cursor: pointer;
  line-height: 0;
}
.housing-chrome-toggle:hover { color: var(--accent); border-color: var(--accent); }
.housing-chrome-toggle svg { width: 18px; height: 18px; display: block; }

/* Collapsed, the canvas should run flush to the site nav. base.html's
   .container adds 2rem of top padding, which the section can't see — so
   pull the whole section up by exactly that much. Nothing else on the page
   sets the value, so this is the only place the two need to agree.

   Default: claw back most of that 2rem, leaving ~0.5rem so the title
   still has a little air under the nav. This page is a workspace, not a
   document — vertical space spent above the board is space the board
   doesn't get, and fitWorkspaceHeight hands whatever is saved here
   straight to the canvas. */
.housing { position: relative; margin-top: -1.5rem; }
/* Collapsed: cancel the padding entirely and sit flush. */
.housing.chrome-hidden { margin-top: -2rem; }
.housing.chrome-hidden .housing-workspace { margin-top: 0; }

/* Pull-down handle for the hidden panel.
   ABSOLUTE, not a zero-height flex row: a flex container with height:0
   stretches its child to zero too, which is what left the button visibly
   sliced in half. Absolute positioning takes it out of flow entirely, so
   it costs no height and cannot be squashed — it simply overlays the top
   edge of the canvas, which is what makes it read as a tab hanging from
   the hidden panel. */
.housing-chrome-handle {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  /* Above every layer the workspace composites (.housing-ctx is 8, the
     popouts 5, the canvas world auto). */
  z-index: 30;
  /* Safari: the canvas world (.hc-world) and the popouts are GPU-promoted
     (will-change + live transforms). Safari's compositor has a long-standing
     habit of painting promoted layers OVER a non-promoted absolute overlay
     that beats them on z-index — the handle simply doesn't get drawn.
     Promoting the handle onto its own layer sidesteps the bug; a no-op
     everywhere else. */
  transform: translateZ(0);
  display: flex;
  justify-content: center;
  /* The row spans the full width; only the button itself is clickable, so
     the canvas underneath stays draggable right up to the tab. */
  pointer-events: none;
}
.housing-chrome-show {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.3rem 1rem;
  background: var(--accent);
  color: var(--accent-fg);
  border: 0;
  border-radius: 0 0 8px 8px;
  font: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.18);
}
.housing-chrome-show:hover { background: var(--accent-hover); }
.housing-chrome-show svg { width: 15px; height: 15px; display: block; }

/* The tab-strip "+" now carries a label, so it can't stay square. */
.dir-tab-add { white-space: nowrap; }

/* ─── Housing canvas workspace (integrator glue) ───────────────────────
   Wraps the canvas and the popout overlay layer. position:relative is
   load-bearing — housing_popouts.js positions its panels absolutely
   against this box, and a static ancestor would anchor them to the
   viewport instead. The canvas supplies its own min-height. */
/* Height is set inline by housing.js (fitWorkspaceHeight) to fill down to
   near the bottom of the viewport. It cannot be pure CSS: the amount of
   page above the canvas changes as the help paragraph and the tab strip
   wrap, so any calc(100vh - <constant>) is wrong at some width. These two
   values are the floor and the pre-JS fallback. */
.housing-workspace {
  position: relative;
  margin-top: 1rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
  height: 68vh;
  min-height: 20rem;
}
/* The canvas fills the workspace rather than imposing its own fixed
   420px — that min-height is the module's standalone default and would
   otherwise pin the viewport short. */
.housing-canvas-host,
.housing-workspace .hc-viewport {
  height: 100%;
  min-height: 0;
}
.housing-canvas-host { display: block; }

/* Right-click menu on the canvas. Absolute inside .housing-workspace, which
   is the positioned ancestor, so the coordinates are workspace-relative. */
.housing-ctx {
  position: absolute;
  z-index: 8;
  min-width: 10rem;
  padding: 0.25rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.18);
}
.housing-ctx button {
  display: block;
  width: 100%;
  text-align: left;
  padding: 0.4rem 0.6rem;
  border: 0;
  border-radius: 4px;
  background: none;
  color: var(--fg);
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
  white-space: nowrap;
}
.housing-ctx button:hover { background: var(--accent-soft); color: var(--accent); }
/* The overlay layer must not intercept clicks meant for the canvas; the
   panels inside it re-enable pointer events on themselves. */
.housing-popout-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.housing-popout-layer > * { pointer-events: auto; }

/* ─── Housing / mobile-home placement (/admin/housing) ─────────────────
   The old three-column board (people roster | houses grid | occupancy
   roll-up) was replaced by the pannable canvas above (see "Housing canvas
   workspace" and the .hc-* rules in housing_canvas.css). Only the pieces
   still rendered by admin_housing.html survive here: the held-person
   banner, the empty-jobsite state, and the post-action notice line. */
.btn-small { padding: 0.3rem 0.6rem; font-size: 0.82rem; }

/* ── Held-person banner ──────────────────────────────────────────────
   Click-to-place needs a persistent "who's in hand" indicator, or a
   stray room click moves the wrong person. */
.housing-held {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  background: var(--accent);
  color: var(--accent-fg);
  border-radius: 4px;
  padding: 0.5rem 0.8rem;
  margin-top: 0.6rem;
  font-size: 0.9rem;
}
.housing-held .muted { color: var(--accent-fg); opacity: 0.75; }
.housing-held .link { color: var(--accent-fg); text-decoration: underline; }

.housing-empty {
  background: var(--card);
  border: 1px dashed var(--border);
  border-radius: 6px;
  padding: 2rem;
  margin-top: 1rem;
  text-align: center;
}
.housing-empty h2 { margin: 0 0 0.4rem; }
.housing-empty p { max-width: 42ch; margin: 0 auto 1rem; }

/* Result line after a destructive action (jobsite deleted). Uses
   the accent-soft tint rather than --good so it reads as information
   rather than celebration — a delete isn't good news. */
.housing-notice {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  background: var(--accent-soft);
  border: 1px solid var(--accent);
  border-radius: 4px;
  padding: 0.5rem 0.7rem;
  font-size: 0.9rem;
  margin: 0.6rem 0 0;
}


/* ── Safest Employee of the Month (/admin/safest-employee) ─────────
   Form on the left, live slide preview on the right. The preview is a
   facsimile of the TV board's spotlight screen and deliberately keeps
   the board's FIXED light palette in both app themes — it previews
   the wall, not this app. */
.eom-layout {
  display: grid;
  grid-template-columns: minmax(300px, 420px) 1fr;
  gap: 1.5rem;
  align-items: start;
}
@media (max-width: 900px) {
  .eom-layout { grid-template-columns: 1fr; }
}

.eom-flash {
  border-radius: 4px;
  padding: 0.5rem 0.7rem;
  font-size: 0.9rem;
  margin: 0.6rem 0;
  border: 1px solid transparent;
}
.eom-flash-good { background: var(--good-soft); color: var(--fg); border-color: var(--good); }
.eom-flash-warn { background: var(--warn-soft); color: var(--fg); border-color: var(--warn); }
.eom-flash-bad  { background: var(--bad-soft);  color: var(--fg); border-color: var(--bad); }

.eom-form {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 1rem 1.2rem 1.2rem;
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}

/* Autocomplete dropdown hangs off the name field. */
.eom-name-field { position: relative; }
.eom-suggest {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 20;
  margin: 2px 0 0;
  padding: 0.25rem;
  list-style: none;
  background: var(--card);
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.15);
  max-height: 16rem;
  overflow-y: auto;
}
.eom-suggest button {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.1rem;
  width: 100%;
  padding: 0.4rem 0.55rem;
  border: 0;
  border-radius: 4px;
  background: none;
  color: var(--fg);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.eom-suggest button:hover,
.eom-suggest button:focus-visible { background: var(--accent-soft); }

/* The preview slide. Fixed light palette on purpose (see above). */
.eom-preview-wrap { min-width: 0; }
.eom-preview {
  display: flex;
  align-items: center;
  gap: 2rem;
  background: #f5f5f4;
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 2rem;
  min-height: 18rem;
}
.eom-preview-portrait {
  flex: none;
  width: 11rem;
  height: 11rem;
  border-radius: 50%;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  /* Same gradient family as the board's initials fallback. */
  background: linear-gradient(135deg, #2d5e3e, #1f4e79);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
}
.eom-preview-portrait img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.eom-preview-initials {
  font-size: 3.6rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: #ffffff;
}
.eom-preview-text { min-width: 0; flex: 1; }
.eom-preview-period {
  margin: 0;
  font-size: 0.78rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: #a3a3a3;
}
.eom-preview-name {
  margin: 0.25rem 0 0;
  font-size: 2.2rem;
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: #171717;
}
.eom-preview-subtitle {
  margin: 0.5rem 0 0;
  font-size: 1rem;
  color: #737373;
}
.eom-preview-citation {
  margin-top: 1rem;
  background: #ffffff;
  border-radius: 10px;
  padding: 0.9rem 1.1rem;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.eom-preview-citation p {
  margin: 0;
  font-size: 0.92rem;
  line-height: 1.55;
  color: #404040;
  overflow-wrap: break-word;
}

.eom-history-h { margin-top: 2rem; }
.eom-badge {
  display: inline-block;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  font-size: 0.75rem;
  background: var(--card-alt);
  color: var(--muted);
  border: 1px solid var(--border);
}
.eom-badge-live {
  background: var(--good-soft);
  color: var(--fg);
  border-color: var(--good);
}

/* Crop editor state on the preview portrait. position:relative anchors
   the absolutely-positioned crop image; touch-action:none keeps mobile
   drags panning the photo instead of scrolling the page. */
.eom-preview-portrait.is-cropping {
  cursor: grab;
  touch-action: none;
}
.eom-preview-portrait.is-cropping:active { cursor: grabbing; }
.eom-crop-img {
  position: absolute;
  max-width: none;      /* inline width/height are the geometry — never shrink-to-fit */
  user-select: none;
  pointer-events: none; /* the circle handles the pointer; the img just renders */
}
.eom-crop-controls {
  margin-top: 0.6rem;
  width: 11rem;         /* match the portrait diameter */
}
.eom-crop-controls label { display: block; }
.eom-crop-controls input[type="range"] { width: 100%; }
.eom-crop-controls p { margin: 0.2rem 0 0; }
.eom-crop-hint { margin: 0 0 0.4rem; line-height: 1.4; }

/* Publish-state line under the save/publish buttons. */
.eom-status {
  margin: 0.2rem 0 0;
  font-size: 0.88rem;
  border-left: 3px solid var(--border-strong);
  padding-left: 0.6rem;
  color: var(--muted);
}
.eom-status-draft { border-left-color: var(--warn); }
.eom-status-live  { border-left-color: var(--good); }
.eom-photo-note { margin: -0.4rem 0 0; }


/* ── Safety Metrics hub (/admin/safety-metrics) ─────────────────────
   The settings-gear directory for the board's curated screens. Two
   cards today; the layout auto-fits rather than hardcoding two, since
   the whole reason this page exists is that the count grew from one. */
.sm-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
  gap: 1rem;
  margin-top: 1rem;
}
.sm-card {
  display: block;
  background: var(--card);
  border: 1px solid var(--border);
  border-left: 3px solid var(--border-strong);
  border-radius: 8px;
  padding: 1rem 1.2rem 1.1rem;
  color: inherit;
  text-decoration: none;
}
.sm-card:hover,
.sm-card:focus-visible {
  border-color: var(--accent);
  background: var(--card-alt);
}
/* A published screen is rail-marked green — same vocabulary the
   directory uses for new hires and the status lines use for live. */
.sm-card.is-live { border-left-color: var(--good); }
.sm-card h2 { margin: 0; font-size: 1.1rem; }
.sm-card-blurb { margin: 0.35rem 0 0.7rem; font-size: 0.88rem; line-height: 1.45; }
.sm-card-status {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.45rem;
  margin: 0;
  font-size: 0.88rem;
}
.sm-card-status.is-broken { color: var(--bad); }
.sm-hub-foot { margin-top: 1.4rem; max-width: 46rem; line-height: 1.5; }


/* ── Industry Recognition designer (/admin/industry-recognition) ────
   Two-column PAGE: form left, preview + soft-check panel right. The
   preview itself is a to-scale 16:9 stage — see .ir-stage below. */
.ir-layout {
  display: grid;
  grid-template-columns: minmax(320px, 460px) 1fr;
  gap: 1.5rem;
  align-items: start;
}
@media (max-width: 960px) {
  .ir-layout { grid-template-columns: 1fr; }
}

.ir-form {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 1rem 1.2rem 1.2rem;
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}

/* A toggleable part: the checkbox owns the fields beneath it, so
   switching it off dims them in place rather than removing them. Kept
   visible-but-dimmed on purpose — the text is still there and still
   saved, which is the whole reason the toggles are separate columns. */
.ir-part {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.7rem 0.8rem 0.8rem;
  background: var(--card-alt);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.ir-part.is-off > .dir-field,
.ir-part.is-off > p { opacity: 0.5; }
.ir-toggle {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.9rem;
  font-weight: 500;
  cursor: pointer;
}
.ir-toggle input { margin: 0; }

/* "Who's recognising us" groups the logo and its fallback text as one
   decision, in priority order. Borderless legend + a hairline rule reads as
   a heading rather than a box inside a box — the parts inside already have
   their own frames. */
.ir-group {
  border: 0;
  border-top: 1px solid var(--border);
  margin: 0.3rem 0 0;
  padding: 0.8rem 0 0;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}
.ir-group > legend {
  padding: 0 0.4rem 0 0;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--fg);
}

/* Logo size slider. Full-width range with the readout in the label row, so
   the number is next to the words rather than under the control. */
.ir-scale-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}
.ir-scale-row input[type="range"] { flex: 1; min-width: 0; }
.ir-scale-readout {
  float: right;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}

.ir-hint {
  font-style: normal;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
}

.ir-req {
  font-style: normal;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--warn);
}

/* Live character counter. Amber past the advisory limit (too long to
   read across a room), red past the schema's hard cap. */
.ir-count { float: right; font-variant-numeric: tabular-nums; }
.ir-count.is-long { color: var(--warn); }
.ir-count.is-over { color: var(--bad); font-weight: 600; }

/* "On the TVs now" bar. Stated above the form and independent of which
   slide is open, because "is the wall correct?" is the question. */
.ir-livebar {
  margin: 0.6rem 0 1rem;
  padding: 0.55rem 0.8rem;
  border-radius: 6px;
  font-size: 0.9rem;
  line-height: 1.45;
  border: 1px solid var(--border);
  border-left-width: 3px;
}
.ir-livebar-on  { border-left-color: var(--good); background: var(--good-soft); }
.ir-livebar-off { border-left-color: var(--warn); background: var(--warn-soft); }
.ir-livebar a { margin-left: 0.4rem; }

.ir-side { min-width: 0; display: flex; flex-direction: column; gap: 0.6rem; }

/* ── The preview stage ──────────────────────────────────────────────
   A TRUE-TO-SCALE facsimile of the board's recognition screen, not an
   approximation. `container-type: size` plus a 16:9 `aspect-ratio` makes
   this a container with a definite width AND height, which means every
   size inside can be written in `cqw`/`cqh` — and those are LITERALLY the
   numbers from safety-metrics' partials/recognition.html, reading its
   `vw` as `cqw` and its `vh` as `cqh`.

   That one-to-one mapping is the point. Hand-tuned "looks about right"
   proportions drift the first time either side is touched, and silently:
   nothing fails, the preview just stops telling the truth. With the units
   aligned, keeping fidelity is copying numbers across, and a diff on one
   side is obviously a diff owed to the other.

   Reconciled against that file on 2026-08-13, when the board side reported
   three drifts back: the card is a FIXED 77.2vh with justify-center (here it
   was content-sized), its padding is py-[2vh] (here it said 3.5vh), and an
   absent period hides the whole footer. All three matched now — and the
   exercise is itself the argument for the convention, because the drift was
   found by COMPARING NUMBERS, which only works because they are the same
   numbers on both sides.

   Fixed light palette (the board's own `board-surface` gradient and brand
   colours, inlined): this previews the wall, so it must NOT follow the
   app's dark theme.

   Requires container query units — Chrome 105+, Firefox 110+, Safari 16+.
   Fine for an internal admin page; if it ever has to degrade, the whole
   stage falls back to unstyled-but-readable rather than broken. */
.ir-stage {
  container-type: size;
  aspect-ratio: 16 / 9;
  width: 100%;
  display: flex;
  flex-direction: column;
  /* The board's slide padding: px-[3vw] py-[2.5vh]. */
  padding: 2.5cqh 3cqw;
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  background: linear-gradient(135deg, #fbfdfb 0%, #f4faf0 45%, #f0f8fb 100%);
}

/* Board chrome. The title is one of THIS form's fields, so it has to be
   previewable; the wordmark is here because its absence is more noticeable
   than its presence — a preview missing it reads as a different screen. */
.ir-stage-head {
  display: flex;
  flex-shrink: 0;
  align-items: baseline;
  justify-content: space-between;
  gap: 2cqw;
}
.ir-stage-title {
  margin: 0;
  font-size: 3.3cqw;            /* board: text-[3.3vw] font-semibold */
  font-weight: 600;
  letter-spacing: -0.02em;
  color: #171717;
  overflow-wrap: anywhere;
}
.ir-stage-brand {
  height: 3.1cqw;
  width: auto;
  flex: none;
}

.ir-stage-main {
  flex: 1;
  display: flex;
  align-items: center;
  min-height: 0;
}

/* The white accent card — the board's `card-accent-top` idiom, shared with
   its hero card and the spotlight's citation, which is what makes this
   screen read as part of the same system.

   ── FIXED HEIGHT, NOT CONTENT-SIZED (corrected 2026-08-13) ──────────
   77.2cqh is the board's 77.2vh, which is itself 7 × 10vh + 6 × 1.2vh —
   a row-and-gap rhythm it shares with the Safety Walks roster card, so
   the two read as the same system rather than as two cards that happen
   to be similar. Do not "simplify" it to a round number.

   This preview was content-sized until the board-side restyle was
   reported back, and that difference is worse than it sounds: a
   content-sized card is ACCURATE ONLY FOR THE COPY THAT HAPPENS TO
   FILL IT. Short copy showed a snug card here and a tall one with the
   content floating in the middle on the wall, and — the expensive
   direction — copy that OVERFLOWS the real card looked perfectly fine
   here, because the preview simply grew. With the height pinned, an
   over-full slide overflows visibly in the preview too, which is the
   only way the vertical budget can be judged from a desk.

   justify-content: center matches the board's justify-center: content
   sits in the middle of the fixed height, not at the top. */
.ir-card {
  position: relative;
  overflow: hidden;             /* clips the accent strip to the radius */
  width: 100%;
  height: 77.2cqh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  border-radius: 1.2cqw;
  background: #ffffff;
  padding: 2cqh 5cqw;
  text-align: center;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
/* ::before rather than border-top, for the reason the board's CSS gives: a
   border sits inside the radius and leaves the gradient pinched at both
   corners. */
.ir-card::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 0.6cqh;
  background: linear-gradient(90deg, #58d100, #008bff);
}

/* The awarding-body line: logo, or the text when there is no logo. The
   container keeps the logo's height either way, so switching between them
   doesn't move the headline. */
.ir-card-brand {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 6.4cqw;
}

/* HEIGHT IS THE CONSTRAINT, WIDTH IS FREE — board: h-[6.4vw] w-auto
   max-w-[46vw] object-contain. The stored asset is already trimmed and
   normalised to a standard height server-side (internal/logoimg), so this
   only has to avoid fighting it.

   NO checkerboard behind it, deliberately, even though a transparent PNG is
   the expected input: the wall's card is white, so showing one here on
   anything else would hide the single most consequential logo problem —
   white-on-transparent artwork, which vanishes on white. The upload warns
   about it, and this is where it is meant to be SEEN. */
.ir-card-logo {
  /* 6.4cqw is the board's own h-[6.4vw], and logo_scale MULTIPLIES it rather
     than replacing it — confirmed against the board on 2026-08-13, which
     applies calc(6.4vw × logo_scale) the same way. The custom property
     defaults to 1 so the rule still reads correctly with no Alpine at all. */
  height: calc(6.4cqw * var(--ir-logo-scale, 1));
  width: auto;
  max-width: 46cqw;
  object-fit: contain;
}
.ir-card-eyebrow {
  margin: 0;
  font-size: 2.35cqw;           /* board: text-[2.35vw] */
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: #a3a3a3;               /* neutral-400 */
  overflow-wrap: break-word;
}
.ir-card-headline {
  margin: 2.4cqh 0 0;
  font-size: 6.5cqw;            /* board: text-[6.5vw] font-bold */
  font-weight: 700;
  line-height: 1.05;            /* load-bearing at this size — see the board */
  letter-spacing: -0.02em;
  color: #171717;               /* neutral-900 */
  overflow-wrap: break-word;
}
.ir-card-body {
  margin: 2cqh 0 0;
  font-size: 2.9cqw;            /* board: text-[2.9vw] */
  line-height: 1.625;           /* leading-relaxed */
  color: #525252;               /* neutral-600 */
  overflow-wrap: break-word;
}
/* w-fit + auto margins so the rule is as wide as the text and no wider: a
   full-width border across a centred card reads as a section divider rather
   than a footnote. The board's comment makes the same point.

   The whole footer — RULE INCLUDED — is hidden when there is neither a period
   nor an attribution. With the period optional as of 2026-08-13 that is a
   reachable state, and a border with nothing under it is worse than no
   footer. The board does the same. */
.ir-card-foot {
  display: flex;
  width: fit-content;
  margin: 2.8cqh auto 0;
  align-items: baseline;
  justify-content: center;
  gap: 1.7cqw;
  border-top: 1px solid #e5e5e5;
  padding-top: 1.6cqh;
  font-size: 2.15cqw;           /* board: text-[2.15vw] */
  color: #737373;               /* neutral-500 */
}
.ir-card-period { font-weight: 600; color: #525252; }
.ir-card-attr { overflow-wrap: break-word; }

/* Soft-check panel. Advisory only — never disables anything. */
.ir-checks {
  background: var(--card);
  border: 1px solid var(--border);
  border-left: 3px solid var(--warn);
  border-radius: 8px;
  padding: 0.8rem 1rem 0.9rem;
}
.ir-checks h3 { margin: 0; font-size: 0.95rem; }
.ir-checks > p { margin: 0.25rem 0 0.6rem; }
.ir-checks ul { margin: 0; padding-left: 1.1rem; }
.ir-checks li {
  font-size: 0.88rem;
  line-height: 1.45;
  margin-bottom: 0.4rem;
}
.ir-checks li button { margin-left: 0.4rem; white-space: nowrap; }
/* Warnings read as warnings; notes are quieter house-style nits. */
.ir-checks li.is-warn::marker { color: var(--warn); }
.ir-checks li.is-note { color: var(--muted); }
.ir-checks li.is-note::marker { color: var(--border-strong); }
.ir-checks-clear { margin: 0; }

/* The live row in the history table, and its delete affordance. */
.ir-row-live > td { background: var(--good-soft); }
.ir-del { color: var(--bad); }

/* Draft-restored notice on the field forms (form_draft.js). Shown only
   when a draft actually refilled something, so an untouched form stays
   silent. Informational, not an error — it is good news. */
.report-restored {
  margin: 0 0 1rem;
  padding: .6rem .8rem;
  border: 1px solid var(--accent);
  border-left-width: 4px;
  border-radius: 8px;
  background: var(--surface-2, transparent);
  color: var(--fg);
  font-size: .9rem;
  line-height: 1.4;
}

/* Pre-flight message: shown when the client-side required-field check
   blocks a submit, so a worker never uploads photos only to be bounced
   by the server for a field they could have filled in first. */
.report-preflight { margin: .4rem 0 .8rem; font-weight: 600; }

/* Offline queue surfaces (queue_ui.js).

   The pill is the worker's only standing signal that something is still
   waiting to leave the phone, so it is fixed and stays put while they
   scroll. The "saved" notice is deliberately worded as saved-on-device
   rather than sent: claiming delivery we haven't achieved is how a
   hazard report quietly goes missing. */
.queue-pill {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 40;
  margin: 0;
  padding: .5rem .9rem;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-size: .85rem;
  font-weight: 600;
  box-shadow: 0 2px 10px rgb(0 0 0 / .25);
  border: 0;
  font-family: inherit;
  cursor: pointer;
}
.queue-pill[hidden] { display: none; }
/* Something needs a person — not merely waiting for signal. */
.queue-pill-warn { background: var(--warn); color: #1a1a1a; }
.queue-warn { border-color: var(--warn); background: var(--warn-soft); }

/* The queue drawer: a bottom sheet listing what's saved on this phone.
   A native <dialog> where supported; otherwise a fixed <section> with
   the same class (queue_ui.js). */
.queue-drawer {
  position: fixed;
  inset: auto 0 0 0;
  margin: 0 auto;
  width: min(100%, 40rem);
  max-height: 85vh;
  overflow-y: auto;
  padding: 1rem 1rem 1.5rem;
  border: 1px solid var(--border);
  border-radius: 14px 14px 0 0;
  background: var(--card);
  color: var(--fg);
  box-shadow: 0 -6px 24px rgb(0 0 0 / .25);
  z-index: 50;
}
section.queue-drawer[hidden] { display: none; }
.queue-drawer::backdrop { background: rgb(0 0 0 / .35); }
.queue-drawer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: .75rem;
}
.queue-drawer-head h2 { margin: 0; font-size: 1.1rem; }
.queue-card {
  border: 1px solid var(--border);
  border-left: 4px solid var(--accent);
  border-radius: 10px;
  padding: .75rem .9rem;
  margin-bottom: .75rem;
  background: var(--bg);
}
.queue-card-attention,
.queue-card-held,
.queue-card-blocked,
.queue-card-notice { border-left-color: var(--warn); }
.queue-card-title { margin: 0 0 .15rem; font-size: 1rem; }
.queue-card-meta { margin: 0 0 .4rem; font-size: .85rem; }
.queue-card-state { margin: .25rem 0; }
.queue-card-errors { margin: .25rem 0 .5rem 1.1rem; color: var(--bad); }
.queue-card-photo {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
  margin: .35rem 0;
}
.queue-card-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .6rem;
  margin-top: .5rem;
}
.queue-card-actions form { margin: 0; }
.queue-discard { margin-left: auto; color: var(--bad); }
.queue-saved {
  margin: 0 0 1rem;
  padding: .7rem .9rem;
  border: 1px solid var(--accent);
  border-left-width: 4px;
  border-radius: 8px;
  font-weight: 600;
  line-height: 1.4;
}

/* Session number clash on the trainer roster (ruling 52). */
.roster-clash { display: flex; align-items: center; gap: .5rem; margin: .5rem 0 0; padding: .4rem .6rem;
  border-left: 3px solid var(--warn); background: var(--warn-soft); color: var(--fg); font-size: .9rem; }
.roster-clash-dot { width: .6rem; height: .6rem; border-radius: 50%; background: var(--warn); flex: none; }
/* Sign-in identity check that isn't verified / new_phone (spec §7.4). */
.roster-id-flag { display: inline-flex; align-items: center; gap: .35rem; margin-left: .4rem;
  font-size: .8rem; color: var(--warn); white-space: nowrap; }
.roster-id-dot { width: .55rem; height: .55rem; border-radius: 50%; background: var(--warn); flex: none; }
.roster-id-flag--neutral { color: var(--muted); }

/* --- Equipment Models (gear, migration 0059) ----------------------------
   One card per machine category: makes as rows, models as removable chips,
   an add form, then the machine → category mapping table. */
.equip-page { max-width: 900px; }
.equip-cat,
.equip-machines {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 1rem 1.1rem;
  margin-bottom: 1rem;
  scroll-margin-top: 1rem;
}
.equip-cat-head h2 { margin: 0; font-size: 1.1rem; }
.equip-cat-head p { margin: 0.15rem 0 0.6rem; font-size: 0.88rem; }
.equip-make {
  display: grid;
  grid-template-columns: 8rem minmax(0, 1fr);
  gap: 0.5rem;
  align-items: baseline;
  padding: 0.45rem 0;
  border-top: 1px solid var(--border);
}
.equip-make-name { font-weight: 700; font-size: 0.82rem; letter-spacing: 0.04em; text-transform: uppercase; }
.equip-models { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.equip-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.15rem;
  margin: 0;
  font: 600 0.82rem ui-monospace, Menlo, monospace;
  background: var(--card-alt);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 0.1rem 0.15rem 0.1rem 0.45rem;
}
.equip-chip-x {
  border: 0;
  background: none;
  color: var(--muted);
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  padding: 0 0.25rem;
  border-radius: 3px;
}
.equip-chip-x:hover { color: var(--bad); background: var(--bad-soft); }
.equip-add {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: flex-end;
  margin-top: 0.75rem;
  padding-top: 0.75rem;
  border-top: 1px dashed var(--border);
}
.equip-add label { display: grid; gap: 0.2rem; font-size: 0.82rem; color: var(--muted); }
.equip-add input { width: 10rem; }
.equip-machines h2 { margin: 0; font-size: 1.1rem; }
.equip-machine-table { width: 100%; border-collapse: collapse; margin-top: 0.5rem; }
.equip-machine-table th,
.equip-machine-table td { text-align: left; padding: 0.45rem 0.4rem; border-top: 1px solid var(--border); font-weight: 400; }
.equip-machine-table th { font-weight: 600; }
.equip-unmapped {
  display: inline-block;
  margin-left: 0.4rem;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--warn);
  background: var(--warn-soft);
  border-radius: 4px;
  padding: 0 0.35rem;
}
.equip-map { display: flex; gap: 0.4rem; align-items: center; justify-content: flex-end; flex-wrap: wrap; }
@media (max-width: 560px) {
  .equip-make { grid-template-columns: minmax(0, 1fr); gap: 0.25rem; }
  .equip-map { justify-content: flex-start; }
}

/* Per-row quick status select on /admin/observations — styled like the
   status badge it replaces, so the column reads the same at a glance. */
.obs-status-quick { margin: 0; }
.obs-status-select {
  font: inherit;
  font-size: 0.78rem;
  font-weight: 600;
  padding: 0.15rem 0.4rem;
  border: 1px solid transparent;
  border-radius: 10px;
  cursor: pointer;
}
.obs-status-select:hover, .obs-status-select:focus-visible { border-color: currentColor; }
.obs-status-select option { background: var(--card); color: var(--fg); }
