/* Hyroo — application styles
   Layered on top of tokens.css; mirrors the prototype's components in plain CSS. */

html, body { height: 100%; }
body { background: var(--bg); color: var(--text); font-size: 13px; }

/* The `hidden` attribute is toggled in JS to show/hide popups (modals, the
   bulk bar, the tweaks panel, notification + résumé panels). Several of those
   components set `display: flex/block` on their class, which has equal
   specificity to the user-agent `[hidden] { display: none }` rule but wins on
   cascade order — leaving them stuck visible. This `!important` backstop makes
   `hidden` authoritative everywhere, so no component needs its own guard. */
[hidden] { display: none !important; }

a { color: inherit; text-decoration: none; }
a:hover { text-decoration: none; }
.hover-underline:hover { text-decoration: underline; }

/* ---------- App shell ---------- */
.app-shell { display: grid; grid-template-columns: 212px 1fr; min-height: 100vh; }

.sidebar {
  background: var(--surface-2);
  border-right: 1px solid var(--border);
  padding: 14px 10px;
  display: flex; flex-direction: column; gap: 14px;
  position: sticky; top: 0; height: 100vh;
  overflow-y: auto;
}

.sb-brand {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 8px; border-radius: var(--r-md);
  background: var(--surface);
  border: 1px solid var(--border);
  cursor: pointer;
}
.sb-brand:hover { border-color: var(--border-2); }
.sb-brand .x { width: 22px; height: 22px; border-radius: 5px; background: var(--border-strong); color: var(--surface); display: grid; place-items: center; font-weight: 700; font-size: 12px; }
/* The Hyroo mark is an image with its own colours: no tile behind it. */
.sb-brand img.x { background: none; object-fit: contain; }
.sb-brand .name { font-weight: 600; font-size: 12.5px; }
.sb-brand .role { font-size: 11px; color: var(--muted); }
.sb-brand select { background: transparent; border: 0; font-size: 11px; color: var(--muted); padding: 0; }

.sb-search {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 10px; border-radius: var(--r-md);
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--muted);
  font-size: 12px;
  cursor: pointer;
}
.sb-search:hover { border-color: var(--border-2); }
.sb-search .kbd { margin-left: auto; }

.sb-section { display: flex; flex-direction: column; gap: 2px; }
.sb-label { font-size: 10.5px; color: var(--muted); padding: 6px 8px 2px; text-transform: uppercase; letter-spacing: .06em; font-weight: 600; }

.nav-item {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 8px; border-radius: var(--r-md);
  color: var(--text-soft); font-size: 12.5px;
  cursor: pointer; border: 1px solid transparent;
}
.nav-item:hover { background: var(--surface-3); color: var(--text); }
.nav-item.active { background: var(--surface); color: var(--text); border-color: var(--border); box-shadow: var(--shadow-sm); }
.nav-item .count { margin-left: auto; font-size: 11px; color: var(--muted); }
.nav-item .icon { width: 14px; height: 14px; color: var(--muted); }
.nav-item.active .icon { color: var(--signal); }

.sb-foot { margin-top: auto; padding: 8px; display: flex; align-items: center; gap: 6px; }

/* ---------- Topbar ---------- */
.topbar {
  height: 44px; padding: 0 16px;
  display: flex; align-items: center; gap: 12px;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 10;
}
.crumbs { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--muted); }
.crumbs .crumb-cur { color: var(--text); font-weight: 500; }
.crumbs .sep { color: var(--muted-2); }
.topbar .spacer { flex: 1; }

/* ---------- Main content ---------- */
.main { display: flex; flex-direction: column; min-width: 0; }
.content { flex: 1; min-height: 0; overflow: auto; }
.content.padded { padding: 24px; }
.content.wide-max { max-width: 1180px; margin: 0 auto; padding: 24px; width: 100%; }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; gap: 6px;
  height: 28px; padding: 0 10px;
  border-radius: var(--r-md); border: 1px solid var(--border);
  background: var(--surface); color: var(--text);
  font-size: 12.5px; font-weight: 500;
  white-space: nowrap; flex-shrink: 0;
  cursor: pointer;
  transition: background .12s, border-color .12s, box-shadow .12s;
}
.btn:hover { border-color: var(--border-2); }
.btn.sm  { height: 24px; padding: 0 8px;  font-size: 11.5px; }
.btn.lg  { height: 34px; padding: 0 14px; font-size: 13px; }
.btn.icon { width: 28px; padding: 0; justify-content: center; }
.btn.icon-sm { width: 24px; height: 24px; padding: 0; justify-content: center; }
.btn.primary { background: var(--border-strong); color: var(--surface); border-color: var(--border-strong); }
.btn.primary:hover { opacity: .92; }
.btn.ai { background: var(--ai); color: #fff; border-color: var(--ai); }
.btn.ai:hover { opacity: .92; }
.btn.danger { background: var(--danger); color: #fff; border-color: var(--danger); }
.btn.ghost { background: transparent; border-color: transparent; color: var(--text-soft); }
.btn.ghost:hover { background: var(--surface-3); }
/* `danger ghost` = destructive, low emphasis. Needed as an explicit pair rule: `.ghost` is declared
   after `.danger` at equal specificity, so on its own it zeroes the danger background AND repaints the
   text --text-soft — leaving a "Delete"/"Reject" button visually identical to a neutral one. The red
   has to come back somewhere, and a 3-class selector is the only thing that outranks `.btn.ghost`. */
.btn.danger.ghost { background: transparent; border-color: transparent; color: var(--danger); }
.btn.danger.ghost:hover { background: var(--danger-bg); }
.btn:disabled { opacity: .5; cursor: not-allowed; }

/* ---------- Cards & layout helpers ---------- */
.card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg); }
.card.pad { padding: 16px; }
/* `pad` reads as a utility and is used as one — on a <summary>, a form inside a card, an empty-state
   message. Until this rule existed only `.card.pad` matched, so every one of those sites silently got
   no padding at all and rendered edge-to-edge: a styling bug with no failing selector to find. */
.pad { padding: 16px; }
.surface-2 { background: var(--surface-2); }
.row { display: flex; align-items: center; gap: 8px; }
.col { display: flex; flex-direction: column; gap: 8px; }
.gap-2 { gap: 2px; } .gap-4 { gap: 4px; } .gap-6 { gap: 6px; } .gap-8 { gap: 8px; }
.gap-12 { gap: 12px; } .gap-16 { gap: 16px; } .gap-20 { gap: 20px; } .gap-24 { gap: 24px; }
.flex-1 { flex: 1; min-width: 0; }
.justify-between { justify-content: space-between; }
.text-muted { color: var(--muted); }
.text-soft { color: var(--text-soft); }
.text-sm { font-size: 12px; }
.text-xs { font-size: 11px; }
.text-lg { font-size: 15px; }
.text-xl { font-size: 18px; }
.text-h1 { font-size: 20px; font-weight: 600; letter-spacing: -0.015em; }
.text-h2 { font-size: 17px; font-weight: 600; letter-spacing: -0.015em; }
.weight-500 { font-weight: 500; }
.weight-600 { font-weight: 600; }
.weight-700 { font-weight: 700; }
.upper { text-transform: uppercase; letter-spacing: .06em; font-size: 10.5px; color: var(--muted); font-weight: 600; }
.divider { width: 1px; height: 14px; background: var(--border-2); }
hr.divider { width: 100%; height: 1px; background: var(--border); border: 0; margin: 12px 0; }

/* ---------- Avatar ---------- */
.avatar {
  width: 24px; height: 24px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  color: #fff; font-weight: 600; font-size: 10px;
  background: var(--muted);
  flex: none;
  letter-spacing: 0;
}
.avatar.ring { box-shadow: 0 0 0 1.5px var(--surface), 0 0 0 2.5px currentColor; }
.avatar-stack { display: inline-flex; }
.avatar-stack .avatar:not(:first-child) { margin-left: -6px; box-shadow: 0 0 0 1.5px var(--surface); }

/* ---------- Badge / pill ---------- */
.badge {
  display: inline-flex; align-items: center; gap: 4px;
  height: 18px; padding: 0 6px; border-radius: 999px;
  font-size: 10.5px; font-weight: 500; color: var(--text-soft);
  background: var(--surface-2); border: 1px solid var(--border);
  /* Pills are fixed-height labels: never let a flex parent squeeze them so the
     text wraps inside the 18px chip (e.g. "AI summary" / "AI fit 92%"). */
  white-space: nowrap; flex-shrink: 0;
}
.badge .dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
.badge.signal  { color: var(--signal);  background: var(--signal-bg);  border-color: var(--signal-soft); }
.badge.success { color: var(--success); background: var(--success-bg); border-color: transparent; }
.badge.warn    { color: var(--warn);    background: var(--warn-bg);    border-color: transparent; }
.badge.danger  { color: var(--danger);  background: var(--danger-bg);  border-color: transparent; }
.badge.ai      { color: var(--ai);      background: var(--ai-bg);      border-color: var(--ai-soft); }

.tag {
  display: inline-flex; align-items: center;
  height: 18px; padding: 0 6px; border-radius: 4px;
  font-size: 10.5px; color: var(--muted);
  border: 1px solid var(--border);
}

/* ---------- Stage chip ---------- */
.stage-chip {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 10px; text-transform: uppercase; letter-spacing: .06em; font-weight: 600;
  color: var(--text-soft);
}
.stage-chip .dot { width: 6px; height: 6px; border-radius: 50%; }

/* ---------- Match score ---------- */
.match-score { position: relative; display: inline-grid; place-items: center; }
.match-score svg { display: block; }
/* P0-F: a number no engine produced (the intake estimate) is drawn muted, so it cannot pass for a score. */
.match-score.est { opacity: .7; }
.match-score .ms-val {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-family: "Geist Mono", ui-monospace, monospace;
  font-weight: 600; font-size: 10px; font-variant-numeric: tabular-nums;
}

/* ---------- Table ---------- */
.table { width: 100%; border-collapse: collapse; }
.table th, .table td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border); }
.table th { font-weight: 600; font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; background: var(--surface); position: sticky; top: 0; }
.table tbody tr { cursor: pointer; }
.table tbody tr:hover { background: var(--surface-2); }

/* Brief one-shot highlight for a row/card whose AI score was just refreshed
   (set via TempData after Re-score). Only `from` is specified, so the row fades
   from the AI accent back to its own natural background — which makes the same
   class work on a transparent table row, a surfaced kanban card, and a split
   thread-row alike. `prefers-reduced-motion` opts out of the animation. */
@keyframes row-flash { from { background-color: var(--ai-soft); } }
.row-flash { animation: row-flash 1.8s ease-out; }
@media (prefers-reduced-motion: reduce) { .row-flash { animation: none; } }
.table .num { text-align: right; font-family: "Geist Mono", ui-monospace, monospace; font-variant-numeric: tabular-nums; }

/* ---------- Inputs ---------- */
.input, .textarea, .select {
  display: block; width: 100%;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r-md);
  padding: 7px 10px; font-size: 13px;
}
.input:focus, .textarea:focus, .select:focus { border-color: var(--signal); outline: none; box-shadow: 0 0 0 3px var(--signal-bg); }
.textarea { resize: vertical; min-height: 72px; }
.field-label { display: block; font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; font-weight: 600; color: var(--muted); margin-bottom: 4px; }

/* ---------- KPI ---------- */
.kpi-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; }
.kpi { padding: 14px; }
.kpi .label { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; font-weight: 600; }
.kpi .val { font-family: "Geist Mono", ui-monospace, monospace; font-size: 24px; font-weight: 600; font-variant-numeric: tabular-nums; margin-top: 4px; letter-spacing: -0.01em; }
.kpi .delta.success { color: var(--success); font-size: 11px; }
.kpi .delta.danger  { color: var(--danger);  font-size: 11px; }
.kpi .sparkline { height: 26px; width: 100%; margin-top: 8px; overflow: visible; display: block; }
.kpi .sparkline polyline { transition: stroke-width .15s ease; }
.kpi .sparkline .spark-dot { opacity: 0; transition: opacity .15s ease; }
.kpi:hover .sparkline polyline { stroke-width: 2; }
.kpi:hover .sparkline .spark-dot { opacity: 1; }

/* ---------- Pipeline kanban ---------- */
.pipeline-funnel { display: flex; align-items: stretch; gap: 6px; padding: 12px 0; }
.pipeline-funnel .stage-card {
  flex: 1; padding: 8px 10px; border-radius: var(--r-md);
  background: var(--surface-2);
  border: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 2px; min-width: 80px;
}
.pipeline-funnel .stage-card .name { font-size: 11px; color: var(--muted); }
.pipeline-funnel .stage-card .cnt { font-family: "Geist Mono", ui-monospace, monospace; font-size: 18px; font-weight: 600; }

.kanban { display: grid; grid-auto-flow: column; grid-auto-columns: 280px; gap: 12px; padding-bottom: 24px; overflow-x: auto; }
.kanban-col {
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-lg);
  display: flex; flex-direction: column; min-height: 200px;
}
.kanban-col .col-head { padding: 10px 12px; display: flex; align-items: center; gap: 8px; border-bottom: 1px solid var(--border); }
.kanban-col .col-head .cnt { margin-left: auto; font-size: 11px; color: var(--muted); font-family: "Geist Mono", ui-monospace, monospace; }
.kanban-col .col-body { padding: 8px; display: flex; flex-direction: column; gap: 8px; flex: 1; min-height: 80px; }
.kanban-col.dragover .col-body { background: var(--signal-bg); border-radius: var(--r-md); }
.kanban-card {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-md);
  padding: 10px; cursor: grab; display: flex; flex-direction: column; gap: 6px;
  box-shadow: var(--shadow-sm);
}
.kanban-card:hover { border-color: var(--border-2); }
.kanban-card.dragging { opacity: .35; }
/* Tilted, shadowed floating drag image (prototype parity). app.js clones the
   card with this class, positions it off-screen, hands it to setDragImage, then
   removes it. .kanban-card.drag-ghost out-specifies the base card's box-shadow. */
.kanban-card.drag-ghost {
  position: fixed; top: -1000px; left: -1000px; margin: 0; pointer-events: none;
  transform: rotate(3deg); box-shadow: var(--shadow-lg);
  border-color: var(--signal-soft); opacity: 1;
}
.kanban-card.focused {
  border-color: var(--signal);
  box-shadow: 0 0 0 2px var(--signal-bg), var(--shadow-sm);
}
.kanban-card .head { display: flex; align-items: center; gap: 6px; }
.kanban-card .name { font-weight: 600; font-size: 12.5px; }
.kanban-card .meta { color: var(--muted); font-size: 11px; display: flex; align-items: center; gap: 8px; }

/* ---------- Drawer ---------- */
.drawer-backdrop {
  position: fixed; inset: 0; background: rgba(15,15,20,.32);
  backdrop-filter: blur(2px); z-index: 50;
}
.drawer {
  position: fixed; top: 0; right: 0; bottom: 0; width: 540px;
  background: var(--surface); border-left: 1px solid var(--border);
  box-shadow: var(--shadow-lg); z-index: 51;
  display: flex; flex-direction: column;
}
.drawer .drawer-head { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 8px; }
.drawer .drawer-body { padding: 18px; overflow: auto; flex: 1; }

/* Transient notice for stubbed actions (see [data-stub] in app.js). */
.toast {
  position: fixed; bottom: 20px; left: 50%; z-index: 60;
  transform: translateX(-50%) translateY(8px);
  background: var(--text); color: var(--surface);
  padding: 10px 16px; border-radius: var(--r-md); font-size: 13px;
  box-shadow: var(--shadow-lg); pointer-events: none;
  opacity: 0; transition: opacity .2s ease, transform .2s ease;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ---------- Sourcing ---------- */
.ai-search-bar {
  background: var(--ai-bg); border: 1px solid var(--ai-soft);
  border-radius: var(--r-lg); padding: 12px 14px;
  display: flex; flex-direction: column; gap: 10px;
}
.ai-search-bar .ai-row { display: flex; align-items: flex-start; gap: 10px; }
.ai-search-bar textarea { background: transparent; border: 0; resize: none; font-size: 14px; flex: 1; color: var(--text); outline: none; }
.criteria-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.criteria-chip { background: var(--surface); border: 1px solid var(--ai-soft); padding: 3px 8px; border-radius: 999px; font-size: 11.5px; color: var(--text-soft); display: inline-flex; gap: 6px; align-items: center; }
.criteria-chip .x { color: var(--muted); cursor: pointer; }

.facet-rail { display: flex; flex-direction: column; gap: 16px; padding: 12px; border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--surface); }
.facet-group { display: flex; flex-direction: column; gap: 6px; }
.facet-group .facet-label { font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; font-weight: 600; color: var(--muted); }
.facet-item { display: flex; align-items: center; gap: 8px; font-size: 12.5px; padding: 4px 0; }
.facet-item .cnt { margin-left: auto; font-size: 11px; color: var(--muted); }

.sourcing-row {
  display: grid;
  grid-template-columns: 26px 36px 1fr auto auto;
  gap: 12px; align-items: center;
  padding: 12px 14px; border-bottom: 1px solid var(--border);
  cursor: pointer; background: var(--surface);
}
.sourcing-row:hover { background: var(--surface-2); }
.sourcing-row .actions { display: flex; align-items: center; gap: 6px; }
.sourcing-row .why { grid-column: 3 / span 3; margin-top: 8px; padding-top: 8px; border-top: 1px dashed var(--border); display: none; }
.sourcing-row.expanded .why { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
/* Natural-language search results reuse .sourcing-row's chrome (hover, padding,
   borders) but have a simpler 3-cell shape — avatar · content · score — with no
   bulk-select checkbox or trailing actions column. Override the 5-track grid so
   the content cell gets the room it needs instead of collapsing into the prospect
   grid's 36px avatar track (which crushed the role text to one word per line). */
.nl-search-panel .sourcing-row { grid-template-columns: 36px 1fr auto; }

/* Talent-pool rows + hover "peek" (candidature explanation vs the target job) */
.pool-row .pool-dot { color: var(--ai); font-size: 14px; text-align: center; }
.peek { position: relative; display: flex; align-items: center; }
.peek-pop {
  position: absolute; right: 0; top: calc(100% + 6px);
  width: 280px; z-index: 40; text-align: left;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-md); box-shadow: 0 10px 28px rgba(0,0,0,.14);
  padding: 10px 12px;
  opacity: 0; visibility: hidden; transform: translateY(-4px);
  transition: opacity .12s ease, transform .12s ease; pointer-events: none;
}
.peek:hover .peek-pop, .peek:focus-within .peek-pop { opacity: 1; visibility: visible; transform: none; }

/* Sequence "Preview" mode hides the editing affordances for a clean read-only look. */
.seq-preview [data-edit-only] { display: none !important; }

/* ---------- Inbox ---------- */
.inbox { display: grid; grid-template-columns: 300px 1fr 280px; height: 100%; }
.inbox > * { border-right: 1px solid var(--border); min-height: 0; overflow: auto; }
.inbox > *:last-child { border-right: 0; }
.thread-row {
  display: grid; grid-template-columns: 28px 1fr; gap: 10px;
  padding: 10px 12px; border-bottom: 1px solid var(--border);
  cursor: pointer; border-left: 2px solid transparent;
}
.thread-row.unread { border-left-color: var(--text); }
.thread-row.active { background: var(--signal-bg); border-left-color: var(--signal); }
.thread-row .subject { font-weight: 500; font-size: 12.5px; }
.thread-row.unread .subject { font-weight: 600; }
.thread-row .snippet { color: var(--muted); font-size: 11.5px; }
.thread-row .time { font-family: "Geist Mono", ui-monospace, monospace; font-size: 10.5px; color: var(--muted); float: right; }

.message-bubble { display: flex; gap: 10px; padding: 10px 16px; }
.message-bubble.out { flex-direction: row-reverse; }
.message-bubble .body { background: var(--surface); border: 1px solid var(--border); padding: 10px 12px; border-radius: var(--r-lg); max-width: 70%; font-size: 13px; }
.message-bubble.out .body { background: var(--signal-bg); border-color: var(--signal-soft); }

/* ---------- Scorecards ---------- */
.score-matrix { width: 100%; border-collapse: collapse; }
.score-matrix th, .score-matrix td { padding: 10px 12px; border-bottom: 1px solid var(--border); vertical-align: middle; font-size: 12.5px; }
.score-matrix th { background: var(--surface-2); font-weight: 600; color: var(--text-soft); font-size: 11.5px; text-align: left; }
.score-dots { display: inline-flex; gap: 3px; }
.score-dots .d { width: 9px; height: 9px; border-radius: 50%; background: var(--surface-3); border: 1px solid var(--border); }
.score-dots .d.on { background: var(--success); border-color: transparent; }

/* ---------- Analytics ---------- */
.funnel-row { display: grid; grid-template-columns: 132px 1fr 48px; gap: 8px; align-items: center; margin-bottom: 8px; }
.funnel-row .bar-track { height: 22px; background: var(--surface-2); border-radius: 4px; overflow: hidden; position: relative; }
.funnel-row .bar { height: 100%; padding: 0 8px; display: flex; align-items: center; color: #fff; font-family: "Geist Mono", ui-monospace, monospace; font-size: 11.5px; }

.legend { display: flex; gap: 10px; flex-wrap: wrap; font-size: 11px; color: var(--muted); }
.legend .item { display: inline-flex; gap: 4px; align-items: center; }

/* ---------- Job creation step header ---------- */
.steps { display: flex; align-items: center; gap: 0; flex: 1; }
.step { display: flex; align-items: center; gap: 6px; color: var(--muted); font-size: 12px; font-weight: 500; }
.step .num { width: 22px; height: 22px; border-radius: 50%; border: 1px solid var(--border); display: inline-grid; place-items: center; font-size: 11px; }
.step.active .num { background: var(--signal); border-color: var(--signal); color: #fff; }
.step.done .num { background: var(--border-strong); border-color: var(--border-strong); color: var(--surface); }
.step.active { color: var(--text); }
.step + .step::before { content: ""; width: 32px; height: 1px; background: var(--border-2); margin-right: 8px; margin-left: -2px; }

/* ---------- Funnel mini ---------- */
.mini-funnel { display: flex; gap: 2px; height: 14px; }
.mini-funnel span { flex: 1; background: var(--border-2); border-radius: 2px; }
/* Per-stage palette, matching design/data.js: sourced+reached share --stage-new,
   screen+hm share --stage-screen (distinguished by opacity), onsite=--stage-iview,
   offer=--stage-offer. */
.mini-funnel span.st-sourced { background: var(--stage-new);    opacity: .55; }
.mini-funnel span.st-reached { background: var(--stage-new); }
.mini-funnel span.st-screen  { background: var(--stage-screen); opacity: .7; }
.mini-funnel span.st-hm      { background: var(--stage-screen); }
.mini-funnel span.st-onsite  { background: var(--stage-iview); }
.mini-funnel span.st-offer   { background: var(--stage-offer); }

/* ---------- Candidate apply page (full-screen mobile-ish) ---------- */
.apply-stage {
  min-height: 100vh; background: var(--bg);
  display: grid; place-items: center; padding: 24px;
}
.apply-card {
  width: 100%; max-width: 480px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-xl);
  box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column; min-height: 640px;
}

/* The candidate apply flow renders as a full-width page, unlike the Account pages that share .apply-card
   and stay a narrow 480px form. Scoped to .apply-stage--wide so widening here cannot affect Login,
   Register, ForgotPassword, ResetPassword, 2fa, ConfirmEmail or AccessDenied. */
.apply-stage--wide .apply-card { max-width: 1040px; }

/* Step 1 puts the job description beside the form instead of stacking it above, so a long JD does not push
   the fields below the fold on a desktop viewport. .apply-fields keeps the column gap that .body provided
   before its children were grouped. Only applied when a JD actually renders (see the `split` flag).

   The selector MUST out-specify `.apply-card .body` (0,2,0) below, which also sets `display:flex` and is
   declared later in this file — as a bare `.apply-split` (0,1,0) the grid silently lost and the columns
   never formed. Keep the `.apply-card .body.apply-split` shape (0,3,0) if this is ever moved or edited.

   Deliberately NO position:sticky / max-height / overflow on the JD column. An earlier attempt stuck it to
   the viewport, which — with the grid not applying — left it floating over the form as the page scrolled.
   Two plainly flowing columns cannot overlap and leave the page as the single scroll container. */
.apply-card .body.apply-split {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 24px; align-items: start;
}
.apply-fields { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.apply-jd { min-width: 0; }

/* "Where are you based?" chips on the apply form. The radio is visually hidden rather than display:none so
   it stays focusable and announced — a display:none control is unreachable by keyboard and absent from the
   accessibility tree, which made this question unanswerable without a mouse on a PUBLIC application form.
   :has(input:checked) supplies the selected look on the client: previously the only source of `primary` was
   the server-rendered class, so clicking a chip checked an invisible radio and nothing changed on screen.
   Deliberately CSS-only — no JS to keep in step, and it works on a keyboard and with scripting disabled. */
.loc-chip { display: inline-flex; cursor: pointer; }
.loc-chip input {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}
/* Adjacent-sibling off :checked rather than :has() on the label — the state is then driven by the radio's
   own checkedness, which is both what the server renders and what the browser updates on click, so the
   selected look cannot disagree with what will actually be posted. */
.loc-chip input:checked + .btn {
  background: var(--border-strong); color: var(--surface); border-color: var(--border-strong);
}
/* The hidden radio still takes focus, so surface it on the chip the user actually sees. */
.loc-chip input:focus-visible + .btn { outline: 2px solid var(--signal); outline-offset: 2px; }

/* Single column on narrow viewports: the JD returns to sitting above the form. Same specificity as the
   rule it overrides, and declared after it, so the stack wins. */
@media (max-width: 900px) {
  .apply-card .body.apply-split { display: flex; flex-direction: column; }
}
.apply-card .head { padding: 14px 18px; border-bottom: 1px solid var(--border); }
.apply-card .head .progress { height: 3px; background: var(--surface-2); border-radius: 999px; margin-top: 8px; overflow: hidden; }
.apply-card .head .progress span { display: block; height: 100%; background: var(--border-strong); }

/* General-purpose meter (UX-5 billing usage). Deliberately NOT the .apply-card-scoped .progress above:
   that selector only paints inside the apply wizard's head, so reusing the bare class name elsewhere
   renders an invisible zero-height bar — a silent no-op that looks like a styling bug at review time. */
.meter { height: 6px; background: var(--surface-2); border-radius: 999px; overflow: hidden; }
.meter > span { display: block; height: 100%; background: var(--signal, var(--border-strong)); }
.meter.is-full > span { background: var(--warn, #A36015); }
.apply-card .body { padding: 22px 22px 14px; flex: 1; display: flex; flex-direction: column; gap: 16px; }
.apply-card .nav { padding: 14px 18px; border-top: 1px solid var(--border); display: flex; gap: 10px; align-items: center; }
.apply-card .role-tag { display: inline-flex; align-items: center; gap: 6px; padding: 4px 8px; border-radius: 999px; background: var(--signal-bg); color: var(--signal); font-size: 11px; font-weight: 600; }

/* Rendered job-description markdown (JobDescriptionMarkdown → Markdig + sanitiser). The JD arrives as
   real h1..h6/ul/ol/p markup, so it is scaled DOWN to body size here: a JD's own "# Staff Engineer"
   heading must not outrank the page's step heading inside the narrow phone frame. */
.jd-body > :first-child { margin-top: 0; }
.jd-body > :last-child { margin-bottom: 0; }
.jd-body h1, .jd-body h2, .jd-body h3, .jd-body h4, .jd-body h5, .jd-body h6 {
    font-size: 13px; font-weight: 700; margin: 12px 0 4px; line-height: 1.35;
}
.jd-body p, .jd-body ul, .jd-body ol { margin: 0 0 8px; }
.jd-body ul, .jd-body ol { padding-left: 18px; }
.jd-body li { margin: 2px 0; }
.jd-body a { color: var(--signal); text-decoration: underline; }
.jd-body code { background: var(--surface-2); border-radius: 4px; padding: 1px 4px; font-size: 12px; }
.jd-body pre { background: var(--surface-2); border-radius: 6px; padding: 8px; overflow-x: auto; }
/* --muted, not --text-muted: the latter is not defined in tokens.css, and an unresolved var() with no
   fallback makes the whole declaration invalid at computed-value time, so the blockquote would inherit body
   colour and lose the de-emphasis this rule exists for. */
.jd-body blockquote { margin: 8px 0; padding-left: 10px; border-left: 2px solid var(--border); color: var(--muted); }
.jd-body hr { border: 0; border-top: 1px solid var(--border); margin: 10px 0; }
.jd-body table { border-collapse: collapse; width: 100%; }
.jd-body th, .jd-body td { border: 1px solid var(--border); padding: 4px 6px; text-align: left; }

/* ---------- Candidate status page ---------- */
.cs-wrap { max-width: 880px; margin: 0 auto; padding: 32px 24px; display: flex; flex-direction: column; gap: 24px; }
.cs-timeline { display: grid; grid-template-columns: repeat(6, 1fr); gap: 12px; margin: 18px 0 6px; }
.cs-step { display: flex; flex-direction: column; gap: 6px; align-items: center; text-align: center; }
.cs-step .circle { width: 26px; height: 26px; border-radius: 50%; border: 1px solid var(--border-2); display: grid; place-items: center; font-family: "Geist Mono", ui-monospace, monospace; font-size: 11px; color: var(--muted); background: var(--surface); }
.cs-step.done .circle  { background: var(--success); border-color: var(--success); color: #fff; }
.cs-step.cur  .circle  { border-color: var(--signal); color: var(--signal); }
.cs-step .name { font-size: 11.5px; color: var(--text-soft); }
.cs-step .when { font-size: 10.5px; color: var(--muted); font-family: "Geist Mono", ui-monospace, monospace; }

/* ---------- Mention chip ---------- */
.mention {
  display: inline-block;
  padding: 0 5px;
  border-radius: 4px;
  background: var(--signal-bg);
  color: var(--signal);
  font-weight: 500;
  font-size: 0.95em;
  border: 1px solid var(--signal-soft);
}
.note-body .mention { line-height: 1.4; }

/* ---------- Thread attachments ---------- */
.attachments {
  display: flex; flex-direction: column; gap: 6px;
  margin-top: 8px;
}
.attachment {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 10px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}

/* ---------- Resume preview modal ---------- */
.resume-modal {
  position: fixed; inset: 0;
  background: rgba(15,15,20,.6);
  backdrop-filter: blur(3px);
  z-index: 80;
  display: flex; flex-direction: column;
  padding: 32px;
}
.resume-modal[hidden] { display: none; }
.resume-modal-head {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  border-bottom: 0;
}
.resume-modal-body {
  flex: 1; min-height: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 0 0 var(--r-lg) var(--r-lg);
  overflow: hidden;
}
.resume-modal-body iframe {
  width: 100%; height: 100%; border: 0; background: var(--surface-2);
}
.resume-fallback {
  height: 100%;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  padding: 32px; text-align: center;
}

/* ---------- Notifications bell + dropdown ---------- */
.notif-wrap { position: relative; }
.notif-trigger { position: relative; }
.notif-dot {
  position: absolute; top: -2px; right: -2px;
  min-width: 14px; height: 14px; padding: 0 3px;
  border-radius: 999px; background: var(--danger); color: #fff;
  font-size: 9px; font-weight: 700; line-height: 14px; text-align: center;
  font-family: "Geist Mono", ui-monospace, monospace;
  border: 1.5px solid var(--bg);
}

.notif-panel {
  position: absolute; right: 0; top: calc(100% + 6px);
  width: 360px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  z-index: 40;
  display: flex; flex-direction: column;
  max-height: 460px;
}
.notif-panel[hidden] {
  display: none;
}
.notif-head {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
}
.notif-body {
  overflow: auto; flex: 1;
}
.notif-item {
  display: block; padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  border-left: 2px solid var(--signal);
  background: var(--surface);
  text-decoration: none; color: inherit;
}
.notif-item:hover { background: var(--surface-2); }
.notif-item.read { border-left-color: transparent; }
.notif-item.read .weight-500 { color: var(--text-soft); }

/* ---------- Saved searches (Sourcing) ---------- */
.saved-wrap { position: relative; }
.saved-panel {
  position: absolute; right: 0; top: calc(100% + 6px);
  width: 300px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  z-index: 40;
  display: flex; flex-direction: column;
  max-height: 360px;
}
.saved-panel[hidden] { display: none; }
.saved-head {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 10px 12px; border-bottom: 1px solid var(--border);
}
.saved-body { overflow: auto; }
.saved-item {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 12px; border-bottom: 1px solid var(--border);
}
.saved-item:last-child { border-bottom: none; }
.saved-apply {
  flex: 1; min-width: 0; text-align: left;
  background: none; border: none; cursor: pointer; color: inherit; font: inherit; padding: 0;
}
.saved-apply:hover .weight-500 { color: var(--signal); }
.saved-q {
  color: var(--muted); font-size: 11px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ---------- Flash ---------- */
.flash {
  margin: 8px 16px 0; padding: 8px 12px;
  background: var(--success-bg); color: var(--success);
  border: 1px solid var(--success); border-radius: var(--r-md);
  font-size: 12.5px;
  animation: flash-fade 4s ease forwards;
}
@keyframes flash-fade {
  0%, 80% { opacity: 1; }
  100%    { opacity: 0; visibility: hidden; }
}
/* Shell-less pages (apply flow, Account pages) centre their card in the viewport, so an in-flow banner
   would sit off-screen above it. Overlay it instead. pointer-events:none because flash-fade ends at
   opacity 0 with the box still laid out — without it the faded banner would keep swallowing clicks on
   whatever sits beneath, which on the apply form is the top of the card. */
.flash-standalone {
  position: fixed; top: 12px; left: 50%; transform: translateX(-50%);
  z-index: 60; margin: 0; max-width: min(560px, calc(100vw - 32px));
  box-shadow: var(--shadow-lg); pointer-events: none;
}

/* ---------- Bulk-select bar ---------- */
.bulk-bar {
  position: sticky; top: 0; z-index: 4;
  display: flex; align-items: center; gap: 8px;
  padding: 8px 12px;
  background: var(--text); color: var(--bg);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-md);
  margin-bottom: 12px;
}
.bulk-bar .divider { background: rgba(255,255,255,.2); }
.bulk-bar select, .bulk-bar .btn {
  background: rgba(255,255,255,.08); color: var(--bg);
  border-color: rgba(255,255,255,.16);
}
.bulk-bar .btn:hover { background: rgba(255,255,255,.16); }
/* The shared rejection-reason select (Views/Shared/_RejectionReasonSelect) at the bar's own control height. */
.bulk-bar .select[data-rejection-reason] { height: 24px; padding: 0 6px; }
.bulk-bar .btn.ghost { color: rgba(255,255,255,.7); }

/* ---------- Modal ---------- */
.modal-backdrop {
  position: fixed; inset: 0; background: rgba(15,15,20,.4);
  display: flex; align-items: center; justify-content: center;
  z-index: 70;
}
.modal {
  width: 480px; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--r-xl);
  box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column;
}
.modal-head { padding: 14px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 8px; }
.modal-head .text-h2 { margin: 0; flex: 1; }
.modal-body { padding: 16px; }

.enroll-opt { cursor: pointer; }
.enroll-opt.sel { background: var(--signal-bg); border-color: var(--signal-soft); }

/* ---------- Cmd-K palette ---------- */
.cmdk-backdrop {
  position: fixed; inset: 0; background: rgba(15,15,20,.4);
  z-index: 60; display: none; align-items: flex-start; justify-content: center; padding-top: 12vh;
}
.cmdk-backdrop.open { display: flex; }
.cmdk-panel {
  width: 540px; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-xl); box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.cmdk-panel input { width: 100%; padding: 14px 16px; border: 0; border-bottom: 1px solid var(--border); font-size: 14px; outline: none; background: var(--surface); color: var(--text); }
.cmdk-panel .results { max-height: 380px; overflow: auto; }
.cmdk-panel .result-item { display: flex; align-items: center; gap: 10px; padding: 10px 16px; font-size: 13px; cursor: pointer; }
.cmdk-panel .result-item:hover, .cmdk-panel .result-item.sel { background: var(--surface-2); }
.cmdk-panel .result-item .kind { margin-left: auto; font-size: 10.5px; color: var(--muted); text-transform: uppercase; }
.cmdk-panel .section { font-size: 10px; text-transform: uppercase; letter-spacing: .06em; padding: 8px 16px 4px; color: var(--muted); font-weight: 600; }
/* NL "Ask AI" results (the _SearchResults partial) rendered inside the palette — relocated
   here from the Sourcing tab. Pad it so the panel isn't edge-to-edge in the 540px palette. */
.cmdk-panel .nl-search-panel { padding: 6px 12px 12px; }

/* ---------- Tweaks panel (floating display settings) ---------- */
.tweaks-fab {
  position: fixed; right: 18px; bottom: 18px; z-index: 60;
  width: 38px; height: 38px; border-radius: 999px;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border); box-shadow: 0 6px 20px rgba(15,15,20,.18);
  cursor: pointer; font-size: 16px; line-height: 1;
}
.tweaks-fab:hover { background: var(--surface-2); }
.tweaks-panel {
  position: fixed; right: 18px; bottom: 64px; z-index: 60;
  width: 264px; padding: 12px 14px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg, 12px);
  box-shadow: 0 14px 44px rgba(15,15,20,.22);
  display: flex; flex-direction: column; gap: 10px;
}
.tweaks-head { display: flex; align-items: center; justify-content: space-between; }
.tweaks-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.tweaks-label { font-size: 12px; color: var(--muted); }

/* The simulated iOS device frame (.ios-frame/.ios-notch/.ios-screen) was removed when the candidate apply
   flow moved to an ordinary centred web page. It was used by exactly one view (Views/Apply/Index.cshtml)
   and nothing else referenced these classes, so the rules are deleted rather than left dead. */

/* ---------- Recruiter copilot (slide-over chat) ---------- */
.copilot-backdrop {
  position: fixed; inset: 0; background: rgba(15,15,20,.32);
  z-index: 70; opacity: 0; transition: opacity .18s ease;
}
.copilot-backdrop.open { opacity: 1; }

.copilot-panel {
  position: fixed; top: 0; right: 0; bottom: 0; width: 420px; max-width: 92vw;
  background: var(--surface); border-left: 1px solid var(--border);
  box-shadow: var(--shadow-lg); z-index: 71;
  display: flex; flex-direction: column;
  /* visibility, not just the transform: a translated panel is still laid out, still hit-testable and
     still in the tab order, so the closed panel's seven controls stayed tabbable while the container
     told assistive tech to ignore them (WCAG 4.1.2 / axe aria-hidden-focus). visibility:hidden is the
     one declaration that removes a subtree from the accessibility tree AND the tab order, and it lives
     with the rest of the closed state so markup and script can never disagree about it. `visibility`
     interpolates discretely with the `visible` endpoint held for the whole duration, so the .2s slide
     is unaffected in either direction. */
  transform: translateX(100%); visibility: hidden; transition: transform .2s ease, visibility .2s ease;
}
.copilot-panel.open { transform: translateX(0); visibility: visible; }

.copilot-head {
  display: flex; align-items: center; gap: 8px;
  padding: 12px 14px; border-bottom: 1px solid var(--border);
}
.copilot-title { display: flex; align-items: center; gap: 8px; font-weight: 600; font-size: 13.5px; }
.copilot-spark { color: var(--ai); }
.copilot-head .badge { font-size: 10px; }
.copilot-head [data-copilot-close] { margin-left: auto; }

.copilot-log {
  flex: 1; overflow-y: auto; padding: 14px;
  display: flex; flex-direction: column; gap: 10px;
}

.copilot-empty { color: var(--muted); display: flex; flex-direction: column; gap: 10px; }
.copilot-lead { margin: 0; font-size: 13px; color: var(--text-soft); }
.copilot-suggests { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
.copilot-chip {
  text-align: left; cursor: pointer;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r-md); padding: 7px 10px; font-size: 12px; color: var(--text-soft);
}
.copilot-chip:hover { border-color: var(--border-2); background: var(--surface-3); }
.copilot-fineprint { margin: 4px 0 0; font-size: 11px; color: var(--muted-2); }

.copilot-msg {
  max-width: 92%; padding: 8px 11px; border-radius: var(--r-lg);
  font-size: 12.5px; white-space: pre-wrap; word-wrap: break-word; line-height: 1.45;
}
.copilot-msg.user { align-self: flex-end; background: var(--signal-bg); color: var(--text); border: 1px solid var(--signal-soft); }
.copilot-msg.assistant { align-self: flex-start; background: var(--surface-2); border: 1px solid var(--border); }
.copilot-msg.assistant.info { background: var(--warn-bg); border-color: var(--warn-bg); color: var(--text-soft); }
.copilot-msg.assistant.error { background: var(--danger-bg); border-color: var(--danger-bg); color: var(--text-soft); }
.copilot-msg.thinking { color: var(--muted); font-style: italic; }

.copilot-tools { align-self: flex-start; display: flex; flex-wrap: wrap; align-items: center; gap: 4px; margin-top: -4px; }
.copilot-tools-label { font-size: 10.5px; color: var(--muted-2); }
.copilot-tool {
  font-size: 10px; color: var(--ai); background: var(--ai-bg);
  border: 1px solid var(--ai-soft); border-radius: 999px; padding: 1px 7px;
}

.copilot-form {
  display: flex; gap: 8px; align-items: flex-end;
  padding: 10px 12px; border-top: 1px solid var(--border); background: var(--surface);
}
.copilot-form textarea {
  flex: 1; resize: none; min-height: 36px; max-height: 160px;
  padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface); color: var(--text); font-size: 12.5px; outline: none;
}
.copilot-form textarea:focus { border-color: var(--signal); }
.copilot-form button[disabled] { opacity: .55; cursor: default; }

/* ===== Reports dashboard charts (self-hosted, rendered by wwwroot/js/reports-charts.js) ===== */
.rpt-chart { display: block; width: 100%; height: auto; overflow: visible; }
.rpt-axis  { stroke: var(--border); stroke-width: 1; }
.rpt-line  { stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.rpt-area  { opacity: 0.12; }
.rpt-bar   { opacity: 0.85; }
.rpt-tick  { fill: var(--muted); font-size: 9px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.rpt-tick.mid { text-anchor: middle; }
.rpt-tick.end { text-anchor: end; }

/* ---------- AI Sourcing Funnel (Views/Sourcing/Funnel.cshtml) ---------- */
.truncate { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.funnel-center { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 36px 24px; }
.funnel-icon { font-size: 30px; }
.funnel-lede { margin-top: 4px; max-width: 440px; }

@keyframes funnel-spin { to { transform: rotate(360deg); } }
.scan-spinner {
  width: 38px; height: 38px; border-radius: 50%;
  border: 3px solid var(--ai-soft); border-top-color: var(--ai);
  animation: funnel-spin 0.8s linear infinite;
}

.funnel-hero { text-align: center; }
.funnel-hero-num { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.funnel-hero-num .big { font-size: 40px; font-weight: 700; line-height: 1.05; color: var(--ai); font-variant-numeric: tabular-nums; }

.super-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 12px; }
.super-card { display: flex; flex-direction: column; }
.super-actions { margin-top: 10px; align-items: center; flex-wrap: wrap; }

.paywall { border: 1px solid var(--ai-soft); background: var(--ai-bg); }
.locked-teaser { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 8px; margin-top: 12px; }
.locked-card {
  height: 46px; border-radius: 8px;
  background: linear-gradient(90deg, var(--surface-2, #f1f1f4) 25%, var(--surface, #fff) 50%, var(--surface-2, #f1f1f4) 75%);
  filter: blur(1px); opacity: 0.7; border: 1px solid var(--border);
}
.buycredits-row { align-items: center; padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px; margin: 0; }

/* ---------- Client tracker (Views/Clients/Tracker.cshtml) ----------
   The densest surface in the app: every candidate is a summary row PLUS a detail band carrying
   its interview rounds, document links and the write affordances for both. These classes give
   that pair one shape. Three things they deliberately do NOT inherit from the generic `.table`:
   its `cursor:pointer` (tracker rows aren't clickable), its `tr:hover` (which lights half a
   candidate — the summary or the detail band, never the pair), and its sticky `th` (dead weight
   inside a wrapper that only scrolls horizontally). */

/* Stat strip — mono values, matching .kpi elsewhere. */
.tk-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(124px, 1fr)); gap: 10px; }
.tk-stat { padding: 11px 13px; display: flex; flex-direction: column; gap: 3px; }
.tk-stat .k { font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; font-weight: 600; color: var(--muted); }
.tk-stat .v {
  font-family: "Geist Mono", ui-monospace, monospace;
  font-size: 22px; font-weight: 600; line-height: 1.1;
  font-variant-numeric: tabular-nums; letter-spacing: -.01em;
}
/* A zero is a fact, not a headline — mute it so the eye lands on the stages with movement. */
.tk-stat.is-zero .v { color: var(--muted-2); }
.tk-stat.is-lead { background: var(--surface-2); }

/* Filter bar — controls sized to the 28px button height so the strip reads as one row of chrome
   instead of four differently-sized boxes with the buttons floating below them. */
.tk-filters { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px; padding: 12px 14px; }
.tk-filters .f { display: flex; flex-direction: column; gap: 4px; }
.tk-filters .input { width: auto; height: 28px; padding: 0 8px; font-size: 12.5px; }
.tk-filters select.input { min-width: 148px; }
.tk-filters input[type="date"].input { width: 138px; }
.tk-filters .acts { display: flex; align-items: center; gap: 6px; margin-left: auto; }

/* Disclosure — replaces the UA triangle with a chevron that turns on open. */
.tk-disc > summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: 8px; }
.tk-disc > summary::-webkit-details-marker { display: none; }
.tk-disc > summary::before {
  content: "›"; display: inline-block; color: var(--muted);
  font-size: 15px; line-height: 1; transition: transform .12s ease;
}
.tk-disc[open] > summary::before { transform: rotate(90deg); }

/* Job section */
.tk-job-head { display: flex; align-items: center; gap: 8px; padding: 11px 14px; border-bottom: 1px solid var(--border); }
.tk-empty { padding: 26px 20px; text-align: center; color: var(--muted); font-size: 12.5px; }

/* Table */
.tk-scroll { overflow-x: auto; }
.tk-table { width: 100%; border-collapse: collapse; font-size: 12px; min-width: 880px; }
.tk-table th {
  text-align: left; padding: 8px 10px; white-space: nowrap;
  font-size: 10.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); border-bottom: 1px solid var(--border);
}
.tk-table td { padding: 9px 10px; vertical-align: top; }
/* One <tbody> per candidate: the summary row and its detail row are ONE unit, so the rule falls
   between candidates (never between a candidate and their own interviews) and hover lights the pair. */
.tk-table tbody + tbody { border-top: 1px solid var(--border); }
.tk-table tbody:hover { background: var(--surface-2); }
.tk-table tr.tk-detail > td { padding-top: 2px; padding-bottom: 13px; }
.tk-name { font-weight: 600; font-size: 12.5px; }
.tk-sub { color: var(--muted); font-size: 11px; }
.tk-num { font-family: "Geist Mono", ui-monospace, monospace; font-variant-numeric: tabular-nums; }
.tk-idx { color: var(--muted-2); }
.tk-actions { display: flex; flex-wrap: wrap; gap: 4px; }

/* Detail band — a fixed label column so Interviews / Documents / Feedback line up down the page. */
.tk-detail-grid { display: grid; grid-template-columns: 74px minmax(0, 1fr); gap: 7px 10px; align-items: start; }
.tk-detail-k {
  font-size: 10px; text-transform: uppercase; letter-spacing: .06em; font-weight: 600;
  color: var(--muted); padding-top: 5px;
}
.tk-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }

/* One fact per chip, joined by muted separators — the old single-line pill concatenated round,
   time, interviewers, scheduling state and decision into one 90-character nowrap label that set
   the width of the whole table. This one wraps. */
.tk-chip {
  display: inline-flex; align-items: center; flex-wrap: wrap; gap: 5px;
  min-height: 22px; padding: 2px 8px; border-radius: 999px;
  background: var(--surface); border: 1px solid var(--border);
  font-size: 11px; color: var(--text-soft); text-align: left;
}
.tk-chip .r { font-weight: 600; color: var(--text); }
.tk-chip .sep { color: var(--muted-2); }
.tk-chip .dot { width: 5px; height: 5px; border-radius: 50%; background: var(--muted-2); }
/* The scheduling state is carried by the dot AND still spelled out in the chip — colour alone is
   not a label. */
.tk-chip.is-scheduled .dot { background: var(--signal); }
.tk-chip.is-done      .dot { background: var(--success); }
.tk-chip.is-cancelled .dot { background: var(--danger); }
.tk-chip.is-noshow    .dot { background: var(--warn); }
/* Dashed = does not reach the client (an internal-only round, an unshared link). */
.tk-chip.is-internal { border-style: dashed; background: transparent; }
.tk-chip.is-add { border-style: dashed; color: var(--muted); background: transparent; cursor: pointer; }
.tk-chip.is-add:hover { color: var(--text); border-color: var(--border-2); }
.tk-flag { font-size: 10px; color: var(--muted); }

/* Inline editors. These open IN FLOW, as a full-width panel under the chip that owns them.
   They used to be `position:absolute` children of a table cell inside the horizontally
   scrolling wrapper — and an `overflow-x:auto` box computes `overflow-y:auto` too, so the
   popup was clipped by the very container it floated over: the Save button sat outside the
   scroll region. A flex item that claims the whole line cannot be clipped by anything. */
.tk-pop { display: inline-flex; }
.tk-pop > summary { list-style: none; cursor: pointer; }
.tk-pop > summary::-webkit-details-marker { display: none; }
.tk-pop > summary.tk-chip:hover { border-color: var(--border-2); color: var(--text); }
.tk-pop[open] { flex-basis: 100%; flex-direction: column; align-items: flex-start; gap: 8px; }
.tk-pop[open] > summary .tk-chip, .tk-pop[open] > summary.tk-chip { border-color: var(--signal); border-style: solid; }
.tk-editor { width: 100%; max-width: 620px; padding: 12px; }
.tk-form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(148px, 1fr)); gap: 10px; }
.tk-field { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.tk-field .input { height: 28px; padding: 0 8px; font-size: 12px; }
.tk-field.wide { grid-column: 1 / -1; }
.tk-editor-foot { display: flex; align-items: center; gap: 8px; margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border); }
/* .field-label carries its own 4px bottom margin for stacked forms; inside these flex columns the
   gap already supplies it, and both would double the label-to-control distance. */
.tk-filters .field-label, .tk-field .field-label { margin-bottom: 0; }

/* Chip-sized remove control — .btn.icon-sm is 24px, which a 22px chip cannot hold. */
.tk-x {
  width: 16px; height: 16px; padding: 0; border: 0; border-radius: 4px;
  background: transparent; color: var(--muted-2);
  font-size: 10px; line-height: 1; cursor: pointer;
  display: inline-grid; place-items: center;
}
.tk-x:hover { color: var(--danger); background: var(--danger-bg); }

/* ---------- Star rating (recruiter's own 1–5 assessment) ----------
   Tokenised, not hard-coded: --warn is the gold in both themes, so the filled star follows the
   light/dark switch instead of staying a fixed hex that goes muddy on one of them. */
.star-rating { display: inline-flex; align-items: center; gap: 2px; }
.star-rating .star {
  background: none; border: 0; padding: 0 1px; cursor: pointer;
  font-size: 15px; line-height: 1; color: var(--muted-2);
  transition: color 120ms ease, transform 120ms ease;
}
.star-rating .star.on { color: var(--warn); }
/* .preview is applied by the delegated hover/focus handler so the whole run up to the pointer lights
   up, not just the star under it — a star rating that only highlights one glyph reads as five
   independent toggles. */
.star-rating .star.preview { color: var(--warn); transform: scale(1.12); }
.star-rating .star:focus-visible { outline: 2px solid var(--signal); outline-offset: 1px; border-radius: 3px; }
/* Saving: dim the control and swallow clicks so a double-click can't queue a second write. */
.star-rating.saving { opacity: .55; pointer-events: none; }

/* The read-only variant renders <span>s, not buttons — no cursor, no hover, nothing focusable. */
.star-rating span.star { cursor: default; padding: 0; }

/* Compact badge for a dense row or card: one ★ plus the number, rather than five glyphs competing with the
   name beside them. Nothing renders at all for an unrated candidate — see the component. */
.star-rating-badge {
  display: inline-flex; align-items: center; gap: 2px;
  font-size: 12px; line-height: 1; color: var(--text-soft); white-space: nowrap;
}
.star-rating-badge .star { font-size: 12px; line-height: 1; color: var(--warn); }

/* ── Application-scoped messaging (docs/candidate-portal/messaging.md §13) ──────────────────────────────
   Two audiences render these classes and only one of them is Razor: wwwroot/js/messaging.js CONSTRUCTS
   .conversation-message / .is-mine / .conversation-message-sender / .conversation-message-body /
   .conversation-message-failed / .conversation-retry / .conversation-notice for every message that
   arrives live, and Views/Shared/_ConversationPanel.cshtml emits the identical element for every message
   already on the page. Renaming one side alone makes a transcript visibly reshuffle the moment somebody
   refreshes, which is why the partial carries the same warning.

   The candidate portal deliberately does NOT inherit this stylesheet (_CandidatePortalLayout links only
   candidate-portal.css), so slice M5 needs its own copy of the bubble rules under the cp-* vocabulary. */

/* /Messages is TWO panes, not the Inbox's three — it has no right rail, because the conversation panel
   already carries the application context that rail would hold.

   Written as `.inbox.conversations` rather than `.conversations` so it wins on SPECIFICITY rather than on
   source order: an equal-specificity override that happens to sit lower in the file is a rule that breaks
   the day somebody sorts this stylesheet, and the failure is a three-column grid with two children — a
   third of the page blank. */
.inbox.conversations { grid-template-columns: 320px 1fr; }

.conversation { display: flex; flex-direction: column; min-height: 0; }
.conversation-head {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 10px 14px; border-bottom: 1px solid var(--border);
}

.conversation-messages {
  list-style: none; margin: 0; padding: 12px 14px;
  display: flex; flex-direction: column; gap: 10px;
  max-height: 60vh; overflow-y: auto;
}
.conversation-message { display: flex; flex-direction: column; gap: 3px; max-width: 78%; align-self: flex-start; }
.conversation-message.is-mine { align-self: flex-end; align-items: flex-end; }
.conversation-message-sender { font-size: 10.5px; color: var(--muted); }
.conversation-message-body {
  margin: 0; padding: 8px 11px; font-size: 13px; line-height: 1.45;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
  /* A message is somebody else's free text up to 4,000 characters (MessageContent.MaxLength): honour its
     own line breaks, and break a 200-character unbroken "word" rather than letting it widen the panel. */
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.conversation-message.is-mine .conversation-message-body {
  background: var(--signal-bg); border-color: var(--signal-soft);
}
/* Optimistic, still in flight. Dimmed rather than hidden: the text the person typed stays readable while
   it is on its way, and the state is reversible in place when the authoritative row lands. */
.conversation-message[data-state="sending"] { opacity: .55; }
.conversation-message[data-state="failed"] .conversation-message-body { border-color: var(--danger); }
.conversation-message-failed { margin: 3px 0 0; font-size: 11px; color: var(--danger); }
.conversation-retry {
  /* 44px, matching the candidate side's B-MSG-3 correction rather than the reference's 32px defect: the
     recruiter surface is used at tablet width (§18 of the tracker's UX sections), and this is the one
     control pressed when something has already gone wrong. Inline-flex keeps the taller target on the
     failure line's baseline instead of breaking it. */
  display: inline-flex; align-items: center; min-height: 44px;
  margin-left: 6px; padding: 0 10px; border: 0; background: none;
  font: inherit; color: var(--signal); text-decoration: underline; cursor: pointer;
}
.conversation-notice { margin: 4px 0 0; font-size: 11px; color: var(--muted); text-align: center; }
.conversation-older { align-self: center; font-size: 11px; }
.conversation-hint { padding: 0 14px 6px; }

.conversation-composer { padding: 12px 14px; border-top: 1px solid var(--border); }
/* §17.3: on a closed application this REPLACES the composer — the composer is absent, not disabled, and
   the endpoint refuses independently with a 409. */
.conversation-closed {
  margin: 0; padding: 12px 14px; border-top: 1px solid var(--border);
  font-size: 12px; color: var(--muted);
}

/* ---------- Pre-execution legal documents (/legal/terms, /legal/dpa) ---------- */
/* Both pages are published as DRAFTS. The banner and the open-term markers are the only thing
   separating a document under review from one a customer may rely on, so they are styled to be
   unmissable rather than tasteful, and they use --warn (which has a dark-theme value) rather than a
   literal so they hold on either ground. Removing these styles does not remove the words. */
.legal-draft-banner {
  padding: 12px 14px; border-radius: var(--r-md);
  background: var(--warn-bg); color: var(--warn);
  border: 1px solid var(--warn);
  font-size: 13px; line-height: 1.5;
}
/* Inline marker for a commercial or jurisdictional term that only the provider can decide. Rendered
   in the flow of the clause it belongs to, so a reader cannot mistake the surrounding sentence for a
   complete one. */
.legal-owed {
  display: inline-block; padding: 1px 7px; border-radius: var(--r-sm);
  background: var(--warn-bg); color: var(--warn);
  border: 1px dashed var(--warn);
  font-size: 11.5px; font-weight: 600; white-space: normal;
}

/* ---------- Hire cascade disclosure ----------
   Shown only when a hire would CLOSE the requisition and sweep the other candidates on it. Deliberately
   sits between the action row and the scorecard table — above the fold, in the path of the eye moving from
   the Hire button downward — because its whole purpose is to be read BEFORE the click rather than to
   explain the result afterwards. Warning-toned, not error-toned: this is a legitimate action with a wide
   blast radius, not a mistake. */
.hire-cascade-note {
  margin: 12px 0 0; padding: 10px 12px;
  background: var(--amber-soft, #FDF6E7); border: 1px solid var(--amber, #E3C169);
  border-radius: 8px; color: var(--ink);
}
.hire-cascade-list {
  margin: 6px 0 0; padding-left: 1.1rem; font-size: .85rem; color: var(--ink-soft);
}
.hire-cascade-list li { margin: 1px 0; }
