/* OpenMarketIL. Swiss grid, modernized. System rules, don't break them
   ad hoc elsewhere in this file:
   1. Sage-cream page background (--white); forest-green ink is a
      component color (buttons, rules), never on <body> directly.
   2. Green marks exactly one "this matters" element, never decoration.
   3. 4px radius on every discrete bordered box (buttons, chips, badges,
      inputs, floating panels); sharp (0) only for what isn't really a
      box -- grid-tile cards, the table, dividers. No box-shadow depth
      anywhere either way. See DESIGN.md's Shapes section for the full
      list of what stays sharp and why.
   4. Two voices: --font (Helvetica) for everything dense; --font-display
      (Overused Grotesk, bold/extrabold) only for the topbar and the two section
      titles.
   5. Job Board and Statistics are permanent, side by side above the
      mobile breakpoint (where they stack), never a drawer or toggle.
   6. Light/dark both read var(--white)/var(--black), never a literal
      hex, but the two palettes are tuned independently rather than one
      being a straight hex-invert of the other. */

@font-face {
  font-family: "Overused Grotesk";
  src: url("fonts/OverusedGrotesk-VF.woff2") format("woff2-variations");
  font-weight: 300 900;
  font-display: swap;
}

:root {
  --white: #f3f6e4;
  --black: #40513b;
  --green: #609966;
  --green-bright: #80ad85;
  --grey: #40513b;
  --grey-line: #c7d9b3; /* reported live: the original #dce8ce sat too close to --white's #f3f6e4 to read as a visible line at all */
  --red: #b8362c;
  --hover-bg: #e9efdb;

  --font: "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-display: "Overused Grotesk", "Helvetica Neue", Helvetica, Arial, sans-serif;

  /* Side padding for .workspace/.container. Scales with viewport
     (20px floor, 64px ceiling) instead of a fixed max-width box. */
  --gutter: clamp(20px, 4vw, 64px);
  --rule: 2px;

  /* Forces native chrome (input caret, scrollbars) to follow OUR toggle
     instead of the OS's own dark-mode setting, which would otherwise
     paint them dark against our light surfaces. */
  color-scheme: light;
}

:root[data-theme="dark"] {
  --white: #17181c;
  --black: #ededec;
  --green: #2fae60;
  --green-bright: #4ee585;
  --grey: #9a9a9a;
  --grey-line: #2b2c31;
  --hover-bg: #1e1f24;
  color-scheme: dark;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--white);
  color: var(--black);
}

body {
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

::selection { background: var(--black); color: var(--white); }

/* Custom scrollbars, theme-tokened like the rest of the page. The thumb's
   border uses the track color, the standard trick for an inset look. */
html { scrollbar-width: thin; scrollbar-color: var(--black) var(--white); }
*::-webkit-scrollbar { width: 12px; height: 12px; }
*::-webkit-scrollbar-track { background: var(--white); }
*::-webkit-scrollbar-thumb {
  background: var(--black);
  border: 3px solid var(--white);
  background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover { background: var(--green); }
*::-webkit-scrollbar-corner { background: var(--white); }

a { color: inherit; }
a.link { color: var(--green); text-decoration: none; border-bottom: 1px solid var(--green); }
a.link:hover { background: var(--green); color: var(--white); }

.icon-link { display: inline-flex; align-items: center; color: var(--black); }
.icon-link:hover { color: var(--green); }

button, input, select {
  font-family: var(--font);
  font-size: 13px;
  color: var(--black);
  caret-color: var(--black); /* belt-and-suspenders on top of color-scheme: light above */
}

.container {
  padding: 0 var(--gutter);
}

.section {
  border-bottom: var(--rule) solid var(--black);
  padding: 12px 0 40px;
}

.section-title {
  font-family: var(--font-display);
  font-size: 34px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  line-height: 1;
  margin: 0 0 12px;
}

/* Topbar */

.topbar {
  font-family: var(--font-display);
  border-bottom: var(--rule) solid var(--black);
  position: sticky;
  top: 0;
  background: var(--white);
  z-index: 10;
}

.topbar .container {
  display: flex;
  align-items: center;
  gap: 16px;
  /* nowrap, deliberately: .ticker has min-width:0 + overflow:hidden so
     it shrinks to fit instead. flex-wrap here wrapped the whole topbar
     onto 3 lines at any width short of "everything fits." */
  flex-wrap: nowrap;
  min-height: 56px;
  padding-top: 10px;
  padding-bottom: 10px;
}
.topbar .container > * { min-width: 0; } /* same min-width:auto issue as everywhere else in this file */
.status-indicator, .wordmark, .topnav { flex: 0 0 auto; } /* natural width; the ticker is the one flexible element between them */
.status-indicator { display: flex; align-items: center; }

/* Topbar ticker: seamless CSS-loop marquee. The item list is duplicated
   once in the track, animated exactly -50% so the loop has no visible seam. */

.ticker-rule { flex: 0 0 auto; width: var(--rule); height: 22px; background: var(--black); }

.ticker {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  height: 20px;
  display: flex;
  align-items: center; /* .ticker-track's height comes from its own text content, not this 20px window; without this it just sits at the top of the clipped box instead of centered in it */
}

.ticker-track {
  display: flex;
  width: max-content;
  animation: ticker-scroll 40s linear infinite;
}
.ticker:hover .ticker-track { animation-play-state: paused; } /* let a visitor actually read/click one without it sliding away */

@keyframes ticker-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

.ticker-item {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0 20px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--black);
  text-decoration: none;
  white-space: nowrap;
  border-right: 1px solid var(--grey-line);
}
.ticker-item:hover { color: var(--green); }
.ticker-item .bullet { color: var(--green); }
.ticker-item .ticker-company { color: var(--grey); }
.ticker-item:hover .ticker-company { color: var(--green); }

.wordmark {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}
/* The provided lockup (logo-horizontal.png) is a solid-fill rounded-rect
   card, not a transparent mark -- same "fixed brand color regardless of
   the page's own theme toggle" precedent as the favicon (DESIGN.md).
   Height picked to sit comfortably inside the topbar's ~36px content
   row (min-height:56px minus its 10px top/bottom padding) without
   forcing it taller; width follows the source image's own aspect ratio. */
.wordmark-logo { height: 34px; width: auto; }

.topnav {
  display: flex;
  align-items: center;
  gap: 28px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.theme-toggle {
  background: none;
  border: none;
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 700;
  color: inherit;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 0;
  cursor: pointer;
  border-bottom: 1px solid transparent;
}
.theme-toggle:hover { color: var(--green); border-color: var(--green); }

/* Auth. Same dropdown shape as .ms-menu (position:relative parent,
   absolute panel, 2px ink border, paper background) -- one hand-built
   pattern, not a second one invented for this. */
/* display:flex here is only for the signed-in case's two triggers
   (+ Alert, email) sitting side by side -- the signed-out case's single
   trigger and the (always position:absolute, out of flow) panel are
   unaffected either way. */
.auth-area { position: relative; display: flex; align-items: center; gap: 12px; }
.auth-trigger {
  background: none;
  border: none;
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 700;
  color: inherit;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 0;
  cursor: pointer;
  border-bottom: 1px solid transparent;
}
.auth-trigger:hover, .auth-trigger.active { color: var(--green); border-color: var(--green); }

.auth-signed-in {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 11px;
  text-transform: none;
  letter-spacing: normal;
}
.auth-signed-in .auth-email {
  color: var(--grey);
  max-width: 160px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.auth-signout {
  background: none;
  border: none;
  font-family: var(--font-display);
  font-size: 11px;
  font-weight: 700;
  color: inherit;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 0;
  cursor: pointer;
}
.auth-signout:hover { color: var(--red); }

.alerts-header {
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-bottom: 10px;
}

.alerts-empty {
  font-size: 12px;
  color: var(--grey);
  margin: 0 0 12px;
}

.alert-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid var(--grey-line);
}
.alert-row:last-child { border-bottom: none; }

.alert-summary {
  font-size: 12px;
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.alert-row.paused .alert-summary { color: var(--grey); }

.alert-actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }

.alert-toggle {
  background: none;
  border: 1px solid var(--black);
  border-radius: 4px;
  padding: 3px 8px;
  font-family: var(--font);
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--black);
  cursor: pointer;
}
.alert-toggle:hover { background: var(--black); color: var(--white); }

.alert-delete {
  background: none;
  border: none;
  color: var(--grey);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  padding: 2px;
}
.alert-delete:hover { color: var(--red); }

.create-alert-feedback {
  margin: 8px 0 0;
  font-size: 11px;
  color: var(--green);
}
.create-alert-feedback.error { color: var(--red); }

/* New-alert form, inside the My Alerts panel (app.js's wireAlertCreateForm) --
   its own filter pickers, independent of the board's .filters above. */
.alert-create {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--grey-line);
}
.alert-create-fields {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 10px;
}
.alert-create-fields input[type="text"] {
  border: var(--rule) solid var(--black);
  border-radius: 4px;
  background: var(--white);
  padding: 9px 10px;
  height: 38px;
  width: 100%;
  font-family: var(--font);
}
.alert-create-fields input[type="text"]:focus { outline: 2px solid var(--green); outline-offset: -2px; }
.alert-create-fields label.toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 38px;
  padding: 0 10px;
  border: var(--rule) solid var(--black);
  border-radius: 4px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  cursor: pointer;
}
.alert-create-fields label.toggle input { accent-color: var(--green); }
.alert-create .btn { width: 100%; }

/* 4px radius -- same scoped exception as .job-detail/.ms-menu above. */
.auth-panel {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  width: 260px;
  max-height: calc(100vh - 80px);
  overflow-y: auto;
  background: var(--white);
  border: var(--rule) solid var(--black);
  border-radius: 4px;
  z-index: 40;
  padding: 14px;
  text-transform: none;
  letter-spacing: normal;
  font-family: var(--font);
}
.auth-panel.alerts-panel { width: 320px; } /* alert summaries + action buttons, plus the new-alert form, need more room than the sign-in form */

.auth-provider-btn {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  height: 38px;
  padding: 0 12px;
  margin-bottom: 8px;
  border: var(--rule) solid var(--black);
  border-radius: 4px;
  background: var(--white);
  color: var(--black);
  font-family: var(--font);
  font-size: 13px; /* Body, matching .filters input/.ms-toggle -- 12.5px sat in the gap between Label and Body for no reason */
  font-weight: 700;
  text-align: left;
  cursor: pointer;
}
.auth-provider-btn svg { flex: 0 0 auto; } /* Google's mark keeps its own brand colors on hover (fill="currentColor" only on GitHub's, which does follow) */
.auth-provider-btn:hover { background: var(--black); color: var(--white); }

.auth-divider {
  text-align: center;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--grey);
  margin: 10px 0;
}

.auth-email-form { display: flex; gap: 6px; }
.auth-email-form[hidden] { display: none; } /* same fix as .job-detail[hidden]: a class rule's display beats the UA [hidden] stylesheet without this */
.auth-email-form input {
  flex: 1 1 auto;
  min-width: 0;
  height: 38px;
  padding: 9px 10px;
  border: var(--rule) solid var(--black);
  border-radius: 4px;
  background: var(--white);
  color: var(--black);
  font-family: var(--font);
  font-size: 13px;
}
.auth-email-form input:focus { outline: 2px solid var(--green); outline-offset: -2px; }
.auth-email-form .btn { flex: 0 0 auto; padding: 0 12px; font-size: 11px; }

.auth-error {
  margin: 8px 0 0;
  font-size: 11px;
  color: var(--red);
}

/* A square block, not a circle, per the sharp-corners rule. Reported
   live: the flicker animation this used to have read as distracting,
   not lively -- a flat, static color communicates the same LIVE/OFFLINE
   state without asking for attention every 1.6s. */
.status-dot {
  width: 8px;
  height: 8px;
  background: var(--green);
  display: inline-block;
  margin-right: 6px;
}
.status-dot.offline { background: var(--red); }

/* Workspace: Job Board (main) + Market Stats (a permanent right-hand
   board, not a drawer/overlay). Stacks vertically under the responsive
   breakpoint at the end of this file. */

.workspace {
  display: grid;
  grid-template-columns: 1fr clamp(360px, 28vw, 460px);
  align-items: start;
  padding: 0 var(--gutter);
}

/* min-width:0. Without it a grid item can't shrink below its content's
   natural width, so one long unbreakable value (a table row, a bar-list
   name) forces the whole page wider instead of wrapping locally. */
.workspace > #board,
.workspace > .stats-board {
  min-width: 0;
}

/* Container, not just a grid item: #board's own width is what the
   filter bar actually has to fit in, and that's decoupled from the
   viewport once the two-column workspace grid is active (Market Stats'
   clamp(360px, 28vw, 460px) column eats a variable, viewport-independent
   chunk of the row). A viewport-width media query for the filter-wrap
   breakpoint below got this wrong: it never fired above 640px viewport
   width even though #board itself could be narrower than that, cutting
   the filter row off instead of wrapping it. See the @container rule in
   the responsive section for the actual fix. */
#board { container-type: inline-size; }

/* No border at all, per request -- the muted row separators inside
   #board are the only divider system now; nothing artificially walls
   the board off from Market Stats beside it. Padding stays, for
   breathing room between the two columns' content. */
.workspace > #board {
  padding-right: var(--gutter);
}
.workspace > #board .container { padding: 0; }

.stats-board {
  position: sticky;
  top: 57px; /* topbar height + its 2px bottom rule */
  max-height: calc(100vh - 57px);
  overflow-y: auto;
  /* Right padding is half the left gutter, not zero. Without it the
     scrollbar (inside this box's own right edge) sits flush against the
     panel content with no breathing room. */
  padding: 12px calc(var(--gutter) / 2) 40px var(--gutter);
}

.stats-board .metrics-grid { grid-template-columns: repeat(2, 1fr); }
.stats-board .panel-grid { grid-template-columns: 1fr; }

/* Metrics grid */

/* Same shape as .panel-grid/.panel below: the black background is just
   the hairline showing through the gaps. The cells are the light
   surface, matching the (correctly white) panels beside them. */
.metrics-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: var(--rule);
  /* --grey-line, not --black -- reported live, the dividers read too
     bold/high-contrast (this is the same "quiet divider" token already
     used for table.jobs row separators, same reasoning: a full-strength
     ink line is a section-level rule, not what a dense grid of small
     tiles needs between every cell). */
  background: var(--grey-line);
  border: var(--rule) solid var(--grey-line);
  /* Scoped to just the Market Stats box, per request -- same 4px radius
     as the filter bar, top corners only (the seam it shares with
     panel-grid below stays square). overflow:hidden so the corner
     .metric-card's square white background actually follows the curve
     instead of poking a square corner past it. */
  border-radius: 4px 4px 0 0;
  overflow: hidden;
}

.metric-card {
  background: var(--white);
  color: var(--black);
  padding: 18px 16px;
  min-height: 116px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

.metric-card .label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--grey);
}

.metric-card .value {
  font-size: 30px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  margin-top: 10px;
  color: var(--black);
}

.metric-card.highlight .value { color: var(--green); }
.metric-card .sub {
  font-size: 11.5px;
  color: var(--grey);
  margin-top: 4px;
}
/* API Status card only, third line -- same size/color as .sub above,
   just a second one, so it introduces no new type-ramp step.
   tabular-nums keeps the countdown's digits from jittering in width
   as it ticks down every second. */
.metric-card .sync-countdown {
  font-size: 11.5px;
  color: var(--grey);
  margin-top: 2px;
  font-variant-numeric: tabular-nums;
}

/* Market-insight panels: who's hiring, what for, where. No ATS-vendor
   breakdown here; that's plumbing, not a market signal. */

.panel-grid {
  margin-top: calc(var(--rule) * -1); /* overlaps metrics-grid's bottom border into one shared line, no visible gap */
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--rule);
  background: var(--grey-line); /* same reasoning as metrics-grid above */
  border: var(--rule) solid var(--grey-line);
  /* Bottom corners only -- see metrics-grid above, this is the other
     half of the same visually-seamless box. */
  border-radius: 0 0 4px 4px;
  overflow: hidden;
}

.panel {
  background: var(--white);
  padding: 18px 20px 20px;
}

.panel-title {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--grey);
  margin-bottom: 12px;
  font-weight: 700;
}

.bar-row {
  display: grid;
  grid-template-columns: 1fr 100px 44px;
  align-items: center;
  gap: 12px;
  font-size: 12.5px;
  padding: 6px 0;
}
.bar-row .name {
  min-width: 0; /* the ellipsis below can't engage without this */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bar-row .bar-track { background: var(--grey-line); height: 9px; position: relative; }
.bar-row .bar-fill { background: var(--black); height: 100%; }
.bar-row:first-child .bar-fill { background: var(--green); }
.bar-row .n { text-align: right; font-variant-numeric: tabular-nums; }
.bar-row.clickable { cursor: pointer; }
.bar-row.clickable .name:hover { color: var(--green); text-decoration: underline; }

.trend-chart { width: 100%; height: 64px; display: block; overflow: visible; }

/* Shared by every trend line: a single smooth stroke, no fill, no
   per-point markers. .closed overrides the color for the second line
   sharing a chart (new-listings dark, closed the signal green). */
.trend-line { fill: none; stroke: var(--black); stroke-width: 1.5; }
.trend-line.closed { stroke: var(--green); }

.trend-legend {
  display: flex;
  gap: 14px;
  font-size: 10.5px;
  color: var(--grey);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-top: 6px;
}
.trend-legend .dot { display: inline-block; width: 7px; height: 7px; margin-right: 4px; }
.trend-legend .dot.new { background: var(--black); }
.trend-legend .dot.closed { background: var(--green); }

.trend-axis {
  display: flex;
  justify-content: space-between;
  font-size: 10.5px;
  color: var(--grey);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-top: 4px;
}

/* One big headline number, for a panel whose whole point is a single
   stat (the dormant-listing rate), not a list of several. */
.ghost-pct {
  font-size: 34px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.ghost-sub { font-size: 11.5px; color: var(--grey); margin-top: 8px; }

@media (max-width: 720px) {
  .panel-grid { grid-template-columns: 1fr; }
}

/* Job board */

/* The list and its detail panel, side by side. Same fixed-width-sidebar
   idiom as .workspace (board/stats). .job-detail starts [hidden] in
   index.html; app.js clears that on first selection. Never a drawer or
   modal, laid out in-flow like everything else in this system. */
.board-split {
  display: flex;
  align-items: flex-start;
  gap: var(--gutter);
}
.board-list { flex: 1 1 0; min-width: 0; }

/* 4px radius, per request -- same scoped exception as the filter bar
   and Market Stats box (see DESIGN.md's Shapes section), extended to
   every standalone floating/bordered container. 20px padding already
   keeps content clear of the corners, so no overflow:hidden needed. */
.job-detail {
  flex: 0 0 clamp(320px, 34%, 420px);
  min-width: 0;
  position: sticky;
  top: 77px; /* topbar (56px content + 2px rule) + a little breathing room, so it doesn't sit flush under the sticky header */
  max-height: calc(100vh - 97px);
  overflow-y: auto;
  border: var(--rule) solid var(--black);
  border-radius: 4px;
  padding: 20px;
}
.job-detail[hidden] { display: none; }

.job-detail-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}
.job-detail-header-actions { display: flex; align-items: flex-start; gap: 8px; flex: 0 0 auto; }
.job-detail-icon-btn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: var(--rule) solid var(--black);
  border-radius: 4px;
  background: var(--white);
  color: var(--black);
  width: 28px;
  height: 28px;
  font-size: 13px;
  cursor: pointer;
}
.job-detail-icon-btn:hover { background: var(--black); color: var(--white); }
/* The "Copied" text feedback (see copyToClipboard) doesn't fit a fixed
   28px square -- briefly relaxes to a small pill instead of clipping,
   then snaps back once the timeout restores the plain icon. */
.job-detail-icon-btn.copied {
  width: auto;
  padding: 0 8px;
  font-size: 11px;
  color: var(--green);
  border-color: var(--green);
  background: var(--white);
}

.job-detail-title {
  font-size: 17px;
  font-weight: 700;
  line-height: 1.3;
  margin: 2px 0 0;
  overflow-wrap: anywhere;
}
.job-detail-company {
  color: var(--grey);
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-top: 4px;
  overflow-wrap: anywhere;
}
.job-detail-badges { margin-top: 8px; display: flex; flex-wrap: wrap; gap: 6px; }

.job-detail-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  margin: 16px 0;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--black);
}
.job-detail-apply {
  display: inline-flex;
  align-items: center;
  height: 38px;
  padding: 0 16px;
  background: var(--green);
  color: var(--white);
  border: var(--rule) solid var(--green);
  border-radius: 4px;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  text-decoration: none;
  white-space: nowrap;
}
.job-detail-apply:hover { background: var(--black); border-color: var(--black); }
.job-detail-star {
  border: var(--rule) solid var(--black);
  border-radius: 4px;
  background: var(--white);
  height: 38px;
  padding: 0 14px;
  font-size: 13px;
  cursor: pointer;
  white-space: nowrap;
}
.job-detail-star.on { color: var(--green); border-color: var(--green); }
.job-detail-star:hover { border-color: var(--green); color: var(--green); }

.job-detail-meta { display: flex; flex-direction: column; gap: 10px; margin-bottom: 20px; }
.job-detail-meta-row { display: flex; gap: 10px; font-size: 12.5px; }
.job-detail-meta-row .label {
  flex: 0 0 92px;
  color: var(--grey);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 10.5px;
  padding-top: 1px;
}
.job-detail-meta-row .value { overflow-wrap: anywhere; }

.job-detail-description-title {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--grey);
  font-weight: 700;
  margin-bottom: 10px;
}
.job-detail-description {
  font-size: 12.5px;
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.job-detail-description.empty { color: var(--grey); font-style: normal; }
.job-detail-desc-heading {
  display: block;
  font-weight: 700;
  text-transform: uppercase;
  font-size: 10px;
  letter-spacing: 0.08em;
  color: var(--grey);
  margin-top: 12px;
}

table.jobs tbody tr.selected { background: var(--hover-bg); }
table.jobs tbody tr[data-id] { cursor: pointer; }

@media (max-width: 1300px) {
  .board-split { flex-direction: column; }
  .job-detail {
    flex-basis: auto;
    width: 100%;
    position: static;
    max-height: none;
  }
}

/* Flex, not a fixed-column grid, so #f-q/#f-keywords/.ms can each take a
   different share of the row. Stays on one line (nowrap) down to 640px
   of #board's own width, then wraps -- see the @container rule below. */
.filters {
  display: flex;
  flex-wrap: nowrap;
  gap: 10px;
  margin-bottom: 20px;
}
.filters > *, .filters-sub > * { min-width: 0; } /* form controls have a real intrinsic width too */
#f-q { flex: 2 1 120px; }
#f-keywords { flex: 1 1 90px; }
.filters .ms { flex: 1 1 80px; }
.filters #f-date-posted,
.filters #f-sort { flex: 1 1 100px; }
.filters label.toggle,
.filters #f-reset { flex: 0 0 auto; }

/* A second flex row nested in the first, not a wrapper div that steps
   out of the layout -- visually just more items in .filters's own row
   above the @container breakpoint below, real box only needed so the
   breakpoint has something to collapse. */
.filters-sub { display: flex; flex: 1 1 auto; flex-wrap: nowrap; gap: 10px; }
.filters-toggle { display: none; }

/* Scoped to just the filter bar, per request -- a small radius and a
   1px (not --rule's 2px) border here only, not a system-wide change to
   DESIGN.md's rounded:none. Everything outside .filters (buttons,
   badges, chips, cards, panels) stays sharp-cornered. */
.filters input[type="text"],
.filters select {
  border: 1.5px solid var(--black);
  border-radius: 4px;
  background: var(--white);
  padding: 9px 10px;
  height: 38px;
  width: 100%;
}
.filters input[type="text"]:focus,
.filters select:focus { outline: 2px solid var(--green); outline-offset: -2px; }

/* The native select arrow sits right against the border by default --
   reported live as too tight. appearance:none drops it in favor of a
   hand-drawn chevron with real breathing room from the edge.
   Reported live: currentColor inside a *referenced* SVG (a data URI
   used as background-image, as opposed to an inline <svg> in the DOM)
   doesn't reliably inherit the element's CSS color -- it fell back to
   black, invisible against dark mode's near-black box fill. Hardcoded
   per theme instead, same hex as --black's two definitions above (a
   data URI can't read a custom property, so these two have to be kept
   in sync with those by hand if that token ever changes). */
.filters select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  padding-right: 30px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%2340513b' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 10px 6px;
}
:root[data-theme="dark"] .filters select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23ededec' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* Multi-select filter. A native <select multiple> needs ctrl/cmd-click
   to pick more than one, not discoverable, so this is a checkbox-dropdown. */
.ms { position: relative; }
.ms-toggle {
  width: 100%;
  height: 38px;
  padding: 9px 10px;
  border: 1.5px solid var(--black);
  border-radius: 4px;
  background: var(--white);
  color: var(--black);
  font-family: var(--font);
  font-size: 13px;
  text-align: left;
  cursor: pointer;
  /* A long label otherwise wraps and spills past this fixed-height box.
     Truncate with an ellipsis instead, same reasoning as .bar-row .name. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ms-toggle.active { border-color: var(--green); color: var(--green); font-weight: 700; }
.ms-toggle:focus { outline: 2px solid var(--green); outline-offset: -2px; }

/* 4px radius -- same scoped exception as .job-detail above. */
.ms-menu {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  width: max(100%, 220px);
  max-height: 320px;
  overflow-y: auto;
  background: var(--white);
  border: var(--rule) solid var(--black);
  border-radius: 4px;
  z-index: 40;
  padding: 8px;
}

.ms-search {
  width: 100%;
  height: 32px;
  padding: 6px 8px;
  margin-bottom: 6px;
  border: 1px solid var(--grey-line);
  border-radius: 4px;
  background: var(--white);
  color: var(--black);
  font-family: var(--font);
  font-size: 12px;
}

.ms-pinned { font-weight: 700; }
.ms-pinned-divider { border-top: 1px solid var(--grey-line); margin: 4px 0; }

.ms-options { display: flex; flex-direction: column; }
.ms-option {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 4px;
  font-size: 12.5px;
  cursor: pointer;
}
.ms-option:hover { background: var(--hover-bg); }
.ms-option input { accent-color: var(--green); flex-shrink: 0; }
.ms-empty { padding: 10px 4px; color: var(--grey); font-size: 12px; }

.ms-clear {
  width: 100%;
  margin-top: 6px;
  padding: 6px;
  border: 1px solid var(--grey-line);
  border-radius: 4px;
  background: none;
  color: var(--grey);
  font-family: var(--font);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  cursor: pointer;
}
.ms-clear:hover { border-color: var(--black); color: var(--black); }

/* Sentence case, not uppercase -- DESIGN.md's own type ramp already
   classifies filters as Body voice, not Label voice (uppercase/tracked
   is for column headers, panel titles, chip text); this was a real drift
   from that, not a documented exception. font-family/size fall through
   from body's var(--font), same as table.jobs' row text -- no separate
   override needed, the shouty look was purely the uppercase+tracking. */
.filters label.toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  border: 1.5px solid var(--black);
  border-radius: 4px;
  padding: 0 10px;
  height: 38px;
  font-size: 12px;
  cursor: pointer;
}
.filters label.toggle input { accent-color: var(--green); }
/* #f-reset (Reset), not .btn generally -- .btn is used well outside the
   filter bar (Create Alert, Sign Out, etc.), which stays sharp-cornered
   and keeps its uppercase/bold Label-style treatment. This one drops
   back to sentence case/regular weight, same reasoning as label.toggle
   above -- it's still `.btn.ghost` otherwise (border, hover, colors). */
#f-reset {
  border-width: 1.5px;
  border-radius: 4px;
  text-transform: none;
  letter-spacing: normal;
  font-weight: 400;
}

.btn {
  border: var(--rule) solid var(--black);
  border-radius: 4px;
  background: var(--black);
  color: var(--white);
  height: 38px;
  padding: 0 16px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 700;
  cursor: pointer;
}
.btn:hover { background: var(--green); border-color: var(--green); }
.btn.ghost { background: var(--white); color: var(--black); }
.btn.ghost:hover { background: var(--black); color: var(--white); border-color: var(--black); }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--green);
  color: var(--white);
  border: var(--rule) solid var(--green);
  border-radius: 4px;
  font-family: var(--font);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 6px 10px;
  margin-bottom: 12px;
  cursor: pointer;
}
.chip:hover { background: var(--black); border-color: var(--black); }

.result-count {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--grey);
  margin-bottom: 12px;
}
.result-count b { color: var(--black); }

/* #board's content padding leaves a gutter-wide gap between the table's
   own right edge and the divider. Widen by --gutter and pull back with a
   matching negative margin to bleed the table out to the true box edge;
   the last column gets that width back as padding to keep its own inset. */
table.jobs {
  width: calc(100% + var(--gutter));
  margin-right: calc(-1 * var(--gutter));
  border-collapse: collapse;
  font-size: 13px;
}
table.jobs thead th {
  text-align: left;
  text-transform: uppercase;
  font-size: 10.5px;
  letter-spacing: 0.08em;
  color: var(--grey);
  border-bottom: var(--rule) solid var(--black);
  padding: 0 10px 10px;
  user-select: none;
  white-space: nowrap;
}
table.jobs thead th:last-child,
table.jobs tbody td:last-child {
  padding-right: calc(10px + var(--gutter));
}
/* Only sortable columns get the pointer cursor. */
table.jobs thead th[data-sort] { cursor: pointer; }
table.jobs thead th[data-sort]:hover { color: var(--black); }
table.jobs thead th.active { color: var(--black); }
table.jobs thead th.active::after { content: attr(data-dir); margin-left: 4px; color: var(--green); }

/* --grey-line, not --black -- DESIGN.md's own color spec names "table
   row separators" as this token's job ("the quiet dividers"). The
   --black version read as a bold, high-contrast white line in dark
   mode against the near-black background, much louder than a 1px rule
   should be. */
table.jobs tbody tr { border-bottom: 1px solid var(--grey-line); }
table.jobs tbody tr:hover { background: var(--hover-bg); }
table.jobs tbody td { padding: 12px 10px; vertical-align: top; }

/* Card layout: logo, title/meta/actions, salary, skills -- four real
   grid columns, not flex items. Flexbox was tried through several
   rounds here (a fixed flex-basis, a centered auto-margin, a shared
   wrapping box) and kept drifting: flexbox is fundamentally a
   content-driven, one-axis model, so a flex item's own position still
   depends on its siblings' content length even with a fixed basis on
   itself. Grid's column tracks are genuinely independent of content --
   this is what actually guarantees the same x-position on every row
   regardless of title/company/location length or how many skills a
   given job has. 40px matches the logo; 1fr lets the card body absorb
   all the variable slack; salary and skills get fixed percentages of
   the cell's own width (15%/30%) rather than pixel widths, so they
   still scale sanely across viewport widths. Auto-placement fills the
   columns in DOM order (logo, body, salary, skills) below -- no
   explicit grid-column needed as long as that order holds.
   min-width:0 on the body is load-bearing (DESIGN.md's own rule):
   without it a long unbroken domain/title stops the grid item from
   ever shrinking below its content width. */
/* Third column (was 15%/30% split for salary+skills) is just salary now
   -- skills pulled from the UI per request, kept in the codebase but
   unused (see app.js). 20% gives it a bit more room now it's not
   sharing the row with a second column.
   First column is 70px, wider than the 40px logo itself (see
   .company-logo.listing's justify-self:center below) -- reported live,
   a column sized exactly to the logo left it sitting flush against the
   star with all the breathing room dumped on the far side, in front of
   the title text instead. */
/* height:100% -- reported live, the logo's align-self:center (see
   .company-logo.listing below) wasn't reliably centering against the
   row's actual rendered height. A <td> with display:grid is unusual:
   it still participates in normal table row-height layout (sized to
   match its siblings/row), but that outer box and the grid's own
   content-driven height are two different things unless the grid is
   told explicitly to fill the box it's given -- without this, align-
   self only had the grid's auto height to center within, which isn't
   necessarily the same as the row's full height. */
table.jobs .title-cell {
  display: grid;
  grid-template-columns: 70px 1fr 20%;
  column-gap: 12px;
  align-items: start;
  height: 100%;
}
.job-card-body { min-width: 0; }

/* overflow-wrap:anywhere. A company domain has no spaces to break at,
   and some job titles arrive as one long hyphenated/slashed word too. */
table.jobs .title-cell a {
  font-weight: 600;
  text-decoration: none;
  color: var(--black);
  overflow-wrap: anywhere;
}
table.jobs .title-cell a:hover { color: var(--green); }
/* Sentence case, not the system's usual uppercase Label treatment --
   deliberately closer to a normal reading line (company/location names
   read oddly shouted in all-caps) than to a table-header-style tag. */
.job-meta { color: var(--grey); font-size: 12px; margin-top: 2px; overflow-wrap: anywhere; }

/* Salary (real disclosed or an estimate, see app.js's jobSalaryHtml)
   plus skill chips -- confirmed live: plain text, no border/background
   at all, same weight as .job-meta, not a boxed/badge treatment. Two
   genuinely separate grid columns now (see .title-cell above), each
   with its own top padding so both start level with .job-meta,
   regardless of how many lines a wide skills column ends up wrapping to
   -- a row with more skills is legitimately taller than one without any,
   same as a row with a longer title already was; that's content-driven
   height, not a bug. */
.job-salary-col,
.job-skills-col {
  padding-top: 20px; /* clears .job-card-title's line so this starts level with .job-meta */
  font-size: 12px;
}
.job-skills-col {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  gap: 4px 10px;
}
.job-salary { color: var(--black); font-weight: 700; }
/* Filled green box per a live reference (a rounded pill on another
   board) -- reusing .chip's solid-green/paper-text treatment. Still
   visually distinct from a real disclosed figure (plain bold black, no
   box) -- deliberately bold/eye-catching regardless, so the rows that DO
   have a number are easy to spot scanning down a column mostly full of
   "Undisclosed." */
.job-salary.estimate {
  display: inline-block;
  background: var(--green);
  color: var(--white);
  border-radius: 4px;
  font-weight: 700;
  padding: 2px 8px;
}
.job-salary.undisclosed { color: var(--grey); font-weight: 400; }
.skill-chip {
  background: none;
  border: none;
  padding: 0;
  color: var(--grey);
  font-family: var(--font);
  font-size: 12px;
  cursor: pointer;
}
.skill-chip:hover { color: var(--green); text-decoration: underline; }
/* + (adjacent sibling), not :not(:last-child) -- .skill-bracket's "]"
   is the column's real last child now (see jobSkillsHtml), so a
   last-child check would put a stray "·" on every chip, right before
   the bracket too. This instead marks every chip preceded by another
   chip specifically, which the bracket spans never are. */
.skill-chip + .skill-chip::before { content: "·"; margin-right: 10px; color: var(--grey-line); }
/* Groups the whole skill list visually, per a live request -- the
   brackets sit right against the first/last chip (no gap token
   consumed for them) via negative margins, rather than reading as
   their own separately-spaced list items. */
.skill-bracket { color: var(--grey); }
.skill-bracket:first-child { margin-right: -6px; }
.skill-bracket:last-child { margin-left: -6px; }

/* Google's favicon endpoint (see app.js's companyLogoUrl) -- no border
   radius, matching DESIGN.md's rounded:none everywhere else in this
   Swiss-grid layout, not a soft app-icon treatment. object-fit:contain
   keeps a non-square favicon from stretching. */
.company-logo {
  width: 16px;
  height: 16px;
  object-fit: contain;
  vertical-align: -3px;
  margin-right: 6px;
  flex-shrink: 0;
}
/* The row's own logo is a flex sibling of .job-card-body (gap above
   handles spacing), not inline text -- sized up to actually read as a
   company mark, the way it does on LinkedIn's job cards. */
/* align-self overrides .title-cell's own align-items:start (grid,
   see above) just for the logo -- everything else in the row still
   top-aligns, but the logo centers against the row's full height,
   which varies with how much the card body wraps to. */
/* Asymmetric on purpose: confirmed via DevTools' own box-model panel
   that 15px/15px (the geometric center of the 70px column) was
   landing correctly, but the star-side path has an extra ~8px the
   text-side doesn't (the star cell's own padding stacks with the
   title cell's), so equal margins here still read visually off-center
   against the row as a whole. 11px/19px compensates for that -- both
   real gaps (star-to-logo, logo-to-text) come out to ~31px. */
.company-logo.listing { width: 40px; height: 40px; margin: 0 19px 0 11px; align-self: center; }
.job-detail-company .company-logo.detail { width: 32px; height: 32px; vertical-align: -8px; margin-right: 8px; }
.bar-row .company-logo { width: 14px; height: 14px; vertical-align: -2px; margin-right: 5px; }

.job-links {
  display: flex;
  align-items: center;
  flex-wrap: wrap; /* individual links can drop to a new row if truly out of width; their own text never should (see white-space: nowrap below) */
  gap: 12px;
  margin-top: 8px;
}

/* The one action this row exists for, green per rule 2 above. Filled,
   not just colored text, so it reads as the row's one button, not a
   same-weight sibling to Save link. Hover inverts to ink, like .chip/.btn. */
.job-links a.apply-link {
  display: inline-flex;
  align-items: center;
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--white);
  background: var(--green);
  border: var(--rule) solid var(--green);
  border-radius: 4px;
  padding: 4px 10px;
  text-decoration: none;
  white-space: nowrap;
}
.job-links a.apply-link:hover { background: var(--black); border-color: var(--black); }

.copy-link-btn {
  font-size: 10.5px;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--grey);
  text-decoration: none;
  border: none;
  border-bottom: 1px solid var(--grey-line);
  background: none;
  padding: 0;
  cursor: pointer;
  white-space: nowrap;
}
.copy-link-btn:hover { color: var(--black); border-color: var(--black); }
.copy-link-btn.copied { color: var(--green); font-weight: 700; border-color: var(--green); }

.badge {
  display: inline-block;
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  border: 1px solid var(--black);
  border-radius: 4px;
  padding: 2px 6px;
  font-weight: 700;
}
.badge.best-effort { border-color: var(--grey); color: var(--grey); }
.title-cell .badge { margin-left: 6px; vertical-align: middle; }

/* Reported live: left-aligned (the table default), a short "Age"
   header sitting above widely variable-width durations ("41M" vs "3H
   58M") reads as off-center even though the text edges genuinely do
   line up -- the column's visual weight drifts right of the header as
   values get longer. Centering both keeps the header sitting where the
   eye actually expects the numbers to cluster. Reset back to left
   inside the mobile stacked-card breakpoint below, where every other
   field (location, salary, ...) stays left-aligned next to its own
   label -- centering just this one there would look inconsistent, not
   fixed. */
table.jobs thead th[data-sort="age"] { text-align: center; }
.age-cell { text-align: center; font-variant-numeric: tabular-nums; white-space: nowrap; }
.age-cell.fresh { color: var(--green); font-weight: 700; }

.star-btn {
  border: none;
  background: none;
  cursor: pointer;
  font-size: 15px;
  color: var(--black);
  padding: 0;
  line-height: 1;
}
.star-btn.on { color: var(--green); }
.star-btn:hover { color: var(--green); }

.pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap; /* page buttons + result-count text won't both fit one row on a phone */
  gap: 10px;
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--grey-line);
  font-size: 12px;
}
.pagination .pages { display: flex; gap: 6px; }
.pagination .pages button {
  border: 1px solid var(--black);
  border-radius: 4px;
  background: var(--white);
  width: 32px;
  height: 32px;
  cursor: pointer;
}
.pagination .pages button.active { background: var(--black); color: var(--white); }
.pagination .pages button:disabled { opacity: 0.3; cursor: default; }

.empty-state, .error-state, .loading-state {
  border: var(--rule) dashed var(--grey-line);
  border-radius: 4px;
  padding: 40px;
  text-align: center;
  color: var(--grey);
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.error-state { border-color: var(--red); color: var(--red); }

/* Footer */

footer {
  padding: 40px 0 60px;
}
footer h3 {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin: 0 0 16px;
}
/* min-width:0. A curl command or long param row is an unbreakable-width
   child that would otherwise force the whole footer wider than the
   viewport instead of scrolling locally. */
.api-endpoint {
  min-width: 0;
  padding: 16px 0;
  border-top: 1px solid var(--grey-line);
}
.api-endpoint:first-of-type { border-top: none; padding-top: 0; }
.api-endpoint-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
}
.api-method {
  flex: 0 0 auto;
  background: var(--black);
  color: var(--white);
  font-size: 10px;
  font-weight: bold;
  letter-spacing: 0.06em;
  padding: 2px 6px;
}
.api-path {
  font-family: "Courier New", monospace;
  font-size: 13px;
  font-weight: bold;
}
.api-endpoint > p {
  font-size: 12px;
  color: var(--grey);
  margin: 0 0 10px;
  max-width: 720px;
}
.api-params {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 10px;
  font-size: 12px;
}
.api-params td {
  padding: 4px 12px 4px 0;
  vertical-align: top;
  color: var(--grey);
}
.api-params td:first-child { white-space: nowrap; width: 1%; }
.api-params .param {
  font-family: "Courier New", monospace;
  font-size: 11.5px;
  font-weight: bold;
  color: var(--black);
}
/* Deliberately fixed, not var(--black)/var(--white). A terminal/code
   block stays dark regardless of the page's own light/dark state. */
footer code {
  display: block;
  background: #0a0a0a;
  color: #2fae60;
  text-shadow: 0 0 1px var(--white);
  padding: 10px 12px;
  font-size: 11.5px;
  margin-bottom: 8px;
  overflow-x: auto;
  white-space: pre;
}
footer p { color: var(--grey); margin: 0 0 8px; }
.footer-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  align-items: center;
  margin-top: 32px;
  padding-top: 16px;
  border-top: 1px solid var(--grey-line);
  color: var(--grey);
  font-size: 12.5px;
}

/* privacy.html only -- a plain reading column, not the board/stats
   two-up layout .workspace otherwise implies. */
.legal .container { max-width: 640px; padding-top: 32px; }
.legal h3 { font-size: 15px; margin: 28px 0 8px; }
.legal p { margin: 0 0 12px; }
.legal-updated { color: var(--grey); font-size: 12.5px; margin-top: -4px; }
.legal-back { margin-top: 32px; }

/* Responsive */

@media (max-width: 960px) {
  .workspace { grid-template-columns: 1fr; }
  .workspace > #board { border-right: none; padding-right: 0; }
  .stats-board {
    position: static;
    max-height: none;
    padding: 24px 0;
    border-top: var(--rule) solid var(--black);
    overflow-y: visible;
  }

  /* Reported live ("atrocious" on mobile): the stacked-inline job-detail
     panel from the 1300px rule above gets appended below the *entire*
     list in document flow, so opening one visibly jerks the whole page
     down -- app.js's manual scroll-to-panel was working around this,
     not fixing it. A fixed full-screen sheet instead: openJobDetail/
     closeJobDetail (app.js) toggle .open on the next frame and lock
     document.body's own scroll for as long as it's up, both gated to
     this exact width via MOBILE_DRAWER_QUERY. */
  .job-detail {
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100vh;
    max-height: none;
    z-index: 20; /* above .topbar's 10 */
    border-width: var(--rule) 0 0 0;
    border-radius: 0;
    background: var(--white);
    transform: translateY(100%);
    transition: transform 0.25s ease;
  }
  .job-detail.open { transform: translateY(0); }
  table.jobs thead { display: none; }
  /* Reset the desktop bleed (width:100%+gutter, negative margin) back to
     plain 100%, meaningless once every cell stacks as its own block. */
  table.jobs, table.jobs tbody, table.jobs tr, table.jobs td { display: block; width: 100%; margin-right: 0; }
  /* One class more specific than the blanket "table.jobs td" reset
     above, so this wins without !important -- logo-left/title-right
     stays a real card at phone width too, instead of the logo stacking
     above the text like every other cell here. Desktop's grid columns
     (see .title-cell above) are meaningless at phone width -- a 15%/30%
     split of ~350px leaves almost nothing to work with -- so this drops
     to a plain wrapping flex row instead: logo+body first, then
     salary/skills wrap to their own line below, sized by their own
     content rather than a fixed track. */
  table.jobs td.title-cell { display: flex; flex-wrap: wrap; }
  table.jobs .job-salary-col,
  table.jobs .job-skills-col { flex: 0 1 auto; padding-top: 4px; }
  table.jobs tr { padding: 10px 0; }
  /* Stacked cells need a label to tell them apart. app.js's data-label
     attribute stands in for the table header that display:none removed. */
  table.jobs td[data-label] { padding: 4px 10px; }
  /* The desktop-only centering above (see .age-cell) would put this
     one stacked field out of step with location/salary/etc., which all
     stay left-aligned next to their own label here. */
  .age-cell { text-align: left; }
  /* The desktop last-column padding (clears the divider) is meaningless
     stacked full-width, just an oversized gap under one value. */
  table.jobs tbody td:last-child { padding-right: 10px; }
  table.jobs td[data-label]::before {
    content: attr(data-label) ": ";
    color: var(--grey);
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
  }
}

/* #board's own width, not the viewport's -- the two-column workspace
   grid can leave #board narrower than a viewport-width breakpoint would
   assume (Market Stats' column eats a variable, viewport-independent
   share of the row), so this has to react to the container. See
   #board's container-type rule above.
   900px, not 640px: measured via headless-Chrome layout, not guessed --
   the unwrapped row's real minimum content width (every filter field
   shrunk as far as flex-basis/min-width:0 allow) comes out around
   810-860px with the current field count, so 640px left a real overflow
   gap between 640px and ~860px of #board width. */
@container (max-width: 900px) {
  .filters { flex-wrap: wrap; }
  .filters > * { flex-basis: 100%; } /* #f-q and .filters-toggle each get their own full-width row */

  /* Reported live ("atrocious" on mobile): every sub-filter wrapping
     onto its own row still left a tall wall of controls between the
     search box and the actual results. Collapsed behind a single
     Filters (N) trigger instead -- #filters-toggle's click handler in
     wireFilters() (app.js) flips .open, and updateFiltersToggleLabel()
     keeps the button's own count in sync every time loadJobs() runs. */
  .filters-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  .filters-sub {
    display: none;
    flex-direction: column;
    gap: 10px;
  }
  .filters-sub.open { display: flex; }
  /* Column flex now, not the row layout #f-keywords/.filters .ms/
     #f-date-posted/#f-sort's own flex-basis (90/80/100px) was sized for
     -- flex-basis there meant a row-axis *width*, so in a column flex
     container it became a *height* instead, rendering every field as an
     80-100px-tall box with visible empty space inside it. Reported live
     as "filters are very spread out." A single `.filters-sub > *`
     couldn't win that fight (one class' worth of specificity loses to
     an ID selector like #f-keywords or #f-date-posted, gap or no gap),
     so each one is named explicitly here to out-specificity its own
     row-layout rule rather than just adding another lower-specificity
     layer on top of it. */
  .filters-sub.open #f-keywords,
  .filters-sub.open .ms,
  .filters-sub.open #f-date-posted,
  .filters-sub.open #f-sort,
  .filters-sub.open label.toggle,
  .filters-sub.open #f-reset {
    flex: 0 0 auto;
    width: 100%;
  }
}

@media (max-width: 640px) {
  /* Wordmark + 4 nav items don't fit a 360-390px phone at full size.
     Tighten rather than let the topbar force wider than the viewport. */
  .topnav { gap: 14px; font-size: 10.5px; }
  .wordmark-logo { height: 28px; }
  .ticker-rule, .ticker { display: none; } /* topbar is already tight without a marquee too */
  .topbar .container { flex-wrap: wrap; row-gap: 8px; }
}
