/* Recruitment — one stylesheet for both halves of the
   site: the public vacancy board a candidate lands on, and the staff area
   behind the sign-in. They share a palette and a set of components on
   purpose, so a candidate who is later a colleague recognises the place. */

:root {
  /* The school's own red, taken from its logo. Everything else on the page
     is a neutral chosen to sit under it: a warm grey rather than a cool one,
     because a blue-grey turns #a6192e slightly purple. */
  --accent: #a6192e;
  --accent-dark: #8a1526;
  --accent-darker: #6b0f1d;
  /* The accent's own channels, so every tint of it is one number away
     rather than the hex written out again at a different alpha. It is what
     functions/_middleware.js sets for a school with its own colour — the
     pills and chips below would otherwise keep this red while the rest of
     the page changed. */
  --accent-rgb: 166, 25, 46;
  --accent-soft: rgba(var(--accent-rgb), 0.07);
  --accent-line: rgba(var(--accent-rgb), 0.22);

  --bg: #f4f2f1;
  --ink: #221f20;
  --muted: #6d6669;
  --muted-soft: #928a8d;
  --card: #ffffff;
  --line: #e0dbda;
  --line-soft: #efebea;

  /* One warning colour and one settled colour, both pulled towards the red
     so a row of pills reads as one family. */
  --gold: #b07d1a;
  --gold-soft: rgba(176, 125, 26, 0.13);
  --danger: #a6192e;
  --danger-dark: #6b0f1d;
  --danger-soft: rgba(var(--accent-rgb), 0.08);
  --success: #2f6b46;
  --success-soft: rgba(47, 107, 70, 0.11);

  --radius: 12px;
  --radius-sm: 8px;
  --radius-xs: 6px;

  --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial,
    sans-serif;
  /* Headings, the brand wordmark and the vacancy titles. Work Sans is served
     from this site (see public/vendor/work-sans/) with Verdana behind it —
     both are wide and open, so a heading doesn't reflow to a different shape
     while the webfont arrives. */
  --font-display: "Work Sans", Verdana, Geneva, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;

  /* Spacing scale — stick to these so the rhythm stays consistent. */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;
  --sp-7: 48px;

  --font-base: 16px;

  --shadow-sm: 0 1px 2px rgba(34, 31, 32, 0.05), 0 2px 6px rgba(34, 31, 32, 0.05);
  --shadow: 0 2px 4px rgba(34, 31, 32, 0.05), 0 8px 24px rgba(34, 31, 32, 0.09);
  --shadow-lg: 0 4px 8px rgba(34, 31, 32, 0.06), 0 18px 48px rgba(34, 31, 32, 0.14);
  --ring: 0 0 0 3px rgba(var(--accent-rgb), 0.24);

  /* One timing for every hover that lifts something, so a row of cards
     settles together rather than one at a time. */
  --lift: 0.18s cubic-bezier(0.2, 0, 0.2, 1);
}

* {
  box-sizing: border-box;
}

*:focus-visible {
  outline: none;
  box-shadow: var(--ring);
  border-radius: var(--radius-xs);
}

html {
  font-size: var(--font-base);
}

body {
  margin: 0;
  /* A short page — one vacancy, or none — should still put its footer at the
     bottom of the window rather than halfway up it. */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

body.modal-open {
  overflow: hidden;
}

h1,
h2,
h3,
h4 {
  margin: 0 0 var(--sp-3);
  /* Every heading in the display face, not just the big one — two sans
     faces mixed at random reads as a mistake rather than a choice. */
  font-family: var(--font-display);
  line-height: 1.25;
  color: var(--accent-dark);
  font-weight: 700;
}

h1 {
  font-size: 1.75rem;
}
h2 {
  font-size: 1.3rem;
}
h3 {
  font-size: 1.1rem;
}
h4 {
  font-size: 0.95rem;
}

p {
  margin: 0 0 var(--sp-3);
}

a {
  color: var(--accent);
}

.hidden {
  display: none !important;
}

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

/* ---------- Scrollbars ---------- */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}

::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

::-webkit-scrollbar-thumb {
  background: var(--line);
  border-radius: 999px;
}

::-webkit-scrollbar-track,
::-webkit-scrollbar-corner {
  background: transparent;
}

/* ---------- The brand mark ---------- */
/* The school's logo is white artwork on the school's red, so it lives on a
   red bar and nowhere else — on white it would be an invisible rectangle.
   That decides the chrome: a red strip across the top of both halves of the
   site, and the red used as an accent everywhere below it.

   Until public/logo.png is in place the <img> fails and the wordmark behind
   it takes over, so the site is never showing a broken image — see the
   onerror in the markup. */
.brandmark {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  text-decoration: none;
  color: #fff;
  min-width: 0;
}

.brandmark .logo {
  height: 52px;
  width: auto;
  max-width: 260px;
  object-fit: contain;
  display: block;
}

/* The fallback: the wordmark without the crest, which is most of the logo
   and reads as deliberate rather than broken. */
.brandmark .wordmark {
  display: none;
  flex-direction: column;
  line-height: 1.15;
}

.brandmark.no-logo .logo {
  display: none;
}

.brandmark.no-logo .wordmark {
  display: flex;
}

.wordmark .name {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.15rem;
  letter-spacing: -0.01em;
  color: #fff;
}

/* Scoped to the brandmark rather than left as `.wordmark .tagline`: the
   sign-in card has a `.tagline` of its own for "Staff sign-in", in muted
   grey, and it used to win on being later in the file — which put grey text
   on the red banner wherever the wordmark showed. */
.brandmark .wordmark .tagline {
  font-size: 0.78rem;
  color: rgba(255, 255, 255, 0.82);
}

/* "Vacancies" / "Recruitment" beside the logo, divided from it by a rule so
   it reads as this site's name rather than part of the school's. */
.brand-sub {
  font-family: var(--font-display);
  padding-left: var(--sp-3);
  border-left: 1px solid rgba(255, 255, 255, 0.35);
  font-size: 0.74rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.92);
  font-weight: 700;
  white-space: nowrap;
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  padding: 9px 18px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--card);
  color: var(--ink);
  font: inherit;
  font-weight: 600;
  line-height: 1.2;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, color 0.15s, transform var(--lift);
  text-decoration: none;
}

.btn:hover:not(:disabled) {
  border-color: var(--accent);
  background: var(--accent-soft);
}

.btn:active:not(:disabled) {
  transform: translateY(1px);
}

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

.btn.primary:hover:not(:disabled) {
  background: var(--accent-dark);
  border-color: var(--accent-dark);
}

.btn.danger {
  border-color: var(--danger);
  color: var(--danger);
  background: var(--card);
}

.btn.danger:hover:not(:disabled) {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
}

.btn.link {
  border-color: transparent;
  background: transparent;
  color: var(--accent);
  padding: 9px 10px;
  font-weight: 600;
}

.btn.link:hover:not(:disabled) {
  background: var(--accent-soft);
  border-color: transparent;
  text-decoration: underline;
}

/* Destructive, sitting beside the action somebody actually came for. The
   school's colour is red, so a red link reads as "this is the brand" rather
   than "be careful" — these stay grey until the cursor is on them, and the
   dialog behind them spells out the consequence anyway. */
.btn.link.danger {
  border-color: transparent;
  background: transparent;
  color: var(--muted);
}

.btn.link.danger:hover:not(:disabled) {
  background: var(--danger-soft);
  border-color: transparent;
  color: var(--danger-dark);
}

.btn.small {
  padding: 5px 11px;
  font-size: 0.85rem;
}

.btn.block {
  width: 100%;
}

/* ---------- Cards ---------- */
.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--sp-5);
  box-shadow: var(--shadow-sm);
}

.card + .card {
  margin-top: var(--sp-4);
}

.section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  flex-wrap: wrap;
  margin-bottom: var(--sp-4);
}

.section-head h2,
.section-head h3 {
  margin: 0;
}

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

.small {
  font-size: 0.85rem;
}

.empty {
  padding: var(--sp-6) var(--sp-4);
  text-align: center;
  color: var(--muted);
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  background: var(--card);
}

/* ---------- Forms ---------- */
.stack {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

label {
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--ink);
}

input[type="text"],
input[type="email"],
input[type="tel"],
input[type="date"],
input[type="number"],
input[type="search"],
input:not([type]),
select,
textarea {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--card);
  color: var(--ink);
  font: inherit;
  transition: border-color 0.15s;
}

input:hover:not(:disabled),
select:hover:not(:disabled),
textarea:hover:not(:disabled) {
  border-color: var(--muted-soft);
}

input:focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--ring);
}

textarea {
  min-height: 110px;
  resize: vertical;
  line-height: 1.5;
}

input[type="file"] {
  width: 100%;
  padding: 9px 12px;
  border: 1px dashed var(--line);
  border-radius: var(--radius-sm);
  background: var(--card);
  font: inherit;
  font-size: 0.9rem;
}

.form-row {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}

.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--sp-3);
}

.form-hint {
  margin: 0;
  font-size: 0.82rem;
  color: var(--muted);
}

.required-mark {
  color: var(--danger);
  font-weight: 700;
}

.choice-group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-4);
}

.check-label {
  display: inline-flex;
  align-items: flex-start;
  gap: var(--sp-2);
  font-weight: 500;
  font-size: 0.92rem;
  cursor: pointer;
}

.check-label input {
  width: auto;
  margin-top: 3px;
  accent-color: var(--accent);
}

.msg {
  margin: var(--sp-3) 0 0;
  min-height: 1.4em;
  font-size: 0.9rem;
  color: var(--muted);
}

.msg.error,
.modal-error,
.form-error {
  color: var(--danger);
}

.msg.success {
  color: var(--success);
}

.notice {
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: var(--accent-soft);
  font-size: 0.92rem;
}

/* A notice that carries a button — the result of a bulk action offering the
   next thing, so the two sit on one line and wrap together on a phone. */
.notice.with-action {
  display: flex;
  /* Top, not centre: on a one-line notice the two are the same, and on a
     paragraph-length one centring leaves the icon floating beside the
     middle of the text. */
  align-items: flex-start;
  flex-wrap: wrap;
  gap: var(--sp-3);
}

/* The sentence takes the room that is going, so the icon stays beside it and
   only the button wraps. Without this a long sentence pushes itself onto a
   line of its own and leaves the icon sitting alone above it. */
.notice.with-action > span {
  flex: 1 1 16rem;
}

/* An offer, which is neither good news nor an alarm. The plain notice above
   is the accent tint, which beside a danger one reads as a second alarm —
   this is the surface the page already uses for things that are simply
   there to be read. */
.notice.quiet {
  background: var(--bg);
  color: var(--ink);
}

.notice.warn {
  border-color: rgba(192, 138, 46, 0.4);
  background: var(--gold-soft);
}

.notice.danger {
  border-color: rgba(165, 52, 42, 0.35);
  background: var(--danger-soft);
  color: var(--danger-dark);
}

.notice.success {
  border-color: rgba(44, 107, 69, 0.3);
  background: var(--success-soft);
  color: var(--success);
}

/* ---------- Pills and tags ---------- */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  white-space: nowrap;
  border: 1px solid transparent;
}

/* Vacancy statuses walk from grey through the school's red and end at
   settled green, so a board of them reads as a progress bar. */
.pill.status-new {
  background: #f1eeed;
  color: #5e5457;
  border-color: #e0dbda;
}
.pill.status-advertised {
  background: rgba(var(--accent-rgb), 0.1);
  color: var(--accent);
  border-color: rgba(var(--accent-rgb), 0.25);
}
.pill.status-shortlisting {
  background: rgba(var(--accent-rgb), 0.16);
  color: var(--accent-dark);
  border-color: rgba(var(--accent-rgb), 0.32);
}
.pill.status-interviewing {
  background: var(--gold-soft);
  color: #8a6110;
  border-color: rgba(192, 138, 46, 0.4);
}
.pill.status-selected {
  background: var(--success-soft);
  color: var(--success);
  border-color: rgba(44, 107, 69, 0.3);
}
.pill.status-closed {
  background: #eae6e5;
  color: #7c7477;
  border-color: #ded8d7;
}

/* Candidate statuses. "Not suitable" is deliberately the quietest thing on
   the row — the candidate stays in the pool, they are just not in this
   conversation. */
.pill.cand-new {
  background: rgba(var(--accent-rgb), 0.12);
  color: var(--accent);
  border-color: rgba(var(--accent-rgb), 0.28);
}
.pill.cand-prescreened {
  background: #efebea;
  color: #5e5457;
  border-color: #e0dbda;
}
.pill.cand-shortlisted {
  background: var(--gold-soft);
  color: #8a6110;
  border-color: rgba(192, 138, 46, 0.4);
}
/* Being seen. A step on from Shortlisted, which is the soft gold above, so
   this is the same hue carrying its weight — the two sit next to each other
   in a list and have to be told apart at a glance. */
.pill.cand-interviewing {
  background: var(--gold);
  color: #fff;
  border-color: var(--gold);
}
.pill.cand-offer {
  background: rgba(44, 107, 69, 0.14);
  color: var(--success);
  border-color: rgba(44, 107, 69, 0.32);
}
.pill.cand-accepted {
  background: var(--success);
  color: #fff;
  border-color: var(--success);
}
.pill.cand-not_suitable {
  background: #f1eeed;
  color: #8695a4;
  border-color: #e6e1e0;
}

/* A pill that opens a menu. It is the same chip, with a caret and something
   to press: where a status is shown is where somebody reaches to change it.
   `.pill` sets the size and the weight, but a button would otherwise take
   the browser's own face for form controls, so the family is spelt out. */
button.pill.menu-pill {
  font-family: inherit;
  cursor: pointer;
  appearance: none;
}

button.pill.menu-pill:hover {
  filter: brightness(0.96);
}

/* The global focus rule squares every corner off; a pill keeps its own. */
button.pill.menu-pill:focus-visible {
  border-radius: 999px;
}

/* Standing in for a form field — in the vacancy form and on a candidate's
   record — rather than sitting in a row. Big enough to be a control, and
   aligned left under its label like every input beside it. */
.form-row > .menu-pill {
  align-self: flex-start;
  padding: 7px 14px;
  font-size: 0.86rem;
}

.menu-pill-caret {
  font-size: 0.62em;
  opacity: 0.65;
}

/* ---------- The menu a status pill drops ---------- */
/* Fixed, and on the body rather than in the row: a row redraws the moment a
   change lands, and a table cell's overflow would clip it. */
.pop-menu {
  position: fixed;
  z-index: 60;
  min-width: 13.5rem;
  max-width: 19rem;
  padding: var(--sp-1);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
}

.pop-menu-caption {
  margin: 0;
  padding: var(--sp-2) var(--sp-3) var(--sp-1);
  color: var(--muted);
  font-size: 0.78rem;
  line-height: 1.35;
}

.pop-menu-item {
  display: block;
  width: 100%;
  padding: var(--sp-2) var(--sp-3);
  border: none;
  border-radius: var(--radius-xs);
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-weight: 600;
  font-size: 0.9rem;
  text-align: left;
  cursor: pointer;
}

.pop-menu-label {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}

.pop-menu-item .fa-solid {
  width: 1rem;
  text-align: center;
  color: var(--accent);
  font-size: 0.9em;
}

.pop-menu-item:hover:not(:disabled),
.pop-menu-item:focus-visible {
  background: var(--accent-soft);
  color: var(--accent-dark);
}

/* Where it is now, and what it can't be. Both are drawn rather than left
   out: "not offered" and "not possible" are different answers, and the
   note says which. */
.pop-menu-item:disabled {
  cursor: default;
  color: var(--muted);
}

.pop-menu-item:disabled .fa-solid {
  color: var(--muted);
}

.pop-menu-item.is-current {
  background: var(--bg);
}

.pop-menu-item.is-current .pop-menu-tick {
  margin-left: auto;
  color: var(--success);
}

.pop-menu-note {
  display: block;
  padding-left: calc(1rem + var(--sp-3));
  color: var(--muted);
  font-size: 0.76rem;
  font-weight: 500;
  line-height: 1.3;
  white-space: normal;
}

.tag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 9px;
  border-radius: var(--radius-xs);
  border: 1px solid var(--line);
  background: #faf8f8;
  color: var(--muted);
  font-size: 0.76rem;
  font-weight: 600;
  white-space: nowrap;
}

.tag.channel-seek {
  border-color: rgba(var(--accent-rgb), 0.3);
  color: var(--accent);
  background: rgba(var(--accent-rgb), 0.06);
}

.tag.channel-gazette {
  border-color: rgba(192, 138, 46, 0.45);
  color: #8a6110;
  background: var(--gold-soft);
}

.tag-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

.count-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 22px;
  padding: 0 7px;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 700;
}

.count-chip.quiet {
  background: #e6e1e0;
  color: var(--muted);
}

/* ---------- Tables ---------- */
.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--card);
}

/* As wide as the content needs and never narrower than the pane it sits in:
   a narrow table fills the width, and a wide one overflows into the
   wrapper's scroll rather than squeezing a column until it clips. */
table {
  width: max-content;
  min-width: 100%;
  border-collapse: collapse;
  font-size: 0.92rem;
}

thead th {
  position: sticky;
  top: 0;
  background: #faf8f8;
  text-align: left;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}

tbody td {
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--line-soft);
  vertical-align: middle;
}

tbody tr:last-child td {
  border-bottom: none;
}

tbody tr.clickable {
  cursor: pointer;
}

tbody tr.clickable:hover {
  background: var(--accent-soft);
}

/* Not suitable, and staff who have left: still there, still readable, and
   plainly not part of what is going on now. Kept at 62% rather than lower —
   a row nobody can read is a row that may as well have been deleted, and
   the whole point is that the school comes back to these. */
tr.dimmed td {
  opacity: 0.62;
}

tr.dimmed td:first-child {
  box-shadow: inset 3px 0 0 var(--accent-line);
}

.cell-strong {
  font-weight: 600;
  color: var(--accent-dark);
}

.cell-actions {
  text-align: right;
}

/* A row's actions wrap within their own column rather than pushing the table
   wider than the screen. Four labelled buttons don't fit on one line beside
   six columns of detail, and dropping to icons alone would mean guessing at
   what "Reset 2FA" does from a shield. */
.row-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--sp-1) var(--sp-2);
  max-width: 15rem;
  margin-left: auto;
}

/* ---------- Modal ---------- */
.modal-root {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-4);
}

.modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(34, 31, 32, 0.48);
  backdrop-filter: blur(2px);
}

.modal-box {
  position: relative;
  width: min(560px, 100%);
  max-height: min(88vh, 900px);
  overflow-y: auto;
  background: var(--card);
  border-radius: var(--radius);
  padding: var(--sp-5);
  box-shadow: var(--shadow-lg);
}

.modal-root.wide-modal .modal-box {
  width: min(820px, 100%);
}

/* An action that belongs in the dialog but not beside its Save: closing a
   vacancy, from inside the vacancy. Pushed to the far end so a hand going
   for Save can't find it. */
.modal-actions .form-aside {
  margin-right: auto;
}

.modal-actions {
  display: flex;
  gap: var(--sp-2);
  justify-content: flex-end;
  flex-wrap: wrap;
  margin-top: var(--sp-4);
}

.modal-error {
  min-height: 1.2em;
  margin: var(--sp-2) 0 0;
  font-size: 0.9rem;
}

/* ---------- Boot ---------- */
.boot-view {
  min-height: 60vh;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted-soft);
}

.spinner {
  width: 26px;
  height: 26px;
  border: 3px solid var(--line);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* ---------- Definition lists ---------- */
.detail-list {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--sp-2) var(--sp-4);
  margin: 0;
  font-size: 0.93rem;
}

.detail-list dt {
  color: var(--muted);
}

.detail-list dd {
  margin: 0;
  font-weight: 600;
  overflow-wrap: anywhere;
}

/* Free text a candidate typed: their paragraphs are the shape of the
   answer, so they survive into the page. */
/* A vacancy summary, as a candidate reads it. It arrives as HTML built by
   richtext.js from the plain text an administrator typed — paragraphs, bold,
   italics and bullets, and nothing else — so it is styled rather than
   printed verbatim. */
.prose {
  overflow-wrap: anywhere;
  line-height: 1.6;
}

.prose p {
  margin: 0 0 var(--sp-3);
}

.prose p:last-child,
.prose ul:last-child {
  margin-bottom: 0;
}

.prose ul {
  margin: 0 0 var(--sp-3);
  padding-left: 1.25rem;
}

.prose li {
  margin-bottom: var(--sp-1);
}

.prose li::marker {
  color: var(--accent);
}

.prose strong {
  font-weight: 700;
  color: var(--ink);
}

/* Free text somebody typed that carries no formatting — a candidate's
   qualifications, their upcoming leave. Their line breaks are the shape of
   the answer, so they survive. */
.plain-text {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  line-height: 1.6;
}

/* ---------- The summary editor ---------- */
.editor {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--card);
  overflow: hidden;
}

.editor:focus-within {
  border-color: var(--accent);
  box-shadow: var(--ring);
}

.editor-toolbar {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2);
  background: #faf8f8;
  border-bottom: 1px solid var(--line);
}

.editor-toolbar .btn.small {
  min-width: 2rem;
  justify-content: center;
}

.editor-hint {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  color: var(--muted-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The box itself is contenteditable, so bold looks bold while it is being
   typed. It is styled as .prose as well, which is what makes what an
   administrator sees here and what a candidate sees on the board the same
   thing. What gets saved is neither — see public/richtext.js. */
.editor-box {
  min-height: 11rem;
  max-height: 22rem;
  overflow-y: auto;
  padding: var(--sp-3) var(--sp-4);
  outline: none;
  font-size: 0.95rem;
}

.editor-box.is-empty::before {
  content: attr(data-placeholder);
  color: var(--muted-soft);
  pointer-events: none;
}

/* A list typed in the box sits where a list on the board sits. */
.editor-box ul {
  padding-left: 1.25rem;
}

/* Contenteditable wraps each line in a bare <div> with no margin of its own,
   and leaves the very first line unwrapped — so the gap goes above every
   block but the first, which is the one arrangement that spaces all of them
   whatever the browser decided to wrap. Without it the box is tighter than
   the board it is previewing. */
.editor-box > div:not(:first-child),
.editor-box > p:not(:first-child),
.editor-box > ul:not(:first-child) {
  margin-top: var(--sp-3);
}

.editor-box > p {
  margin-bottom: 0;
}

/* ===========================================================================
   The public vacancy board
   =========================================================================== */

/* The red strip. Its colour is the logo's own background, so the artwork
   sits on it without a seam. */
.site-header,
.topbar {
  background: var(--accent);
  padding: var(--sp-3) var(--sp-5);
}

.site-header .inner {
  max-width: 1080px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
}

/* One band of red is enough. The introduction sits on a warm tint that fades
   into the page, with the red pointing rather than shouting. */
.hero {
  background: linear-gradient(180deg, #fbf6f6 0%, var(--bg) 100%);
  padding: var(--sp-6) var(--sp-5) var(--sp-7);
}

.hero .inner {
  max-width: 1080px;
  margin: 0 auto;
}

.hero .eyebrow {
  display: block;
  margin-bottom: var(--sp-2);
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
}

.hero h1 {
  color: var(--ink);
  font-family: var(--font-display);
  font-size: clamp(1.7rem, 4vw, 2.4rem);
  margin-bottom: var(--sp-3);
}

.hero p {
  max-width: 62ch;
  margin: 0;
  font-size: 1.02rem;
  color: var(--muted);
}

.page {
  max-width: 1080px;
  margin: 0 auto;
  padding: var(--sp-5) var(--sp-5) var(--sp-7);
}

/* The staff area is a desk, not a page of prose: it carries tables that are
   several columns wider than anything a candidate reads. */
.page.wide {
  max-width: 1400px;
}

/* The board starts straight under the introduction: with the hero fading
   into the page background there is no edge left to overlap. */
.page.overlap {
  padding-top: 0;
}

.filter-bar {
  display: flex;
  align-items: flex-end;
  gap: var(--sp-3);
  flex-wrap: wrap;
  padding: var(--sp-4);
  margin-bottom: var(--sp-4);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
}

.filter-bar .form-row {
  min-width: 170px;
  flex: 1 1 170px;
}

/* Search leads the row and gets the room: it is the one a person reaches for
   when they know what they are looking for, and the dropdowns are for when
   they don't. */
.filter-bar .form-row-search {
  flex: 2 1 240px;
}

/* A <select> and an <input> with the same padding are not the same height —
   the browser gives the input its own line-height — and the bar is aligned
   to the bottom of its controls, so the taller one drags its label up out of
   line with the rest. One height for everything in the row settles it. */
.filter-bar select,
.filter-bar input {
  height: 2.5rem;
}

/* Safari draws its own decoration on a search field that doesn't match
   anything else here. */
.filter-bar input[type="search"]::-webkit-search-decoration {
  -webkit-appearance: none;
}

.filter-bar .filter-count {
  margin-left: auto;
  font-size: 0.88rem;
  color: var(--muted);
  padding-bottom: 10px;
}

.job-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
  gap: var(--sp-4);
}

.job-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-5);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
  transition: transform var(--lift), box-shadow var(--lift), border-color var(--lift);
}

.job-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow);
  border-color: var(--accent);
}

.job-card h3 {
  margin: 0;
  font-size: 1.12rem;
}

.job-card .department {
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* Four lines of the summary, so one wordy vacancy doesn't set the height of
   every card beside it. */
.job-card .summary {
  margin: 0;
  color: var(--muted);
  font-size: 0.92rem;
  display: -webkit-box;
  -webkit-line-clamp: 4;
  line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.job-card .foot {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-2);
  margin-top: auto;
  padding-top: var(--sp-3);
  border-top: 1px solid var(--line-soft);
  font-size: 0.85rem;
  color: var(--muted);
}

.closing-soon {
  color: var(--danger);
  font-weight: 600;
}

/* ---------- One vacancy, and the form under it ---------- */
.job-detail {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--sp-6);
  box-shadow: var(--shadow-sm);
}

.job-detail .department {
  font-size: 0.86rem;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: var(--sp-2);
}

.job-detail h1 {
  font-family: var(--font-display);
  margin-bottom: var(--sp-3);
}

.job-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-5);
  padding: var(--sp-3) 0 var(--sp-4);
  margin-bottom: var(--sp-4);
  border-bottom: 1px solid var(--line-soft);
  font-size: 0.9rem;
  color: var(--muted);
}

.job-meta strong {
  color: var(--ink);
}

.apply-form fieldset {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--sp-4) var(--sp-5) var(--sp-5);
  margin: 0 0 var(--sp-4);
}

.apply-form legend {
  padding: 0 var(--sp-2);
  font-weight: 700;
  color: var(--accent-dark);
  font-size: 0.95rem;
}

.back-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-bottom: var(--sp-4);
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--accent);
  text-decoration: none;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
  font-family: inherit;
}

.back-link:hover {
  text-decoration: underline;
}

.submitted-panel {
  text-align: center;
  padding: var(--sp-7) var(--sp-5);
}

.submitted-panel .tick {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 62px;
  height: 62px;
  margin-bottom: var(--sp-4);
  border-radius: 50%;
  background: var(--success-soft);
  color: var(--success);
  font-size: 1.8rem;
  font-weight: 700;
}

/* ===========================================================================
   The staff area
   =========================================================================== */

.login-view {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-5);
}

.login-card {
  width: min(430px, 100%);
  text-align: center;
  overflow: hidden;
}

/* The card wears the red strip too, so the page a staff member bookmarks is
   recognisably the same site as the board. */
.login-card .card-banner {
  margin: calc(var(--sp-5) * -1) calc(var(--sp-5) * -1) var(--sp-5);
  padding: var(--sp-4) var(--sp-5);
  background: var(--accent);
  display: flex;
  justify-content: center;
}

.login-card .card-banner .logo {
  height: 54px;
  max-width: 100%;
}

.login-card h1 {
  font-family: var(--font-display);
  font-size: 1.35rem;
  margin-bottom: var(--sp-1);
}

/* The direct child only — "Staff sign-in", not the subtitle up in the
   banner. */
/* ---- Support ----
   A short list of ways to reach one person. A definition list rather than a
   table: there are three rows and they are label/value pairs, which is what
   a dl is. */
.support-card { max-width: 34rem; }

.support-card h2 {
  font-family: var(--font-display);
  font-size: 1.05rem;
  margin-bottom: var(--sp-2);
}

.support-card > .muted { margin-bottom: var(--sp-4); }

.support-contact {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: var(--sp-2) var(--sp-4);
  margin: 0;
  align-items: baseline;
}

.support-contact dt {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  color: var(--muted);
  font-size: 0.92rem;
  white-space: nowrap;
}

.support-contact dt i { color: var(--accent); width: 1em; text-align: center; }

.support-contact dd {
  margin: 0;
  font-weight: 600;
  word-break: break-word;
}

.support-contact dd a { color: inherit; }

@media (max-width: 420px) {
  .support-contact { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .support-contact dd { margin-bottom: var(--sp-3); }
}

.login-card > .tagline {
  color: var(--muted);
  font-size: 0.92rem;
  margin-bottom: var(--sp-5);
}

.login-card form,
.login-card .stack {
  text-align: left;
}

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
}

.topbar .who {
  text-align: right;
  font-size: 0.82rem;
  line-height: 1.35;
  color: #fff;
}

.topbar .who .name {
  font-weight: 700;
  display: block;
}

.topbar .who .role {
  color: rgba(255, 255, 255, 0.78);
  display: block;
}

.topbar-right {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}

/* ---------- The menu under the signed-in name ---------- */
.user-menu {
  position: relative;
}

.user-menu-button {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 6px 10px;
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: var(--radius-sm);
  background: transparent;
  color: #fff;
  font: inherit;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}

.user-menu-button:hover,
.user-menu-button[aria-expanded="true"] {
  background: rgba(255, 255, 255, 0.14);
  border-color: rgba(255, 255, 255, 0.45);
}

.user-menu-button > .fa-circle-user {
  font-size: 1.15rem;
  opacity: 0.9;
}

.user-menu-button .chevron {
  font-size: 0.7rem;
  opacity: 0.8;
  transition: transform 0.15s;
}

.user-menu-button[aria-expanded="true"] .chevron {
  transform: rotate(180deg);
}

.user-menu-panel {
  position: absolute;
  top: calc(100% + var(--sp-2));
  right: 0;
  z-index: 40;
  min-width: 13rem;
  padding: var(--sp-1);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
}

.user-menu-item {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  padding: var(--sp-2) var(--sp-3);
  border: none;
  border-radius: var(--radius-xs);
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-weight: 600;
  font-size: 0.9rem;
  text-align: left;
  cursor: pointer;
}

.user-menu-item:hover {
  background: var(--accent-soft);
  color: var(--accent-dark);
}

.user-menu-item .fa-solid {
  width: 1rem;
  text-align: center;
  color: var(--accent);
  font-size: 0.9em;
}

.topbar .btn.link {
  color: rgba(255, 255, 255, 0.92);
}

.topbar .btn.link:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.14);
  color: #fff;
  text-decoration: none;
}

/* Under the red strip rather than part of it: the active tab is marked in
   the school's red, which is enough colour for a dashboard somebody has
   open all day. */
/* Wraps rather than scrolls. A strip of buttons is navigation, and a
   scrollbar under it hides where you can go — on a desktop it was a few
   pixels of grey under six buttons that had room for a second line. */
.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1);
  padding: 0 var(--sp-5);
  background: var(--card);
  border-bottom: 1px solid var(--line);
}

.tab {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border: none;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-weight: 600;
  font-size: 0.92rem;
  cursor: pointer;
  border-bottom: 3px solid transparent;
  margin-bottom: -1px;
  white-space: nowrap;
}

.tab:hover {
  color: var(--accent);
}

.tab.active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

.panel {
  display: none;
}

.panel.active {
  display: block;
}

.subtabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1);
  margin-bottom: var(--sp-4);
  border-bottom: 1px solid var(--line);
}

.subtab {
  padding: var(--sp-2) var(--sp-4);
  border: none;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-weight: 600;
  font-size: 0.9rem;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  white-space: nowrap;
}

.subtab:hover {
  color: var(--accent);
}

.subtab.active {
  color: var(--accent-dark);
  border-bottom-color: var(--accent);
}

.subpanel {
  display: none;
}

.subpanel.active {
  display: block;
}

/* ---------- The vacancy list, staff side ---------- */
.job-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--sp-3) var(--sp-4);
  align-items: start;
  padding: var(--sp-4) var(--sp-5);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  margin-bottom: var(--sp-3);
  box-shadow: var(--shadow-sm);
  transition: border-color var(--lift), box-shadow var(--lift);
}

.job-row:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow);
}

/* The row opens the vacancy for editing, so it says so under the pointer.
   Only administrators get the class — for anybody else the row is a card to
   read, and a pointer promising an editor they can't have is a worse row
   than a plain one. */
.job-row.clickable {
  cursor: pointer;
}

/* The left edge says where a vacancy is before a word of it is read: grey
   while it is being written up, the school's red once it is out in the world
   and running, and pale again once it is finished. */
.job-row {
  border-left: 3px solid var(--line);
}

.job-row.status-advertised,
.job-row.status-shortlisting,
.job-row.status-interviewing,
.job-row.status-selected {
  border-left-color: var(--accent);
}

.job-row.is-closed {
  border-left-color: var(--line-soft);
}

.job-row.is-closed {
  background: #faf8f8;
}

.job-row.is-closed .job-row-title {
  color: var(--muted);
}

.job-row-main {
  min-width: 0;
}

.job-row-title {
  font-family: var(--font-display);
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--accent-dark);
  margin-bottom: var(--sp-1);
}

.job-row-sub {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-4);
  font-size: 0.86rem;
  color: var(--muted);
  margin-bottom: var(--sp-2);
}

/* Across rather than down: five stacked buttons made every row as tall as
   its longest action list, whatever was in the row itself. */
.job-row-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: flex-end;
  gap: var(--sp-2);
  max-width: 24rem;
}

/* The status stepper: one button that moves a vacancy to the next thing
   that happens to it, rather than a dropdown of six that has to be read. */
.stepper {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
}

/* ---------- Candidates ---------- */
.candidate-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
  margin-bottom: var(--sp-4);
}

.status-filters {
  display: flex;
  gap: var(--sp-2);
  flex-wrap: wrap;
}

.status-filter {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--card);
  color: var(--muted);
  font: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
}

.status-filter:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.status-filter.active {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

/* ---------- Several candidates at once ---------- */
/* The result line and the bulk bar are both drawn into #cand-bulk, which is
   empty most of the time. So the space above them is the children's and not
   the container's: an empty container with a margin on it would hold the
   list down by itself, leaving a gap under the status chips with nothing in
   it. This is also what separates the result line from those chips — it is a
   report about what just happened, not another filter, and sitting flush
   against them it read as one. */
#cand-bulk > * {
  margin-top: var(--sp-4);
}

/* The bar only exists while something is ticked, so it can afford to be
   loud: it is the answer to "what happens if I press this", sitting where
   the list was about to be. */
.bulk-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--accent-line);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
}

.bulk-count {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--accent);
}

/* The label and its select read as one control rather than two things that
   happen to be next to each other. */
.bulk-field {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
}

/* The bulk bar's is a chip like every other status control on the site, so
   it is a little larger than one sitting in a row: it is a button somebody
   is aiming at rather than a label being read. */
.bulk-field .menu-pill {
  padding: 5px 12px;
  font-size: 0.82rem;
}

/* Why an action is greyed out, beside the action rather than in a tooltip
   nobody opens. It takes the rest of the row so it never squeezes a button. */
.bulk-why {
  flex-basis: 100%;
}

/* The tick column: as narrow as a box, and never the thing a row is read
   from. */
.select-cell {
  width: 1%;
  padding-right: 0;
}

.select-cell input {
  width: 16px;
  height: 16px;
  margin: 0;
  accent-color: var(--accent);
  cursor: pointer;
  vertical-align: middle;
}

/* A ticked row is the same tint as a hovered one, so it carries a mark down
   its edge as well: otherwise "which of these did I tick" is answered by
   wherever the mouse happens to be sitting. */
tr.picked > td {
  background: var(--accent-soft);
}

tr.picked > td:first-child {
  box-shadow: inset 3px 0 0 var(--accent);
}

/* Who the invitations are about to go to, on the dialog that sends them. */
.invite-list {
  list-style: none;
  margin: 0 0 var(--sp-3);
  padding: 0;
  max-height: 240px;
  overflow-y: auto;
}

.invite-list li {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: 5px 0;
  border-bottom: 1px solid var(--line);
  font-size: 0.92rem;
}

.invite-list li:last-child {
  border-bottom: 0;
}

.doc-links {
  display: flex;
  gap: var(--sp-2);
  flex-wrap: wrap;
}

.doc-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-xs);
  background: #faf8f8;
  color: var(--accent);
  font-size: 0.82rem;
  font-weight: 600;
  text-decoration: none;
}

.doc-link:hover {
  border-color: var(--accent);
  background: var(--accent-soft);
}

.winner-mark {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--success);
  font-weight: 700;
  font-size: 0.8rem;
}

/* ---------- Security / 2FA ---------- */
.qr-box {
  display: flex;
  gap: var(--sp-5);
  flex-wrap: wrap;
  align-items: flex-start;
  padding: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #faf8f8;
}

.qr-box svg {
  border-radius: var(--radius-sm);
  background: #fff;
}

.secret-code {
  font-family: var(--font-mono);
  font-size: 0.95rem;
  letter-spacing: 2px;
  word-break: break-all;
  padding: var(--sp-2) var(--sp-3);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-xs);
}

.recovery-codes {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  gap: var(--sp-2);
  margin: var(--sp-3) 0;
  padding: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #faf8f8;
  font-family: var(--font-mono);
  font-size: 0.95rem;
  letter-spacing: 1px;
}

/* ---------- The application form builder ---------- */
.form-section + .form-section {
  margin-top: var(--sp-4);
}

/* One question. Bordered rather than carded, because a section is the card
   and a question inside another card reads as a separate thing. */
.form-item {
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent-line);
  border-radius: var(--radius-sm);
  background: #faf8f8;
}

.form-item + .form-item {
  margin-top: var(--sp-2);
}

.form-item-head {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  margin-bottom: var(--sp-1);
}

.form-item-label {
  font-weight: 600;
  color: var(--ink);
}

.form-item .row-actions {
  margin-top: var(--sp-2);
  max-width: none;
  justify-content: flex-start;
}

/* The move buttons are an icon each; without this they are a rectangle with
   an arrow adrift in it. */
.form-item .row-actions .btn.small.link,
.form-section .row-actions .btn.small.link {
  min-width: 2rem;
  justify-content: center;
}

/* ---------- The letters to candidates ---------- */
.letter .section-head {
  margin-bottom: var(--sp-2);
}

.letter textarea {
  font-family: var(--font-body);
  line-height: 1.6;
}

/* The placeholders a letter may use. Buttons rather than a list, because
   clicking one puts it where the cursor is. */
.placeholders {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin: 0 0 var(--sp-4);
}

.chip {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  padding: 2px 8px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: #faf8f8;
  color: var(--accent);
  cursor: pointer;
}

.chip:hover {
  border-color: var(--accent);
}

/* What the letter says, with a real name and a real vacancy in it. Set apart
   from the boxes above so it reads as the result rather than another field. */
.letter-preview {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: #faf8f8;
  padding: var(--sp-3) var(--sp-4);
}

.letter-preview .preview-body {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: var(--sp-4);
  margin-top: var(--sp-2);
}

.letter-preview .preview-body p {
  margin: 0 0 var(--sp-3);
}

.letter-preview .preview-body p:last-child {
  margin-bottom: 0;
}

/* The subject line, above the message the way a mail client shows it. */
.preview-subject {
  font-weight: 600;
  color: var(--muted);
  font-size: 0.86rem;
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--line);
}

.letter-preview .preview-body h5 {
  font-family: var(--font-display);
  font-size: 1.1rem;
  margin: 0 0 var(--sp-3);
  color: var(--accent-dark);
}

/* ---------- Interview slots ---------- */
.interview-cell {
  white-space: nowrap;
}

.interview-slot {
  font-size: 0.85rem;
  padding: 4px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: #fff;
  color: var(--ink);
  font-family: var(--font-body);
}

.interview-slot:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.interview-slot.invalid {
  border-color: var(--danger);
}

/* A slot saves the moment it is picked, so it says so for a second and then
   gets out of the way — twenty bookings in a row shouldn't leave twenty
   lines of confirmation stacked down the page. */
.interview-slot.just-saved {
  animation: slot-saved 1.6s ease-out;
}

@keyframes slot-saved {
  0%,
  40% {
    border-color: var(--success);
    box-shadow: 0 0 0 3px var(--success-soft);
  }
  100% {
    border-color: var(--line);
    box-shadow: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .interview-slot.just-saved {
    animation: none;
  }
}

/* ---------- The licence ---------- */
/* A line most of the year; the notice styles take over when there is
   something to do about it. */
.licence-state {
  margin: 0 0 var(--sp-4);
}

/* The quiet line reads as a peer of "Who to ask" above the contact card: the
   label carries the same display face, weight and red, and the value that
   follows stays body text. No icon — a heart beside "Free forever" was doing
   the job the word already does. */
.licence-state.quiet {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  flex-wrap: wrap;
  color: var(--ink);
}

.licence-state.quiet .licence-label {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--accent-dark);
}

/* ---------- Logs ---------- */
/* The provider's own id for a message, so it can be pasted into their
   dashboard. Long and not often wanted, so it is quiet and clipped rather
   than allowed to set the width of the column. */
.log-reference {
  display: inline-block;
  max-width: 22ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--muted-soft);
}

.delivery-note .fa-solid {
  margin-right: 4px;
  opacity: 0.6;
}

.log-time {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--muted);
  white-space: nowrap;
}

.log-action {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--accent);
}

.status-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-right: 6px;
  background: var(--muted-soft);
}

/* Handed over, and nothing heard since. */
.status-dot.sent {
  background: var(--gold);
}

/* The receiving server took it — as far as anything here can know, it
   arrived. */
.status-dot.delivered {
  background: var(--success);
}

.status-dot.failed,
.status-dot.bounced,
.status-dot.complained {
  background: var(--danger);
}

/* ---------- Icons ---------- */
/* Font Awesome, used the same way everywhere: solid weight, on the left of
   the label, sized relative to the text it sits beside, and always
   aria-hidden in the markup — an icon repeats a label here, it never
   replaces one. */
.btn .fa-solid {
  font-size: 0.92em;
}

.btn.small .fa-solid {
  font-size: 0.9em;
}

.tab .fa-solid {
  font-size: 0.9em;
  opacity: 0.85;
}

h2 .fa-solid,
h3 .fa-solid,
h4 .fa-solid,
legend .fa-solid {
  margin-right: var(--sp-2);
  color: var(--accent);
  font-size: 0.82em;
}

.subtab .fa-solid {
  margin-right: 6px;
  font-size: 0.88em;
  opacity: 0.8;
}

.pill .fa-solid,
.tag .fa-solid {
  font-size: 0.82em;
}

thead th .fa-solid {
  margin-right: 5px;
  opacity: 0.55;
}

/* The line above a letter's preview, which is a caption rather than a
   heading and so isn't caught by any of the rules above. */
.letter-preview > .muted .fa-solid {
  margin-right: 5px;
  opacity: 0.55;
}

/* Notices lead with an icon in a column of its own, so a message that wraps
   doesn't run back underneath it. */
.notice {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
}

.notice > .fa-solid {
  margin-top: 3px;
  font-size: 1.05em;
  flex-shrink: 0;
  color: var(--accent);
}

.notice.warn > .fa-solid {
  color: var(--gold);
}

.notice.success > .fa-solid {
  color: var(--success);
}

.notice.danger > .fa-solid {
  color: var(--danger);
}

/* An empty state is the one place an icon is allowed to be large: it is
   doing the work of the picture that isn't there. */
.empty .fa-solid {
  display: block;
  margin: 0 auto var(--sp-3);
  font-size: 1.9rem;
  color: var(--line);
}

/* A label with an icon beside it — a closing date, a department, a
   reference. Used across the public cards and the staff rows so the same
   fact looks the same in both. */
.meta-item {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

.meta-item .fa-solid {
  color: var(--accent);
  font-size: 0.86em;
  opacity: 0.75;
}

.doc-link .fa-solid {
  font-size: 0.9em;
}

.status-filter .fa-solid {
  font-size: 0.85em;
  opacity: 0.85;
}

.submitted-panel .tick .fa-solid {
  font-size: 1.7rem;
}

/* ---------- Phones ---------- */
@media (max-width: 720px) {
  .page {
    padding: var(--sp-4) var(--sp-4) var(--sp-6);
  }

  .hero {
    padding: var(--sp-5) var(--sp-4) var(--sp-6);
  }

  .site-header,
  .topbar {
    padding: var(--sp-3) var(--sp-4);
  }

  .tabs {
    padding: 0 var(--sp-2);
  }

  .job-detail {
    padding: var(--sp-4);
  }

  .job-row {
    grid-template-columns: 1fr;
    padding: var(--sp-4);
  }

  .job-row-actions {
    justify-content: flex-start;
    max-width: none;
  }

  .topbar .who {
    display: none;
  }

  /* On a phone the topbar has the logo, the site's name and the account menu
     competing for 390 pixels, and the name loses: the logo already says
     whose site this is and the tab strip underneath says which one. */
  .topbar .brand-sub {
    display: none;
  }

  .topbar .logo {
    height: 40px;
  }

  .detail-list {
    grid-template-columns: 1fr;
    gap: var(--sp-1) 0;
  }

  .detail-list dt {
    font-size: 0.82rem;
  }

  .detail-list dd {
    margin-bottom: var(--sp-2);
  }
}
