/* F@B AI VETTING — candidate delivery.
   Palette + type per the 2024 F@B VP Brand Bible (§6 color, §7 type):
   deep teal #144D53 (primary) · mint #63B59C (secondary) · sun #F0D143
   (accent) · Bebas Neue (headings/display) · Urbanist (sub-headings/body).

   CONTRAST RULES (measured, see docs/BRAND_ALIGNMENT_BRIEF.md §2):
   - Teal is the ONLY brand color used as text on a light ground (9.48:1).
   - Mint (2.44:1) and sun (1.51:1) FAIL as text on light. They appear only
     as fills, rules, chips and highlights with dark ink on top.
   - Body copy stays ink-on-paper; sun is one accent moment per screen. */

:root {
  --paper: #f4f7f6;
  --paper-raised: #ffffff;
  --ink: #101d20;
  --ink-soft: #455055;
  /* 4.95:1 on white, 4.59:1 on --paper. Do not lighten: this token carries
     10.9-12.8px text (progress labels, footnotes), so it must clear 4.5:1. */
  --ink-faint: #677275;
  --hairline: #dde5e3;
  --teal: #144D53;       /* brand primary — the only brand text-on-light color */
  --teal-deep: #0d383d;  /* hover / pressed */
  --mint: #63B59C;       /* brand secondary — fill/rule only, never text on light */
  --mint-wash: #eaf4f0;
  --sun: #F0D143;        /* brand accent — fill/highlight only, never text on light */
  --sun-wash: #fdf6db;
  /* Functional extension, NOT brand: the Brand Bible has no error color and
     the platform needs one. Used sparingly, only where something is
     genuinely wrong. Pending Sage's nod. */
  --error: #9c3a2f;
  --error-wash: #f9ecea;
  --display: "Bebas Neue", "Oswald", "Arial Narrow", Impact, sans-serif;
  --sans: "Urbanist", "Century Gothic", "Segoe UI", Arial, sans-serif;
  /* Kept deliberately (Bible is silent on monospace): timestamps, UUIDs,
     codes and deltas need alignment. Scoped to .mono instrumentation.
     Pending Sage's nod. */
  --mono: "IBM Plex Mono", "Consolas", monospace;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html { -webkit-text-size-adjust: 100%; }

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.6;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  -webkit-font-smoothing: antialiased;
}

.mono { font-family: var(--mono); font-size: .72rem; letter-spacing: .04em; }

/* Bebas Neue is caps-only with a single weight: transform explicitly so the
   fallback stack matches, and never set 600/700 on it (synthetic bold). */
.display {
  font-family: var(--display);
  font-weight: 400;
  text-transform: uppercase;
}

/* ---------- masthead ---------- */
.masthead {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1.1rem clamp(1.25rem, 5vw, 3rem);
  border-bottom: 1px solid var(--hairline);
  background: var(--paper);
  position: sticky;
  top: 0;
  z-index: 10;
}
.wordmark {
  font-family: var(--display);
  font-weight: 400;
  text-transform: uppercase;
  font-size: 1.5rem;
  line-height: 1;
  letter-spacing: .02em;
}
.masthead-rule { width: 1px; height: 1.4rem; background: var(--hairline); }
.masthead-label {
  font-family: var(--display);
  font-weight: 400;
  font-size: .8rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--ink-faint);
}
.masthead-meta {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: .9rem;
  color: var(--ink-soft);
}
.progress-dots { display: inline-flex; gap: .3rem; }
.progress-dots .dot {
  width: 7px; height: 7px; border-radius: 50%;
  border: 1px solid var(--ink-faint);
  display: inline-block;
}
.progress-dots .dot.done { background: var(--mint); border-color: var(--mint); }
.progress-dots .dot.now  { background: var(--sun); border-color: var(--teal); }
.timer {
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  padding: .25rem .7rem;
  background: var(--paper-raised);
}
.timer.overtime { color: var(--ink); border-color: var(--sun); background: var(--sun-wash); }

/* ---------- page column ---------- */
.sheet {
  width: min(44rem, 100% - 2.5rem);
  margin: 0 auto;
  padding: clamp(2.25rem, 6vh, 4rem) 0 4rem;
  flex: 1;
}

.overline {
  font-family: var(--display);
  font-weight: 400;
  font-size: .84rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--teal);
  margin-bottom: .8rem;
}

h1 {
  font-family: var(--display);
  font-weight: 400;
  text-transform: uppercase;
  font-size: clamp(2.2rem, 5vw, 3rem);
  line-height: 1.05;
  letter-spacing: .01em;
  margin-bottom: 1.1rem;
}

/* Sub-headings are Urbanist 600 per the Bible's own split — long or
   sentence-case headings never go to caps-only Bebas. */
h2 {
  font-family: var(--sans);
  font-weight: 600;
  font-size: 1.25rem;
  margin-bottom: .6rem;
}

.lede { font-size: 1.15rem; color: var(--ink-soft); max-width: 36rem; }

/* ---------- welcome ---------- */
.facts {
  margin: 2.2rem 0;
  border-top: 1px solid var(--hairline);
}
.facts > div {
  display: grid;
  grid-template-columns: 8rem 1fr;
  gap: 1rem;
  padding: .85rem 0;
  border-bottom: 1px solid var(--hairline);
}
.facts dt {
  font-family: var(--display);
  font-weight: 400;
  font-size: .78rem;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--ink-faint);
  padding-top: .3rem;
}
.facts dd { color: var(--ink); }

.consent {
  margin-top: 2.5rem;
  background: var(--paper-raised);
  border: 1px solid var(--hairline);
  border-radius: 3px;
  padding: 1.6rem 1.8rem 1.8rem;
  box-shadow: 0 1px 0 rgba(16,29,32,.04), 0 8px 24px -18px rgba(16,29,32,.35);
}
.consent p { color: var(--ink-soft); font-size: .98rem; }
.consent-error {
  color: var(--error);
  background: var(--error-wash);
  border-left: 3px solid var(--error);
  padding: .5rem .8rem;
  margin-top: .9rem;
  font-size: .95rem;
}
.consent-check {
  display: flex;
  gap: .7rem;
  align-items: flex-start;
  margin: 1.2rem 0 1.5rem;
  cursor: pointer;
  font-size: .98rem;
}
.consent-check input {
  margin-top: .35rem;
  width: 1rem; height: 1rem;
  accent-color: var(--teal);
  flex: none;
}

/* ---------- buttons ---------- */
.btn-primary {
  font-family: var(--display);
  font-weight: 400;
  font-size: 1rem;
  line-height: 1.1;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--paper-raised);
  background: var(--teal);
  border: 1px solid var(--teal-deep);
  border-radius: 2px;
  padding: .8rem 1.6rem .7rem;
  cursor: pointer;
  transition: background .15s ease, transform .15s ease;
}
.btn-primary:hover { background: var(--teal-deep); transform: translateY(-1px); }
.btn-primary:active { transform: translateY(0); }
.btn-block { display: block; width: 100%; }
.btn-ghost {
  font-family: var(--display);
  font-weight: 400;
  font-size: 1rem;
  line-height: 1.1;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--ink-soft);
  background: transparent;
  border: 1px solid var(--hairline);
  border-radius: 2px;
  padding: .8rem 1.6rem .7rem;
  cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
.btn-ghost:hover { background: var(--paper); color: var(--ink); }

/* ---------- scenario ---------- */
.ai-policy {
  display: inline-block;
  color: var(--ink-soft);
  border: 1px solid var(--hairline);
  border-radius: 2px;
  padding: .3rem .6rem;
  background: var(--paper-raised);
  margin-bottom: 1.5rem;
}

.items { display: flex; flex-direction: column; gap: 1.4rem; margin: 1rem 0 2.5rem; }

.item {
  background: var(--paper-raised);
  border: 1px solid var(--hairline);
  border-radius: 3px;
  padding: 1.35rem 1.6rem 1.5rem;
  box-shadow: 0 1px 0 rgba(16,29,32,.04);
  animation: item-in .5s ease backwards;
}
.items .item:nth-child(1) { animation-delay: .05s; }
.items .item:nth-child(2) { animation-delay: .12s; }
.items .item:nth-child(3) { animation-delay: .19s; }
.items .item:nth-child(4) { animation-delay: .26s; }
.items .item:nth-child(5) { animation-delay: .33s; }
.items .item:nth-child(6) { animation-delay: .40s; }
@keyframes item-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

.item-tag {
  font-family: var(--display);
  font-weight: 400;
  font-size: .76rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--ink-faint);
  margin-bottom: .7rem;
}

.item-briefing { background: transparent; border: none; box-shadow: none; padding: .25rem .2rem; }
.item-briefing .item-tag { color: var(--teal); }

.item-document { border-left: 3px solid var(--hairline); }
.document-body {
  font-family: var(--mono);
  font-size: .82rem;
  line-height: 1.7;
  letter-spacing: 0;
  color: var(--ink-soft);
  white-space: normal;
}

/* A mid-scenario disruption is the one thing on screen that should catch
   the eye — that is the sun accent's job (fill/rule only, ink on top). */
.item-disruption { border-left: 3px solid var(--sun); }
.item-disruption .item-tag { color: var(--teal); }
.item-arrived { animation: item-arrive .6s cubic-bezier(.2,.8,.3,1) backwards; }
@keyframes item-arrive {
  from { opacity: 0; transform: translateY(-14px); }
  to   { opacity: 1; transform: none; }
}

.item-pending { display: none; }

.prose { font-size: 1.02rem; }
.prose p { margin-bottom: .8rem; }
.prose p:last-child { margin-bottom: 0; }

/* ---------- response areas ---------- */
.respond { margin-top: 1.2rem; border-top: 1px dashed var(--hairline); padding-top: 1rem; }
.respond-label {
  display: block;
  font-family: var(--display);
  font-weight: 400;
  font-size: .76rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--teal);
  margin-bottom: .55rem;
}
textarea {
  width: 100%;
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.6;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: 2px;
  padding: .85rem 1rem;
  resize: vertical;
  min-height: 8.5rem;
}
textarea:focus {
  outline: none;
  border-color: var(--teal);
  box-shadow: 0 0 0 3px rgba(20,77,83,.14);
}
.save-state {
  display: block;
  text-align: right;
  margin-top: .35rem;
  min-height: 1rem;
  color: var(--ink-faint);
}
.save-chip {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  animation: chip-in .25s ease;
  color: var(--teal);
}
.save-chip::before { content: "✓"; font-size: .8em; }
@keyframes chip-in {
  from { opacity: 0; transform: translateY(2px); }
  to   { opacity: 1; transform: none; }
}

/* ---------- submit ---------- */
.submit-bar {
  border-top: 1px solid var(--hairline);
  padding-top: 1.6rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  flex-wrap: wrap;
}
.submit-note { color: var(--ink-faint); font-size: .92rem; }

/* ---------- terminal pages (done / unavailable) ---------- */
.terminal-page { padding-top: 8vh; }
.terminal-ref { color: var(--ink-faint); margin-top: 2rem; }
.terminal-note { color: var(--ink-soft); margin-top: .5rem; }

/* ---------- colophon ---------- */
.colophon {
  border-top: 1px solid var(--hairline);
  padding: 1rem clamp(1.25rem, 5vw, 3rem);
  font-family: var(--mono);
  font-size: .65rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

@media (max-width: 40rem) {
  .facts > div { grid-template-columns: 1fr; gap: .15rem; }
  .masthead-label { display: none; }
}

/* ---------- email verification codebox ---------- */
.codebox{display:flex;gap:.55rem;margin:1.6rem 0;flex-wrap:wrap;justify-content:center}
.codebox input{width:3rem;height:3.6rem;text-align:center;font-family:var(--mono);
  font-size:1.4rem;border:1px solid var(--hairline);border-radius:4px;background:var(--paper);
  color:var(--ink);transition:border-color .15s,box-shadow .15s}
.codebox input:focus{outline:none;border-color:var(--teal);
  box-shadow:0 0 0 3px rgba(20,77,83,.14)}
.verify-notice{color:var(--teal);background:var(--mint-wash);border-left:3px solid var(--mint);
  padding:.5rem .8rem;margin-bottom:.6rem;font-size:.95rem}
.resend-form{margin-top:1.6rem}
.resend-line{color:var(--ink-soft);font-size:.95rem}
.linklike{background:none;border:none;padding:0;font:inherit;color:var(--teal);
  font-weight:600;cursor:pointer;text-decoration:none}
.linklike:hover{text-decoration:underline}

/* ---------- application form ---------- */
.appform { margin-top: 2rem; }
.appform .field { margin-bottom: 1.6rem; }
.field-label { display: block; font-family: var(--sans); font-size: 1rem;
  color: var(--ink); margin-bottom: .55rem; line-height: 1.4; }
.field-flag { margin-left: .5rem; font-size: .6rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink-faint); border: 1px solid var(--hairline);
  border-radius: 2px; padding: .1rem .35rem; vertical-align: middle; }
.appform input[type="number"], .appform input[type="text"],
.appform input[type="email"] { width: 100%; font-family: var(--sans);
  font-size: 1rem; color: var(--ink); background: var(--paper);
  border: 1px solid var(--hairline); border-radius: 2px; padding: .75rem .9rem; }
.appform input[type="number"]:focus, .appform input[type="text"]:focus,
.appform input[type="email"]:focus { outline: none; border-color: var(--teal);
  box-shadow: 0 0 0 3px rgba(20,77,83,.14); }
.choices { display: flex; flex-wrap: wrap; gap: .5rem; }
.choice { display: inline-flex; align-items: center; gap: .45rem; cursor: pointer;
  border: 1px solid var(--hairline); border-radius: 3px; padding: .5rem .85rem;
  background: var(--paper-raised); font-size: .98rem; transition: border-color .15s; }
.choice:hover { border-color: var(--teal); }
.choice input { accent-color: var(--teal); }
.field-invalid .field-label { color: var(--error); }
.field-invalid input, .field-invalid textarea { border-color: var(--error); }
.field-error { color: var(--error); font-size: .72rem; margin-top: .4rem; }

/* ---------- inbox simulator (real-inbox look) ---------- */
.items.inbox-frame {
  gap: 0;
  border: 1px solid var(--hairline);
  border-radius: 6px;
  overflow: hidden;
  background: var(--paper-raised);
  box-shadow: 0 1px 0 rgba(16,29,32,.04), 0 12px 30px -22px rgba(16,29,32,.4);
}
.inbox-bar {
  display: flex; align-items: center; justify-content: space-between;
  padding: .7rem 1.1rem; background: var(--teal); color: var(--paper-raised);
  border-bottom: 1px solid var(--teal-deep);
}
.inbox-brand { font-size: .74rem; letter-spacing: .16em; text-transform: uppercase;
  display: inline-flex; align-items: center; gap: .5rem; }
.inbox-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--mint); display: inline-block; }
.inbox-sub { font-size: .68rem; letter-spacing: .08em; color: rgba(255,255,255,.75); }

/* children sit flush as rows inside the frame */
.inbox-frame > .email,
.inbox-frame > .item {
  border-radius: 0; border-left: 0; border-right: 0; border-top: 0;
  border-bottom: 1px solid var(--hairline); box-shadow: none; margin: 0;
  animation: none;
}
.inbox-frame > .item-briefing { background: var(--paper); }
.inbox-frame > *:last-child { border-bottom: 0; }

/* email card */
.email { background: var(--paper-raised); padding: 1.1rem 1.35rem 1.3rem;
  animation: item-in .45s ease backwards; }
.email-head { display: flex; gap: .8rem; align-items: flex-start; margin-bottom: .7rem; }
.email-avatar { flex: none; width: 2.2rem; height: 2.2rem; border-radius: 50%;
  background: var(--teal); color: var(--paper-raised); display: flex;
  align-items: center; justify-content: center; font-family: var(--display);
  font-weight: 400; text-transform: uppercase; font-size: 1.05rem; }
.item-disruption .email-avatar { background: var(--sun); color: var(--ink); }
.email-meta { min-width: 0; }
.email-from { font-weight: 600; color: var(--ink); font-size: 1rem;
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.email-tag { font-size: .6rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-faint); border: 1px solid var(--hairline); border-radius: 2px;
  padding: .1rem .35rem; }
.item-disruption .email-tag { color: var(--ink); border-color: var(--sun); background: var(--sun-wash); }
.email-subject { color: var(--ink-soft); font-size: .95rem; }
.email-body { color: var(--ink); }
.email .respond { margin-top: 1rem; }

/* ---------- inbox triage workspace (two-pane) ---------- */
[x-cloak] { display: none !important; }
.triage { margin: 1rem 0 2.5rem; }
/* The frame holds still. Both panes already have a fixed height, so the only
   thing that moved was the page around them; sticking the frame means picking
   a message never slides the list out from under the pointer. */
.scenario-work .inbox-frame.triage {
  position: sticky;
  top: calc(var(--masthead-h) + .75rem);
}
.triage-grid { display: grid; grid-template-columns: minmax(15rem, 19rem) 1fr; }
.triage-list {
  list-style: none;
  border-right: 1px solid var(--hairline);
  background: var(--paper);
  height: clamp(30rem, 72vh, 44rem);
  overflow-y: auto;
}
.triage-row {
  display: flex; gap: .55rem; align-items: flex-start;
  padding: .8rem .95rem;
  border-bottom: 1px solid var(--hairline);
  cursor: pointer;
}
.triage-row:hover { background: rgba(20,77,83,.05); }
.triage-row:focus-visible { outline: 2px solid var(--teal); outline-offset: -2px; }
.triage-row.is-disruption { box-shadow: inset 3px 0 0 var(--sun); }
.triage-row.selected { background: var(--paper-raised); box-shadow: inset 3px 0 0 var(--teal); }
.row-dot {
  flex: none; width: 7px; height: 7px; margin-top: .5rem;
  border-radius: 50%; background: var(--teal); opacity: 0; transition: opacity .2s;
}
.triage-row.unread .row-dot { opacity: 1; }
.triage-row.unread .row-from { font-weight: 700; }
.row-main { min-width: 0; display: flex; flex-direction: column; gap: .1rem; }
.row-from { font-size: .95rem; color: var(--ink); display: flex; gap: .45rem; align-items: center; flex-wrap: wrap; }
.row-subject { font-size: .88rem; color: var(--ink-soft); }
.row-preview {
  font-size: .8rem; color: var(--ink-faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* The pane is the fixed-height box; the MESSAGE scrolls inside it and the
   action area is pinned below, always visible. Previously the whole pane
   scrolled, so the action chips and the reasoning box sat below the fold
   inside a scroll-within-a-scroll and candidates had to hunt for them. */
.triage-detail {
  background: var(--paper-raised);
  height: clamp(30rem, 72vh, 44rem);
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}
.triage-panel {
  display: flex;
  flex-direction: column;
  min-height: 0;
  height: 100%;
}
.email-scroll {
  /* basis 0, not auto: with a basis of auto the browser sizes this from its
     (very tall) content first and the shrink never lands where you expect. */
  flex: 1 1 0;
  /* Not 0. A flex item with min-height:0 will shrink to nothing, which is
     exactly what happened: the pinned footer took 254 of 336px and the email
     got 82. The message is the point of the screen, so it has a floor and the
     footer gives way first. */
  min-height: 13rem;
  overflow-y: auto;
  padding: 1.2rem 1.4rem 1rem;
}
.respond-pinned {
  flex: none;
  padding: .9rem 1.4rem 1.1rem;
  border-top: 1px solid var(--hairline);
  background: var(--paper);
  box-shadow: 0 -6px 14px -12px rgba(16,29,32,.5);
}
/* Everything in here is spend against the message above it, so it is kept
   deliberately tight. The textarea still drags taller if a candidate wants
   more room. */
.respond-pinned .respond-label { margin-bottom: .3rem; }
/* The action label and its chips share a row rather than stacking. Two
   stacked rows of label-then-controls cost ~40px twice over, and that came
   straight out of the message pane. */
.respond-pinned .respond-action-row {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  margin-bottom: .5rem;
}
.respond-pinned .respond-action-row .respond-label { margin-bottom: 0; flex: none; }
.respond-pinned .chips { margin: 0; gap: .4rem; }
.respond-pinned .chip { padding: .38rem .66rem; font-size: .8rem; }
.respond-pinned textarea {
  min-height: 4.5rem;
  resize: vertical;
}
.respond-pinned .save-state { margin-top: .3rem; }
.triage-empty { color: var(--ink-faint); padding: 2.5rem 0; text-align: center; }
.triage-pending { display: none; }
.triage-tasks { margin-top: 1.4rem; }

.chips { display: flex; gap: .5rem; flex-wrap: wrap; margin: .3rem 0 1.1rem; }
.chip {
  font-family: var(--display);
  font-weight: 400;
  font-size: .8rem;
  line-height: 1.1;
  text-transform: uppercase;
  letter-spacing: .06em;
  padding: .5rem .85rem .4rem;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  background: var(--paper);
  color: var(--ink-soft);
  cursor: pointer;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.chip:hover { border-color: var(--teal); color: var(--teal); }
.chip.on { background: var(--teal); border-color: var(--teal-deep); color: var(--paper-raised); }

@media (max-width: 48rem) {
  .triage-grid { grid-template-columns: 1fr; }
  .triage-list {
    height: auto;
    max-height: 11rem;
    border-right: 0;
    border-bottom: 1px solid var(--hairline);
  }
  .triage-detail { height: clamp(28rem, 76vh, 38rem); }
  .email-scroll { padding: 1rem 1.05rem .8rem; }
  .respond-pinned { padding: .8rem 1.05rem 1rem; }
}

/* ---------- live chat persona ---------- */
.chat-frame {
  margin: 1rem 0 2.5rem;
  border: 1px solid var(--hairline);
  border-radius: 6px;
  overflow: hidden;
  background: var(--paper-raised);
  box-shadow: 0 1px 0 rgba(16,29,32,.04), 0 12px 30px -22px rgba(16,29,32,.4);
  display: flex;
  flex-direction: column;
}
.chat-bar {
  display: flex; align-items: center; justify-content: space-between;
  padding: .7rem 1.1rem;
  background: var(--teal);
  color: var(--paper-raised);
  border-bottom: 1px solid var(--teal-deep);
}
.chat-persona { display: inline-flex; align-items: center; gap: .7rem; }
.chat-bar .email-avatar { background: var(--paper-raised); color: var(--teal); }
.chat-who { display: flex; flex-direction: column; line-height: 1.25; }
.chat-title { font-size: .64rem; letter-spacing: .08em; color: rgba(255,255,255,.78); }
.chat-presence { font-size: .68rem; letter-spacing: .08em; color: rgba(255,255,255,.75);
  display: inline-flex; align-items: center; gap: .45rem; }

.chat-thread {
  padding: 1.2rem 1.2rem .8rem;
  /* Grows with the conversation instead of reserving a fixed 24rem. A chat
     that opens with one message used to render that message and then a wall
     of empty paper down to the composer. */
  min-height: 9rem;
  max-height: 26rem;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: .55rem;
  background: var(--paper);
}
/* Sit the conversation on the bottom edge, the way every chat client does, so
   a short thread hugs the composer rather than floating at the top of its box.
   An auto top margin on a spacer rather than justify-content:flex-end, because
   flex-end clips the top of a thread that outgrows the box in some browsers
   and this collapses harmlessly once the content overflows. */
.chat-thread::before { content: ''; margin-top: auto; }
.bubble-row { display: flex; }
.bubble-row.from-you { justify-content: flex-end; }
.bubble {
  max-width: 78%;
  padding: .6rem .9rem;
  border-radius: 12px;
  font-size: .98rem;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: break-word;
  animation: item-in .3s ease backwards;
}
.from-them .bubble {
  background: var(--paper-raised);
  border: 1px solid var(--hairline);
  border-bottom-left-radius: 3px;
}
.from-you .bubble {
  background: var(--teal);
  color: var(--paper-raised);
  border: 1px solid var(--teal-deep);
  border-bottom-right-radius: 3px;
}
.bubble-typing { display: inline-flex; gap: .3rem; align-items: center; padding: .75rem .9rem; }
.bubble-typing span {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--ink-faint);
  animation: typing-dot 1.1s infinite ease-in-out;
}
.bubble-typing span:nth-child(2) { animation-delay: .18s; }
.bubble-typing span:nth-child(3) { animation-delay: .36s; }
@keyframes typing-dot {
  0%, 60%, 100% { transform: none; opacity: .45; }
  30% { transform: translateY(-4px); opacity: 1; }
}

/* The allowance and the clock, above the composer (2026-09-09).
   A cap the candidate cannot see is the Skillfully behaviour this platform
   exists to improve on. */
.chat-meta {
  display: flex; align-items: center; justify-content: space-between;
  gap: .8rem;
}
.chat-count { color: var(--ink-faint); }
.chat-clock {
  color: var(--ink-soft); font-variant-numeric: tabular-nums;
  padding: .15rem .6rem; border-radius: 999px;
  border: 1px solid var(--hairline); background: var(--paper);
}
/* Sun, not error red: the clock running low is the exercise working, not
   something going wrong. The border thickens so it reads at a glance for
   someone mid-sentence who is not looking straight at it. */
.chat-clock.is-urgent {
  background: var(--sun-wash); border-color: var(--sun); color: var(--ink);
}
.chat-expired {
  margin: 0; padding: .45rem .65rem; border-radius: 6px;
  background: var(--sun-wash); border: 1px solid var(--sun);
  color: var(--ink); line-height: 1.5;
}

/* A COLUMN, not a row. It was a row with .chat-meta inside it, so the
   counter, the clock, the textarea and Send were four items on one line and
   the button got squeezed to the frame edge. */
.chat-composer {
  display: flex;
  flex-direction: column;
  gap: .55rem;
  padding: .85rem 1.1rem 1rem;
  border-top: 1px solid var(--hairline);
  background: var(--paper-raised);
}
/* The start gate. Nothing is shown and nothing is timed until the candidate
   presses begin -- the reply clock used to run while they were still reading
   the task, which measures reading speed rather than composure. */
.chat-start {
  padding: 2rem 1.4rem 2.2rem;
  background: var(--paper);
  text-align: center;
}
.chat-start-line { font-size: 1.02rem; max-width: 30rem; margin: 0 auto .5rem; }
.chat-start-line strong { color: var(--teal); }
.chat-start-note {
  color: var(--ink-soft); font-size: .92rem;
  max-width: 28rem; margin: 0 auto 1.2rem;
}

.chat-input-row { display: flex; gap: .7rem; align-items: flex-end; }
.chat-input-row textarea[disabled] {
  opacity: .55; cursor: not-allowed; background: var(--paper);
}
.chat-input-row textarea { min-height: 3.4rem; flex: 1; }
.chat-input-row .btn-primary { flex: none; white-space: nowrap; }

/* ---------- email reply composer (copy_task) ---------- */
.email-stage { margin: 1rem 0 2.5rem; display: flex; flex-direction: column; gap: 1.1rem; }
.email-inbound {
  border: 1px solid var(--hairline);
  border-radius: 6px;
  box-shadow: 0 1px 0 rgba(16,29,32,.04), 0 12px 30px -22px rgba(16,29,32,.4);
}
.composer {
  background: var(--paper-raised);
  border: 1px solid var(--hairline);
  border-radius: 6px;
  overflow: hidden;
  box-shadow: 0 1px 0 rgba(16,29,32,.04);
}
.composer-head {
  display: flex;
  flex-direction: column;
  gap: .2rem;
  padding: .8rem 1.1rem;
  border-bottom: 1px solid var(--hairline);
  background: var(--paper);
}
.composer-line { font-size: .95rem; color: var(--ink); }
.composer-k {
  display: inline-block;
  min-width: 4.2rem;
  font-size: .64rem;
  text-transform: uppercase;
  letter-spacing: .16em;
  color: var(--ink-faint);
}
.composer-tools {
  display: flex;
  align-items: center;
  gap: .35rem;
  padding: .5rem 1.1rem;
  border-bottom: 1px solid var(--hairline);
}
.tool {
  font-family: var(--sans);
  font-size: .88rem;
  min-width: 2rem;
  padding: .3rem .55rem;
  border: 1px solid var(--hairline);
  border-radius: 3px;
  background: var(--paper);
  color: var(--ink-soft);
  cursor: pointer;
}
.tool:hover { border-color: var(--teal); color: var(--teal); }
.tool-italic { font-style: italic; }
.composer-count { margin-left: auto; color: var(--ink-faint); }
.composer-count.over { color: var(--error); }
.composer textarea {
  border: none;
  border-radius: 0;
  background: var(--paper-raised);
  min-height: 12rem;
  padding: 1rem 1.1rem;
}
.composer textarea:focus { box-shadow: none; }
.composer-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: .6rem 1.1rem;
  border-top: 1px solid var(--hairline);
  background: var(--paper);
}
.composer-foot .save-state { margin-top: 0; }

/* ---------- spoken scenario (typed fallback) ---------- */
.spoken-card { border-left: 3px solid var(--sun); }
.spoken-prep {
  display: flex;
  flex-wrap: wrap;   /* three columns in 300px is not three columns */
  gap: .9rem;
  align-items: flex-start;
  margin-top: 1.1rem;
  padding: .85rem 1rem;
  background: var(--sun-wash);
  border: 1px dashed var(--sun);
  border-radius: 4px;
}
.spoken-prep-k {
  flex: none;
  font-size: .64rem;
  text-transform: uppercase;
  letter-spacing: .18em;
  color: var(--teal);
  padding-top: .25rem;
}
.spoken-prep p { color: var(--ink-soft); font-size: .95rem; }
.spoken-note { margin-top: .8rem; color: var(--ink-faint); font-size: .66rem; }

/* Think time, counted down before recording starts on its own (db/025).
   Big number, because it is the only thing on screen that matters for those
   few seconds, and the candidate is looking at the prompt, not at us. */
.spoken-count {
  display: flex; align-items: center; gap: .7rem; flex-wrap: wrap;
  padding: .5rem .9rem; border-radius: 8px;
  background: var(--sun-wash); border: 1px solid var(--sun);
}
.spoken-count-num {
  font-family: var(--display); font-size: 2rem; line-height: 1;
  color: var(--ink); min-width: 1.4em; text-align: center;
  font-variant-numeric: tabular-nums;
}
.spoken-count-k { color: var(--ink-soft); }

/* The read gate's explanation, under a held submit button (db/025). Says WHY
   the button is off -- a disabled control with no reason given reads as a
   broken page, and a candidate who thinks the page is broken reloads it. */
.submit-hold {
  margin-top: .6rem; color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}
.submit-bar button[disabled] { opacity: .45; cursor: not-allowed; }
.submit-bar button[disabled]:hover { transform: none; background: var(--teal); }

/* ---------- prioritisation (drag to order) ----------
   The arrows are not a fallback. Dragging is unusable with a keyboard,
   unreliable on touch and invisible to a screen reader, and this is an
   assessment -- so both controls are always visible and drive the same move().
*/
.rank-card { border-left: 3px solid var(--teal); }
.rank-help { color: var(--ink-soft); margin: .9rem 0 .6rem; }

.rank-list { list-style: none; margin: 0; padding: 0; }
.rank-item {
  display: flex; align-items: center; gap: .7rem;
  padding: .7rem .8rem; margin-bottom: .5rem;
  background: var(--paper-raised); border: 1px solid var(--hairline);
  border-radius: 8px; cursor: grab;
}
.rank-item:last-child { margin-bottom: 0; }
.rank-item.is-dragging { opacity: .5; cursor: grabbing; border-color: var(--teal); }

.rank-pos {
  font-family: var(--display); font-size: 1.35rem; line-height: 1;
  color: var(--teal); min-width: 1.1em; text-align: center;
  font-variant-numeric: tabular-nums;
}
.rank-grip { color: var(--ink-faint); font-size: 1rem; }
.rank-text { flex: 1; line-height: 1.5; }

.rank-moves { display: flex; gap: .3rem; flex-shrink: 0; }
.rank-move {
  width: 2rem; height: 2rem; border-radius: 6px; cursor: pointer;
  background: var(--paper); border: 1px solid var(--hairline);
  color: var(--teal); font-size: .9rem; line-height: 1;
}
.rank-move:hover:not([disabled]) { border-color: var(--teal); background: var(--mint-wash); }
.rank-move[disabled] { opacity: .3; cursor: not-allowed; }

/* Screen-reader announcements. Present in the DOM and out of the visual
   layout: aria-live needs a rendered node, so display:none would silence it. */
.rank-live {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* The candidate's own previous answer, carried into the spoken half. Mint
   rather than the document hairline, so it reads as THEIRS rather than as
   more material handed to them. */
.carried-answer { border-left: 3px solid var(--mint); }
.carried-answer .item-tag { color: var(--teal); }

/* ---------- spoken call (turn-by-turn voice conversation) ----------
   The chat frame with a recorder where the composer would be. The other
   person's next line does not render until the previous reply is stored, so
   the candidate cannot read the end of the conversation before answering the
   start of it. */
.call-frame .chat-thread { min-height: 8rem; }
.call-switch {
  align-self: center; color: var(--ink-faint);
  padding: .4rem 0; letter-spacing: .06em;
}
/* The candidate's own turn, once recorded. Deliberately not a transcript:
   there is not one yet at this point, and a bubble reading "(awaiting
   transcription)" would look like something went wrong. */
.bubble-voice { background: var(--teal); color: var(--paper-raised); opacity: .85; }

.call-deck {
  padding: .9rem 1.1rem 1.1rem;
  border-top: 1px solid var(--hairline);
  background: var(--paper-raised);
}
.call-turn-k { color: var(--teal); margin-bottom: .5rem; }
.call-turn-prompt { font-size: .95rem; color: var(--ink-soft); margin-bottom: .6rem; }
.call-turn .voice { margin-top: 0; }
.call-ended { padding: .2rem; }

/* ---------- entry journey (verify → questions → consent) ----------
   Split screen: brand panel left, action card right. The candidate's first
   impression — warm, branded, confidence-building. */
.stage { display: grid; grid-template-columns: 1.05fr 1fr; min-height: 100vh; flex: 1; }

.brandside {
  position: relative; overflow: hidden; background: var(--paper);
  display: flex; flex-direction: column; justify-content: space-between;
  padding: clamp(2rem, 4vw, 3.5rem);
}
.brandart { position: absolute; inset: 0; z-index: 0; pointer-events: none; opacity: .9; }
/* Exclude .brandart: it is absolutely positioned above, and a bare `> *`
   rule has the same specificity, so it would override that and drop the
   decorative SVG into flow — pushing all the panel copy below the fold. */
.brandside > *:not(.brandart) { position: relative; z-index: 1; }
.brandside .wordmark span, .brandmark span { color: var(--teal); }
.brandcopy { max-width: 30rem; }
.eyebrow {
  font-family: var(--display); font-weight: 400; font-size: .84rem;
  letter-spacing: .08em;
  text-transform: uppercase; color: var(--teal); margin-bottom: 1rem;
}
.brandcopy h1 {
  font-size: clamp(2.4rem, 4vw, 3.4rem); line-height: 1.02;
  letter-spacing: .01em; margin-bottom: 1rem;
}
.brandcopy p { font-size: 1.12rem; color: var(--ink-soft); max-width: 26rem; }
.brandnote {
  font-family: var(--mono); font-size: .68rem; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-faint);
}

.authside {
  background: var(--paper-raised);
  display: flex; align-items: center; justify-content: center;
  padding: clamp(1.5rem, 4vw, 3rem);
}
.authcard { width: min(24rem, 100%); }
.authcard-wide { width: min(30rem, 100%); }
.brandmark {
  display: none; font-family: var(--display); font-weight: 400;
  text-transform: uppercase; letter-spacing: .02em;
  font-size: 1.4rem; margin-bottom: 2rem;
}

.journey { display: flex; gap: .4rem; justify-content: center; margin-bottom: 1.6rem; }
.journey i {
  width: 26px; height: 3px; border-radius: 2px;
  background: var(--hairline); transition: background .3s;
}
.journey i.on { background: var(--teal); }

.kicker {
  font-family: var(--display); font-weight: 400; font-size: .8rem;
  letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-faint);
  text-align: center; margin-bottom: .9rem;
}
/* Card titles carry sentence case and real nouns (role names), so they sit
   in Urbanist 700, not caps-only Bebas — the Bible's own split. */
.card-title {
  font-family: var(--sans); font-weight: 700; font-size: 1.5rem;
  line-height: 1.2; letter-spacing: -.01em;
  text-align: center; margin-bottom: .7rem;
}
.card-lede { color: var(--ink-soft); text-align: center; font-size: 1rem; margin-bottom: 1.6rem; }
.card-lede strong { color: var(--ink); }
.authcard .facts { margin: 1.6rem 0; }
.authcard .consent { margin-top: 1.6rem; padding: 1.3rem 1.4rem 1.5rem; }
.authcard .appform { margin-top: 1.6rem; }
.resend-form { text-align: center; }

/* success mark (done page) */
.success-mark {
  width: 56px; height: 56px; border-radius: 50%;
  background: var(--mint-wash); border: 1px solid var(--mint);
  display: flex; align-items: center; justify-content: center;
  color: var(--teal); margin-bottom: 1.4rem;
}

@media (max-width: 54rem) {
  .stage { grid-template-columns: 1fr; }
  .brandside { min-height: 220px; padding: 1.75rem; }
  .brandcopy h1 { font-size: 2rem; }
  .brandcopy p, .brandnote { display: none; }
  .brandmark { display: block; text-align: center; }
}

/* Chat wrap-up. The persona says goodbye once; the composer retires rather
   than inviting messages nobody will answer. */
.chat-ended {
  padding: 1rem 1.1rem;
  border-top: 1px solid var(--hairline);
  background: var(--mint-wash);
}
.chat-ended-line { font-size: .95rem; }
.chat-ended-note { font-size: .85rem; color: var(--ink-soft); margin-top: .2rem; }

/* ---------- spoken scenario: voice recording ---------- */
/* Brand note: the live dot is the one place --error earns its keep as a fill,
   because a recording indicator has to read as "live" at a glance. */
.voice {
  margin-top: 1rem; padding: 1rem; border: 1px solid var(--hairline);
  border-radius: 8px; background: var(--paper);
}
.voice-controls { display: flex; align-items: center; gap: .8rem; flex-wrap: wrap; }
.voice-btn {
  display: inline-flex; align-items: center; gap: .5rem;
  font-family: var(--display); font-weight: 400; text-transform: uppercase;
  letter-spacing: .06em; font-size: 1rem;
  padding: .5rem 1.1rem; border-radius: 999px; cursor: pointer;
  background: var(--teal); color: #fff; border: 1px solid var(--teal);
}
.voice-btn:hover { background: var(--teal-deep); border-color: var(--teal-deep); }
.voice-btn-stop { background: var(--error); border-color: var(--error); }
.voice-dot { width: 9px; height: 9px; border-radius: 50%; background: #fff; }
.voice-dot-live { animation: voicePulse 1.1s ease-in-out infinite; }
@keyframes voicePulse { 0%,100% { opacity: 1; } 50% { opacity: .25; } }
.voice-elapsed { font-size: 1rem; color: var(--ink); }
.voice-status { color: var(--ink-soft); }

.voice-done { margin-top: .9rem; }
.voice-done-line { font-size: .95rem; margin-bottom: .5rem; }
.voice-player { width: 100%; max-width: 30rem; display: block; }
.voice-again {
  margin-top: .6rem; background: none; cursor: pointer;
  border: 1px solid var(--hairline); border-radius: 999px;
  padding: .3rem .8rem; font-size: .78rem; color: var(--teal);
}
.voice-again:hover { border-color: var(--teal); }
.voice-note { color: var(--ink-faint); margin-top: .4rem; }
.voice-error {
  margin-top: .7rem; padding: .55rem .75rem; font-size: .9rem;
  background: var(--error-wash); border: 1px solid var(--error);
  border-radius: 6px; color: var(--ink);
}

.voice-fallback { margin-top: 1rem; }
.voice-fallback > summary {
  cursor: pointer; color: var(--ink-soft); font-size: .72rem;
  text-transform: uppercase; letter-spacing: .1em;
}
.voice-fallback[open] > summary { margin-bottom: .6rem; }

/* ---------- candidate feedback ---------- */
.feedback {
  margin-top: 1.6rem; padding: 1.1rem; border-radius: 8px;
  background: var(--paper); border: 1px solid var(--hairline);
}
.feedback-lede { font-size: .95rem; margin-bottom: .8rem; }
.feedback-rating { border: 0; padding: 0; margin-bottom: .9rem; display: flex;
  align-items: center; gap: .45rem; flex-wrap: wrap; }
.feedback-rating legend { margin-bottom: .35rem; }
.feedback-star { display: inline-flex; align-items: center; gap: .2rem;
  font-size: .9rem; cursor: pointer; }
.feedback-scale { color: var(--ink-faint); margin-left: .4rem; }
.feedback-field { display: block; }
.feedback-field textarea { width: 100%; margin-top: .3rem; resize: vertical; }
/* .btn-secondary had no base style at all, only a margin, so it rendered as
   a browser default button and looked broken next to everything else. */
.btn-secondary {
  display: inline-block;
  font-family: var(--display);
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: .06em;
  font-size: 1rem;
  line-height: 1;
  padding: .62rem 1.3rem;
  border-radius: 999px;
  cursor: pointer;
  color: var(--teal);
  background: var(--paper-raised);
  border: 1px solid var(--teal);
  transition: background .15s ease, color .15s ease;
}
.btn-secondary:hover { background: var(--teal); color: var(--paper-raised); }
.btn-secondary:disabled { opacity: .5; cursor: not-allowed; }
.feedback .btn-secondary { margin-top: .8rem; }
.feedback-thanks {
  margin-top: 1.6rem; padding: .9rem 1.1rem; border-radius: 8px;
  background: var(--mint-wash); border: 1px solid var(--mint);
  font-size: .95rem;
}
/* Reachable before finishing, on purpose: feedback only on the done page
   would miss exactly the people who abandon, who are the ones being asked
   about. */
.feedback-inline { margin: 1.4rem 0 0; }
.feedback-inline > summary {
  cursor: pointer; color: var(--ink-soft); font-size: .72rem;
  text-transform: uppercase; letter-spacing: .1em;
}

/* Confirmation has to be unmistakable: the first version swapped in a bare
   line of text, and a candidate could not tell whether anything had happened. */
.feedback-thanks { display: flex; gap: .7rem; align-items: flex-start; }
.feedback-tick {
  flex: none; width: 1.5rem; height: 1.5rem; border-radius: 50%;
  background: var(--mint); color: var(--ink);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: .9rem; font-weight: 700;
}
.feedback-thanks-head { font-weight: 600; }
.feedback-thanks-note { font-size: .88rem; color: var(--ink-soft); margin-top: .15rem; }
.feedback-error {
  display: none; margin-top: .6rem; padding: .5rem .7rem; font-size: .88rem;
  background: var(--error-wash); border: 1px solid var(--error); border-radius: 6px;
}
/* htmx sets this while the request is in flight. */
.feedback button[disabled] { opacity: .6; cursor: progress; }

/* ---- triage: fit the workspace to the screen -------------------------
   Measured on a 720px-tall laptop: 564px of heading, lede, AI-notice and
   briefing sat above the inbox, so the action chips began below the fold
   and the reasoning box below that. The workspace is the point of the
   page, so on the triage scenario the preamble is compacted and the frame
   is sized to whatever is left, rather than to a fixed number of rems.
   :has() scopes this to the triage scenario only. */
/* "Scenario 1 of 4" is already in the masthead two lines above, so on the
   triage scenario it is announced to screen readers and not drawn twice.
   That is 65px of vertical space handed back to the workspace. */
/* The triage screen is a workspace, not an article.

   .sheet is min(44rem, ...) because the rest of the assessment is prose and
   prose wants a reading measure. Applying that to a two-pane inbox left 704px
   of a 1280px screen in use and threw away 45% of the window, which then
   forced the message into a narrow column that needed far more vertical
   scrolling than it should have. Width is the cheapest fix available here:
   doubling the pane roughly halves the height the same email occupies. */
.sheet:has(.inbox-frame.triage) { width: min(82rem, 100% - 3rem); }

.sheet:has(.inbox-frame.triage) h1 {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}
.sheet:has(.inbox-frame.triage) .overline { margin-bottom: .5rem; }
.sheet:has(.inbox-frame.triage) .lede {
  font-size: 1rem;
  margin-bottom: .5rem;
}
.sheet:has(.inbox-frame.triage) .ai-policy { margin: .4rem 0 .7rem; }
.sheet:has(.inbox-frame.triage) .items { margin-bottom: .7rem; }
.sheet:has(.inbox-frame.triage) .item-briefing { padding: .7rem .9rem; }
.sheet:has(.inbox-frame.triage) .item-briefing .item-body {
  font-size: .95rem;
  line-height: 1.5;
}

/* One number, applied to both columns, so the list and the message pane
   always end level with each other.

   The previous version subtracted 29rem to keep the whole workspace inside
   the first screen. Measured on a 1280x720 laptop that produced a 336px pane,
   and since the action footer is 254px and cannot shrink, the message was
   left with 82px to show 369px of email. Reading a four-line email meant
   scrolling a box three lines tall.

   Fitting everything above the fold was the wrong thing to optimise for. The
   message is what the candidate is being asked to read, so it gets the space
   and the page is allowed to scroll. min-height on .email-scroll below is the
   real guarantee; this clamp only decides how much MORE than that it gets. */
.triage-list,
.triage-detail { height: clamp(30rem, calc(100vh - 17rem), 48rem); }

@media (max-width: 760px) {
  .triage-list { height: auto; }
  .triage-detail { height: clamp(26rem, calc(100vh - 11rem), 40rem); }
}

/* The base .respond rule adds a top margin to separate it from the message.
   Pinned, it is separated by its own border instead, and that margin was
   being taken straight out of the message's height. */
.respond-pinned { margin-top: 0; }
.email-scroll { padding-top: .9rem; }

/* Spoken scenarios are voice-only: no typed answer is accepted, so the copy
   says so plainly rather than letting a candidate discover it at submit. */
.spoken-required { margin-top: .5rem; }

/* A required step that fails must never be a dead end. There is no typing box
   to fall back to any more, so this has to carry the whole way out: what to
   try, and who to contact if none of it works. */
.voice-blocked {
  margin-top: .9rem; padding: .9rem 1rem;
  border: 1px solid var(--sun); border-radius: 6px;
  background: var(--sun-wash);
  font-size: .92rem; line-height: 1.55;
}
.voice-blocked p { margin: 0 0 .5rem; }
.voice-blocked p:last-child { margin-bottom: 0; }
.voice-blocked ul { margin: 0 0 .6rem; padding-left: 1.15rem; }
.voice-blocked li { margin-bottom: .3rem; }


/* ---------- microphone check ----------
   The setup page before the first spoken scenario. Reuses the .voice shell so
   the recorder a candidate meets here looks like the one they meet in the
   scenario -- the check is worthless if the real thing looks unfamiliar.

   Deliberately calm. This page can only be reached by someone about to be
   asked to speak out loud for the first time, and an alarming setup screen is
   its own kind of failure. Mint for "working", sun for "needs your attention",
   never error red: nothing has gone wrong yet. */
.miccheck-reassure {
  margin-top: .6rem; color: var(--ink-soft); font-size: .92rem;
  max-width: 36rem;
}
.miccheck { margin-top: 1.4rem; }

.miccheck-step { padding: .2rem 0; }
.miccheck-step + .miccheck-step {
  margin-top: 1.1rem; padding-top: 1.1rem;
  border-top: 1px solid var(--hairline);
}
.miccheck-step-k { color: var(--teal); margin-bottom: .6rem; }
.miccheck-step.is-done .miccheck-step-k { color: var(--ink-faint); }

.miccheck-say { font-size: .95rem; color: var(--ink-soft); margin-bottom: .5rem; }
.miccheck-say em { font-style: italic; color: var(--ink); }
.miccheck-say-ok { color: var(--teal); }

/* The meter. A bar that moves is the whole point: it answers "is my
   microphone on?" without the candidate having to interpret anything. */
.miccheck-meter {
  height: 10px; border-radius: 999px; overflow: hidden;
  background: var(--hairline); max-width: 26rem;
}
.miccheck-meter-fill {
  height: 100%; width: 0; border-radius: 999px;
  background: var(--sun);
  /* No CSS transition on width: the width is already smoothed in JS off the
     analyser, and transitioning it again lags the bar behind the voice by
     enough to read as broken. */
}
.miccheck-meter-fill.is-live { background: var(--mint); }

.miccheck-verdict { margin: .9rem 0 .3rem; }

/* The way out, kept quiet and kept present. A candidate who cannot get a
   microphone working must always be able to find this, and a candidate whose
   microphone is fine should never feel invited to use it. */
.miccheck-escape {
  display: block; margin-top: 1.1rem; padding: 0;
  background: none; border: none; cursor: pointer;
  font-family: var(--mono); font-size: .72rem; letter-spacing: .04em;
  color: var(--ink-faint); text-decoration: underline;
}
.miccheck-escape:hover { color: var(--teal); }


/* ---------- share-this-window notice ----------
   A FLOATING NOTIFICATION, not a block in the page flow, and that is the whole
   point of it. The first version sat above the scenario, which put it directly
   BEHIND Chrome's share dialog -- and that dialog opens seconds after load, so
   the candidate never saw the instruction they most needed. Seen live
   2026-08-30.

   Bottom right, because Chrome centres its picker: the two do not overlap at
   any viewport width worth supporting, so the instruction stays readable
   WHILE the candidate is choosing, which is the only moment it is useful. */
.share-notice {
  position: fixed;
  right: 1.25rem;
  bottom: 1.25rem;
  width: min(23rem, calc(100vw - 2.5rem));
  z-index: 900;
  border: 1px solid var(--hairline);
  border-left: 3px solid var(--sun);
  background: var(--paper-raised);
  border-radius: 10px;
  padding: .9rem 1.05rem;
  box-shadow: 0 12px 32px rgba(16, 29, 32, .18), 0 2px 6px rgba(16, 29, 32, .08);
  animation: share-notice-in .32s ease-out both;
}
@keyframes share-notice-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .share-notice { animation: none; }
}
/* Narrow screens: full width along the bottom, still clear of the dialog. */
@media (max-width: 30rem) {
  .share-notice { right: .6rem; left: .6rem; bottom: .6rem; width: auto; }
}

.share-notice-head { display: flex; align-items: baseline; justify-content: space-between; gap: .75rem; }
.share-notice-toggle {
  border: 0; background: none; padding: 0; cursor: pointer;
  font-size: .68rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-faint); text-decoration: underline; text-underline-offset: 3px;
}
.share-notice-toggle:hover { color: var(--ink); }

/* Collapsed: a chip, not a closed door. Everything still reachable in one
   click, because a candidate who hides this and then needs to retry sharing
   must not have thrown the instructions away. */
.share-notice[data-collapsed="true"] { padding: .55rem .85rem; width: auto; }
.share-notice[data-collapsed="true"] .share-notice-body { display: none; }
.share-notice[data-collapsed="true"] .share-notice-head { gap: 1rem; }

.share-notice-k {
  font-size: .68rem; letter-spacing: .09em; text-transform: uppercase;
  color: var(--teal); margin: 0 0 .35rem;
}
.share-notice-do { margin: 0 0 .45rem; line-height: 1.5; font-size: .92rem; }
.share-notice-do em { font-style: normal; color: var(--ink-soft); }
.share-window-name {
  display: inline-block; padding: .05rem .35rem; border-radius: 4px;
  background: var(--sun-wash); white-space: nowrap;
}
.share-notice-why { margin: 0; font-size: .84rem; color: var(--ink-soft); line-height: 1.5; }

/* Sharing started: the instruction has been followed, so stop giving it, then
   take the notification away entirely rather than leaving a panel sitting over
   the scenario for the rest of the sitting. */
.share-notice[data-state="sharing"] { border-left-color: var(--mint); }
.share-notice[data-state="sharing"] .share-notice-k { color: var(--teal); }
.share-notice[data-state="sharing"] .share-notice-do { display: none; }


/* Nothing, or audio only. Keep the instruction up -- they may want to retry --
   and say plainly what the report will show. */
.share-notice[data-state="none"],
.share-notice[data-state="audio-only"] { border-left-color: var(--error); }
.share-notice[data-state="none"] .share-notice-why,
.share-notice[data-state="audio-only"] .share-notice-why { color: var(--error); }

/* A scenario with no target time counts up plainly. It is not overtime:
   there is no time to be over. */
.timer.untimed { background: none; border-color: var(--hairline); color: var(--ink-soft); }

/* ---- Diction drills ------------------------------------------------------
   Deliberately lighter than .spoken-card. A drill is optional practice and a
   spoken scenario is a scored answer; giving them the same visual weight
   would tell a candidate the warm-up counts as much as the assessment. */
.drills-note {
  margin: 0 0 1.6rem;
  padding: .9rem 1.1rem;
  background: var(--sun-wash);
  border: 1px dashed var(--sun);
  border-radius: 4px;
}
.drills-note p { margin: 0; font-size: .95rem; }
.drills-note p + p { margin-top: .4rem; }

.drill-group { margin-bottom: 2.2rem; }
.drill-group-title {
  font-family: var(--display);
  font-size: 1.05rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--teal);
  margin: 0 0 .8rem;
  padding-bottom: .4rem;
  border-bottom: 1px solid var(--hairline);
}

.drill-card {
  border-left: 3px solid var(--hairline);
  padding: 1rem 1.2rem;
}
/* The line to read is the content of this card, so it is set as text to be
   read aloud rather than as a form label: larger, roomier, unhurried. */
.drill-line {
  font-family: var(--body);
  font-size: 1.15rem;
  line-height: 1.55;
  margin: 0;
  color: var(--ink);
}
.drill-focus {
  margin: .35rem 0 0;
  font-size: .8rem;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: .05em;
}
.drill-card .voice { margin-top: .8rem; }

/* ---------- the scenario brief: bulleted, and it follows you ---------- */
/* Sage asked for both on 2026-09-10: bullet points instead of a paragraph,
   and the task staying visible instead of scrolling away.

   --masthead-h is what the brief hangs below. The masthead is itself sticky at
   top 0, so without an offset the two would occupy the same strip. It is a
   variable rather than a number inline so there is one place to correct it,
   and it is a rem value so it tracks the type scale. */
:root { --masthead-h: 3.9rem; }

.scenario-brief {
  position: sticky;
  top: var(--masthead-h);
  z-index: 5;
  background: var(--paper-raised);
  border: 1px solid var(--hairline);
  border-left: 3px solid var(--moss);
  border-radius: 3px;
  padding: .7rem .95rem;
  margin: 0 0 1.5rem;
  /* Capped, with its own scrollbar. A long brief must never be able to take
     the screen away from the work it describes. */
  max-height: 38vh;
  overflow-y: auto;
}
.brief-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  cursor: pointer;
  font-size: .72rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--moss-deep);
  /* The default triangle sits oddly against a flex row; the word Hide/Show
     below carries the affordance instead. */
  list-style: none;
}
.brief-summary::-webkit-details-marker { display: none; }
.brief-toggle { font-size: .68rem; color: var(--ink-soft); }
.brief-toggle::after { content: "Hide"; }
.scenario-brief:not([open]) .brief-toggle::after { content: "Show"; }
.brief-body { padding-top: .5rem; }
.brief-body .lede { font-size: 1.02rem; margin: 0 0 .5rem; }
.brief-points {
  margin: 0 0 .5rem;
  padding-left: 1.1rem;
  max-width: 44rem;
}
.brief-points li {
  margin: .28rem 0;
  color: var(--ink);
  line-height: 1.45;
}
.brief-body .ai-policy { margin: .4rem 0 0; }

/* On a phone the brief would eat most of the viewport, so it gives up less
   of it and the candidate can still close it. */
@media (max-width: 640px) {
  .scenario-brief { max-height: 30vh; }
}

/* The answers a returning candidate already gave. Reuses .facts, which is the
   same shape of thing on the welcome card, and only softens the values so the
   list reads as a record rather than as fields to edit. */
.facts.on-file dd { color: var(--ink-soft); }
.facts.on-file dt { max-width: 26rem; }
.card-note {
  font-size: .72rem;
  color: var(--ink-soft);
  margin: .9rem 0 1.1rem;
}


/* ===== THE BRIEF AS A LEFT RAIL =============================================
   Elijah, 2026-09-11, on the sticky banner that came before: it does not look
   good. The banner kept the brief on screen, which is what Sage asked for, but
   it did it by taking a slice of the work's own column and then moving the
   work every time it opened or closed.

   A quarter and three quarters, side by side. The rail is tinted a shade off
   the work surface so they read as two different things rather than one long
   page, and it sticks on its own so the task is there however far down the
   work you are. */
/* THE PAGE GOES WIDE WHEN THE RAIL IS ON.
   .sheet is min(44rem, ...) -- a narrow reading column, which is right for a
   single stack of prose and wrong for two columns. Splitting 44rem into a rail
   and the work made the work narrower than it had been and left the viewport's
   margins untouched, which is exactly what "condensed in the middle, the
   padding and margin is too large" describes.

   The inbox already widened itself this way; now anything with a rail does. */
.sheet:has(.scenario-split) {
  width: min(94rem, 100% - 3rem);
  padding-top: clamp(1.5rem, 3.5vh, 2.5rem);
}
/* The inbox already had its own widening rule, `.sheet:has(.inbox-frame.triage)`
   at 82rem, and that selector is MORE SPECIFIC than the one above -- so on the
   one screen with both, the narrower of the two won and the rail sat 137px in
   from the edge instead of at it. Spelled out rather than fought with !important,
   because the next person reading either rule should see why this one exists. */
.sheet:has(.inbox-frame.triage):has(.scenario-split) {
  width: min(94rem, 100% - 3rem);
}

.scenario-split {
  display: grid;
  /* A readable fixed-ish rail, not a fraction. A fraction of a narrow page is
     a sliver; a fraction of a wide page is a waste. */
  grid-template-columns: clamp(16rem, 21%, 21rem) minmax(0, 1fr);
  gap: 1.75rem;
  align-items: start;
  margin-bottom: 1.5rem;
}
.scenario-work { min-width: 0; }  /* or a wide inbox row stretches the grid */

/* The rail is one grid child holding the task and, on the inbox, the briefing
   under it. Sticky lives here rather than on the task alone, so the pair
   travels together. */
.scenario-rail {
  position: sticky;
  top: calc(var(--masthead-h) + .75rem);
  z-index: 5;
  min-width: 0;
}
.scenario-brief.brief-rail {
  z-index: 5;
  /* The shade difference Elijah asked for. Deliberately small: this is a
     quieter surface than the work, not a competing one. */
  background: #f1ece1;
  border: 1px solid var(--hairline);
  border-left: 3px solid var(--moss);
  border-radius: 3px;
  padding: .85rem 1rem;
  margin: 0;
  /* Taller than the banner could be, because it no longer costs the work any
     vertical room -- it is beside it, not above it. */
  max-height: calc(100vh - var(--masthead-h) - 3rem);
  overflow-y: auto;
}
.brief-rail .brief-body .lede { font-size: .97rem; line-height: 1.55; }
.brief-rail .brief-points { padding-left: 1.05rem; max-width: none; }
.brief-rail .brief-points li { font-size: .92rem; margin: .45rem 0; line-height: 1.5; }
.brief-rail .ai-policy { font-size: .7rem; line-height: 1.5; }

/* Reference material the scenario hands the candidate -- the inbox briefing
   and its standing guidance -- sits under the task, in the same column.
   Elijah drew exactly this: task top left, briefing beneath it, work on the
   right. It is something you consult while working, so it belongs beside the
   work and not above it, pushing the work off the screen. */
.rail-briefing {
  margin-top: .9rem;
  padding: .8rem .95rem;
  background: var(--paper-raised);
  border: 1px solid var(--hairline);
  border-radius: 3px;
  font-size: .87rem;
  line-height: 1.5;
  color: var(--ink);
  max-height: 38vh;
  overflow-y: auto;
}
.rail-briefing .item-tag { margin: 0 0 .35rem; }
.rail-briefing p { margin: 0 0 .55rem; }
.rail-briefing p:last-child { margin-bottom: 0; }

/* Collapsed, the rail should give its width back rather than sit there as an
   empty quarter -- that is the point of letting them hide it. */
.scenario-split:has(.brief-rail:not([open]))  {
  grid-template-columns: auto minmax(0, 1fr);
}
.brief-rail:not([open]) { padding: .55rem .7rem; }

/* One column on anything narrow. Side by side needs real width; below that
   the rail goes back to being a panel above the work, which is what a phone
   can actually show. */
@media (max-width: 900px) {
  .scenario-split { display: block; }
  .scenario-rail { position: static; }
  .scenario-brief.brief-rail {
    max-height: 30vh;
    margin-bottom: 1.2rem;
  }
}

/* ===== ARRIVING MAIL, ANNOUNCED =============================================
   Elijah, 2026-09-11: when Priya's mail arrives, pop a notification bottom
   right. The row already slides into the list, but a candidate reading the
   detail pane on the right has no reason to be looking at the list on the
   left, so the arrival can pass unnoticed -- and the whole point of a timed
   disruption is that it interrupts.

   aria-live so it is announced rather than only seen. Not a modal and not
   focus-stealing: it must not take the keyboard away from someone mid-reply. */
.mail-toast {
  position: fixed;
  right: 1.25rem;
  bottom: 1.25rem;
  z-index: 60;
  width: min(21rem, calc(100vw - 2.5rem));
  background: var(--paper-raised);
  border: 1px solid var(--hairline);
  border-left: 3px solid var(--clay);
  border-radius: 3px;
  box-shadow: 0 6px 22px rgba(29, 28, 23, .16);
  padding: .7rem .85rem .75rem;
  display: flex;
  gap: .6rem;
  align-items: flex-start;
  animation: toast-in .22s ease-out;
}
@keyframes toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .mail-toast { animation: none; }
}
.mail-toast-dot {
  width: .5rem; height: .5rem; border-radius: 50%;
  background: var(--clay); margin-top: .38rem; flex: none;
}
.mail-toast-body { min-width: 0; }
.mail-toast-kicker {
  font-size: .62rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--clay); display: block; margin-bottom: .15rem;
}
.mail-toast-from { font-weight: 600; font-size: .9rem; display: block; }
.mail-toast-subject {
  font-size: .85rem; color: var(--ink-soft); display: block;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mail-toast-close {
  margin-left: auto; flex: none;
  background: none; border: 0; cursor: pointer;
  font-size: 1rem; line-height: 1; color: var(--ink-soft); padding: .15rem .2rem;
}
.mail-toast-close:hover { color: var(--ink); }


/* Each block of the prep note keeps a readable measure rather than being
   squeezed into a third of whatever is left. Below that they stack. */
.spoken-prep > * { min-width: 13rem; flex: 1 1 16rem; }
.spoken-prep-k { min-width: 0; flex: none; }

/* ---------- nothing can be skipped (2026-09-24) ----------
   The refusal a submit with gaps gets back, at the top of the page where the
   reloaded page puts the candidate's eyes. Same error treatment as the
   consent error: it is a thing they have to act on, not a notice. */
.submit-refused {
  color: var(--ink);
  background: var(--error-wash);
  border-left: 3px solid var(--error);
  padding: .7rem 1rem;
  margin: 0 0 1.4rem;
  font-size: .95rem;
  line-height: 1.5;
}
.submit-refused p { margin: 0 0 .4rem; }
.submit-refused ul { margin: 0; padding-left: 1.15rem; }
.submit-refused li { margin-bottom: .2rem; }
/* The "still to answer" hint under a held submit button. */
.submit-missing { max-width: 42rem; }
/* A scenario submitted with gaps, on an attempt from before the rule: half a
   dot rather than a claim that it was done. */
.progress-dots .dot.done.partial {
  background: linear-gradient(90deg, var(--mint) 50%, transparent 50%);
}
/* The ranking's explicit confirmation. */
.rank-confirm { margin-top: .8rem; }
.rank-confirmed { margin: 0; color: var(--teal); }

/* ==== written workbook (2026-09-25) ==== */
/* The consolidated screen's typed scenarios (formats/_written.html). Built
   from the EA-General formats' own parts -- the inbox frame and its teal bar,
   the reading pane beside a --paper reply pane, chat bubbles, the composer
   foot -- so it reads as the same product rather than a second design.

   Container queries, not media queries: the workbook sits in the full sheet
   today and in the narrower work column if the scenario brief ever becomes a
   left rail on this role, and it should lay itself out by the room it has. */

/* The page is wider when it holds a workbook, and never narrower than a
   split layout already made it. */
.sheet:has(.workbook):not(:has(.scenario-split)) { width: min(66rem, 100% - 2.5rem); }
.sheet:has(.workbook.has-rail):not(:has(.scenario-split)) { width: min(80rem, 100% - 2.5rem); }

.wb-shell { container: wbshell / inline-size; margin: 1rem 0 2.5rem; }
.workbook { --wb-top: calc(var(--masthead-h) + .75rem); }
.wb-main { container: wbmain / inline-size; display: flex; flex-direction: column; gap: 1.4rem; min-width: 0; }
.wb-raw { margin: 0; }

/* ---- the rail ---- */
.wb-rail {
  background: var(--paper-raised);
  border: 1px solid var(--hairline);
  border-radius: 6px;
  padding: .85rem .7rem .7rem;
  box-shadow: 0 1px 0 rgba(16,29,32,.04);
  margin-bottom: 1.2rem;
}
.wb-rail-head { display: flex; align-items: baseline; justify-content: space-between; gap: .6rem; padding: 0 .35rem; }
.wb-rail-k {
  font-family: var(--display); font-weight: 400; text-transform: uppercase;
  font-size: .84rem; letter-spacing: .08em; color: var(--teal);
}
.wb-rail-count { color: var(--ink-faint); font-size: .66rem; }
.wb-rail-bar { height: 4px; border-radius: 999px; background: var(--hairline); margin: .55rem .35rem .6rem; overflow: hidden; }
.wb-rail-bar i { display: block; height: 100%; width: 0; background: var(--mint); transition: width .3s ease; }
.wb-rail-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .15rem; }
.wb-rail-link {
  display: flex; align-items: center; gap: .65rem;
  padding: .5rem .45rem; border-radius: 4px;
  color: var(--ink); text-decoration: none;
  box-shadow: inset 3px 0 0 transparent;
}
.wb-rail-link:hover { background: rgba(20,77,83,.05); }
.wb-rail-link:focus-visible { outline: 2px solid var(--teal); outline-offset: -2px; }
.wb-rail-link.is-current { background: var(--mint-wash); box-shadow: inset 3px 0 0 var(--teal); }
.wb-rail-num {
  flex: none; width: 1.7rem; height: 1.7rem; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--display); font-size: .95rem; line-height: 1;
  border: 1px solid var(--hairline); background: var(--paper); color: var(--ink-soft);
}
.wb-rail-link.is-started .wb-rail-num { border-color: var(--mint); background: linear-gradient(90deg, var(--mint-wash) 50%, var(--paper) 50%); color: var(--ink); }
.wb-rail-link.is-answered .wb-rail-num { background: var(--mint); border-color: var(--mint); color: var(--ink); }
.wb-rail-text { min-width: 0; flex: 1; display: flex; flex-direction: column; line-height: 1.25; }
.wb-rail-name { font-size: .92rem; font-weight: 600; }
.wb-rail-sub { font-size: .8rem; color: var(--ink-soft); }
.wb-rail-state { flex: none; color: var(--ink-faint); font-size: .66rem; }
.wb-rail-link.is-answered .wb-rail-state { color: var(--teal); font-size: .8rem; }

/* ---- the part card: an inbox-style frame ---- */
.wb-card {
  background: var(--paper-raised);
  border: 1px solid var(--hairline);
  border-radius: 6px;
  overflow: hidden;
  box-shadow: 0 1px 0 rgba(16,29,32,.04), 0 12px 30px -22px rgba(16,29,32,.4);
  scroll-margin-top: var(--wb-top);
  animation: item-in .45s ease backwards;
}
.wb-card:focus { outline: none; }
.wb-card:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }
.wb-card-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: .6rem 1.2rem; background: var(--teal); color: var(--paper-raised);
  border-bottom: 1px solid var(--teal-deep);
}
.wb-kicker {
  display: inline-flex; align-items: center; gap: .5rem;
  font-size: .72rem; letter-spacing: .14em; text-transform: uppercase;
}
.wb-card-state { font-size: .66rem; letter-spacing: .08em; color: rgba(255,255,255,.8); }
.wb-card.is-answered .wb-card-state {
  color: var(--ink); background: var(--mint); border-radius: 999px; padding: .12rem .55rem;
}
.wb-card.is-answered .wb-card-state::before { content: "\2713\00a0"; }
.wb-card-bar-doc { background: var(--paper); color: var(--ink-faint); border-bottom-color: var(--hairline); }
.wb-title { margin: 0; padding: 1.05rem 1.35rem 0; font-size: 1.3rem; line-height: 1.3; }
.wb-body { padding: .8rem 1.35rem 1.35rem; }

/* Reading on the left, answering on the right: the inbox's reading pane and
   reply pane, side by side once there is room for both. */
.wb-split { display: grid; grid-template-columns: 1fr; gap: 0; padding: 0; margin-top: .8rem; }
.wb-split .wb-reading { padding: 0 1.35rem 1.2rem; }
.wb-split .wb-questions {
  padding: 1.1rem 1.35rem 1.35rem;
  background: var(--paper);
  border-top: 1px solid var(--hairline);
}
@container wbmain (min-width: 46rem) {
  .wb-split { grid-template-columns: minmax(0, 1fr) minmax(0, 1.12fr); margin-top: 0; }
  .wb-split .wb-reading { padding: 1rem 1.5rem 1.5rem 1.35rem; }
  .wb-split .wb-questions { border-top: 0; border-left: 1px solid var(--hairline); padding: 1.1rem 1.35rem 1.35rem; }
  .wb-card:has(.wb-split) .wb-title { padding-bottom: .1rem; }
  /* The situation stays in view while the second answer is written. */
  .wb-split .wb-reading { position: sticky; top: var(--wb-top); align-self: start; }
  /* One question and nothing else to read: the question on the left, the
     box on the right, the same shape as the split cards. */
  .wb-body:not(.wb-split) .wb-question {
    display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.12fr);
    gap: 1.5rem; align-items: start;
  }
}

/* ---- what the candidate reads ---- */
.wb-reading { min-width: 0; }
.wb-p { font-size: 1.02rem; line-height: 1.65; margin: 0 0 .8rem; max-width: 40rem; }
.wb-reading > :last-child { margin-bottom: 0; }
.wb-document .wb-reading { max-width: 44rem; }

/* What the other person said: a chat bubble from them, not a file. */
.wb-quote { margin: 1rem 0 0; }
.wb-quote-k {
  display: block; font-size: .64rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-faint); margin: 0 0 .35rem .15rem;
}
.wb-bubble {
  margin: 0; max-width: 34rem;
  padding: .7rem .95rem; border-radius: 12px; border-bottom-left-radius: 3px;
  background: var(--mint-wash); border: 1px solid #d3e7df;
  font-size: 1rem; line-height: 1.55; color: var(--ink);
}

/* A note from a person: the inbox's message card. */
.wb-message {
  border: 1px solid var(--hairline); border-radius: 6px; padding: .95rem 1.1rem 1.05rem;
  box-shadow: 0 1px 0 rgba(16,29,32,.04), 0 12px 30px -24px rgba(16,29,32,.4);
  margin: 0 0 .8rem; animation: none;
}
.wb-message .email-head { align-items: center; margin-bottom: .55rem; }

/* ---- the questions ---- */
.wb-question + .wb-question { margin-top: 1.25rem; padding-top: 1.2rem; border-top: 1px dashed var(--hairline); }
.wb-ask { min-width: 0; }
.wb-q-tag {
  font-family: var(--display); font-weight: 400; font-size: .76rem;
  text-transform: uppercase; letter-spacing: .08em; color: var(--teal); margin: 0 0 .3rem;
}
.wb-q-label { display: block; font-size: 1.03rem; font-weight: 600; line-height: 1.45; color: var(--ink); margin: 0 0 .6rem; }
.wb-q-label.wb-q-long { font-weight: 400; font-size: 1.02rem; line-height: 1.65; }
.wb-q-label.respond-label { font-size: .76rem; font-weight: 400; color: var(--teal); }
.wb-helper {
  font-size: .9rem; line-height: 1.5; color: var(--ink-soft);
  border-left: 2px solid var(--mint); padding: .05rem 0 .05rem .6rem; margin: 0 0 .7rem;
}

/* The answer box: the composer's frame, with its word count and save state
   in the foot where the email composer keeps its own. */
.wb-answer {
  background: var(--paper-raised); border: 1px solid var(--hairline); border-radius: 6px;
  overflow: hidden; box-shadow: 0 1px 0 rgba(16,29,32,.04);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.wb-answer:focus-within { border-color: var(--teal); box-shadow: 0 0 0 3px rgba(20,77,83,.14); }
.wb-answer textarea {
  display: block; border: 0; border-radius: 0; background: var(--paper-raised);
  min-height: 10rem; padding: .9rem 1rem;
}
.wb-answer textarea:focus { box-shadow: none; }
.wb-answer-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: .4rem .9rem; border-top: 1px solid var(--hairline); background: var(--paper);
  min-height: 2rem;
}
.wb-answer-foot .save-state { margin: 0; min-height: 0; }
.wb-words { color: var(--ink-faint); }
.wb-split .wb-answer textarea { min-height: 9rem; }

/* ---- the workbook with its rail ---- */
@container wbshell (min-width: 58rem) {
  .workbook.has-rail {
    display: grid; grid-template-columns: clamp(13rem, 24%, 15.5rem) minmax(0, 1fr);
    gap: clamp(1rem, 2.2%, 1.6rem); align-items: start;
  }
  .has-rail .wb-rail {
    position: sticky; top: var(--wb-top); z-index: 4; margin: 0;
    max-height: calc(100vh - var(--wb-top) - 1rem); overflow-y: auto;
  }
}
/* Narrow: the rail becomes a compact progress strip -- numbered stops that
   still jump to their part and still show what is answered. */
@container wbshell (max-width: 57.99rem) {
  .wb-rail { padding: .7rem .75rem .75rem; }
  .wb-rail-head, .wb-rail-bar { padding: 0; margin-left: 0; margin-right: 0; }
  .wb-rail-list { flex-direction: row; flex-wrap: wrap; gap: .4rem; }
  .wb-rail-link { padding: .15rem; border-radius: 50%; box-shadow: none; }
  .wb-rail-link.is-current { background: none; box-shadow: none; }
  .wb-rail-link.is-current .wb-rail-num { outline: 2px solid var(--teal); outline-offset: 1px; }
  .wb-rail-num { width: 2.2rem; height: 2.2rem; font-size: 1.05rem; }
  .wb-rail-text, .wb-rail-state { display: none; }
}
@container wbmain (max-width: 30rem) {
  .wb-card-bar { padding: .55rem .95rem; }
  .wb-title { padding: .9rem 1rem 0; font-size: 1.18rem; }
  .wb-body { padding: .7rem 1rem 1.1rem; }
  .wb-split .wb-reading { padding: 0 1rem 1rem; }
  .wb-split .wb-questions { padding: 1rem 1rem 1.1rem; }
  .wb-card-state { display: none; }
  .wb-card.is-answered .wb-card-state { display: inline; }
}
@media (prefers-reduced-motion: reduce) {
  .wb-card { animation: none; }
  .wb-rail-bar i { transition: none; }
}

/* ---- the Parts list inside the brief rail (2026-09-25) ----
   Unified moved onto EA-General's brief rail, and the workbook's Parts list
   moved into it, under the brief: ONE left column, not a brief rail and a
   parts rail side by side. The rail is one sticky column; the brief keeps its
   own cap, the Parts list gives way first and scrolls, and the recording
   notice (in the rail from 80rem, folded to a chip once sharing starts) sits
   under both. */
@media (min-width: 901px) {
  .scenario-rail:has(.wb-rail) {
    display: flex; flex-direction: column; gap: .9rem;
    max-height: calc(100vh - var(--masthead-h) - 1.5rem);
  }
  /* Both the brief and the Parts list may give way (each scrolls inside
     itself); the notice never does. Spelled with :has(.share-notice) as well,
     to outrank the frame rule that holds the brief at its full height when
     the rail carries only the brief and the notice. */
  .scenario-rail:has(.wb-rail) > .scenario-brief,
  .scenario-rail:has(.wb-rail):has(.share-notice) > .scenario-brief { flex: 0 1 auto; min-height: 7rem; }
  /* Hidden, the brief is its summary line and nothing else. */
  .scenario-rail:has(.wb-rail) > .scenario-brief.brief-rail:not([open]) { min-height: 0; }
  .scenario-rail > .wb-rail { flex: 0 1 auto; min-height: 8.5rem; overflow-y: auto; margin: 0; padding: .7rem .6rem .5rem; }
  .scenario-rail > .wb-rail .wb-rail-bar { margin-bottom: .45rem; }
  .scenario-rail > .wb-rail .wb-rail-link { padding: .34rem .4rem; gap: .55rem; }
  .scenario-rail > .wb-rail .wb-rail-num { width: 1.55rem; height: 1.55rem; font-size: .88rem; }
  .scenario-rail > .wb-rail .wb-rail-name { font-size: .86rem; }
  .scenario-rail > .wb-rail .wb-rail-sub { font-size: .76rem; }
}
/* The frame holds a rail carrying the notice to 8rem short of the viewport,
   so the open notice is on screen at load, under the heading, when the share
   picker opens. Once the notice has folded to its chip that reserve is just
   Parts rows scrolled out of sight, so a workbook rail takes the room back. */
@media (min-width: 80rem) {
  .scenario-rail:has(.wb-rail):has(.share-notice[data-collapsed="true"]) {
    max-height: calc(100vh - var(--masthead-h) - 1.5rem);
  }
}
/* Hiding the brief must not collapse the column to the width of the word
   "Show" when the Parts list lives in it too. */
.scenario-split:has(.brief-rail:not([open])):has(.wb-rail) {
  grid-template-columns: clamp(16rem, 21%, 21rem) minmax(0, 1fr);
}
/* One column on a narrow screen: the Parts list becomes the same compact
   strip of numbered stops the workbook uses when it is narrow. */
@media (max-width: 900px) {
  .scenario-rail > .wb-rail { margin: 0 0 1.2rem; padding: .7rem .75rem .75rem; }
  .scenario-rail > .wb-rail .wb-rail-head,
  .scenario-rail > .wb-rail .wb-rail-bar { padding: 0; margin-left: 0; margin-right: 0; }
  .scenario-rail > .wb-rail .wb-rail-list { flex-direction: row; flex-wrap: wrap; gap: .4rem; }
  .scenario-rail > .wb-rail .wb-rail-link { padding: .15rem; border-radius: 50%; box-shadow: none; }
  .scenario-rail > .wb-rail .wb-rail-link.is-current { background: none; box-shadow: none; }
  .scenario-rail > .wb-rail .wb-rail-link.is-current .wb-rail-num { outline: 2px solid var(--teal); outline-offset: 1px; }
  .scenario-rail > .wb-rail .wb-rail-num { width: 2.2rem; height: 2.2rem; font-size: 1.05rem; }
  .scenario-rail > .wb-rail .wb-rail-text,
  .scenario-rail > .wb-rail .wb-rail-state { display: none; }
}
/* ==== end written workbook ==== */

/* ==== page frame polish (2026-09-25) ====
   The shared frame of every scenario page: heading, width, the brief banner,
   the screen-recording notice and the submit bar. Presentation only -- no
   selector here hides, disables or moves an answer field, and the submit
   gate (scenario.html, FabUnanswered) is untouched.

   Written against the consolidated (EA-UNI) screen looking unfinished beside
   EA-General. EA-General's pages keep their rail; everything below either
   applies to both or is scoped to the page without a rail. */

/* The masthead measures 64-65px; 3.9rem (62.4px) tucked the sticky brief a
   couple of pixels under it. */
:root { --masthead-h: 4.0625rem; }

/* ---- the heading: what this is, then where you are ----------------------
   "Scenario 3 of 10" was the largest thing on the page and said nothing about
   the task. It is the overline now; the heading is the module name (or the
   format label). Smaller than the old h1: it titles a work surface, it is not
   a poster. */
.scenario-head { margin: 0 0 1.2rem; }
.scenario-head .overline { margin-bottom: .4rem; }
.scenario-title {
  font-size: clamp(1.85rem, 3.1vw, 2.5rem);
  line-height: 1.02;
  margin: 0;
}
.sheet:has(.scenario-split) .scenario-head { margin-bottom: 1rem; }

/* ---- width -----------------------------------------------------------------
   Without a rail the scenario sat in the 44rem prose column: ~700px of a
   1366px screen, the rest empty. :where() keeps these at the specificity of
   plain `.sheet`, so every more specific layout rule -- the rail's 94rem, the
   inbox's 82rem, the written workbook's own widths -- still wins. */
.sheet:where(:has(> .scenario)) { width: min(60rem, 100% - 2.5rem); }
.sheet:where(:has(> .scenario.has-dock)) { width: min(86rem, 100% - 3rem); }

/* ---- the recording notice gets a place of its own ------------------------
   It floated over the page, so on a long scenario it sat on answer boxes and
   on the submit bar. Now the layout reserves room for it:

   * No rail: a narrow dock column beside the work. The notice keeps its old
     spot -- bottom right, clear of Chrome's centred picker -- by sticking to
     the bottom of the viewport inside that column, so it can never cover
     anything.
   * Rail (EA-General): it docks in the rail under the task. Only from 80rem,
     where the rail stays left of a centred share picker.
   * Anything narrower: the fixed card it always was, with room reserved at
     the end of the page so the submit bar can be scrolled clear of it.

   Its text, its Hide/Show toggle and the data-state styling are unchanged. */
@media (min-width: 75rem) {
  .scenario.has-dock {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 20rem;
    column-gap: 2rem;
    align-items: stretch;
  }
  .scenario-main { min-width: 0; }
  .scenario-dock {
    min-width: 0;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
  }
  .scenario-dock .share-notice {
    position: sticky;
    right: auto;
    bottom: 1.25rem;
    width: 100%;
    margin-top: 1.5rem;
    box-shadow: 0 6px 18px rgba(16, 29, 32, .10);
  }
  .scenario-dock .share-notice[data-collapsed="true"] { width: auto; align-self: flex-end; }
}
@media (min-width: 80rem) {
  .scenario-rail:has(.share-notice) {
    display: flex;
    flex-direction: column;
    gap: .9rem;
    /* Sized for where the rail sits BEFORE it sticks (under the heading),
       so the notice is on screen at load, when the share picker opens. */
    max-height: calc(100vh - var(--masthead-h) - 8rem);
  }
  /* The briefing gives up height (it scrolls); the task keeps its own cap and
     the notice does not shrink, or the instruction it exists for would scroll
     out of sight. Once sharing starts the notice folds to a chip and the
     briefing gets the room back. */
  .scenario-rail:has(.share-notice) > .scenario-brief { flex: none; }
  .scenario-rail:has(.share-notice) > .rail-briefing {
    flex: 0 1 auto; min-height: 6rem; max-height: none; margin-top: 0;
  }
  .scenario-rail .share-notice {
    position: static;
    width: auto;
    flex: none;
    box-shadow: none;
  }
  .scenario-rail .share-notice[data-collapsed="true"] { align-self: flex-start; }
  /* Hiding the task must not squeeze an open notice into the width of the
     word "Show". */
  .scenario-split:has(.brief-rail:not([open])):has(.share-notice[data-collapsed="false"]) {
    grid-template-columns: clamp(16rem, 21%, 21rem) minmax(0, 1fr);
  }
}
@media (max-width: 74.99rem) {
  .scenario.has-dock:has(.share-notice[data-collapsed="false"]) { padding-bottom: 15rem; }
}
@media (max-width: 79.99rem) {
  .scenario:has(.scenario-rail .share-notice[data-collapsed="false"]) { padding-bottom: 15rem; }
}
@media (max-width: 30rem) {
  .scenario:has(.share-notice[data-collapsed="false"]) { padding-bottom: 22rem; }
}

/* ---- the brief banner (pages without a rail) ------------------------------
   Same surface as EA-General's rail -- the tint Elijah asked for -- so the two
   assessments read as one product, and a soft edge so, when it is holding
   position over the work, it reads as a layer rather than as page. */
.scenario-brief:not(.brief-rail) {
  background: #f1ece1;
  border-color: #e6dfd0;
  box-shadow: 0 8px 16px -14px rgba(16, 29, 32, .45);
}
.scenario-brief:not(.brief-rail) .ai-policy { background: var(--paper-raised); }

/* ---- the submit bar --------------------------------------------------------
   Three flex items wrapping freely put the note left, the button in the
   middle and the "Still to answer" hint wherever it fell. Note left, button
   right, the hint under the button. */
.submit-bar {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 1.5rem;
  row-gap: .45rem;
}
.submit-bar .submit-hold {
  grid-column: 1 / -1;
  justify-self: end;
  text-align: right;
  margin-top: 0;
}
@media (max-width: 40rem) {
  .submit-bar { grid-template-columns: minmax(0, 1fr); }
  .submit-bar .btn-primary { width: 100%; }
  .submit-bar .submit-hold { justify-self: start; text-align: left; }
}

/* ---- spoken: the "for reference" material reads as reading ---------------
   A spoken scenario that follows a written one (the QuickTag job aid, the
   3pm prioritisation) carries the material across as a document item. In the
   inbox's small grey monospace it read as an attachment to squint at, not as
   the thing the candidate is about to talk about. Same words, reading face. */
.scenario:has(.spoken-card) .item-document .document-body {
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.65;
  color: var(--ink);
  max-width: 44rem;
}
.scenario:has(.spoken-card) .item-document { border-left-color: var(--mint); }
/* ==== end page frame polish ==== */
