/* Public, token-authed candidate surfaces: the interview room, its TruGen
   variant, and the HR-facing interview record.

   These pages have no nav (candidates are not users), so they carry their own
   small brand header — otherwise a token link opens on an unbranded page. */

body { padding: var(--sp-6) var(--sp-4) var(--sp-12); }

.pub-brand {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  margin-bottom: var(--sp-6);
  color: var(--ink); text-decoration: none;
}
.pub-brand:hover { text-decoration: none; }
.pub-brand span { font-size: var(--t-base); font-weight: 600; letter-spacing: -0.02em; }
/* Tenant logo in the public brand lockup: it stands alone (no wordmark), so it
   gets room. Letterboxed (contain) so a wide or tall employer logo never
   distorts or overflows. */
.pub-brand__logo { display: block; height: var(--pub-logo-h, 48px); max-width: 260px; object-fit: contain; }

/* "Powered by CareerBridge" on the nav-less candidate pages. The shared
   body.has-footer flex (app.css) pins it to the viewport bottom on short pages
   via margin-top:auto — keep that; only trim the side padding (the body already
   pads the page) and add a little breathing room above the hairline. */
body.has-footer { padding-bottom: var(--sp-4); }
body.has-footer > .site-footer { padding-left: 0; padding-right: 0; padding-top: var(--sp-6); }

.pub-head { text-align: center; margin-bottom: var(--sp-5); }
.pub-head h1 { font-size: var(--t-2xl); font-weight: 700; letter-spacing: -0.02em; margin: 0; }
.pub-head p { font-size: var(--t-sm); color: var(--muted); margin: 6px 0 0; }

.pub-wrap { width: 100%; max-width: 900px; margin: 0 auto; }
.pub-narrow { max-width: 560px; }

/* ── Interview room ───────────────────────────────────────────────────────── */

/* No max-height here: combined with `aspect-ratio` it shrinks the WIDTH to
   satisfy the ratio, and the stage stops filling its card. Cap `.pub-narrow`
   instead. */
.pub-stage { aspect-ratio: 3 / 4; }
.pub-stage #avatar { position: absolute; inset: 0; z-index: 1; }
.pub-stage .stage__status { z-index: 3; }

/* Interview pacing bar: how far in, and how long is left.

   ⚠️ The FILL stays neutral teal at every value — this is progress through a
   set of questions, not a score, and DESIGN.md reserves band colours (amber /
   red) for things that are good or bad. A bar that reddened as the candidate
   advanced would read as them doing badly. Only the CLOCK takes urgency
   colour, because running out of time genuinely is urgent. */
.ivprog { display: flex; flex-direction: column; gap: 6px; }
.ivprog__row {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3);
}
.ivprog__step { font-size: var(--t-sm); font-weight: 600; color: var(--ink); }
.ivprog__clock {
  font-size: var(--t-sm); font-weight: 600; color: var(--muted);
  font-variant-numeric: tabular-nums;   /* stops the countdown jittering */
}
.ivprog__clock.is-warn { color: var(--warn); }
.ivprog__clock.is-urgent { color: var(--danger); }
.ivprog__track {
  height: 6px; border-radius: 999px; background: var(--band-track); overflow: hidden;
}
.ivprog__fill {
  display: block; height: 100%; width: 0%;
  border-radius: 999px; background: var(--primary);
  transition: width 320ms ease;
}
@media (prefers-reduced-motion: reduce) { .ivprog__fill { transition: none; } }

.selfie-wrap { position: relative; margin-top: var(--sp-3); }
.selfie {
  width: 100%; display: block;
  border-radius: var(--r-xl);
  background: var(--slate-900);
  transform: scaleX(-1);   /* mirror, so it reads like a mirror not a camera */
}
.selfie-tag {
  position: absolute; left: var(--sp-2); bottom: var(--sp-2);
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px var(--sp-2);
  border-radius: var(--r-full);
  background: rgb(2 6 23 / 0.6);
  color: var(--slate-200);
  font-size: var(--t-10); font-weight: 600;
}
.rec-dot {
  width: 7px; height: 7px; border-radius: var(--r-full);
  background: var(--red-500);
  animation: cb-pulse 1.6s var(--ease) infinite;
}

iframe.trugen { width: 100%; height: 640px; border: 0; display: block; background: #000; }

/* ── Assessment (candidate-facing questionnaire) ──────────────────────────── */

/* Sticky so a 50-item run always shows how much is left — the single biggest
   driver of abandonment on a long form. */
.asmt-progress {
  position: sticky; top: 0; z-index: 5;
  height: 6px; width: 100%;
  border-radius: var(--r-full);
  background: var(--band-track);
  overflow: hidden;
  margin-bottom: var(--sp-2);
}
.asmt-progress span {
  display: block; height: 100%; width: 0;
  background: var(--primary);
  transition: width var(--t-mid) var(--ease);
}

.asmt-item { transition: border-color var(--t-fast) var(--ease); }
.asmt-item.is-done { border-color: var(--primary-line); }

.asmt-qtext {
  display: flex; align-items: baseline; gap: var(--sp-2);
  margin: 0 0 var(--sp-3);
  font-size: var(--t-base); font-weight: 500; color: var(--ink);
}
.asmt-n {
  flex: none; min-width: 22px;
  font-size: var(--t-xs); font-weight: 700; color: var(--faint);
}

.asmt-scale { display: flex; gap: var(--sp-2); flex-wrap: wrap; }

/* The five Likert options. Only the endpoints and midpoint carry a label
   (that's how the instrument was administered to the norm sample), so the
   unlabelled steps rely on position + size to read as a scale. */
.asmt-opt {
  flex: 1 1 0; min-width: 84px;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: var(--sp-3) var(--sp-2);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
  cursor: pointer;
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.asmt-opt:hover { border-color: var(--border-strong); background: var(--surface-sunken); }
.asmt-opt input { position: absolute; opacity: 0; pointer-events: none; }
.asmt-opt:focus-within { box-shadow: var(--focus-ring); }

.asmt-dot {
  width: 18px; height: 18px; border-radius: var(--r-full);
  border: 2px solid var(--border-strong);
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.asmt-opt.is-on { border-color: var(--primary); background: var(--primary-soft); }
.asmt-opt.is-on .asmt-dot { border-color: var(--primary); background: var(--primary); }

.asmt-optlabel {
  font-size: var(--t-10); font-weight: 600; color: var(--muted);
  text-align: center; line-height: 1.2; min-height: 1.2em;
}
.asmt-opt.is-on .asmt-optlabel { color: var(--primary-text); }

@media (max-width: 640px) {
  .asmt-scale { gap: var(--sp-1); }
  .asmt-opt { min-width: 0; padding: var(--sp-2) 4px; }
}

/* ── Focus & Speed (symbol-digit task) ────────────────────────────────────── */

.sdmt__bar { display: flex; align-items: center; gap: var(--sp-3); }
.sdmt__phase {
  flex: none; font-size: var(--t-10); font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--faint);
}
.sdmt__phase.is-live { color: var(--primary-text); }
.sdmt__timer {
  flex: 1; height: 8px; border-radius: var(--r-full);
  background: var(--band-track); overflow: hidden;
}
.sdmt__timer span { display: block; height: 100%; width: 100%; background: var(--primary); }
.sdmt__secs {
  flex: none; min-width: 34px; text-align: right;
  font-size: var(--t-xs); font-weight: 700; color: var(--muted);
}

/* Three rounds, so the candidate can see how far through they are. Progress,
   not a score: nothing here reflects how well they are doing. */
.sdmt__rounds { display: flex; justify-content: center; gap: var(--sp-2); margin-top: var(--sp-3); }
.sdmt__dot {
  font-size: var(--t-10); font-weight: 600; color: var(--faint);
  padding: 2px var(--sp-2); border-radius: var(--r-full);
  background: var(--surface-sunken); border: 1px solid var(--border-hairline);
}
.sdmt__dot.is-now { color: var(--primary-text); background: var(--primary-soft); border-color: var(--primary-line); }
.sdmt__dot.is-done { color: var(--ok); }

/* The key stays on screen for the whole task: hiding it would measure memory
   rather than processing speed. */
.sdmt__key {
  display: flex; justify-content: center; flex-wrap: wrap; gap: var(--sp-2);
  margin: var(--sp-4) 0;
  padding: var(--sp-3);
  border: 1px solid var(--border); border-radius: var(--r-xl);
  background: var(--surface-sunken);
}
.sdmt__pair {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  min-width: 64px; padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-lg); background: var(--surface);
  border: 1px solid var(--border-hairline);
}
.sdmt__sym { color: var(--ink-2); line-height: 0; }
.sdmt__digit { font-size: var(--t-lg); font-weight: 700; color: var(--primary-text); }

.sdmt__stage {
  position: relative; display: flex; align-items: center; justify-content: center;
  min-height: 196px; border-radius: var(--r-xl); background: var(--surface-sunken);
}
.sdmt__prompt { color: var(--ink); line-height: 0; }

/* Errors flash; correct answers do not. A positive cue on every right answer
   would act as a running score, and watching your score changes how you play. */
.sdmt__flash {
  position: absolute; inset: 0; border-radius: var(--r-xl);
  pointer-events: none; opacity: 0; transition: opacity 110ms var(--ease);
}
.sdmt__flash.is-err { background: var(--red-50); opacity: 1; box-shadow: inset 0 0 0 2px var(--danger); }
.sdmt__flash.is-ok { background: var(--emerald-50); opacity: 1; box-shadow: inset 0 0 0 2px var(--ok); }

.sdmt__pad { display: flex; justify-content: center; gap: var(--sp-2); margin-top: var(--sp-4); flex-wrap: wrap; }
.sdmt__key-btn { min-width: 56px; font-size: var(--t-lg); font-weight: 700; }

.sdmt__ready { font-size: 60px; font-weight: 800; color: var(--primary); margin: 0; line-height: 1.1; }

@media (prefers-reduced-motion: reduce) { .sdmt__flash { transition: none; } }

/* ── Booking the human interview (cal.com embed) ──────────────────────────── */

/* The embed sizes itself and can grow tall as the candidate steps through
   month → day → form, so it gets a floor rather than a fixed height and is left
   to push the card down. `pub-narrow` is too tight for a calendar grid, so the
   page that uses this widens itself below. */
.sched-embed { width: 100%; min-height: 620px; }
.sched-embed iframe { width: 100%; border: 0; display: block; }

.sched-done { border-color: var(--emerald-200); }

/* ── Interview record ─────────────────────────────────────────────────────── */

.rec-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-4); }
.rec-head h1 { font-size: var(--t-2xl); font-weight: 700; letter-spacing: -0.02em; margin: 0; }
.rec-head p { font-size: var(--t-sm); color: var(--muted); margin: 4px 0 0; }

video.rec-video { width: 100%; display: block; background: #000; max-height: 520px; }

/* Kept for older audio-only recordings (a candidate whose camera was refused). */
audio.rec-audio { width: 100%; display: block; }

/* Self-view: the candidate's own camera, corner-inset over the avatar so they
   can check framing without it competing with the interviewer.
   ⚠️ Mirrored, like every video-call app -- an unmirrored self-view reads as
   wrong to the person looking at it. The RECORDING is not mirrored; this
   transform is presentation only. */
.stage__self {
  position: absolute;
  right: 12px;
  bottom: 12px;
  width: 132px;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--r-md);
  border: 2px solid rgba(255,255,255,.5);
  box-shadow: 0 6px 20px rgba(0,0,0,.28);
  background: #111;
  transform: scaleX(-1);
  z-index: 2;
}
@media (max-width: 520px) { .stage__self { width: 92px; } }

/* The per-interview heading carries its own score, so it needs the same
   space-between treatment as the page header. */
.rec-ivhd { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); }

.rec-summary { font-size: var(--t-sm); line-height: 1.65; color: var(--ink-3); white-space: pre-wrap; }

.transcript { display: flex; flex-direction: column; }
.transcript .turn { flex-direction: column; gap: 4px; max-width: 82%; }
.transcript .turn--user { align-self: flex-end; align-items: flex-end; }
.transcript .who { font-size: var(--t-10); text-transform: uppercase; letter-spacing: 0.08em; color: var(--faint); font-weight: 700; }
.transcript .turn--user .who { text-align: right; }
.transcript .turn--assistant .bubble { border-top-left-radius: var(--r-sm); }
.transcript .turn--user .bubble { border-top-right-radius: var(--r-sm); }

/* ── Reasoning test (candidate) ────────────────────────────────────────────── */
/* One question at a time, forward only. The clock is always visible: hiding it
   adds anxiety without adding measurement. */

.rz-rules { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-3); }
.rz-rules li { display: flex; gap: var(--sp-3); align-items: flex-start; font-size: var(--t-sm); color: var(--ink-3); line-height: 1.6; }
.rz-rules li svg { flex: none; margin-top: 2px; color: var(--primary); }

.rz-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-4); padding: var(--sp-3) var(--sp-5);
  border-bottom: 1px solid var(--border); background: var(--surface-sunken);
}
.rz-progress { font-size: var(--t-xs); color: var(--muted); font-weight: 600; flex: 1; }
.rz-track { height: 4px; border-radius: var(--r-full); background: var(--slate-200); margin-top: 6px; overflow: hidden; }
.rz-fill { height: 100%; background: var(--primary); border-radius: var(--r-full); transition: width 200ms var(--ease); }

.rz-clock {
  font-size: var(--t-xl); font-weight: 700; font-variant-numeric: tabular-nums;
  color: var(--ink-2); min-width: 68px; text-align: right;
}
.rz-clock.is-low { color: var(--danger); }

.rz-prompt { font-size: var(--t-base); font-weight: 600; color: var(--ink); margin: 0 0 var(--sp-4); }

/* Series: the terms read as a row, with the blank last. */
.rz-series { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: center; justify-content: center; margin-bottom: var(--sp-5); }
.rz-term {
  min-width: 62px; padding: var(--sp-3) var(--sp-4); border-radius: var(--r-xl);
  background: var(--surface-sunken); border: 1px solid var(--border);
  font-size: var(--t-xl); font-weight: 700; text-align: center;
  font-variant-numeric: tabular-nums; color: var(--ink);
}
.rz-term--q { background: var(--primary-soft); border-color: var(--primary-line); color: var(--primary-text); }

/* Syllogism: premises stacked, conclusion set apart so the two are never confused. */
.rz-syl { max-width: 560px; margin: 0 auto var(--sp-5); }
.rz-prem {
  font-size: var(--t-base); line-height: 1.6; color: var(--ink-2); margin: 0 0 var(--sp-2);
  padding: var(--sp-3) var(--sp-4); background: var(--surface-sunken);
  border-radius: var(--r-lg); border-left: 3px solid var(--slate-300);
}
.rz-concl {
  font-size: var(--t-base); line-height: 1.6; color: var(--ink); margin: var(--sp-4) 0 0;
  padding: var(--sp-3) var(--sp-4); background: var(--primary-soft);
  border-radius: var(--r-lg); border-left: 3px solid var(--primary); font-weight: 600;
}
.rz-concl-lbl {
  display: block; font-size: var(--t-10); text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--primary-text); font-weight: 700; margin-bottom: 2px;
}

/* Matrix: a 3x3 of equal cells, the missing one marked rather than blank. */
/* The gap IS the gridline: a 2px background showing between cells reads as a
   3x3 matrix, where floating shapes on a flat card read as scattered dots. */
/* Wider than the first pass: at 330px a cell is ~105px and a shape drawn
   inside it around 30px, which is too small to read a stroke-width or size
   rule reliably. The grid is the item, so it gets the room. */
.rz-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px;
  width: min(400px, 100%); margin: 0 auto var(--sp-5);
  padding: 2px; background: var(--border-strong); border-radius: var(--r-lg);
  overflow: hidden;
}
.rz-cell {
  aspect-ratio: 1; background: var(--surface);
  display: grid; place-items: center; color: var(--ink-2); padding: 8px;
}
.rz-cell--missing { color: var(--primary); font-size: var(--t-2xl); font-weight: 700; background: var(--primary-soft); }

/* Options sit directly under the stimulus at the same width, so the two read as
   one question rather than two unrelated rows spread across the card. */
.rz-options {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-3);
  max-width: 480px; margin: 0 auto;
}
.rz-options--wide { grid-template-columns: repeat(2, 1fr); max-width: 400px; }
/* Three-way items: a 2-column grid would strand the third option on its own
   row, reading as a different kind of choice from the first two. */
.rz-options--three { grid-template-columns: repeat(3, 1fr); max-width: 560px; }
.rz-options--three .rz-opt { font-size: var(--t-sm); padding: var(--sp-3) var(--sp-2); }
/* Option thumbnails match the grid's width so a cell and an option render a
   shape at the same size. When the options were narrower, comparing them to the
   grid meant mentally rescaling, which is not what the item is testing. */
.rz-options--visual { grid-template-columns: repeat(4, 1fr); max-width: 400px; }
.rz-options--visual .rz-opt { padding: 0; aspect-ratio: 1; }
.rz-options--visual .rz-cell { width: 100%; height: 100%; padding: 8px; }

.rz-opt {
  display: flex; align-items: center; justify-content: center; gap: var(--sp-2);
  padding: var(--sp-4) var(--sp-3); border-radius: var(--r-xl);
  border: 1.5px solid var(--border); background: var(--surface);
  font-size: var(--t-lg); font-weight: 600; color: var(--ink);
  cursor: pointer; transition: border-color 110ms var(--ease), background 110ms var(--ease);
  font-variant-numeric: tabular-nums; text-align: center; line-height: 1.4;
}
.rz-opt:hover { border-color: var(--primary-line); background: var(--primary-soft); }
.rz-opt.is-picked { border-color: var(--primary); background: var(--primary-soft); box-shadow: inset 0 0 0 1px var(--primary); }

.rz-opt--visual { flex-direction: column; padding: var(--sp-2); gap: 2px; }
.rz-opt--visual .rz-cell { background: transparent; width: 100%; }
.rz-opt-tag {
  font-size: var(--t-10); font-weight: 700; color: var(--faint);
  text-transform: uppercase; letter-spacing: 0.06em; flex: none;
}
.rz-opt.is-picked .rz-opt-tag { color: var(--primary-text); }
.rz-options--wide .rz-opt { font-size: var(--t-base); }

.rz-foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5); border-top: 1px solid var(--border); background: var(--surface-sunken);
}

.rz-report { text-align: left; width: 100%; max-width: 480px; margin: var(--sp-4) auto 0; }
.rz-headline { font-size: var(--t-2xl); color: var(--ink); margin-bottom: var(--sp-3); }
.rz-headline strong { font-size: 34px; color: var(--primary-text); }
.rz-headline .hint { display: block; font-size: var(--t-sm); }
.rz-table { width: 100%; margin-bottom: var(--sp-3); }
.rz-table .num { text-align: right; font-variant-numeric: tabular-nums; }

@media (max-width: 640px) {
  .rz-options, .rz-options--visual { grid-template-columns: repeat(2, 1fr); }
}

/* ── Custom MCQ (candidate) ────────────────────────────────────────────────── */
/* Answer text is arbitrary length here (HR writes it), so options stack full
   width and left-align rather than sitting in a 4-up grid like the generated
   reasoning items. */

.rz-options--stack { grid-template-columns: 1fr; max-width: 620px; gap: var(--sp-2); }
.rz-opt--row {
  justify-content: flex-start; text-align: left; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); font-size: var(--t-base); font-weight: 500;
}
.rz-opt--row .rz-opt-tag { flex: none; }
.rz-opt-txt { flex: 1; }

/* Preview-only answer review. Candidates never see this: `/finish` returns the
   report exclusively for a candidate-less (HR self-preview) assignment. */
.mcq-revlist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-3); }
.mcq-rev { border-left: 3px solid var(--border-strong); padding: var(--sp-2) var(--sp-3); background: var(--surface-sunken); border-radius: 0 var(--r-md) var(--r-md) 0; }
.mcq-rev.is-ok { border-left-color: var(--ok); }
.mcq-rev.is-no { border-left-color: var(--danger); }
.mcq-rev__q { font-size: var(--t-sm); font-weight: 600; color: var(--ink); margin: 0 0 4px; line-height: 1.5; }
.mcq-rev__a { font-size: var(--t-xs); color: var(--ink-3); margin: 0; }
.mcq-rev__a--key { color: var(--ok); font-weight: 600; }

/* ── Coding assessment (hr_assessment_coding.html) ────────────────────────── */
/* Wider than the other candidate pages: a code editor beside a problem
   statement needs the room, which is why spec.min_viewport_px is 900 and the
   page refuses below it rather than reflowing into something unusable. */
.pub-wrap--wide { max-width: 1400px; }

.cd-split {
  display: grid;
  grid-template-columns: minmax(320px, 5fr) minmax(420px, 7fr);
  gap: 0;
  align-items: stretch;
}
.cd-statement {
  padding: 20px 22px;
  border-right: 1px solid var(--line);
  max-height: 640px;
  overflow-y: auto;
}
.cd-editor { display: flex; flex-direction: column; min-width: 0; }

.cd-md p { margin: 0 0 10px; }
.cd-md h4 { margin: 16px 0 6px; font-size: 14px; }
.cd-md ul { margin: 0 0 10px 18px; }
.cd-md li { margin: 3px 0; }
.cd-md code, .cd-statement code {
  font-family: ui-monospace, Consolas, monospace;
  font-size: 12.5px;
  background: var(--surface-2);
  padding: 1px 5px;
  border-radius: 3px;
}

.cd-h { margin: 18px 0 8px; font-size: 13px; text-transform: uppercase;
        letter-spacing: .06em; color: var(--muted); }
.cd-lbl { display: block; font-size: 11px; text-transform: uppercase;
          letter-spacing: .05em; color: var(--muted); margin-bottom: 3px; }
.cd-pre {
  font-family: ui-monospace, Consolas, monospace;
  font-size: 12.5px;
  line-height: 1.5;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 8px 10px;
  margin: 0 0 8px;
  white-space: pre-wrap;
  word-break: break-word;
  overflow-x: auto;
}
.cd-pre--err { border-color: #fca5a5; background: #fef2f2; color: #991b1b; }
.cd-sample { margin-bottom: 12px; }

.cd-toolbar {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 14px; border-bottom: 1px solid var(--line);
}
.cd-toolbar .spacer { flex: 1; }
.input--sm { width: auto; padding: 5px 8px; font-size: 13px; }

/* CodeMirror sits between the toolbar and the console; a fixed height keeps the
   console visible without the page scrolling while the candidate types. */
.cd-editor .CodeMirror {
  height: 420px;
  font-family: ui-monospace, Consolas, monospace;
  font-size: 13.5px;
  line-height: 1.55;
}
/* ⚠️ DO NOT style the horizontal geometry of CodeMirror's gutter or lines.
   It measures the gutter once and offsets the text by that width via
   margin-left on .CodeMirror-sizer. Any CSS that changes those widths AFTER
   init -- padding on .CodeMirror-line, padding on .CodeMirror-linenumber -- moves
   the text without moving the offset, so the first characters of every line are
   painted underneath the gutter. That is what made "import sys" render as
   "rt sys" and typing "Hello" show a fragment.

   Vertical padding and colour are safe; horizontal spacing is not. The gutter
   already leaves a sensible gap by default -- leave it alone. */
/* ⚠️ Fixed gutter width, and the font pinned on the line numbers themselves.
   CodeMirror sizes the gutter by MEASURING a rendered line number, so if that
   element resolves a different font (or the page is zoomed) the gutter balloons
   -- reported as a large empty block to the left of line 1 with the code pushed
   out of view. It looked fine in headless Chromium and broken in a real
   browser, which is exactly the signature of a font-dependent measurement.
   `width` here is honoured because .CodeMirror-sizer's offset is set from the
   gutter's ACTUAL rendered width at refresh() time, which the mount and resize
   handlers both trigger. */
.cd-editor .CodeMirror-gutters {
  border-right: 1px solid rgba(255,255,255,.08);
  width: 44px;
}
.cd-editor .CodeMirror-linenumber {
  font-family: ui-monospace, Consolas, monospace;
  font-size: 13.5px;
  width: 44px;
  padding: 0;
  text-align: right;
  box-sizing: border-box;
}
.cd-editor .CodeMirror-lines { padding: 8px 0; }

.cd-console {
  border-top: 1px solid var(--line);
  padding: 12px 14px;
  max-height: 260px;
  overflow-y: auto;
  background: var(--surface-1);
}
/* The closed-book reminder, shown when a candidate returns to the window.

   ⚠️ `[hidden]` needs the explicit rule: the alert classes set `display: flex`,
   which beats the hidden attribute, and without this the banner would sit there
   empty from first paint -- accusing someone who had not left yet. */
/* Persistent "you are being recorded" bar. Deliberately always visible while
   the recorder runs: someone who forgets they are being filmed has not
   meaningfully consented to it. Quiet rather than alarming -- they agreed to
   this before starting, so it is a reminder, not a warning. */
.cd-recbar {
  display: flex; align-items: center; gap: 8px;
  margin: 0 14px 12px; padding: 8px 12px;
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface-1); color: var(--muted);
  font-size: 13px;
}
.cd-recbar[hidden] { display: none; }
.cd-recdot {
  width: 9px; height: 9px; border-radius: 50%;
  background: #ef4444; flex: none;
  animation: cbRecPulse 2s ease-in-out infinite;
}
@keyframes cbRecPulse { 0%,100% { opacity: 1; } 50% { opacity: 0.35; } }
@media (prefers-reduced-motion: reduce) { .cd-recdot { animation: none; } }
/* Failure turns the dot amber and stops it: a pulsing red dot beside "the
   recording could not be saved" says two opposite things at once. */
.cd-recbar.is-failed .cd-recdot { background: var(--warn); animation: none; }

.cd-proctorwarn { margin: 0 14px 12px; }
.cd-proctorwarn[hidden] { display: none; }

.cd-err { color: #b91c1c; font-size: 13px; margin: 0; }
.cd-case { border-left: 3px solid var(--line); padding: 6px 0 6px 10px; margin: 8px 0; }
.cd-case.is-ok { border-color: #10b981; }
.cd-case.is-no { border-color: #ef4444; }
.cd-case__h { font-size: 12.5px; font-weight: 600; margin-bottom: 6px; }

@media (max-width: 1000px) {
  .cd-split { grid-template-columns: 1fr; }
  .cd-statement { border-right: 0; border-bottom: 1px solid var(--line); max-height: 320px; }
}

/* Problem tabs on the coding page. All problems reachable at any time -- the
   tick and count are the only signal that one is done, since with free
   navigation nothing walks the candidate through the set. */
.cd-tabs { display: flex; gap: 6px; flex-wrap: wrap; flex: 1; min-width: 0; }
.cd-tab {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--line); border-radius: 20px;
  background: transparent; color: var(--ink);
  padding: 5px 12px; font-size: 13px; cursor: pointer; white-space: nowrap;
}
.cd-tab:hover { border-color: var(--primary); }
.cd-tab.is-on { background: var(--primary); color: #fff; border-color: var(--primary); }
.cd-tab.is-done { border-color: #10b981; }
.cd-tab.is-done.is-on { background: #10b981; border-color: #10b981; }
.cd-tab.is-started { border-style: dashed; }
.cd-tab__n { font-size: 11.5px; opacity: .75; font-variant-numeric: tabular-nums; }

/* Builder tabs (hr_assessments.html) share the shape but sit on a form. */
.ctabs { display: flex; gap: 6px; flex-wrap: wrap; margin: var(--sp-4) 0 var(--sp-3); }
.ctab {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--line); border-radius: 20px; background: transparent;
  color: var(--ink); padding: 5px 12px; font-size: 13px; cursor: pointer;
  max-width: 240px;
}
.ctab > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ctab.is-on { background: var(--primary); color: #fff; border-color: var(--primary); }
.ctab--add { border-style: dashed; color: var(--muted); }
.ctab__x { display: inline-flex; opacity: .6; }
.ctab__x:hover { opacity: 1; }

/* ── Application form (hr_apply.html) ──────────────────────────────────────
   A two-column job posting: the role + description on the left, a sticky
   application card on the right (see the reference mockup). Built entirely on
   the shared card/field/btn components and design tokens; the one gradient is
   --grad-brand-r, a named brand moment, standing in until a future admin
   colour-palette re-themes the hero. These rules do layout only. */
/* The apply page owns the whole viewport: the hero band bleeds edge-to-edge, so
   this page opts out of the shared public-body padding. Content below the hero
   is re-inset by .pub-wrap's own max-width + the page padding here. */
.apply-body { padding: 0 0 var(--sp-12); }
.apply-page { max-width: 1080px; position: relative; padding: 0 var(--sp-4); }
@media (min-width: 560px) { .apply-page { padding: 0 var(--sp-5); } }

/* Full-bleed hero band. Logo tile + job title sit together on the left; a soft
   radial glow floats top-right for depth. Branded colour applied inline on
   .apply-hero when set, else the CareerBridge gradient. */
.apply-hero { position: relative; width: 100%; color: #fff; overflow: hidden; }
.apply-hero--default { background: var(--grad-brand-r); }
.apply-hero__inner {
  position: relative; z-index: 1;
  max-width: 1080px; margin: 0 auto;
  display: flex; align-items: center; gap: var(--sp-4);
  padding: var(--sp-8) var(--sp-5);
}
.apply-hero__eyebrow {
  display: inline-block; font-size: var(--t-2xs, 11px); font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase; opacity: 0.85; margin-bottom: 4px;
}
.apply-hero__title { margin: 0; font-size: var(--t-2xl); font-weight: 800; letter-spacing: -0.025em; line-height: 1.1; }
/* "All open roles", pushed to the far end of the hero row.
   ⚠️ On the branded band, so it is styled off `currentColor` (#fff) rather
   than the design system's link colour — a teal link on a teal band is
   invisible, and the band takes whatever colour the employer picked. Sits
   above `.apply-hero__glow` on z-index via its parent, so it stays clickable. */
.apply-hero__back {
  margin-left: auto; flex: none;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 14px; border-radius: var(--radius-full, 999px);
  font-size: var(--t-sm); font-weight: 600; text-decoration: none;
  color: #fff; background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.28);
  transition: background 120ms ease;
}
.apply-hero__back:hover { background: rgba(255, 255, 255, 0.24); }
.apply-hero__back:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
/* Under 560px the hero stacks and a right-pushed pill would sit alone on its
   own line; drop it back beside the logo instead. */
@media (max-width: 560px) {
  .apply-hero__back { margin-left: 0; padding: 6px 10px; }
}

/* Decorative glow, top-right; purely aesthetic so aria-hidden. */
.apply-hero__glow {
  position: absolute; top: -40%; right: -5%; width: 380px; height: 380px;
  border-radius: 50%; z-index: 0; pointer-events: none;
  background: radial-gradient(circle, rgba(255,255,255,0.22), transparent 70%);
}

/* Logo tile sitting IN the hero (not overlapping). White chip keeps any logo
   legible on any brand colour. */
.apply-logo {
  flex: none; width: 68px; height: 68px; border-radius: var(--r-lg);
  background: #fff; display: grid; place-items: center; padding: 10px;
  box-shadow: var(--shadow-md);
}
.apply-logo img { max-width: 100%; max-height: 100%; object-fit: contain; }

/* Two columns; the form is second in the DOM so it stacks LAST on narrow. The
   content sits a little above the hero's baseline for a layered feel. */
.apply-cols {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(340px, 400px);
  gap: var(--sp-6); align-items: start; padding-top: var(--sp-6);
}
.apply-role { min-width: 0; }

/* Meta row above the description (mockup): a company chip + a real Share action. */
.apply-meta { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; margin-bottom: var(--sp-4); }
.apply-share {
  display: inline-flex; align-items: center; gap: 6px;
  border: 0; background: none; cursor: pointer; padding: 0;
  color: var(--primary); font-size: var(--t-xs); font-weight: 600;
}
.apply-share:hover { color: var(--primary-hover); }

/* Rendered job description (cbMarkdown output: h4 / ul / li / p). */
.apply-jd__body { color: var(--ink-3); line-height: 1.65; }
.apply-jd__body h4 {
  margin: var(--sp-5) 0 var(--sp-2); color: var(--ink);
  font-size: var(--t-base); font-weight: 700; letter-spacing: -0.01em;
}
.apply-jd__body h4:first-child { margin-top: 0; }
.apply-jd__body p { margin: 0 0 var(--sp-3); }
.apply-jd__body ul { margin: 0 0 var(--sp-3); padding-left: 1.2em; }
.apply-jd__body li { margin: 3px 0; }
.apply-jd__body code {
  font-family: var(--font-mono, monospace); font-size: 0.9em;
  background: var(--surface-sunken); padding: 1px 5px; border-radius: 5px;
}

/* The form card sticks while the (usually longer) description scrolls past. It
   sits fully below the hero — no negative offset — so it never overlaps the
   header band; the lifted shadow alone gives the floating feel. */
.apply-formcol { position: sticky; top: var(--sp-4); }
.apply-formcard { box-shadow: var(--shadow-lg, 0 10px 30px rgba(15,23,42,0.12)); }

@media (max-width: 860px) {
  .apply-cols { grid-template-columns: 1fr; padding-top: var(--sp-5); }
  .apply-formcol { position: static; }
}

/* CV upload control (see reference): file icon, a two-line label, and a Choose
   button + filename on the right. The native input is visually hidden and driven
   by the button.

   The tint follows the EMPLOYER's brand colour with transparency, so it stays
   readable on any brand. `--apply-brand` is injected on :root by the apply
   template only when the employer set a colour; the fallback keeps the default
   CareerBridge teal for everyone else. */
.apply-importbox {
  --upload-tint: var(--apply-brand, var(--primary));
  display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
  padding: var(--sp-4); border-radius: var(--r-md);
  background: color-mix(in srgb, var(--upload-tint) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--upload-tint) 24%, transparent);
}
.apply-upload__icon {
  flex: none; width: 40px; height: 40px; border-radius: var(--r-md);
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--upload-tint) 16%, transparent);
  color: var(--upload-tint);
}
.apply-upload__text { flex: 1 1 140px; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.apply-upload__title {
  font-weight: 600; font-size: var(--t-sm);
  color: color-mix(in srgb, var(--upload-tint) 75%, var(--ink));
}
.apply-upload__hint {
  font-size: var(--t-xs);
  color: color-mix(in srgb, var(--upload-tint) 55%, var(--muted));
}
.apply-upload__action { display: flex; align-items: center; gap: var(--sp-2); }
.apply-upload__name {
  font-size: var(--t-xs); color: var(--muted); max-width: 160px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Sections divide the single form card (not a card each — the mockup's form is
   one panel). A hairline separates them. */
.apply-sec { padding-top: var(--sp-4); border-top: 1px solid var(--line); }
.apply-sec:first-of-type { padding-top: 0; border-top: 0; }
.apply-sechd {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); margin-bottom: var(--sp-3);
}
.apply-req { color: var(--danger); }

.apply-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); }
@media (max-width: 480px) { .apply-grid { grid-template-columns: 1fr; } }

.apply-entry {
  display: flex; gap: var(--sp-2); align-items: flex-start;
  padding: var(--sp-3); border: 1px solid var(--line); border-radius: 10px;
  background: var(--surface-sunken);
}
.apply-entry__fields { flex: 1; display: flex; flex-direction: column; gap: var(--sp-2); min-width: 0; }
.apply-entry__x { flex: none; color: var(--muted); }
.apply-entry__x:hover { color: var(--danger); }
.apply-ef { margin: 0; }

/* Compact density inside the application card (mockup): small labels, tighter
   inputs. Scoped to .apply-formcard so the global .field/.input/.label stay put
   for every other page. */
.apply-formcard .card-hd .eyebrow { font-size: var(--t-2xs, 11px); }
.apply-formcard .label { font-size: var(--t-xs); margin-bottom: 4px; color: var(--ink-3); }
.apply-formcard .input,
.apply-formcard .select,
.apply-formcard .textarea { padding: 8px 11px; font-size: var(--t-sm); }
.apply-formcard .field { margin: 0; }
.apply-formcard .apply-sec { padding-top: var(--sp-3); }

/* Small label for the repeatable-entry rows (each input still gets a real
   label, per the a11y baseline — no placeholder-only inputs). */
.label--sm { font-size: var(--t-2xs, 11px); font-weight: 500; color: var(--muted); margin-bottom: 3px; }

/* Inline validation: error text sits directly under its field, and the input
   gets a danger border. Empty by default so it takes no space until it fires. */
.field-err { margin: 4px 0 0; font-size: var(--t-xs); color: var(--danger); min-height: 0; }
.field-err:empty { display: none; }
.input--error { border-color: var(--danger); }
.input--error:focus { box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 20%, transparent); }

/* Affect panel — the "Tone and energy" chart on the interview record page.
   ⚠️ Neutral teal at every value. Neither end of either axis is the good end,
   so the red-amber-green score band tokens must not appear here — a test greps
   this block for them. */
.affect { display: grid; gap: 8px; }
.affect__speaker { font-variant-numeric: tabular-nums; }
.affect__chart { display: grid; gap: 12px; }
.affect__chart:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.affect__chart svg { width: 100%; height: auto; display: block; }
.affect__caveat { max-width: 62ch; }

.affect__pole { font-size: 11px; fill: var(--muted); }
/* A solid hairline, one shade off the surface. Never dashed: dashing reads as
   a threshold or a projection when this is only the middle of the scale. */
.affect__mid { stroke: var(--border); stroke-width: 1; }
/* The uncertainty interval. A wider band means less certainty, and it must stay
   readable under the estimate rather than behind it. */
.affect__band { fill: var(--primary); fill-opacity: 0.16; }
.affect__band1 { stroke: var(--primary); stroke-opacity: 0.35; stroke-width: 6; }
.affect__line { fill: none; stroke: var(--primary); stroke-width: 2;
                stroke-linejoin: round; stroke-linecap: round; }
.affect__dot { fill: var(--primary); }
.affect__cross { stroke: var(--border-strong); stroke-width: 1; }
/* A 2px surface ring, so the cursor stays legible over its own interval band. */
.affect__cursor { fill: var(--primary); stroke: var(--surface); stroke-width: 2; }
.affect__tip { font-size: 12px; color: var(--muted); margin: 0;
               font-variant-numeric: tabular-nums; }
/* Visually hidden, still announced. Scoped rather than added to app.css's
   deliberately closed utility set. */
.affect__live { position: absolute; width: 1px; height: 1px; overflow: hidden;
                clip-path: inset(50%); white-space: nowrap; }
