/* =====================================================================
   RecruitBot HR portal — what this portal adds on top of the kit.
   Tokens only; no new hexes (add them to _shared/brand/tokens.css).
   ===================================================================== */

/* The candidate record carries tabs, a journey strip and a thread: it
   needs more room than the kit's 580px record drawer. */
.drawer.wide { width: min(780px, 100%); }

/* A row that is waiting on the recruiter: marked on the row itself, not
   only counted in the nav (PATTERNS §6). */
.tbl tbody tr.needs td:first-child { box-shadow: inset 3px 0 0 var(--hl-red); }
.tbl td .who { display: flex; align-items: center; gap: .65rem; }
.avatar {
  width: 32px; height: 32px; flex: 0 0 32px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--brand-100); color: var(--brand-800);
  font-size: .72rem; font-weight: 700; letter-spacing: .02em;
}
.dot-new {
  display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  background: var(--hl-red); margin-left: .35rem; vertical-align: middle;
}

/* ---- Journey strip: one chip per task, in order -------------------- */
/* A tick for what is done, a dot for what is not. Every chip gets a
   mark, not only the two tones that had one: a step still open is the
   one a recruiter is looking for, and it was the only one arriving
   unmarked. The mark is drawn here rather than written into the label
   so it cannot end up doubled. */
.journey { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: .6rem; }
.journey .chip::before { content: "●"; font-size: .55rem; font-weight: 700; }
.journey .chip.ok::before { content: "✓"; font-size: inherit; }

/* ---- Facts row at the top of the record ---------------------------- */
.facts { display: flex; flex-wrap: wrap; gap: .4rem .9rem; align-items: center; margin-top: .35rem; }
.facts .muted { font-size: .84rem; }

/* ---- Scores ---------------------------------------------------------- */
.scores { display: grid; grid-template-columns: repeat(3, 1fr); gap: .75rem; margin-top: .6rem; }
.score { border: 1px solid var(--line); border-radius: var(--r-surf); padding: .7rem .8rem; }
.score b { display: block; font-size: 1.35rem; font-variant-numeric: tabular-nums; }
.score small { color: var(--muted); font-size: .72rem; text-transform: uppercase; letter-spacing: .1em; font-weight: 700; }
.bar { height: 4px; background: var(--shade); border-radius: 2px; margin-top: .45rem; overflow: hidden; }
.bar i { display: block; height: 100%; background: var(--brand-600); }

/* ---- WhatsApp thread ------------------------------------------------- */
.thread { display: flex; flex-direction: column; gap: .45rem; margin: .4rem 0 1rem; }
.bubble {
  max-width: 82%; padding: .5rem .75rem; border-radius: var(--r-surf);
  font-size: .88rem; line-height: 1.45; border: 1px solid var(--line); background: var(--panel);
}
.bubble small { display: block; margin-top: .2rem; font-size: .68rem; color: var(--muted); }
.bubble.cand { align-self: flex-start; }
.bubble.hr, .bubble.bot { align-self: flex-end; background: var(--brand-50); border-color: var(--brand-200); }
.bubble.bot { background: var(--panel-2); }
.quick { display: flex; flex-wrap: wrap; gap: .35rem; margin-bottom: .6rem; }

/* ---- The Applicants toolbar ------------------------------------------
   The kit's .toolbar lets the search grow and wraps the rest. The two
   dropdowns need a width of their own — the kit's .select is
   full-width, which is right in a form and wrong in a toolbar. */
.toolbar .select-wrap { flex: 0 1 auto; }
.toolbar .select { width: auto; min-width: 11rem; max-width: 16rem; margin: 0; }
@media (max-width: 720px) {
  .toolbar .select-wrap { flex: 1 1 100%; }
  .toolbar .select { width: 100%; max-width: none; }
}

/* The chips are two groups: people waiting for an answer, then people
   who have had one. A hairline says so without a second heading. */
.chip-sep {
  width: 1px; align-self: stretch; margin: .1rem .35rem;
  background: var(--line);
}

/* "Waiting on us" is the row that should catch the eye: somebody sent
   something and nobody has looked at it. Same marker the review queues
   use for a row that needs a decision. */
.tbl tbody tr.needs td:first-child { box-shadow: inset 3px 0 0 var(--hl-red); }

/* ---- How long they have been standing there (Phase 5b) --------------
   On the Pipeline, "3 weeks" is not a fact, it is a complaint. Two
   weeks is where a stage stops reading as a process, so that is where
   the number starts changing colour — and it is weight AND colour, not
   colour alone, so it still reads on a washed-out screen and to
   somebody who cannot tell the two apart. */
.tbl .ageing { color: var(--warn-ink, #8a5a00); font-weight: 600; }
.tbl .stale  { color: var(--hl-red); font-weight: 700; }

/* ---- The candidate record (Phase 5a) ---------------------------------
   One drawer, everything about one person. The kit styles .detail-sec,
   .facts and .doclist; these are the two lists it does not have. */

.evlist { list-style: none; margin: .4rem 0 .6rem; padding: 0; }
.evlist li {
  display: flex; gap: .75rem; padding: .6rem 0; border-bottom: 1px solid var(--line);
}
.evlist li:last-child { border-bottom: 0; }
.evlist .grow { flex: 1; min-width: 0; }
.evlist .grow b { display: block; }
.evlist .grow small { display: block; font-size: .8rem; color: var(--ink-2); }
.evlist .grow small.muted { color: var(--muted); font-size: .76rem; }
/* The history reads down from the most recent, so the newest move is
   the one at eye level. */
.evlist .ev b { font-weight: 650; }

/* Contracts on the record (Phase 8b). The same list as the history, with
   one difference: a row's buttons go on their OWN line. A name, two chips
   and up to four controls beside each other is a row that squashes to
   nothing on a phone — and the chip is the part you read first, so it is
   the part that must not be what gives way. */
.ctlist li { flex-wrap: wrap; }
.ctlist .tc-actions { flex: 0 0 100%; margin-top: .4rem; }

/* ---- The second filter row on Applicants (Phase 5b) -----------------
   The kit styles .filterset; what it has no opinion on is that this row
   answers a DIFFERENT question from the one above it. The row above is
   "what is outstanding"; this one is "who can work this shift". A rule
   above it says so without needing a heading, and the row only appears
   crowded if you are using it. */
.toolbar .filterset {
  padding-top: .7rem; margin-top: .1rem; border-top: 1px solid var(--line);
}
.toolbar .filterset > .link { margin-left: .25rem; }
@media (max-width: 720px) {
  .toolbar .filterset select { flex: 1 1 100%; max-width: none; }
}

/* The line above the application sheet inside its modal: why opening it
   was logged. It is the honest part of that screen, so it is not small
   print tucked under the page. */
.sheet-note { margin: 0 0 1rem; max-width: 70ch; }
.sheet .appsheet { margin: 0 auto; }

/* ---- The record, as tabs (Phase 5b) ---------------------------------
   Six tabs in the order a recruiter works: what was said, who they are,
   what they answered, how they came across, what they still owe us, and
   how they got here. The kit styles .tabs and .tab already; what it has
   no opinion on is how wide the drawer should be to hold them, and the
   answer is wider than a form. Selected by content rather than by a
   class, so the console kit stays unedited. */
.drawer:has(.rec-id) { width: min(820px, 100%); }

/* =====================================================================
   The record is three bands, not one long page
   =====================================================================

   The kit's .drawer-body is a single scroll box with padding on it,
   which is right for a form and wrong for this: reading up a
   conversation took the candidate's name, their number and the tab you
   were on off the top of the screen with it.

   So the body becomes a flex column that does NOT scroll — head fixed,
   middle grows and scrolls, foot fixed — and the padding moves inward
   to the bands, so that the scrollbar sits at the panel edge rather
   than inset by 1.4rem.

   `.rec-pane` is added to the kit's own element from onOpen() in
   candidates.js. The kit is vendored and not edited; every other drawer
   in the portal is untouched by this.

   ON THE min-height: 0 BELOW. A flex child's default min-height is
   `auto` — "at least as tall as my content" — which is what normally
   makes a middle band grow past its panel instead of scrolling inside
   it. Here every one of these elements is already a scroll container
   (overflow is not `visible`), and the spec gives those an automatic
   minimum size of zero anyway, so the declarations are belt and braces
   rather than the thing holding it up. They are kept because the day
   one of these loses its `overflow` they become load-bearing again,
   and that is not a day anyone will connect to this file.

   WHAT IS ACTUALLY HOLDING IT UP is `display: flex` on the two
   containers. Mutation testing says so: replacing either with
   `display: block` fails four and three assertions respectively, while
   removing a `min-height: 0` fails none, because it changes nothing.
   ================================================================== */
.drawer-body.rec-pane {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding: 0;
  min-height: 0;
}

.rec-pane > .rec-head {
  flex: 0 0 auto;
  padding: 1.4rem 1.4rem 0;
}

/* The tabs do not get a scrollbar of their own. The kit sets
   overflow-x: auto so a long tab row can be swiped; here that put a
   second scroller inside a panel that already had one, two pixels from
   the first. Six tabs wrap onto a second line perfectly well. */
.rec-pane > .rec-head .tabs {
  overflow-x: visible;
  flex-wrap: wrap;
  margin-bottom: 0;
}

/* The middle band. Every tab but Messages scrolls as a whole. */
.rec-pane > #rec-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 1.25rem 1.4rem 1.4rem;
}

/* Messages is the exception: the thread scrolls and the box you type
   into stays put, which is the shape every chat has for the reason
   every chat has it. */
.rec-pane > #rec-body.chat {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding: 0;
}
.rec-pane > #rec-body.chat > .msg-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 1.25rem 1.4rem .5rem;
}
.rec-pane > #rec-body.chat > .msg-compose {
  flex: 0 0 auto;
  padding: .85rem 1.4rem 1rem;
  border-top: 1px solid var(--line);
  background: var(--panel);
}
.rec-pane > #rec-body.chat > .msg-compose > :first-child { margin-top: 0; }

/* The thread's own margin fought the band padding and left a gap at the
   top that looked like a rendering fault. */
.rec-pane > #rec-body.chat .thread { margin: 0 0 .5rem; }

/* On a phone the panel is the whole screen, so the bands matter more,
   not less — but the gutters can be tighter. */
@media (max-width: 640px) {
  .rec-pane > .rec-head { padding: 1rem 1rem 0; }
  .rec-pane > #rec-body { padding: 1rem; }
  .rec-pane > #rec-body.chat > .msg-scroll { padding: 1rem 1rem .5rem; }
  .rec-pane > #rec-body.chat > .msg-compose { padding: .75rem 1rem .85rem; }
}

/* Stage, where, and the number — one line, because those three are what
   a recruiter checks before reading anything else. */
.rec-id {
  display: flex; align-items: center; gap: .75rem; flex-wrap: wrap;
  margin: 0 0 .6rem;
  min-width: 0;                       /* so a long store name wraps rather
                                         than pushing Move off the edge */
}
.rec-id .num { font-variant-numeric: tabular-nums; }

/* ---- Moving them on, in the header (Phase 5d) -----------------------

   The facts on the left, the one control that CHANGES them on the
   right. It used to be the last section of the Overview tab, below the
   notes — the thing a recruiter opens the record to do, reached by
   scrolling and invisible from every other tab.

   align-items: flex-start, not center: .rec-id wraps to two or three
   lines on a narrow drawer and centring would float the button in the
   middle of that block. It stays level with the first line, which is
   the line with the stage chip on it — the thing it changes.

   margin-left: auto rather than justify-content on the parent, so that
   when the row DOES wrap, the control keeps to the right instead of
   jumping under the chip. */
.rec-top {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
}
.rec-move {
  display: flex; align-items: center; gap: .5rem;
  flex: 0 0 auto; margin-left: auto; margin-bottom: .6rem;
}
/* The kit's .select is a block at full width, which in this row would
   take the whole header. */
.rec-move .select { width: auto; min-width: 10.5rem; margin: 0; }

/* A tick for what is done, a dot for what is not: the same information
   as the Their steps column on the list, so opening a row never tells a
   different story from the row you clicked. */
.drawer .journey { margin: 0 0 1rem; }
.drawer .tabs { margin-bottom: 1.1rem; }

/* ---- The Messages tab -----------------------------------------------
   Sample until Phase 9, and it says so at the top. A recruiter reading
   "What time must I come?" and believing a real person asked it is a
   worse outcome than an empty tab, so the note is not optional
   decoration — it is the honest part of the screen. */
.sample-note {
  border-left: 3px solid var(--line); padding-left: .7rem; margin: 0 0 1rem;
}

.thread { display: flex; flex-direction: column; gap: .8rem; margin-bottom: 1.1rem; }
.bubble {
  max-width: 78%; padding: .7rem .9rem;
  border: 1px solid var(--line); border-radius: 12px; background: var(--panel);
}
.bubble p { margin: 0; }
.bubble small { display: block; margin-top: .3rem; font-size: .76rem; }
/* Ours on the right and filled, theirs on the left and outlined — the
   arrangement every phone already taught them. */
.bubble.out { align-self: flex-end; background: var(--panel-2); border-radius: 12px 12px 3px 12px; }
.bubble.in  { align-self: flex-start; border-radius: 12px 12px 12px 3px; }

.quick { display: flex; gap: .4rem; flex-wrap: wrap; margin-bottom: 1.1rem; }

.rec-lbl { display: block; font-weight: 600; margin-bottom: .35rem; }
#rec-msg { margin-bottom: .4rem; }

.sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* ---- Document rows inside the record -------------------------------- */
.doclist { list-style: none; margin: .4rem 0 0; padding: 0; }
.doclist li {
  display: flex; align-items: center; gap: .75rem; flex-wrap: wrap;
  padding: .7rem 0; border-bottom: 1px solid var(--line);
}
.doclist li:last-child { border-bottom: 0; }
.doclist .grow { flex: 1; min-width: 180px; }
.doclist .grow small { display: block; color: var(--muted); font-size: .76rem; }

/* ---- Store cards ------------------------------------------------------ */
.storegrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: .9rem; }
.storegrid .card { margin: 0; display: flex; justify-content: space-between; gap: .8rem; align-items: flex-start; }
.storegrid .card.on { border-color: var(--brand-400); }
/* The "nothing matches" card sits INSIDE the grid, where the cards were
   rather than above them — so it inherits the flex rule above and
   pushes its heading and its sentence to opposite ends of a very wide
   box. It is a block, like every other .empty. */
.storegrid .card.empty { display: block; padding: 2.6rem 1.4rem; }
.star { border: 0; background: none; padding: .2rem; color: var(--brand-300); }
.star svg { width: 20px; height: 20px; fill: currentColor; }
.star[aria-pressed="true"] { color: var(--brand-600); }
.star:hover { background: var(--shade); }

/* ---- Activity list --------------------------------------------------- */
.feed { list-style: none; margin: 0; padding: 0; }
.feed li { display: flex; gap: .8rem; align-items: center; padding: .85rem 1rem; border-bottom: 1px solid var(--line); cursor: pointer; }
.feed li:last-child { border-bottom: 0; }
.feed li:hover { background: var(--panel-2); }
.feed li.unread { box-shadow: inset 3px 0 0 var(--hl-red); }
.feed li .grow { flex: 1; }
.feed li.unread strong { font-weight: 700; }

/* ---- Prototype switch in the sidebar footer -------------------------- */
.proto-switch { margin-top: .6rem; }
body.nav-rail .proto-switch { display: none; }

/* ---- Sign-in: the logo's own red field ------------------------------ */
.signin-wrap { background: var(--hl-red); }
.signin .brand img { width: 44px; height: 44px; background: var(--hl-red); border-radius: var(--r-ctl); }

@media (max-width: 600px) {
  .scores { grid-template-columns: 1fr; }
  .bubble { max-width: 94%; }
}

/* Tabs keep their natural width and scroll, rather than squeezing. */
.tabs .tab { flex: 0 0 auto; white-space: nowrap; }

/* Scope editor lines: country · region · store, with Remove. */
.scope-row { margin-bottom: .5rem; }
.scope-row select { margin: 0; }
.scopes:empty + .add::before { content: ""; }

/* Audit JSON */
pre.code { white-space: pre-wrap; word-break: break-word; font-size: .78rem; max-height: 320px; overflow: auto; }
.scope-row.pair3 { grid-template-columns: 1fr 1fr 1.5fr; }
@media (max-width: 600px) { .scope-row.pair3 { grid-template-columns: 1fr; } }

/* =====================================================================
   The application sheet (candidates.js)

   An A4 page inside the console, and the ONLY thing that prints. The
   browser's own "Save as PDF" is the PDF generator: selectable text,
   real page breaks, every script the forms are written in, and nothing
   to install. A field never breaks across a page; a section may.
   ===================================================================== */

.appsheet {
  display: block;
  width: 100%;
  max-width: 210mm;
  margin: 0 auto 32px;
  padding: 18mm 16mm;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: var(--shadow-1, 0 1px 2px rgba(0,0,0,.05));
  color: var(--ink);
}

.appsheet-head { display: flex; gap: 18px; align-items: flex-start; border-bottom: 2px solid var(--hl-red); padding-bottom: 12px; }
.appsheet-logo { height: 42px; width: auto; }
.appsheet-head h2 { margin: 0 0 4px; font-size: 1.25rem; letter-spacing: -.01em; }
.appsheet-meta { font-size: .82rem; color: var(--ink-2); line-height: 1.5; }

.appsheet-section { margin-top: 18px; }
.appsheet-section h3 {
  margin: 0 0 10px; padding: 6px 10px;
  font-size: .78rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  color: var(--brand-800); background: var(--brand-50); border-left: 3px solid var(--hl-red);
  border-radius: 4px;
}
.appsheet-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 22px; }
.appsheet-field { border-bottom: 1px dotted var(--line); padding-bottom: 6px; }
.appsheet-field.full { grid-column: 1 / -1; }
.appsheet-field .k { font-size: .72rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.appsheet-field .val { font-size: .95rem; white-space: pre-wrap; word-break: break-word; }
.appsheet-field .val.none { color: var(--muted); font-style: italic; }
/* A dot, not a word: the label stays readable, and the sheet still says
   which answers came from the sensitive set. */
.appsheet-field .lock { color: var(--hl-red); }

.appsheet-foot {
  margin-top: 22px; padding-top: 10px; border-top: 1px solid var(--line);
  font-size: .75rem; color: var(--muted); text-align: center;
}

@media (max-width: 720px) { .appsheet { padding: 20px 16px; } .appsheet-grid { grid-template-columns: 1fr; } }

/* ---- what actually comes out of the printer -------------------------- */
@page { size: A4 portrait; margin: 14mm; }

/* Where the application sheet goes to be printed.
   It lives in a modal now, and the print rules below hide modals —
   they are chrome. Rather than teach those rules to tell a modal that
   IS the document from one that is not (and then fight the modal's own
   positioning and overflow, which is what actually breaks printing),
   the page is cloned in here and everything else is hidden for the
   duration. See printAppsheet() in candidates.js. */
#print-root { display: none; }

@media print {
  /* Printing the cloned page: the console, modals and all, is not in it. */
  body.printing-sheet > *:not(#print-root) { display: none !important; }
  body.printing-sheet #print-root { display: block !important; }

  /* The console is chrome. Only the sheet is the document. */
  .appbar, .nav, .pane-head, .toolbar, .count-line, .banner, .tabs,
  .no-print, .drawer, .drawer-scrim, .sheet, .dialog, .dialog-backdrop,
  #toasts, .toasts, .progress { display: none !important; }
  html, body, #root, main, .pane-in { background: #fff !important; margin: 0 !important; padding: 0 !important;
                                      display: block !important; }
  .appsheet {
    max-width: none; margin: 0; padding: 0;
    border: 0; border-radius: 0; box-shadow: none;
  }
  .appsheet-section { break-inside: auto; }
  .appsheet-section h3 { break-after: avoid; }
  .appsheet-field { break-inside: avoid; }
  /* Keep the red rule and the section bars: without this the sheet
     prints as grey text on white and stops looking like ours. */
  * { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }
}

/* ---- reviewing an uploaded document ----------------------------------
   Big enough to read an ID number off a phone photo, and the decision
   buttons are the sheet's own footer so they are always reachable. */

.docview { display: grid; gap: 14px; }
.docimg {
  display: block; width: 100%; max-height: 60vh; object-fit: contain;
  background: var(--shade); border: 1px solid var(--line); border-radius: 12px;
}
.docframe {
  width: 100%; height: 60vh; border: 1px solid var(--line); border-radius: 12px; background: var(--shade);
}
.docmeta p { margin: 0 0 .35rem; }
.docmeta small { display: block; }

/* ---- reviewing a voice interview --------------------------------------
   The audio first, because listening is the job. The assessment reads as
   what it is: an opinion with its reasoning attached, above the buttons
   that make it a decision. */

.vwrap { display: grid; gap: 14px; }
.vaudio { width: 100%; }

.vhead { display: flex; align-items: center; gap: 16px; }
.vbig { display: flex; align-items: baseline; gap: 2px; }
.vbig b { font-size: 2.4rem; line-height: 1; letter-spacing: -.02em; }
.vbig span { color: var(--muted); }

.vgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.vscore { background: var(--panel-2); border: 1px solid var(--line); border-radius: 12px; padding: 12px; }
.vscore .k { font-size: .72rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.vscore .v b { font-size: 1.3rem; }
.vscore p { margin: .35rem 0 0; }

.vlist h4 { margin: 0 0 .4rem; font-size: .9rem; }
.vlist ul { margin: 0; padding-left: 1.1rem; }
.vlist.warn h4 { color: var(--warn); }

.vanswer { border-bottom: 1px dotted var(--line); padding: 8px 0; }
.vanswer .k { font-weight: 600; }
.vanswer .v { font-variant-numeric: tabular-nums; color: var(--ink-2); }
.vanswer.miss .v { color: var(--danger); }

.vdetails summary { cursor: pointer; font-weight: 650; }
.vtranscript { white-space: pre-wrap; color: var(--ink-2); margin-top: .5rem; }
.turnlines { display: grid; gap: 8px; margin-top: .6rem; max-height: 40vh; overflow-y: auto; }
.turnline { background: var(--panel-2); border: 1px solid var(--line); border-radius: 10px; padding: 8px 10px; }
.turnline b { display: block; font-size: .7rem; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }

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

/* ---- Testing: contract signing (Phase 8) ----------------------------
   The fields on the left, the document on the right, and clicking one
   highlights the other. That link is the whole value of the screen: a
   list of rectangles tells you nothing, and a page with eleven identical
   boxes on it tells you nothing either. */
.tc-split { display: grid; grid-template-columns: minmax(300px, 400px) 1fr; gap: 1rem; align-items: start; }
@media (max-width: 1100px) { .tc-split { grid-template-columns: 1fr; } }

.tc-fields { position: sticky; top: calc(var(--appbar) + 1rem); max-height: 78vh; overflow-y: auto; }
.tc-fields h2 { margin: 0 0 .2rem; font-size: 1rem; }
.tc-list { list-style: none; margin: .8rem 0 0; padding: 0; }
.tc-field {
  padding: .7rem .75rem; margin-bottom: .5rem; cursor: pointer;
  border: 1px solid var(--line); border-radius: 10px; background: var(--panel);
}
.tc-field.hot { border-color: var(--hl-red); box-shadow: 0 0 0 2px var(--danger-bg); }
/* A field the reader could not place is not an error in the document —
   it is the one a person marks by hand. It reads as work, not as a fault. */
.tc-field.bad { background: var(--shade); border-style: dashed; }
.tc-head { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; margin-bottom: .25rem; }
.tc-field input { margin: .35rem 0 0; }
.tc-sign { margin-top: .4rem; }
.tc-pad {
  display: block; width: 100%; height: 110px; touch-action: none; cursor: crosshair;
  background: #fff; border: 1.5px solid var(--line); border-radius: 10px;
}
.tc-signbar { display: flex; align-items: center; gap: .5rem; margin-top: .4rem; flex-wrap: wrap; }
.tc-signbar .tc-typed { flex: 1 1 140px; margin: 0; }
.tc-actions { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; margin-top: .9rem; }

.tc-pages { display: grid; gap: 1rem; }
.tc-page { position: relative; margin: 0; }
.tc-page img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--line); border-radius: 8px; background: #fff;
}
/* Positioned as a PERCENTAGE of the page, so a box lands in the right
   place whatever width the browser draws the image — and what you see is
   the rectangle the stamp will actually use. */
.tc-box {
  position: absolute; border: 1.5px solid var(--hl-red); border-radius: 2px;
  background: rgba(212, 35, 41, .10); cursor: pointer;
}
.tc-box.hot { background: rgba(212, 35, 41, .28); box-shadow: 0 0 0 2px var(--hl-red); }
.tc-page figcaption { margin-top: .3rem; }

.filebtn { margin: 0; }
.btn-like {
  display: inline-flex; align-items: center; padding: .48rem .9rem; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--r-pill);
  background: var(--panel); font-size: .85rem; font-weight: 600; color: var(--ink);
  text-decoration: none;
}
.btn-like:hover { border-color: var(--brand-300); color: var(--brand-700); }

/* ---- Configuration -> Start screen ----------------------------------
   A rough sketch of the candidate's phone, so an admin editing the
   paragraph can see where it lands rather than imagining it. Not a
   pixel copy of the portal and not pretending to be: it is here to
   show the paragraph in its place, above the number and the two ticks. */
.phone-preview {
  max-width: 380px; margin: 8px 0 4px; padding: 18px;
  border: 1px solid var(--line); border-radius: var(--r-ctl); background: var(--panel);
}
.phone-preview h4 { margin: 0 0 6px; font-size: 1.25rem; }
.phone-preview input[type="checkbox"] { margin-right: 8px; vertical-align: -2px; }
.blurb-preview {
  margin: 14px 0; padding: 12px 14px; border-radius: 14px;
  background: var(--brand-50); border: 1px solid var(--brand-200);
  color: var(--ink); font-size: .95rem; line-height: 1.5;
  white-space: pre-wrap;
}
.fake-field {
  margin: 4px 0 14px; padding: 12px 14px; border: 1px solid var(--line);
  border-radius: var(--r-ctl); color: var(--muted); background: var(--bg);
}


/* The 24-hour window, closed.
 *
 * Deliberately NOT styled as an error: it is a fact about how WhatsApp
 * works, not something the recruiter did wrong. Amber rather than red,
 * and it replaces the reply box rather than sitting above a disabled
 * one — a greyed-out textarea invites somebody to try to re-enable it,
 * and the rule is enforced in Postgres either way.
 */
.window-shut {
  border: 1px solid var(--line);
  border-left: 3px solid var(--warn, #b7791f);
  border-radius: 8px;
  padding: .85rem 1rem;
  background: var(--panel-2);
  margin-bottom: 1rem;
}
.window-shut p { margin: 0 0 .35rem; }
.window-shut p:last-child { margin-bottom: 0; }
.window-shut .held-draft {
  width: 100%; margin-top: .35rem; font: inherit; font-size: .85rem;
  background: var(--panel); color: var(--ink);
  border: 1px solid var(--line); border-radius: 6px; padding: .5rem;
}

/* =====================================================================
   DELETING A CANDIDATE (Phase 4e)
   ---------------------------------------------------------------------
   Set apart at the bottom of the History tab, because it is the one
   control here that destroys a record rather than changing it. The rule
   above it is doing the work: it says "below this line is different"
   before anybody reads the heading.

   Deliberately NOT a big red button. A danger-coloured link under an
   explanation is findable by somebody looking for it and unremarkable
   to somebody who is not — which is the right way round for a thing
   that should be pressed a few times a year.
   ===================================================================== */
.danger-zone {
  margin-top: 1.6rem;
  padding-top: 1rem;
  border-top: 1px solid var(--line);
}
.danger-zone h3 {
  margin: 0 0 .3rem;
  font-size: .95rem;
  color: var(--danger);
}
.danger-zone p { margin: 0 0 .6rem; }

/* The counted list on the confirmation sheet. A column, not the inline
   row .facts gives by default — these are quantities to be read one by
   one, and wrapping them into a paragraph is how a 3 gets mistaken for
   a 1. */
.facts.kv {
  display: block;
  margin: .4rem 0 0;
  padding: 0;
  list-style: none;
}
.facts.kv li {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: .3rem 0;
  border-bottom: 1px solid var(--line);
  font-size: .88rem;
}
.facts.kv li:last-child { border-bottom: 0; }
.facts.kv .k { color: var(--ink-2); }
.facts.kv .v { font-variant-numeric: tabular-nums; font-weight: 600; }

/* ---- Asking a cold thread to reply (Phase 9d) -----------------------
   Inside .window-shut, separated by a rule: the text above explains why
   there is no reply box, and this is the one thing that can be done
   about it. Without the rule the button reads as part of the
   explanation and gets skimmed past. */
.open-chat:not(:empty) {
  margin-top: .8rem; padding-top: .8rem; border-top: 1px solid var(--line);
}
.open-chat .banner { margin: 0 0 .6rem; }
.oc-act { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }

/* What a candidate typed onto their own contract, on the approval sheet.
   Inside a .banner.warn, so it inherits that colour; the list is only
   here to stop two notes running into one sentence. */
.ct-added { margin: .5rem 0; padding-left: 1.1rem; }
.ct-added li { margin: .15rem 0; font-weight: 600; }
