/* CrewDB v4 — the app's one stylesheet.
 *
 * The LOOK is dtsw's: light page, dark toolbar, tab bar with dropdown groups,
 * seg-pills, multiselect panels, chips. The MECHANISM is dtss's: one token
 * palette that drives both this file and the AG Grid theme, with a second
 * palette behind `data-theme="dark"`.
 *
 * ONE PALETTE. grid.js hands AG Grid `var(--token)` references into this file,
 * so flipping the theme repaints the grid with the page and no grid has to be
 * rebuilt or even told. Do not put a raw hex anywhere but the two blocks below.
 *
 * Day is the default here, which is the opposite of dtss — this is an office
 * tool on a desk, not a laptop on a truck at a 4am call.
 */

/* The browser hides [hidden] with `display:none` from its own stylesheet, and
   ANY explicit display in this file outranks that. This has to come first and
   carry !important, or every `el.hidden = true` in the app silently does
   nothing. dtss learned this with a topbar that would not hide; jsdom cannot
   catch it, because jsdom does not do the cascade. */
[hidden] { display: none !important; }

/* ============================================================
   THE PALETTE — light (the default, dtsw's look)
   ============================================================ */
:root {
  color-scheme: light;

  --bg: #f5f6f8;
  --card: #ffffff;
  --card-2: #f4f6f9;
  --card-3: #eef0f3;
  --ink: #1f2430;
  --muted: #5b6472;
  --muted-2: #6b7280;
  --dim: #9ca3af;
  --line: #d7dbe0;
  --line-strong: #c3c9d1;
  --hover: #f3f4f6;

  --accent: #4b7bec;
  --accent-hover: #3a67d6;
  --accent-dim: rgba(75, 123, 236, 0.06);
  --accent-ink: #16305e;
  --accent-line: #b3c7e4;

  /* The toolbar is dark in BOTH themes. It is the app's mark and the one
     constant strip on the screen; inverting it in day mode was dtsw's whole
     look, and inverting it back in night mode would leave the page with no
     edge at the top. */
  --toolbar-bg: #1f2430;
  --toolbar-fg: #f5f6f8;
  --toolbar-muted: #9ca3af;
  --toolbar-hover-fg: #d1d5db;
  --toolbar-line: #11141b;
  --toolbar-control: #2c3240;
  --toolbar-control-line: #3b4252;
  --toolbar-control-hover: #3a4252;

  --btn-bg: #2c3240;
  --btn-fg: #ffffff;
  --btn-hover: #3a4252;

  --ok: #15803d;
  --on-ok: #ffffff;
  --ok-dim: #e7f6ec;
  --ok-ink: #186a3b;
  --warn: #a15c07;
  --warn-dim: #fdf4e3;
  --warn-ink: #7d6608;
  --danger: #b23a3a;
  --danger-hover: #c94848;
  --danger-dim: #fdecec;
  --danger-ink: #922b21;

  --seg-bg: #eef0f3;
  --seg-line: #e0e3e8;
  --seg-ink: #5a6b8a;
  --seg-active-bg: #2c3240;
  --seg-active-ink: #ffffff;

  --chip-bg: #eef0f3;
  --chip-line: #e0e3e8;
  --chip-ink: #5a6b8a;
  --chip-active-bg: #2c3240;
  --chip-active-ink: #ffffff;

  /* who-am-I marker. Amber in dtsw because it meant "you are somewhere other
     than the default"; here it means "this is who you are signed in as", which
     wants the same at-a-glance weight. */
  --session-bg: #7a4a10;
  --session-fg: #ffd9a0;
  --session-line: #b3792a;
  --session-reader-bg: #2c3a55;
  --session-reader-fg: #cfd8e8;
  --session-reader-line: #4a5b78;

  /* Named for what the GRID calls them, not for what the page calls them. */
  --grid-bg: #ffffff;
  --grid-header: #f4f6f9;
  --grid-hover: #f3f4f6;
  --grid-selected: #e6edfd;
  --grid-input: #ffffff;

  /* Role pills are tinted by their general department. The HUE comes from the
     department name (people.js hashes it), so a department keeps its colour
     everywhere without a 48-entry table nobody would maintain; these four
     decide how loud that hue is, and they differ per theme because a wash that
     whispers on white shouts on near-black. */
  --dept-s: 42%;
  --dept-l: 92%;
  --dept-ink-l: 30%;
  --dept-line-l: 78%;

  /* The same hue again, at a fraction of the strength, for a tag INSIDE a
     grid cell. Not the pill's own colour at low alpha: --dept-l is 92% here
     and 20% in the dark, so a wash of either would vanish into its own page.
     A mid-lightness hue at ~13% alpha reads as a soft tint on both. */
  --tag-tint-s: 60%;
  --tag-tint-l: 45%;
  --tag-tint-a: 0.13;

  --overlay: rgba(20, 23, 28, 0.42);
  --shadow: 0 12px 32px rgba(31, 36, 48, 0.18);
  --shadow-sm: 0 4px 12px rgba(0, 0, 0, 0.08);

  /* Geometry, not palette. A radius is the same shape in both themes, so these
     two are declared once and are deliberately absent from the dark block —
     theme.test.mjs names them so the omission reads as a decision. */
  --radius: 10px;
  --radius-sm: 4px;
}

/* ============================================================
   THE PALETTE — dark (dtss's night, kept value for value)

   NOT an inversion. Accents brighten, the page goes near-black
   while the sheet stays a shade above it, and tints get STRONGER
   rather than weaker — a wash that whispers on white shouts on
   near-black, and the reverse.
   ============================================================ */
:root[data-theme="dark"] {
  color-scheme: dark;

  --bg: #0f1114;
  --card: #16191e;
  --card-2: #1a1d23;
  --card-3: #20242c;
  --ink: #e6e8eb;
  --muted: #8b93a1;
  --muted-2: #9aa3b1;
  --dim: #6b7280;
  --line: #23272f;
  --line-strong: #2a2f3a;
  --hover: #232833;

  --accent: #4f8cff;
  --accent-hover: #6b9dff;
  --accent-dim: rgba(79, 140, 255, 0.12);
  --accent-ink: #cfe0ff;
  --accent-line: #2b3f63;

  --toolbar-bg: #16191e;
  --toolbar-fg: #e6e8eb;
  --toolbar-muted: #8b93a1;
  --toolbar-hover-fg: #cbd2dc;
  --toolbar-line: #0a0c0f;
  --toolbar-control: #20242c;
  --toolbar-control-line: #2a2f3a;
  --toolbar-control-hover: #2a3039;

  --btn-bg: #20242c;
  --btn-fg: #e6e8eb;
  --btn-hover: #2a3039;

  --ok: #4ade80;
  --on-ok: #08130c;
  --ok-dim: #14301f;
  --ok-ink: #86efac;
  --warn: #f5c26b;
  --warn-dim: #2e2515;
  --warn-ink: #e8d5a8;
  --danger: #f87171;
  --danger-hover: #fb8f8f;
  --danger-dim: #331a1a;
  --danger-ink: #f2b8b8;

  --seg-bg: #20242c;
  --seg-line: #2a2f3a;
  --seg-ink: #9aa3b1;
  --seg-active-bg: #4f8cff;
  --seg-active-ink: #0f1114;

  --chip-bg: #20242c;
  --chip-line: #2a2f3a;
  --chip-ink: #9aa3b1;
  --chip-active-bg: #4f8cff;
  --chip-active-ink: #0f1114;

  --session-bg: #3a2a10;
  --session-fg: #ffd9a0;
  --session-line: #6b5423;
  --session-reader-bg: #1a2740;
  --session-reader-fg: #cfe0ff;
  --session-reader-line: #2b3f63;

  --grid-bg: #14161a;
  --grid-header: #1a1d23;
  --grid-hover: #1b1f27;
  --grid-selected: #1a2233;
  --grid-input: #0f1114;

  --dept-s: 30%;
  --dept-l: 20%;
  --dept-ink-l: 76%;
  --dept-line-l: 32%;

  --tag-tint-s: 70%;
  --tag-tint-l: 62%;
  --tag-tint-a: 0.17;

  --overlay: rgba(0, 0, 0, 0.6);
  --shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
  --shadow-sm: 0 4px 12px rgba(0, 0, 0, 0.35);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  color: var(--ink);
  background: var(--bg);
}

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

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

/* --------------------------------------------------------------------
   Toolbar — one merged bar: app mark · nav · flex gap · right cluster
   -------------------------------------------------------------------- */

/* NOWRAP. A bar that wraps is a two-row bar by another route, which grows the
   header on a laptop instead of staying one strip. The fit is bought back by
   the width tiers at the END of this file. */
.toolbar {
  display: flex;
  align-items: center;
  padding: 0 16px;
  min-height: 44px;
  background: var(--toolbar-bg);
  color: var(--toolbar-fg);
  border-bottom: 1px solid var(--toolbar-line);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.18);
  gap: 12px;
  flex-wrap: nowrap;
}
.toolbar[hidden] { display: none !important; }

.toolbar h1 {
  margin: 0;
  font-size: 17px;
  font-weight: 600;
  letter-spacing: 0.3px;
  white-space: nowrap;
}

.toolbar-right {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: nowrap;
  margin-left: auto;
}

.session-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border-radius: 3px;
  background: var(--session-bg);
  color: var(--session-fg);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  border: 1px solid var(--session-line);
  white-space: nowrap;
}
/* Same trap as .toolbar above — the explicit display would beat [hidden]. */
.session-chip[hidden] { display: none !important; }
.session-chip.is-reader {
  background: var(--session-reader-bg);
  color: var(--session-reader-fg);
  border-color: var(--session-reader-line);
}
.session-chip .session-role { opacity: 0.75; font-weight: 500; }

/* Toolbar icon buttons: theme toggle, sign out. */
.tb-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  background: var(--toolbar-control);
  color: var(--toolbar-fg);
  border: 1px solid var(--toolbar-control-line);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.15s;
  position: relative;
}
.tb-icon-btn:hover { background: var(--toolbar-control-hover); }
.tb-icon-btn svg { width: 18px; height: 18px; display: block; }
.tb-icon-btn[hidden] { display: none !important; }

/* Instant hover tooltip — the native `title` delay is too slow for a bar you
   use every few seconds. */
.tb-icon-btn[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  padding: 4px 8px;
  background: var(--ink);
  color: var(--bg);
  font-size: 11px;
  border-radius: var(--radius-sm);
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.12s;
  z-index: 1200;
}
.tb-icon-btn[data-tooltip]:hover::after { opacity: 1; }

/* --------------------------------------------------------------------
   Tab bar — an inline flex region inside .toolbar
   -------------------------------------------------------------------- */

.tab-bar {
  display: flex;
  align-items: stretch;
  gap: 0;
  align-self: stretch;
  /* Do not shrink: the children are nowrap buttons that cannot get narrower
     than their labels, so a shrunk nav would just paint over the right
     cluster. Overflowing into a page scroll is the better failure. */
  flex: 0 0 auto;
}
.tab-bar[hidden] { display: none !important; }

/* THE `hidden` ATTRIBUTE MUST ACTUALLY HIDE, everywhere in the tab bar.
   Tab visibility has one source — the caps list in /api/whoami, applied by
   applyTabCaps() — and it works by setting `hidden`. That is enough for a bare
   .tab-btn, which declares no display, and silently fails for .tab-dropdown,
   which declares inline-flex: a reader would see an empty "Curate ▾" header
   with every item inside it correctly hidden. One rule for the whole bar, so a
   control added later cannot reintroduce it. */
.tab-bar [hidden] { display: none !important; }

.tab-btn {
  background: transparent;
  color: var(--toolbar-muted);
  border: none;
  border-bottom: 2px solid transparent;
  padding: 10px 13px;
  font-size: 13px;
  font-weight: 500;
  font-family: inherit;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
  /* A wrapped label grows the toolbar's height instead of adding a flex line. */
  white-space: nowrap;
}
.tab-btn:hover { color: var(--toolbar-hover-fg); }
.tab-btn.active {
  color: var(--toolbar-fg);
  border-bottom-color: var(--accent);
}

.tab-dropdown { position: relative; display: inline-flex; }

.tab-dropdown-toggle {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.tab-dropdown-caret { font-size: 10px; line-height: 1; }

.tab-dropdown-menu {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  min-width: 180px;
  background: var(--toolbar-control);
  border: 1px solid var(--toolbar-control-line);
  border-top: none;
  box-shadow: 0 6px 14px rgba(0, 0, 0, 0.35);
}
.tab-dropdown-menu[hidden] { display: none; }

.tab-dropdown-item {
  text-align: left;
  border-bottom: none;
  padding: 8px 14px;
}
.tab-dropdown-item:hover {
  background: var(--toolbar-control-hover);
  color: var(--toolbar-fg);
}
.tab-dropdown-item.active {
  color: var(--toolbar-fg);
  background: var(--toolbar-control-hover);
  border-bottom-color: transparent;
}

/* --------------------------------------------------------------------
   View area
   -------------------------------------------------------------------- */

#view {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.view-pane {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.view-pane[hidden] { display: none !important; }

.view-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 20px;
  background: var(--card);
  border-bottom: 1px solid var(--line);
}
.view-head h2 {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
}
.view-head .view-sub { color: var(--muted); font-size: 12.5px; }
.view-head-right { margin-left: auto; display: flex; align-items: center; gap: 8px; }

.view-body { flex: 1; min-height: 0; padding: 20px; overflow: auto; }
.view-body.is-grid { padding: 0; display: flex; flex-direction: column; }

/* The placeholder every view but Admin → Projects shows today. */
.soon-card {
  max-width: 560px;
  margin: 48px auto;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 28px 30px;
  box-shadow: var(--shadow-sm);
}
.soon-card h3 { margin: 0 0 8px; font-size: 16px; font-weight: 600; }
.soon-card p { margin: 0 0 6px; color: var(--muted); font-size: 13.5px; line-height: 1.55; }
.soon-card .soon-tag {
  display: inline-block;
  margin-bottom: 14px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--accent-dim);
  color: var(--accent);
  border: 1px solid var(--accent-line);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* --------------------------------------------------------------------
   Zones — the toolbar strips a view stacks above its grid
   -------------------------------------------------------------------- */

.zone {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 20px;
  background: var(--card);
  border-bottom: 1px solid var(--line);
}
.zone[hidden] { display: none !important; }
.zone-filters { flex-wrap: wrap; }
.zone-selection {
  background: var(--accent-dim);
  border-left: 3px solid var(--accent);
  padding-left: 17px;    /* offset for the left border */
}
.zone-actions { justify-content: space-between; }

/* Admin → Projects & sync */
.sync-line {
  padding: 6px 16px;
  font-size: 13px;
  color: var(--ink);
  background: var(--card);
  border-bottom: 1px solid var(--line);
}
.sync-line-error { color: var(--danger); }
.sync-line-wait { color: var(--muted); }
.sync-link { margin-left: 10px; }
.sync-progress { font-size: 12.5px; color: var(--muted); }
.sync-row-actions { display: inline-flex; align-items: center; gap: 6px; }
.sync-note { font-size: 12.5px; color: var(--ink); }
.sync-note-wait { color: var(--muted); }
.sync-note-warn { color: var(--muted); }
.sync-note-error { color: var(--danger); }
/* The nightly sync strip (prompt 28) */
.sync-schedule { flex-direction: column; align-items: stretch; gap: 4px; font-size: 13px; }
.sync-schedule .view-sub { font-size: 13px; }
.sync-schedule-last { color: var(--muted); }
.sync-schedule-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.sync-schedule-label { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; }
.sync-schedule-time { font: inherit; font-size: 13px; padding: 2px 6px;
  border: 1px solid var(--line); border-radius: 4px; background: var(--card); color: var(--ink); }
.sync-runs-box summary { cursor: pointer; font-size: 12.5px; color: var(--muted); }
.sync-runs { margin: 4px 0 0; padding-left: 20px; font-size: 12.5px; }
.sync-run-error { color: var(--danger); }
.sync-run-open, .sync-run-throttled { color: var(--muted); }

/* Admin → Backup */
.backup-body { padding: 16px; max-width: 900px; font-size: 13px; }
.backup-body h3 { font-size: 14px; margin: 18px 0 6px; }
.backup-list { font-size: 13px; padding-left: 20px; }
.backup-download {
  display: inline-block; padding: 7px 14px; border-radius: 4px;
  background: var(--btn-bg); color: var(--btn-fg); text-decoration: none; font-size: 13px;
}
.backup-download:hover { background: var(--btn-hover); }
.zone-actions-left, .zone-actions-right {
  display: flex; align-items: center; gap: 6px;
}

.zone button {
  background: var(--btn-bg);
  color: var(--btn-fg);
  border: none;
  border-radius: var(--radius-sm);
  padding: 6px 12px;
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
  transition: background 0.15s;
}
.zone button:hover:not(:disabled) { background: var(--btn-hover); }
.zone button:disabled { opacity: 0.45; cursor: default; }
.zone button.btn-danger { background: var(--danger); }
.zone button.btn-danger:hover:not(:disabled) { background: var(--danger-hover); }

/* A chip is still a chip inside a filter zone. `.zone button` is (0,1,1) and
   `.chip` is (0,1,0), so without this an UNPRESSED toggle in a zone came out
   in the pressed colours — "Unmapped titles only" and "Has a suggestion" both
   looked switched on from the moment they were drawn. jsdom cannot see this;
   the cascade is a thing you have to look at. */
.zone .chip {
  background: var(--chip-bg);
  color: var(--chip-ink);
  border: 1px solid var(--chip-line);
  border-radius: 999px;
  padding: 3px 10px;
  font-size: 12.5px;
  font-weight: 500;
}
.zone .chip:hover:not([aria-pressed="true"]) {
  background: var(--hover);
  color: var(--ink);
}
.zone .chip[aria-pressed="true"] {
  background: var(--chip-active-bg);
  color: var(--chip-active-ink);
  border-color: var(--chip-active-bg);
  font-weight: 600;
}

.zone select,
.zone input[type="search"],
.zone input[type="text"] {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 6px 10px;
  font-size: 13px;
  font-family: inherit;
  color: var(--ink);
  min-width: 110px;
}
.zone input[type="search"] { min-width: 160px; }
.zone label { font-size: 13px; color: var(--muted); }

/* Icon-only action buttons — one shape for every one of them. */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-width: 36px;
  height: 36px;
  padding: 0 8px;
  box-sizing: border-box;
  position: relative;
}
.icon-btn .icon { width: 18px; height: 18px; flex: none; display: block; }
.icon-btn .btn-busy-label { font-size: 13px; }
.icon-btn .btn-busy-label:empty { display: none; }
.icon-btn[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  padding: 4px 8px;
  background: var(--ink);
  color: var(--bg);
  font-size: 11px;
  border-radius: var(--radius-sm);
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.12s;
  z-index: 1200;
}
.icon-btn[data-tooltip]:hover::after { opacity: 1; }

/* --------------------------------------------------------------------
   Seg-pill — a segmented control, categories as shapes
   -------------------------------------------------------------------- */

.seg-pill {
  display: inline-flex;
  align-items: center;
  height: 32px;
  box-sizing: border-box;
  padding: 3px;
  gap: 2px;
  background: var(--seg-bg);
  border: 1px solid var(--seg-line);
  border-radius: 999px;
}
.seg-pill .seg-pill-btn {
  appearance: none;
  border: none;
  background: transparent;
  color: var(--seg-ink);
  height: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 11px;
  border-radius: 999px;
  font-size: 13px;
  font-family: inherit;
  font-weight: 500;
  line-height: 1;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
/* The icon is decorative; keep pointer-events off it so a delegated handler's
   target is always the button itself. */
.seg-pill .seg-pill-btn .icon { pointer-events: none; }
.seg-pill .seg-pill-btn:hover:not([aria-pressed="true"]) {
  background: var(--hover);
  color: var(--ink);
}
.seg-pill .seg-pill-btn[aria-pressed="true"] {
  background: var(--seg-active-bg);
  color: var(--seg-active-ink);
  font-weight: 600;
}
.seg-pill .seg-pill-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* --------------------------------------------------------------------
   Chips — a strip of single- or multi-select tags
   -------------------------------------------------------------------- */

.chip-strip {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px;
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 28px;
  padding: 3px 10px;
  border: 1px solid var(--chip-line);
  border-radius: 999px;
  background: var(--chip-bg);
  color: var(--chip-ink);
  font-size: 12.5px;
  font-family: inherit;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.chip:hover:not([aria-pressed="true"]) { background: var(--hover); color: var(--ink); }
.chip[aria-pressed="true"] {
  background: var(--chip-active-bg);
  color: var(--chip-active-ink);
  border-color: var(--chip-active-bg);
  font-weight: 600;
}
.chip .chip-count {
  font-size: 11px;
  opacity: 0.75;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.chip:disabled { opacity: 0.45; cursor: default; }

/* --------------------------------------------------------------------
   Multiselect — trigger + floating panel with search and checkboxes
   -------------------------------------------------------------------- */

.ds-multiselect { position: relative; display: inline-block; }

.ds-multiselect-trigger {
  background: var(--card);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 6px 10px;
  font-size: 13px;
  font-family: inherit;
  min-width: 160px;
  text-align: left;
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  cursor: pointer;
}
.ds-multiselect-trigger:hover { background: var(--hover); }
.ds-multiselect-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.ds-multiselect-arrow { opacity: 0.5; font-size: 11px; }

.ds-multiselect-panel {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  min-width: 220px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
  z-index: 100;
  padding: 6px 0;
}
.ds-multiselect-panel[hidden] { display: none !important; }

.ds-multiselect-search {
  display: block;
  width: calc(100% - 24px);
  margin: 2px 12px 6px;
  padding: 5px 8px;
  font-size: 13px;
  font-family: inherit;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}

.ds-multiselect-actions {
  display: flex;
  gap: 10px;
  padding: 4px 12px 8px;
  border-bottom: 1px solid var(--line);
}
.ds-multiselect-actions button {
  background: transparent;
  color: var(--muted);
  border: none;
  padding: 0;
  font-size: 12px;
  font-family: inherit;
  cursor: pointer;
  text-decoration: underline;
}
.ds-multiselect-actions button:hover { color: var(--accent); }

.ds-multiselect-list {
  list-style: none;
  margin: 0;
  padding: 4px 0;
  max-height: 260px;
  overflow-y: auto;
}
.ds-multiselect-list li { margin: 0; padding: 0; }
.ds-multiselect-list label {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 12px;
  cursor: pointer;
  font-size: 13px;
  user-select: none;
}
.ds-multiselect-list label:hover { background: var(--hover); }
.ds-multiselect-list input { margin: 0; cursor: pointer; }
.ds-multiselect-list .ds-ms-count {
  margin-left: auto;
  padding-left: 12px;
  color: var(--muted);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}
.ds-multiselect-empty {
  padding: 8px 12px;
  color: var(--muted);
  font-size: 12.5px;
}

/* Tree picker — the multiselect panel with an indented, foldable list. */
.tree-node { display: flex; align-items: center; gap: 6px; padding: 3px 12px; }
.tree-node:hover { background: var(--hover); }
.tree-node.is-child { padding-left: 34px; }
.tree-node label {
  display: flex; align-items: center; gap: 8px;
  cursor: pointer; font-size: 13px; user-select: none; flex: 1; min-width: 0;
}
.tree-node .tree-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tree-node.is-department label { font-weight: 600; }
.tree-toggle {
  width: 18px; height: 18px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: none; cursor: pointer;
  color: var(--muted); font-size: 10px; line-height: 1; padding: 0;
  font-family: inherit;
}
.tree-toggle:hover { color: var(--ink); }
.tree-spacer { width: 18px; flex: none; }

/* --------------------------------------------------------------------
   Drawer — right-side panel
   -------------------------------------------------------------------- */

.drawer-scrim {
  position: fixed;
  inset: 0;
  background: var(--overlay);
  z-index: 900;
}
.drawer-scrim[hidden] { display: none !important; }

.drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(480px, 92vw);
  display: flex;
  flex-direction: column;
  background: var(--card);
  border-left: 1px solid var(--line);
  box-shadow: var(--shadow);
  z-index: 901;
}
.drawer[hidden] { display: none !important; }

.drawer-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--line);
  background: var(--card-2);
}
.drawer-head h3 { margin: 0; font-size: 15px; font-weight: 600; }
.drawer-head .drawer-sub { color: var(--muted); font-size: 12px; }
.drawer-close {
  margin-left: auto;
  background: transparent;
  border: none;
  color: var(--muted);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  padding: 0 2px;
  font-family: inherit;
}
.drawer-close:hover { color: var(--ink); }

.drawer-body { flex: 1; overflow-y: auto; padding: 4px 0 18px; }
.drawer-section { padding: 14px 18px; border-bottom: 1px solid var(--line); }
.drawer-section:last-child { border-bottom: none; }
.drawer-section-title {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  margin-bottom: 8px;
}
.drawer-foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 12px 18px;
  border-top: 1px solid var(--line);
  background: var(--card-2);
}

/* --------------------------------------------------------------------
   Dialog — modal with confirm / cancel
   -------------------------------------------------------------------- */

.dialog-scrim {
  position: fixed;
  inset: 0;
  background: var(--overlay);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  padding: 24px;
}
.dialog-scrim[hidden] { display: none !important; }

.dialog {
  width: min(460px, 100%);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.dialog-head { padding: 16px 20px 0; }
.dialog-head h3 { margin: 0; font-size: 16px; font-weight: 600; }
.dialog-body { padding: 10px 20px 4px; color: var(--muted); font-size: 13.5px; line-height: 1.55; }
.dialog-foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 16px 20px;
}
.dialog-foot button {
  background: var(--btn-bg);
  color: var(--btn-fg);
  border: none;
  border-radius: var(--radius-sm);
  padding: 7px 14px;
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
  transition: background 0.15s;
}
.dialog-foot button:hover { background: var(--btn-hover); }
.dialog-foot button.btn-quiet {
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--line);
}
.dialog-foot button.btn-quiet:hover { background: var(--hover); color: var(--ink); }
.dialog-foot button.btn-danger { background: var(--danger); color: #fff; }
.dialog-foot button.btn-danger:hover { background: var(--danger-hover); }

/* --------------------------------------------------------------------
   Sign-in
   -------------------------------------------------------------------- */

#signin {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
#signin[hidden] { display: none !important; }

.signin-card {
  width: min(360px, 100%);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 28px 28px 24px;
}
.signin-card h1 { margin: 0 0 4px; font-size: 20px; font-weight: 600; letter-spacing: 0.3px; }
.signin-card .signin-sub { margin: 0 0 20px; color: var(--muted); font-size: 13px; }
.signin-card label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  margin-bottom: 5px;
}
.signin-card input {
  width: 100%;
  padding: 8px 10px;
  margin-bottom: 14px;
  font-size: 14px;
  font-family: inherit;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.signin-card input:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.signin-card button[type="submit"] {
  width: 100%;
  padding: 9px 14px;
  background: var(--accent);
  color: #fff;
  border: none;
  border-radius: var(--radius-sm);
  font-size: 14px;
  font-family: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s;
}
.signin-card button[type="submit"]:hover { background: var(--accent-hover); }
.signin-card button[type="submit"]:disabled { opacity: 0.6; cursor: default; }
.signin-error {
  margin: 0 0 14px;
  padding: 8px 10px;
  background: var(--danger-dim);
  color: var(--danger-ink);
  border: 1px solid var(--danger);
  border-radius: var(--radius-sm);
  font-size: 12.5px;
}
.signin-error[hidden] { display: none !important; }

/* --------------------------------------------------------------------
   Status toast
   -------------------------------------------------------------------- */

#toast {
  position: fixed;
  bottom: 16px;
  right: 16px;
  padding: 10px 16px;
  border-radius: var(--radius-sm);
  background: #1f2430;
  color: #fff;
  font-size: 13px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
  z-index: 1300;
  max-width: 420px;
}
#toast[hidden] { display: none !important; }
#toast.error { background: var(--danger); }
#toast.ok { background: #1f2430; }

/* --------------------------------------------------------------------
   Grid host
   -------------------------------------------------------------------- */

.grid-host { flex: 1; min-height: 240px; width: 100%; }

/* --------------------------------------------------------------------
   Topbar search
   -------------------------------------------------------------------- */

.tb-search {
  position: relative;
  display: flex;
  align-items: center;
  flex: 0 1 260px;
  min-width: 150px;
}
.tb-search .tb-search-icon {
  position: absolute;
  left: 8px;
  width: 15px;
  height: 15px;
  color: var(--toolbar-muted);
  pointer-events: none;
}
.tb-search input {
  width: 100%;
  background: var(--toolbar-control);
  color: var(--toolbar-fg);
  border: 1px solid var(--toolbar-control-line);
  border-radius: var(--radius-sm);
  padding: 7px 10px 7px 28px;
  font-size: 13px;
  font-family: inherit;
}
.tb-search input::placeholder { color: var(--toolbar-muted); }
.tb-search input:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--toolbar-control-hover);
}
/* The "/" hint, dropped as soon as the field is in use. */
.tb-search .tb-search-key {
  position: absolute;
  right: 8px;
  padding: 1px 5px;
  border: 1px solid var(--toolbar-control-line);
  border-radius: 3px;
  font-size: 10px;
  color: var(--toolbar-muted);
  pointer-events: none;
}
.tb-search.is-active .tb-search-key { display: none; }

.search-panel {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  width: min(460px, 90vw);
  max-height: 60vh;
  overflow-y: auto;
  background: var(--card);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
  z-index: 1100;
  padding: 4px 0;
}
.search-panel[hidden] { display: none !important; }

.search-hit {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--line);
  padding: 8px 12px;
  cursor: pointer;
  font-family: inherit;
}
.search-hit:last-child { border-bottom: none; }
.search-hit:hover,
.search-hit.is-active { background: var(--hover); }
.search-hit-top { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.search-hit-name { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.search-hit-nick { font-size: 12.5px; color: var(--muted); font-weight: 500; }
.search-hit-roles { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; margin-top: 4px; }
.search-hit-sub { margin-top: 3px; font-size: 11.5px; color: var(--muted); }
.search-hit-matched { margin-top: 2px; font-size: 11.5px; color: var(--muted); font-style: italic; }
.search-empty { padding: 14px 14px; color: var(--muted); font-size: 13px; }

/* --------------------------------------------------------------------
   Pills
   -------------------------------------------------------------------- */

.pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 1px 8px;
  border-radius: 999px;
  border: 1px solid var(--chip-line);
  background: var(--chip-bg);
  color: var(--chip-ink);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
/* Tinted by its general department — see --dept-* in the palette. */
.pill-role {
  background: hsl(var(--h, 220) var(--dept-s) var(--dept-l));
  color: hsl(var(--h, 220) var(--dept-s) var(--dept-ink-l));
  border-color: hsl(var(--h, 220) var(--dept-s) var(--dept-line-l));
}
.pill-more { background: transparent; border-style: dashed; }
.pill-status-Active { background: var(--ok-dim); color: var(--ok); border-color: var(--ok); }
.pill-status-Retired { background: var(--card-3); color: var(--muted); }
.pill-status-Foreign { background: var(--accent-dim); color: var(--accent); border-color: var(--accent-line); }
.pill-status-External { background: var(--warn-dim); color: var(--warn); border-color: var(--warn); }
.pill-pinned { background: var(--accent-dim); color: var(--accent); border-color: var(--accent-line); }

/* --------------------------------------------------------------------
   Cell tags — a category INSIDE a grid cell

   A pill is a control's shape: a border, a button's padding, something you
   press. Nothing in a grid cell is pressable, and a 42px row cannot afford
   to spend half its height saying so. These are the cell dialect of the
   same idea — flat, 18px, no border, no shadow, no min-width — and they are
   emitted by grid.js's tagRenderer so every grid says a category the same
   way. See the note there.
   -------------------------------------------------------------------- */

.tag {
  display: inline-block;
  max-width: 100%;
  height: 18px;
  line-height: 18px;
  padding: 0 6px;
  border: none;
  border-radius: 9px;
  background: var(--card-3);
  color: var(--ink);
  font-size: 11.5px;
  font-weight: 500;
  vertical-align: middle;
  /* A role name is longer than its column. Cut it and keep the row one line;
     the whole name is in the tooltip. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tag + .tag { margin-left: 4px; }

/* Tinted by its general department — the hue is pills.js's hash, the strength
   is --tag-tint-* rather than --dept-*: in a cell a department is context,
   and the ink stays the page's own so the value reads before the colour. */
.tag--dept {
  background: hsl(var(--h, 220) var(--tag-tint-s) var(--tag-tint-l) / var(--tag-tint-a));
  color: var(--ink);
}

.tag--neutral { background: var(--card-3); color: var(--muted); }

/* A draft is not an engagement, and grey is how this app says "not yet". */
.tag--draft { background: var(--card-3); color: var(--muted); }

/* The two engagements that are worth looking up from the row for. */
.tag--daily,
.tag--additional { background: var(--warn-dim); color: var(--warn-ink); }

/* core is the default and most of the column. Named so the renderer has one
   class per engagement, but it paints nothing: it IS the plain muted text. */
.tag--core {
  height: auto;
  padding: 0;
  border-radius: 0;
  background: none;
  color: var(--muted);
  font-weight: 400;
}

/* Not a category: a blank, or a value this column has no colour for. */
.cell-quiet { color: var(--muted); }

/* Status is four words in a narrow column and every row has one. Colour
   alone carries it — the tokens are the page's, so a status is the same
   colour in a cell as it is on the pill in the Person header. */
.cell-status { font-weight: 500; }
.cell-status--Active { color: var(--ok); }
.cell-status--Retired { color: var(--muted); }
.cell-status--Foreign { color: var(--accent); }
.cell-status--External { color: var(--warn); }

.pill-dead { background: var(--danger-dim); color: var(--danger); border-color: var(--danger); }
.pill-auto { background: transparent; color: var(--muted); border-color: var(--line); font-weight: 500; }
.pill-manual { background: var(--accent-dim); color: var(--accent); border-color: var(--accent-line); }

/* --------------------------------------------------------------------
   Person page
   -------------------------------------------------------------------- */

.person-page {
  padding: 18px 20px 40px;
  max-width: 1500px;
  margin: 0 auto;
  width: 100%;
}
.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  margin-bottom: 16px;
}
.card-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--line);
}
.card-head h3 { margin: 0; font-size: 13px; font-weight: 600;
                text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
.card-head-right { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.card-body { padding: 14px 16px; }
.card-body.is-grid { padding: 0; }

.person-head { display: flex; gap: 24px; flex-wrap: wrap; padding: 18px 20px; }
.person-head-main { flex: 1 1 380px; min-width: 0; }
.person-name { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.person-name h2 { margin: 0; font-size: 24px; font-weight: 650; letter-spacing: -0.01em; }
.person-name .person-nick { font-size: 17px; color: var(--muted); font-weight: 500; }
.person-depts { margin-top: 4px; font-size: 12.5px; color: var(--muted); }
.person-roles { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 10px; }

.stat-row { display: flex; gap: 22px; flex-wrap: wrap; margin-top: 16px; }
.stat { display: flex; flex-direction: column; gap: 1px; }
.stat-value { font-size: 18px; font-weight: 650; font-variant-numeric: tabular-nums; }
.stat-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }

.person-contacts { flex: 0 1 320px; min-width: 240px; }
.contact-primary { margin-bottom: 10px; }
.contact-primary-label { font-size: 11px; text-transform: uppercase;
                         letter-spacing: 0.04em; color: var(--muted); }
.contact-primary-value { display: flex; align-items: center; gap: 6px; font-size: 14px; }
.contact-primary-value span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.contact-other { display: flex; align-items: center; gap: 6px; font-size: 12px;
                 color: var(--muted); padding: 2px 0; }
.contact-other.is-dead span.contact-value { text-decoration: line-through; }
.contact-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.contact-seen { margin-left: auto; font-size: 11px; color: var(--dim); white-space: nowrap; }

.mini-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; padding: 0; flex: none;
  background: transparent; border: 1px solid var(--line);
  border-radius: var(--radius-sm); color: var(--muted); cursor: pointer;
  position: relative; font-family: inherit;
}
.mini-btn:hover:not(:disabled) { background: var(--hover); color: var(--ink); }
.mini-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.mini-btn svg { width: 13px; height: 13px; display: block; }
.mini-btn[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute; bottom: calc(100% + 5px); left: 50%;
  transform: translateX(-50%);
  padding: 3px 7px; background: var(--ink); color: var(--bg);
  font-size: 10.5px; border-radius: 3px; white-space: nowrap;
  opacity: 0; pointer-events: none; transition: opacity 0.12s; z-index: 1200;
}
.mini-btn[data-tooltip]:hover::after { opacity: 1; }

.role-lists { display: flex; gap: 28px; flex-wrap: wrap; }
.role-list { flex: 1 1 220px; min-width: 200px; }
.role-list h4 { margin: 0 0 6px; font-size: 11px; text-transform: uppercase;
                letter-spacing: 0.04em; color: var(--muted); }
.role-line { display: flex; align-items: center; gap: 6px; padding: 3px 0; font-size: 13px; }
.role-line .role-when { margin-left: auto; font-size: 11px; color: var(--dim); white-space: nowrap; }
.role-rule { margin-top: 12px; font-size: 11.5px; color: var(--muted); font-style: italic; }

.plain-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.plain-table th {
  text-align: left; font-size: 11px; text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--muted); font-weight: 600;
  padding: 4px 10px 4px 0; border-bottom: 1px solid var(--line);
}
.plain-table td { padding: 4px 10px 4px 0; border-bottom: 1px solid var(--line); }
.plain-table tr:last-child td { border-bottom: none; }
.plain-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.identity-cols { display: flex; gap: 28px; flex-wrap: wrap; }
.identity-col { flex: 1 1 300px; min-width: 260px; }

.person-grid { height: 520px; }
.muted-row { color: var(--muted); font-style: italic; }
.empty-note { color: var(--muted); font-size: 13px; padding: 6px 0; }

.back-link {
  display: inline-flex; align-items: center; gap: 5px;
  background: transparent; border: none; padding: 0 0 10px;
  color: var(--muted); font-size: 12.5px; font-family: inherit; cursor: pointer;
}
.back-link:hover { color: var(--accent); }

/* --------------------------------------------------------------------
   Toolbar width tiers
   --------------------------------------------------------------------
   The top bar does not wrap, so a narrow window is paid for in width rather
   than in a second row: padding, gaps and label padding, never a control.

   MUST STAY AT THE END OF THIS FILE. A media query adds no specificity, so
   these only beat .tab-btn and .session-chip by source order. Moved above
   them, the tiers silently stop applying.
   -------------------------------------------------------------------- */

@media (max-width: 1512px) {
  .toolbar { gap: 8px; padding: 0 12px; }
  .tab-btn { padding: 10px 9px; }
  .toolbar-right { gap: 8px; }
}

@media (max-width: 1300px) {
  .toolbar { gap: 6px; padding: 0 10px; }
  .tab-btn { padding: 10px 6px; font-size: 12.5px; }
  .toolbar h1 { font-size: 15px; }
  .toolbar-right { gap: 6px; }
  /* The chip is the who-am-I marker — compacted, never dropped. */
  .session-chip { padding: 2px 6px; font-size: 10.5px; letter-spacing: 0.02em; }
  .tb-icon-btn { width: 28px; height: 28px; }
}

/* --------------------------------------------------------------------
   Crew finder
   -------------------------------------------------------------------- */

/* A facet of zero is still a number worth showing — "nobody here" — but it is
   not one to read first. */
.ds-ms-count.is-zero { opacity: 0.45; }

/* The word in front of a zone's controls. Small, muted, fixed width enough to
   line the two filter rows up with each other. */
.zone-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  min-width: 84px;
}

/* The answer, in the head, where the eye already is after choosing a filter. */
.count-pill {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--accent-dim);
  color: var(--accent);
  border: 1px solid var(--accent-line);
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}
.count-pill:empty { display: none; }

.crew-text { min-width: 180px; }

/* Landing and empty state share a shape: a quiet card, not an error. */
.crew-hint {
  max-width: 560px;
  margin: 48px auto;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 28px 30px;
  box-shadow: var(--shadow-sm);
}
.crew-hint h3 { margin: 0 0 8px; font-size: 16px; font-weight: 600; }
.crew-hint p { margin: 0 0 8px; color: var(--muted); font-size: 13.5px; line-height: 1.55; }
.crew-hint-sub { font-size: 12.5px; }
.crew-empty-btn {
  margin-top: 10px;
  background: var(--btn-bg);
  color: var(--btn-fg);
  border: none;
  border-radius: var(--radius-sm);
  padding: 7px 14px;
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
}
.crew-empty-btn:hover { background: var(--btn-hover); }

/* Name + nickname in one cell: the nickname is how the office says the name,
   and it belongs beside it rather than in a column of its own. */
/* The Grouped view (prompt 25): dtsw's view segment and its two chevrons,
   one tight cluster beside the count. */
.crew-arrange { display: inline-flex; align-items: center; gap: 2px; }
.crew-arrange .seg-pill-btn .icon { display: inline-flex; }
.crew-arrange .seg-pill-btn svg { width: 18px; height: 18px; display: block; }
.crew-group-name { font-weight: 600; }
.crew-group-count { color: var(--muted); font-variant-numeric: tabular-nums; }
.cell-mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
             font-size: 12.5px; font-variant-numeric: tabular-nums; }

.crew-name-cell { display: inline-flex; align-items: baseline; gap: 6px; min-width: 0; }
.crew-name { overflow: hidden; text-overflow: ellipsis; }
.crew-nick { color: var(--muted); font-size: 12px; white-space: nowrap; }

/* Several tags in one cell, clipped rather than wrapped — a 42px row holds one
   line and a wrapped pill would push the whole grid's rhythm out. */
.crew-roles-cell {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  overflow: hidden;
  min-width: 0;
}

/* A role held before and not now. Same shape as .tag--dept, no hue: the
   distinction the eye has to make is current vs past, not which department. */
.tag--past {
  background: var(--card-2);
  color: var(--muted);
  border: 1px dashed var(--line-strong);
}

.crew-contact-cell {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
  width: 100%;
}
.crew-contact-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The copy button appears on hover: 6,500 of them at rest would be a column of
   buttons rather than a column of addresses. */
.crew-copy { opacity: 0; transition: opacity 0.12s; flex: none; }
.ag-row:hover .crew-copy,
.crew-copy:focus-visible { opacity: 1; }

/* A row is a link to a person — on THIS grid. The projects grid and the
   Person page's shows grid are read-only and must not claim otherwise. */
.crew-grid .ag-row { cursor: pointer; }

/* --------------------------------------------------------------------
   Shows
   -------------------------------------------------------------------- */

/* The picker sits IN the view head, next to the title, because choosing the
   show is not a filter — it is what the view is of. */
.show-picker { margin-left: 4px; }
.show-picker-trigger { min-width: 220px; font-weight: 600; }
.show-picker-trigger.is-empty { font-weight: 400; color: var(--muted); }
.show-picker-panel { min-width: 380px; }

.show-option {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 6px 10px;
  background: transparent;
  border: none;
  border-radius: var(--radius-sm);
  font: inherit;
  color: var(--ink);
  text-align: left;
  cursor: pointer;
}
.show-option:hover { background: var(--card-2); }
.show-option.is-current { background: var(--accent-dim); }
/* A live show is the one you are most likely to want, and bold says so
   without needing a legend. */
.show-option.is-live .show-option-name { font-weight: 600; }
.show-option-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.show-option-meta { color: var(--muted); font-size: 12px; white-space: nowrap; }

.pill-state-live {
  background: var(--accent-dim); color: var(--accent);
  border: 1px solid var(--accent-line);
}
.pill-state-historical {
  background: var(--card-2); color: var(--muted);
  border: 1px solid var(--line);
}

/* The summary strip: numbers first, their labels under them, the way dtsw's
   summary bar reads. */
.zone-stats {
  gap: 26px;
  padding-top: 10px;
  padding-bottom: 10px;
  background: var(--card-2);
}
.stat-cell { display: inline-flex; flex-direction: column; line-height: 1.2; }
.stat-cell-value { font-size: 16px; font-weight: 600; }
.stat-cell-label {
  font-size: 10.5px; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--muted);
}
.stat-cell-right { margin-left: auto; display: flex; gap: 14px; align-items: center; }
.stat-cell-note { color: var(--muted); font-size: 12px; }

/* A chip strip inside a zone wraps rather than pushing the zone wider: a show
   with twenty departments is a normal show. */
.chip-strip-host { display: flex; min-width: 0; flex: 1; }
.chip-strip-host .chip-strip { flex-wrap: wrap; }
.zone-slot { display: inline-flex; }

/* A startform nobody has matched to a person yet. Not an error — it is what
   the Match inbox is for — but it is why this row does not open a page. */
.tag--unresolved {
  background: var(--card-2);
  color: var(--muted);
  border: 1px dashed var(--line-strong);
  font-weight: 600;
}
.crew-name-cell.is-unresolved .crew-name { color: var(--muted); }

/* Only a resolved row goes anywhere. */
.show-grid .ag-row { cursor: pointer; }
.show-grid .ag-row-group { cursor: default; }

/* A filter zone whose controls wrap onto several lines. Everything lines up
   with the FIRST line — a label centred against four rows of chips reads as a
   label for the middle row. */
.zone.zone-wrap { align-items: flex-start; }
.zone.zone-wrap > .zone-label,
.zone.zone-wrap > .seg-pill,
.zone.zone-wrap > .ds-multiselect > .ds-multiselect-trigger { margin-top: 3px; }
.zone.zone-wrap .chip-strip { row-gap: 6px; }

/* A toast with something to press. It does not time out, so it needs to look
   like an offer rather than a notice. */
#toast.has-action { display: inline-flex; align-items: center; gap: 12px; }
#toast[hidden].has-action { display: none !important; }
.toast-action {
  background: rgba(255, 255, 255, 0.15);
  color: inherit;
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: var(--radius-sm);
  padding: 4px 10px;
  font: inherit;
  font-size: 12.5px;
  cursor: pointer;
  white-space: nowrap;
}
.toast-action:hover { background: rgba(255, 255, 255, 0.28); }

/* --------------------------------------------------------------------
   The contact sheet
   -------------------------------------------------------------------- */

/* Hidden on screen, always. It exists so window.print() has something clean
   to print — a popup would be a popup, and popups get blocked. */
#print-sheet { display: none; }


/* ---------------------------------------------------------------------------
   The Titles workbench
   ---------------------------------------------------------------------------
   An inbox is read down a column, not across a row: the eye follows the count
   and the title, and everything else is there to confirm a decision already
   made. Hence the fixed grid — every row's title starts at the same x, so a
   hundred of them read as a list rather than as a hundred little layouts. */

.tt-hint { margin: 0 0 6px; font-size: 12px; }

/* --------------------------------------------------------------------
   The Inbox grid
   --------------------------------------------------------------------
   Prompt 08 called the Inbox "a list, rows, keyboard-driven" and it was
   built as a hand-made CSS grid. Six prompts later it was the only list in
   the app with no column resizing, no sorting, no chooser, no filters and
   no export — on the one screen Kalman actually works in. Prompt 14 put it
   on AG Grid like everything else, and every one of those came free. The
   rules below are the two things a grid does NOT give you for nothing. */

/* One: the role picker is a cellRenderer, so it is visible in every row —
   and its dropdown is `position: fixed`, placed by the picker itself,
   because AG Grid's body viewport scrolls and would clip anything
   positioned inside a cell. The cell still has to stop clipping the input's
   own focus ring. */
/* The hint line sits above the grid inside `is-grid`, which has no padding. */
.view-body.is-grid > .tt-hint { padding: 6px 12px 4px; margin: 0; }

.inbox-grid .ag-cell.cell-picker {
  overflow: visible;
  padding-top: 0;
  padding-bottom: 0;
  display: flex;
  align-items: center;
}
.inbox-grid .ag-cell.cell-picker .tt-picker { width: 100%; }

/* Two: the columns that carry the decision read louder than the ones that
   only confirm it. */
.inbox-grid .ag-cell.cell-strong { font-weight: 600; }
.inbox-grid .ag-cell.cell-fee { font-variant-numeric: tabular-nums; }

.tt-map-selected { font-weight: 600; }
.zone-gap { flex: 1; }

/* --- the scope control --- */
/* It NAMES the scope in force. The first version was a caret over a blank
   checked row followed by the other three scopes, so the current one was
   invisible and the tick appeared to sit on nothing. */
.tt-scope-menu { position: relative; }
.tt-scope-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--card);
  color: var(--ink);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  white-space: nowrap;
}
.tt-scope-btn:hover { background: var(--hover); }
.tt-scope-label { font-weight: 600; }
.tt-scope-list {
  position: absolute;
  z-index: 50;
  top: calc(100% + 4px);
  left: 0;
  min-width: 260px;
  margin: 0;
  padding: 4px;
  list-style: none;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: var(--card);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
}
.tt-scope-list[hidden] { display: none !important; }
.tt-scope-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 8px;
  border-radius: 5px;
  font-size: 13px;
  cursor: pointer;
  white-space: nowrap;
}
.tt-scope-item:hover { background: var(--hover); }
.tt-scope-item.is-current { background: var(--accent-dim); font-weight: 600; }
/* A fixed slot, so the four labels line up whether or not they are ticked. */
.tt-scope-tick { width: 12px; color: var(--accent); }


/* THE CELL IS THE CONTROL.
 *
 * The picker was built for the CSS row of prompt 08, which grew to fit it.
 * Dropped into an AG Grid cell it kept its own border, padding and — the
 * actual culprit — an INHERITED line-height: AG Grid sets `line-height` on
 * `.ag-cell` to centre one line of text, so `font: inherit` gave the input a
 * 35px line box and a 43px outer height inside a 38px row. Every filled
 * picker sat 3px proud of its row and overlapped the ones above and below.
 *
 * So: explicit line-height, border-box, exactly the height of the cell. And a
 * transparent border at rest rather than none, so hover and focus can colour
 * it without moving a pixel.
 */
.tt-picker { position: relative; width: 100%; height: 100%; }
.tt-picker-input {
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0 8px;
  /* Transparent, not absent: hover and focus colour this border, and a border
     that appears from nothing moves every pixel beside it. */
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  /* NO FILL AT REST. Prompt 15 tinted every resting picker to say "editable",
     but every cell in this column is editable, so the tint distinguished
     nothing and turned the column into a wall of grey boxes — and at focus the
     accent border drew a second outline around the fill. A value in ordinary
     cell type, and a muted placeholder where there is none, says it on its
     own, and lets the eye run down the column comparing VALUES. */
  background: transparent;
  color: var(--ink);
  font-family: inherit;
  font-size: 13px;
  line-height: normal;
}
.tt-picker-input::placeholder { color: var(--dim); }
.tt-picker-input:hover { border-color: var(--line); }
.tt-picker-input:focus {
  border-color: var(--accent);
  outline: none;
}

/* Only when there is something to clear, and only when the cell is under the
   pointer or holding focus. Absolutely placed, so showing it moves nothing;
   the room it needs is reserved by `.has-value`, which changes when the value
   does rather than when the pointer does. */
.tt-picker.has-value .tt-picker-input { padding-right: 24px; }
.tt-picker-clear {
  position: absolute;
  top: 50%;
  right: 4px;
  transform: translateY(-50%);
  display: none;
  width: 18px;
  height: 18px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--muted);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
}
.tt-picker:hover .tt-picker-clear,
.tt-picker:focus-within .tt-picker-clear { display: block; }
.tt-picker-clear:hover { background: var(--hover); color: var(--ink); }
/* FIXED, not absolute, and placed from the input's rectangle by the picker
   itself. AG Grid's body viewport scrolls, so anything absolutely positioned
   inside a cell is clipped by it — and a role picker whose list you cannot see
   is not a role picker. */
.tt-picker-list {
  position: fixed;
  /* Above the dialog scrim (1000): the Move dialog's "Move to whom?" list
     opened BEHIND it, so + New person was unreachable (2026-11-07). */
  z-index: 1350;
  min-width: 260px;
  max-height: 280px;
  overflow: auto;
  margin: 2px 0 0;
  padding: 4px;
  list-style: none;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: var(--card);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
}
.tt-picker-list[hidden] { display: none !important; }
.tt-picker-item {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding: 4px 6px;
  border-radius: 5px;
  cursor: pointer;
}
.tt-picker-item:hover,
.tt-picker-item[aria-selected="true"] { background: var(--accent-dim); }
.tt-picker-dept-name { color: var(--muted); font-size: 12px; }
.tt-picker-empty { padding: 4px 6px; color: var(--muted); }
/* Two lines on purpose: "＋ create “Storyboard Artist” under" and then the
   department beside the button. One line squeezed the sentence into a column
   three words wide. */
.tt-picker-new {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding: 6px;
  margin-top: 4px;
  border-top: 1px solid var(--line);
  font-size: 12px;
}
.tt-picker-new > span { flex-basis: 100%; }
.tt-picker-dept { flex: 1; min-width: 0; }

/* --- filters --- */
.tt-filter-text, .tt-filter-min {
  padding: 4px 8px;
  border: 1px solid var(--line-strong);
  border-radius: 6px;
  background: var(--card);
  color: var(--ink);
  font: inherit;
  font-size: 13px;
}
.tt-filter-min { width: 68px; }
.zone-sep { display: inline-flex; align-items: center; gap: 6px; }

/* --- the Review pane --- */
/* A flagged record is marked where it sits, whichever lens is on, so it stays
   legible in the unfiltered tree too. Quiet: a health view with forty alarms
   on screen is a health view nobody reads. */
.review-grid .ag-row.row-odd { box-shadow: inset 3px 0 0 var(--warn); }
.review-grid .ag-cell.cell-odd { color: var(--warn-ink); font-size: 12px; }
.review-grid .ag-cell.cell-link { color: var(--accent-ink); cursor: pointer; }
.review-grid .ag-cell.cell-link:hover { text-decoration: underline; }
.review-grid .ag-cell.cell-fee { font-variant-numeric: tabular-nums; }
/* The picker cell is prompt 15/16's, reused rather than re-stated. */
.review-grid .ag-cell.cell-picker {
  overflow: visible;
  padding-top: 0;
  padding-bottom: 0;
  display: flex;
  align-items: center;
}
.review-grid .ag-cell.cell-picker .tt-picker { width: 100%; }

/* --- the obvious rungs --- */
/* The rung is how much spelling was ignored to make the match, and it warms as
   more was thrown away: `plural` is one dropped letter from a different word,
   and the row wearing that tag is the one to look at twice. It was a column on
   a tab of its own until prompt 10; it is a tag on an inbox row now. */
.tt-rung { font-variant: small-caps; letter-spacing: 0.02em; }
.tt-rung--exact { background: var(--ok-dim); color: var(--ok-ink); }
.tt-rung--spacing,
.tt-rung--joined { background: var(--card-3); color: var(--muted); }
/* The two rungs that threw a whole word away wear the same warning colour:
   what came off is bigger than a space or an s. */
.tt-rung--singular,
.tt-rung--abbrev { background: var(--warn-dim); color: var(--warn-ink); }

/* --- the abbreviation dialog --- */
.tt-abbrev-form {
  display: flex; align-items: center; gap: 8px; margin: 0 0 10px;
}
.tt-abbrev-form input { flex: 1 1 0; min-width: 0; }
.tt-abbrev-list { max-height: 240px; overflow-y: auto; display: block; }

/* --- the preview table --- */
.tt-sample { border-collapse: collapse; width: 100%; font-size: 12px; }
.tt-sample th, .tt-sample td {
  padding: 3px 8px 3px 0;
  text-align: left;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.tt-sample th { color: var(--muted); font-weight: 600; }

/* --- history --- */
.tt-history { display: flex; flex-direction: column; gap: 2px; }
.tt-act {
  display: grid;
  grid-template-columns: 160px 110px minmax(240px, 1fr) 100px 80px;
  align-items: center;
  gap: 10px;
  padding: 5px 10px;
  border-bottom: 1px solid var(--line);
  font-size: 13px;
}
.tt-act.is-undone { opacity: 0.55; text-decoration: line-through; }
.tt-when { font-variant-numeric: tabular-nums; }
.tt-undo {
  padding: 2px 10px;
  border: 1px solid var(--line-strong);
  border-radius: 6px;
  background: var(--card-2);
  color: var(--ink);
  cursor: pointer;
}
.tt-undo:hover { background: var(--hover); }

/* ---------------------------------------------------------------------------
   The Match tab
   ---------------------------------------------------------------------------
   Everything the Titles inbox already solved is REUSED rather than restated:
   the picker cell (`.tt-picker*`), the hint line, the history rows and the
   rung tag are the same classes. What is here is the two things this screen
   has that that one does not — a rung palette of its own, and the pair of
   person cards a duplicate is decided from. */

/* The rung warms as the evidence thins, the same idea as the titles ladder's:
   a name and a contact agreeing is the strong end, a near name is a
   suggestion wearing a warning. Every one of these is a tag the ladder wrote,
   never a colour somebody chose per row. */
.mt-rung--cmid,
.mt-rung--two_keys { background: var(--ok-dim); color: var(--ok-ink); }
.mt-rung--rare_name,
.mt-rung--contact { background: var(--card-3); color: var(--muted); }
.mt-rung--name { background: var(--warn-dim); color: var(--warn-ink); }
/* A near name is a guess, and the one rung that never writes anything. */
.mt-rung--fuzzy { background: var(--danger-dim); color: var(--danger-ink); }
.mt-rung--none { background: var(--card-3); color: var(--muted); }

/* The picker's list is wider than the roles one, and stacked rather than
   side by side: a role option is a name and a department, two words each,
   but a candidate carries a SENTENCE — "same phone · 3 shows · last Gaffer on
   Oasis 2025" — and that sentence is the reason the row is on screen at all.
   Beside the name it wrapped both of them into columns three words wide. */
.mt-picker-list { min-width: 340px; }
.mt-picker-list .tt-picker-item {
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
}
.mt-picker-list .tt-picker-role { white-space: nowrap; }
.mt-picker-list .tt-picker-dept-name { line-height: 1.35; }
/* The two fixed answers sit under the people, with a line to say they are a
   different kind of thing: "who is this" has three shapes of answer and only
   one of them is a person. */
.mt-picker-fixed { border-top: 1px solid var(--line); color: var(--muted); }

/* --- the evidence on the row --- */
/* The row is a COMPARISON, not a verdict: two columns of the same shape, one
   line per key, a mark on each. Which key matched is never a mystery — the
   tick is on the line. The cell is `autoHeight`, because a multi-candidate row
   stacks candidates and a fixed height would clip one of them. */
/* `.match-grid`, not `.inbox-grid`: the Titles inbox wears that class too, and
   its rows are 42px with one line of text in them. Everything below is true
   only where a row is as tall as a comparison. */
.match-grid .ag-cell.cell-evidence {
  align-items: flex-start;
  padding-top: 6px;
  padding-bottom: 6px;
  line-height: 17px;
  white-space: normal;
  overflow: hidden;
}
/* Every other cell in a tall row keeps its content on the FIRST line — the
   name, the picker and the rung belong beside the TOP of the comparison, not
   floating in the middle of it. */
.match-grid .ag-cell { align-items: flex-start; padding-top: 8px; }
.match-grid .ag-cell.cell-picker { align-items: flex-start; padding-top: 5px; }
/* "The cell IS the control" was written for a 42px row (prompt 15). In a row
   as tall as a comparison it would make a 200px-tall input with its text
   floating in the middle, so here the control is a control's height and sits
   at the top, beside the first line of the evidence it answers. */
.match-grid .ag-cell.cell-picker .tt-picker { height: auto; }
.match-grid .ag-cell.cell-picker .tt-picker-input { height: 30px; }
.mt-ev { display: flex; flex-direction: column; gap: 6px; }
.mt-ev-card {
  border-left: 2px solid var(--line);
  padding: 1px 0 1px 8px;
}
/* key · mark · on record — a CONTENDER line. The left side is its own column
   in the grid (prompt 18d §1), said once per row rather than once per
   contender, so this line has one value column and not two. */
.mt-ev-line {
  display: grid;
  grid-template-columns: 54px 14px 1fr;
  gap: 0 8px;
  align-items: baseline;
  font-size: 12px;
}
/* The LEFT block has no mark, because a mark is a statement about a pair and
   there is no contender in it — so its key column lines up with the
   contenders' and the value takes the mark's 14px as well. */
.mt-self-line { grid-template-columns: 54px 1fr; }
.mt-ev-head { margin: 0 0 1px; display: flex; gap: 6px; align-items: baseline; }
.mt-self { border-left-color: var(--accent-line); }
.mt-self-name { font-weight: 600; }
/* The last job and the score close a contender block, and they are quieter
   than the five keys above them: the keys are what agreed, and these two are
   context for it. */
.mt-ev-last .mt-ev-vals, .mt-ev-score .mt-ev-vals { color: var(--muted); }
.mt-ev-score { margin-top: 2px; }
.mt-ev-kept { font-size: 12px; padding-left: 10px; }
.mt-contender + .mt-contender { margin-top: 2px; }
.mt-ev-person {
  padding: 0;
  border: 0;
  background: none;
  color: var(--accent-ink);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  text-align: left;
}
.mt-ev-person:hover { text-decoration: underline; }
.mt-ev-key { color: var(--muted); font-size: 11px; }
.mt-ev-mark { font-weight: 700; text-align: center; }
.mt-ev--agrees .mt-ev-mark { color: var(--ok); }
/* A difference is never held against a candidate — numbers change and people
   have several addresses — so it is stated, not alarmed about. */
.mt-ev--differs .mt-ev-mark { color: var(--muted-2); }
.mt-ev--absent .mt-ev-mark { color: var(--dim); }
.mt-ev-vals { display: block; min-width: 0; }
.mt-ev-val { display: inline; }
.mt-ev-val + .mt-ev-val::before { content: ' · '; color: var(--dim); }
.mt-ev-val.is-matched { font-weight: 600; color: var(--ok-ink); }
/* Dead contacts are kept for matching and never offered as a way to reach
   somebody (§5.5). On this screen they are exactly the evidence that explains
   an old number, so they are shown — struck through, so nobody dials one. */
.mt-ev-val.is-dead { text-decoration: line-through; color: var(--dim); }
.mt-ev-note { color: var(--dim); font-size: 11px; margin-left: 4px; }
.mt-ev-empty { color: var(--dim); }
.mt-ev-none { border-left-color: transparent; padding-left: 0; }

/* --- history by run (prompt 18d §5) ---
   The ladder decides silently, which is the design. Silent must not mean
   invisible: a run is a header you can open, every number in it is counted
   from the rows under it, and there is an undo on the run AND on each row. */
.mt-runs { display: flex; flex-direction: column; gap: 8px; padding: 4px 0; }
.mt-run {
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--card);
}
.mt-run-head {
  display: flex;
  gap: 10px;
  align-items: baseline;
  padding: 8px 12px;
}
.mt-run-toggle {
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink);
  font: inherit;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  white-space: nowrap;
}
.mt-run-what { flex: 1; min-width: 0; }
.mt-run-rows {
  border-top: 1px solid var(--line);
  padding: 6px 12px 10px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.mt-run-row { padding: 3px 0; }
.mt-run-row + .mt-run-row { border-top: 1px solid var(--line); }
.mt-run-row-head {
  display: flex;
  gap: 8px;
  align-items: baseline;
  font-size: 12.5px;
}
.mt-run-row-head .mt-ev-person { flex: 0 0 auto; }
.mt-run-row-head .tt-undo { margin-left: auto; }
.mt-run-row-ev { font-size: 11.5px; padding-left: 2px; }
.mt-run-row.is-undone { opacity: 0.55; }
.mt-run-row.is-undone .mt-ev-person { text-decoration: line-through; }

/* --- "this startform belongs to somebody else" (prompt 18d §3) ---
   One dialog for both places it is asked from — the Person page's Shows grid
   and the Duplicates card — because it is one question. */
/* Wider than the 460px a question needs, because this one is a LIST you read
   across: the name the row was filed under, the job, the show, and the keys
   that came with it. At 460px the job column truncated to "Crane Operator →
   …" on every line, which is exactly the column that says which row is the
   wrong one. Scoped by `:has()` so a plain confirm keeps its own width. */
.dialog:has(.mv-list) { width: min(860px, 100%); }
/* Two columns of filing histories need the room. */
.dialog:has(.mv-cols) { width: min(1100px, 100%); }
.dialog:has(.mv-cols) > .dialog-body > .tt-filter-text { width: 100%; }
.mv-list {
  max-height: 40vh;
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: 6px;
  margin: 0 0 10px;
}
.mv-sf {
  display: grid;
  grid-template-columns: 20px minmax(120px, 1fr) minmax(120px, 1.2fr)
                         minmax(90px, 0.9fr) minmax(0, 1.4fr);
  gap: 8px;
  align-items: baseline;
  padding: 5px 8px;
  font-size: 12.5px;
  cursor: pointer;
}
.mv-sf + .mv-sf { border-top: 1px solid var(--line); }
.mv-sf:hover { background: var(--hover); }
/* The name it was FILED UNDER, first and unmuted: on the pair this exists for
   it is the only thing on the row that says which startform is the wrong
   one. */
.mv-sf-name { font-weight: 600; }
.mv-sf-job, .mv-sf-keys { min-width: 0; overflow: hidden; text-overflow: ellipsis;
                          white-space: nowrap; }
.mv-target { display: flex; gap: 8px; align-items: center; margin: 0 0 8px; }
.mv-target .tt-picker { flex: 1; min-width: 0; }
.mv-target .tt-picker-input { height: 30px; }
/* The reason takes the width, because it is a sentence: "this is Epstein's
   startform, filed under Szalay in v3" does not fit in a filter box. */
.dialog:has(.mv-list) > .dialog-body > .tt-filter-text { width: 100%; }

/* Who a person has been recorded as NOT being, and why. The note is the
   memory that stops the question being asked twice (prompt 18g), so it reads
   as a sentence rather than as a field. */
.distinct-from { display: flex; gap: 6px; align-items: baseline; flex-wrap: wrap; }
.distinct-from + .distinct-from { margin-top: 2px; }

/* The GCMID, wherever a person is named on Match (prompt 18i). Small, muted,
   after the name, and the SAME everywhere — two people with one name are told
   apart by the number and nothing else. */
.gcmid {
  margin-left: 5px;
  color: var(--dim);
  font-size: 11px;
  font-weight: 400;
  font-variant-numeric: tabular-nums;
}
.mv-person { display: inline-flex; gap: 2px; align-items: baseline; }
.mv-person-name { font-weight: 600; }

/* --- a move between two people: two columns (prompt 18i) ---
   Kalman: *"somehow it should be more intuitive."* A list with a picker over
   it was the wrong shape for a question that is always about two people. */
.mv-cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin: 0 0 10px;
}
.mv-col {
  border: 1px solid var(--line);
  border-radius: 6px;
  min-width: 0;
  overflow: hidden;
}
.mv-col-head {
  display: flex;
  gap: 8px;
  align-items: baseline;
  justify-content: space-between;
  padding: 6px 8px;
  background: var(--card-2);
  border-bottom: 1px solid var(--line);
}
.mv-col-list { max-height: 38vh; overflow-y: auto; }
.mv-col-sf {
  display: grid;
  grid-template-columns: 18px minmax(90px, 1fr) minmax(90px, 1.1fr) minmax(70px, 0.8fr);
  gap: 6px;
  align-items: baseline;
  padding: 4px 8px;
  font-size: 12px;
  cursor: pointer;
}
.mv-col-sf + .mv-col-sf { border-top: 1px solid var(--line); }
.mv-col-sf:hover { background: var(--hover); }
.mv-col-sf > span { min-width: 0; overflow: hidden; text-overflow: ellipsis;
                    white-space: nowrap; }
/* The keys go on their own line: they are corroboration, and squeezing them
   into a fourth column left nothing legible in any of them. */
.mv-col-sf .mv-sf-keys { grid-column: 2 / -1; color: var(--dim); font-size: 11px; }
/* The row that does not belong in its column IS the reason the dialog is
   open, so it is the one thing on the screen that is coloured. */
.mv-col-sf.is-odd { background: var(--warn-dim); }
.mv-col-sf.is-odd:hover { background: var(--warn-dim); }
.mv-odd-why {
  grid-column: 2 / -1;
  color: var(--warn-ink);
  font-size: 11.5px;
  font-weight: 600;
  white-space: normal !important;
}
.mv-directions {
  display: flex;
  gap: 8px;
  justify-content: space-between;
  margin: 0 0 8px;
}
.mv-directions button:disabled { opacity: 0.45; cursor: default; }
/* How a startform came to be on this person, on the Compare page's open
   columns (prompt 18j §5). `v3_map` is a filing nobody checked. */
.mv-sf-filed {
  grid-column: 2 / -1;
  color: var(--dim);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}
/* Read-only columns: the same rows, no checkbox, and no pointer promising
   something to click (Compare, and the pair card's Show records). */
.mv-col-sf:not(label) { cursor: default; grid-template-columns:
  0 minmax(90px, 1fr) minmax(90px, 1.1fr) minmax(70px, 0.8fr); }
.mv-col-sf:not(label):hover { background: none; }
.mv-col-sf:not(label).is-odd:hover { background: var(--warn-dim); }

/* --- Compare: two people, chosen by hand (prompt 18j §5) --- */
.cmp-pickers { max-width: 720px; }
.cmp-pickers .zone-label { white-space: nowrap; }
/* The records under a pair card, behind Show records. Taller than the
   dialog's: on this page they are the point, not a step. */
.sus-records { margin: 8px 0 4px; }
.sus-records .mv-col-list { max-height: 46vh; }

/* Where a person came from, on the Identity card. */
.created-by { display: flex; gap: 8px; align-items: baseline; }
.run-link { color: var(--accent-ink); }

/* --- the five marks as one column (prompt 18l §3) ---
   Sortable, so "name ✓ first" is a click. Tabular so the five glyphs line up
   down the column and a row that agrees on everything is a shape you can see
   without reading it. */
.mt-marks { display: inline-flex; gap: 2px; font-variant-numeric: tabular-nums; }
.mt-mark { width: 12px; text-align: center; font-size: 12px; }
.mt-mark--agrees { color: var(--ok-ink); }
.mt-mark--differs { color: var(--muted); }
.mt-mark--absent { color: var(--dim); }

/* --- Ready, and the rung that proposed it (prompt 18j) ---
   The ladder no longer writes what it can answer: the row says which rung
   answered it and that it is Ready, and one keystroke or one bulk button
   writes it. A word, not a colour: this column is read, not scanned. */
.mt-why { display: flex; flex-direction: column; gap: 3px; align-items: flex-start; }
.mt-ready { align-self: flex-start; background: var(--ok-dim); color: var(--ok-ink); }
.mt-upgrade { font-size: 11px; white-space: normal; line-height: 1.25; }

/* --- the inbox row's own actions (prompt 18g) ---
   Kalman: *"the decide/match buttons/messages are not that intuitive."* So
   `This one` sits on the contender it answers, and `New person` / `Can't
   tell` on the row — a tick that meant "yes, that one" and a button at the
   top that meant "write it" were two gestures for one sentence. */
.mt-ev-pick {
  margin-left: auto;
  padding: 1px 8px;
  font-size: 11.5px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--card-2);
  color: var(--ink);
  font-family: inherit;
  cursor: pointer;
  white-space: nowrap;
}
.mt-ev-pick:hover { border-color: var(--accent); background: var(--accent-dim); }
.mt-contender:first-child .mt-ev-pick { border-color: var(--accent-line); }
.mt-row-actions {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 4px;
  width: 100%;
  min-width: 0;
}
.mt-row-actions > button { white-space: nowrap; }
.mt-row-new { font-weight: 600; }
/* A link, not a button: `Somebody else…` is the rare path — nine rows in ten
   are answered by a button on a contender — and it must not read as one of
   the three that are. */
.btn-link {
  padding: 0;
  border: 0;
  background: none;
  color: var(--accent-ink);
  font: inherit;
  font-size: 12px;
  text-align: left;
  cursor: pointer;
}
.btn-link:hover { text-decoration: underline; }
.mt-row-actions .tt-picker { margin-top: 2px; }

/* --- the Suspicious lens (prompt 18e) ---
   Five rules over one union view, one lens at a time. The card shapes all
   reuse `.mt-dupe` and `.mt-card` — prompt 18's Duplicates card IS one of the
   five, and a health view whose five rules looked like five different screens
   would be five things to learn. */
.sus-body {
  display: grid;
  grid-template-columns: minmax(220px, 320px) minmax(0, 1fr);
  gap: 14px;
  padding: 0 14px 12px;
}
.sus-side { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.sus-sfs { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sus-sfs h4 { margin: 0 0 4px; }
/* One startform on one line, and the name it was FILED UNDER first: on a
   misfile that is the only thing on the row that says which one is wrong. */
.sus-sf {
  display: grid;
  grid-template-columns: minmax(110px, 1.1fr) minmax(110px, 1.3fr)
                         minmax(80px, 0.9fr) minmax(0, 0.8fr) auto;
  gap: 8px;
  align-items: baseline;
  padding: 3px 0;
  font-size: 12.5px;
}
.sus-sf + .sus-sf { border-top: 1px solid var(--line); }
.sus-sf > span { min-width: 0; overflow: hidden; text-overflow: ellipsis;
                 white-space: nowrap; }
.sus-sf-name { font-weight: 600; }
.sus-sf button { padding: 2px 8px; font-size: 12px; }
/* Why this startform is on this person (prompt 22). Under the row and across
   all its columns, because these lines are sentences and wrap. */
.sus-ev { grid-column: 1 / -1; display: flex; flex-direction: column; gap: 1px;
          font-size: 12px; color: var(--muted); }
.sus-ev-line { display: flex; flex-wrap: wrap; column-gap: 12px; row-gap: 1px; }
.sus-ev-bit { display: inline-flex; gap: 4px; align-items: baseline;
              min-width: 0; overflow-wrap: anywhere; }
.sus-ev .mt-mark { width: auto; }
.sus-ev-gone { color: var(--muted); }
.sus-ev-filer { font-style: italic; }
.sus-ev .btn-link { padding: 0; font-size: 12px; }
/* Probably one person (prompt 24): where two names part, and what both hold. */
.sus-diff { display: flex; flex-wrap: wrap; gap: 4px 14px; align-items: baseline;
            margin: 0 0 10px; }
.sus-diff-word { font-weight: 600; }
.sus-diff mark, .sus-diff-word mark { background: var(--warn-dim);
                                       color: var(--warn-ink); padding: 0 1px;
                                       border-radius: 2px; }
.sus-shared-line { display: flex; flex-wrap: wrap; gap: 4px 12px; margin: 8px 0 0;
                   font-size: 12.5px; }
.sus-shared { background: var(--ok-dim); color: var(--ok-ink); padding: 0 4px;
              border-radius: 3px; }
/* The question a card asks, in a line above the answers (prompt 23). */
.sus-question { margin: 12px 0 0; font-weight: 600; }
.mt-dupe-answers .mt-dupe-actions { margin-top: 8px; }
.sus-reasons { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 8px; }
/* Who else could it be. */
.sus-others { margin-top: 10px; }
.sus-others h4 { margin: 0 0 4px; }
.sus-others-list { display: grid; gap: 8px;
                   grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
.sus-other { min-width: 0; }
.sus-other-why { margin: 2px 0 6px; font-size: 12px; }
.sus-other button { padding: 2px 8px; font-size: 12px; }
.sus-others-none { margin: 0; font-size: 12.5px; }

/* --- duplicates: two people, side by side --- */
.mt-dupes { display: flex; flex-direction: column; gap: 14px; padding: 4px 0; }
.mt-dupe {
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--card);
  padding: 12px 14px;
}
.mt-dupe-head {
  display: flex; align-items: center; gap: 10px; margin: 0 0 10px;
  flex-wrap: wrap;
}
.mt-dupe-why { font-size: 12.5px; }
/* Side by side, and the same width: a card that was wider would read as the
   answer, and which of the two survives is exactly what has not been decided
   yet. */
.mt-card-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}
.mt-card {
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--card-2);
  padding: 10px 12px;
  cursor: pointer;
  position: relative;
}
.mt-card:hover { border-color: var(--accent-line); }
.mt-card h4 { margin: 0 0 2px; font-size: 14.5px; font-weight: 600; }
.mt-card-id { font-size: 12px; margin: 0 0 6px; }
.mt-card-tag {
  position: absolute; top: 8px; right: 10px;
  font-size: 11px; font-weight: 700; color: var(--dim);
}
.mt-card-line {
  display: grid;
  grid-template-columns: 62px 1fr;
  gap: 8px;
  font-size: 12.5px;
  padding: 1px 0;
}
.mt-dupe-actions {
  display: flex; align-items: center; gap: 8px; margin: 12px 0 0;
  flex-wrap: wrap;
}

@media (max-width: 720px) {
  .mt-card-pair { grid-template-columns: 1fr; }
}

/* ---------------------------------------------------------------------------
   The Roles workbench (prompt 19)
   ---------------------------------------------------------------------------

   Two panes, and the tree is NOT a grid. A grid gives you resizable columns and
   a row model; what this needs is a drag handle, an indent that means something,
   and a row you can drop another row onto — none of which AG Grid is for.

   Every colour is a token, like everywhere else outside the two palette blocks
   and the contact sheet. */

.rw-panes {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(420px, 1.6fr) minmax(320px, 1fr);
  gap: 0;
  border-top: 1px solid var(--line);
}

.rw-tree {
  overflow: auto;
  border-right: 1px solid var(--line);
  padding: 0 0 40px;
}

.rw-head,
.rw-node {
  display: grid;
  /* name · kind · records · people · last used. The name flexes and nothing
     else does, so the three numbers line up down the whole tree however deep
     it goes. */
  grid-template-columns: 18px minmax(0, 1fr) 40px 72px 62px 86px;
  align-items: center;
  gap: 8px;
  padding: 4px 12px;
  font-size: 13px;
  border-bottom: 1px solid var(--line);
}
.rw-head { position: sticky; top: 0; background: var(--card); z-index: 2;
           font-size: 11.5px; text-transform: uppercase;
           letter-spacing: 0.04em; }

.rw-node { cursor: pointer; }
/* The indent is the tree. One variable, set on the row, so a nested department
   needs no extra rule. */
.rw-node .rw-name { padding-left: calc(var(--depth, 0) * 16px); }
.rw-name { display: flex; align-items: center; gap: 4px; }
.rw-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The twisty keeps its width when there is nothing to twist, so a leaf's name
   starts where its siblings' do. */
.rw-twist {
  flex: 0 0 12px; width: 12px; text-align: left;
  border: none; background: none; padding: 0; line-height: 1;
  font-size: 10px; color: var(--dim); cursor: pointer;
}
.rw-twist.is-blank { cursor: default; }
.rw-node:hover { background: var(--hover); }
.rw-node:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* A bar, not just a tint. `--accent-dim` is 6% in the light palette, which is
   the right weight for a hovered row and far too quiet for "this is the node
   the pane on the right is editing" — on the office screen it was invisible.
   Inset rather than a border so nothing moves by a pixel, and declared BEFORE
   the drop rules so a drop indicator wins over it. */
.rw-node.is-selected {
  background: var(--accent-dim);
  box-shadow: inset 3px 0 0 0 var(--accent);
}
.rw-node.is-department .rw-name { font-weight: 600; }
.rw-node.is-retired { opacity: 0.55; }
.rw-node.is-retired .rw-name { text-decoration: line-through; }

.rw-grip { color: var(--dim); cursor: grab; font-size: 12px; user-select: none; }
.rw-num { text-align: right; font-variant-numeric: tabular-nums; }
.rw-when { text-align: right; font-variant-numeric: tabular-nums;
           font-size: 12px; }
.rw-successor { font-size: 12px; }
.rw-kind { font-size: 11px; }

/* Where the row would land. `inset` box-shadows rather than borders, because a
   border would move the row by a pixel as the pointer crosses it. */
.rw-node.is-drop-before { box-shadow: inset 0 2px 0 0 var(--accent); }
.rw-node.is-drop-after  { box-shadow: inset 0 -2px 0 0 var(--accent); }
.rw-node.is-drop-into   { box-shadow: inset 0 0 0 2px var(--accent); }

.rw-detail { overflow: auto; padding: 12px 14px 40px; }
.rw-card { display: flex; flex-direction: column; gap: 8px; }
.rw-card-head { display: flex; align-items: center; gap: 8px; }
.rw-card p { margin: 0; font-size: 13px; }

.rw-rename {
  flex: 1 1 0;
  min-width: 0;
  font-size: 15px;
  font-weight: 600;
  padding: 5px 8px;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: var(--ink);
}
/* Editable at rest without a fill, the same ruling prompt 16 settled for the
   Titles picker: a tint here would say "editable" about the only field on the
   card that is. */
.rw-rename:hover { border-color: var(--line); }
.rw-rename:focus { border-color: var(--accent); outline: none;
                   background: var(--card-2); }

.rw-note {
  width: 100%;
  font: inherit;
  font-size: 12.5px;
  padding: 5px 8px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--card-2);
  color: var(--ink);
  resize: vertical;
}

.rw-usage {
  display: flex; flex-direction: column; gap: 3px;
  padding: 8px 0; margin: 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  font-size: 12.5px;
}
.rw-usage-link { color: var(--accent-ink); text-decoration: none; }
.rw-usage-link:hover { text-decoration: underline; }

.rw-acts { display: flex; flex-wrap: wrap; gap: 6px; }

.rw-form { display: flex; align-items: center; gap: 8px; margin: 0 0 10px;
           flex-wrap: wrap; }
.rw-form input[type="text"] { flex: 1 1 160px; min-width: 0; }
.rw-select {
  font: inherit; font-size: 13px; padding: 4px 6px;
  border: 1px solid var(--line); border-radius: 6px;
  background: var(--card-2); color: var(--ink);
  max-width: 100%;
}
.rw-check { display: flex; align-items: center; gap: 6px; font-size: 13px;
            margin: 0 0 6px; }

/* --- the split dialog --- */
.rw-split-rules { max-height: 280px; overflow-y: auto;
                  border-top: 1px solid var(--line); }

/* STACKED, not two columns. The meta line is "13 shows · 2 departments · 13
   rules · 30 records · 24 people" — sixty characters that will not share a row
   with a core title inside a dialog, and side by side the two overlapped on
   the first real split. The name gets the row; the sentence about it gets the
   next one. */
.rw-split-group,
.rw-split-rule {
  display: grid;
  gap: 0 8px;
  padding: 4px 0;
  border-bottom: 1px solid var(--line);
  font-size: 12.5px;
}
.rw-split-group {
  grid-template-columns: 18px minmax(0, 1fr);
  grid-template-areas: "toggle name" ".      meta";
  align-items: center;
}
.rw-split-group > .rw-split-toggle { grid-area: toggle; }
.rw-split-group > label { grid-area: name; }
.rw-split-group > .rw-split-meta { grid-area: meta; }
.rw-split-rule {
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  padding-left: 26px;
}
.rw-split-rule span:last-child { text-align: right; white-space: nowrap; }
.rw-split-group label,
.rw-split-rule label { display: flex; align-items: center; gap: 6px;
                       min-width: 0; }
.rw-split-group label span,
.rw-split-rule label span { overflow: hidden; text-overflow: ellipsis;
                            white-space: nowrap; }
.rw-split-toggle {
  border: none; background: none; color: var(--dim); cursor: pointer;
  font-size: 11px; padding: 0; line-height: 1;
}
.rw-split-meta { font-size: 12px; line-height: 1.4; }

/* --- split, level two --- */
.rw-records { height: 46vh; min-height: 260px; }
.rw-records-bar { display: flex; align-items: center; gap: 8px;
                  margin: 0 0 8px; flex-wrap: wrap; }
/* The count on the left, then the free text, then the two acts on the right —
   the same reading order as every other action zone in this app. */
.rw-records-bar > span:first-child { margin-right: auto; }
.rw-records-bar .rw-filter { max-width: 180px; }

@media (max-width: 980px) {
  .rw-panes { grid-template-columns: 1fr; }
  .rw-tree { border-right: none; border-bottom: 1px solid var(--line);
             max-height: 50vh; }
}

@media print {
  /* Everything else goes. `body.printing` rather than a bare @media rule so a
     browser's own print of the app (Cmd-P on the grid) is untouched. */
  body.printing > *:not(#print-sheet) { display: none !important; }
  body.printing #print-sheet { display: block; }

  @page { size: A4 portrait; margin: 14mm; }

  /* Paper is white and ink is black, whatever the screen was set to. A dark
     theme printed is a page of toner and an unreadable sheet. */
  html, body, body.printing #print-sheet {
    background: #fff !important;
    color: #000 !important;
  }
  body.printing #print-sheet {
    font: 10pt/1.35 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          Helvetica, Arial, sans-serif;
  }
}

#print-sheet .print-head { border-bottom: 1.5pt solid #000; padding-bottom: 4pt; }
#print-sheet h1 { margin: 0; font-size: 14pt; font-weight: 700; }
#print-sheet .print-sub {
  display: flex; gap: 12pt; font-size: 9pt; color: #444; margin-top: 2pt;
}
#print-sheet .print-count { margin-left: auto; }
#print-sheet .print-date { font-variant-numeric: tabular-nums; }

#print-sheet .print-group { break-inside: auto; }
#print-sheet .print-dept-head {
  margin: 10pt 0 3pt;
  font-size: 10pt;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  border-bottom: 0.5pt solid #999;
  padding-bottom: 1pt;
  /* A heading at the foot of a page with its first row overleaf is a heading
     nobody reads. */
  break-after: avoid;
  break-inside: avoid;
}

/* The Crew tab's Grouped view (prompt 25): a role under its department. */
#print-sheet .print-role-head {
  margin: 6pt 0 1pt;
  font-size: 9.5pt;
  font-weight: 700;
  color: #333;
  break-after: avoid;
  break-inside: avoid;
}

/* A person is one line and must not be split across a page. */
#print-sheet .print-row {
  display: grid;
  /* name · roles · dept · phone · email · show. The last column holds a show
     name and a year and must not wrap them apart. */
  grid-template-columns: 19% 20% 13% 15% 21% 12%;
  gap: 0 6pt;
  padding: 2.4pt 0;
  border-bottom: 0.25pt solid #ddd;
  break-inside: avoid;
  page-break-inside: avoid;
}
#print-sheet .print-name { font-weight: 700; }
#print-sheet .print-nick { font-weight: 400; color: #555; }
#print-sheet .print-roles,
#print-sheet .print-dept,
#print-sheet .print-show { color: #333; }
#print-sheet .print-phone { font-variant-numeric: tabular-nums; white-space: nowrap; }
#print-sheet .print-email { word-break: break-all; }
#print-sheet .print-empty { padding: 12pt 0; color: #444; }
