/* Moneyness — the venue panel (.vp).
 *
 * WHY THIS FILE WAS REWRITTEN (2026-08-07)
 *
 * detail.js was rebuilt onto the `.vp` namespace at 9a9e360 and this stylesheet
 * was not rebuilt with it. Measured live before writing a line of this file,
 * at 1440x900, four panels open:
 *
 *   derive/BTC   719px   callput/BTC  696px   aevo/ETH  696px   rysk/ETH  719px
 *   text nodes at 15px: 87 · at 34px: 1 (the venue NAME) · asset chip rows: 2
 *   first number appears 219px into the panel
 *
 * Every one of those numbers is the UA default. There were ZERO `.vp-` rules in
 * any stylesheet in the app — the panel was rendering unstyled, so the largest
 * type on it was a proper noun (from `html body h2{font-size:34px!important}` in
 * instrument.css) and every actual figure sat at body size. That is the same
 * defect §56 fixed on the overview, one surface across.
 *
 * So this file is the venue panel's half of the hero-number skeleton. The two
 * surfaces now teach the same reading habit:
 *
 *   bar      identity and state, one line, SMALL — you clicked it to get here
 *   hero     ONE number at display size: what a floor costs on this venue
 *   chips    which assets it carries — ONE row at every width, overflow behind +N
 *   toggles  expiry and distance-from-spot, directly under what they drive
 *   stats    label over number, flat, no boxes
 *   method   provenance and the vol history, last and collapsed
 *
 * "n/a IS NOT 0", at the one place it is easiest to break. Four of six venues
 * publish no fill feed at all. An absence never gets the treatment a value gets:
 *   .vp-na          hero word, set BACK — smaller than the figure, ink3, weight 400
 *   .vp-chip.st-unknown   dashed edge + a HATCHED mark, the coverage grid's own
 *                         vocabulary for "we cannot see", never a quiet blank
 *   .vp-st.unk      the stat's value in ink3 with a dashed rule under it, so it
 *                   cannot be scanned as a measured quantity
 *
 * SPECIFICITY, AND A CLAIM THAT WAS FALSE UNTIL IT WAS MEASURED.
 *
 * The note this file was written against said `.vp` sits "in a namespace
 * instrument.css does not reach". That is true of its CLASS rules and false of
 * its blunt layer. instrument.css §17 declares
 *
 *   body *:not(svg):not(svg *) { font-size: var(--t-sm) !important; … }
 *
 * which reaches every element the app will ever render, .vp included, and also
 * pins font-family, font-weight, letter-spacing, border-radius and box-shadow.
 * The first pass of this file set `.vp-num-n { font-size: var(--t-hero) }` with
 * no flag and measured 15px live — a class at (0,1,0) beats that selector's
 * (0,0,3) easily, but no specificity beats an !important with none of its own.
 *
 * So every one of those six properties carries !important below, and nothing
 * else does. That is not escalation, it is the contract instrument.css states
 * for itself: "the role rules below win it back, and they all carry a class, so
 * they beat this on specificity as well as order." A class plus the flag. The
 * six are listed here so a later reader can tell a deliberate flag from a
 * defensive one — anything outside that list with an !important is a bug.
 *
 * PALETTE: not touched. Light-vs-dark is an open owner decision, so nothing
 * below introduces a hex of its own; every colour is a token, and each fallback
 * is that token's existing value copied from style.css, never a new one.
 */

/* ---- shell --------------------------------------------------------------- */

#detail-root { display: block; }

.vp {
  display: flex;
  flex-direction: column;
  gap: 30px;
  max-width: 100%;
  min-width: 0;
}

.vp-loading {
  margin: 0;
  padding: 26px 2px;
  color: var(--ink3, #5f6873);
  font-size: var(--t-sm, 12px) !important;
}

/* ══════════════════════════════════════════════════════════════════════════
   1 — THE BAR. Identity is small now, and that is the point.

   The name was the single largest thing on the panel and it is the one thing
   the reader already knew: they pressed its row to get here. It is one line at
   data size carrying what the name cannot say — how this venue lets you get a
   price, what it settles on, whether it is answering us, and whether it
   publishes its fills.
   ══════════════════════════════════════════════════════════════════════════ */

.vp-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 12px;
  min-width: 0;
}

.vp-back,
.vp-x {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font: inherit;
  font-size: var(--t-xs, 10px) !important;
  letter-spacing: var(--tr-label, .14em) !important;
  text-transform: uppercase;
  color: var(--ink3, #5f6873);
  transition: color var(--t-state, 150ms) var(--t-ease, cubic-bezier(.16, 1, .3, 1));
}
.vp-back:hover,
.vp-x:hover { color: var(--ink, #1e2329); }
.vp-back:focus-visible,
.vp-x:focus-visible { outline: 2px solid var(--line2, #d5d9de); outline-offset: 3px; }
.vp-back { margin-right: 4px; }

.vp-logo { display: inline-flex; flex: 0 0 auto; align-items: center; }
.vp-logo svg, .vp-logo img { display: block; }

/* (0,2,0) beats instrument.css's `html body h2` at (0,0,3). No escalation. */
.vp .vp-name {
  margin: 0;
  font-size: var(--t-md, 14px) !important;
  line-height: 1.2 !important;
  font-weight: 600 !important;
  letter-spacing: normal !important;
  text-transform: none;
  color: var(--ink, #1e2329);
  white-space: nowrap;
}

.vp-meta {
  font-size: var(--t-sm, 12px) !important;
  color: var(--ink3, #5f6873);
  cursor: help;
  min-width: 0;
}

.vp-bar-r {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 14px;
  margin-left: auto;
  min-width: 0;
}

/* THE FEED MARKER DOES NOT MOVE AND DOES NOT SOFTEN. "no trade feed" is the
   reason every activity figure on an Aevo or a Rysk page reads "we cannot see"
   instead of 0, so it keeps the corner a reader checks for the state of things
   — and it is marked, not merely greyed. */
.vp-mark {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--t-xs, 10px) !important;
  letter-spacing: var(--tr-label, .14em) !important;
  text-transform: uppercase;
  color: var(--ink3, #5f6873);
  cursor: help;
  white-space: nowrap;
}
.vp-mark::before {
  content: "";
  width: 6px; height: 6px; flex: none;
  border-radius: 999px !important;
  background: currentColor;
}
.vp-mark.yes,
.vp-mark.ok   { color: var(--good, #087f5b); }
.vp-mark.warn { color: var(--warn, #e8590c); }
.vp-mark.bad  { color: var(--bad, #e03131); }
/* No feed is not a bad feed and not an empty one. Hollow, because there is
   nothing behind it to fill. */
.vp-mark.no::before { background: none; box-shadow: inset 0 0 0 1px currentColor !important; }

.vp .vp-link {
  font-size: var(--t-xs, 10px) !important;
  color: var(--ink3, #5f6873) !important;
  border-bottom: 1px solid var(--line, #eaecef);
  white-space: nowrap;
}
.vp .vp-link:hover {
  color: var(--ink, #1e2329) !important;
  border-bottom-color: var(--ink, #1e2329) !important;
}

/* The contract another surface asked for and did NOT get. Above the hero,
   because it is the reason the hero says what it says. */
.vp-req {
  margin: 0;
  padding: 10px 0 10px 12px;
  border-left: 2px solid var(--warn, #e8590c);
  font-size: var(--t-sm, 12px) !important;
  line-height: 1.55;
  color: var(--ink2, #474d57);
  max-width: 78ch;
}
.vp-req code {
  font-family: var(--mono, monospace) !important;
  font-size: .95em !important;
  color: var(--ink, #1e2329);
}
.vp-req em { font-style: normal; font-weight: 600 !important; color: var(--ink, #1e2329); }

/* ══════════════════════════════════════════════════════════════════════════
   2 — THE HERO. The same shape the overview opens with, one surface across.

     eyebrow  what this number is, and the contract it prices
     FIGURE   the number, at --t-hero
     sub      one line: what you pay, what it locks in, off which side
     alt      the two follow-up structures, beside it, one size down

   Nothing draws a box. §54 established that separation is space and one
   hairline; a hero in a card would be the exact thing that pass removed, one
   size larger.
   ══════════════════════════════════════════════════════════════════════════ */

/* The alt column is bounded rather than sized to its content: an `auto` track
   let the two supporting figures set their own width, so the hero's left column
   — the one carrying the answer — grew and shrank by ~90px between venues
   depending on how long the words "the cap pays for the floor" happened to be.
   The answer's column should not move because a footnote got longer. */
.vp-hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(190px, 264px);
  align-items: start;
  gap: 26px 56px;
  min-width: 0;
}
.vp-lead { min-width: 0; }

.vp-eyebrow {
  margin: 0 0 14px;
  font-size: var(--t-xs, 10px) !important;
  letter-spacing: var(--tr-label, .14em) !important;
  text-transform: uppercase;
  font-weight: 600 !important;
  color: var(--ink, #1e2329);
}

/* THE NUMBER. Mono, because it is a figure. Tight tracking and 0.92 leading so
   a 76px number does not reserve 100px of line box under itself. */
.vp-num {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 16px;
  min-width: 0;
}
.vp-num-n {
  font-family: var(--mono, monospace) !important;
  font-size: var(--t-hero, clamp(48px, 6.6vw, 76px)) !important;
  font-weight: 600 !important;
  line-height: .92;
  letter-spacing: -.045em !important;
  font-variant-numeric: tabular-nums;
  color: var(--ink, #1e2329);
}
/* The unit is not information at display size, so it is set back: a fraction
   of the figure's size and one step of ink quieter. */
.vp-num-u {
  font-family: var(--mono, monospace) !important;
  font-size: calc(var(--t-hero, 76px) * .42) !important;
  font-weight: 400 !important;
  line-height: .92;
  letter-spacing: 0 !important;
  color: var(--ink2, #474d57);
  margin-left: -.04em;
}
.vp-num-of {
  font-size: var(--t-md, 14px) !important;
  color: var(--ink3, #5f6873);
}

/* AN ABSENCE IS NOT A QUANTITY. "on request", "not listed", "no poll yet" are
   words, not values — set at a size the eye reads as a state, in ink3 at weight
   400, so it can never be scanned as a number that happens to be small. This is
   the single easiest place on the site to break "n/a is not 0". */
.vp-num.vp-na {
  font-family: var(--mono, monospace) !important;
  font-size: clamp(24px, 2.6vw, 34px) !important;
  font-weight: 400 !important;
  line-height: 1.05;
  letter-spacing: -.02em !important;
  color: var(--ink3, #5f6873);
}

.vp-sub {
  margin: 18px 0 0;
  font-size: var(--t-md, 14px) !important;
  line-height: 1.55;
  color: var(--ink3, #5f6873);
  max-width: 64ch;
}
.vp-sub b { font-weight: 600 !important; color: var(--ink, #1e2329); }
.vp-sub em { font-style: normal; font-weight: 600 !important; color: var(--ink2, #474d57); }

/* The other two structures, BESIDE the hero rather than under it. Same expiry,
   same distance from spot, same four states — one size down, because the reader
   opened a venue to find out what a floor costs and these are the two follow-up
   questions, not the question. */
.vp-alt {
  display: flex;
  flex-direction: column;
  gap: 20px;
  min-width: 0;
}
.vp-alt-i {
  display: grid;
  gap: 3px;
  min-width: 0;
  cursor: help;
}
.vp-alt-k {
  font-size: var(--t-xs, 10px) !important;
  letter-spacing: var(--tr-label, .14em) !important;
  text-transform: uppercase;
  color: var(--ink3, #5f6873);
}
.vp-alt-v {
  font-family: var(--mono, monospace) !important;
  font-size: 22px !important;
  font-weight: 600 !important;
  line-height: 1.1;
  letter-spacing: -.02em !important;
  font-variant-numeric: tabular-nums;
  color: var(--ink, #1e2329);
}
/* A collar the cap more than pays for. The sign is the whole point, so it gets
   the direction token rather than the neutral one. */
.vp-alt-v.vp-credit { color: var(--pos, #1971c2); }
.vp-alt-v.vp-na {
  font-size: var(--t-md, 14px) !important;
  font-weight: 400 !important;
  letter-spacing: normal !important;
  color: var(--ink3, #5f6873);
}
.vp-alt-n {
  font-size: var(--t-xs, 10px) !important;
  line-height: 1.45;
  color: var(--ink3, #5f6873);
}

/* ══════════════════════════════════════════════════════════════════════════
   3 — THE CHIPS. ONE ROW, ALWAYS, AT EVERY WIDTH.

   Measured before this rule existed: seven chips wrapped onto TWO rows on a
   1440 screen, and detail.js only ever emits six plus a count — the wrap was
   purely the absence of a stylesheet. On CallPut, twelve assets. The row never
   wraps now: past the width it has, it scrolls sideways, which is still one
   row on a phone.

   WHAT THE CHIPS STOPPED SAYING AND WHERE IT WENT. Each chip used to carry its
   state in words. That is honesty rule 1 and it is not droppable, so it moved
   to the line directly under the row, in full, for the asset actually selected.
   The other chips keep the state as a MARK — and the three marks are three
   different objects, because "listed and never traded" and "we cannot see" are
   different facts and only one of them is evidence.
   ══════════════════════════════════════════════════════════════════════════ */

.vp-carries {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
  padding-top: 26px;
  border-top: 1px solid var(--line, #eaecef);
}
.vp-carries-l {
  flex: none;
  font-size: var(--t-xs, 10px) !important;
  letter-spacing: var(--tr-label, .14em) !important;
  text-transform: uppercase;
  color: var(--ink3, #5f6873);
}
/* The row takes the space that is left and scrolls inside it; the +N control
   is its sibling, so it is pinned outside the scroll box at every width. */
.vp-carries > .vp-chiprow { flex: 0 1 auto; }
.vp-carries > .vp-more { flex: 0 0 auto; }

.vp-chiprow {
  display: flex;
  flex-wrap: nowrap;          /* THE rule. One row, at every width. */
  align-items: center;
  gap: 8px;
  min-width: 0;
  overflow-x: auto;
  overflow-y: hidden;
  padding: 2px 0;
  scrollbar-width: none;
  -ms-overflow-style: none;
  scroll-snap-type: x proximity;
  overscroll-behavior-x: contain;
}
.vp-chiprow::-webkit-scrollbar { display: none; }

.vp-chip {
  flex: 0 0 auto;
  scroll-snap-align: start;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 13px;
  background: transparent;
  border: 1px solid var(--line, #eaecef);
  border-radius: 999px !important;
  font: inherit;
  font-size: var(--t-sm, 12px) !important;
  line-height: 1.2;
  color: var(--ink2, #474d57);
  cursor: pointer;
  white-space: nowrap;
  transition: border-color var(--t-state, 150ms) var(--t-ease, cubic-bezier(.16, 1, .3, 1)),
              color var(--t-state, 150ms) var(--t-ease, cubic-bezier(.16, 1, .3, 1)),
              transform var(--t-state, 150ms) var(--t-ease, cubic-bezier(.16, 1, .3, 1));
}
.vp-chip:hover { color: var(--ink, #1e2329); border-color: var(--line2, #d5d9de); transform: translateY(-1px); }
.vp-chip:focus-visible { outline: 2px solid var(--line2, #d5d9de); outline-offset: 2px; }
.vp-chip.on {
  color: var(--ink, #1e2329);
  border-color: var(--ink, #1e2329);
  font-weight: 600 !important;
  cursor: default;
}
.vp-chip.on:hover { transform: none; }
.vp-chip-m { display: inline-flex; flex: none; align-items: center; }
.vp-chip-m svg, .vp-chip-m img { display: block; }

/* The state, as a mark. Three states, three objects — the same vocabulary the
   coverage grid uses, one object smaller. */
.vp-chip::after {
  content: "";
  width: 6px; height: 6px; flex: none;
  margin-left: 1px;
}
.vp-chip.st-traded::after {
  border-radius: 999px !important;
  background: var(--good, #087f5b);
}
.vp-chip.st-listed::after {
  border-radius: 999px !important;
  box-shadow: inset 0 0 0 1px var(--ink3, #5f6873) !important;
}
/* WE CANNOT SEE. Hatched, and the chip's own edge goes dashed with it, so a
   venue with no fill feed is SAID to be unreadable rather than left looking
   quiet. It is never a blank and never a zero. */
.vp-chip.st-unknown { border-style: dashed; }
.vp-chip.st-unknown::after {
  background: repeating-linear-gradient(
    45deg,
    var(--warn, #e8590c) 0 1px,
    transparent 1px 3px
  );
  box-shadow: inset 0 0 0 1px var(--warn, #e8590c) !important;
}
.vp-more::after { content: none; }
.vp-more { color: var(--ink3, #5f6873); }

/* The selected asset's state, in a sentence, where a fragment used to be. */
.vp-state {
  margin: 0;
  font-size: var(--t-sm, 12px) !important;
  line-height: 1.55;
  color: var(--ink3, #5f6873);
  max-width: 78ch;
}
.vp-state em { font-style: normal; font-weight: 600 !important; color: var(--ink2, #474d57); }
.vp-state.st-unknown { color: var(--ink2, #474d57); }

/* ══════════════════════════════════════════════════════════════════════════
   4 — THE TOGGLES, directly under the number they drive — the one place a
   control can sit and need no label explaining what it does.
   ══════════════════════════════════════════════════════════════════════════ */

.vp-tog {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px 26px;
  min-width: 0;
}
.vp-grp {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  min-width: 0;
}
.vp-grp-l {
  margin-right: 4px;
  font-size: var(--t-xs, 10px) !important;
  letter-spacing: var(--tr-label, .14em) !important;
  text-transform: uppercase;
  color: var(--ink3, #5f6873);
}

.vp-pill {
  padding: 5px 11px;
  background: transparent;
  border: 1px solid var(--line, #eaecef);
  border-radius: 999px !important;
  font: inherit;
  font-family: var(--mono, monospace) !important;
  font-size: var(--t-sm, 12px) !important;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
  color: var(--ink2, #474d57);
  cursor: pointer;
  white-space: nowrap;
  transition: border-color var(--t-state, 150ms) var(--t-ease, cubic-bezier(.16, 1, .3, 1)),
              color var(--t-state, 150ms) var(--t-ease, cubic-bezier(.16, 1, .3, 1)),
              transform var(--t-state, 150ms) var(--t-ease, cubic-bezier(.16, 1, .3, 1));
}
.vp-pill:hover { color: var(--ink, #1e2329); border-color: var(--line2, #d5d9de); transform: translateY(-1px); }
.vp-pill:focus-visible { outline: 2px solid var(--line2, #d5d9de); outline-offset: 2px; }
.vp-pill.on {
  color: var(--ink, #1e2329);
  border-color: var(--ink, #1e2329);
  font-weight: 600 !important;
  transform: none;
}
/* An expiry with nothing priced on it is still a real expiry and stays
   pressable — it is dimmed, not removed, because "this tenor exists and carries
   no price" is itself an answer. */
.vp-thin { color: var(--ink3, #5f6873); border-style: dashed; }

.vp-tog-r {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 14px;
  margin-left: auto;
  min-width: 0;
}
.vp-spot {
  font-family: var(--mono, monospace) !important;
  font-size: var(--t-sm, 12px) !important;
  font-variant-numeric: tabular-nums;
  color: var(--ink2, #474d57);
  white-space: nowrap;
}
.vp-rule { font-size: var(--t-xs, 10px) !important; color: var(--ink3, #5f6873); }
.vp-rule code { font-family: var(--mono, monospace) !important; font-size: 1em !important; color: var(--ink2, #474d57); }

/* ══════════════════════════════════════════════════════════════════════════
   5 — THE STATS. Label over number, flat, nothing drawing a box.

   Only the metrics that exist for THIS venue carry a figure; the ones that do
   not still get a column, because their absence is the informative part — and
   the column says why in words rather than printing a zero.
   ══════════════════════════════════════════════════════════════════════════ */

.vp-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
  gap: 24px 34px;
  padding-top: 26px;
  border-top: 1px solid var(--line, #eaecef);
}
.vp-st { display: grid; gap: 5px; min-width: 0; cursor: help; align-content: start; }
.vp-st-k {
  font-size: var(--t-xs, 10px) !important;
  letter-spacing: var(--tr-label, .14em) !important;
  text-transform: uppercase;
  color: var(--ink3, #5f6873);
}
.vp-st-v {
  font-family: var(--mono, monospace) !important;
  font-size: 22px !important;
  font-weight: 600 !important;
  line-height: 1.1;
  letter-spacing: -.02em !important;
  font-variant-numeric: tabular-nums;
  color: var(--ink, #1e2329);
}
.vp-st-n { font-size: var(--t-xs, 10px) !important; line-height: 1.45; color: var(--ink3, #5f6873); }

/* NOT MEASURED. "we cannot see" / "not published" is a state, not a quantity,
   so it drops to text size at weight 400 in ink3 and carries a dashed rule —
   the same hatch argument as the chip mark. It must not be scannable as a
   figure, and it is never rendered as 0. */
.vp-st.unk .vp-st-v {
  font-size: var(--t-md, 14px) !important;
  font-weight: 400 !important;
  letter-spacing: normal !important;
  color: var(--ink3, #5f6873);
  border-bottom: 1px dashed var(--line2, #d5d9de);
  padding-bottom: 5px;
  justify-self: start;
}

/* ══════════════════════════════════════════════════════════════════════════
   6 — METHOD AND PROVENANCE, LAST AND QUIET.

   It answers "how do you know", which is the second question, and a second
   question is what a disclosure is for. The vol chart mounts lazily: a closed
   disclosure fires no /api/history request, on open or on any 60s refresh.
   ══════════════════════════════════════════════════════════════════════════ */

.vp-foot {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-top: 22px;
  border-top: 1px solid var(--line, #eaecef);
}

.vp-d > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 9px 0;
  font-size: var(--t-sm, 12px) !important;
  line-height: 1.5;
  color: var(--ink2, #474d57);
  transition: color var(--t-state, 150ms) var(--t-ease, cubic-bezier(.16, 1, .3, 1));
}
.vp-d > summary::-webkit-details-marker { display: none; }
.vp-d > summary::before {
  content: "+";
  flex: none;
  width: 1em;
  font-family: var(--mono, monospace) !important;
  color: var(--ink3, #5f6873);
}
.vp-d[open] > summary::before { content: "\2212"; }
.vp-d > summary span {
  font-size: var(--t-xs, 10px) !important;
  line-height: 1.5;
  color: var(--ink3, #5f6873);
}
.vp-d > summary:hover { color: var(--ink, #1e2329); }
.vp-d > summary:focus-visible { outline: 2px solid var(--line2, #d5d9de); outline-offset: 2px; }

.vp-dbody { padding: 6px 0 18px 1.6em; }
.vp-dbody .ivc { margin-top: 8px; }

.vp-ms { display: grid; gap: 18px; }
.vp-m {
  display: grid;
  grid-template-columns: 190px minmax(0, 1fr);
  gap: 4px 22px;
  align-items: start;
}
.vp-m-k {
  font-size: var(--t-xs, 10px) !important;
  letter-spacing: var(--tr-label, .14em) !important;
  text-transform: uppercase;
  color: var(--ink3, #5f6873);
  padding-top: 2px;
}
.vp-m-b { min-width: 0; }
.vp-m-b code {
  display: inline-block;
  font-family: var(--mono, monospace) !important;
  font-size: var(--t-xs, 10px) !important;
  line-height: 1.5;
  color: var(--ink2, #474d57);
  word-break: break-all;
}
.vp-m-b p {
  margin: 4px 0 0;
  font-size: var(--t-sm, 12px) !important;
  line-height: 1.55;
  color: var(--ink3, #5f6873);
  max-width: 78ch;
}
.vp-m-b p b { font-weight: 600 !important; color: var(--ink2, #474d57); }
.vp-m-l { margin: 6px 0 0; padding-left: 16px; display: grid; gap: 3px; }
.vp-m-l li { font-size: var(--t-xs, 10px) !important; line-height: 1.5; color: var(--ink3, #5f6873); }
.vp-m-l code { font-family: var(--mono, monospace) !important; word-break: break-all; }

.vp-fine {
  margin: 16px 0 0;
  font-size: var(--t-xs, 10px) !important;
  line-height: 1.55;
  color: var(--ink3, #5f6873);
  max-width: 78ch;
}

/* ══════════════════════════════════════════════════════════════════════════
   7 — NARROW. The chip row is the one thing that does NOT reflow: it scrolls.
   ══════════════════════════════════════════════════════════════════════════ */

@media (max-width: 1060px) {
  .vp-hero { grid-template-columns: minmax(0, 1fr); gap: 26px; }
  .vp-alt { flex-direction: row; flex-wrap: wrap; gap: 20px 44px; }
  .vp-alt-i { flex: 1 1 200px; }
}

@media (max-width: 700px) {
  .vp { gap: 24px; }
  .vp-bar-r { margin-left: 0; width: 100%; }
  /* The label takes its own line; the row and the control that empties it stay
     on ONE line together, because separating them is what put "+6" 328px off
     the right edge of a phone in the first place. */
  .vp-carries { flex-wrap: wrap; gap: 10px 12px; }
  .vp-carries-l { flex: 0 0 100%; }
  /* `flex: 0 1 auto` wraps here rather than shrinking: flex WRAPS on an item's
     hypothetical main size, which for basis:auto is its content — 678px of
     chips against a 350px line — so the row claimed the whole line and pushed
     +N onto a second one. Measured. A zero basis makes the row take what is
     left after +N instead of asking for what it holds, and the two stay on one
     line at any width. */
  .vp-carries > .vp-chiprow { flex: 1 1 0; min-width: 0; }
  .vp-tog-r { margin-left: 0; width: 100%; }
  .vp-stats { grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); gap: 20px 24px; }
  .vp-m { grid-template-columns: minmax(0, 1fr); gap: 4px; }
  .vp-dbody { padding-left: 0; }
}

/* ══════════════════════════════════════════════════════════════════════════
   8 — MOTION: ARRIVAL AND STATE CHANGE

   detail.js sets data-anim on the panel root every render, and there are
   exactly three values it can take:

     open    a venue was opened. The whole panel arrives, staggered downward
             in reading order, so the motion TEACHES the hierarchy.
     state   the reader changed the asset, the expiry or the distance. Only
             what actually changed re-reads: the figure, its sentence, the two
             structures beside it, and the state line. The chrome holds still,
             because nothing about it changed.
     quiet   the 60s poll. NOTHING moves. A page that pulses at you once a
             minute for no reason is worse than a still one.

   THE RULES:
   1. TRANSFORM AND OPACITY ONLY. Both are compositor properties: no layout, no
      paint, no main-thread work per frame on a panel that re-reads every 60s.
   2. NOTHING TRAVELS MORE THAN 14px. The travel only says "this is new".
   3. ~340ms end to end on open, 260ms on a state change — a state change is a
      confirmation, not an arrival, and must never feel slower than the click.
   4. prefers-reduced-motion KILLS ALL OF IT. No shortened version, no
      fade-only fallback. Every element at its end state, immediately.
   ══════════════════════════════════════════════════════════════════════════ */

@keyframes vp-rise {
  from { opacity: 0; transform: translate3d(0, 14px, 0); }
  to   { opacity: 1; transform: none; }
}
/* The figure gets its own curve: it scales up from 0.94 as well as rising, so
   the largest thing on the panel arrives as an event and the rest arrives as
   content. */
@keyframes vp-fig {
  from { opacity: 0; transform: translate3d(0, 10px, 0) scale(.94); }
  to   { opacity: 1; transform: none; }
}
/* A swap, not an arrival: shorter, shallower, no scale. */
@keyframes vp-swap {
  from { opacity: 0; transform: translate3d(0, 6px, 0); }
  to   { opacity: 1; transform: none; }
}

.vp[data-anim="open"] > .vp-bar,
.vp[data-anim="open"] > .vp-req,
.vp[data-anim="open"] .vp-eyebrow,
.vp[data-anim="open"] .vp-sub,
.vp[data-anim="open"] .vp-alt,
.vp[data-anim="open"] > .vp-carries,
.vp[data-anim="open"] > .vp-state,
.vp[data-anim="open"] > .vp-tog,
.vp[data-anim="open"] > .vp-stats,
.vp[data-anim="open"] > .vp-foot {
  animation: vp-rise 460ms var(--t-ease, cubic-bezier(.16, 1, .3, 1)) backwards;
  will-change: transform, opacity;
}
.vp[data-anim="open"] > .vp-bar        { animation-delay: 0ms; }
.vp[data-anim="open"] > .vp-req        { animation-delay: 30ms; }
.vp[data-anim="open"] .vp-eyebrow      { animation-delay: 55ms; }
.vp[data-anim="open"] .vp-num {
  animation: vp-fig 640ms var(--t-ease, cubic-bezier(.16, 1, .3, 1)) 80ms backwards;
  will-change: transform, opacity;
}
.vp[data-anim="open"] .vp-sub          { animation-delay: 135ms; }
.vp[data-anim="open"] .vp-alt          { animation-delay: 190ms; }
.vp[data-anim="open"] > .vp-carries    { animation-delay: 190ms; }
.vp[data-anim="open"] > .vp-state      { animation-delay: 225ms; }
.vp[data-anim="open"] > .vp-tog        { animation-delay: 260ms; }
.vp[data-anim="open"] > .vp-stats      { animation-delay: 300ms; }
.vp[data-anim="open"] > .vp-foot       { animation-delay: 340ms; }

/* The chips arrive along the row they will live on, so the eye learns it is a
   row and not a block. Capped at the sixth: past that they are behind a count
   anyway, and a stagger with no end reads as a page still loading. */
.vp[data-anim="open"] .vp-chip {
  animation: vp-rise 420ms var(--t-ease, cubic-bezier(.16, 1, .3, 1)) backwards;
}
.vp[data-anim="open"] .vp-chiprow > .vp-chip:nth-child(1)   { animation-delay: 200ms; }
.vp[data-anim="open"] .vp-chiprow > .vp-chip:nth-child(2)   { animation-delay: 234ms; }
.vp[data-anim="open"] .vp-chiprow > .vp-chip:nth-child(3)   { animation-delay: 268ms; }
.vp[data-anim="open"] .vp-chiprow > .vp-chip:nth-child(4)   { animation-delay: 302ms; }
.vp[data-anim="open"] .vp-chiprow > .vp-chip:nth-child(5)   { animation-delay: 336ms; }
.vp[data-anim="open"] .vp-chiprow > .vp-chip:nth-child(n+6) { animation-delay: 370ms; }
/* The overflow control is a sibling of the row, not a child of it, so it needs
   its own delay or nth-child would time it against .vp-carries instead. It
   arrives last of the group: it is the way out of the row, not part of it. */
.vp[data-anim="open"] .vp-carries > .vp-more { animation-delay: 404ms; }

/* A STATE CHANGE MOVES ONLY WHAT CHANGED. */
.vp[data-anim="state"] .vp-num,
.vp[data-anim="state"] .vp-sub,
.vp[data-anim="state"] .vp-alt-i,
.vp[data-anim="state"] > .vp-state {
  animation: vp-swap 260ms var(--t-ease, cubic-bezier(.16, 1, .3, 1)) backwards;
  will-change: transform, opacity;
}
.vp[data-anim="state"] .vp-num        { animation-delay: 0ms; }
.vp[data-anim="state"] .vp-sub        { animation-delay: 40ms; }
.vp[data-anim="state"] .vp-alt-i      { animation-delay: 70ms; }
.vp[data-anim="state"] > .vp-state    { animation-delay: 70ms; }

/* data-anim="quiet" is the poll and matches nothing above. That is deliberate:
   it is the absence of a rule, not a rule that undoes one. */

@media (prefers-reduced-motion: reduce) {
  .vp *,
  .vp {
    animation: none !important;
    transition: none !important;
  }
  .vp-chip:hover,
  .vp-pill:hover { transform: none !important; }
}

/* ===========================================================================
   LODGERS — every affordance, one vocabulary
   ---------------------------------------------------------------------------
   .cw-*, .cov-*, .sum-v and .board-all are drawn by grid.js and command.js and
   styled in style.css, which is owned by another agent this session. These
   rules live here because detail.css is loaded after style.css and therefore
   wins at equal specificity — but they are lodgers, and the move is in the
   report. Nothing below sets a colour outside the token palette.

   The audit that produced them found the same failure in five places: an
   element that IS a link to something the app can show, drawn as static text.
   The rule applied to all of them, and the reason each part of it is here:
     cursor          nothing says "press me" like the pointer, and its absence
                     is the first thing that reads as "this is just a label"
     hover           a state change under the cursor is the confirmation
     :focus-visible  ui.css gives one ring to the controls it knows about; the
                     elements below are new and would have had none
     aria-label      set in JS beside the handler, so a label can never
                     describe a target that has changed
   =========================================================================== */

/* The generic hit target, applied by grid.js's clickable(). */
.hit { cursor: pointer; }
.hit:focus-visible {
  outline: 2px solid var(--mint2); outline-offset: 2px; border-radius: var(--r-mark);
}

/* ---- venue cards --------------------------------------------------------- */
/* The card that opened the panel below. Without it the only evidence of what
   you clicked is a panel that is a screen away once you have scrolled. */
.cw-card.open {
  border-color: var(--mint2);
  box-shadow: inset 3px 0 0 var(--mint), 0 2px 14px -6px rgba(0, 0, 0, .22);
}
.cw-card.open .cw-go { color: var(--mint); }

/* Asset chips. .cw-chips was flex-wrap:nowrap + overflow:hidden, which clipped
   the overflow chip itself on a narrow card — a control you cannot see is not
   a control, and one you can half-see is worse. */
.cw-chips { flex-wrap: wrap; overflow: visible; }
.cw-chip[data-asset], .cw-chip.more { cursor: pointer; }
.cw-chip[data-asset]:hover, .cw-chip.more:hover {
  background: var(--mint-dim); color: var(--mint);
}
.cw-chip[data-asset]:focus-visible, .cw-chip.more:focus-visible {
  outline: 2px solid var(--mint2); outline-offset: 1px;
}
/* The chip for the asset already on screen. Marked, never pressable: it leads
   to the state you are in. */
.cw-chip.on { cursor: default; }
/* :not([hidden]) is load-bearing. An author-stylesheet `display:contents` beats
   the UA's `[hidden]{display:none}` outright, so writing it unguarded would
   render the collapsed chips at all times and the "+4" control would expand
   something already on screen. */
.cw-rest:not([hidden]) { display: contents; }

/* ---- the venue named in the summary sentence ----------------------------- */
.sum-v {
  background: none; border: 0; padding: 0; cursor: pointer;
  font: inherit; color: var(--mint);
  text-decoration: underline; text-underline-offset: 3px;
  text-decoration-color: var(--line2);
}
.sum-v:hover { text-decoration-color: var(--mint); }

/* ---- the coverage board -------------------------------------------------- */
/* The asset headings are rotated (.cov-ah), so a background hover would light
   a box that does not line up with the words. The words themselves respond. */
th.cov-a { cursor: pointer; }
th.cov-a:hover .cov-ah b, th.cov-a:focus-visible .cov-ah b { color: var(--mint); }
th.cov-a:focus-visible { outline: 2px solid var(--mint2); outline-offset: -2px; }
th.cov-v:focus-visible { outline: 2px solid var(--mint2); outline-offset: -2px; }
th.cov-v { cursor: pointer; }
td.cov-c:not(.none):focus-visible { outline: 2px solid var(--mint2); outline-offset: -1px; }

/* ---- the board-wide matrix, below and closed ----------------------------- */
/* It answers "what can you do at ANY venue", which is a different question
   from the one this section opens with. Below the panel, and shut, it is
   available without being in the way. */
.board-all { margin-top: 14px; }
.board-all > summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: center; gap: 8px;
  padding: 11px 15px;
  border: 1px solid var(--line); border-radius: var(--r-card);
  background: var(--s1); color: var(--ink2);
  font: 12.5px/1.3 var(--sans);
}
.board-all > summary::-webkit-details-marker { display: none; }
.board-all > summary::after {
  content: "▸"; margin-left: auto; color: var(--ink3); font-size: 11px;
}
.board-all[open] > summary::after { content: "▾"; }
.board-all > summary:hover { color: var(--ink); border-color: var(--mint2); }
.board-all > summary:focus-visible {
  outline: 2px solid var(--mint2); outline-offset: 2px;
}
.board-all[open] > summary { margin-bottom: 10px; }

/* ---- a view that holds two blocks ----------------------------------------
   Set by detail.js only while a venue panel is open. See the note at open().
   Measured with the panel open and this rule absent: .split height 0, hero
   panel 2px, #where-grid a 20px window over 438px of venue cards — the cards
   that opened the panel were gone, and the asset chips on them were not
   clickable because elementFromPoint landed on .view instead of the chip.
   Neither block may take the other's height: both size to their content and
   the pane scrolls, which is what a two-block section has to do. */
.view.has-panel { display: block; }
.view.has-panel > .split,
.view.has-panel > #detail-root,
.view.has-panel > .board-all { flex: 0 0 auto; }
/* The tape is the one thing in there that WANTS a bound — it is an infinite
   list and would otherwise run to whatever the feed holds, pushing the venue
   panel off the bottom of a section it is supposed to sit above. This is the
   same ceiling style.css gives it in the non-shell layout. */
.view.has-panel .tape-panel { max-height: 620px; }
