/* ============================================================================
   opps-v2.css — the redesigned BROWSE view.

   Scoped entirely under `body.v2`, loaded after opps.css, and every rule here is
   additive: with the flag off (?v2=0) nothing in this file matches and the page
   is byte-for-byte the old one. That is deliberate — this is a large
   user-visible change and it has to be A/B-able and one-flag reversible.

   Design + reasoning: docs/opportunity-search-redesign-design.md
   Mockup:             mockups/opportunity-search-redesign-v1.html

   Tokens come from the platform (explorer/hitch-tokens.css via opps.css). The
   only new one is --accent-text, because #e8a13c as TEXT on the light parchment
   runs at 1.96:1 — unreadable in the default theme. Fills keep #e8a13c.

   That rule is why the verdict bands emit TWO inline variables rather than one:
   --vc is the band's fill (a card's top rule, a board dot, a left border) and
   --vct is the same band as type. They differ only for Watch, whose fill is the
   platform accent. Every rule that colours a WORD by band takes --vct; every
   rule that draws one takes --vc. New band, new decision — see V2_BANDS.
   ========================================================================== */
:root { --accent-text: #e8a13c; }
body.light { --accent-text: #8a5a10; }

/* the rail is gone — the filters moved inline and the results get the width */
body.v2 .workspace { display: block; }
body.v2 .rail { display: none; }
body.v2 .main { padding: 18px 26px 90px; max-width: 1560px; margin: 0 auto; }

/* ── BAND 1 · the answer ──────────────────────────────────────────────────
   The page's largest mark is the bid call, and every numeral in it is the
   bucket filter. It replaces the "Opportunities in your lane" slogan, the
   7-cell KPI grid, the AI-triage chip row and the duplicated result count. */
body.v2 .v2-answer {
  font-family: var(--serif); font-weight: 400; font-size: 3.2rem; line-height: .95;
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 0 4px; margin-bottom: 6px;
}
body.v2 .v2-vbtn {
  font: inherit; color: var(--ink-dim); display: inline-flex; align-items: baseline; gap: 9px;
  padding: 0 5px; border-radius: 6px; transition: .16s; white-space: nowrap;
  background: none; border: none; cursor: pointer;
}
body.v2 .v2-vbtn:hover { background: rgba(232,161,60,.10); }
body.v2 .v2-vbtn b { font-weight: 400; color: var(--vct, var(--vc, var(--ink))); font-variant-numeric: lining-nums tabular-nums; }
body.v2 .v2-vbtn span { font-size: 1.45rem; color: var(--ink-dim); }
body.v2 .v2-vbtn.on { background: rgba(232,161,60,.15); }
body.v2 .v2-vbtn.on span { color: var(--ink); }
/* the discard pile never gets headline scale */
body.v2 .v2-vbtn.mute b, body.v2 .v2-vbtn.mute span { font-size: 1.05rem; color: var(--ink-faint); }
body.v2 .v2-sep { color: var(--ink-faint); font-size: 1.45rem; padding: 0 2px; }
body.v2 .v2-of { color: var(--ink-faint); }
body.v2 .v2-of b { font-weight: 400; color: var(--ink-dim); font-variant-numeric: lining-nums tabular-nums; }
body.v2 .v2-head { display: flex; align-items: baseline; justify-content: space-between; gap: 22px; flex-wrap: wrap; }
body.v2 .v2-byline { font-family: var(--mono); font-size: .76rem; color: var(--ink-dim); text-align: right; }
body.v2 .v2-byline a { color: var(--accent-text); }
/* WHY THE PURSUE BUCKET IS EMPTY — one word in the byline, the paragraph behind
   an ⓘ. Printed in full it ran four mono lines at the top-right of the page and
   took roughly a third of the fold, shouting louder than the headline it was
   annotating. The glance-level fact is "Nothing to pursue"; the reasoning is a
   read-once, so it opens on demand and anchors to the byline's right edge. */
/* inline-block, not inline: the popover anchors to this box, and an inline box
   that wraps mid-phrase would anchor it to whichever fragment came first.
   (Named -whyi, not -why: .v2-why is the row-level why-line and clips.) */
body.v2 .v2-whyi { position: relative; display: inline-block; white-space: nowrap; }
body.v2 .v2-ibtn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px; margin-left: 5px; padding: 0; vertical-align: 1px;
  font-family: var(--serif); font-size: .68rem; font-style: italic; line-height: 1;
  color: var(--ink-faint); background: none; border: 1px solid var(--line);
  border-radius: 50%; cursor: pointer; transition: .16s;
}
body.v2 .v2-ibtn:hover, body.v2 .v2-ibtn[aria-expanded="true"] {
  border-color: var(--accent); color: var(--accent-text);
}
body.v2 .v2-nowhy {
  display: none; position: absolute; top: calc(100% + 8px); right: 0; z-index: 70;
  width: 330px; padding: 11px 13px; text-align: left; white-space: normal;
  background: var(--bg-soft); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 26px 60px -20px rgba(0,0,0,.55);
  font-family: var(--mono); font-size: .72rem; line-height: 1.55; color: var(--ink-dim);
}
body.v2 .v2-nowhy.open { display: block; }
body.v2 .v2-nowhy a { color: var(--accent-text); }
body.v2 .v2-nowhy .h {
  display: block; margin-bottom: 6px; font-size: .62rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink-faint);
}

/* ── BAND 2 · markets — the scope control AND the saved list, one row ───── */
body.v2 .v2-mkbar {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 20px 0 11px; padding-bottom: 11px; border-bottom: 1px solid var(--line-soft);
}
body.v2 .v2-mkl {
  font-family: var(--mono); font-size: .6rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-faint); margin-right: 3px;
}
body.v2 .v2-mktab {
  display: inline-flex; align-items: center; gap: 8px; font-size: .86rem; padding: 7px 13px;
  border-radius: 10px; border: 1px solid var(--line); background: var(--panel); color: var(--ink-dim);
  transition: .18s; white-space: nowrap; cursor: pointer;
}
body.v2 .v2-mktab:hover { border-color: var(--ink-dim); color: var(--ink); }
body.v2 .v2-mktab.on {
  color: var(--ink); border-color: rgba(232,161,60,.55);
  background: rgba(232,161,60,.09); box-shadow: inset 0 -2px 0 var(--accent);
}
body.v2 .v2-mktab .n { font-family: var(--mono); font-size: .68rem; color: var(--ink-faint); }
body.v2 .v2-mktab.on .n { color: var(--accent-text); }
body.v2 .v2-mktab .ed {
  font-size: .78rem; color: var(--ink-faint); padding: 0 1px; border-radius: 6px; transition: .16s;
}
body.v2 .v2-mktab .ed:hover { color: var(--accent-text); }

/* ── BAND 2.5 · the market editor — the definition, visible and editable ── */
body.v2 .v2-mked {
  border: 1px solid rgba(232,161,60,.35); border-radius: 12px; background: var(--panel);
  padding: 14px 16px 12px; margin: 0 0 14px;
}
body.v2 .v2-mked-head { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; margin-bottom: 10px; }
body.v2 .v2-mked-head b { font-size: .95rem; color: var(--ink); }
body.v2 .v2-mked-head .reach { font-family: var(--mono); font-size: .64rem; color: var(--ink-faint); }
body.v2 .v2-mkg { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; padding: 5px 0; }
body.v2 .v2-mkg .gl {
  font-family: var(--mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-faint); min-width: 118px; cursor: help;
}
body.v2 .v2-mkterm {
  display: inline-flex; align-items: center; gap: 6px; font-size: .8rem; padding: 4px 6px 4px 10px;
  border: 1px solid var(--line); border-radius: 100px; background: var(--bg); color: var(--ink);
}
body.v2 .v2-mkterm.hot { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(232,161,60,.14); }
body.v2 .v2-mkterm button {
  background: none; border: none; color: var(--ink-faint); font-size: .68rem; cursor: pointer; padding: 2px 4px;
}
body.v2 .v2-mkterm button:hover { color: var(--ink); }
body.v2 .v2-mkadd {
  background: none; border: 1px dashed var(--line); border-radius: 100px; outline: none;
  color: var(--ink); font-size: .78rem; padding: 4px 10px; width: 8.5em; transition: .18s;
}
body.v2 .v2-mkadd:focus { border-color: var(--accent); border-style: solid; width: 13em; }
body.v2 .v2-mkadd::placeholder { color: var(--ink-faint); }
body.v2 .v2-mked-foot { display: flex; align-items: center; gap: 10px; margin-top: 10px; }
body.v2 .v2-mked-foot .err { font-size: .78rem; color: #e26d5c; margin-right: auto; }
body.v2 .v2-mksave {
  font-family: var(--mono); font-size: .7rem; padding: 6px 14px; border-radius: 100px;
  border: 1px solid rgba(232,161,60,.5); background: rgba(232,161,60,.12); color: var(--accent-text);
  cursor: pointer; transition: .16s;
}
body.v2 .v2-mksave:hover { background: rgba(232,161,60,.22); }
body.v2 .v2-mksave[disabled] { opacity: .55; cursor: default; }
body.v2 .v2-mkcancel {
  font-family: var(--mono); font-size: .7rem; padding: 6px 12px; border-radius: 100px;
  border: 1px solid var(--line); background: none; color: var(--ink-dim); cursor: pointer;
}
body.v2 .v2-mkcancel:hover { color: var(--ink); }
/* the "market: term" hit is a BUTTON (it opens the editor) — reset the chrome
   so it sits in the sub-line like its .v2-found siblings, then say clickable */
body.v2 button.v2-found.mk {
  background: none; border: none; padding: 0; cursor: pointer;
  font-family: var(--mono); font-size: .62rem; letter-spacing: .05em;
  color: var(--ink-faint); text-transform: lowercase; transition: .16s;
}
body.v2 button.v2-found.mk:hover { color: var(--accent-text); text-decoration: underline; }

/* ── BAND 3 · the command bar ───────────────────────────────────────────── */
body.v2 .v2-cmd { display: flex; gap: 10px; align-items: flex-start; flex-wrap: wrap; margin-bottom: 12px; }
/* THE BOX IS NOT A FLEX ITEM OF THIS ROW — .v2-qfield is, and the box fills it.
   The help line under the box carries #v2-qpend, which echoes what you have
   typed ("Press Enter to search for “artificial intelligence”"). While the box
   and the help line were siblings on this row, that echo was content-sizing a
   flex item next to a flex:1 box, so every keystroke made the help wider and
   the box narrower — the box shrank 945px → 649px over the word "artificial
   intelligence", one letter at a time, under the user's cursor. Stacking the
   two inside one field means the box's width is the field's width and cannot
   depend on anything anyone types. */
body.v2 .v2-qfield { flex: 1; min-width: 340px; }
body.v2 .v2-qbox {
  display: flex; align-items: center; background: var(--panel);
  border: 1px solid var(--line); border-radius: 12px; padding: 0 14px; transition: .2s;
}
body.v2 .v2-qbox:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(232,161,60,.10); }
body.v2 .v2-qbox .ic { color: var(--ink-faint); font-size: 1rem; margin-right: 11px; }
body.v2 .v2-qbox input {
  flex: 1; min-width: 0; background: none; border: none; outline: none; padding: 14px 0;
  font-size: 1.02rem; font-family: var(--serif); color: var(--ink);
}
body.v2 .v2-qbox input::placeholder { color: var(--ink-faint); font-family: var(--serif); }
body.v2 .v2-qadd {
  display: inline-flex; align-items: center; gap: 6px; font-family: var(--mono); font-size: .68rem;
  padding: 5px 12px; border-radius: 100px; border: 1px solid rgba(232,161,60,.5);
  background: rgba(232,161,60,.10); color: var(--accent-text); white-space: nowrap;
  transition: .16s; margin-left: 8px; cursor: pointer;
}
body.v2 .v2-qadd:hover { background: rgba(232,161,60,.2); border-color: var(--accent); }
body.v2 .v2-qclr { color: var(--ink-faint); font-size: 1rem; padding: 0 4px; margin-left: 6px; background: none; border: none; cursor: pointer; }
body.v2 .v2-qclr:hover { color: var(--ink); }
/* RUN THE SEARCH. The box no longer filters as you type — a keystroke on a
   gov-wide corpus cost two full scans and the page stuttered mid-word — so the
   button that runs it is always on the box, and lights up the moment what you
   have typed differs from what the board is showing. */
body.v2 .v2-qgo {
  font-family: var(--mono); font-size: .68rem; letter-spacing: .04em;
  padding: 6px 14px; border-radius: 100px; border: 1px solid var(--line);
  background: none; color: var(--ink-dim); white-space: nowrap;
  transition: .16s; margin-left: 8px; cursor: pointer;
}
body.v2 .v2-qgo:hover { color: var(--ink); border-color: var(--ink-faint); }
body.v2 .v2-qgo.dirty {
  background: var(--accent); border-color: var(--accent); color: #1a1206; font-weight: 500;
}
body.v2 .v2-qgo.dirty:hover { filter: brightness(1.08); }
/* …but NOT across the run it started. `transition` above is unqualified, so it
   covers the background too, and a colour transition is driven by the main
   thread — which v2Busy then hands to the scan. The only frame that gets
   painted is the transition's first, so the button held its "not run yet" gold
   for the entire search it had just run. It goes quiet the instant the scan
   starts, and keeps its fade-in for the other direction. */
body.v2.v2-busy .v2-qgo { transition: none; }
body.v2 .v2-qgo:disabled { opacity: .4; cursor: default; }
body.v2 .v2-qgo:disabled:hover { color: var(--ink-dim); border-color: var(--line); }
body.v2 .v2-kbd {
  font-family: var(--mono); font-size: .62rem; color: var(--ink-faint); border: 1px solid var(--line);
  border-radius: 5px; padding: 2px 6px; margin-left: 8px;
}

/* ── BAND 4 · filter chips + pills ──────────────────────────────────────── */
body.v2 .v2-filters { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
body.v2 .v2-fchip {
  position: relative; display: inline-flex; align-items: center; gap: 8px; font-size: .85rem;
  padding: 8px 13px; border: 1px solid var(--line); border-radius: 10px; background: var(--panel);
  color: var(--ink-dim); transition: .18s; white-space: nowrap; cursor: pointer;
}
body.v2 .v2-fchip:hover { border-color: var(--ink-dim); color: var(--ink); }
/* Open / All — a segmented toggle rather than a chip, because it is not one
   filter among ten: it decides whether the board is showing work you can still
   act on. Reads as a switch, sits first, and never opens a popover. */
body.v2 .v2-seg {
  display: inline-flex; align-items: stretch; border: 1px solid var(--line);
  border-radius: 10px; overflow: hidden; background: var(--panel);
}
body.v2 .v2-seg button {
  font-family: var(--mono); font-size: .68rem; letter-spacing: .1em; text-transform: uppercase;
  padding: 9px 15px; border: 0; background: transparent; color: var(--ink-faint);
  cursor: pointer; transition: .18s;
}
body.v2 .v2-seg button:hover { color: var(--ink); }
body.v2 .v2-seg button.on { background: rgba(232,161,60,.14); color: var(--accent-text); }
body.v2 .v2-seg button + button { border-left: 1px solid var(--line); }
body.v2 .v2-fchip .fl {
  font-family: var(--mono); font-size: .64rem; letter-spacing: .13em; text-transform: uppercase; color: var(--ink-faint);
}
body.v2 .v2-fchip .fv { color: var(--ink); }
body.v2 .v2-fchip .car { font-size: .6rem; color: var(--ink-faint); }
body.v2 .v2-fchip.set { border-color: rgba(232,161,60,.5); background: rgba(232,161,60,.07); color: var(--ink); }
body.v2 .v2-fchip.set .fl { color: var(--accent-text); }
body.v2 .v2-fchip.open { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(232,161,60,.10); }
body.v2 .v2-fchip .badge {
  font-family: var(--mono); font-size: .62rem; background: var(--accent); color: #1a1206;
  border-radius: 100px; padding: 1px 6px;
}
/* the overflow fold — one button, one shelf, used by BOTH bars (markets and
   filters). It looks like a chip because it behaves like one: click, a panel
   drops, the things inside it are the same controls that were on the row. */
body.v2 .v2-fold {
  position: relative; display: inline-flex; align-items: center; gap: 8px; font-size: .85rem;
  padding: 8px 13px; border: 1px dashed var(--line); border-radius: 10px; background: var(--panel);
  color: var(--ink-dim); transition: .18s; white-space: nowrap; cursor: pointer;
}
body.v2 .v2-fold:hover { border-color: var(--ink-dim); color: var(--ink); }
body.v2 .v2-fold.open { border-style: solid; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(232,161,60,.10); }
/* the phone form: the count and the caret, once its own name is what is
   standing between the row and one line */
body.v2 .v2-fold.tight .fl { display: none; }
body.v2 .v2-fold .fl {
  font-family: var(--mono); font-size: .64rem; letter-spacing: .13em; text-transform: uppercase; color: var(--ink-faint);
}
body.v2 .v2-fold .fv { font-family: var(--mono); font-size: .72rem; color: var(--ink); }
body.v2 .v2-fold .car { font-size: .6rem; color: var(--ink-faint); }
body.v2 .v2-fold .badge {
  font-family: var(--mono); font-size: .62rem; background: var(--accent); color: #1a1206;
  border-radius: 100px; padding: 1px 6px;
}
body.v2 .v2-shelf {
  display: none; position: absolute; top: calc(100% + 7px); right: 0; z-index: 60;
  width: max-content; max-width: min(660px, 88vw);
  background: var(--bg-soft); border: 1px solid var(--line); border-radius: 12px; padding: 10px;
  box-shadow: 0 26px 60px -20px rgba(0,0,0,.55); cursor: default;
}
body.v2 .v2-fold.open .v2-shelf { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }

body.v2 .v2-clrall {
  font-family: var(--mono); font-size: .66rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-faint); padding: 6px 10px; border-radius: 8px; background: none; border: none;
  cursor: pointer; transition: .18s;
}
body.v2 .v2-clrall:hover { color: var(--red); }

/* the popover. white-space:normal is load-bearing — .v2-fchip sets nowrap so the
   chip label never breaks, and every wrapping line inside would inherit it. */
body.v2 .v2-pop {
  position: absolute; top: calc(100% + 7px); left: 0; z-index: 70; width: 320px;
  background: var(--bg-soft); border: 1px solid var(--line); border-radius: 12px; padding: 12px;
  box-shadow: 0 26px 60px -20px rgba(0,0,0,.55); white-space: normal; text-align: left;
  font-weight: 300; cursor: default;
  /* the rail's fixed-position placement (v2PopFitFixed, app.js) caps a short
     viewport's popover with an inline max-height — and this box had no
     `overflow` of its own, so a card taller than that cap kept painting its
     tail (the Clear/Done footer, the last couple of options) PAST its own
     background, transparent, straight over whatever the results table drew
     at that screen position: "clash of the cards" all over again, but as
     bleed-through text instead of a merged edge. flex + overflow:hidden
     confines every child to the capped box; .v2-opts is the one flexible
     child (min-height:0 below), so it is what shrinks and scrolls while the
     header, search box and footer stay fully painted and always reachable. */
  display: flex; flex-direction: column; overflow: hidden;
}
body.v2 .v2-pop.wide { width: 392px; }

/* ── A FLOATING PANEL HAS TO BE A DIFFERENT SURFACE FROM THE ONE UNDER IT ───
   Reported from the product as "clash of the cards": the Source popover open
   over the results read as merged into them. Measured in a real browser
   (2026-08-26), the popover's own background against the results card it
   floats over:

       dark    rgb(16,18,22)   vs rgb(16,18,22)    1.00:1   — the SAME colour
       light   rgb(255,253,248) vs rgb(251,248,241) 1.04:1

   So in both themes the only thing separating a floating panel from the panel
   beneath it is its border and its shadow, and in light both are nearly
   invisible on paper: a 15%-alpha hairline and a shadow at .30 alpha spread
   -22px. Dark got away with it because its shadow is .55 alpha over a much
   darker ground; light did not.

   ⚠ THE FIX IS NOT A DIFFERENT BACKGROUND COLOUR. Every candidate is within a
   percent or two of --panel in one theme or the other — the palette is
   deliberately a narrow set of near-neighbours — so a lift big enough to read
   would have to leave the palette. THE EDGE is what carries elevation, and it
   is sized to the 3:1 that WCAG 1.4.11 asks of a non-text boundary rather than
   to taste. Composited against the results card behind it:

       light   border .15 -> 1.37:1     border .50 -> 3.45:1
       dark    border .10 -> 1.28:1     border .40 -> 3.53:1

   Under it, a tight CONTACT shadow — the near-black hairline a lifted card
   casts where it meets what is beneath — which separates the panel from a
   surface of any colour, and an ambient shadow that is actually visible on
   paper (the old one was .30 alpha spread -22px, i.e. nothing).

   One rule for every floating panel on this board, so the next one added
   inherits it instead of copying three lines and dropping half of them. */
body.v2 .v2-pop,
body.v2 .v2-nowhy,
body.v2 .v2-shelf {
  border-color: rgba(244,241,234,.40);
  box-shadow:
    0 1px 3px rgba(0,0,0,.6),
    0 28px 62px -18px rgba(0,0,0,.72);
}
body.light.v2 .v2-pop,
body.light.v2 .v2-nowhy,
body.light.v2 .v2-shelf {
  border-color: rgba(26,18,6,.50);
  box-shadow:
    0 2px 6px -1px rgba(26,18,6,.22),
    0 20px 46px -14px rgba(26,18,6,.34);
}

body.v2 .v2-pop.right { left: auto; right: 0; }
body.v2 .v2-pop-h {
  font-family: var(--mono); font-size: .62rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-faint); margin: 2px 2px 9px; display: flex; align-items: center;
  flex: none;
}
body.v2 .v2-pop-h .r { margin-left: auto; color: var(--accent-text); letter-spacing: .06em; }
body.v2 .v2-pop-search {
  width: 100%; background: var(--field, var(--panel2)); border: 1px solid var(--line); border-radius: 8px;
  padding: 8px 11px; font-size: .85rem; outline: none; margin-bottom: 8px; color: var(--ink);
  flex: none;
}
body.v2 .v2-pop-search:focus { border-color: var(--accent); }
body.v2 .v2-opts {
  max-height: 332px; overflow-y: auto; display: flex; flex-direction: column; gap: 1px; margin: 0 -4px;
  /* the ONE child allowed to shrink inside .v2-pop's flex column — without
     min-height:0 a flex item defaults to its content's height as a FLOOR, so
     the parent's overflow:hidden could not shrink this to fit a capped
     popover and the whole point of making it the flexible child would be
     silently defeated. flex:1 lets it fill whatever the header/search/
     footer leave; the max-height above still bounds the uncapped case. */
  flex: 1 1 auto; min-height: 0;
}
body.v2 .v2-opt {
  display: flex; align-items: center; gap: 10px; padding: 7px 9px; border-radius: 8px; font-size: .86rem;
  color: var(--ink-dim); transition: .13s; text-align: left; width: 100%; background: none; border: none; cursor: pointer;
}
body.v2 .v2-opt:hover { background: var(--panel2); color: var(--ink); }
body.v2 .v2-opt .bx {
  width: 15px; height: 15px; border: 1px solid var(--line); border-radius: 4px; display: grid;
  place-items: center; font-size: .6rem; color: transparent; flex: 0 0 auto; transition: .15s;
}
body.v2 .v2-opt.on .bx { background: var(--accent); border-color: var(--accent); color: #1a1206; }
body.v2 .v2-opt.on { color: var(--ink); }
body.v2 .v2-opt .lab { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.v2 .v2-opt .lab .sub { font-family: var(--mono); font-size: .66rem; color: var(--ink-faint); display: block; }
/* a consortium portal or forecast portal, listed under its group line in the Source picker */
body.v2 .v2-opt.kid { padding-left: 22px; }
body.v2 .v2-opt .cn { font-family: var(--mono); font-size: .7rem; color: var(--ink-faint); }
/* the verdict popover's own row color (v2OptRow's dotCss param) — every
   other .v2-opt list is plain, so this is additive and only ever appears
   when a caller passes one. */
body.v2 .v2-vdot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
/* "What's new" is the one popover whose options come in two groups — the same
   four windows asked of the posted date and of the last activity — and without
   a divider the eight rows read as one ladder with four duplicate labels. Not a
   heading in the .v2-pop-h sense: it names a GROUP inside the list, so it sits
   in the scroller with the rows rather than pinned above them. */
body.v2 .v2-opt-sec {
  font-family: var(--mono); font-size: .62rem; letter-spacing: .09em; text-transform: uppercase;
  color: var(--ink-faint); padding: 9px 9px 4px; border-top: 1px solid var(--line-soft); margin-top: 3px;
}
body.v2 .v2-opts .v2-opt-sec:first-child { border-top: 0; margin-top: 0; padding-top: 2px; }
body.v2 .v2-pop-foot {
  display: flex; gap: 8px; align-items: center; margin-top: 10px; padding-top: 9px;
  border-top: 1px solid var(--line-soft);
  flex: none;
}
body.v2 .v2-lk {
  font-family: var(--mono); font-size: .66rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-faint); background: none; border: none; cursor: pointer;
}
body.v2 .v2-lk:hover { color: var(--accent-text); }
body.v2 .v2-pop-foot .sp { margin-left: auto; }
/* the Value popover's free range: Min / Max boxes (in $M) under the bands, and
   the "hide ones with no estimate" switch — inside the band list, which is the
   popover's one scroller. */
body.v2 .v2-vr { flex: none; display: flex; flex-direction: column; gap: 1px; }
body.v2 .v2-vr-row { display: flex; align-items: center; gap: 6px; padding: 2px 5px 0; }
body.v2 .v2-vr-in {
  flex: 1 1 0; width: 0; min-width: 0; background: var(--field, var(--panel2)); border: 1px solid var(--line);
  border-radius: 8px; padding: 7px 9px; font-size: .85rem; color: var(--ink); outline: none;
}
body.v2 .v2-vr-in:focus { border-color: var(--accent); }
body.v2 .v2-vr-in.bad { border-color: var(--red); }
body.v2 .v2-vr-to { font-size: .75rem; color: var(--ink-faint); }
body.v2 .v2-vr-hint { font-family: var(--mono); font-size: .62rem; color: var(--ink-faint); padding: 5px 5px 4px; }
/* the Timing ladder is now the same checkbox list every other chip uses (.v2-opts
   + .v2-opt, whose .lab .sub carries the "next month" / "this quarter" line), so
   the bespoke .v2-ladder / .v2-lrow rules that lived here are gone. A radio-shaped
   row cannot state "everything except Closed", which is what the filter defaults
   to — the affordance had to match the model. */

body.v2 .v2-pills { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; margin: -2px 0 13px; }
body.v2 .v2-pill {
  display: inline-flex; align-items: center; gap: 8px; font-family: var(--mono); font-size: .71rem;
  padding: 4px 6px 4px 11px; border-radius: 100px; border: 1px solid rgba(232,161,60,.4);
  background: rgba(232,161,60,.08); color: var(--ink);
}
body.v2 .v2-pill i { font-style: normal; color: var(--ink-faint); text-transform: uppercase; letter-spacing: .1em; font-size: .62rem; }
body.v2 .v2-pill button {
  width: 15px; height: 15px; border-radius: 50%; display: grid; place-items: center; font-size: .7rem;
  color: var(--ink-faint); transition: .15s; background: none; border: none; cursor: pointer;
}
body.v2 .v2-pill button:hover { background: rgba(224,123,123,.18); color: var(--red); }

/* ── BAND 5 · results header ────────────────────────────────────────────── */
body.v2 .v2-rhead { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-bottom: 12px; }
body.v2 .v2-sortsel {
  display: flex; align-items: center; gap: 7px; font-family: var(--mono); font-size: .68rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-faint);
}
body.v2 .v2-sortsel select {
  background: var(--panel); border: 1px solid var(--line); border-radius: 8px; padding: 6px 9px;
  font-family: var(--mono); font-size: .7rem; color: var(--ink); outline: none; cursor: pointer;
  text-transform: none; letter-spacing: 0;
}
body.v2 .v2-views { display: flex; border: 1px solid var(--line); border-radius: 10px; overflow: hidden; background: var(--panel); }
body.v2 .v2-views button {
  padding: 7px 14px; font-family: var(--mono); font-size: .68rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-dim); border: none; border-right: 1px solid var(--line);
  transition: .18s; background: none; cursor: pointer;
}
body.v2 .v2-views button:last-child { border-right: none; }
body.v2 .v2-views button:hover { color: var(--ink); background: var(--panel2); }
/* the active view is an underline, not an amber fill — amber is reserved for
   "a control you have set", and a view is not a narrowing */
body.v2 .v2-views button.on { color: var(--ink); background: rgba(232,161,60,.09); box-shadow: inset 0 -2px 0 var(--accent); }
body.v2 .v2-tabtog {
  font-family: var(--mono); font-size: .66rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-faint); padding: 6px 10px; border-radius: 8px; background: none; border: none;
  cursor: pointer; transition: .16s; white-space: nowrap;
}
body.v2 .v2-tabtog:hover { color: var(--ink); }
body.v2 .v2-tabtog.on { color: var(--ink-dim); }
body.v2 .v2-rhead .sp { margin-left: auto; }

/* ── the bid call ───────────────────────────────────────────────────────── */
/* One shape for all four bands: the word, the score under it, nothing else.
   The word is sized to the row's own title (1.02rem) rather than shouting over
   it at 1.45 — the column is the first thing read on a row, it does not also
   have to be the biggest. Emphasis is carried by colour: a live call takes the
   band's own hue at full strength, a decided one (.q — Team on it / No bid)
   takes that hue mixed halfway into faint ink, so thirty-six rows still resolve
   into "these five need me" without two type systems fighting. */
body.v2 .v2-call { display: block; line-height: 1.3; }
body.v2 .v2-call .w {
  font-family: var(--serif); font-size: 1.02rem; color: var(--vct, var(--vc)); line-height: 1.3;
  text-transform: none; letter-spacing: 0; display: block; white-space: nowrap;
}
body.v2 .v2-call .sc {
  font-family: var(--mono); font-size: .62rem; color: var(--ink-faint); display: block;
  margin-top: 2px; font-variant-numeric: tabular-nums; white-space: nowrap;
}
/* decided rows: same box, muted ink. The band colour is mixed down rather than
   dropped — Team on it stays recognisably blue against No bid's grey, so the
   four verdicts remain scannable without the word competing with the live
   calls above it. */
body.v2 .v2-call.q .w { color: color-mix(in srgb, var(--vct, var(--vc)) 50%, var(--ink-faint)); font-size: .97rem; }
body.v2 .v2-call.q .sc { opacity: .78; }
/* the verdict column never reflows: it is as wide as its widest word ("Nothing
   open yet"; v2Call breaks "Can't prime —<br>set-aside" at its dash) and no wider, so the score always sits directly under the word instead
   of being pushed onto a line of its own. Section headers are exempt — they
   are a colspan row whose note is meant to wrap. */
body.v2 .v2-tbl .c-verdict { width: 1%; white-space: nowrap; }

/* ── TABLE ──────────────────────────────────────────────────────────────── */
/* overflow-x: auto, not hidden. The wrapper clips its corners either way; the
   difference is that when the picked columns are wider than the page the reader
   can scroll to the right-hand ones instead of losing Respond by and Est. value
   off the edge (which is what `hidden` did). */
body.v2 .v2-tbl { border: 1px solid var(--line); border-radius: 12px; overflow-x: auto; overflow-y: hidden; background: var(--panel); }
body.v2 .v2-tbl table { width: 100%; border-collapse: collapse; }

/* ── COLUMN WIDTHS — declared, one per column, whatever set is visible ─────
   Every th/td carries `c-<column id>` (app.js v2ColCls). Without these the
   browser's auto layout sized every column from the text on the page, so the
   same table changed shape from one column combination to the next and from
   one page of rows to the next: Buyer squeezed to one word per line, Description
   sat at the width of an em dash, and the title took whatever was left.
   The rule: Opportunity is the ONE elastic column and takes the surplus; every
   other column has a fixed width sized to its widest normal value; short
   values never wrap. `width` on a cell is a preferred width in auto layout,
   which is the intended behaviour — the column holds that width until the
   table runs out of room, and then the wrapper scrolls. */
body.v2 .v2-tbl .c-title { min-width: 320px; }
body.v2 .v2-tbl .c-buyer { width: 190px; min-width: 160px; }
body.v2 .v2-tbl .c-due { width: 104px; white-space: nowrap; }
body.v2 .v2-tbl .c-value { width: 108px; white-space: nowrap; }
body.v2 .v2-tbl .c-activity { width: 90px; white-space: nowrap; }
body.v2 .v2-tbl .c-noticetype { width: 130px; white-space: nowrap; }
body.v2 .v2-tbl .c-naics { width: 84px; white-space: nowrap; }
body.v2 .v2-tbl .c-psc { width: 72px; white-space: nowrap; }
body.v2 .v2-tbl .c-pop { width: 170px; }
body.v2 .v2-tbl .c-source { width: 120px; white-space: nowrap; }
body.v2 .v2-tbl .c-sol { width: 190px; white-space: nowrap; }
body.v2 .v2-tbl .c-incumbent { width: 170px; }
body.v2 .v2-tbl .c-notices { width: 110px; white-space: nowrap; }
body.v2 .v2-tbl .c-score { width: 84px; white-space: nowrap; }
body.v2 .v2-tbl .c-summary { width: 280px; min-width: 240px; }
body.v2 .v2-tbl .c-cf { width: 150px; }
body.v2 .v2-tbl .c-acts { width: 1%; white-space: nowrap; }
/* the buyer's own cap used to be the only thing bounding this column; the cell
   width above now does that, so the div just fills the cell */
body.v2 .v2-tbl thead th {
  font-family: var(--mono); font-size: .62rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-faint); text-align: left; padding: 11px 14px; background: var(--panel2);
  border-bottom: 1px solid var(--line); white-space: nowrap; font-weight: 400; cursor: pointer; user-select: none;
}
body.v2 .v2-tbl thead th:hover { color: var(--ink); }
body.v2 .v2-tbl thead th.on { color: var(--accent-text); }
body.v2 .v2-tbl thead th.r, body.v2 .v2-tbl tbody td.r { text-align: right; }
body.v2 .v2-tbl tbody td { padding: 12px 14px; border-bottom: 1px solid var(--line-soft); vertical-align: top; }
body.v2 .v2-tbl tbody tr { transition: background .13s; cursor: pointer; }
body.v2 .v2-tbl tbody tr:hover td { background: var(--row); }
body.v2 .v2-tbl tbody tr:last-child td { border-bottom: none; }
/* ── result sections: Direct matches / Inside larger opportunities ────────
   (opps-clin-extraction-design §6a decision 1). The hidden section takes the
   blue of the Team verdict band it produces, so the same signal is the same
   colour wherever the user meets it. Rendered only when both sets exist. */
body.v2 .v2-sec .sech { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 10px; }
body.v2 .v2-tbl .v2-sec td {
  padding: 13px 14px 9px; background: var(--panel2); border-bottom: 1px solid var(--line);
}
body.v2 .v2-tbl .v2-sec { cursor: default; }
body.v2 .v2-tbl .v2-sec:hover td { background: var(--panel2); }
body.v2 .v2-cards .v2-sec, body.v2 > .v2-sec { margin: 0; }
body.v2 .v2-sec + .v2-cards { margin-top: 9px; }
body.v2 .v2-sec ~ .v2-sec { margin-top: 22px; }
body.v2 .v2-sec .sl {
  font-family: var(--mono); font-size: .62rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent-text);
}
body.v2 .v2-sec.sub .sl { color: var(--blue); }
/* not a match claim in either direction — these rows are context, and the
   header should not borrow the gold that means "your market" */
body.v2 .v2-sec.out .sl { color: var(--ink-faint); }
body.v2 .v2-sec .sn {
  font-family: var(--mono); font-size: .6rem; color: var(--ink-faint);
  border: 1px solid var(--line); border-radius: 100px; padding: .05rem .45rem;
}
body.v2 .v2-sec .sd { font-size: .76rem; color: var(--ink-faint); line-height: 1.4; }
/* a hidden-match row is a real result, not a dimmed one — it just isn't the
   prime bid the rows above it are, and the section header already said so */
body.v2 .v2-tbl tbody tr.v2-hidrow td:first-child { box-shadow: inset 2px 0 0 var(--blue); }

/* 92ch is about what the Opportunity column holds at the widest layout, and it
   is stated in characters because a title is bounded in characters, not pixels
   (extract_facts asks the model for <=90; build_recompetes.title_for cuts the
   award description at 120). It was 560px — roughly 66 characters — which made
   the CSS, not the producer, the thing that decided where a title wrapped: a
   90-character title took two lines inside an ~800px column with room to spare.
   The cap is here to bound the column, NOT to wrap: the raw SAM title is the
   fallback when there is no clean_title and nothing bounds its length, so
   uncapped it would take the auto layout's whole surplus and squeeze Buyer to
   its min-content. A title longer than the column still wraps, and should — at
   this width no value here can hold 120 characters on one line. */
body.v2 .v2-title { font-size: 1.02rem; font-weight: 500; line-height: 1.32; color: var(--ink); max-width: 92ch; }
body.v2 .v2-title a { color: inherit; text-decoration: none; border-bottom: 1px solid transparent; }
body.v2 .v2-title a:hover { color: var(--accent-text); border-bottom-color: rgba(232,161,60,.5); }
/* the why-line — the most decision-useful string in the product, and in the old
   grid it was only reachable as a tooltip. It takes the whole cell. It used to
   carry the title's 560px measure, but every other column is pinned (the
   verdict shrinks to fit, Buyer caps at 180) so the auto layout hands this one
   ~800px — and the ellipsis was firing on the literal instead of on the space
   available, clipping "early enough to shape it" off a 105-character read with
   245px of the cell still empty to its right. No measure cap, because this is
   one sentence and not a paragraph; the title keeps its cap for that reason.

   IT MUST NOT BE `nowrap` WITHOUT A max-width. A nowrap block's MIN-content is
   its whole string — it cannot shrink — so the longest why-line on screen
   becomes the floor for this column, the table is forced past 100%, and
   .v2-tbl's `overflow: hidden` (which is there to clip the border-radius) eats
   Buyer, Respond by and Est. value off the right edge rather than scrolling to
   them. Shipped that way for one deploy on 2026-08-09: the ellipsis was kept
   "as a backstop" while the cap was removed, but with nothing constraining the
   box text-overflow never engages — it widens the column instead. The strings
   here have no length bound to lean on either: the hidden-match branch of
   v2Why builds one out of a CLIN description.

   So it WRAPS, and the measure only decides where. Min-content is a word, the
   column can always shrink, and a long why costs a second line instead of the
   right-hand half of the table. */
body.v2 .v2-why {
  font-size: .8rem; color: var(--ink-dim); line-height: 1.45; margin-top: 4px;
  max-width: 100ch;
}
body.v2 .v2-sub {
  font-family: var(--mono); font-size: .68rem; color: var(--ink-faint); margin-top: 5px;
  display: flex; gap: 9px; flex-wrap: wrap; align-items: center;
}
body.v2 .v2-sub .sol { color: var(--ink-dim); }
/* the set-aside slot. A named lane ("SDVOSBC") is a fact about who may bid and
   reads at the sub-line's own weight; a placeholder ("TBD", "TO BE DETERMINED")
   is the buyer saying they have not decided, and sitting at that same weight it
   read as a competition category — the reported bug. It stays visible, because
   blanking it would read as full-and-open, but it recedes: lower-cased so it
   stops shouting like a code, italic and faint so the eye skips it, and it says
   why on hover. */
body.v2 .v2-sa.tbd {
  color: var(--ink-faint); opacity: .62; font-style: italic;
  text-transform: lowercase; cursor: help;
}
body.v2 .v2-sa.tbd::before { content: "set-aside "; font-style: normal; }
body.v2 .v2-buyer { font-size: .85rem; color: var(--ink-dim); line-height: 1.35; }
body.v2 .v2-buyer i { font-style: normal; font-family: var(--mono); font-size: .66rem; color: var(--ink-faint); }
/* countdown first and graded. Red at 14 days, not 7 — at 7 only a handful of
   rows are ever marked, which is an easter egg rather than a signal. */
body.v2 .v2-due {
  font-family: var(--mono); font-size: 1rem; font-variant-numeric: tabular-nums; white-space: nowrap;
  line-height: 1.15; color: var(--ink-faint);
}
body.v2 .v2-due.soon { color: var(--ink); }
body.v2 .v2-due.hot { color: var(--red); }
body.v2 .v2-due .abs { display: block; font-size: .66rem; color: var(--ink-faint); margin-top: 3px; }
body.v2 .v2-val {
  font-family: var(--serif); font-size: 1.12rem; font-variant-numeric: lining-nums tabular-nums;
  white-space: nowrap; line-height: 1;
}
body.v2 .v2-none { color: var(--ink-faint); font-family: var(--mono); font-size: .76rem; }
/* "shared ceiling" beside a value that is a whole vehicle's ceiling (app.js
   valCeilTag). Unprefixed so the legacy table's value cell gets it too. */
.v2-ceil {
  font-family: var(--mono); font-size: .58rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-faint); margin-left: 5px; white-space: nowrap; cursor: help;
}
.v2-ceil.blk { display: block; margin: 4px 0 0; }
body.v2 .v2-flag {
  display: inline-flex; align-items: center; gap: 4px; font-family: var(--mono); font-size: .6rem;
  letter-spacing: .07em; text-transform: uppercase; color: var(--ink-faint);
  border: 1px solid var(--line); border-radius: 100px; padding: 2px 8px;
}
body.v2 .v2-flag.new { color: var(--green); border-color: rgba(107,191,89,.4); }
body.v2 .v2-flag.inc { color: var(--blue); border-color: rgba(111,176,232,.4); }
/* rows already decided stop competing — nothing hidden, they just quieten */
body.v2 .v2-tbl tbody tr[data-verdict=nobid] td { padding-top: 8px; padding-bottom: 8px; }
body.v2 .v2-tbl tbody tr[data-verdict=nobid] .v2-title { color: var(--ink-dim); font-size: .95rem; }
/* row actions: no resting chrome, words on hover, state as text */
body.v2 .v2-acts { display: flex; gap: 6px; justify-content: flex-end; opacity: 0; transition: opacity .14s; }
body.v2 .v2-tbl tbody tr:hover .v2-acts, body.v2 .v2-tbl tbody tr:focus-within .v2-acts { opacity: 1; }
body.v2 .v2-act {
  border: 1px solid var(--line); border-radius: 7px; padding: 4px 10px; font-family: var(--mono);
  font-size: .66rem; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-dim);
  transition: .16s; white-space: nowrap; background: none; cursor: pointer;
}
body.v2 .v2-act:hover { border-color: var(--ink-dim); color: var(--ink); }
body.v2 .v2-state {
  display: inline-flex; align-items: center; gap: 5px; font-family: var(--mono); font-size: .68rem;
  color: var(--ink-dim); white-space: nowrap; justify-content: flex-end;
}
body.v2 .v2-state .st { color: var(--ink-faint); }

/* ── CARDS — the read, where the table is the scan ──────────────────────── */
body.v2 .v2-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(360px, 1fr)); gap: 13px; }
body.v2 .v2-card {
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px; overflow: hidden;
  display: flex; flex-direction: column; transition: .2s; cursor: pointer;
  border-top: 2px solid var(--vc, var(--line));
}
body.v2 .v2-card:hover { border-color: var(--accent); transform: translateY(-2px); }
body.v2 .v2-card.quiet { border-top-color: var(--line); }
body.v2 .v2-card.quiet .v2-ctitle { color: var(--ink-dim); }
body.v2 .v2-cbody { padding: 14px 16px 0; flex: 1; display: flex; flex-direction: column; }
body.v2 .v2-ccall { display: flex; align-items: baseline; gap: 9px; margin-bottom: 7px; }
body.v2 .v2-ccall .w { font-family: var(--serif); font-size: 1.4rem; line-height: 1; color: var(--vct, var(--vc)); }
body.v2 .v2-ccall .sc { font-family: var(--mono); font-size: .64rem; color: var(--ink-faint); }
body.v2 .v2-ctitle { font-size: 1.02rem; font-weight: 500; line-height: 1.32; color: var(--ink); }
body.v2 .v2-ctitle a { color: inherit; text-decoration: none; }
body.v2 .v2-ctitle a:hover { color: var(--accent-text); }
body.v2 .v2-cbuyer { font-family: var(--mono); font-size: .72rem; color: var(--ink-dim); margin-top: 6px; }
body.v2 .v2-cbuyer i { font-style: normal; color: var(--ink-faint); }
/* the card's reason for existing: the why WRAPS here, where the table clips it */
body.v2 .v2-cwhy { font-size: .86rem; color: var(--ink-dim); line-height: 1.6; margin-top: 11px; }
body.v2 .v2-cinc { font-family: var(--mono); font-size: .7rem; color: var(--ink-dim); margin-top: 9px; }
body.v2 .v2-cinc span { color: var(--ink-faint); }
body.v2 .v2-csub {
  font-family: var(--mono); font-size: .68rem; color: var(--ink-faint); margin: 12px 0 14px;
  display: flex; gap: 9px; flex-wrap: wrap; align-items: center;
}
body.v2 .v2-csub .sol { color: var(--ink-dim); }
body.v2 .v2-cfacts { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: var(--line-soft); margin-top: auto; }
body.v2 .v2-cf { background: var(--panel2); padding: 10px 12px; }
body.v2 .v2-cf .fl {
  font-family: var(--mono); font-size: .56rem; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-faint);
}
body.v2 .v2-cf .fv {
  font-family: var(--mono); font-size: 1rem; font-variant-numeric: tabular-nums; margin-top: 4px;
  color: var(--ink-faint); line-height: 1.15;
}
body.v2 .v2-cf .fv.soon { color: var(--ink); }
body.v2 .v2-cf .fv.hot { color: var(--red); }
body.v2 .v2-cf .fv.money {
  font-family: var(--serif); font-size: 1.12rem; color: var(--ink); font-variant-numeric: lining-nums tabular-nums;
}
body.v2 .v2-cf .fv.none { color: var(--ink-faint); font-size: .76rem; }
body.v2 .v2-cf .fs { font-family: var(--mono); font-size: .64rem; color: var(--ink-faint); margin-top: 3px; }
body.v2 .v2-cfoot {
  display: flex; align-items: center; gap: 9px; padding: 10px 14px; border-top: 1px solid var(--line-soft);
}
body.v2 .v2-cfoot .sp { margin-left: auto; }
body.v2 .v2-cfoot .v2-acts { opacity: 0; }
body.v2 .v2-card:hover .v2-acts, body.v2 .v2-card:focus-within .v2-acts { opacity: 1; }
body.v2 .v2-open {
  background: transparent; color: var(--ink); border: 1px solid var(--line); border-radius: 100px;
  padding: 5px 12px; font-size: .78rem; cursor: pointer; transition: .2s; text-decoration: none;
}
body.v2 .v2-open:hover { border-color: var(--ink-dim); }

/* ── BOARD — short-term vs long-term, derived ───────────────────────────── */
body.v2 .v2-bbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: -2px 0 13px; }
body.v2 .v2-bbar .bl {
  font-family: var(--mono); font-size: .62rem; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-faint);
}
body.v2 .v2-bbar .hint { font-family: var(--mono); font-size: .66rem; color: var(--ink-faint); margin-left: auto; }
body.v2 .v2-board { display: flex; gap: 12px; overflow-x: auto; padding-bottom: 14px; align-items: flex-start; }
body.v2 .v2-bcol {
  flex: 1 0 288px; max-width: 380px; background: var(--bg-soft); border: 1px solid var(--line);
  border-radius: 12px; display: flex; flex-direction: column; max-height: calc(100vh - 190px);
}
body.v2 .v2-bcol.drop { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(232,161,60,.12); }
body.v2 .v2-bch {
  padding: 11px 13px; border-bottom: 1px solid var(--line-soft); position: sticky; top: 0;
  background: var(--bg-soft); border-radius: 12px 12px 0 0; z-index: 2;
}
body.v2 .v2-bch .bt {
  font-family: var(--mono); font-size: .64rem; letter-spacing: .13em; text-transform: uppercase;
  color: var(--ink-dim); display: flex; align-items: center; gap: 8px;
}
body.v2 .v2-bch .bt .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--vc, var(--accent)); }
body.v2 .v2-bch .bt .n {
  margin-left: auto; font-family: var(--serif); font-size: 1.15rem; color: var(--ink);
  letter-spacing: 0; font-variant-numeric: tabular-nums;
}
body.v2 .v2-bcb { padding: 9px; display: flex; flex-direction: column; gap: 8px; overflow-y: auto; }
body.v2 .v2-bcard {
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 11px 12px;
  cursor: grab; transition: .16s; border-left: 2px solid var(--vc, var(--line));
}
body.v2 .v2-bcard:hover { border-color: var(--accent); transform: translateY(-1px); }
body.v2 .v2-bcard.dragging { opacity: .4; }
body.v2 .v2-bcard .bk {
  font-family: var(--mono); font-size: .58rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-faint); display: flex; gap: 8px; align-items: center; margin-bottom: 5px;
}
body.v2 .v2-bcard .bti { font-size: .88rem; line-height: 1.32; color: var(--ink); }
body.v2 .v2-bcard .bti a { color: inherit; text-decoration: none; }
body.v2 .v2-bcard .bm {
  display: flex; align-items: center; gap: 9px; margin-top: 8px; font-family: var(--mono);
  font-size: .66rem; color: var(--ink-faint);
}
body.v2 .v2-bcard .bm .hot { color: var(--red); }
body.v2 .v2-bempty {
  padding: 16px 12px; font-family: var(--mono); font-size: .66rem; color: var(--ink-faint);
  text-align: center; line-height: 1.6;
}

/* ── empty + source line ────────────────────────────────────────────────── */
body.v2 .v2-empty { border: 1px dashed var(--line); border-radius: 12px; padding: 52px 26px; text-align: center; }
body.v2 .v2-empty .et { font-family: var(--serif); font-size: 1.5rem; margin-bottom: 7px; }
body.v2 .v2-empty .es { font-size: .86rem; color: var(--ink-dim); max-width: 46ch; margin: 0 auto 16px; line-height: 1.65; }

@media (max-width: 900px) {
  body.v2 .v2-answer { font-size: 2.1rem; }
  body.v2 .v2-vbtn span, body.v2 .v2-sep { font-size: 1rem; }
  body.v2 .v2-cards { grid-template-columns: 1fr; }
  body.v2 .main { padding: 14px 14px 60px; }
}
@media (prefers-reduced-motion: reduce) {
  body.v2 * { animation: none !important; transition: none !important; }
}

/* ── keyword box: label + operator help ─────────────────────────────────────
   The operators used to live only in the placeholder, so they disappeared the
   instant you typed — the one moment you could learn them was the one moment
   you weren't looking. */
body.v2 .v2-qlab {
  display: block; font-family: var(--mono); font-size: .6rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-faint); margin: 0 0 6px 2px;
}
body.v2 .v2-qhelp {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px;
  margin: 7px 2px 0; font-size: .74rem; color: var(--ink-faint);
}
body.v2 .v2-qhelp button {
  background: none; border: none; padding: 0; cursor: pointer; font: inherit;
  color: var(--ink-faint); border-bottom: 1px dotted var(--line); transition: .13s;
}
body.v2 .v2-qhelp button:hover { color: var(--accent-text); border-bottom-color: var(--accent); }
body.v2 .v2-qhelp b { font-family: var(--mono); font-weight: 500; color: var(--ink-dim); }
body.v2 .v2-qhelp button:hover b { color: var(--accent-text); }
/* the box and the board disagree, said on the line the eye is already on. It
   holds no space when empty, so the help row does not jump as you type — and
   it is sized from the space LEFT OVER rather than from its own text (basis 0,
   grow 1, ellipsis), so a long query cannot wrap this row to a second line and
   shove the results down. It only ever gets to say less, never to move
   anything. */
body.v2 .v2-qpend { display: none; color: var(--accent-text); }
body.v2 .v2-qpend.on {
  display: block; flex: 1 1 0; min-width: 0; text-align: right;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ── "Also search inside documents" ─────────────────────────────────────────
   Its own row under the box, not another item on .v2-qhelp: that row is
   instruction ("+word must appear") and this is a CONTROL that changes the
   answer. Mixing the two put a live switch among four things that only ever
   explain — the same reason #v2-qpend keeps its own line.

   The receipt sits beside it rather than under it, so ticking the box does not
   push the results band down by a line and then pull it back up when the
   answer lands. */
body.v2 .v2-qdocsrow {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 12px;
  margin: 7px 2px 0; font-size: .74rem;
}
/* v2DocsTogHtml() renders NOTHING off the fast board (there is no attachment
   text to search there), and an empty flex row still takes its 7px of margin —
   a gap under the keyword box on exactly the layout that has no control to
   explain it. */
body.v2 .v2-qdocsrow:empty { display: none; }
body.v2 .v2-qdocs {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  color: var(--ink-faint); transition: color .13s; user-select: none;
}
body.v2 .v2-qdocs:hover, body.v2 .v2-qdocs.on { color: var(--accent-text); }
body.v2 .v2-qdocs input { margin: 0; accent-color: var(--accent); cursor: pointer; }
/* The receipt only ever gets the space left over — a long "not searched
   inside documents: …" list must not wrap this row and shove the board. */
body.v2 .v2-qdocsr {
  flex: 1 1 0; min-width: 0; color: var(--ink-faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
body.v2 .v2-qdocsr.err { color: var(--warn, #c2703a); }

/* ── the scan is running ────────────────────────────────────────────────────
   The answer band's counts belong to the PREVIOUS search until this one lands
   — "0 of 0" left at full strength over an in-flight search is the page
   asserting a finding it has not made yet. Dim it, and let the orb in the
   results band be the live thing on screen. */
body.v2.v2-busy .v2-answer, body.v2.v2-busy .v2-byline { opacity: .35; transition: opacity .12s; }
body.v2.v2-busy .v2-answer button { pointer-events: none; }
/* the house loader (.ask-loading / .cap-orb / .ask-loadmsg / .ask-loadsub —
   global-search.css + opps.css), sized for the results band */
/* the count above the rows is a floor while the description sidecar is still
   downloading — amber, because it is a caveat on the answer, not provenance */
body.v2 .v2-partial {
  color: var(--accent-text); border: 1px dashed rgba(232,161,60,.4);
  border-radius: 8px; padding: 7px 11px; margin-bottom: 10px;
}
body.v2 .v2-searching { min-height: 300px; justify-content: center; gap: 12px; }
body.v2 .v2-searching .ask-loadmsg { font-size: 1.24rem; }
body.v2 .v2-searching .ask-loadsub { color: var(--ink-faint); }

/* ── enrichment surfaces ────────────────────────────────────────────────────
   The eleven fields the redesign was ignoring, placed where they change a
   decision. Restrained on purpose: this page's original complaint was too many
   unlabelled marks, so each one is a word, not a glyph, and carries a title. */
/* --accent-text, not --amber: the latter is defined nowhere in the estate, so
   these two rules always took the hard-coded fallback and ran at 2.1:1 on the
   light theme — the same failure the Watch band had. Borders stay #e8a13c. */
body.v2 .v2-flag.amd  { color: var(--accent-text); border-color: rgba(232,161,60,.34); }
body.v2 .v2-flag.ota  { color: var(--blue, #7aa7d8); border-color: rgba(122,167,216,.34); }
/* a gate is a wall, not a score — it reads as a requirement */
body.v2 .v2-flag.gate {
  color: var(--red, #e0655f); border-color: rgba(224,101,95,.38); letter-spacing: .04em;
}
body.v2 .v2-flag.doc  { color: var(--ink-faint); }
body.v2 .v2-flag.doc.warn { color: var(--accent-text); border-color: rgba(232,161,60,.3); }

/* incumbent on the table sub-line */
body.v2 .v2-inc {
  color: var(--ink-dim); font-style: italic;
}
body.v2 .v2-inc::before { content: "vs "; font-style: normal; color: var(--ink-faint); }

/* the estimated RFP date, under an empty response date */
body.v2 .v2-none .abs.est {
  display: block; font-family: var(--mono); font-size: .64rem; color: var(--accent-text);
  margin-top: 2px; letter-spacing: .04em;
}

/* card: extracted summary + scope chips */
body.v2 .v2-csum {
  font-size: .82rem; line-height: 1.55; color: var(--ink-dim); margin: 8px 0 0;
}
body.v2 .v2-cchips { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 9px; }
body.v2 .v2-chip {
  font-size: .68rem; padding: 2px 8px; border-radius: 999px;
  border: 1px solid var(--line); color: var(--ink-faint); white-space: nowrap;
}
body.v2 .v2-chip.plat { color: var(--ink-dim); border-color: var(--line-soft); background: var(--panel2); }
body.v2 .v2-cinc i { font-family: var(--mono); font-size: .66rem; color: var(--ink-faint); font-style: normal; }

/* where the query landed. The peripheral variant is the one that matters — it
   means your words turned up inside somebody else's scope. */
body.v2 .v2-found {
  font-family: var(--mono); font-size: .62rem; letter-spacing: .05em;
  color: var(--ink-faint); text-transform: lowercase;
}
body.v2 .v2-found.per { color: var(--accent-text); }

/* ── passed work ────────────────────────────────────────────────────────────
   One click to pass, undo in place. No dialog and no reason box: this gets used
   while triaging a list, which is exactly when an interruption costs most. */
body.v2 .v2-pass {
  background: none; border: none; padding: 0 0 0 2px; cursor: pointer;
  font-family: var(--mono); font-size: .62rem; letter-spacing: .06em;
  color: transparent; transition: color .13s;
}
body.v2 .v2-row:hover .v2-pass, body.v2 .v2-card:hover .v2-pass { color: var(--ink-faint); }
body.v2 .v2-pass:hover { color: var(--red) !important; }
body.v2 .v2-pass.undo { color: var(--accent-text) !important; }
body.v2 .v2-passed {
  font-family: var(--mono); font-size: .62rem; letter-spacing: .05em; color: var(--ink-faint);
}
/* the toggle on the answer line */
body.v2 .v2-passtog {
  margin-left: 14px; background: none; border: 1px solid var(--line); border-radius: 999px;
  padding: 3px 11px; cursor: pointer; font-family: var(--mono); font-size: .62rem;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-faint); transition: .15s;
  vertical-align: middle;
}
body.v2 .v2-passtog:hover { color: var(--ink); border-color: var(--ink-faint); }
body.v2 .v2-passtog.on { color: var(--accent-text); border-color: var(--accent); }
body.v2 .v2-passtog b { font-weight: 500; }

/* ---- archive scope (the FY2015+ server search, reachable from v2 at last) ----
   The verdict band, byline and sort describe the loaded corpus; the archive is
   a different one, served a page at a time, pre-sorted server-side by last
   activity with nothing else in the grain to sort by — hidden, not
   re-labelled, since a count or an order that cannot be right does not belong
   on the page. Table and Cards DO apply (v2Hist adapts each row and paints it
   through the same v2Table/v2Cards this scope's live corpus uses); only Board
   groups by something an archived record doesn't have (a verdict, a pipeline
   stage), so it alone stays hidden here. */
body.archmode .v2-answer, body.archmode .v2-byline,
body.archmode .v2-sortsel { display: none; }
body.archmode #v2-views [data-v2view="board"] { display: none; }
body.v2 #v2-histmore { margin-top: 16px; }
body.v2 #v2-histn { margin-bottom: 10px; }

/* ── the hold, before the payload lands ────────────────────────────────────
   app.js renderDataPending. Each bar stands where a band of the real page will
   stand and is sized to it, so the page that arrives lands on top of its own
   outline instead of replacing a differently-shaped screen. The band sizes are
   read off .v2-answer (3.2rem/.95), .v2-qbox (14px padding + 1.02rem, plus
   the .v2-qhelp line stacked under it) and .v2-fchip (8px + .85rem) above —
   keep them in step. */
body.v2 .v2-skel { display: block; }
body.v2 .v2-skel .ln { display: block; }
/* the headline is a flex ITEM here, and its only child is the bar — without a
   basis it measures its (empty) content and the bar comes out 0px wide */
body.v2 .v2-skel .v2-answer { flex: 1 1 auto; min-width: 0; }
body.v2 .v2-skel .ln.hd { height: 3.05rem; width: min(620px, 100%); border: 0; border-radius: 10px; }
body.v2 .v2-skel .ln.mk { height: 32px; width: min(390px, 60%); margin: 16px 0 14px; border-radius: 10px; }
/* the BOX is 52px; the operator help stacked under it carries the rest of the
   band, and text that small is not worth a shimmer bar of its own — so the
   bar is the box's size and the help's 20px rides in the margin, which is
   what keeps every band below this one landing where it lands for real. */
body.v2 .v2-skel .ln.cmd { height: 52px; margin-bottom: 32px; border-radius: 12px; }
body.v2 .v2-skel .ln.fil { height: 36px; margin-bottom: 10px; border-radius: 10px; }
body.v2 .v2-skel .ln.fil2 { height: 36px; width: min(430px, 45%); margin-bottom: 16px; border-radius: 10px; }
body.v2 .v2-skel .ln.rh { height: 30px; width: min(520px, 55%); margin: 0 0 14px auto; border-radius: 10px; }
body.v2 .v2-skelrows { display: flex; flex-direction: column; gap: 9px; border-top: 1px solid var(--line); padding-top: 9px; }
body.v2 .v2-skelrows .ln.row { height: 52px; }
/* the rows fade out down the page: the ones you would read first are the ones
   that resolve first, and a full screen of identical shimmer reads as a wall */
body.v2 .v2-skelrows .ln.row:nth-child(n+4) { opacity: .6; }
body.v2 .v2-skelrows .ln.row:nth-child(n+7) { opacity: .3; }

/* ── the head-start table ──────────────────────────────────────────────────
   The preview wears the loaded table's own chrome (app.js headStartTable), so
   only these three rules differ — each one marking something the preview
   genuinely cannot do yet, rather than styling it differently for its own sake.

   The verdict bar is sized to .v2-call's two lines (a 1.02rem serif word over a
   .62rem mono score) so the column that never reflows does not reflow here
   either when the real call lands in it. */
body.v2 .hs-tbl .ln.call { height: 34px; width: 76px; border-radius: 8px; }
/* the header does not sort and the row does not open — only the title link in
   it does, and that page is server-rendered, so it works mid-download */
body.v2 .hs-tbl thead th { cursor: default; }
body.v2 .hs-tbl thead th:hover { color: var(--ink-faint); }
body.v2 .hs-tbl tbody tr { cursor: default; }

/* ── the head-start card and board ─────────────────────────────────────────
   Same principle: the loaded view's own chrome, and a rule here only where the
   preview cannot yet do what that chrome offers.

   The card's verdict line is a 1.4rem serif word; the bar takes its place so
   the body below it does not move when the word lands. A preview card has no
   band colour, so its top rule is the plain line rather than a --vc it would
   have to invent — and it does not lift on hover, because there is nothing to
   click but the two links inside it. */
body.v2 .hs-cards .v2-card { cursor: default; }
body.v2 .hs-cards .v2-card:hover { border-color: var(--line); transform: none; }
body.v2 .hs-cards .v2-ccall .ln.call { height: 22px; width: 118px; border-radius: 7px; }
/* the board's score sits at the end of the meta line; ~2 digits of mono */
body.v2 .hs-board .v2-bcard { cursor: default; }
body.v2 .hs-board .v2-bcard:hover { border-color: var(--line); transform: none; }
body.v2 .hs-board .v2-bcard .ln.sc { height: 13px; width: 26px; border-radius: 5px; }
/* the ungroupable groupings (Should we bid, Pipeline stage) — real columns,
   shimmer instead of cards filed on a claim the preview cannot make */
body.v2 .hs-board .ln.bcard { height: 74px; border-radius: 9px; }
body.v2 .hs-board .ln.bcard:nth-child(2) { opacity: .6; }
body.v2 .hs-board .ln.bcard:nth-child(3) { opacity: .3; }
/* the Group-by control is painted because it is part of the board's shape, and
   disabled because re-grouping needs rows that have not arrived */
body.v2 .hs-bbar .v2-views button[disabled] { cursor: default; opacity: .75; }

/* ── PAGES ─────────────────────────────────────────────────────────────────
   The board reads one page from the server in fast mode, so there is a page
   control — top AND bottom, because those answer different questions. At the
   top it says where you are before you start reading; at the bottom it is the
   next thing under your cursor when you finish, instead of a scroll back up.

   Deliberately quiet: this is navigation furniture around the rows, not a
   band competing with the masthead. The range reads first because "1–20 of
   30,003" is the fact; page N of M is the mechanism. */
body.v2 .v2-pager {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 2px; font-size: .8rem; color: var(--ink-dim);
}
body.v2 .v2-pager.top { border-bottom: 1px solid var(--line); margin-bottom: 10px; }
body.v2 .v2-pager.bottom { border-top: 1px solid var(--line); margin-top: 14px; }
body.v2 .v2-pager .sp { flex: 1; }
body.v2 .v2-pager .rng { font-variant-numeric: tabular-nums; }
body.v2 .v2-pager .rng b { color: var(--ink); font-weight: 600; }
/* the one count line's extra facts (documents, data as of) — quieter than the range */
body.v2 .v2-pager .v2-pinfo { margin-left: 8px; font-size: .85em; color: var(--ink-dim); white-space: nowrap; }
body.v2 .v2-pager .pn { font-variant-numeric: tabular-nums; min-width: 6ch; text-align: center; }
body.v2 .v2-psize { display: inline-flex; align-items: center; gap: 6px; }
body.v2 .v2-psize select {
  background: var(--bg-soft, transparent); color: var(--ink);
  border: 1px solid var(--line); border-radius: 7px; padding: 3px 6px;
  font: inherit; font-variant-numeric: tabular-nums;
}
body.v2 .v2-pbtn {
  background: none; color: var(--ink); border: 1px solid var(--line);
  border-radius: 7px; padding: 4px 11px; font: inherit; cursor: pointer;
}
body.v2 .v2-pbtn:hover:not([disabled]) { border-color: var(--accent); color: var(--accent); }
/* A disabled page button STAYS ON THE PAGE rather than disappearing at the
   ends: a control that vanishes takes the layout with it, and the row of
   numbers jumps as you reach the first or last page. */
body.v2 .v2-pbtn[disabled] { opacity: .38; cursor: default; }
body.v2 .v2-pbtn:focus-visible, body.v2 .v2-psize select:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
/* the masthead when there is no verdict tally to print — see v2FastAnswer */
body.v2 .v2-fastcount { font-family: var(--serif); }
body.v2 .v2-fastcount b { font-style: italic; color: var(--accent); }
body.v2 .v2-fasterr { color: var(--bad, #e0655f); }

@media (max-width: 720px) {
  /* the size control is a setting, and at this width the two page buttons and
     the range are what the reader is reaching for */
  body.v2 .v2-pager .v2-psize { display: none; }
}

/* ── THE COMPANY'S OWN FIELDS AS TABLE COLUMNS (migration 0102) ────────────
   The one thing that separates these cells from every other column on this
   table: their value is not on the row. It comes from the company's own
   workspace record, so the header carries a mark saying whose answer it is —
   a column headed "Internal priority" sitting between Buyer and Respond by
   would otherwise read as something Hitch publishes about the notice, which
   is the one thing this feature must never be mistaken for.
   ⚠ NO SORT AFFORDANCE HERE. These headers carry no data-v2sort and must not
   grow a hover or a caret: the server pages the rows and the browser holds
   one page, so a sort would reorder a fraction of the board while looking
   like it answered the question. */
.v2-tbl th.cf { color: var(--accent-text); }
.v2-tbl th.cf::after { content: " \25CF"; font-size: 7px; vertical-align: middle; opacity: .75; }
.v2-tbl th.cf:hover { color: var(--accent-text); }
.v2-cf { font-size: .81rem; line-height: 1.42; }
.v2-cf .none { opacity: .32; }

/* ── what a keyword matched, under a row or inside a card (app.js v2MatchHtml;
   the budget Workstation's .ws-match strip, same look) ── */
body.v2 .v2-tbl tbody tr:has(+ tr.v2-mrow) td { border-bottom: none; padding-bottom: 6px; }
body.v2 .v2-tbl tbody tr.v2-mrow { cursor: default; }
body.v2 .v2-tbl tbody tr.v2-mrow:hover td { background: var(--row); }
body.v2 .v2-tbl tbody tr.v2-mrow td { padding-top: 0; }
body.v2 .v2-matches { display: flex; flex-direction: column; gap: 5px; max-width: 110ch; }
body.v2 .v2-card .v2-matches { margin-top: 9px; }
body.v2 .v2-match {
  border-left: 2px solid var(--accent); padding: .3rem .6rem; border-radius: 0 5px 5px 0;
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}
body.v2 .v2-match-w {
  display: block; font-size: .66rem; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--accent-text);
}
body.v2 .v2-match-f { text-transform: none; letter-spacing: 0; font-weight: 400; color: var(--ink-dim); }
body.v2 .v2-match-t { display: block; font-size: .84rem; line-height: 1.5; color: var(--ink-dim); margin-top: 2px; overflow-wrap: anywhere; }
body.v2 .v2-match-t mark {
  background: color-mix(in srgb, var(--accent) 28%, transparent); color: var(--ink);
  border-radius: 2px; padding: 0 1px;
}
body.v2 .v2-match-more { font-size: .74rem; color: var(--ink-faint); padding-left: .7rem;
  display: flex; flex-wrap: wrap; gap: 4px 10px; }
/* "Match in" (opportunities/docs/match-in.md): the strongest line, then where
   else it is, then the other keywords and the ones that were not found */
body.v2 .v2-match.doc { border-left-color: var(--ink-faint); background: color-mix(in srgb, var(--ink) 4%, transparent); }
body.v2 .v2-match.doc .v2-match-w { color: var(--ink-dim); }
body.v2 .v2-match-k, body.v2 .v2-match-x {
  text-transform: none; letter-spacing: 0; font-weight: 500; font-size: .68rem;
  border: 1px solid var(--line); border-radius: 3px; padding: 0 4px; margin-left: 2px; color: var(--ink-dim);
}
body.v2 .v2-match-also { display: block; margin-top: 4px; padding-top: 3px; font-size: .74rem;
  color: var(--ink-dim); border-top: 1px dashed color-mix(in srgb, var(--accent) 30%, transparent); }
body.v2 .v2-match-kw mark { background: color-mix(in srgb, var(--accent) 22%, transparent); color: var(--ink);
  border-radius: 2px; padding: 0 2px; }
body.v2 .v2-match-n { font-size: .72rem; color: var(--ink-dim); padding-left: .1rem; }
body.v2 .v2-match-n .miss { color: var(--ink-faint); }
/* ── THE TABLE'S ROW + EVIDENCE LINE (2026-10-30; app.js V2_COLS "title",
   v2EvidenceHtml). Every fact has one place: the why under the Fit grade,
   one quiet facts line under a two-line title, "amended" under the date, Pass
   and the stage in the actions column, and the keyword evidence as one line
   inside the grid — where | quote | keywords — with no box and no banner. ── */
body.v2 .v2-tbl .v2-fwhy {
  white-space: normal; width: 150px; margin-top: 6px; font-size: .74rem; line-height: 1.4;
  color: var(--ink-faint); display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
body.v2 .v2-tbl .v2-title { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
body.v2 .v2-new {
  font-family: var(--mono); font-size: .62rem; font-weight: 500; letter-spacing: .08em; text-transform: uppercase;
  color: var(--green); vertical-align: 2px; margin-right: 2px;
}
body.v2 .v2-tbl .v2-meta { gap: 3px 0; }
body.v2 .v2-tbl .v2-meta > :not(:first-child):not(.v2-flag)::before {
  content: "\00B7"; padding: 0 7px; color: var(--ink-faint); font-style: normal; font-weight: 400;
}
/* the undecided set-aside keeps its own "set-aside" word (.v2-sa.tbd) after the dot */
body.v2 .v2-tbl .v2-meta > .v2-sa.tbd:not(:first-child)::before { content: "\00B7\00A0\00A0 set-aside "; padding: 0 0 0 7px; }
body.v2 .v2-tbl .v2-meta > .v2-flag { margin-left: 8px; border-radius: 4px; }
body.v2 .v2-tbl .v2-meta > .v2-flag + .v2-flag { margin-left: 5px; }
body.v2 .v2-inc2 { color: var(--ink-dim); }
body.v2 .v2-inc2 .rc { font-weight: 600; color: var(--blue); }
body.v2 .v2-inc2 i { font-style: normal; color: var(--ink-faint); }
body.v2 .v2-inc2 .nm { letter-spacing: .02em; }
body.v2 .v2-docs.warn b { color: var(--accent-text); }
body.v2 .v2-tbl .v2-amd { display: block; margin-top: 4px; font-size: .74rem; color: var(--accent-text); }
body.v2 .v2-tbl .v2-stg { font-size: .74rem; color: var(--ink-dim); margin-top: 4px; }
body.v2 .v2-tbl .v2-tpass { margin-top: 4px; white-space: normal; max-width: 150px; margin-left: auto; }
/* the actions cell is a narrow right-aligned STACK, one item per line: the
   accent "★ Following", the pipeline stage, then Follow/Unfollow over Pass as
   matching pills of one width that show on row hover/focus.
   ⚠ NOT ONE LINE. #3005 (2026-11-01) put all four inline with `nowrap`; the
   cell then needed ~300px, the table outgrew the page, and the wrapper's
   horizontal scroll hid Pass (and half of Follow) off the right edge on a
   normal 1530px screen with the default columns. Stacked, the column is one
   pill wide (~90px). */
/* …and PINNED to the right edge. When the picked columns are wider than the
   screen the wrapper scrolls (see TABLE above); the actions stay in view rather
   than scrolling off with the last column. The cell needs an opaque background
   to cover what slides under it — --row is a translucent tint, so on hover it is
   layered over --panel instead of replacing it. */
body.v2 .v2-tbl th.c-acts, body.v2 .v2-tbl td.c-acts { position: sticky; right: 0; z-index: 1; }
body.v2 .v2-tbl td.c-acts { background: var(--panel); }
body.v2 .v2-tbl tbody tr:hover td.c-acts { background: linear-gradient(var(--row), var(--row)), var(--panel); }
body.v2 .v2-tbl td.c-acts > * { display: flex; align-items: center; justify-content: flex-end; margin: 6px 0 0; max-width: none; }
body.v2 .v2-tbl td.c-acts > :first-child { margin-top: 0; }
body.v2 .v2-tbl td.c-acts .v2-state { color: var(--accent-text); }
body.v2 .v2-tbl td.c-acts .v2-state .st { color: var(--accent-text); }
body.v2 .v2-tbl td.c-acts .v2-stg { white-space: normal; text-align: right; max-width: 120px; margin-left: auto; }
body.v2 .v2-tbl td.c-acts .v2-act,
body.v2 .v2-tbl td.c-acts .v2-pass { min-width: 84px; justify-content: center; text-align: center; box-sizing: border-box; }
body.v2 .v2-tbl td.c-acts .v2-passed { white-space: normal; text-align: right; max-width: 120px; }
body.v2 .v2-tbl td.c-acts .v2-pass {
  border: 1px solid transparent; border-radius: 7px; padding: 4px 10px; font-weight: 600;
  text-transform: capitalize; white-space: nowrap;
}
body.v2 .v2-tbl tbody tr:hover td.c-acts .v2-pass,
body.v2 .v2-tbl tbody tr:focus-within td.c-acts .v2-pass { color: var(--ink-dim); border-color: var(--line); }
body.v2 .v2-tbl td.c-acts .v2-pass:hover { color: var(--red) !important; border-color: var(--red); }
body.v2 .v2-tbl td.c-acts .v2-pass.undo { border-color: var(--accent); }
/* a quiet (no-bid) row's evidence line quietens with it */
body.v2 .v2-tbl tbody tr.v2-mrow[data-verdict=nobid] .v2-ev { opacity: .78; }
body.v2 .v2-tbl tbody tr:hover .v2-pass { color: var(--ink-faint); }
/* the evidence line belongs to the row above it: one hover, one block */
body.v2 .v2-tbl tbody tr[data-v2row]:hover + tr.v2-mrow td,
body.v2 .v2-tbl tbody tr[data-v2row]:has(+ tr.v2-mrow:hover) td { background: var(--row); }
body.v2 .v2-tbl tbody tr.v2-mrow td { padding-top: 2px; padding-bottom: 14px; }
body.v2 .v2-tbl .v2-ev-c1 { white-space: nowrap; }
body.v2 .v2-ev-n { font-size: .74rem; color: var(--ink-dim); font-variant-numeric: tabular-nums; }
body.v2 .v2-ev-n .dots { color: var(--accent-text); letter-spacing: 1px; }
body.v2 .v2-ev {
  display: grid; grid-template-columns: 150px minmax(0, 1fr) 210px; gap: 18px; align-items: start;
  max-width: 1200px; padding-top: 8px; border-top: 1px dashed var(--line-soft);
}
body.v2 .v2-ev.nokw { grid-template-columns: 150px minmax(0, 1fr); }
body.v2 .v2-ev .v2-match-w {
  display: block; font-size: .8rem; font-weight: 600; letter-spacing: 0; text-transform: none; color: var(--ink);
}
body.v2 .v2-ev-sub { display: block; font-size: .72rem; color: var(--ink-faint); margin-top: 2px; line-height: 1.35; }
body.v2 .v2-ev-body { min-width: 0; font-size: .84rem; line-height: 1.5; color: var(--ink-dim); }
body.v2 .v2-ev-file {
  display: block; font-weight: 500; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
body.v2 .v2-ev-q { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; }
body.v2 .v2-ev-q mark {
  background: color-mix(in srgb, var(--accent) 28%, transparent); color: var(--ink); border-radius: 2px; padding: 0 1px;
}
body.v2 .v2-ev-none { font-style: italic; color: var(--ink-faint); }
body.v2 .v2-ev-kw { display: flex; flex-direction: column; gap: 2px; font-size: .78rem; line-height: 1.4; min-width: 0; }
body.v2 .v2-ev-kw .hit { color: var(--ink); }
body.v2 .v2-ev-kw .hit i { font-style: normal; color: var(--ink-faint); }
body.v2 .v2-ev-kw .miss { color: var(--ink-faint); text-decoration: line-through; }
body.v2 .v2-ev-kw .more { color: var(--ink-faint); }
@media (max-width: 900px) {
  body.v2 .v2-ev, body.v2 .v2-ev.nokw { grid-template-columns: minmax(0, 1fr); gap: 6px; }
}
/* the Match in popover */
body.v2 .v2-kwin-pre { display: flex; flex-wrap: wrap; gap: 6px; padding: 4px 0 8px; }
body.v2 .v2-kwin-pre .v2-lk { border: 1px solid var(--line); border-radius: 999px; padding: 4px 10px; }
body.v2 .v2-kwin-note { font-size: .74rem; color: var(--ink-dim); padding: 8px 2px 2px; line-height: 1.45; }
body.v2 .v2-opt-sec .sub { font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--ink-faint); }
body.v2 .v2-kwin:empty { display: none; }
/* the Match in chip's own container (deliberately NOT .v2-filters, whose first
   instance on the page several probes measure): chip layout as in .v2-filters */
body.v2 .v2-kwin { display: flex; flex-wrap: wrap; gap: 6px; position: relative; }

/* The archive under the live results (app.js v2ArchTail, 2026-10-24): its own
   section after the live list; the top line stays out of the way until it has a count. */
body.v2 .v2-archtail { margin-top: 28px; }
body.v2 .v2-archtail .v2-archmore { margin-top: 12px; }
body.v2 .v2-archtop { margin-left: 12px; font-size: .85em; color: var(--ink-dim); }
body.v2 .v2-archtop:empty { display: none; }
/* Open/All (2026-10-26): under Open the archive's count is one link that switches to All */
body.v2 .v2-archtopline .v2-archtop { margin-left: 0; }
body.v2 .v2-archtopline:has(.v2-archtop:empty) { display: none; }
body.v2 .v2-archshow { background: none; border: 0; padding: 0; font: inherit; color: var(--accent-text);
  cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
body.v2 .v2-archshow:hover { text-decoration-thickness: 2px; }
