/* Moneyness — the "Where can I trade?" screener.
 *
 * Owned by grid.js and nothing else. Every colour is a token from style.css:
 * there is a paper theme and a night-shift theme and a hard-coded hex follows
 * neither.
 *
 * ── ONE VARIABLE LAYS OUT THE WHOLE TABLE ────────────────────────────────
 * --col-layout is declared once, on the scrollport, and read by the header and
 * by all 38 rows. Change that one number and the header and every row re-lay
 * in a single paint: no JS measuring, no ResizeObserver, and the header cannot
 * drift from the rows because it is not a second declaration, it is the same
 * one. All fractional — a fixed px column would stop tracking the pane, which
 * is 1.55fr of a two-column split and therefore a different width at 1280,
 * 1440 and 1920.
 *
 * The identity column takes 30 of 101fr — 30% of the table, 2.7x the next
 * widest. The thing you are looking for is the LISTING (which asset, at which
 * venue); everything to its right is supporting evidence for it. Hierarchy
 * that lives in the layout does not need a font-weight to carry it.
 *
 * NINE COLUMNS, NOT TEN. Measured at 1440: with ten, the pane's 790px put
 * "No trades" out as "No tra…" and pushed the last column past the right edge.
 * A truncated value is a wrong value. The tenth column was "Pricing", which on
 * 26 of 38 rows restated what the Two-sided column already said — so it became
 * that column's sub-line and everything else got its width back.
 */

#where-grid.sc-host{
  /* .chart-wrap sets padding:14px 15px 6px. The table draws its own gutters
     and a sticky header cannot sit inside a padded box without a gap opening
     above it as you scroll. #where-grid.sc-host outranks #where-grid. */
  padding:0;
  /* Derived from the LONGEST VALUE each column can actually hold, read off the
     live API rather than guessed: "No trades" (status), "17h ago" (last
     trade), "no feed" (fills), "63,905" (spot), "414C/414P" (contracts),
     "on request" (two-sided), "to 325d" (expiries), "– 320,000" (strikes).
     Every one of those fits without an ellipsis at the pane's real width, and
     the surplus goes to the identity column. */
  /* SIX COLUMNS. Derived from the longest value each can actually hold, read
     off the live API rather than guessed: "On request" (pricing), "no offer"
     (floor cost), "No feed" (traded), "no feed" (volume), and the widest
     sub-line on the board, "13 exp · 1,000–4,000" (listed). Every one fits
     without an ellipsis at the pane's real width and the surplus goes to the
     identity column, which carries 30 of 97fr — the thing you are looking for
     is the LISTING and everything right of it is evidence for it.

     WAS NINE. Spot went because it was one index price repeated (2.8–27.9bps
     of venue-to-venue dispersion across every multi-venue asset); Status and
     Last trade merged into one column because they were one sentence written
     in two cells; Fills, Two-sided, Expiries and Strike range became the
     sub-lines of the columns whose question they were answering. */
  --col-layout: 30fr 13fr 11fr 13fr 12fr 18fr;
  --sc-row-h:38px;
}

/* No asset picked = no floor to price, because /api/venue-compare is
   per-underlying. The column is DROPPED rather than filled with 39 dashes: a
   column that is empty on every row teaches the reader to stop looking at it. */
#where-grid[data-scope="all"]{ --col-layout: 32fr 15fr 15fr 14fr 20fr; }
#where-grid[data-scope="all"] .sc-head > :nth-child(3),
#where-grid[data-scope="all"] .sc-row > :nth-child(3){ display:none; }

.sc{
  display:flex; flex-direction:column;
  /* 790px is the pane's own width at 1440, and the fr's above are tuned so
     every live value fits exactly there. Narrower than that the table scrolls
     sideways rather than truncating: a truncated value is a wrong value, and
     for a strike it is the abbreviation this product refuses — 1,750 clipped
     to "1,75…" is the same string as 1,759. The identity column is sticky, so
     what leaves the screen is always evidence, never the subject.

     There is deliberately NO media query. The one variable above is the whole
     layout at every width; a second copy of it under a breakpoint is a second
     thing that can disagree with the first. */
  min-width:760px;
  font-variant-numeric:tabular-nums;
}

/* ── the hint and the filter bar ─────────────────────────────────────────── */
/* Both are tape.js's controls, reused rather than re-invented: .tw-hint,
   .tw-fbar, .tw-chip, .tw-fn and .tw-clear are already defined in tools.css
   and already carry the app's focus, hover and radius treatment. Two tables
   that filter the same way must LOOK like they filter the same way, and a
   second stylesheet for the same control is a second thing that can drift.
   All that is added here is the gutter, because #where-grid.sc-host sets
   padding:0 so the sticky header can reach the panel's edge. */
.sc-hint{padding:9px 13px 0; margin:0 0 8px}
.sc-fbar{margin:0 13px 9px}

/* ── header ─────────────────────────────────────────────────────────────── */
.sc-head{
  display:grid; grid-template-columns:var(--col-layout);
  position:sticky; top:0; z-index:var(--z-sticky);
  /* Opaque, and exactly the panel's own ground: .panel is --fill-1 over --bg,
     so a translucent header would show the rows sliding under it. */
  background:var(--bg);
  background-image:linear-gradient(var(--fill-1),var(--fill-1));
  border-bottom:1px solid var(--line2);
}
.sc-h{
  appearance:none; background:none; border:0; margin:0;
  padding:9px 8px 8px; min-width:0;
  font:600 10px/1.2 var(--mono); text-transform:uppercase; letter-spacing:.09em;
  color:var(--ink3); text-align:left; cursor:pointer;
  display:flex; align-items:center; gap:3px;
  /* Never wrap and never spill. Two headings that overflowed printed on top of
     each other at 1440 — a header that lies about which column it names is
     worse than a shorter word. */
  white-space:nowrap; overflow:hidden;
  /* THE AFFORDANCE IS AN UNDERLINE, NOT A COLOUR. Colour on a header competes
     with the state colours in the rows below it, and there is only one thing
     on this table that colour is allowed to mean: how recently something
     traded. */
  text-decoration:underline transparent 1px;
  text-underline-offset:3px;
  transition:color 90ms linear, text-decoration-color 90ms linear;
}
.sc-h.nos{cursor:default}
.sc-h.a-r{justify-content:flex-end; text-align:right}
.sc-h:hover,.sc-h:focus-visible{color:var(--ink2); text-decoration-color:var(--line2)}
/* The active column speaks the SAME language as hover — a heavier version of
   the same underline, not a second vocabulary. Hover and active read as one
   system, so hovering a column previews what selecting it will look like. */
.sc-h.on{color:var(--ink); text-decoration-color:var(--mint)}
.sc-ar{
  width:0; height:0; flex:none; opacity:0;
  border-left:3px solid transparent; border-right:3px solid transparent;
  /* Nudged up 1px: an arrow centred on the box sits low against uppercase
     type, which has no descenders to balance it. */
  position:relative; top:-1px;
  transition:opacity 90ms linear;
}
.sc-h[data-dir] .sc-ar{opacity:1}
.sc-h[data-dir="up"] .sc-ar{border-bottom:4px solid var(--mint)}
.sc-h[data-dir="down"] .sc-ar{border-top:4px solid var(--mint)}

/* ── rows ───────────────────────────────────────────────────────────────── */
.sc-body{display:flex; flex-direction:column}

/* EVERY ROW IS AN ANCHOR. Middle-click opens a tab, cmd-click backgrounds ten
   in five seconds, the destination shows in the status bar on hover, and
   right-click offers Copy Link Address. A <div onClick> gives up all four and
   the markup looks identical. */
.sc-row{
  display:grid; grid-template-columns:var(--col-layout);
  align-items:center; min-height:var(--sc-row-h);
  border-bottom:1px solid var(--line);
  color:inherit; text-decoration:none;
  /* Rows are reordered by writing `order`, never by moving nodes: a re-parent
     drops focus, kills a text selection and costs a mutation record per row. */
  transition:background 80ms linear;
  /* No content-visibility and no windowing. 38 rows is not a list that needs
     either, and both trade away scroll fidelity and find-in-page for work the
     browser was not doing anyway. */
}
.sc-row:hover{background:var(--glass)}
/* A ROW-WIDE UNDERLINE ON HOVER DESTROYS THE ONE SIGNAL THE CELLS USE.
   .sc-row already declares text-decoration:none, but a global a:hover rule
   outranks it, and text-decoration is painted by the ANCESTOR across every
   descendant — the child computing "none" does not stop the line. Measured:
   hovering any row underlined all six cells at once, which is precisely when
   the reader is trying to tell the three clickable values from the three
   inert ones. The row is a link; its affordance is the cursor, the background
   and the status bar, not a line through the data. */
.sc-row:hover,.sc-row:focus,.sc-row:focus-visible{text-decoration:none}
.sc-row:focus-visible{outline:1.5px solid var(--mint2); outline-offset:-2px; border-radius:var(--r-mark)}
.sc-row.open{
  background:var(--mint-dim);
  box-shadow:inset 2px 0 0 var(--mint);
}

/* AN EXPLICIT FILTER REMOVES. A DECLUTTER DIMS. THEY ARE NOT THE SAME GESTURE.
   Both used to dim to 50%, and the owner's verdict on picking SPCX was "it
   should not show the other assets" — twice. He is right, and the reason is
   about intent rather than taste: "hide this pair" is a housekeeping action on
   a row you want out of your way but still want to see the shape of, so it
   dims and keeps its space. "Show me SPCX" is a QUESTION, and answering it with
   36 ghost rows the reader has to look past is not an answer.
   Measured before the change: picking SPCX left 36 of 38 rows on screen at
   opacity .5, and because only the cell CONTENTS dimmed, the rows themselves
   still read at full strength.
   So the asset filter now removes, and the dim is kept for the declutter it was
   written for. The count and the clear control live in the summary line, so a
   short board always explains itself. */
.sc-row[data-hidden="true"]{display:none}
.sc-row[data-declutter="true"] .cell > *{opacity:.5}
.sc-row[data-declutter="true"]{background:transparent}
.sc-row[data-declutter="true"]:hover{background:var(--glass)}

.cell{
  min-width:0; padding:5px 8px;
  display:flex; flex-direction:column; justify-content:center; gap:1px;
  font:11.5px/1.25 var(--mono);
  /* Positioned so the visually-hidden column label below anchors to its own
     cell. Left unpositioned it resolves against the page and drags the
     document's scroll extent around. */
  position:relative;
}
.cell.a-r{align-items:flex-end; text-align:right}
.cell b{font-weight:650; color:var(--ink); white-space:nowrap;
  overflow:hidden; text-overflow:ellipsis; max-width:100%}
.cell i{font-style:normal; font-size:10px; color:var(--ink3); white-space:nowrap}
.cell:empty{display:none}

/* The visually-hidden column label. It lets the row's own aria-label stay
   static — a label that carried live numbers would rewrite an attribute on
   fifteen rows every poll for nothing a reader can see. */
.sc-l{position:absolute; width:1px; height:1px; margin:-1px; padding:0;
  overflow:hidden; clip-path:inset(50%); border:0; white-space:nowrap}

/* ── identity ───────────────────────────────────────────────────────────── */
/* STICKY LEFT. The pane is 611px at 1280 and the table wants 760, so something
   has to leave the screen — and the one thing that must not is the label that
   says which row you are reading. The coverage table this replaced froze its
   first column for exactly this reason. The ground is painted rather than left
   transparent because a sticky cell with no background lets the columns it is
   supposed to cover slide through it. */
.sc-id{
  /* justify-content:center comes from .cell, where the axis is vertical and it
     means "centre the two lines". Turning this cell's axis horizontal made it
     mean "centre the whole name block", so a row carrying the cheapest-floor
     badge started 75px left of the row above it and the left edge of the
     column zig-zagged. The axis changed; the alignment has to change with it. */
  flex-direction:row; justify-content:flex-start;
  align-items:center; gap:9px; padding-left:13px;
  position:sticky; left:0; z-index:var(--z-raised);
  background:var(--bg);
  background-image:linear-gradient(var(--fill-1),var(--fill-1));
}
.sc-row:hover .sc-id{
  background-image:linear-gradient(var(--glass),var(--glass)),
                   linear-gradient(var(--fill-1),var(--fill-1));
}
.sc-row.open .sc-id{
  background-image:linear-gradient(var(--mint-dim),var(--mint-dim)),
                   linear-gradient(var(--fill-1),var(--fill-1));
}
.sc-head .sc-h:first-child{
  position:sticky; left:0; z-index:var(--z-raised);
  background:var(--bg);
  background-image:linear-gradient(var(--fill-1),var(--fill-1));
}
.sc-mk{
  display:inline-flex; align-items:flex-end; flex:none; position:relative;
  cursor:pointer; border-radius:var(--r-mark); padding:1px;
  transition:box-shadow 90ms linear;
}
.sc-mk:hover{box-shadow:0 0 0 2px var(--mint-dim)}
/* The venue mark rides the asset mark, because the row is one thing — this
   asset AT this venue — and two marks side by side read as two subjects. */
.sc-vm{margin-left:-7px; display:inline-flex; filter:drop-shadow(0 0 1px var(--bg))}
.sc-nm{min-width:0; display:flex; flex-direction:column; gap:1px}
.sc-t{display:flex; align-items:baseline; gap:5px; min-width:0}
/* flex:none on the ticker, ellipsis on the venue name. Without it a long venue
   name won the flex fight and squeezed "ETH" to 8px of an element that wanted
   23 — the ticker is the row's subject and is the one thing that may not be
   clipped. */
.sc-t b{font:650 13px/1.2 var(--mono); letter-spacing:-.01em; flex:none}
.sc-t em{font-style:normal; color:var(--ink3); font-size:11px; flex:none}
.sc-t > span{font:500 12px/1.2 var(--sans); color:var(--ink2);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
/* The flag lives on the SECOND line, beside the chain, not in the name.
   Measured at 1440: sitting next to the venue name it took 138px of a 230px
   cell and squeezed "Derive" out of existence — the row said "ETH /" and then
   a badge. The name is the row's subject; a badge that hides it has cost more
   than it can be worth. */
.sc-l2{display:flex; align-items:center; gap:6px; min-width:0}
.sc-sub{font-style:normal; font:10px/1.2 var(--mono); color:var(--ink3);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0}
.sc-flag{
  flex:none; font:600 8px/1 var(--mono); text-transform:uppercase;
  letter-spacing:.05em; color:var(--good); border:1px solid var(--good);
  border-radius:var(--r-pill); padding:2px 4px; white-space:nowrap;
}

/* ── the values that filter ─────────────────────────────────────────────── */
/* EVERY CELL THAT DOES SOMETHING LOOKS LIKE IT DOES, AND EVERY CELL THAT DOES
   NOT, DOES NOT. The failure this replaces is a table where the only way to
   learn which columns are controls is to click each one and see. Filter values
   get a hit target, a hover ground and a lit state; magnitudes (floor cost,
   volume, listed) are inert in the body and their HEADER is the button —
   tape.js's split, for tape.js's reason: clicking "1.15%" should say something
   about THAT venue, and "rank the board by cost" is about the column. */
.sc-b{
  display:flex; flex-direction:column; gap:1px; min-width:0; max-width:100%;
  /* Negative margin against the padding so the hit target grows OUTWARD and
     the row height does not move when a value becomes clickable. */
  padding:3px 6px; margin:-3px -6px;
  border-radius:5px; cursor:pointer;
  transition:background 80ms linear, box-shadow 80ms linear;
}
.cell.a-r .sc-b{align-items:flex-end}
/* THE AFFORDANCE AT REST IS AN UNDERLINE, NOT A COLOUR — the same rule the
   sort headers follow at the top of this file, and for the same reason:
   colour on this table means exactly one thing (how recently something
   traded), so a tinted "Firm" would be a second vocabulary arguing with the
   recency ramp. A hover-only affordance is not an affordance; a reader has to
   already suspect the cell does something before they can discover that it
   does. Dotted at rest, solid mint when it is doing it. */
.sc-b > b{
  text-decoration:underline dotted var(--line2) 1px;
  text-underline-offset:3px;
  transition:color 80ms linear, text-decoration-color 80ms linear;
}
.sc-b:hover{background:var(--glass)}
.sc-b:hover > b{color:var(--mint); text-decoration-color:var(--mint)}
.sc-b.on{background:var(--glass); box-shadow:inset 0 0 0 1px var(--mint2)}
.sc-b.on > b{color:var(--mint); text-decoration:none}
.sc-b:focus-visible{outline:2px solid var(--mint2); outline-offset:1px}

/* The chain, on the identity line. It is a filter and not a caption: "I am
   already on Base, what can I trade there" is a question a self-directed
   trader arrives with, and it was previously answerable only by reading 39
   sub-lines. Each chain is its own control because a venue can be on two and
   "HyperEVM · Ethereum" is not somewhere you can be. */
.sc-ch{
  flex-direction:row; padding:1px 4px; margin:-1px 0;
  font:10px/1.2 var(--mono); color:var(--ink3);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  text-decoration:underline dotted var(--line2) 1px;
  text-underline-offset:2px;
}
.sc-ch + .sc-ch{margin-left:3px}
.sc-ch:hover{color:var(--mint); text-decoration-color:var(--mint)}
.sc-ch.on{color:var(--mint); text-decoration:none}

/* ── how you get a price ────────────────────────────────────────────────── */
/* NEUTRAL INK, NEVER A HUE. Colour means exactly one thing on this table —
   how recently something traded — and a green "Firm" would be a second
   vocabulary competing with the recency ramp one column right. The four
   pricing states are separated by WEIGHT, which is the same device the four
   trade states use. */
.cell.pr-firm b{color:var(--ink); font-weight:650}
.cell.pr-empty b,.cell.pr-req b{color:var(--ink2); font-weight:500}
.cell.pr-marks b{color:var(--ink3); font-weight:500}

/* ── the four states ────────────────────────────────────────────────────── */
/* ONE SWATCH SHAPE PER MEANING, and colour means exactly one thing on this
   table: how recently something traded.
     Status cell     solid = feed and trades · dashed = feed and none ·
                     hatched = no feed at all. Neutral ink, never a hue, so it
                     cannot be mistaken for the recency ramp two columns right.
     Last trade cell the recency ramp, --q5 (freshest) to --q1.
   The first version painted the hatch BEHIND the words and the text was
   unreadable at 11px — the mark has to sit beside the value, not under it. */
.cell b{position:relative}
.cell.sw b, .cell.st-traded b, .cell.st-listed b, .cell.st-unknown b,
.cell.dim b, .cell.unk b, .cell[data-band] b{padding-left:12px}  /* §69 ladder */
.cell.st-traded b::before, .cell.st-listed b::before, .cell.st-unknown b::before,
.cell[data-band] b::before{
  content:""; position:absolute; left:0; top:50%; width:6px; height:6px;
  margin-top:-3px; border-radius:var(--r-mark); box-sizing:border-box;
}
.cell.st-traded b{color:var(--ink)}
.cell.st-traded b::before{background:var(--ink2)}
/* A feed that shows nothing: a dashed edge, never a fill. A grey fill would
   read as a real low value, so "nothing trades here" would masquerade as "a
   little trades here". */
.cell.st-listed b{color:var(--ink2); font-weight:500}
/* --ink3, NOT --empty, AND THIS IS A CONTRAST FIX RATHER THAN A PALETTE
   CHANGE. Measured on the live page: --empty resolves to #20241a against a
   --bg of #181a20 on the night theme — about 1.1:1, which is not a faint
   mark, it is an absent one. Both swatches were invisible in dark and only
   just visible in light, so two of the four states were being carried
   entirely by their words. --ink3 is the app's existing quiet neutral
   (#949ead / #5f6873), so the rule that matters here is kept: one swatch
   shape per meaning, and NEVER a hue — colour on this table still means
   exactly one thing, how recently something traded. */
.cell.st-listed b::before{border:1px dashed var(--ink3)}
/* NO FEED IS NOT ZERO. 21 of 38 listings are in this state, and a blank or a
   zero there would claim the venue has never traded an option — which we do
   not know and which is almost certainly false. */
.cell.st-unknown b{color:var(--ink2); font-weight:500}
.cell.st-unknown b::before{
  border:1px solid var(--ink3);
  background-image:repeating-linear-gradient(45deg,var(--ink3) 0 1.2px,transparent 1.2px 3.2px);
}
/* The words carry it in the Last trade and Fills cells — "unknown", "no feed",
   "never" — so those need no second mark, only a weight that keeps them from
   reading as measurements. */
.cell.unk b, .cell.dim b{color:var(--ink3); font-weight:500; padding-left:0}
.cell.unk i, .cell.dim i{color:var(--ink3)}

/* Recency, from the token ramp. On paper the darkest step is the strongest; on
   the night theme it is the brightest. --q5 is "freshest" in both, so nothing
   here changes with the theme and no JS reads a colour. */
.cell[data-band="0"] b::before{background:var(--q5)}
.cell[data-band="1"] b::before{background:var(--q4)}
.cell[data-band="2"] b::before{background:var(--q3)}
.cell[data-band="3"] b::before{background:var(--q2)}
.cell[data-band="4"] b::before{background:var(--q1)}
.cell.a-r[data-band] b{padding-left:0; padding-right:11px}
.cell.a-r[data-band] b::before{left:auto; right:0}

.cell.n b{font-weight:600}
/* An indicative price is the same number as a firm one and not the same
   offer. Quieter ink and a sub-line that says "indicative", so a cheaper RFQ
   quote cannot win a comparison against a resting book by looking identical
   to it. This is the row-level half of the answer strip's "cheapest firm". */
.cell.ind b{color:var(--ink2); font-weight:500}
.cell.ind i{color:var(--ink3); font-style:italic}
.cell.rng b{font-size:11px}
.cell.rng i{color:var(--ink3)}
.cell.rng i::before{content:"– "}

/* The legend's two empty-state swatches had the same problem as the cells:
   --empty is #20241a on a #181a20 ground, so "feed, no trades" and "no feed —
   unknown" were two blank gaps in a row of visible colour chips. Scoped to
   this legend rather than fixed in style.css, because .lg is shared furniture
   and this is the only surface whose swatches were pointed at a background
   token. Same neutral ink as the cells, so the legend and the table agree. */
#where-legend .lg i.dash{border-color:var(--ink3)}
#where-legend .lg i.hatch{
  border-color:var(--ink3);
  background-image:repeating-linear-gradient(45deg,var(--ink3) 0 1.2px,transparent 1.2px 3.2px);
}

/* ── the preview ────────────────────────────────────────────────────────── */
#sc-pop{
  position:fixed; z-index:var(--z-pop); width:360px; max-width:calc(100vw - 24px);
  background:var(--bg); background-image:linear-gradient(var(--fill-1),var(--fill-1));
  border:1px solid var(--edge-2); border-radius:var(--r-card);
  box-shadow:0 10px 30px rgba(0,0,0,.18);
  padding:11px 13px 12px;
  /* IT PHYSICALLY CANNOT TAKE THE CURSOR. Scrolling past forty rows with a
     preview that can be hovered starts a show/hide loop the instant the card
     slides under the pointer; the reader ends up chasing it. */
  pointer-events:none;
  /* It grows OUT of the row you are pointing at, not next to it. */
  transform-origin:center left;
  transform:scale(.985);
  opacity:0;
  /* Long enough to mask the pop of the first layout, short enough that it is
     never something you wait for. */
  transition:opacity 60ms linear, transform 60ms ease-out;
}
#sc-pop.on{opacity:1; transform:scale(1)}
@media (prefers-reduced-motion:reduce){
  #sc-pop{transition:none; transform:none}
  #sc-pop.on{transform:none}
}
.pp-h{display:flex; align-items:center; gap:7px; font:600 12.5px/1 var(--sans);
  padding-bottom:8px; border-bottom:1px solid var(--line)}
.pp-x{color:var(--ink3)}
.pp-v{margin:9px 0 0; font:11.5px/1.45 var(--sans); color:var(--ink2)}
.pp-v.traded{color:var(--ink)}
.pp-v.unknown{color:var(--ink2); border-left:2px solid var(--empty); padding-left:8px}
.pp-v.listed{color:var(--ink2); border-left:2px dashed var(--empty); padding-left:8px}
.pp-s{display:flex; flex-wrap:wrap; gap:4px 12px; margin-top:9px;
  font:10.5px/1.3 var(--mono); color:var(--ink3)}
.pp-s b{color:var(--ink); font-weight:650}
.pp-m{color:var(--ink2)}
.pp-n{margin:8px 0 0; font:10.5px/1.45 var(--mono); color:var(--ink2)}
.pp-n code{font-family:var(--mono); color:var(--ink)}
.pp-n.muted,#sc-pop .muted{color:var(--ink3)}
.pp-n.pp-stale{color:var(--warn)}
.pp-t{width:100%; border-collapse:collapse; margin-top:9px;
  font:10px/1 var(--mono)}
.pp-t th{text-align:right; color:var(--ink3); font-weight:600; padding:3px 4px;
  border-bottom:1px solid var(--line); text-transform:uppercase; letter-spacing:.05em}
.pp-t th:first-child,.pp-t td:first-child{text-align:left}
.pp-t td{text-align:right; padding:3px 4px; color:var(--ink)}
.pp-more{margin:5px 0 0; font:10px/1 var(--mono); color:var(--ink3)}

/* ── narrow ─────────────────────────────────────────────────────────────── */
/* Under 1100px the overview stacks and the pane takes the full width, so the
   table gets its room back and nothing has to be dropped. */
/* Nothing to do. The one --col-layout above holds at every width, and
   min-width + the sticky identity column handle the narrow case without a
   second layout that could disagree with the first. */


/* ── column modes: THERE ARE NONE ──────────────────────────────────────────
   #where-grid[data-cols="simple"] lived here and hid five of nine columns
   behind a "More detail" button. What it left on screen was Market, Status,
   Last trade and Spot — which is exactly the set the owner could not read a
   purpose into, so the disclosure control was not softening the table, it was
   selecting for the weakest part of it. Six columns now, all of them
   answering a question the reader arrived with, none of them hidden. The long
   tail that genuinely is per-row detail — per-expiry counts, strike bands,
   maker/taker concentration, the tightest quote — is in the hover preview and
   the venue panel, which is where detail ABOUT ONE ROW belongs.

   The only mode left is data-scope, at the top of this file, and it is not a
   preference: it drops the floor column when there is no asset to price. */

/* ── the answer strip ──────────────────────────────────────────────────────
   A SENTENCE, NOT A CHAIN OF FRAGMENTS. It used to be a flex row of six
   clauses joined by interpuncts and set to white-space:nowrap, so it read as
   debris and scrolled sideways when it did not fit. A stat line is the right
   shape for a row of LABELLED numbers; it is the wrong shape for six clauses
   of one argument, because the reader has to supply the grammar. It wraps
   now, it has verbs, and the caveat that keeps it honest ("that is the floor
   on its own") is set quieter rather than dropped. */
.answer-strip{
  flex:none;
  margin:0;padding:11px 14px 12px;
  border-bottom:1px solid var(--line);
  font:400 13px/1.62 var(--sans);color:var(--ink2);
  /* max-width in CH, not px: the measure is about how far the eye travels
     back to the next line, and that is a function of the type, not the pane.
     Without it the sentence runs the full 1,248px of the panel at 1440. */
  max-width:92ch;
}
.answer-strip b{color:var(--ink);font-weight:700}
/* The honesty clause. Quieter than the number, never smaller than readable —
   it is the difference between "protection costs 1.15%" and the truth. */
.answer-strip .as-caveat{color:var(--ink3)}
/* Its own line. Inline, the CTA landed in the middle of the closing clause
   and cut the sentence in half — a button is a full stop, so it goes after
   the full stop. */
.answer-strip .next-step{display:block;width:max-content;margin:9px 0 0}


/* ── the venue page owns the stage (owner report, 2026-08-05) ──────────────
   Opening a row used to STACK the venue page on top of the board: seven KPI
   cards, an asset strip, three pricing cards, and then the entire original
   panel again underneath. Two surfaces, one scroll, no hierarchy: "rối tung".
   When the venue page is open the board yields the stage; the back bar and
   the Close button both return to it (same hash, #trade). */
.view[data-view="overview"]:has(#detail-root:not(:empty)) > .split,
.view[data-view="overview"]:has(#detail-root:not(:empty)) > .board-all{
  display:none;
}
.dt-back{
  display:inline-block;background:none;border:0;cursor:pointer;
  font:600 12px/1 var(--sans);color:var(--ink2);
  padding:0 0 12px;
}
.dt-back:hover{color:var(--ink)}


/* ══ THE TWO VERBS, AND HOW A READER TELLS THEM APART ═══════════════════════
 *
 * Both tables in this app now answer to a click on almost every value, and the
 * clicks do two different things:
 *
 *   FILTER    the value narrows the table you are already looking at. Nothing
 *             navigates, nothing is lost, and clicking it again undoes it.
 *   NAVIGATE  the value takes you to the surface that answers the question it
 *             raises — the cost board, the fill tape, the venue page.
 *
 * A reader must be able to tell which is which BEFORE clicking, and the app
 * already had both marks; nothing new is introduced here:
 *
 *   dotted underline  = filters in place. It is what .sc-b and .tw-b have
 *                       carried since those controls existed.
 *   solid underline   = leaves. It is what .tw-addr.lk — the explorer links,
 *                       the only thing in the fill table that has ever gone
 *                       anywhere — has carried since the tape shipped.
 *
 * No new colour, no new size, no new radius, no shadow. Colour still means
 * exactly one thing on the board (how recently something traded) and mint is
 * still the one interactive accent.
 *
 * WHY TAPE RULES LIVE IN THE SCREENER'S STYLESHEET. tools.css owns .tw-* and
 * is another agent's file this pass. These rules are additive, they are scoped
 * to the classes this pass introduced (.tw-ab, .tw-av, .tw-ex, .tw-go), and
 * screener.css is loaded on every page after tools.css — so they cascade
 * correctly and can be lifted into tools.css verbatim whenever that file is
 * next open. */

/* ── a value that LEAVES, inside a row that is already a link ────────────── */
/* The row is an <a href> and an anchor inside an anchor is not valid HTML —
   the parser unnests it and the ROW's link breaks. So these are spans with
   role=button, activated through the one dispatcher grid.js also gives the
   keyboard. What is lost is ⌘-click on the cell specifically; what a ⌘-click
   still does is open the row's own destination, which is a real answer rather
   than nothing. */
.sc-go[data-go] > b{
  text-decoration:underline var(--line2) 1px;
  text-underline-offset:3px;
}
.sc-go[data-go]:hover > b{text-decoration-color:var(--mint)}

/* ── AND A CELL THAT LEADS NOWHERE DOES NOT LOOK LIKE IT DOES ────────────── */
/* This is the whole non-negotiable, in four declarations. "no feed", a real
   "$0", "no offer" and a floor with no asset picked all render the same
   wrapper — so without the attribute that names a destination the wrapper
   loses its pointer, its hover ground and its underline, and reads as exactly
   what it is: a printed number. */
.sc-b:not([data-v]):not([data-go]){cursor:default}
.sc-b:not([data-v]):not([data-go]):hover{background:none}
.sc-b:not([data-v]):not([data-go]) > b,
.sc-b:not([data-v]):not([data-go]):hover > b{text-decoration:none}

/* ── the asset ticker in the identity cell ───────────────────────────────── */
/* Same dotted mark as every other filter on the board, because it does the
   same thing: scopes the board to one asset, and clears it when you click the
   one you are already scoped to. */
.sc-af{
  cursor:pointer; border-radius:3px;
  text-decoration:underline dotted var(--line2) 1px;
  text-underline-offset:3px;
  transition:text-decoration-color 80ms linear;
}
.sc-af:hover{text-decoration-color:var(--mint)}
.sc-af:focus-visible{outline:2px solid var(--mint2); outline-offset:1px}
/* The venue half of the name goes where the row goes, so it takes the row's
   affordance and not a second one. */
.sc-vn{cursor:pointer}

/* ── the fill table's two-verb address cell ──────────────────────────────── */
/* .tw-b is width:100% because in every other column it fills its cell. Here it
   shares the cell with the explorer arrow, so it takes only what it needs and
   the arrow keeps its 12px on the right. */
.c-taker .tw-b, .c-maker .tw-b{
  width:auto; flex:1 1 auto; height:auto;
  flex-direction:row; align-items:center; gap:7px;
  margin:0 -6px; padding:2px 6px;
}
/* cursor:help belongs to a plain address, which is a value you can only read.
   Inside a button it is a value you can act on. */
.tw-b .tw-av{cursor:inherit}
.tw-b:hover .tw-av{
  text-decoration:underline dotted var(--mint2) 1px;
  text-underline-offset:3px;
}
.tw-b.on .tw-av{color:var(--mint)}
/* The arrow that leaves. Solid underline is inherited from .tw-addr.lk — this
   only stops the glyph itself from being underlined, which reads as a stray
   hairline at 11px. */
.tw-ex{
  flex:none; font-size:11px; line-height:1;
  text-decoration:none; padding:2px;
}
.tw-ex:hover{text-decoration:none; color:var(--mint)}

/* ── the way out of a filtered tape ──────────────────────────────────────── */
/* A real <a href> in the filter bar, styled as the bar's own furniture: same
   height, same radius, same mono as .tw-chip and .tw-clear beside it. The mint
   arrow is the app's existing "this leads somewhere" mark, borrowed from
   .next-step rather than invented here. */
.tw-go{
  display:inline-flex; align-items:center; gap:6px;
  padding:4px 8px; border-radius:5px;
  border:1px solid var(--line2); background:transparent;
  color:var(--ink2); font:10.5px/1 var(--mono); text-decoration:none;
  white-space:nowrap;
}
.tw-go b{color:var(--mint); font-weight:700; transition:transform var(--t)}
.tw-go:hover{color:var(--ink); border-color:var(--mint2)}
.tw-go:hover b{transform:translateX(2px)}
.tw-go:focus-visible{outline:2px solid var(--mint2); outline-offset:2px}

/* ══════════════════════════════════════════════════════════════════════════
 * THE BAND — the control above the board, and the cards it recomputes
 * ══════════════════════════════════════════════════════════════════════════
 * Owned by grid.js. Everything here is built out of the app's OWN tokens, so
 * there is no second design system in this file and nothing to firewall.
 *
 * ── WHAT instrument.css:343 PINS, AND WHAT IT DOES NOT ───────────────────
 * `body *:not(svg):not(svg *)` pins font-family, font-size, font-weight,
 * letter-spacing, border-radius and box-shadow with !important, and an
 * important declaration beats a normal one at ANY specificity. So:
 *   - NO NEW SIZE IS INVENTED HERE. Four sizes, from the scale the app already
 *     declares: --t-xs (11px) every label, --t-sm (15px) every sentence,
 *     --t-lg (30px) the eleven venue counts, and nothing at --t-hero, because
 *     this screen has eleven figures meant to be read against each other rather
 *     than one headline, and a 76px figure repeated eleven times is not a
 *     hierarchy. The four-card KPI row that also used --t-lg was removed on
 *     2026-09-05 (owner: "this data you can remove"); its coverage disclosure
 *     survives on the cards' own fills row and in the #cb-foot line, both of
 *     which are rendered text and not a title attribute.
 *   - box-shadow is pinned to none, so a selected or hovered card is marked
 *     with `outline`, which the normaliser does not touch.
 *   - border-radius is pinned to 0 and that pin does NOT reach pseudo-elements:
 *     `body *` matches elements, never ::-webkit-slider-thumb. That is why the
 *     handles below can be round without !important, and it is confirmed on the
 *     rendered PNG rather than inferred from the selector.
 *
 * ── --accent-ink IS NOT THE INK FOR AN ACCENT FILL ───────────────────────
 * Read off the live page, not off the name: this theme resolves --accent and
 * --accent-ink to the SAME value (#ff7a1a). Anything painted ON an accent fill
 * takes --bg, or it renders as a blank blob. Same for --fill-1 and --fill-2,
 * which both resolve to transparent and are therefore not grounds.
 *
 * ── THE SECOND BLANKET, AND IT IS THE ONE THAT BITES ─────────────────────
 * instrument.css:1883 sets `font-family: var(--sans) !important` on the SAME
 * `body *` selector, LATER in the file than :343. So the resolved default face
 * on the live page is INTER, not the mono stack, whatever :343 says. Measured
 * in the browser rather than read off a comment: getComputedStyle(document.body)
 * .fontFamily is "Inter" on the served theme. Every FIGURE below therefore
 * claims `font-family: var(--mono) !important` for itself, or its digits are
 * proportional and a column of counts does not line up. agent 1's system.css
 * makes the same claim for .ds-stat-n; it was confirmed here independently
 * before it was adopted.
 *
 * ── THIS IS THE TWO-HANDLE EXTENSION OF .ds-range ────────────────────────
 * system.css (agent 1) ships .ds-range / .ds-range-i, a ONE-handle rail lifted
 * from payoff.css. A band needs two handles on one track, which that primitive
 * does not cover, so .cb-h is its two-handle sibling and is deliberately drawn
 * to the SAME metrics: 20px webkit thumb, 16px moz, --accent fill, a 2px --bg
 * ring, the same focus ring. When the two branches meet, .cb-h should fold into
 * .ds-range-i as its multi-thumb variant rather than stay a second slider.
 * ONE DELIBERATE DIFFERENCE: touch-action is pan-y here and `none` there. Both
 * fire the horizontal drag — verified with a REAL CDP touch drag on an iPhone
 * 13 profile, not with a synthesised PointerEvent, which a native range input
 * correctly ignores. `none` also swallows the vertical swipe, and this rail
 * spans the full width of a board that is ~4,000px tall on a phone, so it would
 * trap the page scroll under the reader's thumb.
 *
 * ── ONE ACCENT, ONE JOB ──────────────────────────────────────────────────
 * payoff.css set the rule for this app and this sheet follows it: the accent
 * marks THE LIVE THING YOU ARE MOVING and nothing else — the selected span of
 * each rail, the two handles, and the venue cards' count bar. It is never spent
 * on a heading, a border or a state.
 *   --neg    is never used here. Nothing on this screen is a loss.
 *   --warn   marks a cell we did not read, and only that.
 *   ink      everything else, in three steps.
 *
 * ── THE HATCH IS THE PRODUCT'S ONE HONEST TEXTURE ────────────────────────
 * A cell we cannot see is drawn as diagonal hatch on --warn and carries words.
 * It is never a 0, never an empty box and never a pale colour that could be
 * mistaken for a small number. The table below this control already uses the
 * phrase "our gap, not their silence" for the same fact; the mark here is the
 * same fact in the same vocabulary rather than a second one that can drift.
 */

.cb { display: block; padding: 0 0 4px; }
.cb[hidden] { display: none; }

.cb-eyebrow {
  display: block;
  font-size: var(--t-xs) !important;
  letter-spacing: var(--tr-label, .09em) !important;
  text-transform: uppercase;
  color: var(--ink3);
  white-space: nowrap;
}

/* ── 1. THE BAND ───────────────────────────────────────────────────────── */

.cb-band {
  border: 1px solid var(--line);
  border-top: 2px solid var(--accent);
  background: var(--s1);
  padding: 14px 16px 16px;
  /* 18px, not 14px: the four-card KPI row that used to sit between this control
     and the card heading was carrying that gap, and it was removed on
     2026-09-05. Measured on the rendered PNG, not guessed from the number. */
  margin: 0 0 18px;
}
.cb-band-h {
  display: flex; align-items: baseline; gap: 10px 14px; flex-wrap: wrap;
  margin: 0 0 14px;
}
.cb-band-say {
  margin: 0; flex: 1 1 260px; min-width: 0;
  color: var(--ink2); font-size: var(--t-sm) !important; line-height: 1.5;
}
.cb-reset {
  font-size: var(--t-xs) !important; letter-spacing: var(--tr-label, .09em) !important;
  text-transform: uppercase;
  padding: 6px 11px; cursor: pointer;
  background: transparent; color: var(--ink2);
  border: 1px solid var(--line2);
}
.cb-reset:hover { color: var(--ink); border-color: var(--ink3); }
.cb-reset:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* TWO RAILS SIDE BY SIDE AT WIDTH, STACKED BELOW 900. They are one control in
   two axes and reading them as a pair is the point: narrowing tenor visibly
   redraws the moneyness histogram, which is what "linked" means here. */
.cb-rails { display: grid; grid-template-columns: 1fr 1fr; gap: 18px 34px; }
@media (max-width: 900px) { .cb-rails { grid-template-columns: 1fr; gap: 22px; } }

.cb-ax-h { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.cb-ax-v {
  font-family: var(--mono) !important;
  font-size: var(--t-lg) !important;
  line-height: 1.05; color: var(--ink);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.cb-ax-v em { font-style: normal; color: var(--ink3); padding: 0 .34em;
  font-size: var(--t-xs) !important; letter-spacing: var(--tr-label, .09em) !important;
  text-transform: uppercase; vertical-align: middle; }

/* ── the track: histogram, dimming, selected span, two handles ───────────
   HEIGHT IS FIXED AND THE HISTOGRAM FILLS IT. The bars are counts per bucket,
   scaled to the tallest bucket, so the shape is a distribution and the height
   of any one bar is not a quantity you can read off — which is why no axis is
   drawn for it and no number is printed on it. It is there to tell you where
   the contracts are before you drag, and nothing more. */
.cb-track {
  position: relative;
  height: 62px;
  margin: 10px 0 0;
  /* The rail's usable travel is inset by half a handle at each end so the
     handle CENTRE can reach the extent rather than stopping at the track's
     edge. Everything positioned in percent below shares this box. */
  padding: 0;
}
.cb-hist {
  position: absolute; left: 0; right: 0; bottom: 14px; top: 0;
  display: flex; align-items: flex-end; gap: 1px;
}
/* THE DIMMING IS WHAT MAKES THE BAND A BAND, AND IT IS CARRIED BY THE BARS.
   The first pass laid a --bg panel over each excluded end of the rail. On a
   stretch with no contracts in it that panel is a black slab across the
   control, which is what the first PNG showed and it reads as a fault rather
   than as a selection. Painting the bars instead means an excluded bucket stays
   VISIBLE and goes quiet, which is the difference between a filter and a claim
   that nothing is out there.
   data-on is absent for a beat on first paint, so the IN colour is the default
   and the OUT state is what the attribute buys. An unpainted rail therefore
   opens bright rather than blank. */
.cb-hist i {
  flex: 1 1 0; min-width: 0; height: 0;
  background: var(--ink3);
}
.cb-hist i[data-on="0"] { background: var(--line); }

.cb-line {
  position: absolute; left: 0; right: 0; bottom: 13px; height: 1px;
  background: var(--line2); pointer-events: none;
}
.cb-sel {
  position: absolute; bottom: 12px; height: 3px;
  background: var(--accent); pointer-events: none;
}

/* ── the handles ────────────────────────────────────────────────────────
   TWO NATIVE RANGE INPUTS, STACKED. Native because the platform ships the
   keyboard contract (arrows, PageUp/PageDown, Home, End), the touch target and
   the focus ring, and every hand-rolled slider on this codebase has dropped one
   of the three.

   pointer-events is none on the INPUT and auto on its THUMB, which is what lets
   two of them share one track: without it the upper input's invisible track
   covers the lower input's thumb and one handle can never be grabbed. The
   event target is still the input — a pseudo-element is a hit region, not a
   target — so the focus, the value and touch-action all belong to it.

   touch-action IS ON THE INPUT AND NOT ON .cb-track. It has to be on the
   element the pointer actually lands on; declared on the wrapper it is inert
   and the drag never fires on a phone. pan-y and not none, so a vertical swipe
   still scrolls the page from on top of the control. */
.cb-h {
  position: absolute; left: 0; bottom: 0;
  width: 100%; height: 28px; margin: 0; padding: 0;
  -webkit-appearance: none; appearance: none;
  background: transparent;
  pointer-events: none;
  touch-action: pan-y;
}
.cb-h::-webkit-slider-runnable-track { height: 28px; background: transparent; border: 0; }
.cb-h::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  pointer-events: auto;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--bg);
  margin-top: 4px;
  cursor: ew-resize;
}
.cb-h::-moz-range-track { height: 28px; background: transparent; border: 0; }
.cb-h::-moz-range-thumb {
  pointer-events: auto;
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--accent); border: 2px solid var(--bg); cursor: ew-resize;
}
.cb-h:focus { outline: none; }
/* THE RING GOES ROUND THE TRACK, NOT ROUND THE THUMB. A focus ring on a 18px
   pseudo-element inside a 28px box is clipped on three sides; on the input it
   is a visible rectangle that says which axis has the keyboard. */
.cb-h:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }

.cb-ax-e {
  display: flex; justify-content: space-between; margin-top: 4px;
  color: var(--ink3);
}
.cb-ax-e i {
  font-style: normal;
  font-family: var(--mono) !important;
  font-size: var(--t-xs) !important; letter-spacing: var(--tr-label, .06em) !important;
  font-variant-numeric: tabular-nums;
}

.cb-wait {
  margin: 12px 0 0; color: var(--ink2);
  font-size: var(--t-sm) !important; line-height: 1.5;
}
.cb-wait[hidden] { display: none; }
.cb-band[data-state="error"] .cb-wait { color: var(--warn); }
/* A partial read is marked, never smoothed over: the counts stand but they say
   what they cover. */
.cb-band[data-state="partial"] .cb-wait {
  color: var(--warn);
  border-left: 3px solid var(--warn); padding-left: 10px;
}
/* A RAIL YOU CAN GRAB WITH NOTHING UNDER IT TO RECOMPUTE IS A LIE ABOUT WHAT
   THE CONTROL DOES. While the contract list is loading or has failed the two
   rails are dimmed AND inert: the handles are still where the last band left
   them, so nothing jumps, but dragging one would move a window over numbers
   that are not on screen. */
.cb-band[data-state="loading"] .cb-rails,
.cb-band[data-state="error"] .cb-rails { opacity: .3; pointer-events: none; }
.cb-band[data-state="loading"] .cb-h,
.cb-band[data-state="error"] .cb-h { pointer-events: none; }
.cb-vhead[hidden], .cb-foot[hidden], .cb-scope[hidden] { display: none; }

/* ── 2. THE VENUE CARDS ────────────────────────────────────────────────── */

.cb-vhead {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  margin: 0 0 9px;
}
.cb-vhead-n {
  font-style: normal; color: var(--ink3);
  font-size: var(--t-xs) !important; letter-spacing: var(--tr-label, .04em) !important;
}
.cb-vgrid {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: 10px; margin: 0 0 14px;
}
@media (max-width: 1180px) { .cb-vgrid { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 860px)  { .cb-vgrid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px)  { .cb-vgrid { grid-template-columns: 1fr; } }
.cb-vgrid[hidden] { display: none; }

.cb-v {
  display: grid;
  grid-template-rows: auto auto auto auto auto;
  gap: 9px;
  padding: 12px 13px 13px;
  background: var(--s1);
  border: 1px solid var(--line);
  color: inherit; text-decoration: none;
  min-width: 0;
}
.cb-v[href]:hover { border-color: var(--line2); background: var(--s2); }
.cb-v:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }

/* A VENUE THAT DOES NOT LIST THE ASSET STILL GETS A CARD. It is drawn as an
   outline with nothing in it, which is what it is. Removing it would shrink the
   denominator of every count on this screen without saying so. */
.cb-v[data-lists="0"] {
  background: transparent;
  border-style: dashed;
  opacity: .55;
}
/* Listed, but nothing inside the band. This is a MEASURED zero — we read the
   venue's whole chain and none of it falls here — so it is drawn as a 0 and
   dimmed, never hatched. Hatch means "we did not look". */
.cb-v[data-lists="1"][data-here="0"] .cb-v-c { color: var(--ink3); }

.cb-v-h { display: flex; align-items: center; gap: 7px; min-width: 0; }
.cb-v-mk { display: inline-flex; flex: 0 0 auto; }
.cb-v-mk svg, .cb-v-mk img { width: 18px; height: 18px; display: block; }
.cb-v-n {
  font-size: var(--t-xs) !important; letter-spacing: var(--tr-label, .09em) !important;
  text-transform: uppercase; color: var(--ink2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.cb-v-fig { display: block; min-width: 0; }
.cb-v-c {
  display: block;
  font-family: var(--mono) !important;
  font-size: var(--t-lg) !important; line-height: 1; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.cb-v-u {
  display: block; font-style: normal; margin-top: 4px;
  font-size: var(--t-xs) !important; line-height: 1.4; color: var(--ink3);
}

/* ── the spread rail: where this venue's contracts sit in the band ───────
   TWELVE BUCKETS, ALL ELEVEN CARDS ON THE SAME HORIZONTAL SCALE, each scaled
   to its OWN tallest bucket vertically. Horizontal is comparable across cards
   and is the reading: a venue whose bars sit hard against 1.0 and one whose
   bars run the whole width are offering different things. Vertical is NOT
   comparable across cards and is not meant to be, which is why no axis is
   drawn on it and why the magnitude is stated once, in figures, above.
   A venue with nothing in the band gets a rail of zero-height bars: the row
   holds its space so eleven cards stay the same height, and an empty rail
   under a "0" is the same fact twice, which is the point. */
.cb-v-sp {
  display: flex; align-items: flex-end; gap: 2px;
  height: 22px; border-bottom: 1px solid var(--line);
}
.cb-v-sp i { flex: 1 1 0; min-width: 0; height: 0; background: var(--ink3); }
.cb-v-sp[data-on="0"] i { background: var(--line); }
/* A venue that does not list the asset has no distribution to draw, and an
   empty rail there would look like a venue that lists it and has none. */
.cb-v[data-lists="0"] .cb-v-sp { visibility: hidden; }

/* ── the firm row ────────────────────────────────────────────────────────
   Three kinds, three drawings, and the difference between them is the point:
     book    a bar. A count of resting two-sided quotes over the contracts in
             the band on a venue whose book we read.
     rfq     no bar at all and the words "quoted on request". Not a short bar,
             not a zero: an RFQ venue rests nothing, so firmness does not apply.
     unread  a hatched bar. We did not call the book. The server says so itself
             in quote_model_notes, and that sentence is on the card's title. */
.cb-v-firm { display: flex; align-items: center; gap: 8px; min-width: 0; }
.cb-bar {
  flex: 0 0 46px; height: 4px; background: var(--line); position: relative;
  overflow: hidden;
}
.cb-bar > span { display: block; height: 100%; width: 0; background: var(--accent); }
.cb-v-firm i {
  font-style: normal; min-width: 0;
  font-size: var(--t-xs) !important; line-height: 1.35; color: var(--ink3);
  overflow: hidden; text-overflow: ellipsis;
}
.cb-v-firm[data-kind="rfq"] .cb-bar,
.cb-v-firm[data-kind="na"] .cb-bar { visibility: hidden; }
.cb-v-firm[data-kind="unread"] .cb-bar {
  background: repeating-linear-gradient(135deg,
    var(--warn) 0 2px, transparent 2px 5px);
}
.cb-v-firm[data-kind="unread"] i { color: var(--warn); }

/* ── the fills row ───────────────────────────────────────────────────────
   3 of the 11 venues publish a fill feed we read. On the other 8 this row is
   HATCHED and says so. It is never blank and never 0: a blank reads as nothing
   happened, and 0 claims we measured nothing happening. */
.cb-v-see {
  display: flex; align-items: center; gap: 7px;
  border-top: 1px solid var(--line); padding-top: 8px; min-width: 0;
}
/* THE PIP IS A TEXTURE, NOT A GLYPH. At 8px square the 2px hatch below read as
   a tick mark on the rendered PNG, which is the opposite of what it means. A
   wider, shorter bar reads as hatch at the same weight. */
.cb-v-see::before {
  content: ""; flex: 0 0 14px; height: 7px; background: var(--line2);
}
.cb-v-see i {
  font-style: normal; min-width: 0;
  font-size: var(--t-xs) !important; line-height: 1.35; color: var(--ink3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cb-v-see[data-kind="traded"]::before { background: var(--pos); }
.cb-v-see[data-kind="traded"] i { color: var(--ink2); }
.cb-v-see[data-kind="quiet"]::before { background: var(--line2); }
.cb-v-see[data-kind="unread"]::before {
  background: repeating-linear-gradient(135deg,
    var(--warn) 0 2px, transparent 2px 4px);
  outline: 1px solid var(--warn);
}
.cb-v-see[data-kind="unread"] i { color: var(--warn); }
.cb-v-see[data-kind="na"] { visibility: hidden; }

/* ── the provenance line ───────────────────────────────────────────────── */
.cb-foot {
  margin: 0 0 6px; color: var(--ink3);
  font-size: var(--t-xs) !important; line-height: 1.6;
  max-width: 96ch;
}

/* THE SCOPE LINE. It sits directly above the table's own heading and it is the
   only sentence on this screen that is about a DIFFERENT object, so it is
   ruled off rather than run on. --warn when the band is narrowed, because that
   is when a reader can actually be misled; ink3 when the band is wide open and
   the two halves happen to agree. */
.cb-scope {
  margin: 10px 0 2px; padding: 8px 0 0;
  border-top: 1px solid var(--line);
  color: var(--ink3);
  font-size: var(--t-xs) !important; line-height: 1.6;
  max-width: 96ch;
}

/* ── touch ──────────────────────────────────────────────────────────────
   A 18px handle is under the 44px a finger needs. The INPUT is what receives
   the pointer, so its height is what grows; the visible handle grows with it so
   the target and the mark are the same object rather than a small dot inside a
   large invisible box. */
@media (pointer: coarse) {
  .cb-track { height: 74px; }
  .cb-h { height: 40px; }
  .cb-h::-webkit-slider-runnable-track { height: 40px; }
  .cb-h::-webkit-slider-thumb { width: 26px; height: 26px; margin-top: 7px; }
  .cb-h::-moz-range-thumb { width: 24px; height: 24px; }
  .cb-hist { bottom: 20px; }
  .cb-line { bottom: 19px; }
  .cb-sel { bottom: 18px; }
}
