/* ---------------------------------------------------------------------------
   Design tokens (TRI-17)
   Values are the "Drop-in tokens" block from the Job Tracker Redesign mockup,
   with one deliberate departure: the accent family.

   The mockup predates the Tripwire logo and used indigo (#574fd6). TRI-33
   locked the mark to teal #00878f and states that TRI-17 should adopt it as
   the real accent token, so the accent family below is teal. Everything else
   -- surfaces, text, status, spacing, type, radii, elevation -- is verbatim
   from the mockup. The status colours stay as designed: they are semantic
   (watching / applied / interviewing / offer / rejected), not brand colour.

   --accent / --accent-soft are intentionally NOT redeclared in the legacy
   :root below, so these values win for the components that already use them.

   Only --accent-hover and --accent-border are derived rather than given: the
   mockup's indigo hover/border have no teal equivalent in TRI-33, so they
   follow the same relationship to the base that the mockup's own pair does.

   The [data-theme="dark"] block is defined now but not yet exposed in the UI;
   wiring the toggle is TRI-30.
   --------------------------------------------------------------------------- */

:root {
  /* surface */
  --bg-app:#f7f7f8;   --bg-surface:#ffffff;  --bg-subtle:#fafafa;
  --bg-hover:#f4f4f6; --border:#ebebf0;      --border-strong:#e0e0e7;

  /* text */
  --text:#17171a;     --text-2:#5c5c66;      --text-3:#8a8a94;
  --text-4:#a2a2ac;

  /* accent (primary actions / active nav / links only) */
  --accent:#007c84;   --accent-hover:#00686f; --accent-soft:#e5f6f6;
  --accent-border:#a3d5d8;

  /* status */
  --st-watching:#1d4ed8;    --st-watching-bg:#e8effc;
  --st-applied:#0f766e;     --st-applied-bg:#e0f2f0;
  --st-interviewing:#b35208;--st-interviewing-bg:#fbf0dc;
  --st-offer:#137e3b;       --st-offer-bg:#e5f4ea;
  --st-rejected:#676e7c;    --st-rejected-bg:#f0f0f3;
  /* Skipped reads quieter than rejected: rejected is an outcome somebody
     else decided, skipped is one you did, and it should not compete with
     the live pipeline for attention. TRI-94. */
  --st-skipped:#8a8a94;     --st-skipped-bg:#f4f4f6;
  --posting-open:#16a34a;   --posting-closed:#9ca3af;

  /* spacing */
  --sp-1:4px; --sp-2:8px;  --sp-3:12px; --sp-4:16px;
  --sp-5:20px; --sp-6:24px; --sp-8:32px; --sp-10:40px;

  /* type */
  --font:"Inter",system-ui,sans-serif;
  --mono:"JetBrains Mono",ui-monospace,monospace;
  --fs-xs:11px; --fs-sm:12.5px; --fs-base:13.5px;
  --fs-md:15px; --fs-lg:18px;   --fs-xl:22px; --fs-2xl:26px;
  --lh-tight:1.35; --lh-body:1.6;

  /* danger / destructive. Distinct from --warn, which is what closed and
     stale postings use: a destructive action is not a stale posting. */
  --danger:#b91c1c;         --danger-strong:#dc2626;
  --danger-bg:#fdeaea;      --danger-bg-soft:#fdf4f4;
  --danger-border:#f5c2c2;  --danger-border-soft:#f0d5d5;

  /* Label colour on an accent fill, and the knob on a switch. Named rather
     than #fff because both invert on dark. */
  --on-accent:#ffffff;
  --knob:#ffffff;

  /* Input fill. Its own token because on dark an input must sit one step
     LIGHTER than its card, which is the opposite of the light theme where
     both are white. */
  --input-bg:#ffffff;

  /* Impersonation banner */
  --banner-bg:#fbf0dc;  --banner-border:#f0dcb4;  --banner-text:#7c4a06;

  /* radii + elevation */
  --r-sm:6px; --r-md:7px; --r-lg:10px; --r-full:999px;
  --shadow-1:0 1px 2px rgba(16,17,26,.04);
  --shadow-2:0 2px 8px rgba(16,17,26,.07);
  --shadow-pop:0 16px 48px rgba(16,17,26,.18);
}

/* Dark theme.
   Surfaces are raised with lightness rather than shadow, because a drop
   shadow does nothing against a near-black ground — so the shadows go to
   none and cards rely on their border.

   Status pairs invert: on light a status is deep text on a pale tint; on dark
   it is pale text on a deep tint. Reusing the light hexes here fails contrast
   in both directions, which is why every pair is restated rather than
   inherited. */
[data-theme="dark"] {
  --bg-app:#0e0e11;   --bg-surface:#161619;  --bg-subtle:#1b1b1f;
  --bg-hover:#202024; --border:#26262c;      --border-strong:#3a3a44;
  --text:#f2f2f4;     --text-2:#a8a8b2;      --text-3:#7b7b85;
  --text-4:#6a6a74;

  --accent:#4eccd3;   --accent-hover:#6ed7dd; --accent-soft:#083a3d;
  --accent-border:#1c5f63;

  /* Near-black label on the accent fill, so the fill keeps its weight
     instead of glowing. */
  --on-accent:#062123;
  --knob:#f2f2f4;

  /* One step lighter than --bg-surface: that step is what makes a field read
     as a field without a heavy border. */
  --input-bg:#1b1b1f;

  --st-watching:#8fb3f7;    --st-watching-bg:#16233d;
  --st-applied:#5fd4c8;     --st-applied-bg:#0c2b28;
  --st-interviewing:#e0a458;--st-interviewing-bg:#33240e;
  --st-offer:#6ed08a;       --st-offer-bg:#122a1a;
  --st-rejected:#a8a8b2;    --st-rejected-bg:#24242a;
  --st-skipped:#7b7b85;     --st-skipped-bg:#1d1d22;
  --posting-open:#4ade80;   --posting-closed:#6b7280;

  --danger:#f4867f;         --danger-strong:#e5484d;
  --danger-bg:#2a1517;      --danger-bg-soft:#231113;
  --danger-border:#5c2326;  --danger-border-soft:#4a1d20;

  --banner-bg:#33240e;  --banner-border:#4d3714;  --banner-text:#e0a458;

  --shadow-1:none;
  --shadow-2:none;
  --shadow-pop:0 16px 48px rgba(0,0,0,.55);
}

/* No explicit choice means follow the OS. Scoped with :not([data-theme="light"])
   so an explicit Light setting still wins on a dark-set machine. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg-app:#0e0e11;   --bg-surface:#161619;  --bg-subtle:#1b1b1f;
    --bg-hover:#202024; --border:#26262c;      --border-strong:#3a3a44;
    --text:#f2f2f4;     --text-2:#a8a8b2;      --text-3:#7b7b85;
    --text-4:#6a6a74;

    --accent:#4eccd3;   --accent-hover:#6ed7dd; --accent-soft:#083a3d;
    --accent-border:#1c5f63;
    --on-accent:#062123;
    --knob:#f2f2f4;
    --input-bg:#1b1b1f;

    --st-watching:#8fb3f7;    --st-watching-bg:#16233d;
    --st-applied:#5fd4c8;     --st-applied-bg:#0c2b28;
    --st-interviewing:#e0a458;--st-interviewing-bg:#33240e;
    --st-offer:#6ed08a;       --st-offer-bg:#122a1a;
    --st-rejected:#a8a8b2;    --st-rejected-bg:#24242a;
  --st-skipped:#7b7b85;     --st-skipped-bg:#1d1d22;
    --posting-open:#4ade80;   --posting-closed:#6b7280;

    --danger:#f4867f;         --danger-strong:#e5484d;
    --danger-bg:#2a1517;      --danger-bg-soft:#231113;
    --danger-border:#5c2326;  --danger-border-soft:#4a1d20;

    --banner-bg:#33240e;  --banner-border:#4d3714;  --banner-text:#e0a458;

    --shadow-1:none;
    --shadow-2:none;
    --shadow-pop:0 16px 48px rgba(0,0,0,.55);
  }
}

* { box-sizing: border-box; }

/* Base link styling.
   There was none: retiring the pre-redesign block took the only `a` rule with
   it, so every unclassed link fell back to the browser default blue — barely
   readable on a dark background and nothing to do with the palette.

   Underlined by default, because a link sitting inside a sentence should not
   rely on colour alone to announce itself. The links that are not prose —
   card titles, nav items, rail links — set their own decoration and win on
   specificity, so this only reaches the ones that needed it. */
a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
}
a:hover { color: var(--accent-hover); }
a:focus-visible {
  outline: none;
  border-radius: var(--r-sm);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

body {
  margin: 0;
  font-family: var(--font);
  color: var(--text);
  background: var(--bg-app);
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.brand-mark {
  flex-shrink: 0;
  display: block;
}

/* The content column. Left-aligned and full-width up to its max, with the
   same horizontal padding as .page-header so the title and the content below
   it share an edge.

   NOT `margin: 0 auto`: .app-main is a flex column, and an auto cross-axis
   margin suppresses the default stretch, which makes this shrink to fit its
   contents instead of filling the column. That collapsed the card grid to a
   single narrow centred column while the header stayed left-aligned. */
.container {
  width: 100%;
  max-width: 1120px;
  padding: var(--sp-6) var(--sp-8) 56px;
}

section { margin-bottom: 2.5rem; }

h2 {
  font-size: var(--fs-md);
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
  margin: 0 0 var(--sp-3);
}


.empty-state {
  color: var(--text-3);
  font-size: var(--fs-base);
  padding: var(--sp-5);
  background: var(--bg-subtle);
  border: 1px dashed var(--border-strong);
  border-radius: var(--r-md);
}

/* Impersonation banner (TRI-28).
   Amber, not red: viewing as another user is a state you chose, not an error.
   Red would read as something having gone wrong, and an admin who sees red on
   every impersonated page stops reading it.

   It sits inside .app-main, so it spans the content column and never covers
   the sidebar. */
.impersonation-banner {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 9px var(--sp-8);
  background: var(--banner-bg);
  border-bottom: 1px solid var(--banner-border);
  color: var(--banner-text);
  font-size: var(--fs-base);
}
.impersonation-banner .dot {
  width: 6px; height: 6px; flex: 0 0 6px;
  border-radius: 50%;
  background: var(--st-interviewing);
}
.impersonation-banner strong { font-weight: 600; }
.impersonation-banner a {
  margin-left: auto;
  color: var(--banner-text);
  font-weight: 500;
  text-decoration: underline;
  white-space: nowrap;
}

/* Buttons (TRI-19): exactly three classes. No bare button{} rule — that is
   what made every page override it inline. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 7px 13px;
  border-radius: var(--r-md);
  font-family: inherit;
  font-size: var(--fs-base);
  font-weight: 500;
  line-height: 1.2;
  cursor: pointer;
  text-decoration: none;
  transition: background .12s, border-color .12s;
}

.btn-primary {
  background: var(--accent);
  border: 1px solid var(--accent);
  color: var(--on-accent);
}
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }

.btn-secondary {
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  color: var(--text-2);
}
.btn-secondary:hover { background: var(--bg-hover); border-color: var(--text-4); }

/* Destructive is a surface button with red text, not a red fill: it should
   read as dangerous on inspection, not shout from across the page. */
.btn-destructive {
  background: var(--bg-surface);
  border: 1px solid var(--danger-border-soft);
  color: var(--danger);
}
.btn-destructive:hover { background: var(--danger-bg-soft); border-color: var(--danger-border); }

.btn:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* --- Auth page --- */
.auth-page {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  background: var(--bg-app);
}

/* Login (TRI-29). Spacing and type only — the layout is the same centred card.

   The element rules that used to live here (.auth-card label/input/button)
   are gone rather than retuned: at 0,1,1 they outranked the .field-label and
   .input classes, so this page quietly opted out of the form system TRI-20
   built. The markup carries those classes already. */
.auth-card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
  padding: var(--sp-8);
  width: 340px;
  max-width: calc(100vw - var(--sp-8));
}

.auth-card .brand { margin-bottom: var(--sp-2); }
.auth-card h1 {
  font-size: var(--fs-lg);
  font-weight: 600;
  letter-spacing: -0.02em;
  margin: 0;
}
.auth-sub {
  color: var(--text-2);
  font-size: var(--fs-base);
  margin: 0 0 var(--sp-6);
}

/* The only full-width button in the app: on a single-purpose card there is
   nothing for it to sit beside. */
.auth-card .btn { width: 100%; margin-top: var(--sp-2); }

/* Errors are red, not the rust that used to double as the warn colour.
   Rust is what closed/stale postings use, and an error is not a stale
   posting. */
.error {
  background: var(--danger-bg);
  border: 1px solid var(--danger-border);
  color: var(--danger);
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--fs-sm);
  border-radius: var(--r-md);
  margin-bottom: var(--sp-4);
}

/* ---------------------------------------------------------------------------
   App shell + sidebar (TRI-18)
   Replaces the per-page .topbar. The shell owns the viewport height and the
   main column scrolls on its own, so the sidebar stays put while a long
   dashboard scrolls past it.
   --------------------------------------------------------------------------- */

.app-shell {
  display: flex;
  height: 100vh;
  width: 100%;
  overflow: hidden;
}

.sidebar {
  width: 264px;
  flex: 0 0 264px;
  background: var(--bg-subtle);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  padding: 18px 12px 12px;
}

.sidebar-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 8px 18px;
  /* It became a link to the dashboard; it should not start looking like one. */
  text-decoration: none;
  color: inherit;
  border-radius: var(--r-md);
}
.sidebar-brand:hover .sidebar-wordmark { color: var(--accent); }
.sidebar-brand:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.sidebar-wordmark {
  font-size: var(--fs-md);
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
}

.sidebar-section {
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-4);
  padding: 0 10px 8px;
}

.sidebar-nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.nav-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 10px;
  border-radius: var(--r-md);
  font-size: var(--fs-base);
  color: var(--text-2);
  text-decoration: none;
  transition: background .12s;
}

.nav-item:hover { background: var(--bg-hover); }
.nav-item:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* Active state per TRI-18: --bg-hover fill, --accent text, 600 weight. */
.nav-item.is-active {
  background: var(--bg-hover);
  color: var(--accent);
  font-weight: 600;
}

.nav-count {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  color: var(--text-4);
}

.nav-item.is-active .nav-count { color: var(--accent); }

.sidebar-user {
  margin-top: auto;
  padding: 12px 10px;
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 10px;
}

.sidebar-avatar {
  width: 28px;
  height: 28px;
  flex: 0 0 28px;
  border-radius: var(--r-full);
  background: var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text-2);
}

.sidebar-user-text { min-width: 0; display: flex; flex-direction: column; }

.sidebar-user-name {
  font-size: var(--fs-base);
  font-weight: 500;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sidebar-signout { font-size: var(--fs-xs); color: var(--text-3); text-decoration: none; }
.sidebar-signout:hover { color: var(--accent); text-decoration: underline; }
.sidebar-signout:focus-visible,
.segmented a:focus-visible,
.rail-link:focus-visible,
.gen-actions button:focus-visible {
  outline: none;
  border-radius: var(--r-sm);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* The main column is the only thing that scrolls. */
.app-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  background: var(--bg-app);
  overflow-y: auto;
}

/* Below the sidebar's usable width the shell stacks instead of squeezing the
   content column to nothing. */
@media (max-width: 860px) {
  .app-shell { flex-direction: column; height: auto; overflow: visible; }
  .sidebar { width: 100%; flex: none; border-right: 0; border-bottom: 1px solid var(--border); }
  .sidebar-nav { flex-direction: row; flex-wrap: wrap; }
  .sidebar-user { margin-top: 12px; }
  .app-main { overflow: visible; }
}

/* Page header — a minimal title bar so pages are not headingless between
   TRI-18 and TRI-19. TRI-19 replaces this with the real header partial
   (title + subtitle + right-aligned actions). */
.page-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-6);
  padding: 22px var(--sp-8) var(--sp-4);
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border);
}

.page-header h1 {
  margin: 0;
  font-size: var(--fs-xl);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text);
}

.page-header-text { min-width: 0; }

.page-subtitle {
  margin: 5px 0 0;
  font-size: var(--fs-base);
  color: var(--text-2);
}

.page-actions { display: flex; gap: var(--sp-2); flex-shrink: 0; }

@media (max-width: 860px) {
  .page-header { flex-direction: column; align-items: flex-start; gap: var(--sp-3); }
}

/* ---------------------------------------------------------------------------
   Form controls (TRI-20)
   Focus is a replacement for the native outline, never a removal: accent
   border plus a 3px --accent-soft ring. Error state is the same shape in red,
   so a field that fails validation reads as the same control in a bad state
   rather than a different control.
   --------------------------------------------------------------------------- */

.field { margin-bottom: var(--sp-5); }

.field-label,
.form-section-label {
  display: block;
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--text-2);
  margin-bottom: 6px;
}

.input,
.select,
.textarea {
  width: 100%;
  padding: 8px 11px;
  background: var(--input-bg);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  font-size: var(--fs-base);
  font-family: inherit;
  color: var(--text);
  transition: border-color .12s, box-shadow .12s;
}

.textarea {
  padding: 10px 12px;
  line-height: 1.5;
  resize: vertical;
}

.input:focus,
.select:focus,
.textarea:focus {
  outline: none;                 /* replaced below, never simply removed */
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* Error state: red border + red 3px ring, with the message below the field. */
.input.has-error,
.select.has-error,
.textarea.has-error {
  border-color: var(--danger-strong);
  box-shadow: 0 0 0 3px var(--danger-bg);
}

.input.has-error:focus,
.select.has-error:focus,
.textarea.has-error:focus {
  border-color: var(--danger-strong);
  box-shadow: 0 0 0 3px var(--danger-bg);
}

.field-error {
  font-size: var(--fs-xs);
  color: var(--danger);
  margin-top: 5px;
}

.field-hint {
  display: block;
  font-size: var(--fs-xs);
  color: var(--text-3);
  margin-top: 5px;
}

/* Pasted resume text is read as a document, so it keeps the mono face. */
.textarea-mono { font-family: var(--mono); }

/* Success confirmation, the counterpart to .error.
   Uses the offer green rather than the accent: the accent is reserved for
   primary actions, the active nav item and links, and a confirmation is a
   state you read, not a thing you click. */
.success {
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--st-offer);
  border-radius: var(--r-md);
  background: var(--st-offer-bg);
  color: var(--st-offer);
  font-size: var(--fs-base);
}

/* Checkbox groups. Native controls tinted with accent-color rather than a
   hidden input behind a styled span: it keeps the native focus ring and the
   platform's own checked semantics, which a custom span has to reimplement. */
.check-group { display: flex; flex-direction: column; gap: 9px; }

.check-row {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: var(--fs-base);
  color: var(--text);
}

.check-row input[type="checkbox"] {
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
  accent-color: var(--accent);
  margin: 0;
}

/* A conditional field belonging to the checkbox above it. The indent is the
   checkbox width plus its gap, so the field visibly hangs off its control. */
.check-nested {
  margin-left: 25px;
  padding: 6px 10px;
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
  font-family: inherit;
  color: var(--text-2);
  width: calc(100% - 25px);
}

.check-nested:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* Toggle: a real checkbox, visually hidden, driving a track + knob. No JS, and
   it stays keyboard operable and announced as a checkbox. */
.toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--bg-hover);
  cursor: pointer;
}

.toggle-title { font-size: var(--fs-base); font-weight: 500; color: var(--text); }

.toggle-input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.toggle-track {
  width: 34px;
  height: 20px;
  flex: 0 0 34px;
  border-radius: var(--r-full);
  padding: 2px;
  display: flex;
  justify-content: flex-start;
  background: var(--border-strong);
  transition: background .12s;
}

.toggle-knob {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--knob);
  box-shadow: 0 1px 2px rgba(16,17,26,.2);
  transition: transform .12s;
}

.toggle-input:checked + .toggle-track { background: var(--accent); }
.toggle-input:checked + .toggle-track .toggle-knob { transform: translateX(14px); }
.toggle-input:focus-visible + .toggle-track { box-shadow: 0 0 0 3px var(--accent-soft); }

/* Dropzone */
.dropzone {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  padding: 18px;
  background: var(--bg-subtle);
  border: 1px dashed var(--border-strong);
  border-radius: 9px;
  font-size: var(--fs-sm);
  color: var(--text-3);
}

fieldset.field-group { border: 0; padding: 0; margin: 0 0 var(--sp-5); }
fieldset.field-group legend {
  padding: 0;
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--text-2);
  margin-bottom: 6px;
}

/* Visually hidden, still announced. Used for labels on fields whose purpose is
   clear visually from the control they hang off, but which still need a real
   <label for> binding. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---------------------------------------------------------------------------
   Job cards (TRI-21)
   Hover lifts the border and the shadow only — no transform. The list has to
   stay still while you scan it; cards that move under the cursor make a grid
   harder to read, not livelier.
   --------------------------------------------------------------------------- */

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: var(--sp-3);
}

.job-card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-4);
  box-shadow: var(--shadow-1);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  transition: box-shadow .14s, border-color .14s;
}

.job-card:hover {
  border-color: var(--border-strong);
  box-shadow: var(--shadow-2);
}

.job-card-head { display: flex; gap: 11px; align-items: flex-start; }

.avatar {
  width: 34px;
  height: 34px;
  flex: 0 0 34px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.job-card-id { min-width: 0; flex: 1; }

.job-card-title {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: var(--lh-tight);
}
.job-card-title a { color: var(--text); text-decoration: none; }
.job-card-title a:hover { color: var(--accent); text-decoration: underline; }

.job-card-company { font-size: var(--fs-base); color: var(--text-2); margin-top: 2px; }

/* Open/closed indicator. A closed posting keeps full text contrast — only the
   dot and the label change colour, because a closed posting is still a live
   record you act on. */
.posting-state {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: var(--fs-xs);
  font-weight: 500;
  white-space: nowrap;
}
.posting-state .dot { width: 5px; height: 5px; border-radius: 50%; flex: 0 0 5px; }
.posting-state.is-open { color: var(--st-offer); }
.posting-state.is-open .dot { background: var(--posting-open); }
.posting-state.is-closed { color: var(--text-3); }
.posting-state.is-closed .dot { background: var(--posting-closed); }

.chip-row { display: flex; flex-wrap: wrap; gap: 6px; }

.chip {
  padding: 3px 8px;
  background: var(--bg-hover);
  border-radius: 5px;
  font-size: var(--fs-sm);
  color: var(--text-2);
}

.job-card-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  padding-top: 11px;
  border-top: 1px solid var(--bg-hover);
}

.job-card-seen { font-size: var(--fs-sm); color: var(--text-3); }

/* Compact button for use inside a card footer or table row. */
.btn-sm { padding: 5px 11px; font-size: var(--fs-sm); border-radius: var(--r-sm); }
.btn-secondary.btn-sm:hover {
  background: var(--accent-soft);
  border-color: var(--accent-border);
  color: var(--accent);
}

.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-bottom: var(--sp-3);
}
.section-head h2 {
  margin: 0;
  font-size: var(--fs-md);
  font-weight: 600;
  letter-spacing: -0.01em;
  text-transform: none;
  border-bottom: 0;
  padding-bottom: 0;
  color: var(--text);
}

/* ---------------------------------------------------------------------------
   Saved-job pipeline rows (TRI-22)
   The status lives on a 3px left border rather than on a chip in every row:
   it reads as a pipeline lane from the corner of the eye without competing
   with the title for attention.
   --------------------------------------------------------------------------- */

.pipeline { display: flex; flex-direction: column; gap: var(--sp-2); }

.pipe-row {
  display: flex;
  align-items: center;
  gap: 14px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--st-rejected);
  border-radius: 9px;
  padding: 13px var(--sp-4);
  box-shadow: var(--shadow-1);
  transition: box-shadow .14s, border-color .14s;
}

.pipe-row:hover { box-shadow: var(--shadow-2); }

/* Status drives only the left edge and the chip — never the accent, which is
   reserved for primary actions, active nav and links. */
.pipe-row.is-watching     { border-left-color: var(--st-watching); }
.pipe-row.is-applied      { border-left-color: var(--st-applied); }
.pipe-row.is-interviewing { border-left-color: var(--st-interviewing); }
.pipe-row.is-offer        { border-left-color: var(--st-offer); }
.pipe-row.is-rejected     { border-left-color: var(--st-rejected); }
.pipe-row.is-skipped      { border-left-color: var(--st-skipped); }
/* Dimmed rather than hidden — these are only on screen because the reader
   asked to see them, and they should read as set aside, not as pipeline. */
.pipe-row.is-skipped      { opacity: .72; }
.pipe-row.is-skipped:hover, .pipe-row.is-skipped:focus-within { opacity: 1; }

/* Keeps the title readable before the meta group gets any space. */
.pipe-id { min-width: 180px; flex: 1 1 220px; }

.pipe-title {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pipe-title a { color: var(--text); text-decoration: none; }
.pipe-title a:hover { color: var(--accent); text-decoration: underline; }

.pipe-sub {
  font-size: var(--fs-sm);
  color: var(--text-2);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Wraps to a second line rather than clipping when the row gets narrow. */
.pipe-meta {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--sp-2) 14px;
}

.pipe-meta > * { white-space: nowrap; }
.pipe-salary { font-size: var(--fs-sm); color: var(--text-2); }
.pipe-updated { font-size: var(--fs-sm); color: var(--text-3); }

.status-chip {
  padding: 3px 9px;
  border-radius: var(--r-full);
  font-size: var(--fs-xs);
  font-weight: 500;
}
.status-chip.is-watching     { color: var(--st-watching);     background: var(--st-watching-bg); }
.status-chip.is-applied      { color: var(--st-applied);      background: var(--st-applied-bg); }
.status-chip.is-interviewing { color: var(--st-interviewing); background: var(--st-interviewing-bg); }
.status-chip.is-offer        { color: var(--st-offer);        background: var(--st-offer-bg); }
.status-chip.is-rejected     { color: var(--st-rejected);     background: var(--st-rejected-bg); }
.status-chip.is-skipped      { color: var(--st-skipped);      background: var(--st-skipped-bg); }

/* Segmented Cards/Table control. Anchors, not buttons: the view is a URL, so
   it survives a reload and needs no JavaScript. */
.segmented {
  display: flex;
  padding: 2px;
  background: var(--bg-hover);
  border-radius: var(--r-md);
  gap: 2px;
}

.segmented a {
  padding: 4px 12px;
  border-radius: 5px;
  font-size: var(--fs-sm);
  color: var(--text-2);
  text-decoration: none;
}
.segmented a.is-on {
  background: var(--bg-surface);
  color: var(--text);
  font-weight: 600;
  box-shadow: 0 1px 2px rgba(16,17,26,.06);
}

/* Dense table view. minmax() so cells ellipsise instead of slicing. */
.pipe-table {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: var(--shadow-1);
}

.pipe-tr {
  display: grid;
  grid-template-columns: minmax(200px, 2.2fr) minmax(104px, 1fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, .8fr);
  gap: var(--sp-4);
  padding: var(--sp-3) var(--sp-4);
  align-items: center;
}

.pipe-thead {
  background: var(--bg-subtle);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-3);
}

.pipe-tbody .pipe-tr { border-bottom: 1px solid var(--border); font-size: var(--fs-base); }
.pipe-tbody .pipe-tr:last-child { border-bottom: 0; }
.pipe-tbody .pipe-tr > * { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pipe-num { text-align: right; color: var(--text-3); }

/* ---------------------------------------------------------------------------
   Job detail: two columns with a sticky context rail (TRI-23)
   --------------------------------------------------------------------------- */

.job-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: var(--sp-5);
  align-items: start;
}

.job-main { display: flex; flex-direction: column; gap: var(--sp-4); min-width: 0; }

.job-main section,
.rail-card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 18px;
  box-shadow: var(--shadow-1);
}

.job-main section { margin-bottom: 0; }

.job-main section h2 {
  margin: 0 0 14px;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.01em;
  text-transform: none;
  border-bottom: 0;
  padding-bottom: 0;
  color: var(--text);
}

.job-rail { position: sticky; top: 0; display: flex; flex-direction: column; gap: var(--sp-4); }

.rail-company { display: flex; gap: var(--sp-3); align-items: center; margin-bottom: var(--sp-4); }
.avatar-lg { width: 38px; height: 38px; flex: 0 0 38px; border-radius: 9px; font-size: var(--fs-md); }
.rail-company-name { font-size: var(--fs-base); font-weight: 600; }
.rail-company-loc { font-size: var(--fs-sm); color: var(--text-3); }

/* Open state is a tint band rather than a chip: it is the single most load-
   bearing fact on this page, since a closed posting changes what you do next. */
.rail-band {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 9px 11px;
  border-radius: var(--r-md);
  font-size: var(--fs-sm);
  font-weight: 500;
  margin-bottom: var(--sp-4);
}
.rail-band .dot { width: 6px; height: 6px; border-radius: 50%; flex: 0 0 6px; }
.rail-band.is-open { background: var(--st-offer-bg); color: var(--st-offer); }
.rail-band.is-open .dot { background: var(--posting-open); }
.rail-band.is-closed { background: var(--st-rejected-bg); color: var(--st-rejected); }
.rail-band.is-closed .dot { background: var(--posting-closed); }

.rail-status { margin-bottom: var(--sp-4); }
.rail-status .btn { width: 100%; }

.rail-meta { margin: 0; display: flex; flex-direction: column; gap: 9px; }
.rail-meta > div { display: flex; justify-content: space-between; gap: var(--sp-3); font-size: var(--fs-sm); }
.rail-meta dt { color: var(--text-3); }
.rail-meta dd { margin: 0; color: var(--text); font-weight: 500; text-align: right; }

.rail-link {
  display: block;
  margin-top: var(--sp-4);
  padding-top: 14px;
  border-top: 1px solid var(--bg-hover);
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--accent);
  text-decoration: none;
}
.rail-link:hover { text-decoration: underline; }

/* Below the rail's usable width the two columns stack, rail last: on a narrow
   screen the thing you came to do matters more than the context beside it. */
@media (max-width: 900px) {
  .job-layout { grid-template-columns: minmax(0, 1fr); }
  .job-rail { position: static; order: 2; }
  .job-main { order: 1; }
}

/* ---------------------------------------------------------------------------
   Generated content (TRI-24)
   A plain bordered card with a quiet header. No sparkles, no gradient border,
   no "AI" badge: the output is a draft you edit, and dressing it up as
   something special makes it harder to read, not more trustworthy.
   --------------------------------------------------------------------------- */

.gen-card {
  margin-top: var(--sp-4);
  border: 1px solid var(--border);
  border-radius: 9px;
  overflow: hidden;
}

.gen-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-2) 14px;
  background: var(--bg-subtle);
  border-bottom: 1px solid var(--border);
}

.gen-label {
  font-size: var(--fs-xs);
  font-weight: 500;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-4);
}

.gen-actions { display: flex; gap: 14px; align-items: center; }

.gen-actions button,
.gen-actions .gen-action {
  background: none;
  border: 0;
  padding: 0;
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--accent);
  cursor: pointer;
  text-decoration: none;
}
.gen-actions button:hover,
.gen-actions .gen-action:hover { text-decoration: underline; }

/* Long output wraps; it never scrolls sideways. */
.gen-body {
  padding: var(--sp-4) 18px;
  font-size: var(--fs-base);
  line-height: 1.65;
  color: var(--text);
  max-width: 68ch;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* ---------------------------------------------------------------------------
   Rendered generated documents (TRI-93)
   The raw output is Markdown-ish, and .gen-body's pre-wrap was what made the
   syntax visible. Rendered documents opt out of pre-wrap — the structure now
   lives in the elements — while the raw-text panes above keep it.
   Spacing is set with margins on the children rather than gaps on the
   container, because a resume's rhythm is "tight under a heading, loose
   between sections", which a uniform gap cannot express.
   --------------------------------------------------------------------------- */

.gen-doc { white-space: normal; }

.gen-doc > *:first-child { margin-top: 0; }
.gen-doc > *:last-child  { margin-bottom: 0; }

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

.gen-doc h3,
.gen-doc h4,
.gen-doc h5,
.gen-doc h6 {
  margin: var(--sp-5) 0 var(--sp-2);
  line-height: 1.3;
  color: var(--text);
}

.gen-doc h3 { font-size: var(--fs-lg); font-weight: 600; }
.gen-doc h4 { font-size: var(--fs-base); font-weight: 600; letter-spacing: 0.02em; }
.gen-doc h5,
.gen-doc h6 { font-size: var(--fs-base); font-weight: 600; }

.gen-doc ul,
.gen-doc ol { margin: 0 0 var(--sp-3); padding-left: 1.35em; }
.gen-doc li { margin-bottom: var(--sp-1); }
.gen-doc li:last-child { margin-bottom: 0; }

.gen-doc hr {
  margin: var(--sp-4) 0;
  border: 0;
  border-top: 1px solid var(--border);
}

.gen-doc strong { font-weight: 600; }

.qa-question { font-size: var(--fs-base); font-weight: 500; margin: 0; }

/* ---------------------------------------------------------------------------
   Dense management table (TRI-25)
   Columns are minmax(0, Nfr) rather than plain fr: without the 0 minimum a
   grid track refuses to shrink below its content, so a long company name
   pushes the row wider instead of ellipsising inside it.
   --------------------------------------------------------------------------- */

.data-table {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
  overflow: hidden;
}

.data-tr {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1.6fr) minmax(0, 1fr) minmax(0, .7fr);
  gap: var(--sp-4);
  align-items: center;
  padding: 11px 18px;
  border-bottom: 1px solid var(--bg-hover);
}
.data-tr:last-child { border-bottom: 0; }
.data-tr:not(.data-thead):hover { background: var(--bg-subtle); }

.data-thead {
  background: var(--bg-subtle);
  border-bottom: 1px solid var(--border);
  padding: var(--sp-3) 18px;
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-3);
}

.data-right { display: flex; justify-content: flex-end; }
.data-roles { font-size: var(--fs-base); color: var(--text-2); }

.data-company { display: flex; align-items: center; gap: var(--sp-3); min-width: 0; }
.avatar-sm { width: 30px; height: 30px; flex: 0 0 30px; border-radius: 7px; font-size: var(--fs-sm); }
.data-company-id { min-width: 0; }
.data-company-name {
  font-size: var(--fs-base);
  font-weight: 550;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.data-company-sub {
  font-size: var(--fs-xs);
  color: var(--text-4);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Monospace, because a keyword filter is matched literally — the face makes
   a stray space or capital visible. */
.kw-input {
  width: 100%;
  padding: 5px 9px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
  font-family: var(--mono);
  color: var(--text-2);
  background: var(--bg-subtle);
}
.kw-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.kw-input:disabled { opacity: .5; cursor: not-allowed; }
.kw-input.is-saving { border-color: var(--accent-border); }


/* Failure toast. Only ever shown when a save did not land, so it is allowed
   to be conspicuous. */
.toast {
  position: fixed;
  bottom: var(--sp-5);
  left: 50%;
  transform: translateX(-50%);
  padding: 10px var(--sp-4);
  background: var(--text);
  color: var(--bg-surface);
  border-radius: var(--r-md);
  font-size: var(--fs-sm);
  box-shadow: var(--shadow-pop);
  z-index: 50;
}

.form-narrow { max-width: 480px; }

/* Explanatory line under a section heading. */
.section-intro {
  font-size: var(--fs-sm);
  color: var(--text-3);
  margin: 0 0 var(--sp-4);
  max-width: 70ch;
}

/* ---------------------------------------------------------------------------
   Resumes (TRI-26)
   --------------------------------------------------------------------------- */

.dropzone { flex-direction: column; gap: var(--sp-2); text-align: center; transition: border-color .12s, background .12s; }
.dropzone-prompt { cursor: pointer; color: var(--text-3); }
.dropzone-browse { color: var(--accent); font-weight: 500; text-decoration: underline; }

/* The native input stays in the flow rather than being hidden behind the
   label: without JavaScript it is the only thing that can report which file
   was chosen. */
.dropzone input[type="file"] { font-size: var(--fs-sm); color: var(--text-3); max-width: 100%; }

.dropzone.is-dragover { border-color: var(--accent); background: var(--accent-soft); }
.dropzone.is-dragover .dropzone-prompt { color: var(--accent); }

.dropzone-or { text-align: center; margin: var(--sp-3) 0; }

.resume-tr { grid-template-columns: minmax(0, 1.4fr) minmax(0, 2.2fr) minmax(0, .8fr) minmax(0, 1.1fr); }

.resume-id { min-width: 0; display: flex; flex-direction: column; gap: 4px; align-items: flex-start; }

.tag-default {
  padding: 2px 7px;
  border-radius: var(--r-full);
  background: var(--accent-soft);
  color: var(--accent);
  font-size: var(--fs-xs);
  font-weight: 500;
}

/* Two lines, then ellipsis. A resume is long; the point of the preview is to
   tell two versions apart, not to read one. */
.resume-preview {
  font-size: var(--fs-sm);
  color: var(--text-3);
  line-height: 1.45;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-width: 0;
}

.resume-actions { display: flex; gap: var(--sp-2); justify-content: flex-end; flex-wrap: wrap; }

@media (max-width: 860px) {
  .resume-tr { grid-template-columns: minmax(0, 1fr); gap: var(--sp-2); }
  .resume-tr.data-thead { display: none; }
  .resume-actions { justify-content: flex-start; }
}

/* ---------------------------------------------------------------------------
   Settings (TRI-27)
   Two independent cards, each with its own submit — saving preferences must
   never look like it might also change the password.
   --------------------------------------------------------------------------- */

.settings-card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
  padding: 20px;
  max-width: 560px;
  margin-bottom: var(--sp-5);
}

.settings-card .section-head { margin-bottom: var(--sp-2); }

/* A message under a nested location field lines up with the field, not with
   the checkbox above it. */
.check-nested + .field-error { margin-left: 25px; }

/* Admin tables (TRI-28) */
.admin-users-tr { grid-template-columns: minmax(0, 1.2fr) minmax(0, 1.6fr) minmax(0, .7fr) minmax(0, .9fr) minmax(0, .8fr); }
.admin-log-tr   { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr); }

@media (max-width: 860px) {
  .admin-users-tr, .admin-log-tr { grid-template-columns: minmax(0, 1fr); gap: var(--sp-2); }
  .admin-users-tr.data-thead, .admin-log-tr.data-thead { display: none; }
}

/* Theme picker (TRI-30). Radios, so it works without JavaScript and is
   announced as a single-choice group. */
.theme-choices { display: flex; gap: var(--sp-2); flex-wrap: wrap; }

.theme-choice {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 7px 13px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  background: var(--bg-surface);
  color: var(--text-2);
  font-size: var(--fs-base);
  cursor: pointer;
}
.theme-choice input { accent-color: var(--accent); margin: 0; }
.theme-choice.is-on { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.theme-choice:focus-within { box-shadow: 0 0 0 3px var(--accent-soft); }

/* ---------------------------------------------------------------------------
   Sidebar search button + command palette (TRI-31)
   --------------------------------------------------------------------------- */

.sidebar-search {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  margin-bottom: var(--sp-4);
  padding: 7px 10px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  color: var(--text-3);
  font-family: inherit;
  font-size: var(--fs-base);
  text-align: left;
  cursor: pointer;
}
.sidebar-search:hover { border-color: var(--border-strong); color: var(--text-2); }
.sidebar-search:focus-visible { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.sidebar-kbd { font-family: var(--mono); font-size: var(--fs-xs); color: var(--text-4); }

.palette {
  position: fixed;
  inset: 0;
  display: none;
  justify-content: center;
  align-items: flex-start;
  padding-top: 12vh;
  background: rgba(16, 17, 26, .45);
  z-index: 100;
}
.palette.is-open { display: flex; }

.palette-box {
  width: min(560px, calc(100vw - var(--sp-8)));
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-pop);
  overflow: hidden;
}

.palette-input {
  width: 100%;
  padding: var(--sp-4);
  border: 0;
  border-bottom: 1px solid var(--border);
  background: transparent;
  color: var(--text);
  font-family: inherit;
  font-size: var(--fs-md);
}
.palette-input:focus { outline: none; }

.palette-list { list-style: none; margin: 0; padding: var(--sp-2); max-height: 320px; overflow-y: auto; }

.palette-item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: 8px 11px;
  border-radius: var(--r-sm);
  cursor: pointer;
}
.palette-item.is-active { background: var(--bg-hover); }
.palette-t { font-size: var(--fs-base); color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.palette-item.is-active .palette-t { color: var(--accent); font-weight: 500; }
.palette-s { font-size: var(--fs-xs); color: var(--text-4); white-space: nowrap; }
.palette-empty { padding: 8px 11px; font-size: var(--fs-base); color: var(--text-3); }

@media (max-width: 860px) { .sidebar-search { max-width: 320px; } }

/* Deploy log. Genuine machine output, so it keeps <pre> and its monospace
   face — unlike the generated-prose cards, which are meant to be read. */
.log-block {
  margin: 0;
  padding: var(--sp-4);
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  font-family: var(--mono);
  font-size: var(--fs-sm);
  line-height: 1.6;
  color: var(--text-2);
  overflow-x: auto;
}

/* ATS token-format hints. Without JavaScript the first hint shows, which is
   the ATS the picker defaults to, so the pairing is still correct. */
.ats-hints .ats-hint { display: none; }
.ats-hints .ats-hint:first-child { display: inline; }
.ats-hints.is-enhanced .ats-hint,
.ats-hints.is-enhanced .ats-hint:first-child { display: none; }
.ats-hints.is-enhanced .ats-hint.is-shown { display: inline; }

/* Cron health panel (admin) */
.cron-tr { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 1.2fr) minmax(0, 1fr); }

.cron-alert {
  display: block;
  background: var(--danger-bg);
  border-top: 0;
}
.cron-alert-body { font-size: var(--fs-sm); color: var(--danger); line-height: 1.5; }
.cron-alert-msg { display: block; margin-top: 4px; font-family: var(--mono); font-size: var(--fs-xs); }

@media (max-width: 860px) {
  .cron-tr { grid-template-columns: minmax(0, 1fr); gap: var(--sp-2); }
  .cron-tr.data-thead { display: none; }
}

/* ---------------------------------------------------------------------------
   Resume match (TRI-14)
   The number is never shown bare — it sits next to a bar and the list of terms
   it is derived from, so it reads as "these words are missing" rather than as
   a verdict on the resume.
   --------------------------------------------------------------------------- */

.match-list { display: flex; flex-direction: column; gap: var(--sp-4); }

.match-row { display: flex; flex-direction: column; gap: 7px; }

.match-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3); }
.match-label {
  font-size: var(--fs-base);
  font-weight: 500;
  min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.match-score { font-size: var(--fs-md); font-weight: 600; font-variant-numeric: tabular-nums; }
.match-score.is-strong  { color: var(--st-offer); }
.match-score.is-partial { color: var(--st-interviewing); }
.match-score.is-weak    { color: var(--text-3); }
/* TRI-53: not scored, which is not the same as scored low. Deliberately mute
   and not a number — a 0% here would say "you are unqualified" about a posting
   whose description was never fetched. */
.match-score.is-unscored { color: var(--text-3); opacity: 0.55; font-weight: 400; }

.match-bar {
  height: 6px;
  border-radius: var(--r-full);
  background: var(--bg-hover);
  overflow: hidden;
}
.match-bar-fill { display: block; height: 100%; border-radius: var(--r-full); }
.match-bar-fill.is-strong  { background: var(--st-offer); }
.match-bar-fill.is-partial { background: var(--st-interviewing); }
/* Weak uses a neutral rather than red: a low score means "different vocabulary",
   which is not an error and often just means the wrong resume for this role. */
.match-bar-fill.is-weak    { background: var(--text-4); }

.match-missing { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; }
.match-missing-label { font-size: var(--fs-xs); color: var(--text-3); margin-right: 2px; }
.match-missing .chip { font-family: var(--mono); font-size: var(--fs-xs); }

/* Editing a generated draft (TRI-12) */
.is-hidden { display: none !important; }

.gen-edit { padding: var(--sp-4) 18px; border-top: 1px solid var(--border); }
.gen-edit .textarea { font-family: inherit; line-height: 1.6; }
.gen-edit-actions { display: flex; align-items: center; gap: var(--sp-2); margin-top: var(--sp-3); flex-wrap: wrap; }
.gen-edit-actions .field-hint { margin: 0 0 0 auto; }

.gen-edited { color: var(--st-interviewing); }
.gen-body-tight { padding-top: 0; }

/* ---------------------------------------------------------------------------
   Insights (TRI-16)
   --------------------------------------------------------------------------- */

.funnel { display: flex; flex-direction: column; gap: var(--sp-3); max-width: 640px; }

.funnel-row {
  display: grid;
  grid-template-columns: 92px minmax(0, 1fr) 44px 44px;
  align-items: center;
  gap: var(--sp-3);
}
.funnel-label { font-size: var(--fs-sm); color: var(--text-2); }
.funnel-bar { height: 22px; background: var(--bg-hover); border-radius: var(--r-sm); overflow: hidden; }
.funnel-fill { display: block; height: 100%; background: var(--accent); border-radius: var(--r-sm); }
.funnel-count {
  font-size: var(--fs-base); font-weight: 600;
  text-align: right; font-variant-numeric: tabular-nums;
}
.funnel-rate {
  font-size: var(--fs-sm); color: var(--text-3);
  text-align: right; font-variant-numeric: tabular-nums;
}

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

.stat {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-4);
  box-shadow: var(--shadow-1);
}
.stat-label { font-size: var(--fs-sm); color: var(--text-2); }
.stat-value {
  font-size: var(--fs-2xl); font-weight: 600; letter-spacing: -0.03em;
  margin-top: 4px; font-variant-numeric: tabular-nums; color: var(--text);
}
/* A dash is not a number — it must not read as a very bad score. */
.stat-value.is-unknown { color: var(--text-4); font-weight: 500; }
.stat-hint { font-size: var(--fs-xs); color: var(--text-3); margin-top: 6px; line-height: 1.4; }

.spark { display: flex; align-items: flex-end; gap: 6px; height: 140px; }
.spark-col { flex: 1 1 0; display: flex; flex-direction: column; justify-content: flex-end; height: 100%; min-width: 0; }
.spark-bar {
  display: block;
  width: 100%;
  min-height: 2px;          /* a zero week stays visible as a baseline */
  background: var(--accent-soft);
  border-top: 2px solid var(--accent);
  border-radius: 2px 2px 0 0;
}
.spark-label {
  font-size: var(--fs-xs); color: var(--text-4);
  margin-top: 6px; text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

@media (max-width: 860px) {
  .funnel-row { grid-template-columns: 80px minmax(0, 1fr) 36px 40px; gap: var(--sp-2); }
  .spark-label { display: none; }
}

/* API token reveal (TRI-13). Shown once, so it has to be easy to copy. */
.token-reveal {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3);
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  margin: var(--sp-3) 0;
}
.token-reveal code {
  font-family: var(--mono); font-size: var(--fs-sm);
  color: var(--text); word-break: break-all; min-width: 0;
}

/* Contacts (TRI-15) */
.contact-tr { grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) minmax(0, .9fr) minmax(0, 1.3fr); }
.contact-actions { display: flex; gap: 6px; justify-content: flex-end; flex-wrap: wrap; }

.rail-people { margin-top: var(--sp-4); padding-top: 14px; border-top: 1px solid var(--bg-hover); }
.rail-person { display: flex; align-items: center; gap: var(--sp-2); margin-top: var(--sp-2); }
.rail-person-id { min-width: 0; display: flex; flex-direction: column; }
.rail-person-name {
  font-size: var(--fs-sm); font-weight: 500; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rail-person-rel { font-size: var(--fs-xs); color: var(--text-3); }
.rail-person-x {
  margin-left: auto; background: none; border: 0; padding: 2px 6px;
  color: var(--text-4); font-size: var(--fs-md); cursor: pointer; line-height: 1;
}
.rail-person-x:hover { color: var(--danger); }
.rail-person-add { margin-top: var(--sp-3); }

@media (max-width: 860px) {
  .contact-tr { grid-template-columns: minmax(0, 1fr); gap: var(--sp-2); }
  .contact-tr.data-thead { display: none; }
  .contact-actions { justify-content: flex-start; }
}

/* Admin user row actions */
.user-actions { display: flex; gap: 6px; justify-content: flex-end; align-items: center; flex-wrap: wrap; }
.select-sm { padding: 4px 8px; font-size: var(--fs-sm); width: auto; }

@media (max-width: 860px) { .user-actions { justify-content: flex-start; } }

/* ---------------------------------------------------------------------------
   First-run setup (welcome.php)
   Reuses .auth-card for the frame, then widens it and undoes the one rule that
   does not survive the change: .auth-card .btn is full-width because a login
   card has a single button, and this card has "Continue" next to "Skip".
--------------------------------------------------------------------------- */
.setup-card { width: 480px; }
.setup-card .btn { width: auto; }

.setup-title {
  font-size: var(--fs-lg);
  font-weight: 600;
  letter-spacing: -0.02em;
  margin: var(--sp-5) 0 var(--sp-2);
}

/* The rail says where you are without promising anything: steps are not
   clickable, because a skipped step is not "unfinished business" and offering
   navigation back to it implies it is. */
.setup-rail {
  display: flex;
  gap: var(--sp-2);
  list-style: none;
  margin: var(--sp-4) 0 0;
  padding: 0;
}
.setup-rail li {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-xs);
  /* --text-2 rather than --text-3: the step numbers are 11px, too small for
     the large-text exemption, and --text-3 measured 4.31 against the card. */
  color: var(--text-2);
  min-width: 0;
}
.setup-rail-dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  flex: 0 0 20px;
  border-radius: 50%;
  border: 1px solid var(--border);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}
/* Labels vanish on the steps you are not on, so the current one has room to
   read as a label rather than three competing ones.
   Hidden the sr-only way, not display:none — the number beside it is
   aria-hidden decoration, so display:none would leave a screen reader with
   two empty list items and no idea what steps 2 and 3 are. */
.setup-rail li:not(.is-current) .setup-rail-label {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
.setup-rail li.is-current { color: var(--text); }
.setup-rail li.is-current .setup-rail-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.setup-rail li.is-current .setup-rail-dot {
  background: var(--accent);
  border-color: var(--accent);
  /* --on-accent, not #fff: the dark theme's accent is a pale teal, and white
     on it measured 1.93 — the same reason .btn-primary uses this token. */
  color: var(--on-accent);
}
.setup-rail li.is-past .setup-rail-dot {
  background: var(--accent-soft);
  border-color: var(--accent-soft);
  color: var(--accent-hover);   /* same reason as .feed-tab.is-active */
}

.setup-have {
  color: var(--text-2);
  font-size: var(--fs-sm);
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: var(--sp-2) var(--sp-3);
  margin: 0 0 var(--sp-4);
}

.setup-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-5);
}

/* Skipping the whole flow is a real option, so it is a real control and not a
   greyed-out afterthought — but it is quieter than Continue, because the
   default answer to "shall we set this up" is yes. */
.setup-skip-all {
  margin-top: var(--sp-5);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--border);
}
.btn-link {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-size: var(--fs-sm);
  color: var(--text-2);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.btn-link:hover { color: var(--text); }
.btn-link:focus-visible {
  outline: none;
  border-radius: var(--r-sm);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.setup-summary {
  list-style: none;
  margin: 0 0 var(--sp-6);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}
.setup-summary li {
  font-size: var(--fs-base);
  color: var(--text-2);
  padding-left: 22px;
  position: relative;
}
/* A tick for what is done and a dash for what is not — shape, not colour
   alone, since "done" and "skipped" must be distinguishable without it. */
.setup-summary li::before {
  content: "–";
  position: absolute;
  left: 4px;
  color: var(--text-4);
}
.setup-summary li.is-done { color: var(--text); }
.setup-summary li.is-done::before {
  content: "✓";
  left: 2px;
  color: var(--accent);
}

@media (max-width: 560px) {
  .setup-card { width: 100%; }
  .setup-actions { flex-direction: column; align-items: stretch; }
  .setup-card .setup-actions .btn { width: 100%; text-align: center; }
}

/* ---------------------------------------------------------------------------
   Company search (includes/company-search.php)
   Shared by the setup flow and the companies page, so everything here is
   scoped to .cs-* and assumes nothing about which shell it sits in.
--------------------------------------------------------------------------- */
.cs-form {
  display: flex;
  gap: var(--sp-2);
  margin-bottom: var(--sp-4);
}
.cs-form .input { flex: 1 1 auto; min-width: 0; }
/* A <button> will not stretch as a flex item here — align-self:stretch and
   height:100% both left it at its natural size while it bottom-aligned against
   the taller input. Centring is what fixes the sag.
   No matching height: measured once the webfont has actually loaded, the input
   is 34.5px and the button 32.2px, and 2.3px centred is invisible. The 40px I
   first "measured" was the fallback font's metrics before Inter arrived. */
.cs-form { align-items: center; }
.cs-form .btn { flex: 0 0 auto; width: auto; }

.cs-results {
  list-style: none;
  margin: 0 0 var(--sp-4);
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  overflow: hidden;
}
.cs-result {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-3);
  background: var(--bg-surface);
}
.cs-result + .cs-result { border-top: 1px solid var(--border); }

.cs-result-main { min-width: 0; }
.cs-result-name {
  display: block;
  font-size: var(--fs-base);
  color: var(--text);
  font-weight: 500;
}
/* The platform and slug are how you tell two boards with the same name apart,
   so they must stay readable — --text-2, not the faintest token available. */
.cs-result-meta {
  display: block;
  font-size: var(--fs-sm);
  color: var(--text-2);
  overflow: hidden;
  text-overflow: ellipsis;
}
.cs-follow { flex: 0 0 auto; margin: 0; }

.cs-following {
  flex: 0 0 auto;
  font-size: var(--fs-sm);
  color: var(--text-2);
  white-space: nowrap;
}

.cs-empty, .cs-note {
  font-size: var(--fs-sm);
  color: var(--text-2);
  margin: 0 0 var(--sp-4);
}

.cs-manual {
  border-top: 1px solid var(--border);
  padding-top: var(--sp-4);
  margin-top: var(--sp-2);
}
.cs-manual > summary {
  cursor: pointer;
  font-size: var(--fs-sm);
  color: var(--text-2);
  margin-bottom: var(--sp-3);
}
.cs-manual > summary:hover { color: var(--text); }
.cs-manual > summary:focus-visible {
  outline: none;
  border-radius: var(--r-sm);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

@media (max-width: 560px) {
  .cs-form { flex-direction: column; }
  .cs-form .btn { width: 100%; }
  .cs-result { flex-direction: column; align-items: stretch; gap: var(--sp-2); }
}

/* ---------------------------------------------------------------------------
   Explore feed (explore.php)
--------------------------------------------------------------------------- */
.feed-meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  flex-wrap: wrap;
  margin-bottom: var(--sp-4);
}
.feed-scope { display: flex; gap: var(--sp-1); }
.feed-tab {
  font-size: var(--fs-sm);
  color: var(--text-2);
  text-decoration: none;
  padding: 4px 10px;
  border-radius: var(--r-full);
}
.feed-tab:hover { background: var(--bg-hover); color: var(--text); }
/* --accent-hover, not --accent. Accent text on the soft accent tint measured
   4.47 in light mode — just under AA, and it is a pairing used in more than one
   place (see .setup-rail .is-past). The hover token is the same hue a shade
   deeper, so it reads identically and clears the bar. */
.feed-tab.is-active {
  background: var(--accent-soft);
  color: var(--accent-hover);
  font-weight: 500;
}
.feed-corpus { font-size: var(--fs-sm); color: var(--text-2); margin: 0; }

.feed-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  overflow: hidden;
}
.feed-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-3);
  background: var(--bg-surface);
}
.feed-row + .feed-row { border-top: 1px solid var(--border); }
.feed-row:hover { background: var(--bg-hover); }

.feed-row-main { min-width: 0; }
.feed-title {
  font-size: var(--fs-base);
  font-weight: 500;
  color: var(--text);
  text-decoration: none;
}
.feed-title:hover { color: var(--accent); text-decoration: underline; }
.feed-sub {
  margin: 2px 0 0;
  font-size: var(--fs-sm);
  color: var(--text-2);
}

.feed-row-side {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex: 0 0 auto;
}
.feed-follow { margin: 0; }

@media (max-width: 640px) {
  .feed-row { flex-direction: column; align-items: stretch; gap: var(--sp-2); }
  .feed-row-side { justify-content: space-between; }
}

/* A collapsed cross-board duplicate. Stated, not hidden — the whole risk of
   dedup is removing something without saying so. */
.feed-dupe { color: var(--text-3); }

/* TRI-100: a posting demanding work authorization this user has not got. It
   sits at the bottom of the feed already; this is the sentence that says why.
   Amber rather than red — it is a caution about a posting, not an error, and
   the reading behind it can be wrong. */
.work-auth-flag {
  margin: var(--sp-1) 0 0;
  font-size: var(--fs-sm);
  color: var(--st-interviewing);
}

/* ---------------------------------------------------------------------------
   Job description (TRI-41)
   The body is third-party HTML passed through includes/html-sanitize.php, so
   the tag set it can contain is a known, short list — these rules cover it.
--------------------------------------------------------------------------- */
.job-description { margin-bottom: var(--sp-6); }

.jd-body {
  font-size: var(--fs-base);
  line-height: 1.65;
  color: var(--text);
  /* A long description on a wide screen is unreadable at full width. */
  max-width: 68ch;
}
.jd-body > *:first-child { margin-top: 0; }
.jd-body > *:last-child { margin-bottom: 0; }
.jd-body p { margin: 0 0 var(--sp-3); }
.jd-body h1, .jd-body h2, .jd-body h3,
.jd-body h4, .jd-body h5, .jd-body h6 {
  /* Normalised to one size: boards start their headings at wildly different
     levels, and an h1 inside the page's own h2 section would outrank it. */
  font-size: var(--fs-md);
  font-weight: 600;
  margin: var(--sp-5) 0 var(--sp-2);
  color: var(--text);
}
.jd-body ul, .jd-body ol { margin: 0 0 var(--sp-3); padding-left: var(--sp-5); }
.jd-body li { margin-bottom: 6px; }
.jd-body a { color: var(--accent); }
.jd-body strong, .jd-body b { font-weight: 600; }
.jd-body blockquote {
  margin: 0 0 var(--sp-3);
  padding-left: var(--sp-3);
  border-left: 2px solid var(--border);
  color: var(--text-2);
}
.jd-body pre {
  background: var(--bg-subtle);
  padding: var(--sp-3);
  border-radius: var(--r-md);
  overflow-x: auto;
}
.jd-body code { font-size: var(--fs-sm); }
.jd-body hr { border: 0; border-top: 1px solid var(--border); margin: var(--sp-4) 0; }
.jd-body table { border-collapse: collapse; margin-bottom: var(--sp-3); display: block; overflow-x: auto; }
.jd-body th, .jd-body td { border: 1px solid var(--border); padding: 6px 10px; text-align: left; }
/* Boards occasionally emit an <img>; the sanitizer drops it, but a stray
   oversized element must never widen the page. */
.jd-body * { max-width: 100%; }

.jd-source {
  font-size: var(--fs-sm);
  color: var(--text-2);
  margin: var(--sp-4) 0 0;
}

.source-badge {
  display: inline-block;
  font-size: var(--fs-xs);
  padding: 2px 8px;
  border-radius: var(--r-full);
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  color: var(--text-2);
}
.source-badge-alt {
  text-decoration: none;
  margin-left: 4px;
}
.source-badge-alt:hover { color: var(--text); border-color: var(--text-4); }

/* Location inputs sit in the same row as the keyword search, and need to be
   narrower than it — a state is two characters. */
.cs-input-narrow { flex: 0 1 160px; min-width: 90px; }
.cs-input-tiny   { flex: 0 1 90px;  min-width: 64px; }
@media (max-width: 640px) {
  .cs-input-narrow, .cs-input-tiny { flex: 1 1 auto; }
}

/* Two fields side by side in a settings form. */
.field-row { display: flex; gap: var(--sp-3); }
.field-row > .field { flex: 1 1 0; min-width: 0; }
@media (max-width: 560px) { .field-row { flex-direction: column; gap: 0; } }

/* ---------------------------------------------------------------------------
   Job comment log (TRI-42)
   Reuses .gen-card from the Q&A history directly above it, so the two logs on
   this page look like the same kind of thing — which they are.
--------------------------------------------------------------------------- */
.comment-add { margin-bottom: var(--sp-4); }
.comment-add .textarea { margin-bottom: var(--sp-2); }

/* The delete button sits in .gen-actions beside Edit, so its form must not
   introduce a block box or the two controls stop sharing a line. */
.comment-delete { display: inline; margin: 0; }
.comment-delete button {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-size: var(--fs-sm);
  color: var(--text-2);
  cursor: pointer;
}
.comment-delete button:hover { color: var(--danger); }
.comment-delete button:focus-visible {
  outline: none;
  border-radius: var(--r-sm);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* ---------------------------------------------------------------------------
   AI job summary (TRI-47)
   Sits above the description, because it exists to be read INSTEAD of it.
--------------------------------------------------------------------------- */
.job-summary { margin-bottom: var(--sp-5); }

.ai-summary {
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: var(--sp-4);
}
.ai-summary-label {
  display: block;
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-2);
  margin-bottom: var(--sp-2);
}
.ai-summary-body {
  margin: 0;
  font-size: var(--fs-md);
  line-height: 1.55;
  color: var(--text);
  max-width: 62ch;
}
/* Attribution is not decoration: a reader deciding whether to apply should
   know this paragraph was written by a model, not by the company. */
.ai-summary-foot {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
  margin: var(--sp-3) 0 0;
  font-size: var(--fs-sm);
  color: var(--text-2);
}
.ai-summary-regen { margin: 0; }
.ai-summary-regen button,
.ai-summary-ask .field-hint { font-size: var(--fs-sm); }
.ai-summary-regen button {
  background: none;
  border: none;
  padding: 0;
  font-family: inherit;
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.ai-summary-regen button:hover { color: var(--accent-hover); }
.ai-summary-regen button:focus-visible,
.ai-summary-ask button:focus-visible {
  outline: none;
  border-radius: var(--r-sm);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.ai-summary-ask {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
  margin: 0;
}
.ai-summary-ask .field-hint { margin: 0; }

/* Feed attribution. Rise's terms require a visible credit on every posting they
   supply, so this is an obligation rather than a detail — muted, but present. */
.rail-credit {
  margin: var(--sp-2) 0 0;
  font-size: var(--fs-sm);
  color: var(--text-2);
}

/* "Also posted on" — the other boards a deduped posting appears on. Muted:
   it is provenance, not the job. */
.job-card-also {
  margin: 6px 0 0;
  font-size: var(--fs-sm);
  color: var(--text-2);
}

/* The description heading and its copy button share a line. */
.jd-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
}
.jd-head h2 { margin-bottom: var(--sp-3); }
.jd-copy {
  flex: 0 0 auto;
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-size: var(--fs-sm);
  color: var(--text-2);
  cursor: pointer;
}
.jd-copy:hover { color: var(--accent); }
.jd-copy:focus-visible {
  outline: none;
  border-radius: var(--r-sm);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* Dashboard sort control (TRI-44). */
.sort-bar {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
  flex-wrap: wrap;
}
.sort-label { font-size: var(--fs-sm); color: var(--text-2); }

/* In a section header the bar is a sibling of the h2, not a block above the
   content, so its own bottom margin would push it off the heading's baseline. */
.section-head .sort-bar { margin-bottom: 0; }

/* The heading and the control share a row until there is not room for both. */
.section-head { flex-wrap: wrap; }

/* Explaining why relevance has nothing to rank against needs a line of its own —
   in the header row it pushed the sort links off the edge on a narrow window. */
.sort-note {
  font-size: var(--fs-sm);
  color: var(--text-2);
  margin: 0 0 var(--sp-3);
}

/* Why a posting ranked where it did. */
.job-card-fit {
  margin: 6px 0 0;
  font-size: var(--fs-sm);
  color: var(--text-2);
}

/* Company-grouped roles (TRI-45). Server-rendered one company at a time, so
   this is a row that appears in the table rather than a panel that slides. */
.data-roles-list {
  grid-column: 1 / -1;
  padding: var(--sp-3) var(--sp-3) var(--sp-3) 46px;
  background: var(--bg-subtle);
  border-top: 1px solid var(--border);
}
.role-list { list-style: none; margin: 0; padding: 0; }
.role-row {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  padding: 5px 0;
  flex-wrap: wrap;
}
.role-row + .role-row { border-top: 1px solid var(--border); }
.role-title { font-size: var(--fs-base); color: var(--text); text-decoration: none; }
.role-title:hover { color: var(--accent); text-decoration: underline; }
.role-meta { font-size: var(--fs-sm); color: var(--text-2); }
/* The count is a control now, so it should look like one. */
.data-roles a { color: var(--accent); text-decoration: none; }
.data-roles a:hover { text-decoration: underline; }

/* Saved searches (TRI-49). */
.saved-searches {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  margin-bottom: var(--sp-3);
}
.saved-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 8px;
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  background: var(--bg-surface);
  font-size: var(--fs-sm);
}
.saved-chip a { color: var(--text); text-decoration: none; }
.saved-chip a:hover { color: var(--accent); }
.saved-chip form { display: inline; margin: 0; }
.saved-chip button {
  background: none; border: none; padding: 0 0 0 2px;
  font: inherit; line-height: 1; color: var(--text-3); cursor: pointer;
}
.saved-chip button:hover { color: var(--danger); }
.save-search { display: flex; gap: var(--sp-2); align-items: center; margin: 0 0 var(--sp-3); }

/* Extension install steps. Numbered because the order matters and skipping one
   leaves you with a token and nothing to paste it into. */
.install-steps {
  margin: 0 0 var(--sp-4);
  padding-left: var(--sp-5);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}
.install-steps li { font-size: var(--fs-base); color: var(--text); }
.install-steps .field-hint { display: block; margin: 4px 0 0; }
.install-steps code {
  background: var(--bg-subtle);
  padding: 1px 5px;
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
}

/* The Generate button belongs next to the text that tells you to press it. */
.token-actions { margin-top: var(--sp-3); }


/* TRI-52: add a company by pasting its careers page address. -------------- */

.url-add { margin-bottom: var(--sp-4); }
.url-add .field { margin-bottom: var(--sp-2); }

/* What was found, before anything is saved. Deliberately looks like a result
   rather than a form: the question it answers is "is this the right company",
   and the only editable thing is what to call it. */
.detected-card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent, var(--text));
  border-radius: var(--r-lg);
  padding: var(--sp-3);
  margin-bottom: var(--sp-4);
  box-shadow: var(--shadow-1);
}
.detected-head {
  margin: 0 0 var(--sp-1);
  font-size: var(--fs-md);
  color: var(--text);
}
/* The board address. Shown because it is the thing that was guessed, and a
   wrong guess is recognisable here and nowhere else. */
.detected-token {
  margin: 0 0 var(--sp-3);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--fs-sm);
  color: var(--text-2);
  overflow-wrap: anywhere;
}


/* TRI-55: interview prep. --------------------------------------------------- */

.prep-set {
  margin-top: var(--sp-4);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--border);
}
.prep-meta { font-size: var(--fs-sm); color: var(--text-2); margin-bottom: var(--sp-2); }

/* The figures the model was told not to invent.
   Deliberately NOT .field-hint: measured at 4.6:1 and 11px, that made the one
   line the reader must not skip the faintest and smallest on the page. Full
   text colour, body size. */
.prep-warning {
  border-left: 3px solid var(--st-interviewing, var(--accent));
  padding-left: var(--sp-2);
  margin: 0 0 var(--sp-3);
  color: var(--text);
  font-size: var(--fs-sm);
}
.prep-warning code {
  background: var(--bg-subtle, var(--bg-surface));
  padding: 0.05em 0.3em;
  border-radius: var(--r-sm, 4px);
}

.prep-q { margin-bottom: var(--sp-3); }
.prep-question { margin: 0 0 var(--sp-2); font-weight: 600; color: var(--text); }

/* Which kind of question, so the reader knows whether a STAR answer is even
   the right shape before reading it. */
.prep-kind {
  display: inline-block;
  font-size: var(--fs-xs, 0.72rem);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0.1em 0.45em;
  border-radius: var(--r-sm, 4px);
  margin-right: var(--sp-1);
  vertical-align: 0.1em;
  background: var(--accent-soft);
  /* --accent on --accent-soft measures 4.47:1 in light mode, which fails AA for
     text this small by a hair. --accent-hover is the same hue, darker. */
  color: var(--accent-hover, var(--accent));
}
.prep-kind.is-role { background: var(--bg-subtle, var(--bg-surface)); color: var(--text-2); }

/* Notes to speak from, so the labels stay out of the way of the content. */
.prep-star { margin: 0; display: grid; grid-template-columns: max-content 1fr; gap: 0.25rem var(--sp-2); }
.prep-star dt {
  font-size: var(--fs-sm); color: var(--text-2); font-weight: 500;
}
.prep-star dd { margin: 0; color: var(--text); }

.prep-note { margin: var(--sp-1) 0 0; color: var(--text-2); }

/* A reply the parser could not read is still shown rather than discarded — the
   user paid for it. It carries .gen-doc, so it is Markdown-rendered like every
   other generated document and no longer needs pre-wrap to keep its shape —
   which is what used to make its blank lines double up.
   Kept muted and boxed: it is still the "we could not read this properly" case,
   and it should not pass for a parsed prep set. */
.prep-raw {
  font-size: var(--fs-sm);
  color: var(--text-2);
  background: var(--bg-subtle, var(--bg-surface));
  padding: var(--sp-2);
  border-radius: var(--r-md, 6px);
  overflow-x: auto;
}


/* TRI-70: the job page's tool tabs. ---------------------------------------- */

.job-tabs {
  display: flex;
  gap: var(--sp-1);
  border-bottom: 1px solid var(--border);
  margin: var(--sp-5) 0 0;
  flex-wrap: wrap;
}
.job-tab {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--text-2);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  /* Sits ON the container's border rather than above it, so the active
     underline replaces that line instead of doubling it. */
  margin-bottom: -1px;
}
.job-tab:hover { color: var(--text); }
.job-tab.is-active { color: var(--accent-hover); border-bottom-color: var(--accent); }
.job-tab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  border-radius: var(--r-sm) var(--r-sm) 0 0;
}

/* What already exists on this job, without opening the tab. */
.job-tab-count {
  font-family: var(--mono);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  background: var(--bg-hover);
  color: var(--text-2);
  border-radius: var(--r-full);
  padding: 1px 7px;
  min-width: 20px;
  text-align: center;
}
.job-tab.is-active .job-tab-count { background: var(--accent-soft); color: var(--accent-hover); }

.job-tabpanel { padding-top: var(--sp-5); }
/* The panel's first section already carries the gap above it. */
.job-tabpanel > section:first-child { margin-top: 0; }

/* Rail summary: what is on this job, each line a way in. */
.rail-summary { list-style: none; margin: 0; padding: 0; }
.rail-summary li + li { border-top: 1px solid var(--border); }
.rail-summary a {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) 0;
  font-size: var(--fs-sm);
  color: var(--text-2);
  text-decoration: none;
}
.rail-summary a:hover { color: var(--accent); }
.rail-summary .rail-num {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  color: var(--text);
}


/* The employer's own posting, now that the card title leads to the tracked
   record instead. Quiet: it is the secondary destination, not the primary. */
.job-card-orig {
  margin-left: var(--sp-2);
  color: var(--text-3);
  text-decoration: none;
  white-space: nowrap;
}
.job-card-orig:hover { color: var(--accent); text-decoration: underline; }

/* Explore's way in to the tracked record. Sits in the meta line rather than
   taking the title, which for feed rows carries an attribution obligation. */
.feed-details { color: var(--text-2); text-decoration: none; }
.feed-details:hover { color: var(--accent); text-decoration: underline; }

/* TRI-74: the two blocks of characters somebody has to copy accurately —
   a TOTP key and a set of recovery codes. Monospace and generous letter
   spacing because these get transcribed by hand under mild stress, and
   0/O and 1/l are the difference between working and not. */
.totp-secret,
.recovery-codes {
  font-family: var(--mono);
  font-size: 1rem;
  letter-spacing: 0.08em;
  line-height: 1.7;
  padding: 0.85rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg-subtle);
  color: var(--text);
  overflow-x: auto;
  user-select: all;   /* one click selects the whole thing */
}

/* TRI-74: the enrolment QR. The white panel is padding around the symbol's own
   quiet zone, not decoration — a code butted against other content is markedly
   harder for a camera to lock onto, and it fails by simply not being picked up
   rather than by anything you could diagnose.

   Deliberately white in both themes: a scanner looks for contrast in a known
   polarity, and inverting a QR for dark mode is a real way to produce one that
   some phones will not read. */
.qr-panel {
  display: inline-block;
  padding: 0.75rem;
  margin: 0.25rem 0 1rem;
  background: #ffffff;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  line-height: 0;   /* no descender gap under the inline svg */
}

.qr-panel .qr-code {
  display: block;
  width: 220px;
  height: auto;
  max-width: 100%;
}


/* TRI-54: applications that have gone quiet. ------------------------------- */

/* Deliberately not an alert colour. This is a reminder, not a failure, and a
   dashboard that opens with something red every morning stops being read. */
.nudge-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-2); }

.nudge-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--st-interviewing);
  border-radius: var(--r-lg);
  padding: var(--sp-3) var(--sp-4);
  box-shadow: var(--shadow-1);
}
/* A save that never became an application is a smaller thing than an
   application nobody answered. */
.nudge-row.is-unapplied_save { border-left-color: var(--border-strong); }

.nudge-id { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 240px; }
.nudge-title { font-weight: 600; color: var(--text); text-decoration: none; }
.nudge-title:hover { color: var(--accent); text-decoration: underline; }
.nudge-company { font-size: var(--fs-sm); color: var(--text-2); }
.nudge-why { font-size: var(--fs-sm); color: var(--text-2); flex: 0 1 auto; }


/* Settings, moved out of the nav into the user block. ---------------------- */
.sidebar-gear {
  margin-left: auto;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: var(--r-md);
  color: var(--text-3);
}
.sidebar-gear:hover { color: var(--text); background: var(--bg-hover); }
.sidebar-gear.is-active { color: var(--accent); background: var(--accent-soft); }
.sidebar-gear:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The city for a work type that needs one (onboarding). -------------------- */
.work-city { margin-top: var(--sp-3); max-width: 340px; }
/* Shown when hybrid OR on-site is ticked; remote needs no city.
   Hidden only when NEITHER is — written this way round on purpose: if :has()
   is unsupported the selector is dropped and the field stays visible, which is
   usable. The opposite phrasing would hide it permanently. */
.check-group:not(:has(#want_hybrid:checked)):not(:has(#want_onsite:checked)) + .work-city {
  display: none;
}


/* Explore's work-type filter. --------------------------------------------- */
/* Its own row, and deliberately OUTSIDE .cs-form in the markup — it belongs to
   the form through the HTML form="" attribute instead.
   Sharing that flex row was the mistake: beside four text inputs the chips
   collapsed into a narrow column, one per line. Letting the row wrap fixed the
   chips and broke the inputs, which had shared it correctly since it was
   written. Taking the chips out of the flex context leaves both alone. */
.cs-types {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  /* A fieldset carries a browser border and padding nothing else here has. */
  border: 0;
  margin: calc(var(--sp-3) * -1) 0 var(--sp-4);
  padding: 0;
  min-inline-size: 0;
}
/* Chips rather than a checkbox column: this sits inline in the search bar,
   and three stacked checkboxes there would dominate a row whose subject is
   the query. */
.cs-type {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: 6px 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-full);
  font-size: var(--fs-sm);
  color: var(--text-2);
  cursor: pointer;
  white-space: nowrap;
  user-select: none;
}
.cs-type:hover { color: var(--text); background: var(--bg-hover); }
.cs-type:has(input:checked) {
  background: var(--accent-soft);
  border-color: var(--accent-border);
  color: var(--accent-hover);
}
/* The box itself stays, so the control is still a checkbox to a screen reader
   and to anyone whose browser does not support :has(). */
.cs-type input { margin: 0; }
.cs-type:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }

.feed-note {
  font-size: var(--fs-sm);
  color: var(--text-2);
  margin: 0 0 var(--sp-3);
}


/* --- Skipped: the toggle, the restore, and the Skip button (TRI-94) --------- *
 *
 * A quiet button. Skip sits beside Save on every card in the feed and must not
 * compete with it — the default action is still to save something, and two
 * equally weighted buttons would make every card a question. Text only until
 * hovered, so the row reads as one action with an escape hatch.
 */
.btn-quiet {
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-3);
}
.btn-quiet:hover {
  background: var(--bg-hover);
  border-color: var(--border);
  color: var(--text-2);
}

/* Skip and Save are two separate forms, so they need a flex parent of their
   own to sit on one line inside the card footer. */
.job-card-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
}

/* Holds the "Show skipped" link and the Cards/Table control on one line, and
   lets them wrap together rather than separately on a narrow window. */
.section-tools {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

.skipped-toggle {
  font-size: var(--fs-sm);
  color: var(--text-2);
  text-decoration: none;
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  white-space: nowrap;
}
.skipped-toggle:hover { background: var(--bg-hover); color: var(--text); }
/* On, it states the current view rather than offering it — the same job
   .segmented a.is-on does, in the shape a single link can manage. */
.skipped-toggle.is-on {
  background: var(--accent-soft);
  border-color: var(--accent-border);
  color: var(--accent);
}
.skipped-toggle:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* In the table view the chip and the Restore button share one cell. */
.pipe-tr .unskip-form { display: inline-block; margin-left: var(--sp-1); }

/* In-flight state for AI actions (TRI-108). Markup-free: app.js adds .is-busy to
   the clicked button and a .busy-note line to its form. */
button.is-busy { position: relative; padding-left: 2.1em; cursor: progress; }
button.is-busy::before {
  content: "";
  position: absolute; left: 0.85em; top: 50%;
  width: 0.85em; height: 0.85em; margin-top: -0.43em;
  border: 2px solid currentColor; border-right-color: transparent;
  border-radius: 50%;
  animation: busy-spin 0.8s linear infinite;
}
@keyframes busy-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  button.is-busy::before { animation: none; border-right-color: currentColor; opacity: 0.5; }
}
.busy-note { flex-basis: 100%; margin: 0.25rem 0 0; }

/* ---- Inbox (TRI-127) ---------------------------------------------------- */
.inbox-muted { color: var(--text-3); font-size: var(--fs-sm); }
.inbox-address { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-3); margin: 0 0 var(--sp-3); }
.inbox-address code, .job-tabpanel code { font-family: var(--mono); }

.inbox-split {
  display: grid;
  grid-template-columns: minmax(260px, 340px) 1fr;
  gap: var(--sp-5);
  margin-top: var(--sp-4);
  align-items: stretch;
  /* Both panes fill the screen under the header; each scrolls on its own. */
  height: calc(100vh - 290px);
  min-height: 420px;
}
.inbox-list {
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--bg-surface);
  overflow-y: auto;
}
.inbox-pane {
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--bg-surface);
  padding: var(--sp-5);
  overflow-y: auto;
}
.inbox-placeholder { height: 100%; display: flex; align-items: center; justify-content: center; color: var(--text-3); }
.inbox-placeholder p { margin: 0; }
.inbox-item {
  display: flex; flex-direction: column; gap: 2px;
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border);
  color: var(--text); text-decoration: none;
}
.inbox-item:last-child { border-bottom: 0; }
.inbox-item:hover { background: var(--bg-hover); }
.inbox-item.is-active { background: var(--bg-hover); box-shadow: inset 2px 0 0 var(--accent); }
.inbox-item-top { display: flex; justify-content: space-between; gap: var(--sp-3); }
.inbox-who { font-size: var(--fs-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.inbox-when { font-size: var(--fs-xs); color: var(--text-3); white-space: nowrap; }
.inbox-subject { font-size: var(--fs-sm); color: var(--text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.inbox-item.is-unread .inbox-who, .inbox-item.is-unread .inbox-subject { font-weight: 600; color: var(--text); }
.inbox-item.is-unread .inbox-who::before {
  content: ''; display: inline-block; width: 7px; height: 7px; margin-right: 6px;
  border-radius: 50%; background: var(--accent); vertical-align: middle;
}
.inbox-chip { font-size: var(--fs-xs); color: var(--accent-hover); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.inbox-chip.is-none { color: var(--text-3); }
.inbox-pager { display: flex; gap: var(--sp-4); padding: var(--sp-3) var(--sp-4); margin: 0; font-size: var(--fs-sm); }
.inbox-empty { padding: var(--sp-5); color: var(--text-3); margin: 0; }

.inbox-detail-head h2 { margin: 0 0 var(--sp-2); }
.inbox-meta { margin: 2px 0; font-size: var(--fs-sm); color: var(--text-2); }
.inbox-addr { color: var(--text-3); }
.inbox-dot { margin: 0 var(--sp-1); color: var(--text-3); }
.inbox-filing { margin: var(--sp-4) 0; padding: var(--sp-3) var(--sp-4); border: 1px solid var(--border); border-radius: var(--r-md); }
.inbox-filing p { margin: 0 0 var(--sp-2); font-size: var(--fs-sm); }
.inbox-assign { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
.inbox-assign .field-label { margin: 0; }
.inbox-assign .select { max-width: 320px; }
.inbox-body { white-space: normal; overflow-wrap: anywhere; line-height: 1.55; margin: var(--sp-4) 0; }
.inbox-actions { margin-top: var(--sp-4); }
.inbox-thread-item { padding: var(--sp-4) 0; border-bottom: 1px solid var(--border); }
.inbox-thread-item h3 { margin: var(--sp-1) 0; font-size: var(--fs-md, 1rem); }

@media (max-width: 860px) {
  .inbox-split { grid-template-columns: 1fr; height: auto; }
  .inbox-list, .inbox-pane { max-height: none; }
}

.inbox-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.inbox-delete { margin-top: var(--sp-5); border-top: 1px solid var(--border); padding-top: var(--sp-3); font-size: var(--fs-sm); }
.inbox-delete summary { cursor: pointer; color: var(--text-3); }
.inbox-delete summary:hover { color: var(--danger); }
.inbox-delete form { margin-top: var(--sp-2); }

/* Inbox: turn an unplaced message into a job. Collapsed so it stays out of the way. */
.inbox-track { margin-top: var(--sp-3); }
.inbox-track > summary { cursor: pointer; font-size: var(--fs-sm); color: var(--text-2); }
.inbox-track form { margin-top: var(--sp-3); display: grid; gap: var(--sp-2); max-width: 420px; }

/* Admin: where new postings come from. Five columns; stacks on a phone. */
.yield-tr { grid-template-columns: minmax(0, 2fr) repeat(4, minmax(0, 1fr)); }
@media (max-width: 720px) {
  .yield-tr { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-2); }
  .yield-tr.data-thead { display: none; }
}
