/* ─────────────────────────────────────────────────────────
   ShiftClicks — style-v2-jobs.css
   Job-board layout for jobs.html + job-detail.js. Loads AFTER style-v2.css
   and depends on its tokens and data components (.panel,
   .data-row, .badge, .chip, .field-search, .pagination,
   .skeleton, .empty-state, .notice, .toast, .jd-*).

   Only page-specific composition lives here. Anything another
   page could reuse — job-detail.js in particular — belongs in
   style-v2.css under DATA COMPONENTS instead.

   Never redefine a token in this file. jobs.html did exactly
   that (a whole dark-default :root block) and it's why the old
   page can't coexist with the Open Air theme system.
───────────────────────────────────────────────────────── */

/* ─── PAGE SHELL ──────────────────────────────────────────
   style-v2.css gives every <section> clamp(64px,9vw,110px)
   vertical padding. That rhythm suits a marketing page but
   pushes the feed too far down, so the two sections here
   opt out with tighter values.
──────────────────────────────────────────────────────────── */
#jobs-search  { padding-top: clamp(28px, 4vw, 46px); padding-bottom: clamp(22px, 3vw, 30px); }
#jobs-feed    { padding-top: 0; padding-bottom: clamp(40px, 6vw, 72px); }
#jobs-explore { padding-top: clamp(40px, 6vw, 72px); border-top: 1px solid var(--border); }

/* ─── HERO ────────────────────────────────────────────────
   Copy left, growth art right — same 2-column shape as
   index.html's .hero-grid, one notch narrower on the art side.
──────────────────────────────────────────────────────────── */
.jobs-hero-grid {
  display: grid;
  grid-template-columns: 1.25fr 1fr;
  gap: clamp(20px, 4vw, 56px);
  align-items: center;
  margin-bottom: clamp(22px, 3vw, 32px);
}

/* No ambient wash here, deliberately. index.html's .hero-atmosphere works
   because it spans a full-bleed section, so its radial gradients fade out
   past the viewport. Inside a ~420px box the same gradients get clipped at
   the element edge mid-falloff and read as a stray rectangular panel. The
   tinted steps carry the colour instead. */
.jobs-art {
  position: relative;
  justify-self: center;
  width: 100%;
  max-width: 420px;
}
.jobs-art svg { width: 100%; height: auto; display: block; }

.grow-steps .step { stroke: none; }
.step-b { fill: var(--tint-blue); }
.step-g { fill: var(--tint-green); }

.grow-line {
  stroke: url(#growLine);
  stroke-width: 3.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Earnings at each rung. Marigold — the site's third accent, and the one not
   already carrying meaning here (blue = advertiser, green = jobseeker).
   paint-order:stroke draws the --bg stroke *behind* the glyph, knocking a halo
   out of the route line so the $ stays legible where it sits on the vertex. */
.grow-money {
  fill: var(--gold);
  stroke: var(--bg);
  stroke-width: 4;
  paint-order: stroke;
  font-family: var(--font-display);
  font-weight: 800;
  user-select: none;
}
/* Size carries the raise. */
.t1 .grow-money { font-size: 15px; }
.t2 .grow-money { font-size: 18px; }
.t3 .grow-money { font-size: 21px; }
.t4 .grow-money { font-size: 26px; }

.grow-label {
  fill: var(--fg2);
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 650;
  user-select: none;
}
.t4 .grow-label { fill: var(--green); font-weight: 750; }

@media (prefers-reduced-motion: no-preference) {
  .grow-line {
    /* Path length is ~348 user units; 360 gives a small safety margin. */
    stroke-dasharray: 360;
    stroke-dashoffset: 360;
    animation: growDraw 1.4s cubic-bezier(0.4, 0, 0.3, 1) 0.15s forwards;
  }

  /* opacity:0 lives ONLY inside no-preference — same guard as .rise in
     style-v2.css. A reduce user never gets the hidden state, so there is no
     way to end up with permanently invisible labels if the animation is
     dropped. Delays track the line reaching each vertex: the bars sit at
     0 / 32 / 66 / 100% of the path, so over a 1.4s draw starting at 0.15s
     that lands at ~0.15 / 0.6 / 1.05 / 1.5s. */
  .grow-tier { opacity: 0; animation: growTierIn 0.5s ease forwards; }
  .t1 { animation-delay: 0.15s; }
  .t2 { animation-delay: 0.6s; }
  .t3 { animation-delay: 1.05s; }
  .t4 { animation-delay: 1.5s; }
}
@keyframes growDraw   { to { stroke-dashoffset: 0; } }
@keyframes growTierIn { from { opacity: 0; } to { opacity: 1; } }

/* ─── PAGE HEAD ───────────────────────────────────────────
   <mark> for the accent phrase matches the site-wide pattern
   (.hero-copy h1 mark on index, .page-head h1 mark on the
   secondary pages) — same element, same token.
──────────────────────────────────────────────────────────── */
.jobs-head { max-width: 640px; }
.jobs-head h1 {
  font-size: clamp(34px, 4.4vw, 50px);
  margin: 0 0 12px;
}
.jobs-head h1 mark { background: none; color: var(--accent-text); }
.jobs-head p {
  color: var(--fg2);
  font-size: 17px;
  max-width: 54ch;
}

/* ─── SEARCH PANEL ────────────────────────────────────────
   .panel sets overflow:hidden so .data-row borders clip to the
   rounded corners. This panel has no rows but does contain the
   absolutely-positioned category listbox, which must escape the
   panel's bounds — so it opts back out.
──────────────────────────────────────────────────────────── */
.search-panel {
  padding: clamp(18px, 2.4vw, 24px);
  overflow: visible;
}
.search-row {
  display: flex;
  gap: 10px;
  margin-bottom: 10px;
}
.search-row .field-search:first-child { flex: 1.4 1 260px; }

.filter-row {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
}
.filter-row .select-chevron { flex: 0 1 170px; min-width: 140px; }

/* Pushes "Clear all" to the far end of the row. */
#filterClear { margin-left: auto; }

/* ─── CATEGORY MULTI-SELECT ───────────────────────────────
   A listbox popover, not a native <select> — it's multi-select
   with checkboxes. #catFilterBtn is the trigger, the dropdown
   is role="listbox" and its options are real <button>s so they
   are focusable; arrow-key roving lives in jobs-v2.js.
──────────────────────────────────────────────────────────── */
.cat-wrap { position: relative; flex: 0 1 230px; min-width: 180px; }

.cat-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  font-family: var(--font-body);
  font-size: 15px;
  color: var(--fg);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 11px 14px;
  cursor: pointer;
  text-align: left;
  transition: border-color 0.15s;
}
.cat-btn:hover { border-color: var(--fg3); }
.cat-btn.open  { border-color: var(--accent); }
.cat-btn-label {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cat-btn-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 19px; height: 19px;
  padding: 0 5px;
  border-radius: 99px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 11px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}
.cat-btn-chevron {
  width: 12px; height: 12px;
  color: var(--fg3);
  flex-shrink: 0;
  transition: transform 0.18s;
}
.cat-btn.open .cat-btn-chevron { transform: rotate(180deg); }

.cat-dropdown {
  display: none;
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  z-index: 60;
  max-height: 290px;
  overflow-y: auto;
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 8px 26px rgba(0, 0, 0, 0.14);
}
.cat-dropdown.open { display: block; }

.cat-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 10px;
  border: none;
  border-radius: 7px;
  background: transparent;
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--fg2);
  text-align: left;
  cursor: pointer;
  transition: background 0.12s, color 0.12s;
}
.cat-item:hover { background: var(--tint-blue); color: var(--fg); }
.cat-item[aria-selected="true"] { color: var(--fg); font-weight: 650; }

.cat-item-box {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px; height: 16px;
  border: 1.5px solid var(--border);
  border-radius: 5px;
  background: var(--bg);
  flex-shrink: 0;
  transition: background 0.12s, border-color 0.12s;
}
.cat-item[aria-selected="true"] .cat-item-box {
  background: var(--accent);
  border-color: var(--accent);
}
/* Checkmark drawn in CSS so it follows the selected state without
   the JS having to swap SVG strings in and out. */
.cat-item[aria-selected="true"] .cat-item-box::after {
  content: '';
  width: 9px; height: 5px;
  border-left: 2px solid var(--on-accent);
  border-bottom: 2px solid var(--on-accent);
  transform: rotate(-45deg) translate(1px, -1px);
}
.cat-item-label.is-all { font-weight: 700; }

.cat-divider {
  height: 1px;
  margin: 5px 8px;
  background: var(--border);
}

.cat-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.cat-pills:not(:empty) { margin-top: 12px; }

/* ─── FEED HEADER ─────────────────────────────────────────── */
.feed-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  margin: clamp(26px, 3.5vw, 38px) 0 14px;
  scroll-margin-top: 84px;   /* clears the sticky nav on goToPage() */
}
.feed-head-left {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.feed-head h2 { font-size: clamp(21px, 2.4vw, 26px); }
.feed-head-right {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 13px;
  color: var(--fg3);
}
#jobsCount { font-size: 14px; color: var(--fg2); }
#jobsCount strong { color: var(--fg); font-weight: 700; font-variant-numeric: tabular-nums; }

/* ─── JOB ROW ─────────────────────────────────────────────
   Extends .data-row. The toggle is a real <button>; the panel
   below it carries `inert` while collapsed so its links stay
   out of the tab order.
──────────────────────────────────────────────────────────── */
.jobs-list { margin: 0; padding: 0; list-style: none; }

.job-row-heading { margin: 0; font-size: inherit; font-weight: inherit; }

.job-row-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  width: 100%;
  padding: 16px 20px;
  border: none;
  background: transparent;
  font-family: var(--font-body);
  text-align: left;
  cursor: pointer;
}
.job-row-main { min-width: 0; flex: 1; }

.job-title {
  display: block;
  font-family: var(--font-display);
  font-size: 16.5px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--fg);
  margin-bottom: 5px;
}
.job-row.open .job-title,
.job-row-toggle:hover .job-title { color: var(--accent-text); }

.job-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  font-size: 13px;
  color: var(--fg2);
}
.job-meta-item {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
}
.job-meta-item svg {
  width: 11px; height: 11px;
  color: var(--fg3);
  flex-shrink: 0;
}

.job-row-aside {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}
.job-chevron {
  width: 16px; height: 16px;
  color: var(--fg3);
  flex-shrink: 0;
  transition: transform 0.22s, color 0.15s;
}
.job-row.open .job-chevron { transform: rotate(180deg); color: var(--accent-text); }

/* Collapsed → open. max-height is animated rather than `hidden`
   toggled, so the row expands smoothly; `inert` (set in JS) is
   what actually removes the collapsed content from the tab
   order and the accessibility tree. */
.job-detail {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s ease;
}
.job-row.open .job-detail { max-height: 1800px; }

.job-detail-inner {
  padding: 0 20px 20px;
  border-top: 1px solid var(--border);
  margin-top: 2px;
  padding-top: 16px;
}

/* Long descriptions get a fade rather than a hard cut. The full
   text lives on the SSR /jobs/:id page. */
.job-detail-inner .jd-body {
  max-height: 340px;
  overflow: hidden;
  position: relative;
  -webkit-mask-image: linear-gradient(to bottom, #000 76%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 76%, transparent 100%);
}

.job-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 18px;
}
.job-share.copied { color: var(--green); box-shadow: inset 0 0 0 1px var(--green); }

@media (prefers-reduced-motion: no-preference) {
  .job-share.copied { animation: copyPop 0.35s ease; }
}
@keyframes copyPop {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.06); }
  100% { transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .job-detail  { transition: none; }
  .job-chevron { transition: color 0.15s; }
}

/* ─── SKELETON ROW ────────────────────────────────────────── */
.job-row-skeleton { padding: 20px; }

/* ─── EXPLORE CARDS ───────────────────────────────────────
   Real <button>s (jobs.html used <div onclick>). Each carries
   data-filter-cat; jobs-v2.js binds them to filterByCategory().
──────────────────────────────────────────────────────────── */
.explore-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}
.explore-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  gap: 8px;
  padding: 24px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  font-family: var(--font-body);
  cursor: pointer;
  transition: transform 0.15s, border-color 0.15s;
}
.explore-card:hover { transform: translateY(-3px); border-color: var(--accent); }
.explore-card h3 { font-size: 19px; }
.explore-card p {
  color: var(--fg2);
  font-size: 14.5px;
  margin: 0;
}
.explore-card-go {
  margin-top: 6px;
  font-size: 13.5px;
  font-weight: 650;
  color: var(--accent-text);
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.explore-card-go svg { width: 12px; height: 12px; }
.explore-card:hover .explore-card-go svg { transform: translateX(2px); }

@media (prefers-reduced-motion: reduce) {
  .explore-card:hover { transform: none; }
  .explore-card-go svg { transition: none; }
}

/* ─── PAGINATION SPACING ──────────────────────────────────── */
#jobsPagination { margin-top: 26px; }

/* ═════════════════════════════════════════════════════════
   JOB DETAIL PAGE  (netlify/functions/job-detail.js)
   SSR'd at /jobs/:id.

   Only page-specific layout lives here. The chrome reuses
   style-v2.css components directly (.badge, .btn, .panel,
   .notice, .toast, .empty-state), and the description body
   is governed by the shared .jd-* block in style-v2.css —
   NOT redefined here. That block is the single source of
   truth for how a job description looks anywhere on the site.
   ═════════════════════════════════════════════════════════ */

.jd-section { padding-top: clamp(24px, 3.5vw, 40px); padding-bottom: clamp(48px, 7vw, 88px); }

.jd-back-row { margin-bottom: clamp(16px, 2vw, 24px); }
.jd-back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 14px;
  font-weight: 650;
  color: var(--fg2);
  text-decoration: none;
  transition: color 0.15s;
}
.jd-back:hover { color: var(--accent-text); }
.jd-back svg { width: 13px; height: 13px; }

/* Sidebar is 310px, matching the old page. Collapses at 960px —
   its own breakpoint, off the 900/860/600/480 ladder, because the
   constraint here is the sidebar's fixed width, not the type. */
.jd-page {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 310px;
  gap: clamp(24px, 3.5vw, 44px);
  align-items: start;
}
/* job-detail.js emitted this class but never defined it, so a job with
   no sponsor AND no suggestions kept the 1fr/310px grid and rendered a
   dead 310px gutter. */
.jd-page--no-sidebar { grid-template-columns: minmax(0, 1fr); max-width: 820px; }

.jd-main { min-width: 0; }

.jd-header { margin-bottom: 18px; }
.jd-title {
  font-size: clamp(27px, 3.4vw, 38px);
  line-height: 1.15;
  margin: 10px 0 12px;
}
.jd-meta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
  font-size: 14.5px;
  color: var(--fg2);
}
.jd-meta-item { display: inline-flex; align-items: center; gap: 6px; }
.jd-meta-item svg { width: 13px; height: 13px; color: var(--fg3); flex-shrink: 0; }
.jd-badges { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 12px; }

.jd-anon { margin-bottom: 22px; }
/* The old page rendered this same reassurance twice, top and bottom of
   the description. Once is enough — it reads as filler repeated. */

/* No max-height/fade here, unlike .job-detail-inner .jd-body in the list:
   this page IS the full description. */
.jd-body { font-size: 15.5px; }

.jd-cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-top: 28px;
  padding-top: 24px;
  border-top: 1px solid var(--border);
}
.jd-share-btn.copied { color: var(--green); box-shadow: inset 0 0 0 1px var(--green); }
.jd-source-note {
  margin-top: 12px;
  font-size: 12.5px;
  color: var(--fg3);
  max-width: 60ch;
}

/* ─── SIDEBAR ─────────────────────────────────────────────── */
.jd-sidebar { min-width: 0; }
.jd-sidebar-sticky {
  position: sticky;
  top: 84px;            /* clears the 68px sticky nav */
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.sp-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 11px 16px;
  border-bottom: 1px solid var(--border);
}
.sp-body { padding: 16px; }
.sp-title { font-size: 16px; font-weight: 700; line-height: 1.3; margin-bottom: 3px; }
.sp-company { font-size: 13.5px; color: var(--fg2); margin: 0 0 12px; }
.sp-perks { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }
.sp-btn { width: 100%; }

/* Gold hairline marks the card as paid placement without the old
   gradient border, which read as decoration rather than disclosure.
   The "Sponsored" badge does the actual disclosing. */
.sp-card { border-top: 3px solid var(--gold); }

.sug-list { display: flex; flex-direction: column; }
.sug-card {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 13px 16px;
  border-bottom: 1px solid var(--border);
  text-decoration: none;
  transition: background 0.15s;
}
.sug-card:last-child { border-bottom: none; }
.sug-card:hover { background: color-mix(in srgb, var(--accent) 4%, transparent); }
.sug-title {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--fg);
}
.sug-card:hover .sug-title { color: var(--accent-text); }
.sug-company { font-size: 12.5px; color: var(--fg2); }
.sug-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; margin-top: 5px; }
.sug-match { font-size: 11.5px; font-weight: 650; color: var(--green); }
.sug-view-all {
  display: block;
  padding: 12px 16px;
  border-top: 1px solid var(--border);
  font-size: 13px;
  font-weight: 650;
  color: var(--accent-text);
  text-decoration: none;
}
.sug-view-all:hover { background: color-mix(in srgb, var(--accent) 4%, transparent); }

/* ─── KEY/VALUE ROWS (Pay, Shift, Duration …) ─────────────
   Emitted only by job-detail.js's parser, so it lives here
   rather than in style-v2.css's shared .jd-* block. If the
   list page's parser ever gains KV support this should move.
──────────────────────────────────────────────────────────── */
.jd-kv-block {
  margin: 0 0 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.jd-kv-row {
  display: flex;
  gap: 14px;
  padding: 9px 14px;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
}
.jd-kv-row:last-child { border-bottom: none; }
.jd-kv-key { flex: 0 0 130px; font-weight: 700; color: var(--fg); }
.jd-kv-val { color: var(--fg2); min-width: 0; }

@media (max-width: 960px) {
  .jd-page { grid-template-columns: minmax(0, 1fr); }
  .jd-sidebar-sticky { position: static; }
}
@media (max-width: 600px) {
  .jd-cta .btn { flex: 1 1 100%; }
  .jd-kv-row { flex-direction: column; gap: 2px; }
  .jd-kv-key { flex: none; }
}

/* ─── RESPONSIVE ──────────────────────────────────────────
   Ladder matches style-v2.css: 900 / 860 / 600 / 480.
──────────────────────────────────────────────────────────── */
@media (max-width: 900px) {
  .explore-grid { grid-template-columns: 1fr; }
  .search-row   { flex-wrap: wrap; }
  .search-row .field-search,
  .search-row .field-search:first-child { flex: 1 1 100%; }

  /* Art is dropped rather than stacked. This is a search-first page —
     stacking a ~300px graphic between the headline and the search box
     would push the one control everyone came for below the fold.
     index.html drops .route-graph at this same breakpoint for the
     same reason. */
  .jobs-hero-grid { grid-template-columns: 1fr; }
  .jobs-art { display: none; }
}

@media (max-width: 600px) {
  .filter-row > * { flex: 1 1 100%; }
  .cat-wrap { flex: 1 1 100%; }
  .filter-row .select-chevron { flex: 1 1 100%; }
  #filterClear { margin-left: 0; }

  /* Badges move under the title so long job titles keep their width. */
  .job-row-toggle {
    align-items: flex-start;
    flex-direction: column;
    gap: 10px;
    padding: 15px 16px;
  }
  .job-row-aside {
    width: 100%;
    justify-content: flex-start;
  }
  .job-chevron { margin-left: auto; }
  .job-detail-inner { padding: 16px 16px 18px; }
  .job-actions .btn { flex: 1 1 100%; }
  .feed-head { align-items: flex-start; flex-direction: column; gap: 6px; }
}

@media (max-width: 480px) {
  .search-panel { padding: 15px; }
  .job-title { font-size: 15.5px; }
}
