/*
 * Design tokens transcribed verbatim from DESIGN.md frontmatter
 * (ux-JOB-2026-07-10). Single source of truth — do not hardcode
 * hex values or raw px sizes in templates, reference these instead.
 */
:root {
  /* Colors */
  --color-bg: #FAFBF9;
  --color-card: #FFFFFF;
  --color-hairline: #E8ECE7;
  --color-ink: #171D19;
  --color-ink-2: #5C675F;
  /* Story 1.9 v5 design-refresh boost: corrected from #8A948C, which fails
     WCAG-AA on --color-card/--color-bg (tertiary-text contrast). #6A736C
     is the exact value the approved v5 mockup research pass verified
     (~4.6:1 on white). This is the one deliberate value SWAP the boost
     makes (Task 8) -- every other addition below is purely additive. */
  --color-ink-3: #6A736C;
  --color-accent: #0B6B45;
  --color-accent-deep: #095638;
  --color-accent-tint: #E7F3EC;
  --color-accent-tint-2: #F1F7F3;
  --color-on-accent: #FFFFFF;
  --color-on-accent-soft: rgba(255, 255, 255, .72);
  --color-star-off: #D7DED8;
  --color-bezel: #1B1F1C;
  /* v6 overhaul: the page canvas OUTSIDE the app column on wide screens --
     one step deeper than --color-bg so the device column reads as the
     elevated app surface. Invisible on mobile (column spans the viewport). */
  --color-canvas: #E9EEE7;
  --shell-w: 480px;
  --color-status-pending-bg: #FBF3E3;
  --color-status-pending-ink: #8A5A0B;
  --color-status-pending-dot: #C9862A;
  /* Ad-hoc story (2026-07-14): the one status tone this system didn't have
     yet -- everything before this was "pending/amber" or "accent/green",
     nothing for "over the line" (the /admin/costs dashboard's at-or-over-cap
     state). Same bg/ink/dot trio shape as --color-status-pending-* above,
     not a new system. */
  --color-status-danger-bg: #FBE7E4;
  --color-status-danger-ink: #96281B;
  --color-status-danger-dot: #C0392B;

  /* --- Story 1.9 v5 design-refresh boost (additive) -----------------------
     Transcribed verbatim from the approved
     ux-designs/.working/v5-premium-redesign.html research pass. */
  --hairline-strong: #D7DED7;
  --accent-press: #074A2F;
  --star: #E0A310;
  --ease: cubic-bezier(.2, .7, .2, 1);
  /* Elevation trio -- DESIGN.md's existing "one elevated element per
     screen" discipline still governs where these are applied: --lift on
     the single active answer panel, --inset-hi on accent-filled
     interactive surfaces (active chips, primary CTA, primary-work-type
     chip, selected CEFR segment). Never on feed rows/info cards/chat
     bubbles/tab bars. */
  --lift: 0 1px 2px rgba(16, 40, 28, .05), 0 10px 26px -12px rgba(16, 40, 28, .16);
  --lift-accent: 0 1px 2px rgba(9, 86, 56, .14), 0 14px 30px -10px rgba(9, 86, 56, .34);
  --inset-hi: inset 0 1px 0 rgba(255, 255, 255, .16);

  /* Typography */
  --font-family-system: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", system-ui, sans-serif;

  --type-display-size: 32px;
  --type-display-weight: 700;
  --type-display-tracking: -0.03em;
  --type-display-leading: 1.05;

  --type-title-size: 23px;
  --type-title-weight: 700;
  --type-title-tracking: -0.02em;
  --type-title-leading: 1.18;

  --type-heading-size: 19px;
  --type-heading-weight: 700;
  --type-heading-tracking: -0.02em;
  --type-heading-leading: 1.25;

  --type-body-size: 14px;
  --type-body-weight: 600;
  --type-body-tracking: -0.01em;
  --type-body-leading: 1.3;

  --type-meta-size: 12.5px;
  --type-meta-weight: 500;
  --type-meta-leading: 1.4;

  --type-label-size: 11px;
  --type-label-weight: 700;
  --type-label-tracking: .02em;

  --type-wordmark-size: 12px;
  --type-wordmark-weight: 800;
  --type-wordmark-tracking: .14em;

  /* Spacing scale */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-7: 32px;
  --space-gutter: 16px;
  --space-card-pad: 14px;

  /* Radius scale */
  --radius-sm: 12px;
  --radius-md: 18px;
  --radius-lg: 20px;
  --radius-xl: 24px;
  --radius-full: 9999px;
  /* The one deliberate deviation from the radius scale (DESIGN.md
     "Shapes"): chat bubbles get an asymmetric ~5px tail corner on the
     sender-direction side, signalling AI vs. user without drawing a literal
     tail. */
  --radius-bubble-tail: 5px;

  /* Component sizes (DESIGN.md "Components" -- chip, chat bubble). First
     transcribed here for Story 1.3's onboarding chat, the first
     content-heavy screen built from these tokens. */
  --size-chip-height: 34px;
  --size-avatar-sm: 28px;
  --size-icon-btn: 34px;
  --size-inputbar-height: 46px;
}

/*
 * Story 1.9 -- v5 design-refresh boost, shared component layer (Task 8).
 * Consolidates the 3 near-identical copy-pasted .chip/button/profile-row
 * CSS blocks that previously lived separately in onboarding.html,
 * profile.html and admin_user_profile.html into ONE definition here --
 * each of those templates' own <style> block now only carries page-layout
 * rules it doesn't share with the others. Boost, not a token swap: base
 * shapes/spacing/type from DESIGN.md are unchanged, this section adds
 * elevation (--lift/--inset-hi), tactile :hover/:active/transition states
 * (previously zero anywhere in the app), and the new field-level component
 * classes Story 1.9's v2 fields need (skills open-field picker, CEFR
 * language rows, wage range, work-type mini-chips).
 *
 * Gradients: DESIGN.md's "no gradients" rule and the approved v5 mockup are
 * in direct tension -- resolved by adding exactly the 3 v5-approved uses as
 * named, scoped exceptions (documented on each rule below). No gradient is
 * introduced anywhere beyond these 3.
 */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation: none !important;
    transition: none !important;
  }
}

/* Gradient exception #1/3: page-canvas radial wash. v6 overhaul: painted on
   the .app-shell device column (base.html) instead of <body>, so on wide
   screens the column reads as the app surface against --color-canvas;
   visually identical on mobile, where the column spans the whole viewport. */
body {
  background: var(--color-canvas);
}
.app-shell {
  background: radial-gradient(130% 85% at 50% -12%, #F1F5F0 0%, var(--color-bg) 52%) no-repeat,
    var(--color-bg);
}

/* --- Avatar --------------------------------------------------------------- */
.ai-avatar {
  width: var(--size-avatar-sm);
  height: var(--size-avatar-sm);
  border-radius: var(--radius-full);
  /* Gradient exception #2/3: avatar fill. */
  background: linear-gradient(160deg, var(--color-accent), var(--color-accent-deep));
  color: var(--color-on-accent);
  font-size: var(--type-label-size);
  font-weight: 800;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  box-shadow: var(--lift-accent), var(--inset-hi);
}

/* --- Onboarding progress bar --------------------------------------------------- */
.progress-track {
  height: 4px;
  border-radius: var(--radius-full);
  background: var(--color-hairline);
  margin-top: var(--space-3);
  overflow: hidden;
}
.progress-fill {
  height: 100%;
  border-radius: var(--radius-full);
  /* Gradient exception #3/3: progress-bar fill. */
  background: linear-gradient(90deg, var(--color-accent), var(--color-accent-deep));
}

/* --- Chips ------------------------------------------------------------------ */
.reply-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.chip {
  height: var(--size-chip-height);
  padding: 0 var(--space-4);
  border-radius: var(--radius-full);
  font-size: var(--type-meta-size);
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  white-space: nowrap;
  background: var(--color-card);
  border: 1px solid var(--hairline-strong);
  color: var(--color-ink-2);
  cursor: pointer;
  font-family: inherit;
  transition: transform .12s var(--ease), background .18s var(--ease), border-color .18s var(--ease), color .18s var(--ease);
}
.chip:active {
  transform: scale(.96);
}
button.chip {
  font-family: inherit;
}
.chip.active {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-on-accent);
  box-shadow: var(--inset-hi);
}
.chip-toggle {
  display: inline-flex;
  /* Ad-hoc story (2026-07-15): the sole positioning context the
     ranked-chip rank badge (.chip-rank-badge below) anchors to -- a bare
     `position: relative` with no top/left/etc. of its own never shifts
     .chip-toggle-input's existing (offset-less) absolute positioning
     either, so this is a safe, non-breaking addition shared by every
     .chip-toggle instance (skills/languages/CEFR/common-trait chips too),
     not just ranked ones. */
  position: relative;
}
.chip-toggle-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.chip-toggle-input:checked + .chip,
.chip-toggle-input:focus-visible + .chip {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-on-accent);
  box-shadow: var(--inset-hi);
}
.chip-toggle-input:focus-visible + .chip {
  outline: 2px solid var(--color-accent-deep);
  outline-offset: 2px;
}

/* --- Ranked chip picker (tap-in-priority-order work_type/contract_type/
   benefits, ad-hoc story 2026-07-15) ------------------------------------
   The rank badge is a small circular corner-overlay on its chip -- same
   accent fill + radius-full pill shape every other "filled" accent surface
   in this system already uses (.chip.active, the now-deleted .mc.primary
   work-type-star chip before it), not a new color. Visibility is driven
   purely by the `hidden` attribute
   (base.html's delegated listener toggles it), NOT a class -- same
   `[hidden]`-is-the-single-source-of-truth discipline admin_accounts.html
   already established (see that file's own comment for the display:flex-
   fighting-[hidden] bug this avoids): the base rule sets display:none, and
   only the :not([hidden]) variant turns it on, so there is no origin/
   specificity race between an author rule and the `[hidden]` UA style. */
.chip-rank-badge {
  display: none;
  position: absolute;
  top: -6px;
  right: -6px;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  border-radius: var(--radius-full);
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-size: 10px;
  font-weight: 800;
  line-height: 18px;
  text-align: center;
  box-shadow: var(--inset-hi);
  pointer-events: none;
}
.chip-rank-badge:not([hidden]) {
  display: block;
}

/* --- Buttons / CTA ----------------------------------------------------------- */
.btn-confirm-selection {
  height: var(--size-chip-height);
  padding: 0 var(--space-5);
  border: none;
  border-radius: var(--radius-full);
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-size: var(--type-meta-size);
  font-weight: 700;
  cursor: pointer;
  box-shadow: var(--lift-accent), var(--inset-hi);
  transition: transform .12s var(--ease), background .18s var(--ease);
}
.btn-confirm-selection:hover {
  background: var(--color-accent-deep);
}
.btn-confirm-selection:active {
  transform: scale(.985);
  background: var(--accent-press);
}
.multi-submit-wrap {
  margin-top: var(--space-3);
}
.guardrail-note {
  font-size: var(--type-label-size);
  color: var(--color-ink-3);
  line-height: 1.4;
}

/* --- Input bar (text steps / add-trait / add-skill) -------------------------- */
.inputbar-wrap {
  padding-top: var(--space-3);
}
.inputbar {
  height: var(--size-inputbar-height);
  border-radius: var(--radius-full);
  background: var(--color-card);
  border: 1px solid var(--color-hairline);
  display: flex;
  align-items: center;
  padding: 0 var(--space-2) 0 var(--space-4);
  gap: var(--space-2);
}
.inputbar .text-input,
.text-input {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  font-size: var(--type-body-size);
  font-family: var(--font-family-system);
  color: var(--color-ink);
}
.inputbar .icon-btn {
  width: var(--size-icon-btn);
  height: var(--size-icon-btn);
  border: none;
  border-radius: var(--radius-full);
  background: var(--color-accent-tint);
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  cursor: pointer;
  transition: background .18s var(--ease), transform .12s var(--ease);
}
.inputbar .icon-btn:active {
  transform: scale(.94);
}
.inputbar .icon-btn svg {
  width: 15px;
  height: 15px;
  stroke: var(--color-accent-deep);
  fill: none;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --- Bubbles (onboarding chat) ------------------------------------------------ */
.msg-row {
  display: flex;
  gap: var(--space-2);
  align-items: flex-end;
}
.msg-row.user {
  justify-content: flex-end;
}
.bubble {
  max-width: 82%;
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-card-pad);
  font-size: var(--type-body-size);
  line-height: var(--type-body-leading);
}
.bubble.ai {
  background: var(--color-card);
  border: 1px solid var(--color-hairline);
  color: var(--color-ink);
  border-bottom-left-radius: var(--radius-bubble-tail);
}
.bubble.user {
  background: var(--color-accent);
  color: var(--color-on-accent);
  border-bottom-right-radius: var(--radius-bubble-tail);
  font-weight: 500;
}
.bubble.confirm {
  background: var(--color-accent-tint);
  border: 1px solid var(--color-accent-tint);
  color: var(--color-accent-deep);
  font-weight: 700;
  font-size: var(--type-meta-size);
  border-bottom-left-radius: var(--radius-bubble-tail);
}
.bubble.user.file {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: 600;
}
.file-icon {
  width: 22px;
  height: 22px;
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, .2);
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
}
.file-icon svg {
  width: 12px;
  height: 12px;
  stroke: var(--color-on-accent);
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.cv-upload-form,
.cert-add-form {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-2);
}
.cv-upload-form .btn-confirm-selection,
.cert-add-form .cert-add-btn {
  flex: none;
}
.cv-file-input,
.cert-file-input {
  flex: 1;
  min-width: 0;
  font-size: var(--type-meta-size);
  color: var(--color-ink-2);
}

/* --- Profile rows (self-edit + admin-edit) ------------------------------------ */
.profile-row {
  background: var(--color-card);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-lg);
  padding: var(--space-card-pad);
}
.profile-row-label {
  font-size: var(--type-label-size);
  font-weight: var(--type-label-weight);
  letter-spacing: var(--type-label-tracking);
  color: var(--color-ink-3);
  text-transform: uppercase;
  margin-bottom: var(--space-1);
}
.profile-row-value {
  font-size: var(--type-body-size);
  font-weight: var(--type-body-weight);
  color: var(--color-ink);
  line-height: var(--type-body-leading);
}

/* --- Settings group + horizontal row (redesign 2026-07-15) -----------------
   The simple editable fields on /profile are grouped into ONE surface with
   hairline dividers, not a stack of separate bordered cards (design-taste-
   frontend §4.4). `.profile-row-main` is the display-mode layout: title +
   value stacked left, Edit affordance inline right. All scoped so the
   richer standalone `.profile-row` blocks (CV/traits/improve/duo/
   notifications) and the admin cross-user view keep their existing card
   treatment untouched. */
.settings-group {
  background: var(--color-card);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.settings-group .profile-row {
  background: transparent;
  border: none;
  border-radius: 0;
  border-top: 1px solid var(--color-hairline);
  padding: var(--space-3) var(--space-card-pad);
}
.settings-group .profile-row:first-child {
  border-top: none;
}
.settings-group .profile-row-label {
  text-transform: none;
  letter-spacing: -0.01em;
  font-size: var(--type-body-size);
  font-weight: 650;
  color: var(--color-ink);
  margin-bottom: 1px;
}
.settings-group .profile-row-value {
  font-size: var(--type-meta-size);
  font-weight: var(--type-meta-weight);
  color: var(--color-ink-2);
  line-height: 1.35;
}
/* Edit mode: the field title sits DIRECTLY above its chip/range/skill control
   (in display mode the title is nested inside `.profile-row-main` instead), so
   the tight 1px margin-bottom above reads as cramped/overlapping against the
   options -- Teo, 2026-07-15. The `> ` direct-child selector matches ONLY the
   edit layout; display-mode titles are nested deeper and keep their tight
   spacing. Applies to every settings-group edit field (work type, skills,
   contract type, wage, benefits, ...). */
.settings-group .profile-row > .profile-row-label {
  margin-bottom: var(--space-3);
}
/* Rich profile cards (CV / confirmed traits / notifications / improve / duo)
   sit OUTSIDE `.settings-group`, so without this they fall back to the base
   UPPERCASE `.profile-row-label`. Give them the same sentence-case title
   treatment the settings group uses, so the whole /profile page reads as one
   system rather than "clean fields up top, old uppercase cards below"
   (Teo, 2026-07-15). Scoped by the `.pcard` / `.imp-card` classes, so the
   admin cross-user read-only cards (plain `.profile-row-label`) stay as-is. */
.pcard .profile-row-label,
.imp-card .profile-row-label {
  text-transform: none;
  letter-spacing: -0.01em;
  font-size: var(--type-body-size);
  font-weight: 650;
  color: var(--color-ink);
  margin-bottom: var(--space-1);
}
/* Notifications card: description + CTA were stacked with no air between the
   sentence and the button. `.imp-sub` gives the description the same relaxed
   meta treatment Improve/DUO use; the CTA gets a clear gap above it. */
#field-row-push .imp-sub {
  margin-top: var(--space-1);
}
#field-row-push .btn-confirm-selection {
  margin-top: var(--space-4);
}
/* Confirmed-traits card: the "Tap any that describe you" caption sat right on
   top of the checklist chips. Give the checklist its own breathing room below
   the caption. */
.pcard .profile-row-caption + .trait-more-wrap,
.pcard .profile-row-caption + form,
.pcard .profile-row-caption + .trait-checklist {
  margin-top: var(--space-3);
}
.profile-row-main {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}
.profile-row-text {
  min-width: 0;
}
.profile-row-main .profile-row-edit-btn {
  margin-top: 0;
  flex: none;
}
.profile-row-error {
  font-size: var(--type-meta-size);
  font-weight: var(--type-meta-weight);
  color: var(--color-status-pending-ink);
  margin-bottom: var(--space-2);
}
/* Post-review fix (2026-07-14, design audit): a small labeled divider for
   introducing a distinct sub-section within a `.profile-row` card (first
   use: traits_row's checklist, separating it from the empty-state/
   confirmed-chips content above). Hairline-topped like `.lang-add-form`'s
   existing divider precedent, not a new visual language. */
.profile-row-caption {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px dashed var(--color-hairline);
  font-size: var(--type-meta-size);
  font-weight: 600;
  color: var(--color-ink-3);
}
.profile-row-confirm {
  font-size: var(--type-meta-size);
  font-weight: 700;
  color: var(--color-accent-deep);
  margin-bottom: var(--space-2);
}
.profile-row-edit-btn,
.profile-row-cancel-btn,
.lang-remove-btn,
.cert-remove-btn {
  margin-top: var(--space-3);
  height: 32px;
  padding: 0 var(--space-4);
  border-radius: var(--radius-full);
  border: 1px solid var(--hairline-strong);
  background: var(--color-card);
  color: var(--color-ink-2);
  font-size: var(--type-meta-size);
  font-weight: 700;
  cursor: pointer;
  transition: transform .12s var(--ease), background .18s var(--ease);
}
.profile-row-edit-btn:active,
.profile-row-cancel-btn:active,
.lang-remove-btn:active,
.cert-remove-btn:active {
  transform: scale(.96);
}
.trait-remove-form,
.lang-remove-form,
.cert-remove-form {
  display: inline-flex;
  margin: 0;
}

/* --- Personality trait checklist (Story 1.10, AD-7) --------------------------
   Additive component layer, same pattern as the Skills open-field picker
   below -- ports the approved v5 mockup's `.cl`/`.clgroup` categorized-
   checklist rows (ux-designs/.working/v5-premium-redesign.html) onto this
   app's actual token names. No design-token values themselves change. */
.trait-more {
  margin-top: 0;
}
.trait-more summary {
  cursor: pointer;
  list-style: none;
  font-size: var(--type-meta-size);
  font-weight: 700;
  color: var(--color-accent-deep);
  border: 1px solid var(--hairline-strong);
  background: var(--color-card);
  border-radius: var(--radius-full);
  padding: var(--space-2) var(--space-4);
  display: inline-block;
}
.trait-more summary::-webkit-details-marker {
  display: none;
}
.trait-more[open] summary {
  margin-bottom: var(--space-3);
}
.trait-group + .trait-group {
  margin-top: var(--space-3);
}
.trait-group-title {
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--color-ink-3);
  margin: 0 2px var(--space-2);
}
.trait-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-md);
  background: var(--color-card);
  cursor: pointer;
  font-family: inherit;
  text-align: left;
  transition: transform .12s var(--ease), background .18s var(--ease), border-color .18s var(--ease);
}
.trait-row + .trait-row {
  margin-top: var(--space-2);
}
.trait-row:active {
  transform: scale(.99);
}
.trait-row-box {
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: 7px;
  border: 1.5px solid var(--hairline-strong);
  display: flex;
  align-items: center;
  justify-content: center;
}
.trait-row.active {
  border-color: var(--color-accent);
  background: var(--color-accent-tint-2);
}
.trait-row.active .trait-row-box {
  background: var(--color-accent);
  border-color: var(--color-accent);
  box-shadow: var(--inset-hi);
}
/* Post-review fix (2026-07-15): a solid-fill square read as "just colored
   in" rather than a checked checkbox -- an explicit checkmark glyph inside
   makes the checked state legible at a glance, same convention a native
   checkbox already uses. */
.trait-row.active .trait-row-box::after {
  content: "✓";
  color: var(--color-card);
  font-size: 13px;
  font-weight: 800;
  line-height: 1;
}
.trait-row-text {
  font-size: var(--type-body-size);
  font-weight: 550;
  letter-spacing: -.008em;
  color: var(--color-ink);
}

/* --- Trait "mark all" + search (ad-hoc story, 2026-07-14) -------------------- */
.trait-more-wrap {
  margin-top: var(--space-3);
}
.trait-more-controls {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}
.trait-search-input {
  flex: 1 1 auto;
  min-width: 0;
  height: 38px;
  padding: 0 var(--space-3);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-full);
  background: var(--color-card);
  font-family: inherit;
  font-size: var(--type-body-size);
  color: var(--color-ink);
}
.trait-mark-all-btn {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  height: 38px;
  padding: 0 var(--space-4);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-full);
  background: var(--color-card);
  color: var(--color-accent-deep);
  font-family: inherit;
  font-size: var(--type-meta-size);
  font-weight: 700;
  cursor: pointer;
}
.trait-mark-all-btn:active {
  transform: scale(.97);
}
.trait-mark-all-btn.is-pending {
  opacity: .6;
  cursor: default;
}
/* Post-review fix (2026-07-15): "Mark all" reads as a checkbox toggle now
   (Teo's explicit follow-up), not a one-way action pill -- a small box that
   fills + shows a checkmark once EVERY trait in the checklist is already
   confirmed. Driven purely by CSS `:has()` off the real chip/row `.active`
   classes (same "derive from real DOM state, don't track a separate JS
   flag" discipline `.filepick`'s has-file state already established) --
   correct on first render too, not just after a click. */
.trait-mark-all-box {
  flex: none;
  width: 18px;
  height: 18px;
  border-radius: 5px;
  border: 1.5px solid var(--hairline-strong);
  display: flex;
  align-items: center;
  justify-content: center;
}
.trait-checklist:not(:has(.chip:not(.active))):not(:has(.trait-row:not(.active))) .trait-mark-all-box {
  background: var(--color-accent);
  border-color: var(--color-accent);
}
.trait-checklist:not(:has(.chip:not(.active))):not(:has(.trait-row:not(.active))) .trait-mark-all-box::after {
  content: "✓";
  color: var(--color-card);
  font-size: 12px;
  font-weight: 800;
  line-height: 1;
}
.trait-mark-all-caption {
  font-size: var(--type-meta-size);
  color: var(--color-ink-3);
  margin-bottom: var(--space-3);
}

/* --- Onboarding history "Edit" affordance (ad-hoc story, 2026-07-14) -------- */
.history-edit-row {
  justify-content: flex-end;
}
.history-edit-btn {
  margin-left: calc(var(--size-avatar-sm) + var(--space-2));
  border: none;
  background: none;
  color: var(--color-ink-3);
  font-family: inherit;
  font-size: var(--type-meta-size);
  font-weight: 700;
  text-decoration: underline;
  cursor: pointer;
  padding: 0;
}

/* --- "DUO-friendly" badge/disclosure (ad-hoc story, 2026-07-14) ------------- */
.duo-badge {
  display: inline-block;
  font-size: var(--type-meta-size);
  font-weight: 700;
  color: var(--color-accent-deep);
  background: var(--color-accent-tint);
  padding: 3px var(--space-3);
  border-radius: var(--radius-full);
}
.duo-badge-row {
  margin-top: 2px;
}
/* Post-review fix (2026-07-15, DUO badge overlap): on the Job Brief the DUO
   badge is a standalone sibling directly after `#brief-live-region`'s
   verified pill -- give it its own line + clear top gap so the two pills
   never touch. Scoped to `.brief-title-block` so the hero/feed-row DUO
   badges (which have their own layout) are untouched. */
.brief-title-block .duo-badge {
  display: flex;
  width: fit-content;
  margin-top: var(--space-3);
}
/* Feed hero DUO badge: was an inline-block with no vertical margin, so it
   collided with the star/"Strong match" row above and the "Open brief" CTA
   below (Teo, 2026-07-15). Own line + clear space top and bottom, same fix
   shape as the Job Brief badge above. Scoped to `.hero` so the regular
   feed-row badge (`.duo-badge-row`) keeps its own tight layout. */
.hero .duo-badge {
  display: flex;
  width: fit-content;
  margin-top: var(--space-3);
  margin-bottom: 2px;
}
/* The shared "What does DUO-friendly mean?" disclosure sat flush against the
   filter chips above and the hero card below. Give it its own vertical rhythm
   and a tappable-height summary. */
.duo-explain {
  margin: var(--space-2) 0 var(--space-4);
}
.duo-explain summary {
  cursor: pointer;
  font-size: var(--type-meta-size);
  color: var(--color-ink-3);
  padding: var(--space-1) 0;
}
.duo-explain p {
  font-size: var(--type-meta-size);
  color: var(--color-ink-2);
  line-height: 1.5;
  margin-top: var(--space-1);
}

/* --- Skills open-field picker (AD-17) ---------------------------------------- */
.skill-more {
  margin-top: var(--space-2);
}
.skill-more summary {
  cursor: pointer;
  list-style: none;
  font-size: var(--type-meta-size);
  font-weight: 700;
  color: var(--color-accent-deep);
  border: 1px solid var(--hairline-strong);
  background: var(--color-card);
  border-radius: var(--radius-full);
  padding: var(--space-2) var(--space-4);
  display: inline-block;
}
.skill-more summary::-webkit-details-marker {
  display: none;
}
.skill-more[open] summary {
  margin-bottom: var(--space-2);
}
/* Post-review fix (2026-07-15): same visual language as .trait-search-input
   (pill input, card background). */
.skill-search-input {
  display: block;
  width: 100%;
  height: 38px;
  margin-bottom: var(--space-2);
  padding: 0 var(--space-3);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-full);
  background: var(--color-card);
  font-family: inherit;
  font-size: var(--type-body-size);
  color: var(--color-ink);
}
.addnew {
  margin-top: var(--space-2);
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.addnew .text-input {
  flex: 1 1 auto;
  min-width: 0;
  width: 100%;
  height: var(--size-inputbar-height);
  border-radius: var(--radius-full);
  background: var(--color-card);
  border: 1px dashed var(--color-accent);
  padding: 0 var(--space-4);
}
/* Post-review fix (2026-07-15): same visual language as .trait-mark-all-btn
   (outline pill, accent-deep text) -- a distinct action from the primary
   "Confirm selection" CTA below, so it stays a lighter-weight pill rather
   than a second filled button. */
.skill-add-btn {
  flex: none;
  height: var(--size-inputbar-height);
  padding: 0 var(--space-4);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-full);
  background: var(--color-card);
  color: var(--color-accent-deep);
  font-family: inherit;
  font-size: var(--type-meta-size);
  font-weight: 700;
  cursor: pointer;
}
.skill-add-btn:active {
  transform: scale(.97);
}
/* Post-review fix (2026-07-15): the "your selected skills" strip -- starts
   empty (no default/"common" chips pre-shown, Teo's explicit call) and
   collapses to zero height until something is ticked, so it never reads as
   an empty box sitting above the "View list" disclosure. `:not(:has(...))`
   rather than `:empty` -- Jinja's `{% for %}...{% endfor %}` leaves
   whitespace text nodes when the loop yields nothing (trim_blocks isn't
   configured), which would keep a plain `:empty` from ever matching. */
.skill-selected-chips:not(:has(.chip-toggle)) {
  display: none;
}
.learn {
  margin-top: var(--space-1);
  font-size: var(--type-label-size);
  line-height: 1.42;
  color: var(--color-ink-2);
}

/* --- Languages + CEFR + certificate (AD-9 extended) --------------------------- */
.lang {
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  background: var(--color-card);
  margin-top: var(--space-2);
}
.lang-h {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-2);
}
.lang-name {
  font-size: var(--type-body-size);
  font-weight: 700;
  letter-spacing: -.01em;
}
.lang-lvl {
  font-size: var(--type-label-size);
  font-weight: 700;
  color: var(--color-accent-deep);
  background: var(--color-accent-tint);
  padding: 4px var(--space-3);
  border-radius: var(--radius-full);
}
.cefr {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  background: var(--color-bg);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-full);
  padding: 3px;
}
.cert-file {
  margin-top: var(--space-2);
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--type-meta-size);
  font-weight: 650;
  color: var(--color-ink);
  background: var(--color-accent-tint-2);
  border: 1px solid var(--color-accent-tint);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
}
.cert-file .nm {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cert-file .ok {
  margin-left: auto;
  flex: none;
  font-size: var(--type-label-size);
  font-weight: 750;
  color: var(--color-accent-deep);
}
.cert-add-btn {
  height: 32px;
  padding: 0 var(--space-3);
  border: 1px dashed var(--hairline-strong);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-ink-2);
  font-size: var(--type-label-size);
  font-weight: 700;
  cursor: pointer;
}
.lang-add-form {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px dashed var(--color-hairline);
}
.lang-custom-input {
  display: none;
  width: 100%;
  margin-top: var(--space-2);
  height: 40px;
  border-radius: var(--radius-md);
  background: var(--color-bg);
  border: 1px solid var(--color-hairline);
  padding: 0 var(--space-3);
}
/* Post-review fix (2026-07-15): only relevant once "Other language" is
   picked -- was previously always visible/always-editable regardless of
   which language chip was selected. */
.lang-add-form:has(.lang-other-radio:checked) .lang-custom-input {
  display: block;
}
.lang-add-form .cefr {
  margin-top: var(--space-2);
}
.lang-add-form .btn-confirm-selection {
  margin-top: var(--space-3);
}

/* --- Wage range ---------------------------------------------------------------- */
.range {
  display: flex;
  align-items: stretch;
  gap: var(--space-2);
}
.range .r {
  flex: 1;
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  background: var(--color-card);
}
.range .rl {
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--color-ink-3);
}
.range .range-num {
  margin-top: var(--space-1);
  width: 100%;
  border: none;
  outline: none;
  background: transparent;
  font-size: 22px;
  font-weight: 800;
  letter-spacing: -.02em;
  color: var(--color-ink);
  font-variant-numeric: tabular-nums;
  font-family: var(--font-family-system);
}
.range .dash {
  align-self: center;
  color: var(--color-ink-3);
  font-weight: 700;
}
.unit-r {
  margin-top: var(--space-2);
  text-align: right;
  font-size: var(--type-meta-size);
  font-weight: 700;
  color: var(--color-ink-3);
}
.range-form .btn-confirm-selection {
  margin-top: var(--space-3);
  width: 100%;
}

/* ============================================================================
   Story 2.10: Job Feed shared component layer (UX-DR2/3/9/11), transcribed
   from the approved job-feed.html mockup. Two genuinely new size/radius
   one-offs below (--size-avatar-lg/--size-mono/--radius-mono) -- same "named
   one-off, not a silent hardcode" discipline --radius-bubble-tail already
   established; the mockup's own --r-card:24px needed no new token, it is
   simply this app's existing --radius-xl, reused directly in .hero below.
   ============================================================================ */
:root {
  --size-avatar-lg: 42px;
  --size-mono: 44px;
  --radius-mono: 14px;
}

/* --- Feed header (wordmark + title + avatar) -------------------------------- */
.feed-header {
  flex: none;
  padding-bottom: var(--space-2);
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
}
.feed-wordmark {
  font-size: var(--type-wordmark-size);
  font-weight: var(--type-wordmark-weight);
  letter-spacing: var(--type-wordmark-tracking);
  color: var(--color-accent);
  margin-bottom: 2px;
}
.feed-title {
  font-size: var(--type-display-size);
  font-weight: var(--type-display-weight);
  letter-spacing: var(--type-display-tracking);
  line-height: var(--type-display-leading);
  color: var(--color-ink);
  margin: 0;
}
.avatar-lg {
  width: var(--size-avatar-lg);
  height: var(--size-avatar-lg);
  border-radius: var(--radius-full);
  background: var(--color-accent-tint);
  border: 1px solid #D6E9DE;
  color: var(--color-accent-deep);
  font-size: var(--type-body-size);
  font-weight: 700;
  letter-spacing: .02em;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
}

/* --- Filter chips row (Task 4, UX-DR9) -- reuses the SAME .chip component
   the onboarding quick-replies already established (Story 1.3/1.9), just
   as plain links instead of hx-post buttons (server-side query-param
   filtering, this story's chosen mechanism -- see feed.py). ------------- */
.feed-chips {
  flex: none;
  display: flex;
  gap: var(--space-2);
  padding: var(--space-1) 0 var(--space-4);
  overflow-x: auto;
  /* Post-review fix (2026-07-14, design audit): the chip row's own
     right edge was clipping mid-chip with no visual hint that it's
     scrollable, reading as a layout bug rather than an intentional
     horizontal-scroll affordance -- a soft fade signals "more chips this
     way" without needing a visible scrollbar. `padding-bottom` above
     already reserves space so the fade never covers the row's own
     bottom margin. */
  mask-image: linear-gradient(to right, black calc(100% - 28px), transparent 100%);
  -webkit-mask-image: linear-gradient(to right, black calc(100% - 28px), transparent 100%);
}
a.chip {
  text-decoration: none;
}

/* --- Alarm hero card (Task 3, UX-DR2) --------------------------------------- */
.hero {
  background: var(--color-accent);
  border-radius: var(--radius-xl);
  padding: var(--space-4);
  box-shadow: var(--lift-accent);
  color: var(--color-on-accent);
  flex: none;
  margin-bottom: var(--space-4);
}
.hero-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-3);
}
.alarm-pill {
  display: flex;
  align-items: center;
  gap: 7px;
  height: 26px;
  padding: 0 11px;
  border-radius: var(--radius-full);
  background: rgba(255, 255, 255, .14);
  border: 1px solid rgba(255, 255, 255, .22);
  color: var(--color-on-accent);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .10em;
}
.pulse {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--color-on-accent);
  position: relative;
  flex: none;
}
.pulse::after {
  content: "";
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  border: 1.5px solid var(--color-on-accent);
  opacity: 0;
}
/* AC #5 / UX-DR13: the ring animation is skipped ENTIRELY (not merely
   slowed) under prefers-reduced-motion -- wrapped in its own
   no-preference query (in addition to the blanket `reduce`-scoped
   !important override already at the top of this file), matching the
   approved mockup's own approach exactly. The pill/label stay visible
   without motion either way -- nothing here is hidden by the media query,
   only the animation declaration. */
@media (prefers-reduced-motion: no-preference) {
  .pulse::after {
    animation: feed-pulse-ring 1.8s ease-out infinite;
  }
}
@keyframes feed-pulse-ring {
  0% { transform: scale(.4); opacity: .7; }
  70% { transform: scale(1.15); opacity: 0; }
  100% { transform: scale(1.15); opacity: 0; }
}
.hero-time {
  font-size: var(--type-meta-size);
  font-weight: 600;
  color: var(--color-on-accent-soft);
  font-variant-numeric: tabular-nums;
}
.hero h2 {
  font-size: var(--type-heading-size);
  font-weight: var(--type-heading-weight);
  letter-spacing: var(--type-heading-tracking);
  line-height: var(--type-heading-leading);
  color: var(--color-on-accent);
  margin: 0;
}
.hero .employer {
  font-size: var(--type-body-size);
  color: var(--color-on-accent-soft);
  margin-top: 2px;
}
/* Post-review fix (2026-07-14, design audit): salary was not shown
   anywhere in the UI. `--color-on-accent` (not the softer `-soft` variant
   used for employer/location) since pay is a decision-driving fact on the
   hero's own "best match" card, not secondary metadata. */
.hero-salary {
  font-size: var(--type-body-size);
  font-weight: 700;
  color: var(--color-on-accent);
  margin-top: 3px;
}
.hero-stars {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-top: var(--space-3);
}
.hero-stars svg {
  width: 15px;
  height: 15px;
  fill: var(--color-on-accent);
}
.hero-stars .match {
  margin-left: 7px;
  font-size: var(--type-meta-size);
  font-weight: 700;
  color: var(--color-on-accent-soft);
}
.hero-cta-form {
  margin: var(--space-3) 0 0;
}
.hero .cta {
  width: 100%;
  height: 48px;
  border: none;
  border-radius: var(--radius-full);
  background: #FFFFFF;
  color: var(--color-accent-deep);
  font-size: var(--type-body-size);
  font-weight: 700;
  letter-spacing: -.01em;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  cursor: pointer;
  font-family: inherit;
  transition: transform .12s var(--ease);
}
.hero .cta:active {
  transform: scale(.985);
}
.hero .cta svg {
  width: 16px;
  height: 16px;
  stroke: var(--color-accent-deep);
  fill: none;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --- Section label + count badge -------------------------------------------- */
.feed-section {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-4) 2px var(--space-2);
}
.feed-section h3 {
  font-size: var(--type-body-size);
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--color-ink);
  margin: 0;
}
.badge {
  min-width: 22px;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--radius-full);
  background: var(--color-accent-tint);
  color: var(--color-accent-deep);
  font-size: var(--type-meta-size);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* --- Feed cards list (Task 2, UX-DR3) --------------------------------------- */
.feed-list {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.job {
  background: var(--color-card);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-lg);
  padding: var(--space-3) var(--space-card-pad);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  position: relative;
}
/* Task 3's "demoted-but-still-unread-marked" detail (AC #2): a small
   accent dot, purely decorative/supplementary to the row's own visible
   text -- no extra aria treatment needed beyond the row's natural reading
   order (see _feed_macros.html). */
.job.unread::before {
  content: "";
  position: absolute;
  top: var(--space-3);
  right: var(--space-3);
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-accent);
}
.mono {
  width: var(--size-mono);
  height: var(--size-mono);
  border-radius: var(--radius-mono);
  background: var(--color-accent-tint);
  border: 1px solid #DAEBE1;
  color: var(--color-accent-deep);
  font-size: var(--type-body-size);
  font-weight: 700;
  letter-spacing: .01em;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
}
.job-main {
  flex: 1;
  min-width: 0;
}
.job-main .role {
  font-size: var(--type-body-size);
  font-weight: 600;
  letter-spacing: -.01em;
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--color-ink);
}
.job-main .firm {
  font-size: var(--type-meta-size);
  color: var(--color-ink-2);
  margin-top: 1px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* 2026-08-01 (Teo's effectiveness audit): the "N openings" suffix on a row
   that stands for several near-identical listings at the same employer and
   place (job/web/routes/feed.py's collapse_similar_listings). Deliberately
   quieter than the employer/location it trails -- it is a footnote about the
   row, not a status the eye should land on first, so it takes the tertiary
   ink step rather than a pill/badge treatment (a badge here would compete
   with the Dutch-requirement badge directly below it, which genuinely does
   need the attention). */
.job-main .firm .firm-count {
  color: var(--color-ink-3);
}
/* Post-review fix (2026-07-14, design audit): salary was not shown
   anywhere in the UI -- added alongside employer/location, one step up in
   weight/color from `.firm`'s muted meta styling since pay is decision-
   driving, not secondary metadata. */
.row-salary {
  font-size: var(--type-meta-size);
  font-weight: 700;
  color: var(--color-accent-deep);
  margin-top: 2px;
}
.job-main .stars {
  display: flex;
  gap: 1.5px;
  margin-top: var(--space-1);
}
.job-main .stars svg {
  width: 11px;
  height: 11px;
  fill: var(--color-ink);
}
.job-main .stars svg.off {
  fill: var(--color-star-off);
}
.when {
  flex: none;
  height: 24px;
  padding: 0 9px;
  border-radius: var(--radius-full);
  background: var(--color-bg);
  border: 1px solid var(--color-hairline);
  color: var(--color-ink-2);
  font-size: 11.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: center;
  white-space: nowrap;
}
.feed-empty {
  font-size: var(--type-body-size);
  color: var(--color-ink-2);
  padding: var(--space-4) 2px;
}

/* --- Tab bar (Task 1, UX-DR11), shared shell component ---------------------
   base.html renders this via _shell_macros.html's tab_bar() whenever a
   template's context sets `active_tab`; omitted entirely (not merely
   CSS-hidden) on any screen that doesn't set it. */
.tabbar {
  flex: none;
  position: sticky;
  bottom: 0;
  border-top: 1px solid var(--color-hairline);
  background: var(--color-card);
  padding: var(--space-2) var(--space-6) max(var(--space-2), env(safe-area-inset-bottom));
}
.tabs {
  display: flex;
}
.tab {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 4px 0 2px;
  color: var(--color-ink-3);
  text-decoration: none;
}
.tab svg {
  width: 24px;
  height: 24px;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.tab span {
  font-size: 10.5px;
  font-weight: 600;
}
.tab.active {
  color: var(--color-accent);
}
.tab.active span {
  font-weight: 700;
}

/* --- Admin cross-nav (Ad-hoc story, 2026-07-14), shared shell component ----
   The 3 admin screens (Signups/Users/Costs) have no place in the main
   tab_bar (admin isn't a bottom-nav destination) -- this small pill row is
   their own equivalent, rendered via _shell_macros.html's admin_nav(). */
.admin-nav {
  display: flex;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}
.admin-nav-link {
  font-size: var(--type-meta-size);
  font-weight: 700;
  font-family: inherit;
  color: var(--color-ink-2);
  text-decoration: none;
  padding: 7px var(--space-4);
  border-radius: var(--radius-full);
  border: 1px solid var(--hairline-strong);
}
.admin-nav-link.active {
  color: var(--color-on-accent);
  background: var(--color-accent);
  border-color: transparent;
}

/* ============================================================================
   Story 2.11: Job Brief shared component layer (UX-DR6/7/13/17), transcribed
   from the approved job-brief.html mockup onto this app's existing token
   names -- no new hex/px one-off beyond what the mockup itself needed a
   named exception for (--r-card was already --radius-xl and is reused
   verbatim via --radius-lg/--radius-full below; --size-mono/--size-avatar-lg
   are Story 2.10's tokens, not re-declared here).
   ============================================================================ */

/* --- Header: back-chevron, no tab bar (UX-DR17 single-surface-depth) ------- */
.brief-header {
  flex: none;
  padding-bottom: var(--space-2);
}
.back-btn {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-full);
  background: var(--color-card);
  border: 1px solid var(--color-hairline);
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  text-decoration: none;
}
.back-btn svg {
  width: 19px;
  height: 19px;
  stroke: var(--color-ink);
  fill: none;
  stroke-width: 2.3;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --- Title block ------------------------------------------------------------ */
.brief-title-block {
  flex: none;
  padding-bottom: var(--space-4);
}
.brief-wordmark {
  font-size: var(--type-wordmark-size);
  font-weight: var(--type-wordmark-weight);
  letter-spacing: var(--type-wordmark-tracking);
  color: var(--color-accent);
  text-transform: uppercase;
  margin-bottom: var(--space-2);
}
.role-title {
  font-size: var(--type-title-size);
  font-weight: var(--type-title-weight);
  letter-spacing: var(--type-title-tracking);
  line-height: var(--type-title-leading);
  color: var(--color-ink);
  margin: 0;
}
.employer-line {
  font-size: var(--type-body-size);
  color: var(--color-ink-2);
  margin-top: var(--space-1);
}
/* Post-review fix (2026-07-14, design audit): salary was not shown
   anywhere in the UI -- also repeated in the "Basic info" card below, but
   surfaced here too so it's visible without scrolling past the live
   match/verify region. */
.brief-salary {
  font-size: var(--type-body-size);
  font-weight: 700;
  color: var(--color-accent-deep);
  margin-top: 2px;
}
.brief-stars {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-top: var(--space-3);
}
.brief-stars svg {
  width: 15px;
  height: 15px;
  fill: var(--color-ink);
}
.brief-stars svg.off {
  fill: var(--color-star-off);
}
.brief-stars .match {
  margin-left: 7px;
  font-size: var(--type-meta-size);
  font-weight: 700;
  color: var(--color-ink-2);
}

/* --- Verified-live badge (UX-DR6): reassurance, not alarm -- deliberately
   LOWER visual weight than .hero's pulsing "NEW ALARM" pill: a static
   accent-tint pill, no pulse/animation at all. ---------------------------- */
.verified-badge {
  /* Post-review fix (2026-07-15, DUO badge overlap): `display: flex` +
     `width: fit-content` instead of `inline-flex`. An inline-level pill's
     own height + margin-top does NOT contribute to its block parent's
     (#brief-live-region) computed height, so the pill visually overflowed
     the bottom of that block and the following `.duo-badge` sat on top of
     it. A block-level, content-width flex box counts its full height and
     top margin toward the parent, so the DUO badge now clears it cleanly. */
  display: flex;
  width: fit-content;
  align-items: center;
  gap: 6px;
  height: 27px;
  padding: 0 12px 0 10px;
  border-radius: var(--radius-full);
  background: var(--color-accent-tint);
  border: 1px solid #D6E9DE;
  color: var(--color-accent-deep);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin-top: var(--space-3);
}
.verified-badge svg {
  width: 13px;
  height: 13px;
  stroke: var(--color-accent-deep);
  fill: none;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --- Gone/dead-listing badge (post-review Fix 2, Edge Case Hunter): a
   posting the app itself has confirmed gone -- never the reassuring
   accent-tinted .verified-badge, a neutral/muted tone instead, matching
   .noapply-badge's own neutral ink-2 treatment below. --------------------- */
.gone-badge {
  /* Same block-level, content-width treatment as `.verified-badge` above
     (both render in the same `#brief-live-region` slot) so a following
     sibling never overlaps it. */
  display: flex;
  width: fit-content;
  align-items: center;
  gap: 6px;
  height: 27px;
  padding: 0 12px 0 10px;
  border-radius: var(--radius-full);
  background: var(--color-bg);
  border: 1px solid var(--color-hairline);
  color: var(--color-ink-2);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin-top: var(--space-3);
}
.gone-badge svg {
  width: 13px;
  height: 13px;
  stroke: var(--color-ink-2);
  fill: none;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --- Section titles + info cards (conditions / basic info) ----------------- */
.section-title {
  font-size: var(--type-body-size);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--color-ink);
  margin: var(--space-5) 2px var(--space-2);
}
.section-title:first-child {
  margin-top: 0;
}
.info-card {
  background: var(--color-card);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-lg);
  padding: 6px var(--space-4) 4px;
}
.cond-row {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-3);
  padding: 11px 0;
  border-bottom: 1px solid var(--color-hairline);
}
.cond-row:last-child {
  border-bottom: none;
}
.cond-label {
  font-size: var(--type-meta-size);
  color: var(--color-ink-2);
  flex: none;
  width: 92px;
  padding-top: 1px;
}
/* UX-DR13 (AC #5): reflow, never truncate -- no `text-overflow`/`nowrap`
   anywhere on conditions content, so a long AI-summarized value wraps
   correctly instead of being cut off, even at the largest dynamic-type/
   browser zoom level. */
.cond-value {
  font-size: var(--type-meta-size);
  font-weight: 600;
  color: var(--color-ink);
  text-align: right;
  line-height: 1.4;
  flex: 1;
  white-space: normal;
  overflow-wrap: break-word;
}
/* Post-review fix (2026-07-14, Teo feedback: "job description should
   always be visible"): the actual scraped listing text -- posting.description
   was already stored/used for scoring but never shown to the user anywhere
   on the Job Brief. */
.posting-description {
  font-size: var(--type-body-size);
  font-weight: var(--type-body-weight);
  color: var(--color-ink);
  line-height: 1.55;
  white-space: pre-line;
  margin: 0;
}
/* Post-review fix (2026-07-14): a link to the real listing page must be
   visible for EVERY apply_mode, not just external_only -- this app never
   actually delivers a CV anywhere itself (job/domain/application.py's own
   docstring: "sending" = one Application record, not an email/API call),
   so the ONLY way to actually apply is this link. Styled to read as a link
   (accent color + underline) despite sitting inside .cond-value's own
   right-aligned/ink-colored default. */
.cond-value a {
  color: var(--color-accent-deep);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.source-badge {
  height: 22px;
  padding: 0 10px;
  border-radius: var(--radius-full);
  background: var(--color-bg);
  border: 1px solid var(--color-hairline);
  color: var(--color-ink-2);
  font-size: 11.5px;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
}

/* --- Apply-mode CTA (Task 4, UX-DR7) ---------------------------------------- */
.cta-outline {
  margin-top: var(--space-2);
  height: 52px;
  border-radius: var(--radius-full);
  background: transparent;
  border: 1.5px solid var(--color-accent);
  color: var(--color-accent-deep);
  font-size: 15.5px;
  font-weight: 700;
  letter-spacing: -0.01em;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  text-decoration: none;
}
.cta-outline svg {
  width: 16px;
  height: 16px;
  stroke: var(--color-accent-deep);
  fill: none;
  stroke-width: 2.3;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --- No-direct-apply state (UX-DR7 / FR-6 edge case) ------------------------ */
.noapply-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 27px;
  padding: 0 12px;
  border-radius: var(--radius-full);
  background: var(--color-bg);
  border: 1px solid var(--color-hairline);
  color: var(--color-ink-2);
  font-size: 11.5px;
  font-weight: 700;
}
.noapply-badge svg {
  width: 13px;
  height: 13px;
  stroke: var(--color-ink-2);
  fill: none;
  stroke-width: 2.3;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.noapply-note {
  font-size: var(--type-meta-size);
  color: var(--color-ink-2);
  line-height: 1.5;
  margin: var(--space-3) 2px var(--space-4);
}
.noapply-note a {
  color: var(--color-accent-deep);
  font-weight: 700;
}

/* --- CV send selector (Story 3.1 Task 2, UX-DR5) ----------------------------
   2 stacked radio-style rows -- reuses the SAME hidden-radio +
   sibling-selector mechanism `.chip-toggle-input:checked + .chip` already
   established above (Story 1.9), just a full-width row shape (`.cvsel-row`)
   instead of a pill chip: selected row gets an accent border +
   accent-tint-2 background, same "active state = accent border + tint bg"
   language `.trait-row.active` already established for a different
   full-width-row component. AC #4 (UX-DR13): `min-height: 44px` on both the
   row and the CTA keeps the tap-target floor even where the visual content
   is shorter. --------------------------------------------------------------- */
.cv-select-form {
  margin-top: var(--space-1);
}
.cvsel-toggle {
  display: block;
}
.cvsel-toggle + .cvsel-toggle {
  margin-top: var(--space-2);
}
.cvsel-row {
  display: block;
  box-sizing: border-box;
  min-height: 44px;
  width: 100%;
  padding: 9px var(--space-4);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-md);
  background: var(--color-card);
  cursor: pointer;
  transition: border-color .18s var(--ease), background .18s var(--ease);
}
.chip-toggle-input:checked + .cvsel-row {
  border-color: var(--color-accent);
  background: var(--color-accent-tint-2);
}
.chip-toggle-input:focus-visible + .cvsel-row {
  outline: 2px solid var(--color-accent-deep);
  outline-offset: 2px;
}
.cvsel-row.disabled {
  cursor: not-allowed;
  opacity: .55;
}
.cvsel-title {
  display: block;
  font-size: var(--type-body-size);
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--color-ink);
}
.cvsel-caption {
  display: block;
  margin-top: 2px;
  font-size: var(--type-meta-size);
  color: var(--color-ink-2);
  line-height: 1.4;
}
.cv-send-cta {
  width: 100%;
  min-height: 44px;
}

/* --- "recommended" tag (Story 3.2 Task 5, AC #4, UX-DR5) --------------------
   Reuses `.badge`'s exact accent-tint/accent-deep color pair above (same
   "this is the app's endorsement" visual language, not a new one) but sized
   for a short text label rather than a fixed-size numeric count. -------- */
.cvsel-tag {
  display: inline-block;
  margin-left: var(--space-1);
  padding: 2px var(--space-2);
  border-radius: var(--radius-full);
  background: var(--color-accent-tint);
  color: var(--color-accent-deep);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .02em;
  text-transform: uppercase;
  vertical-align: middle;
}

/* --- Improve failure states (Story 3.2 Task 4): the generic retry-able
   error and the untraceable-claim block both re-render `cv_send_selector`
   with this note above the rows -- deliberately the SAME neutral tone
   `.noapply-note` already uses elsewhere in this component family, just a
   small top margin since it now sits above form content rather than below
   it. ------------------------------------------------------------------- */
.cvsel-error-note {
  margin-top: 0;
  margin-bottom: var(--space-3);
}

/* --- Sent-state row (Story 3.1 Task 4, AC #3): the static "Sent
   [level] · [timestamp]" row that replaces the selector once an
   Application exists -- no form, no CTA, no way to re-send in v1.
   Deliberately the SAME quiet, neutral tone as `.gone-badge`/
   `.noapply-badge` above (a settled fact, not a call to action), never
   `.verified-badge`'s accent-tint reassurance treatment. -------------------- */
.sent-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  box-sizing: border-box;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-md);
  background: var(--color-card);
  color: var(--color-ink-2);
  font-size: var(--type-meta-size);
  font-weight: 650;
}
.sent-row svg {
  width: 15px;
  height: 15px;
  stroke: var(--color-accent-deep);
  fill: none;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex: none;
}
/* Story 3.6 Task 2 retrofit -- see `sent_state_row`'s own docstring
   (_brief_macros.html) for why this is a modifier on the row's existing
   container rather than a nested badge span. Same token pair
   `.level-badge.improve` below uses, so the two visual weights never
   drift apart even though they're two separate rulesets. */
.sent-row.level-improve {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-on-accent);
}
.sent-row.level-improve svg {
  stroke: var(--color-on-accent);
}

/* ============================================================================
   Story 3.6: CV-level badge (UX-DR4, its first real visual implementation
   in this codebase) + the Applications row it was built for.
   2 escalating visual weights of the SAME accent hue, no new hue: Original
   = outline/transparent background, `ink-2` text; Improve = solid accent
   fill, `on-accent` text -- shared by `application_row`'s badge below AND
   `sent_state_row`'s `level-improve` modifier above (UX-DR4 is explicitly
   ONE component used in two places, not two divergent treatments).
   Deliberately NOT `.cvsel-tag` (that's a different, tag-shaped
   "recommended" pill component, Story 3.2) reused verbatim.
   ============================================================================ */
.level-badge {
  flex: none;
  display: inline-flex;
  align-items: center;
  height: 21px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-full);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .02em;
  white-space: nowrap;
}
.level-badge.original {
  background: transparent;
  border: 1px solid var(--color-hairline);
  color: var(--color-ink-2);
}
.level-badge.improve {
  background: var(--color-accent);
  border: 1px solid var(--color-accent);
  color: var(--color-on-accent);
}

/* `a.job` (application_row, _feed_macros.html): the Application row is a
   real link (AC #2), unlike `feed_row`'s plain non-interactive `div.job`
   above -- this reset makes an anchor carrying the same `.job` class look
   byte-for-byte identical (no default underline/link-blue), so the two
   share one visual definition even though one is clickable. */
a.job {
  text-decoration: none;
  color: inherit;
}
.app-right {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-1);
}
.app-right .sent-stamp {
  font-size: 10.5px;
  color: var(--color-ink-3);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ============================================================================
   Story 3.4: Reply-All bar/sheet + summary banner (UX-DR12). No approved
   mockup covers this screen state -- dev agent's call on exact visual
   treatment (Dev Agent Record). Composed entirely from EXISTING tokens/
   component language rather than inventing new values: the bar itself
   reuses `.job`'s own Card/hairline/radius-lg shape (it sits in the same
   feed-list rhythm, right after the last row), the disclosure trigger
   reuses `.skill-more`/`.trait-more`'s existing `<details>`-summary pattern
   (job/web/templates/_onboarding_macros.html, tokens.css above -- the ONE
   no-JS disclosure widget already established in this app, rather than
   inventing a JS-driven modal/sheet), and the summary banner reuses
   `.verified-badge`'s accent-tint/accent-deep "reassuring fact" color pair.
   ============================================================================ */

/* The bar + its expanding sheet (Task 3, AC #1/#5): a native <details>
   element -- `<summary>` IS the always-visible bar (closed state), its
   content IS the bottom sheet (open state). Free keyboard/focus semantics
   (Enter/Space toggles, no ARIA to hand-roll) and, critically for AC #5
   (UX-DR13), free correct DOM/tab order: this element lives in normal
   document flow directly after `.feed-list`/the empty-state paragraph in
   feed.html, so it is reachable immediately after the last feed row,
   never stranded past the tab bar or absolutely-positioned out of order. */
.reply-all-bar {
  margin-top: var(--space-3);
  background: var(--color-card);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-lg);
  padding: var(--space-3) var(--space-card-pad);
}
.reply-all-bar summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: 44px;
  cursor: pointer;
  list-style: none;
  font-size: var(--type-body-size);
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--color-ink);
}
.reply-all-bar summary::-webkit-details-marker {
  display: none;
}
.reply-all-bar-label {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.reply-all-bar[open] summary {
  margin-bottom: var(--space-3);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--color-hairline);
}
.reply-all-bar summary svg {
  width: 18px;
  height: 18px;
  stroke: var(--color-accent-deep);
  fill: none;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex: none;
  transition: transform .18s var(--ease);
}
.reply-all-bar[open] summary svg {
  transform: rotate(180deg);
}

/* Summary banner (Task 3): "Sent: {sent}/{total} listings." -- the
   URL-echoed, transient result of a just-completed Reply-All redirect.
   Reuses `.verified-badge`'s exact accent-tint reassurance color language
   (a settled, positive fact), never `.noapply-note`'s neutral tone (a
   partial-success count is still good news, not a caveat). */
.reply-all-banner {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--type-meta-size);
  font-weight: 700;
  color: var(--color-accent-deep);
  background: var(--color-accent-tint);
  border: 1px solid var(--color-accent-tint);
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-4);
  margin: 0 2px var(--space-3);
}
.reply-all-banner svg {
  width: 15px;
  height: 15px;
  stroke: var(--color-accent-deep);
  fill: none;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex: none;
}

/* ============================================================================
   v6 design-overhaul pass (2026-07-12): shell device-frame, auth screens,
   shared file-pick control, app-wide focus floor. Composed from existing
   tokens -- the only genuinely new values are --color-canvas/--shell-w
   (declared up in :root, not hardcoded here or in base.html).
   ============================================================================ */

/* --- Flex-stretch bug class fix: list containers must flow naturally, never
   `flex: 1` inside the .screen column -- a short list otherwise stretches
   and distributes dead whitespace (the Applications day-sections gap bug,
   the onboarding mid-screen hole). Applies to feed + applications lists. --- */
.feed-list {
  flex: none;
}

/* --- Focus floor (a11y): one visible ring language app-wide. :where() keeps
   specificity at zero so any component-specific focus treatment above
   (e.g. .chip-toggle-input:focus-visible + .chip) still wins unchanged. --- */
:where(button, a, summary):focus-visible {
  outline: 2px solid var(--color-accent-deep);
  outline-offset: 2px;
}

/* --- Auth screens (signup / pending): the first surfaces anyone sees.
   One centered card on the canvas wash -- same Card/hairline/radius-xl
   language as every other elevated element, pill inputs matching the
   app's existing pill control language (.inputbar/.chip/.cta). ----------- */
.auth-screen {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  width: 100%;
  max-width: 400px;
  margin: 0 auto;
  padding: var(--space-6) 0;
}
.auth-brand {
  text-align: center;
  font-size: var(--type-wordmark-size);
  font-weight: var(--type-wordmark-weight);
  letter-spacing: var(--type-wordmark-tracking);
  color: var(--color-accent);
  margin-bottom: var(--space-4);
}
.auth-card {
  background: var(--color-card);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-xl);
  padding: var(--space-6) var(--space-5);
  box-shadow: var(--lift);
}
.auth-card-center {
  text-align: center;
}
.auth-card-center .status-pill {
  margin-bottom: var(--space-4);
}
.auth-title {
  font-size: var(--type-title-size);
  font-weight: var(--type-title-weight);
  letter-spacing: var(--type-title-tracking);
  line-height: var(--type-title-leading);
  color: var(--color-ink);
  margin: 0 0 var(--space-2);
}
.auth-sub {
  font-size: var(--type-meta-size);
  font-weight: var(--type-meta-weight);
  color: var(--color-ink-2);
  line-height: 1.5;
  margin: 0 0 var(--space-5);
}
.auth-card-center .auth-sub {
  margin-bottom: 0;
}
.auth-foot {
  text-align: center;
  font-size: var(--type-meta-size);
  font-weight: var(--type-meta-weight);
  color: var(--color-ink-3);
  line-height: 1.5;
  margin: var(--space-4) var(--space-4) 0;
}
.auth-foot a {
  color: var(--color-accent);
  font-weight: 700;
  text-decoration: none;
}
.auth-foot a:hover {
  text-decoration: underline;
}
.form-error {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  background: var(--color-status-pending-bg);
  border: 1px solid #F0DFC0;
  color: var(--color-status-pending-ink);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  font-size: var(--type-meta-size);
  font-weight: 600;
  line-height: 1.45;
  margin-bottom: var(--space-4);
}
.form-error svg {
  width: 15px;
  height: 15px;
  flex: none;
  margin-top: 2px;
  stroke: currentColor;
  fill: none;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.field {
  margin-bottom: var(--space-4);
}
.field-label {
  display: block;
  font-size: var(--type-label-size);
  font-weight: var(--type-label-weight);
  letter-spacing: var(--type-label-tracking);
  text-transform: uppercase;
  color: var(--color-ink-3);
  margin-bottom: var(--space-2);
}
.field-input {
  width: 100%;
  height: 48px;
  border-radius: var(--radius-full);
  border: 1px solid var(--hairline-strong);
  background: var(--color-bg);
  padding: 0 var(--space-5);
  font-size: var(--type-body-size);
  font-weight: 500;
  font-family: var(--font-family-system);
  color: var(--color-ink);
  transition: border-color .18s var(--ease), background .18s var(--ease), box-shadow .18s var(--ease);
}
.field-input:focus {
  outline: none;
  border-color: var(--color-accent);
  background: var(--color-card);
  box-shadow: 0 0 0 3px var(--color-accent-tint);
}
.btn-primary {
  width: 100%;
  height: 50px;
  border: none;
  border-radius: var(--radius-full);
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-size: 15.5px;
  font-weight: 700;
  letter-spacing: -.01em;
  font-family: inherit;
  cursor: pointer;
  box-shadow: var(--lift-accent), var(--inset-hi);
  transition: transform .12s var(--ease), background .18s var(--ease);
}
.btn-primary:hover {
  background: var(--color-accent-deep);
}
.btn-primary:active {
  transform: scale(.985);
  background: var(--accent-press);
}
.auth-card .btn-primary {
  margin-top: var(--space-2);
}
/* Semantic status: waiting-for-approval. The dot carries real state
   (account pending), not decoration. */
.status-pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 27px;
  padding: 0 12px;
  border-radius: var(--radius-full);
  background: var(--color-status-pending-bg);
  color: var(--color-status-pending-ink);
  font-size: 11.5px;
  font-weight: 700;
}
.status-pill .dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--color-status-pending-dot);
  flex: none;
}

/* --- File-pick control (CV upload, onboarding + Profile): replaces the raw
   native <input type="file"> row. Clear 2-step flow: (1) tap the dashed
   tile to choose a PDF -> tile turns solid accent-tinted and shows the
   chosen filename (base.html's delegated change-listener), (2) tap the
   full-width "Upload CV" CTA below it. Until a file is chosen the CTA is
   dimmed via :has() -- still clickable, so native `required` validation
   speaks when tapped early (no JS-dependent dead end). ------------------- */
.filepick {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: 54px;
  padding: var(--space-2) var(--space-4) var(--space-2) var(--space-2);
  border: 1.5px dashed var(--hairline-strong);
  border-radius: var(--radius-md);
  background: var(--color-bg);
  cursor: pointer;
  transition: border-color .18s var(--ease), background .18s var(--ease);
}
.filepick:hover {
  border-color: var(--color-accent);
}
.filepick:has(.filepick-input:focus-visible) {
  outline: 2px solid var(--color-accent-deep);
  outline-offset: 2px;
}
/* Post-review fix (2026-07-15): the input used to be squeezed into a
   pointer-events:none 1x1px corner, relying on the <label> wrapping it to
   forward activation -- that forwarding is unreliable on some mobile
   WebKit builds (tap never opens the file sheet). Stretching the real
   input over the whole tile means every tap lands on the input directly,
   no forwarding involved. */
.filepick-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.filepick-ic {
  flex: none;
  width: 38px;
  height: 38px;
  border-radius: var(--radius-sm);
  background: var(--color-accent-tint);
  display: flex;
  align-items: center;
  justify-content: center;
}
.filepick-ic svg {
  width: 17px;
  height: 17px;
  stroke: var(--color-accent-deep);
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.filepick-name {
  flex: 1;
  min-width: 0;
  font-size: var(--type-meta-size);
  font-weight: 600;
  color: var(--color-ink-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.filepick-hint {
  flex: none;
  font-size: var(--type-label-size);
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--color-accent-deep);
  background: var(--color-card);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-full);
  padding: 4px 10px;
}
.filepick.has-file,
.cv-upload-form:has(.filepick-input:valid) .filepick {
  border-style: solid;
  border-color: var(--color-accent);
  background: var(--color-accent-tint-2);
}
.filepick.has-file .filepick-name {
  color: var(--color-ink);
  font-weight: 650;
}
/* v6: the CV upload form is now a vertical stack (tile above, full-width
   CTA below) -- overrides the older flex-row declaration higher up.
   Post-review fix (2026-07-14, design audit): `.cert-add-form` joins this
   override now that it also uses the `.filepick` tile (see `cert_filepick()`
   in _onboarding_macros.html) -- it must NOT stay on the older flex-row
   rule (`.cv-upload-form, .cert-add-form { display:flex; align-items:
   center; flex-wrap:wrap; }` above), which would squeeze the full-width
   tile into a wrapped row instead of stacking it above the button. */
.cv-upload-form,
.cert-add-form {
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-3);
  margin-top: var(--space-3);
}
.cv-upload-form .btn-confirm-selection {
  width: 100%;
  min-height: 44px;
}
/* `.cert-add-btn` keeps its own smaller/dashed "optional action" sizing
   (32px, see its own rule above) -- only stretched to full width here, not
   forced to the primary CTA's 44px min-height, since the certificate
   upload is deliberately lighter-weight than the required CV upload. */
.cert-add-form .cert-add-btn {
  width: 100%;
}
.cv-upload-form:has(.filepick-input:invalid) .btn-confirm-selection,
.cert-add-form:has(.filepick-input:invalid) .cert-add-btn {
  opacity: .5;
  box-shadow: none;
}

/* ============================================================================
   Improve-consent settings (Profile "Improve mode") -- master switch + two
   truthful-attestation consent checkboxes with a "read more" declaration.
   Composed from existing tokens; the toggle + checkbox visuals are the only
   new shapes, kept in the same accent language as the rest of the app.
   ============================================================================ */
.imp-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
}
.imp-sub {
  margin-top: var(--space-1);
  font-size: var(--type-meta-size);
  color: var(--color-ink-2);
  line-height: 1.5;
}
.imp-switch {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-4);
  cursor: pointer;
}
.imp-switch-text {
  font-size: var(--type-body-size);
  font-weight: 650;
  color: var(--color-ink);
}
.imp-switch-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.imp-switch-track {
  flex: none;
  width: 46px;
  height: 28px;
  border-radius: var(--radius-full);
  background: var(--hairline-strong);
  padding: 3px;
  transition: background .18s var(--ease);
}
.imp-switch-knob {
  display: block;
  width: 22px;
  height: 22px;
  border-radius: var(--radius-full);
  background: var(--color-card);
  box-shadow: 0 1px 3px rgba(16, 40, 28, .25);
  transition: transform .18s var(--ease);
}
.imp-switch-input:checked + .imp-switch-track {
  background: var(--color-accent);
}
.imp-switch-input:checked + .imp-switch-track .imp-switch-knob {
  transform: translateX(18px);
}
.imp-switch-input:focus-visible + .imp-switch-track {
  outline: 2px solid var(--color-accent-deep);
  outline-offset: 2px;
}

.imp-consents {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-hairline);
  transition: opacity .18s var(--ease);
}
/* Master toggle off: consents stay in the DOM (so they always submit and
   persist) but read as inactive. Not `pointer-events: none` -- the user must
   still be able to pre-set a consent before enabling, and toggling one submits
   the form which re-renders with the master state unchanged. */
.imp-consents.imp-off {
  opacity: .5;
}
.imp-consent {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  cursor: pointer;
}
.imp-consent + .imp-more {
  margin-top: var(--space-1);
}
.imp-more + .imp-consent {
  margin-top: var(--space-3);
}
.imp-consent-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.imp-consent-box {
  flex: none;
  width: 22px;
  height: 22px;
  margin-top: 1px;
  border-radius: 7px;
  border: 1.5px solid var(--hairline-strong);
  background: var(--color-card);
  position: relative;
  transition: background .18s var(--ease), border-color .18s var(--ease);
}
.imp-consent-input:checked + .imp-consent-box {
  background: var(--color-accent);
  border-color: var(--color-accent);
  box-shadow: var(--inset-hi);
}
.imp-consent-input:checked + .imp-consent-box::after {
  content: "";
  position: absolute;
  left: 6px;
  top: 2px;
  width: 6px;
  height: 11px;
  border: solid var(--color-on-accent);
  border-width: 0 2.2px 2.2px 0;
  transform: rotate(45deg);
}
.imp-consent-input:focus-visible + .imp-consent-box {
  outline: 2px solid var(--color-accent-deep);
  outline-offset: 2px;
}
.imp-consent-title {
  display: block;
  font-size: var(--type-body-size);
  font-weight: 650;
  letter-spacing: -.01em;
  color: var(--color-ink);
}
.imp-consent-caption {
  display: block;
  margin-top: 2px;
  font-size: var(--type-meta-size);
  color: var(--color-ink-2);
  line-height: 1.45;
}
.imp-more {
  margin-left: calc(22px + var(--space-3));
}
.imp-more summary {
  list-style: none;
  cursor: pointer;
  font-size: var(--type-label-size);
  font-weight: 700;
  letter-spacing: .02em;
  color: var(--color-accent-deep);
  display: inline-block;
  padding: var(--space-1) 0;
}
.imp-more summary::-webkit-details-marker {
  display: none;
}
.imp-decl {
  margin: var(--space-2) 0 0;
  font-size: var(--type-meta-size);
  color: var(--color-ink-2);
  line-height: 1.55;
  background: var(--color-accent-tint-2);
  border: 1px solid var(--color-accent-tint);
  border-radius: var(--radius-md);
  padding: var(--space-3);
}
.imp-confirm {
  margin-top: var(--space-3);
  margin-bottom: 0;
}

/* ============================================================================
   Skill-discovery (2026-07-12): the onboarding "Find more skills (AI)"
   helper -- an outline dashed trigger + AI suggestion chips the user taps to
   confirm. Reuses the existing `.chip` shape for suggestions.
   ============================================================================ */
.skill-discovery {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px dashed var(--color-hairline);
}
.skill-discover-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 36px;
  padding: 0 var(--space-4);
  border-radius: var(--radius-full);
  border: 1px dashed var(--color-accent);
  background: var(--color-accent-tint-2);
  color: var(--color-accent-deep);
  font-size: var(--type-meta-size);
  font-weight: 700;
  font-family: inherit;
  cursor: pointer;
  transition: transform .12s var(--ease), background .18s var(--ease);
}
.skill-discover-btn:hover {
  background: var(--color-accent-tint);
}
.skill-discover-btn:active {
  transform: scale(.97);
}
.skill-discover-btn[disabled] {
  opacity: .6;
  cursor: progress;
}
.skill-discover-btn svg {
  width: 15px;
  height: 15px;
  fill: var(--color-accent-deep);
}
.skill-suggest-head {
  margin-top: var(--space-3);
  font-size: var(--type-meta-size);
  font-weight: 650;
  color: var(--color-ink-2);
  margin-bottom: var(--space-2);
}
.skill-suggest-list {
  margin-top: 0;
}
/* An unconfirmed suggestion reads as a dashed "add me" chip, distinct from a
   solid confirmed/selected chip -- so it's obvious these are proposals to tap,
   not already-selected skills. */
.chip.skill-suggest {
  border-style: dashed;
  border-color: var(--color-accent);
  color: var(--color-accent-deep);
  background: var(--color-card);
}
/* Confirmed marker left behind in #skill-suggestions after a tap (the real
   checkbox is OOB-appended into #skill-confirmed-extras) -- solid, done-looking. */
.chip.skill-suggest.is-added {
  border-style: solid;
  background: var(--color-accent-tint);
  cursor: default;
}
.skill-confirmed-extras:empty {
  display: none;
}
.skill-confirmed-extras {
  margin-bottom: var(--space-2);
}

/* --- Dutch-necessity badge (2026-07-25, Teo's dealbreaker signal) ---------
   One badge family, tone-classed: danger = Dutch required (dealbreaker for a
   no-Dutch profile -- the score cap hides these below the feed threshold
   anyway, so this mostly appears on the Brief), warn = likely needed,
   neutral = unlikely, ok = explicitly English-friendly. Real semantic state,
   not decoration. */
.lang-badge {
  display: flex;
  width: fit-content;
  font-size: var(--type-meta-size);
  font-weight: 700;
  padding: 3px var(--space-3);
  border-radius: var(--radius-full);
  margin-top: var(--space-2);
}
.lang-badge-danger {
  color: var(--color-status-danger-ink);
  background: color-mix(in oklab, var(--color-status-danger-dot) 14%, white);
}
.lang-badge-warn {
  color: var(--color-status-pending-ink);
  background: color-mix(in oklab, var(--color-status-pending-dot) 16%, white);
}
.lang-badge-neutral {
  color: var(--color-ink-2);
  background: var(--color-bg);
  border: 1px solid var(--color-hairline);
}
.lang-badge-ok {
  color: var(--color-accent-deep);
  background: var(--color-accent-tint);
}
/* Hero variant: on the solid accent card, a translucent white pill. */
.lang-badge-hero {
  color: var(--color-on-accent);
  background: rgba(255, 255, 255, .16);
  border: 1px solid rgba(255, 255, 255, .24);
  margin-top: var(--space-2);
  margin-bottom: 0;
}

/* 2026-07-28: the post-send actions -- download the prepared CV, open the
   listing. Recording an application while handing the user nothing was the
   missing half of "prepare and open"; these two are what let them finish. */
.sent-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.sent-action {
  flex: 1 1 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;            /* UX-DR13 tap-target floor */
  padding: 0 var(--space-4);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-md);
  background: var(--color-card);
  color: var(--color-ink);
  font-weight: 600;
  text-decoration: none;
}
.sent-action.primary {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-on-accent);
}
.sent-action:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* 2026-08-03 ("keep me signed in"): the one new control on the auth screens.
   Built from existing tokens only -- no new colour or spacing value. The 44px
   min-height is the same tap-target floor UX-DR13 already requires of every
   other row in this app, and it applies to the whole label so the text is a
   tap target too, not just the 16px box. */
.remember-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 44px;
  margin: var(--space-2) 0 var(--space-3);
  font-size: var(--type-meta-size);
  color: var(--color-ink-2);
  cursor: pointer;
}
.remember-row input {
  width: 18px;
  height: 18px;
  accent-color: var(--color-accent);
  flex: none;
}
