/* Levengarth jobs board — shared styling for the chooser (/roles) and the
 * stream pages (/roles/<area>). Linked after each page's inline base block, so
 * it relies on the palette variables (--mortar, --ash-lichen, --stone-grey,
 * --slate, --amber, --on-amber) declared there.
 *
 * The board is a sanctioned scrolling exception to the one-viewport rule
 * (CLAUDE.md Layout): a list of open roles cannot honestly be held to one
 * screen. Header stays fixed; content flows; the footer follows the content.
 *
 * Amber discipline holds: nothing here is amber except the B1 buttons
 * (Calculate Score, and later Apply). Cards and rows are navigation, bordered
 * in stone-grey/slate, never amber. Match greens are Phase 2 and are not here
 * yet — the Phase 1 Match column is locked.
 */

/* Board pages scroll: release the fixed-viewport height the base block sets. */
.board-page { min-height: 100dvh; }

/* Keep the vertical scrollbar track present at all times so filtering never
 * resizes the page. Without this, narrowing the list removes the scrollbar,
 * the viewport widens by its width, and the centred layout jumps — most
 * visibly while dragging a salary slider. `overflow-y: scroll` reserves the
 * track in every browser; `scrollbar-gutter: stable` alone did not, because it
 * only applies to a scroll container and html's default overflow is visible. */
html { overflow-y: scroll; scrollbar-gutter: stable; }

.board {
  position: relative;
  z-index: 2;
  flex: 1;
}
/* Two-class selector so it beats the page's own single-class .wrap padding
 * regardless of stylesheet order (both sit on <div class="wrap board">). */
.wrap.board { padding: 116px 40px 64px; }

/* Small Cinzel top line that serves as the page h1 on the stream pages, and the
 * eyebrow on the chooser. Not a giant heading — the roles are the priority. */
.board-eyebrow {
  font-family: var(--wordmark);
  font-weight: 600;
  font-size: 0.8125rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ash-lichen);
  margin: 0 0 10px;
}
.board h1.board-title {
  font-family: var(--work);
  font-weight: 400;
  font-size: clamp(1.5rem, 3vw, 2.1rem);
  line-height: 1.2;
  color: var(--mortar);
  letter-spacing: 0.005em;
  margin: 0;
}
.board-lede {
  margin: 14px 0 0;
  font-size: 1.05rem;
  color: var(--ash-lichen);
  line-height: 1.5;
  max-width: 46ch;
}

/* ---------------- Chooser: area cards ---------------- */
.area-cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px;
  margin-top: 40px;
}
.area-card {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 26px 24px;
  text-decoration: none;
  color: var(--mortar);
  background: rgba(236, 238, 234, 0.02);
  border: 1px solid var(--slate);
  border-radius: 2px;
  transition: border-color 0.18s ease, background 0.18s ease, transform 0.18s ease;
}
.area-card:hover,
.area-card:focus-visible {
  border-color: var(--ash-lichen);
  background: rgba(236, 238, 234, 0.05);
}
.area-card .ac-name {
  font-size: 1.15rem;
  color: var(--mortar);
  line-height: 1.25;
}
.area-card .ac-count {
  font-size: 0.8125rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ash-lichen);
}
.area-card .ac-types {
  margin-top: 2px;
  font-size: 0.9rem;
  color: var(--ash-lichen);
  line-height: 1.6;
}
.area-card .ac-go {
  margin-top: auto;
  font-size: 0.8125rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ash-lichen);
  border-bottom: 1px solid transparent;
  align-self: flex-start;
  padding-bottom: 2px;
}
.area-card:hover .ac-go,
.area-card:focus-visible .ac-go {
  color: var(--mortar);
  border-bottom-color: var(--mortar);
}

/* ---------------- Stream: head row ---------------- */
.stream-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
}
.stream-count {
  font-size: 0.9rem;
  color: var(--ash-lichen);
  letter-spacing: 0.02em;
}
/* The way back to hidden roles. A quiet inline control, not a button block. */
.hidden-toggle {
  margin-left: 10px;
  font-family: var(--work);
  font-size: 0.8125rem;
  letter-spacing: 0.02em;
  color: var(--ash-lichen);
  background: none;
  border: 0;
  padding: 0 0 1px;
  border-bottom: 1px solid var(--stone-grey);
  cursor: pointer;
  transition: color 0.18s ease, border-color 0.18s ease;
}
.hidden-toggle:hover,
.hidden-toggle:focus-visible { color: var(--mortar); border-bottom-color: var(--mortar); }

/* ---------------- Filters ---------------- */
.filters {
  margin-top: 22px;
  border: 1px solid var(--slate);
  border-radius: 2px;
  background: rgba(236, 238, 234, 0.02);
}
.filters-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 12px 16px;
}
.filters-toggle {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: none;
  border: 0;
  cursor: pointer;
  font-family: var(--work);
  font-size: 0.8125rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ash-lichen);
  padding: 0;
}
.filters-toggle:hover,
.filters-toggle:focus-visible { color: var(--mortar); }
.filters-toggle .chev {
  display: inline-block;
  transition: transform 0.18s ease;
  font-size: 0.8125rem;
}
.filters.open .filters-toggle .chev { transform: rotate(180deg); }
.filters-active {
  font-size: 0.8125rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ash-lichen);
}
.filters-clear {
  background: none;
  border: 0;
  cursor: pointer;
  font-family: var(--work);
  font-size: 0.8125rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ash-lichen);
  border-bottom: 1px solid transparent;
  padding: 0 0 2px;
}
.filters-clear:hover,
.filters-clear:focus-visible { color: var(--mortar); border-bottom-color: var(--mortar); }
.filters-bar-right { display: flex; align-items: center; gap: 16px; }

/* Two fields to a row: five filters across one line was too much to read. */
.filters-fields {
  display: none;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px 22px;
  padding: 6px 16px 20px;
  border-top: 1px solid var(--slate);
}
.filters.open .filters-fields { display: grid; }
/* Requirements is a checkbox cluster, so it takes the full width. */
.filt-reqs { grid-column: 1 / -1; }
.reqs-grid { display: flex; flex-wrap: wrap; gap: 10px 24px; }

/* The generic field-label styling. It must NOT reach the checkbox labels
 * (.filt-check): a bare `.filt label` (0,0,1,1) outweighs `.filt-check`
 * (0,0,1,0), so without this exclusion it silently forces the checkbox row
 * back to display:block + uppercase, killing the flex gap. That is the trap
 * this bug kept coming back from. Exclude .filt-check here, and .filt-check
 * below is also qualified with `label` so it wins outright. */
.filt label:not(.filt-check) {
  display: block;
  font-size: 0.8125rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ash-lichen);
  margin-bottom: 8px;
}
.filt input[type='text'],
.filt input[type='search'],
.filt select {
  width: 100%;
  font-family: var(--work);
  font-size: 0.9rem;
  color: var(--mortar);
  background: rgba(236, 238, 234, 0.04);
  border: 1px solid var(--stone-grey);
  border-radius: 1px;
  padding: 10px 12px;
  transition: border-color 0.18s ease;
}
.filt input:focus,
.filt select:focus { outline: none; border-color: var(--ash-lichen); }
.filt input::placeholder { color: var(--ash-lichen); text-transform: none; }

/* Dark controls, including the native dropdown list the browser draws for a
 * <select>: color-scheme is what makes that popup dark rather than white. */
.filt input[type='text'],
.filt input[type='search'],
.filt select { color-scheme: dark; }
.filt select {
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  padding-right: 34px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23BFC4C2' stroke-width='1.4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 10px 6px;
}
.filt select option { background: var(--stone-dark); color: var(--mortar); }

.filt-loc-row { display: flex; gap: 10px; }
.filt-loc-row input { flex: 1 1 auto; min-width: 0; }
.filt-loc-row select { flex: 0 0 42%; }

/* Help text under a control. 12px is the floor, and this is help, not action. */
.filt-hint { font-size: 0.75rem; color: var(--ash-lichen); line-height: 1.5; }

.filt label.filt-check {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  flex: 0 0 auto;
  font-size: 0.8125rem;
  line-height: 1.2;
  color: var(--ash-lichen);
  letter-spacing: 0.02em;
  text-transform: none;
  margin-bottom: 0;
  cursor: pointer;
}
/* line-height 1.2 (not the body's 1.7) so the box centres cleanly against the
 * cap height of the text rather than the whole leading, and the gap gives the
 * label room to breathe. */
.filt label.filt-check > span { white-space: nowrap; }
.filt-reqs > label {
  display: block;
  font-size: 0.8125rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ash-lichen);
  margin-bottom: 10px;
}
.filt label.filt-check input[type='checkbox'] { flex: 0 0 auto; width: 15px; height: 15px; margin: 0; accent-color: var(--ash-lichen); color-scheme: dark; cursor: pointer; }

/* The Salary slot is always on the page; the range inside it needs a Type,
 * because a year salary and a day rate cannot share one scale. */
.filt-salary-body { min-height: 22px; }
.salary-range { display: flex; flex-direction: column; gap: 8px; max-width: 420px; }
.salary-vals {
  display: flex;
  justify-content: space-between;
  font-size: 0.8125rem;
  color: var(--ash-lichen);
  letter-spacing: 0.02em;
}
.dual { position: relative; height: 26px; }
.dual input[type='range'] {
  position: absolute;
  left: 0; top: 0;
  width: 100%;
  margin: 0;
  background: none;
  pointer-events: none;
  -webkit-appearance: none;
  appearance: none;
}
.dual .track {
  position: absolute;
  left: 0; right: 0; top: 11px;
  height: 3px;
  background: var(--slate);
  border-radius: 2px;
}
.dual .track-fill {
  position: absolute;
  top: 11px; height: 3px;
  background: var(--ash-lichen);
  border-radius: 2px;
}
.dual input[type='range']::-webkit-slider-thumb {
  -webkit-appearance: none;
  pointer-events: auto;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: var(--mortar);
  border: 1px solid var(--stone-dark);
  cursor: pointer;
  margin-top: 0;
}
.dual input[type='range']::-moz-range-thumb {
  pointer-events: auto;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: var(--mortar);
  border: 1px solid var(--stone-dark);
  cursor: pointer;
}
.dual input[type='range']::-webkit-slider-runnable-track { background: none; }
.dual input[type='range']::-moz-range-track { background: none; }

/* ---------------- Roles table ----------------
 * A single column of rows. Each row is two lines: the link (the four selling
 * facts) and, beneath, the stage element with the match indicator, conditional
 * Apply and save/hide. Match is per-row, read from a device-local score store. */
.roles-table { margin-top: 26px; }
.roles-grid-cols {
  /* Role | Location | Type | Salary. Posted is a filter, not a column, and the
   * save/hide and match controls sit on the row's second line, so the first line
   * is just the four selling facts. minmax(0, …) on every column so widths are
   * fixed proportions, never content-sized — the columns must not jump as the
   * list filters. */
  grid-template-columns: minmax(0, 1.8fr) minmax(0, 1fr) minmax(0, 0.85fr) minmax(0, 1.2fr);
}
.roles-head {
  display: grid;
  gap: 16px;
  padding: 0 16px 12px;
  border-bottom: 1px solid var(--slate);
}
.roles-head .col {
  font-size: 0.8125rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ash-lichen);
  display: flex;
  align-items: center;
  gap: 7px;
}
/* Every column except the locked Match sorts, so the header is a button. */
.roles-head button.col {
  font-family: var(--work);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-align: left;
  transition: color 0.18s ease;
}
.roles-head button.col:hover { color: var(--mortar); }
.col .sort-caret {
  display: none;
  font-size: 0.7rem;
  line-height: 1;
}
.col .sort-caret::before { content: "\25B2"; }
.col.is-sorted .sort-caret { display: inline-block; }
.col.is-sorted.is-desc .sort-caret::before { content: "\25BC"; }
.col.is-sorted { color: var(--mortar); }
.col .filt-dot {
  display: none;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--mortar);
}
.col.is-filtered .filt-dot { display: inline-block; }
.col.is-filtered { color: var(--mortar); }

.job-row {
  padding: 16px;
  color: var(--mortar);
  border-bottom: 1px solid var(--slate);
  transition: background 0.16s ease, opacity 0.16s ease;
}
/* The whole first line is the link: a grid anchor holding the four cells, so it
 * stays aligned to the header. Interactive controls (Apply, save, hide) cannot
 * live inside an anchor, so they sit on the second line. */
.job-link.line-1 {
  display: grid;
  gap: 16px;
  align-items: baseline;
  text-decoration: none;
  color: inherit;
}
.job-row:hover,
.job-row:has(.job-link:focus-visible) {
  background: rgba(236, 238, 234, 0.05);
}
.job-row:has(.job-link:focus-visible) { box-shadow: inset 2px 0 0 var(--amber); }
.job-link:focus-visible { outline: none; }

/* Second line: stage on the left, and the right cluster (conditional Apply, the
 * match indicator, save/hide) pushed to the far edge. */
.line-2 { display: flex; align-items: flex-end; gap: 24px; margin-top: 16px; }
.row-right { margin-left: auto; display: flex; align-items: center; gap: 18px; flex: 0 0 auto; }
.btn-slot { display: flex; justify-content: flex-end; }
.job-apply { white-space: nowrap; }

/* Save and hide. Quiet until you go near them; the saved heart stays filled. */
.job-acts { display: flex; align-items: center; justify-content: flex-end; gap: 4px; }
.act {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 2px;
  cursor: pointer;
  color: var(--stone-grey);
  transition: color 0.16s ease, background 0.16s ease;
}
.job-row:hover .act { color: var(--ash-lichen); }
.act:hover,
.act:focus-visible { color: var(--mortar); background: rgba(236, 238, 234, 0.06); }
.act.is-on { color: var(--ash-lichen); }
/* Row heart: hollow, and filled when saved. */
.act svg path { fill: none; }
.act.is-on svg path:first-child { fill: currentColor; }
.job-row:hover .act.is-on { color: var(--mortar); }

/* Viewed: the title steps back. Applied: a plain tag, no colour of its own. */
.job-row.is-viewed .job-title { color: var(--ash-lichen); }
.job-tag {
  margin-left: 10px;
  font-size: 0.8125rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ash-lichen);
  border: 1px solid var(--stone-grey);
  border-radius: 2px;
  padding: 2px 7px;
  white-space: nowrap;
}
.job-row.is-hidden-role { opacity: 0.72; }

.job-title-cell { min-width: 0; display: flex; align-items: baseline; flex-wrap: wrap; gap: 0 4px; }
.job-title {
  font-size: 1.02rem;
  color: var(--mortar);
  line-height: 1.3;
  border-bottom: 1px solid transparent;
  display: inline;
}
.job-cell { min-width: 0; overflow-wrap: break-word; }
.job-row:hover .job-title,
.job-row:has(.job-link:focus-visible) .job-title { border-bottom-color: var(--mortar); }
.job-cell {
  font-size: 0.9rem;
  color: var(--ash-lichen);
  line-height: 1.4;
}
/* Type carries the contract length beneath it, so it stacks. */
.job-type { display: flex; flex-direction: column; line-height: 1.25; }
/* 13px, the functional floor: it lives inside the row link. */
.job-type .dur { font-size: 0.8125rem; color: var(--ash-lichen); opacity: 0.85; }
.job-cell .cell-label {
  display: none;
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ash-lichen);
  margin-bottom: 3px;
}
/* Salary is the most enticing figure on the row, so it carries full mortar.
 * The " Est." qualifier stays muted, a footnote to the number, not part of it. */
.job-salary { color: var(--mortar); }
.job-salary .est {
  color: var(--ash-lichen);
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
}
/* Stage element: a label and four ticks. Neutral palette only — future slate,
 * past stone-grey, current mortar. No amber, no animation. */
.job-stage { flex: 0 0 auto; }
.stage-label {
  display: block;
  font-size: 0.8125rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ash-lichen);
  line-height: 1;
}
.stage-label .vel { letter-spacing: 0.04em; text-transform: none; color: var(--ash-lichen); opacity: 0.85; }
.stage-ticks { display: flex; gap: 3px; margin-top: 8px; }
.stage-ticks i { display: block; width: 30px; height: 3px; background: var(--slate); }
.stage-ticks i.is-past { background: var(--stone-grey); }
.stage-ticks i.is-now { background: var(--mortar); }
/* On the detail page the stage element sits under the salary/duration. */
.role-stage { margin-top: 16px; }

/* Per-role match: ascending bars plus the word. Green means act (strong/viable),
 * stone-grey means pass (unlikely). Triple-encoded — bar count, colour and word
 * — so colour is never the only signal. Amber never touches the indicator.
 * The single filled Unlikely bar is stone-grey: 2.75:1 on the ink ground, below
 * the 3:1 non-text floor, accepted because the word label carries AA on its own
 * (documented exception, see CLAUDE.md). Unfilled bars stay slate. */
.job-match { display: flex; align-items: flex-end; gap: 9px; flex: 0 0 auto; }
.match-bars { display: flex; align-items: flex-end; gap: 2px; height: 14px; flex: 0 0 auto; }
.match-bars i { display: block; width: 4px; background: var(--slate); }
.match-bars i:nth-child(1) { height: 6px; }
.match-bars i:nth-child(2) { height: 10px; }
.match-bars i:nth-child(3) { height: 14px; }
.match-bars i.strong { background: var(--match-strong); }
.match-bars i.viable { background: var(--match-viable); }
.match-bars i.unlikely { background: var(--stone-grey); }
.match-word {
  font-size: 0.8125rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ash-lichen);
  line-height: 1;
}

/* A filled (Hired) role lingers, de-emphasised, before it ages off the board. */
.job-row.is-filled { opacity: 0.72; }
.job-row.is-filled:hover { opacity: 0.9; }

/* An unlikely match dims only its line-one cells (role name and meta) to 72%,
 * never the row, the stage, the indicator, its label or the heart. Not applied
 * on a filled row, which is already de-emphasised, to avoid compounding the
 * opacity. The dimmed mortar text still clears 4.5:1 on the ink ground. */
.job-row.is-unlikely:not(.is-filled) .job-link { opacity: 0.72; }

/* Unlikely fold: strong and viable matches lead; the unlikely ones collapse
 * beneath a hairline divider with a B3 Show control (never amber). */
.unlikely-fold {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px;
  border-top: 1px solid var(--slate);
}
.unlikely-count {
  font-size: 0.8125rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ash-lichen);
}
.unlikely-toggle { flex: 0 0 auto; }
.unlikely-group[hidden] { display: none; }

.salary-caveat {
  margin-top: 18px;
  font-size: 0.75rem;
  color: var(--ash-lichen);
  line-height: 1.5;
  max-width: 60ch;
}

/* ---------------- Register interest: the role-type board ----------------
 * Deliberately reads as a list of kinds of work, not a list of vacancies: no
 * town, no salary, no posted date, no row furniture borrowed from the table.
 * The note saying so is help text at the 12px floor, in ash-lichen. */
.reg-note {
  margin-top: 14px;
  /* 13px, not the 12px help floor: this line carries a link to the jobs board,
     and functional text holds 13px. */
  font-size: 0.8125rem;
  color: var(--ash-lichen);
  line-height: 1.6;
  max-width: 60ch;
}
.reg-note a { color: var(--ash-lichen); border-bottom: 1px solid var(--stone-grey); text-decoration: none; }
.reg-note a:hover, .reg-note a:focus-visible { border-bottom-color: var(--ash-lichen); }

.reg-sec { margin-top: 38px; }
.reg-sec .reg-h {
  font-family: var(--work);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ash-lichen);
  padding-bottom: 10px;
  border-bottom: 1px solid var(--slate);
}
.reg-list { list-style: none; margin: 0; padding: 0; }
/* Each row: the type (a link into its page) on the left, and a B2 Register
 * button on the right so a visitor can register without the detour. */
.reg-row {
  display: flex;
  align-items: center;
  gap: 20px;
  border-bottom: 1px solid var(--slate);
}
.reg-item {
  display: block;
  flex: 1 1 auto;
  min-width: 0;
  padding: 16px 0;
  text-decoration: none;
}
.reg-item:hover, .reg-item:focus-visible { background: rgba(236, 238, 234, 0.03); }
.reg-name { display: block; color: var(--mortar); font-size: 1.05rem; line-height: 1.35; }
.reg-item:hover .reg-name, .reg-item:focus-visible .reg-name { text-decoration: underline; }
.reg-sum {
  display: block;
  margin-top: 4px;
  color: var(--ash-lichen);
  font-size: 0.8125rem;
  line-height: 1.6;
  max-width: 78ch;
}
.reg-register { flex: 0 0 auto; }

/* B2 secondary button (per the hierarchy): transparent, mortar text, amber
 * border, fills solid amber on hover. Same as the Candidates fork routes. */
.btn-ghost {
  display: inline-block;
  font-family: var(--work);
  font-weight: 600;
  font-size: 0.8125rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 10px 20px;
  text-decoration: none;
  border-radius: 1px;
  background: transparent;
  color: var(--mortar);
  border: 1px solid var(--amber);
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease;
}
.btn-ghost:hover, .btn-ghost:focus-visible { background: var(--amber); color: var(--on-amber); }

/* The closing register block on a role-type page. */
.reg-cta { margin-top: 34px; }

@media (max-width: 600px) {
  /* On a phone the Register button drops under the type. */
  .reg-row { flex-direction: column; align-items: stretch; gap: 0; }
  .reg-register { align-self: flex-start; margin: 0 0 16px; }
}

/* ---------------- Standing speculative route (chooser) ----------------
 * A talent-pool entry, not a vacancy, and it must not read as one: no row
 * furniture, full width under the area cards rather than a fourth card in
 * their grid. It carries the chooser's one amber, since it is the only button
 * on the page, so "if it is amber, it is a button" holds. */
.spec-card {
  margin-top: 34px;
  padding: 28px 26px;
  border: 1px solid var(--slate);
  border-radius: 2px;
  background: rgba(236, 238, 234, 0.02);
  max-width: 60ch;
}
/* Eyebrow, not a heading: the display face (Cinzel) in mortar, tracked caps.
 * 12px sits above the 12px hard floor and is not a link, so the 13px functional
 * floor does not apply; mortar (not stone-grey) clears the a11y colour check. */
.spec-card .spec-eyebrow {
  font-family: var(--wordmark);
  font-size: 0.75rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--mortar);
  line-height: 1.2;
}
.spec-card .spec-body {
  margin-top: 12px;
  color: var(--ash-lichen);
  font-size: 1rem;
  line-height: 1.6;
}
.spec-card .spec-go { margin-top: 22px; }

.board-empty {
  margin-top: 30px;
  padding: 30px 26px;
  border: 1px solid var(--slate);
  border-radius: 2px;
  background: rgba(236, 238, 234, 0.02);
  max-width: 52ch;
}
.board-empty p { color: var(--ash-lichen); font-size: 1rem; line-height: 1.6; }
.board-empty .board-empty-actions { margin-top: 20px; display: flex; gap: 14px; flex-wrap: wrap; }

/* ---------------- Buttons the board introduces ----------------
 * B1 (amber fill) and B3 (no amber) per CLAUDE.md hierarchy. B3 is deliberately
 * quiet: ash-lichen text on a stone-grey rule, not mortar on mortar, which read
 * as loud as a primary action. Stone-grey is a border here, never the text. */
.btn-b3 {
  display: inline-block;
  font-family: var(--work);
  font-weight: 600;
  font-size: 0.8125rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 11px 20px;
  text-decoration: none;
  border-radius: 1px;
  background: transparent;
  color: var(--ash-lichen);
  border: 1px solid var(--stone-grey);
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease, border-color 0.18s ease;
}
.btn-b3:hover,
.btn-b3:focus-visible {
  background: rgba(236, 238, 234, 0.06);
  color: var(--mortar);
  border-color: var(--ash-lichen);
}

/* Compact B1, for a primary action inside a small box (the Match lock). */
.btn-sm {
  font-size: 0.8125rem;
  letter-spacing: 0.1em;
  padding: 9px 16px;
}

/* ---------------- Per-role detail page ----------------
 * Generated static by tools/generate-board.js. A back row, a full-width title
 * block (title, salary, then contract length on its own line), a label/value
 * spec list, then the body. Actions (save + Apply) sit top-right on desktop and
 * become a fixed bottom bar on mobile. Amber is the Apply button only; the
 * heart is never amber (ash-lichen outline, mortar fill when saved). */

/* The detail wraps sit above the drystone ground (which is absolute, z-index 0). */
.role-top, .role-head, .role-detail, .apply-wrap { position: relative; z-index: 2; }

/* Back row: a hairline row, a small chevron and label, a real link to the
 * parent stream. Not a bordered button. */
.wrap.role-top { padding: 100px 40px 0; }
.role-back {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 12px 0;
  font-family: var(--work);
  font-size: 0.8125rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ash-lichen);
  text-decoration: none;
  transition: color 0.16s ease;
}
.role-back:hover, .role-back:focus-visible { color: var(--mortar); }
.role-back svg { flex: 0 0 auto; }

/* Title block. Desktop: actions top-right, title flexes to fill, actions aligned
 * to the top of the h1. A hairline separates the back row from the title. */
.wrap.role-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
  border-top: 1px solid var(--slate);
  padding: 20px 40px 4px;
}
.role-head-main { flex: 1 1 auto; min-width: 0; }
.role-title {
  font-family: var(--work);
  font-weight: 400;
  font-size: clamp(1.5rem, 3vw, 2.1rem);
  line-height: 1.2;
  color: var(--mortar);
  letter-spacing: 0.005em;
  margin: 0;
}
.role-salary { margin-top: 10px; font-size: 1.2rem; color: var(--mortar); letter-spacing: 0.01em; }
.role-salary .est { font-size: 0.8125rem; color: var(--ash-lichen); letter-spacing: 0.04em; }
.role-duration { margin-top: 4px; font-size: 0.95rem; color: var(--ash-lichen); }

.role-actions { flex: 0 0 auto; display: flex; align-items: center; gap: 14px; }
/* Heart: never amber. Unsaved is an ash-lichen outline; saved fills mortar. */
.role-save {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  min-height: 44px;
  background: none;
  border: 1px solid var(--stone-grey);
  border-radius: 2px;
  color: var(--ash-lichen);
  cursor: pointer;
  transition: color 0.16s ease, border-color 0.16s ease;
}
.role-save:hover, .role-save:focus-visible { color: var(--mortar); border-color: var(--ash-lichen); }
.role-save svg path { fill: none; }
.role-save.is-on { color: var(--mortar); }
.role-save.is-on svg path { fill: currentColor; }
.role-apply { flex: 0 0 auto; }

/* Stream CV bar: the persistent "see your match" prompt, counterpart to the
 * detail-page Apply bar. Desktop is a static row under the context band, above
 * the filters; mobile is the fixed bottom bar (shared .pinned-bar surface). One
 * <a>; amber sits only on the CTA face, never the bar surface or border. */
.cv-bar {
  display: flex;
  align-items: stretch;
  margin: 18px 0 24px;
  border: 1px solid var(--slate);
  border-radius: 2px;
  overflow: hidden;
  text-decoration: none;
  background: rgba(20, 23, 25, 0.35);
}
.cv-bar-copy {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  padding: 12px 18px;
  font-size: 0.875rem;            /* 14px, above the 13px functional floor */
  color: var(--ash-lichen);
  line-height: 1.4;
}
.cv-bar-cta {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 12px 26px;
  font-family: var(--work);
  font-weight: 600;
  font-size: 0.8125rem;           /* 13px functional floor */
  letter-spacing: 0.14em;
  text-transform: uppercase;
  background: var(--amber);
  color: var(--on-amber);
  transition: background 0.18s ease;
}
.cv-bar:hover .cv-bar-cta,
.cv-bar:focus-visible .cv-bar-cta { background: var(--amber-hover); }

/* Spec list: label/value rows, a hairline between each; label left in small
 * uppercase ash-lichen, value right in mortar. */
.role-spec { margin: 24px 0 0; max-width: 62ch; }
.role-spec .spec-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
  padding: 12px 0;
  border-bottom: 1px solid var(--slate);
}
.role-spec .spec-row:first-child { border-top: 1px solid var(--slate); }
.role-spec dt {
  font-size: 0.8125rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ash-lichen);
  flex: 0 0 auto;
}
.role-spec dd { margin: 0; color: var(--mortar); text-align: right; }

.wrap.role-detail { padding: 8px 40px 64px; }

.role-summary {
  margin: 20px 0 0;
  font-size: 1.05rem;
  color: var(--mortar);
  line-height: 1.6;
  max-width: 62ch;
}

.role-sec { margin-top: 34px; }
.role-h {
  font-family: var(--work);
  font-weight: 400;
  font-size: 0.8125rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ash-lichen);
  margin: 0 0 10px;
}
.role-body {
  font-size: 1rem;
  color: var(--mortar);
  line-height: 1.65;
  max-width: 62ch;
}

.role-needs { list-style: none; margin: 0; padding: 0; max-width: 62ch; }
.role-needs li {
  font-size: 1rem;
  color: var(--mortar);
  line-height: 1.6;
  padding: 8px 0;
  border-bottom: 1px solid var(--slate);
}
.role-needs li:last-child { border-bottom: 0; }
/* Real jobs render the job_description as consecutive paragraphs. */
.role-body + .role-body { margin-top: 14px; }

/* ---------------- Apply form (detail page) ----------------
 * A native on-page application, the counterpart to Billy's screening. Fields sit
 * two-to-a-row on desktop and one on mobile, matching the board filters. Amber is
 * the submit button only; the error rule is stone-grey, never amber. */
.apply-wrap { margin-top: 44px; padding: 0 40px; }
.apply-head { margin-bottom: 18px; }
.apply-form { max-width: 640px; }
.apply-form[hidden] { display: none; }
.af-lead { margin: 0 0 18px; font-size: 0.95rem; color: var(--ash-lichen); line-height: 1.5; max-width: 62ch; }
.af-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px 22px; }
.af-field { display: flex; flex-direction: column; min-width: 0; }
.af-field.af-wide { grid-column: 1 / -1; }
.af-field label {
  font-size: 0.8125rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ash-lichen);
  margin-bottom: 8px;
}
.af-field input,
.af-field select {
  width: 100%;
  font-family: var(--work);
  font-size: 0.95rem;
  color: var(--mortar);
  background: rgba(236, 238, 234, 0.04);
  border: 1px solid var(--stone-grey);
  border-radius: 1px;
  padding: 11px 12px;
  color-scheme: dark;
  transition: border-color 0.18s ease;
}
.af-field input:focus,
.af-field select:focus { outline: none; border-color: var(--ash-lichen); }
.af-field input::placeholder { color: var(--ash-lichen); }
.af-field select {
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  padding-right: 34px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23BFC4C2' stroke-width='1.4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 10px 6px;
}
.af-field select option { background: var(--stone-dark); color: var(--mortar); }
.af-field input[type="file"] { padding: 9px 12px; cursor: pointer; }
.af-field input[type="file"]::file-selector-button {
  font-family: var(--work);
  font-size: 0.8125rem;
  letter-spacing: 0.06em;
  color: var(--mortar);
  background: rgba(236, 238, 234, 0.06);
  border: 1px solid var(--stone-grey);
  border-radius: 1px;
  padding: 6px 12px;
  margin-right: 12px;
  cursor: pointer;
}
/* Carries a link, so it holds the 13px functional floor, not the 12px help floor. */
.af-consent { margin-top: 18px; font-size: 0.8125rem; color: var(--ash-lichen); line-height: 1.6; max-width: 62ch; }
.af-consent a { color: var(--mortar); text-decoration: underline; }
.af-error {
  margin-top: 14px;
  font-size: 0.8125rem;
  color: var(--mortar);
  border-left: 2px solid var(--stone-grey);
  padding-left: 12px;
  line-height: 1.5;
}
.af-submit { margin-top: 20px; }
.af-submit:disabled { opacity: 0.6; cursor: default; }
.apply-done { max-width: 62ch; }
.apply-done p { margin-top: 10px; font-size: 1.05rem; color: var(--mortar); line-height: 1.6; }

/* ---------------- Mobile: table rows become stacked cards ---------------- */
@media (max-width: 720px) {
  .area-cards { grid-template-columns: 1fr; gap: 14px; }
}
@media (max-width: 600px) {
  .wrap.board { padding: 104px 20px 44px; }
  .board-lede { font-size: 1rem; }
  .roles-head { display: none; }
  .job-row { padding: 16px 4px; }
  /* Line one: title and salary share the top line (salary is the figure that
     sells the role, mortar and a size up), location and type stack beneath. */
  .job-link.line-1 {
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: 12px;
    row-gap: 4px;
    align-items: baseline;
  }
  .job-title-cell { grid-column: 1; grid-row: 1; min-width: 0; }
  .job-salary {
    grid-column: 2; grid-row: 1; justify-self: end; white-space: nowrap;
    font-size: 1.05rem; color: var(--mortar);
  }
  .job-loc, .job-type { grid-column: 1 / -1; }
  /* Contract length reads inline with the type on a phone. */
  .job-type { flex-direction: row; gap: 5px; align-items: baseline; }
  .job-title { font-size: 1.05rem; }
  .job-cell { font-size: 0.9rem; }
  /* Line two wraps: stage first, then the right cluster drops beneath when tight. */
  .line-2 { flex-wrap: wrap; gap: 14px 16px; margin-top: 14px; }
  .row-right { gap: 14px; }
  .stage-ticks i { width: 22px; }
  .job-acts { flex: 0 0 auto; gap: 6px; }
  .act { color: var(--ash-lichen); }
  .roles-table { display: block; }
  .filters-fields { grid-template-columns: 1fr; }
  .filt-loc-row select { flex: 0 0 46%; }
  .role-summary { font-size: 1rem; }
  .role-sec { margin-top: 28px; }
}

/* ---------------- Detail page: mobile (< 768px) ----------------
 * Title block full width; the actions become a fixed bottom bar (heart 64px on
 * the left, hairline divider, Apply filling the rest). The page carries a
 * matching bottom padding so the bar never covers the footer or the last line. */
@media (max-width: 767px) {
  .wrap.role-top { padding: 92px 20px 0; }
  .wrap.role-head { display: block; padding: 18px 20px 0; }
  .role-title { font-size: 1.4rem; }
  .role-salary { margin-top: 8px; font-size: 1.1rem; }

  /* Shared bottom-bar surface: the detail Apply bar and the stream CV bar sit
     here on mobile, so the two pages read as one system. Height and padding are
     defined once, on the children below (min-height 56). */
  .pinned-bar {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 30;
    display: flex;
    align-items: stretch;
    gap: 0;
    background: rgba(20, 23, 25, 0.94);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border-top: 1px solid var(--slate);
    padding-bottom: env(safe-area-inset-bottom);
  }

  /* CV bar sheds its desktop framing; the .pinned-bar surface takes over. */
  .cv-bar { margin: 0; border: 0; border-radius: 0; background: none; overflow: visible; }
  .cv-bar-copy { min-height: 56px; padding: 0 16px; }
  .cv-bar-cta { min-height: 56px; }
  /* Room below the footer so the fixed CV bar never covers it. */
  .stream-page { padding-bottom: calc(64px + env(safe-area-inset-bottom)); }

  .role-save {
    flex: 0 0 64px;
    width: 64px;
    height: auto;
    min-height: 56px;
    border: 0;
    border-right: 1px solid var(--slate);
    border-radius: 0;
  }
  .role-apply {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 56px;
    border-radius: 0;
  }
  .wrap.role-detail { padding: 8px 20px 40px; }
  /* Room below the footer so the fixed bottom bar never covers it. */
  .role-page { padding-bottom: calc(64px + env(safe-area-inset-bottom)); }
  /* Apply form: one column on a phone, matching the detail padding. */
  .apply-wrap { padding: 0 20px; }
  .af-grid { grid-template-columns: 1fr; }
}
