/* Moneyness — institutional structure, crypto surface.
 *
 * Reference mix: DeGiro / BNP dense professional tables, Aave's soft depth,
 * Hyperliquid's mint-on-near-black. Serious enough for a desk, modern enough
 * for a DEX.
 *
 * Validated against the #fdfcf7 paper ground before use:
 *   recency ramp  #4dabf7 -> #0d4478    all checks pass, light end 2.41:1
 *   venue hues    #2563eb #d97706 #db2777   all-pairs CVD + contrast pass
 *   skew poles    #f87171 <-> #60a5fa   dE 20.0 protan, 27.8 normal
 * Rejected: blue+magenta+violet sets at dE 4.7-5.4 protan — indistinguishable
 * to a red-green colourblind reader however good they look.
 */
:root{
  /* Paper — the first palette chosen. Warm ground, blue data ramp.
   * All values previously validated: ramp light end clears the surface at
   * 2.41:1, venue trio passes all-pairs CVD, skew poles pass. The dark mint
   * variant is preserved at /tmp/style.mint.bak if it is ever wanted back.
   *
   * Note the ramp direction flips with the ground: on paper the DARKEST step
   * is the strongest, so the freshest trades are the darkest cells. On the
   * dark theme it was the reverse. grid.js maps freshest -> --q5 either way,
   * so the token values carry that difference and the JS does not change. */
  /* WISE RETHEME (2026-08-05). Ground moves from warm paper to pure white with
   * sage-tinted surfaces (#e8ebe6 family); ink moves to the olive near-black
   * #0e0f0c; the interactive accent moves from brand blue to the green family,
   * with the lime #9fe870 reserved for FILLS (brand tokens below) and a deep
   * green #157033 for accent TEXT, because lime on white is 1.5:1 and can
   * never carry a glyph. All values below re-measured this session:
   *   ink 19.2:1 white / 16.0:1 sage · ink2 9.4:1 · ink3 5.3:1 white
   *   mint #157033  6.2:1 white, 5.1:1 sage — AA at caption size
   *   ramp q1 #4dabf7 2.48:1 white — clears the 2:1 floor the paper ground set
   * The blue data ramp and venue trio are KEPT: they are data-viz, already
   * CVD-validated, and the Wise system itself keeps green out of data. */
  /* MINIMAL / SCIENCE (2026-08-05, direction call by Danny). The surface is
   * an instrument, not a brand moment: warm-white paper, neutral hairlines,
   * near-black ink, ONE restrained green for interaction, and no fills where
   * a line will do. The lime brand fill is retired from chrome; ink itself is
   * the fill for primary controls, which is as quiet as a button can be.
   * Re-measured this session: ink 18.3:1, ink2 8.4:1, ink3 5.2:1,
   * mint 6.0:1 on bg, all AA or better on bg and s1. */
  /* BINANCE VIBE (2026-08-05, direction call by Danny). The instrument
   * becomes an exchange: three grey levels carry ALL elevation (bg, surface,
   * elevated), shadow exists only on popovers, and the accent is the Binance
   * yellow #F0B90B. Yellow cannot carry text on a light ground (1.6:1), so
   * on light the accent TEXT is the deep gold #8A6600 (5.0:1) and yellow
   * appears only as a FILL with dark text. Re-measured this session:
   * ink 15.2:1, ink2 8.2:1, ink3 4.9:1 bg / 4.3:1 s2 large-only. */
  --bg:#fafafa;
  --card:#ffffff;
  --elev:0 4px 16px rgba(24,26,32,.16), 0 0 0 1px rgba(24,26,32,.04);
  --elev-sm:none;
  --s1:#f5f5f5;
  --s2:#eaecef;
  --s3:#dfe2e7;
  --line:#eaecef;
  --line2:#d5d9de;
  --glass:rgba(24,26,32,.04);

  --ink:#1e2329;
  --ink2:#474d57;
  --ink3:#5f6873;

  --mint:#8a6600;
  --mint2:#5c4400;
  --mint-dim:rgba(240,185,11,.16);

  --q1:#4dabf7; --q2:#3388d8; --q3:#2470b5; --q4:#175d97; --q5:#0d4478;
  --v1:#1971c2; --v2:#e8590c; --v3:#087f5b;
  --neg:#e03131; --mid:#868e96; --pos:#1971c2;
  --empty:#e3e5e8;

  --good:#087f5b; --warn:#e8590c; --bad:#e03131;

  /* Category hues 4 and 5, for the Flow treemap. The map drew five
   * categories from three hues: equity and gold both landed on the warn
   * orange, and crypto/index/pre-IPO were three blues a CVD reader cannot
   * split. Gold gets the gold family, pre-IPO the violet. */
  /* v4 moved off the gold family: the Binance accent IS gold now, and a
   * data category wearing the interactive colour would read as a control. */
  --v4:#0f766e;
  --v5:#7048e8;

  /* Category tokens, light. See the dark block for why these exist and for the
   * measured limit on six-way separation. Values are the current effective
   * ones — --cat-crypto was --q4, the fourth step of the recency ramp. */
  --cat-crypto:#175d97;
  --cat-equity:var(--v2);
  --cat-etf:var(--v3);
  --cat-index:var(--v1);
  --cat-gold:var(--v4);
  --cat-preipo:var(--v5);

  /* Chip ink, light. Defined for the first time — see the dark block. */
  --good-ink:#065f46;
  --warn-ink:#9a3412;

  /* THE BRAND FILL. Lime is a surface colour, never a text colour: it ships
   * with its own ink because #9fe870 carries 1.5:1 against white and no glyph
   * survives on it except the near-black. Same values in both themes — the
   * flag does not change colour at night. */
  /* The primary fill is the Binance yellow, dark text on it (9.7:1). */
  --brand-fill:#f0b90b;
  --brand-fill-2:#fcd535;
  --on-brand:#181a20;
  --focus:rgba(148,110,0,.6);
  --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
  --sans:"Inter","Segoe UI",system-ui,-apple-system,Helvetica,Arial,sans-serif;

  /* THE ALPHA LADDER — four roles, one overlay hue.
   * Audited before this existed: 48 filled elements on the overview alone, all
   * SOLID, drawn from 9 distinct background colours. A solid fill has to be
   * re-picked every time the thing behind it changes, which is why --s1..--s3
   * kept multiplying; a translucent overlay is correct over the paper ground,
   * over the body's two radial gradients, and over another panel, unchanged.
   *
   * --ov is the overlay HUE as raw channels so the alphas can be authored per
   * theme. On paper it is the warm ink that --s1..--s3 and --line already walk
   * toward, NOT neutral black: solved against the existing tokens, .17 of
   * rgb(61,52,35) over --bg reproduces --line and .28 reproduces --line2, so
   * the ladder lands on the palette that was already contrast-validated rather
   * than greying the page out.
   *
   * DO NOT copy dark's values onto light. rgba(255,255,255,.04) is the
   * reference implementation and it is invisible on a #fdfcf7 ground — that is
   * the whole reason this is a token and not a literal. */
  /* Re-solved for the sage ground: the overlay hue is now the deep forest the
   * surface family already walks toward, not the old warm ink. .17 over white
   * lands on the sage line family rather than greying the page. */
  --ov:24,26,32;
  --fill-1:rgba(var(--ov),.05);   /* card / panel fill      */
  --fill-2:rgba(var(--ov),.09);   /* control fill           */
  --edge-1:rgba(var(--ov),.17);   /* card / panel border    */
  --edge-2:rgba(var(--ov),.28);   /* control border         */

  /* ONE RADIUS SCALE. Measured before this: TEN live radii — 5px on 82
   * elements, 3px on 69, 4px on 30, 6px on 28, then 8, 99, 10, 2, 7 and 50%.
   * Nothing chose those; each was picked next to whatever was already there.
   * Four steps, each with a job, and --r is kept pointing at the card step so
   * the stylesheets that already say var(--r) move with the scale instead of
   * being left behind at 8px. */
  /* Wise scale: the reference runs 8/12/16/24 with 24 as the card signature.
   * On a data surface this dense, 24 rounds away real table corners, so the
   * card step sits at 16 and controls at 12 — the same ratios, one notch in. */
  /* One display step. The whole app ran 9px to 15.5px and hierarchy was
   * carried by weight alone; the numbers that ARE the pitch (the recording
   * counter, the footer counter) now get scale. Inter 900 is loaded and was
   * previously unused at any size a reader notices. */
  --fs-display:24px;

  --r-mark:3px;         /* data swatches: legend keys, bars, coverage cells */
  --r-ctl:6px;          /* every control                                    */
  --r-card:8px;         /* every card and panel                             */
  --r-pill:999px;       /* status chips only                                */
  --r:var(--r-card);

  /* Height of the fixed chrome: header 61 + trust 40 + ticker 46. Published
   * the way the reference implementations publish theirs, so nothing else has
   * to hard-code it. It is EXACT rather than approximate only because the
   * three bands above are given explicit heights — the trust bar used to wrap
   * to a second line at 1280 and silently make this number wrong by 23px.
   * Note the stage no longer needs it to position sticky children: a pane is
   * its own scrollport now, so its sticky rows stick at 0. */
  /* header 61 + trust bar 40. Was 147 when a 46px ticker sat between them;
     removing the ticker without correcting this left 46px of dead space above
     every section, because the stage sizes itself from this number. If the
     chrome changes height again, this must change with it — it is the one
     value in the layout that cannot be derived from the elements it describes. */
  --layout-top-offset:101px;

  /* One named z-scale. Measured before this: 14 of 17 sticky elements sat at
   * z-index:auto, so wherever two of them crossed the winner was decided by
   * DOM order — the frozen first column of the coverage table and the sticky
   * header resolved differently depending on which section rendered first. */
  --z-raised:1;
  --z-sticky:20;
  --z-header:30;
  --z-pop:60;
}

/* NIGHT SHIFT — the palette this app shipped with before the paper conversion.
 * Recovered rather than reinvented: these exact values were already contrast-
 * and CVD-validated, and every token name matches the block above, so no rule
 * anywhere else in the app has to know which theme is active.
 *
 * READ BEFORE CHANGING THE RAMP. q1..q5 runs LIGHT -> DARK on paper and
 * DARK -> BRIGHT here. That is not an inconsistency to tidy up: grid.js maps
 * the freshest trade to --q5 in both themes, so the reversal has to live in the
 * token VALUES or the freshest cells would render as the faintest ones on one
 * of the two grounds. The JavaScript stays identical because of this. */
:root[data-theme="dark"]{
  /* WISE NIGHT (2026-08-05). The ground is the Wise ink itself — #0e0f0c with
   * its olive cast — and the accent is the lime at full strength: #9fe870
   * measures 13.05:1 here, so at night the lime IS a legal text colour and
   * --mint carries it directly. Surfaces walk the ink toward olive, not teal.
   * All values re-measured this session: ink 17.7:1, ink2 9.7:1, ink3 6.2:1,
   * good 10.0:1, pos 7.6:1, neg 6.9:1 — every one clears AA on both --bg and
   * --s1. */
  /* MINIMAL / SCIENCE, night. Neutral near-black, not olive: at night the
   * instrument goes monochrome and the data ramp is the only bright thing.
   * The interactive green steps down from brand lime to a phosphor that
   * still reads 11.4:1. Re-measured: ink 16.6:1, ink2 8.3:1, ink3 5.3:1. */
  /* BINANCE DARK, the brand default. Three greys are the whole elevation
   * story: #181A20 ground, #1E2329 surface, #2B3139 raised/hover. Yellow is
   * a legal text colour here (9.7:1). Re-measured: ink 14.7:1, ink2 9.2:1,
   * ink3 5.9:1 bg / 4.5:1 s2. */
  --bg:#181a20;
  --card:#1e2329;
  --elev:0 4px 16px rgba(0,0,0,.4), 0 0 0 1px rgba(255,255,255,.04);
  --elev-sm:none;
  --s1:#242930;
  --s2:#2b3139;
  --s3:#353b45;
  --line:#2b3139;
  --line2:#3b424d;
  --glass:rgba(255,255,255,.04);

  --ink:#eaecef;
  --ink2:#b7bdc6;
  --ink3:#949ead;

  --mint:#f0b90b;
  --mint2:#fcd535;
  --mint-dim:rgba(240,185,11,.12);

  /* Recency ramp, rebuilt in the lime family. Same contract as before: the
   * freshest trade maps to --q5, so on this ground the ramp runs DARK -> BRIGHT
   * and the brightest cell is the brand lime. Monotone in relative luminance
   * (.070 .137 .263 .448 .663 — measured, each step ≥ .06 apart) and the dark
   * end clears the ground at 2.19:1. */
  /* RECENCY RAMP, REBUILT IN BLUE (2026-08-05 visual direction pass).
   *
   * It was lime (#375721 -> #9fe870), left over from the Wise theme. Two
   * things were wrong with that once the accent became Binance yellow:
   *
   *   1. HUE COLLISION. Lime and #f0b90b are neighbours. The brightest ramp
   *      step — "traded in the last hour", the single most important state on
   *      the board — sat one hue step from the colour that means "control,
   *      click me". A fresh trade and a button read as the same family.
   *   2. THE RAMP CHANGED HUE WITH THE THEME. Light ran blue, dark ran lime.
   *      One encoding, two hues, depending on time of day.
   *
   * Blue is the fix for both: it is the complement of the yellow accent, so a
   * data cell can never be mistaken for a control, and it is the hue light was
   * already using, so the ramp now means the same thing in both themes.
   *
   * Steps are the documented blue ramp's 600/500/400/300/200. Validated as an
   * ORDINAL ramp (the correct gate for a sequential encoding) on this ground:
   * lightness monotone PASS, adjacent dL >= 0.06 PASS, hue spread 3 deg PASS,
   * and the dark end #184f95 clears the surface at 2.15:1 against the 2:1
   * floor. The first draft ran darker (#1b3a5c) and measured 1.50:1 — it
   * failed, which is why these are the reference steps and not hand-mixed.
   *
   * Direction is unchanged: grid.js maps freshest -> --q5 in both themes, so
   * on this ground the ramp runs DARK -> BRIGHT and the freshest cell is the
   * brightest. The token values carry the flip; the JS does not change. */
  --q1:#184f95; --q2:#256abf; --q3:#3987e5; --q4:#6da7ec; --q5:#9ec5f4;
  --v1:#2563eb; --v2:#d97706; --v3:#db2777;
  --neg:#f87171; --mid:#7f837b; --pos:#60a5fa;
  --empty:#20241a;

  /* Good is emerald, not lime: the brand fill cannot double as a status
   * colour or every CTA reads as "success". #34d399 sits a full hue step
   * toward cyan and still measures 10:1 here. */
  /* Status speaks exchange: Binance green up, Binance red down. The skew
   * poles --neg/--pos stay blue/red on purpose - that pair is the
   * CVD-validated DATA encoding and profit/loss language must not eat it. */
  --good:#0ecb81; --warn:#f0b90b; --bad:#f6465d;
  --v4:#14b8a6;
  --v5:#9775fa;

  /* CATEGORY TOKENS — the Flow treemap's six asset classes.
   *
   * These exist because tools.js mapped `crypto` to --q4, a step of the
   * RECENCY RAMP. A categorical encoding was borrowing ink from a sequential
   * one, which meant the ramp could not be retuned without silently repainting
   * the treemap: moving the dark ramp from lime to blue would have turned the
   * largest category on the page from green to blue as a side effect, with
   * nothing in either file to warn anyone. Decoupling costs one indirection
   * and zero pixels — the values below are the CURRENT effective ones. */
  --cat-crypto:#82c44e;
  --cat-equity:var(--v2);
  --cat-etf:var(--v3);
  --cat-index:var(--v1);
  --cat-gold:var(--v4);
  --cat-preipo:var(--v5);

  /* MEASURED LIMIT, recorded here so it is not rediscovered.
   * Six categories cannot be separated to standard by colour alone. Searched
   * this session with the dataviz validator (all-pairs) across five runs —
   * randomized free hue/lightness/chroma per slot, ~2.4M samples in the final
   * run alone, plus every 6-subset of the skill's own documented palette and
   * several hand-designed semantic sets. Best achievable worst-pair
   * normal-vision dE, over all of them:
   *     6 slots  13.6 light / 10.8 dark   — floor is 15, unreachable
   *     5 slots  16.1 light / 13.6 dark   — light only
   *     4 slots  25.7 light / 19.1 dark   — both PASS, with headroom
   * The shipped six measure dE 4.7 light / 13.4 dark, so light is genuinely
   * bad. Every hue reassignment tried either regressed dark or broke a
   * category's hue identity across themes without reaching the floor, so the
   * hues are LEFT ALONE rather than churned. The only change that reaches
   * standard is fewer categories: fold to four (crypto, equity, index, gold)
   * plus a neutral "Other". That is a product call, flagged not taken.
   * Until then the treemap's direct box labels are the mandatory secondary
   * encoding the relief rule requires — do not remove them. */

  /* Ink for the tinted status chips on the Strategies board. These were
   * REFERENCED by opps.css (.cf.credit, .cf.rfq, .cf.two) and DEFINED
   * NOWHERE: `color: var(--good-ink)` with no fallback is invalid at computed
   * value time, so those chips silently inherited their parent's colour
   * instead of taking a semantic one. Measured against the rendered chip
   * background, not the declared one. */
  --good-ink:#5ee9b5;
  --warn-ink:#fcd535;
  --brand-fill:#f0b90b;
  --brand-fill-2:#fcd535;
  --on-brand:#181a20;
  --focus:rgba(240,185,11,.55);

  /* The ladder, dark side. White overlay, and these four alphas ARE the
   * reference values — they were authored for a near-black ground and this one
   * is #08110f. The alphas differ from paper's on purpose: the same alpha does
   * not carry the same weight lightening a dark ground as it does darkening a
   * light one, which is exactly what a single hard-coded rgba would have got
   * wrong in one of the two themes. */
  --ov:255,255,255;
  --fill-1:rgba(var(--ov),.04);
  --fill-2:rgba(var(--ov),.08);
  --edge-1:rgba(var(--ov),.12);
  --edge-2:rgba(var(--ov),.16);
}

/* The toggle. Only one icon is ever rendered: the one for the theme you would
 * be switching TO, because a control that shows its current state reads as a
 * status light and gets ignored. */
.theme-btn{
  display:inline-grid; place-items:center;
  width:30px; height:30px; padding:0;
  background:var(--fill-2); color:var(--ink2);
  border:1px solid var(--edge-2); border-radius:var(--r-ctl);
  cursor:pointer;
  transition:color var(--t), border-color var(--t), background-color var(--t);
}
.theme-btn:hover{ color:var(--mint); border-color:var(--mint-dim); background:var(--fill-1); }
.theme-btn svg{ width:15px; height:15px; grid-area:1/1; }
.theme-btn .th-sun{ display:none; }
:root[data-theme="dark"] .theme-btn .th-sun{ display:block; }
:root[data-theme="dark"] .theme-btn .th-moon{ display:none; }

*{box-sizing:border-box}

/* ── THE LOCKED SHELL ───────────────────────────────────────────────────────
 * The document does not scroll. Only panes do.
 *
 * Before this, the page was a 1240px document in a 900px viewport with a
 * sticky header riding it — which is where scroll chaining, mobile
 * address-bar resize jank and scroll-restoration ambiguity all come from. Now
 * html/body are exactly one viewport and hidden, the chrome is a fixed-height
 * band, and the rail and the stage each own their own scrollTop.
 *
 * dvh, not vh, and in that order: vh first as the fallback for engines without
 * dvh, dvh second so it wins where supported. vh alone is measured against the
 * LARGEST viewport, so on mobile the bottom of the shell sits behind the
 * address bar until it retracts — reintroducing exactly the resize jank this
 * change exists to remove.
 *
 * ⚠️ CONTRACT FOR OTHER FILES: window.scrollTo() and window.scrollY are now
 * inert app-wide. The stage's scroller is `main.stage`. Anything that used to
 * scroll the window must scroll that element instead. detail.js's
 * scrollToPanel() and nav.js's reset-to-top both still call window.scrollTo and
 * are no-ops until they are updated — written up in the handover, NOT patched
 * here, because those files belong to other agents. */
html{height:100%}
body{
  height:100vh; height:100dvh;
  overflow:hidden;
  display:flex; flex-direction:column;
}
/* The chrome. Fixed height, so --layout-top-offset below is exact rather than
   a number that drifts when the trust bar wraps. */
.top,.trust,.ticker,footer{flex:none}
/* The one flexible band. min-height:0 is load-bearing: without it a flex item
   refuses to shrink below its content and the panes inside never scroll —
   they just push the shell taller than the viewport it is pinned to. */
.app{flex:1 1 auto;min-height:0}

body{
  margin:0;color:var(--ink);font:13.5px/1.55 var(--sans);
  -webkit-font-smoothing:antialiased;font-variant-numeric:tabular-nums;
  background:var(--bg);
}
a{color:var(--mint);text-decoration:none}
a:hover{text-decoration:underline}
:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:var(--r-mark)}

/* ── header ─────────────────────────────────────────────── */
.top{
  display:flex;justify-content:space-between;align-items:center;gap:14px;
  padding:12px 20px;position:sticky;z-index:var(--z-header);
  height:61px;
  /* -1px, not 0. At fractional browser zoom a sticky band pinned to 0 leaves a
     sub-pixel seam that the row border underneath bleeds through as a flicker
     while scrolling. One pixel of overlap removes it permanently. */
  top:-1px;
  /* THEME-BLIND BUG, fixed. These two stops were the literal paper --bg
     (#fdfcf7) and never followed night shift, so the sticky header stayed
     near-white on the dark theme — and h1 inherits --ink, which is ALSO
     near-white there. The product's own name was rendering invisible in dark
     mode, and it screenshotted as an empty header rather than as an error.
     color-mix keeps the frosted translucency the backdrop-filter needs while
     taking its colour from whichever --bg is live. */
  background:linear-gradient(180deg,
    color-mix(in srgb, var(--bg) 97%, transparent),
    color-mix(in srgb, var(--bg) 90%, transparent));
  backdrop-filter:blur(14px);
  border-bottom:1px solid var(--edge-1);
  box-shadow:0 1px 0 var(--edge-1);
}
.brand{display:flex;align-items:center;gap:11px}
.logo{
  /* Was a text glyph. Now a drawn mark: the payoff line breaking out of its
     ring. Same tile, same gradient, same 30px — only the contents changed. */
  color:var(--on-brand);width:30px;height:30px;border-radius:var(--r-ctl);
  display:grid;place-items:center;
  background:var(--brand-fill);
}
h1{font:700 15.5px/1.1 var(--sans);margin:0;letter-spacing:-.02em}
.logo svg{width:19px;height:19px;display:block}

/* ---- the CAP mark, assembling ------------------------------------------- */
/* ONE curve for the whole gesture, and it is not the control curve. Controls
   run --t-ease (cubic-bezier(.2,.6,.3,1)) because an affordance should answer
   the cursor. This is a statement, not an affordance, so it runs the reference
   expo-out — measured off paradigm.xyz's own transitions, where every
   positional move uses cubic-bezier(.16,1,.3,1): it covers most of its
   distance almost immediately and then settles hard, which is what reads as
   "machined" rather than "eased". Durations stay in the app's band (the
   slowest control step is 260ms; the ticker's data-arrival fade is 500ms) and
   the whole sequence is under the ~1.1s a first paint gets before it becomes
   something you wait for. */
:root{ --cm-ease:cubic-bezier(.16,1,.3,1); }

/* The payoff draws through the ticks. Path length is 21.05 user units
   (9.3/9.3 diagonal = 13.15, plus the 7.9 flat) — measured off the geometry,
   not guessed, because a dasharray shorter than the path leaves a visible
   gap that never closes. */
.capmark .cm-pay{
  stroke-dasharray:21.1; stroke-dashoffset:21.1;
  animation:cm-draw 640ms var(--cm-ease) 200ms forwards;
}
/* The ticks are the fills arriving — and they LEAVE. Held at rest they turned
   a 19px tile into a comb: five hairlines under a heavy stroke read as noise,
   not as structure, which is the opposite of the brief. So they are purely
   transitional. They rise, the payoff draws through them, and they fade out,
   leaving the resting mark as just the payoff and its strike — which is what
   has to survive at 16px and in one colour.

   The gesture keeps its meaning and loses its clutter: you see the trades add
   up to the shape, then the shape is what remains. Hover brings them back —
   the mark showing its working on demand rather than permanently.

   transform-box:fill-box is required. The default (view-box) puts the origin
   at the SVG's corner, not the tick's, and the scale throws each tick across
   the tile. */
.capmark .cm-tk{
  opacity:0; transform:scaleY(.15);
  transform-box:fill-box; transform-origin:50% 100%;
  animation:cm-tick 1000ms var(--cm-ease) forwards;
}
.capmark .cm-tk:nth-child(1){animation-delay:0ms}
.capmark .cm-tk:nth-child(2){animation-delay:55ms}
.capmark .cm-tk:nth-child(3){animation-delay:110ms}
.capmark .cm-tk:nth-child(4){animation-delay:165ms}
.capmark .cm-tk:nth-child(5){animation-delay:220ms}
/* The strike locks in last: it is the conclusion, not the setup. */
.capmark .cm-strike{opacity:0;animation:cm-strike 260ms var(--t-ease) 800ms forwards}

@keyframes cm-draw  {to{stroke-dashoffset:0}}
@keyframes cm-tick{
  0%  {opacity:0;   transform:scaleY(.15)}
  25% {opacity:.5;  transform:none}
  55% {opacity:.5;  transform:none}
  100%{opacity:0;   transform:none}
}
@keyframes cm-strike{to{opacity:.5}}

/* Hover is a TRANSITION, not a replay. Restarting a CSS animation needs a
   class toggle and a forced reflow in JS; a transition on the already-settled
   end state costs nothing and cannot get stuck half-drawn if the pointer
   leaves mid-run. The ticks and strike step up, the payoff does not move —
   the number the mark stands for should not appear to change on hover. */
.brand .logo .cm-tk,
.brand .logo .cm-strike{transition:opacity var(--t)}
.brand:hover .logo .cm-tk{opacity:.6;animation:none}
.brand:hover .logo .cm-strike{opacity:.85}

/* Reduced motion: the finished mark, immediately. Not a shorter animation —
   none at all, and every element already at its end state. */
@media (prefers-reduced-motion:reduce){
  .capmark .cm-pay{stroke-dashoffset:0;animation:none}
  /* The ticks are transitional, so with no transition there is nothing for
     them to be: the reduced-motion mark is the RESTING mark, payoff and
     strike, not a frozen mid-build frame. */
  .capmark .cm-tk{opacity:0;transform:none;animation:none}
  .capmark .cm-strike{opacity:.5;animation:none}
  .brand .logo .cm-tk,.brand .logo .cm-strike{transition:none}
}
.tag{margin:2px 0 0;font-size:11px;color:var(--ink3);letter-spacing:.01em}
.status{
  display:flex;align-items:center;gap:7px;font:10.5px/1 var(--mono);color:var(--ink2);
  padding:6px 11px;border:1px solid var(--edge-2);border-radius:var(--r-pill);background:var(--fill-2);
}
.dot{width:6px;height:6px;border-radius:50%;background:var(--ink3);flex:none}
.dot.live{background:var(--mint);box-shadow:0 0 0 3px var(--mint-dim);position:relative}
.dot.stale{background:var(--warn)} .dot.down{background:var(--bad)}

/* THE LIVE PULSE. The one continuous animation in the app, and it is allowed
   to exist because it is the only thing on the page whose whole meaning is
   "this is still happening". It rides .dot.live ONLY — a stale or down feed
   goes still, so the motion can never claim a connection the app does not
   have. That rule matters more here than anywhere: this product's claim is
   that it shows what actually traded, and a pulse under a dead feed would be
   the interface lying.

   Cost: a pseudo-element animating transform + opacity only, which the
   compositor owns outright — no layout, no paint, no main-thread work per
   frame. Deliberately NOT the box-shadow above, which would repaint a ring
   every frame on a page already polling live data. Measured after shipping:
   no scripting cost attributable to it over a 5s idle capture. */
.dot.live::after{
  content:""; position:absolute; inset:0; border-radius:50%;
  background:var(--mint); opacity:.55;
  animation:dot-pulse 2.4s var(--t-ease) infinite;
}
@keyframes dot-pulse{
  0%  {transform:scale(1);   opacity:.5}
  70% {transform:scale(2.6); opacity:0}
  100%{transform:scale(2.6); opacity:0}
}

/* SECTION ENTRY. The rail swaps whole views by toggling [hidden], which was
   an instant cut — the densest surfaces in the app replaced themselves with
   no indication anything had happened, and on the wider sections it read as a
   page reload. This is the reference's entrance, measured off paradigm.xyz:
   transform + opacity, ~500ms, cubic-bezier(.16,1,.3,1). Toggling [hidden]
   flips display, and leaving display:none restarts a CSS animation, so this
   re-runs on every section change without a line of JS.

   6px, not more. The move only has to say "this is new content"; a larger
   travel on a 1200px-tall table turns navigation into an event. Data does not
   animate in — only the container does, so no number ever appears to arrive
   that was already there. */
.view:not([hidden]){animation:view-in 460ms var(--cm-ease) both}
@keyframes view-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}

@media (prefers-reduced-motion:reduce){
  .dot.live::after{animation:none;opacity:0}
  .view:not([hidden]){animation:none}
}

/* ── stat rail ──────────────────────────────────────────── */
/* ---- live ticker --------------------------------------------------------
   Replaced the statbar. A marquee of what just traded, on every section. */
.ticker{
  border-bottom:1px solid var(--line);
  background:linear-gradient(90deg,var(--s1),var(--bg) 22%,var(--bg) 78%,var(--s1));
  overflow:hidden; position:relative; height:46px;
}
/* Fade the ends so items enter and leave instead of being guillotined. */
.ticker::before,.ticker::after{
  content:""; position:absolute; top:0; bottom:0; width:52px; z-index:2; pointer-events:none;
}
.ticker::before{left:0;background:linear-gradient(90deg,var(--bg),transparent)}
.ticker::after{right:0;background:linear-gradient(270deg,var(--bg),transparent)}
.tk-track{
  display:flex; align-items:center; gap:0; height:100%; width:max-content;
  animation:tkroll linear infinite;
}
/* -50% exactly: the track is duplicated, so the second copy sits under the
   cursor at the instant the first ends. Any other value stutters at the wrap. */
@keyframes tkroll{from{transform:translateX(0)}to{transform:translateX(-50%)}}
.ticker:hover .tk-track{animation-play-state:paused}
@media (prefers-reduced-motion:reduce){
  .tk-track{animation:none}
  .ticker{overflow-x:auto}
}
.tk-i{
  display:inline-flex; align-items:baseline; gap:7px; flex:none;
  padding:0 18px; border-right:1px solid var(--line);
  font:11.5px/1 var(--mono); white-space:nowrap; color:var(--ink2);
}
.tk-age{font-style:normal;color:var(--ink3);font-size:10px;min-width:26px}
.tk-a{font-weight:700;color:var(--ink);font-size:12px}
.tk-act{font-style:normal;font-weight:600}
.tk-act.put{color:var(--v1)} .tk-act.call{color:var(--v3)}
.tk-act.sell{opacity:.85}
.tk-d{font-style:normal;color:var(--ink3);font-size:10.5px}
.tk-p{font-style:normal;font-weight:700;color:var(--ink)}
/* Anything under five minutes old gets the pulse. That is the "something is
   happening" signal; older rows are just context. */
.tk-i.tk-new .tk-a{color:var(--mint)}
.tk-i.tk-new{animation:tkflash 2.4s ease-out}
@keyframes tkflash{from{background:var(--mint-dim)}to{background:transparent}}
/* The .stat rules that sat here are GONE. The statbar they styled was replaced
   by the ticker, and they were left behind matching nothing — measured 0 across
   all five sections. They were not merely dead: `.stat` is a one-word global,
   and it captured an unrelated `.op-chip.stat` on the Strategies board, giving
   it a ::before gradient bar and a padding override that only fired under
   720px. A dead rule with a common name is worse than a dead rule. */

/* The stage FILLS its grid column. It used to carry `max-width:1440px;margin:0
   auto`, and auto inline margins on a grid item switch it out of stretch and
   into fit-content sizing — so the stage collapsed to its own max-content and
   centred the remainder as empty page. Measured at 1920x1080 before the fix:
   Strategies 912px, Venues 820px, Flow 819px, Activity 865px, all IDENTICAL at
   1280 and 1440, which is the tell that viewport width was reaching nothing.
   That left 398-444px of dead gutter. The 1440 cap was never even reached, so
   it was capping a width the page could not produce. */
/* THE STAGE IS THE SCROLLER. This is the element other files must scroll
   instead of the window. overscroll-behavior stops a pane that has hit its end
   from handing the gesture to anything else — with the document locked there is
   nothing to hand it to, and the browser would otherwise rubber-band. */
main{padding:12px 14px 40px;width:100%;min-width:0;
  height:100%;overflow-y:auto;overscroll-behavior:contain;
  display:flex;flex-direction:column;gap:14px}

/* ── panels ─────────────────────────────────────────────── */
.panel{
  background:var(--card);
  border:0;border-radius:var(--r-card);overflow:hidden;
}
.hero-panel{border:0}
.panel-head{
  display:flex;justify-content:space-between;align-items:center;gap:14px;flex-wrap:wrap;
  padding:10px 14px 9px;border-bottom:1px solid var(--line);
}
.panel-head h2{font:700 14px/1.25 var(--sans);margin:0;letter-spacing:-.01em}
.panel-head .sub{margin:4px 0 0;font-size:12px;line-height:1.5;color:var(--ink3);max-width:80ch}
.controls{display:flex;align-items:center;gap:8px;font-size:11.5px;color:var(--ink2);flex-wrap:wrap}
.controls label{font:600 10px/1 var(--mono);text-transform:uppercase;letter-spacing:.1em;color:var(--ink3)}
.controls select{
  background:var(--fill-2);color:var(--ink);border:1px solid var(--edge-2);
  border-radius:var(--r-ctl);padding:5px 9px;font:11.5px/1 var(--mono);cursor:pointer;
}
.controls select:hover{border-color:var(--mint-dim)}
.check{display:flex;align-items:center;gap:5px;cursor:pointer;
  font:600 10px/1 var(--mono);text-transform:uppercase;letter-spacing:.1em;color:var(--ink3)}
.seg{display:inline-flex;border:1px solid var(--edge-2);border-radius:var(--r-ctl);overflow:hidden;background:var(--fill-2)}
.seg button{background:transparent;color:var(--ink2);border:0;padding:5px 12px;
  font:600 11.5px/1 inherit;cursor:pointer;transition:color var(--t),background-color var(--t)}
.seg button+button{border-left:1px solid var(--line2)}
.seg button:hover{color:var(--ink)}
.seg button.on{
  background:var(--brand-fill);color:var(--on-brand);font-weight:700;
}

.chart-wrap,#where-grid{padding:14px 15px 6px;overflow-x:auto}
#where-grid svg,.chart-wrap svg{height:auto;width:auto;max-width:100%;overflow:visible}
.legend{display:flex;align-items:center;gap:11px;padding:0 15px 11px;margin:6px 0 0;
  font:10px/1 var(--mono);color:var(--ink3);flex-wrap:wrap;
  text-transform:uppercase;letter-spacing:.06em}
.lg{display:inline-flex;align-items:center;gap:5px;white-space:nowrap}
.lg i{width:11px;height:11px;border-radius:var(--r-mark);display:inline-block;flex:none}
.lg i.dash{background:none;border:1px dashed var(--empty)}
.lg i.hatch{background:none;border:1px solid var(--empty);
  background-image:repeating-linear-gradient(45deg,var(--empty) 0 1.5px,transparent 1.5px 4px)}
.lg.lead{color:var(--ink2);font-weight:700}
.rowsub{padding:8px 15px 13px;margin:0;font-size:12px;color:var(--ink2);max-width:104ch;line-height:1.6}
.rowsub strong{color:var(--ink);font-weight:650}
/* grid.js renders a venue name inside this sentence as a bare <button>, and NO
   stylesheet claimed it: the control audit found it as the app's only button
   still wearing browser chrome — a grey 3D pill sitting mid-paragraph. It reads
   as what it is, a venue you can open from inside the sentence, so it wears the
   inline-link treatment rather than becoming another pill. It lives here
   because #where-summary is already styled in this file. */
.sum-v{background:none;border:0;padding:0;font:inherit;color:var(--mint);
  cursor:pointer;text-decoration:underline;text-underline-offset:2px;
  text-decoration-color:var(--mint-dim)}
.sum-v:hover{text-decoration-color:var(--mint2)}

/* ── tables ─────────────────────────────────────────────── */
.table-scroll{overflow-x:auto;max-height:540px;overflow-y:auto}
table{width:100%;border-collapse:collapse;font:11.5px/1.5 var(--mono);white-space:nowrap;
  font-variant-numeric:tabular-nums}
th{
  text-align:right;padding:8px 13px;color:var(--ink3);font-weight:700;
  border-bottom:1px solid var(--line2);position:sticky;top:-1px;
  z-index:var(--z-sticky);
  background:linear-gradient(180deg,var(--s3),var(--s2));
  font-size:10px;text-transform:uppercase;letter-spacing:.11em;
}
th:first-child,td:first-child{text-align:left}
td{padding:6px 13px;text-align:right;border-bottom:1px solid var(--line)}
tbody tr{transition:background-color var(--t)}
tbody tr:hover{background:var(--glass)}
tr.clickable{cursor:pointer}
tr.clickable:hover td:first-child{color:var(--mint)}
.muted{color:var(--ink3)}
.neg{color:var(--neg)} .pos{color:var(--pos)}
.tiny{font-size:10px;color:var(--ink3)}
.pill{padding:2px 6px;border-radius:var(--r-mark);font:600 10px/1.5 var(--mono);background:var(--s3);
  color:var(--ink2);border:1px solid var(--line2);text-transform:uppercase;letter-spacing:.07em}
.pill.ok{background:rgba(8,127,91,.12);color:var(--mint);border-color:rgba(8,127,91,.3)}
.pill.warn{background:rgba(232,89,12,.12);color:var(--warn);border-color:rgba(232,89,12,.3)}
.pill.err{background:rgba(224,49,49,.11);color:var(--bad);border-color:rgba(224,49,49,.3)}
.spread-wide{color:var(--warn)} .spread-vwide{color:var(--bad)}
.cap{display:inline-block;width:36px;height:5px;background:var(--s3);border-radius:var(--r-mark);
  overflow:hidden;vertical-align:middle;margin-right:5px;border:1px solid var(--line)}
.cap-bar{display:block;height:100%;background:linear-gradient(90deg,var(--v2),#f0a04b)}

/* ── stat cards ─────────────────────────────────────────── */
.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(158px,1fr));gap:10px;padding:14px 15px 4px}
.card{
  background:var(--fill-1);
  border:1px solid var(--edge-1);border-radius:var(--r-card);padding:11px 13px;position:relative;overflow:hidden;
}
.card::before{content:"";position:absolute;inset:0 0 auto 0;height:1px;
  background:linear-gradient(90deg,var(--mint-dim),transparent 65%)}
.card .label{font:700 10px/1 var(--mono);text-transform:uppercase;letter-spacing:.12em;color:var(--ink3)}
.card .value{font:650 20px/1.2 var(--mono);margin-top:6px;letter-spacing:-.03em}
.card .sub{font-size:10.5px;color:var(--ink3);margin-top:3px}
.card .value.neg{color:var(--neg)} .card .value.pos{color:var(--pos)}
.note{font-size:11.5px;color:var(--ink2);max-width:84ch}
.k{width:9px;height:9px;border-radius:var(--r-mark);display:inline-block}
.k.call{background:var(--pos)} .k.put{background:var(--neg)} .k.atm{background:var(--mid)}
.empty{padding:26px 15px;text-align:center;color:var(--ink3);font-size:11.5px}
.closebtn{background:var(--fill-2);color:var(--ink2);border:1px solid var(--edge-2);
  border-radius:var(--r-ctl);padding:5px 11px;font:600 11px/1 inherit;cursor:pointer}
.closebtn:hover{background:var(--fill-1);color:var(--ink);border-color:var(--mint-dim)}

/* ── entity marks ───────────────────────────────────────────
 * Identity, not data. Every mark here is monochrome on purpose: the mint ramp
 * means recency, --v1..--v3 mean venue, --neg/--mid/--pos mean skew polarity,
 * and a tinted logo sitting beside a tinted cell would give the reader two
 * colour languages with no way to tell which one is measurement. Shape carries
 * identity; colour stays reserved. Nothing below defines a new colour value —
 * it is all surface and ink tokens that already existed.
 */
.mk{display:block;flex:none;color:var(--ink)}
.ent{display:inline-flex;align-items:center;gap:6px;white-space:nowrap;vertical-align:middle}
.ent>span{min-width:0}
.ent.lead>span{color:var(--ink);font-weight:650}
.ents{display:flex;flex-wrap:wrap;gap:4px 12px}
.ents .ent{color:var(--ink2)}
tr.clickable:hover .ent.lead>span{color:var(--mint)}
.ent-sep{color:var(--ink3);margin:0 8px;font-weight:400}
#detail-title{display:flex;align-items:center;flex-wrap:wrap}
.ask-ent{display:block;margin:0 0 7px}
#where-summary .ent{margin-right:1px}
/* Row labels in the grid SVG. `fill` is set as a presentation attribute by
   grid.js; a CSS property beats it, so hover/focus can recolour the text
   without the renderer knowing about states. */
.rowlink{cursor:pointer}
.rowlink text{text-decoration:underline;text-underline-offset:3px;text-decoration-thickness:1px}
.rowlink:hover text{fill:var(--ink)}
.rowlink:focus{outline:none}
.rowlink:focus-visible text{fill:var(--ink)}
.rowlink:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:var(--r-mark)}

/* ── asset picker (custom listbox over a live <select>) ─────
 * The native control is still the value. This is a skin with a keyboard.
 */
.cs-native{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip-path:inset(50%);border:0;white-space:nowrap}
.cs{position:relative;display:inline-flex}
.cs-btn{
  display:inline-flex;align-items:center;gap:7px;
  background:var(--fill-2);color:var(--ink);border:1px solid var(--edge-2);
  border-radius:var(--r-ctl);padding:4px 8px 4px 5px;font:11.5px/1 var(--mono);cursor:pointer;
  transition:border-color var(--t);
}
.cs-btn:hover{border-color:var(--mint-dim)}
.cs-btn b{font-weight:700;letter-spacing:.02em}
.cs-car{color:var(--ink3);font-size:10px;margin-left:1px}
.cs-pop{
  position:absolute;top:calc(100% + 5px);left:0;z-index:40;
  display:flex;flex-direction:column;min-width:236px;max-height:340px;overflow-y:auto;
  padding:5px;background:var(--bg);border:1px solid var(--edge-1);border-radius:var(--r-card);
  box-shadow:0 12px 30px rgba(0,0,0,.55), inset 0 1px 0 rgba(255,255,255,.03);
}
.cs-pop[hidden]{display:none}
.cs-grp{
  padding:8px 7px 4px;font:700 10px/1 var(--mono);color:var(--ink3);
  text-transform:uppercase;letter-spacing:.12em;
}
.cs-opt{
  display:flex;align-items:center;gap:8px;padding:5px 7px;border-radius:var(--r-ctl);
  cursor:pointer;font:11.5px/1.3 var(--mono);color:var(--ink2);
}
.cs-opt b{color:var(--ink);font-weight:700;letter-spacing:.02em}
.cs-opt i{font-style:normal;color:var(--ink3);margin-left:auto;padding-left:12px;font-size:10.5px}
.cs-opt:hover{background:var(--glass)}
.cs-opt:focus{outline:none;background:var(--s3);box-shadow:inset 0 0 0 1px var(--line2)}
.cs-opt[aria-selected="true"]{background:var(--s3)}
.cs-opt[aria-selected="true"] b{color:var(--mint)}
.cs-opt[aria-selected="true"] .mk{color:var(--mint)}

/* ── ask ────────────────────────────────────────────────── */
.ask-panel{padding:14px 15px}
.ask-head{display:flex;gap:8px;align-items:center}
#ask-q{
  flex:1;min-width:200px;background:var(--fill-2);color:var(--ink);
  border:1px solid var(--edge-2);border-radius:var(--r-ctl);padding:10px 13px;font:13.5px/1.3 var(--sans);
  transition:border-color var(--t), box-shadow var(--t);
}
#ask-q:focus{outline:0;border-color:var(--mint);box-shadow:0 0 0 3px var(--mint-dim)}
#ask-q::placeholder{color:var(--ink3)}
#ask-go{
  background:var(--brand-fill);color:var(--on-brand);border:0;
  border-radius:var(--r-ctl);padding:10px 19px;font:700 12.5px/1 inherit;cursor:pointer;
}
#ask-go:hover{filter:brightness(1.07)}
.ask-chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}
.chip-q{
  background:var(--fill-2);color:var(--ink3);border:1px solid var(--edge-2);
  border-radius:var(--r-pill);padding:4px 11px;font:10.5px/1 var(--mono);cursor:pointer;
  transition:border-color var(--t),color var(--t);
}
.chip-q:hover{border-color:var(--mint-dim);color:var(--mint)}
#ask-answer{
  margin-top:12px;padding:13px 15px;border-radius:var(--r-card);
  background:var(--fill-1);
  border:1px solid var(--edge-1);border-left:2px solid var(--mint2);
}
.ask-headline{margin:0 0 7px;font-size:14.5px;font-weight:650;line-height:1.4;max-width:84ch;
  letter-spacing:-.01em}
.ask-headline.ask-unknown{color:var(--ink2);font-weight:600}
.ask-detail{margin:0 0 5px;font-size:12px;color:var(--ink2);max-width:96ch;line-height:1.6}
.ask-thinking{margin:0;font-size:11.5px;color:var(--ink3)}
.ask-more{margin:8px 0 0}
.linkish{background:none;border:0;color:var(--mint);font:11.5px var(--mono);cursor:pointer;
  padding:0;text-decoration:underline;text-underline-offset:2px}

footer{padding:16px 20px 30px;border-top:1px solid var(--line);
  font:10.5px/1.6 var(--mono);color:var(--ink3);text-align:center;
  display:flex;flex-direction:column;align-items:center;gap:8px}
#method{max-width:76ch;text-align:left;margin-top:4px}
#method p{margin:0 0 7px;font-size:11px;line-height:1.65;color:var(--ink3)}

@media (prefers-reduced-motion:reduce){*{transition:none!important}}
@media (max-width:900px){
  main{padding:12px 10px 32px}
  .top{padding:10px 12px}
  th,td{padding:5px 9px}
}

/* ---- two-column: grid beside the tape ----
   SCOPED to the overview view, not left as a bare `.split`. As a global class
   name it reached into every subtree and captured a `.cf.split` badge inside
   the Strategies matrix, stretching it across a 354px cell; that agent had to
   rename its own class to escape it. The class in index.html is unchanged —
   only this selector's reach is. Verified: one .split in the app, the overview
   one, so nothing else depended on the bare name.
   The tape column is CAPPED, not fractional. Its rows are two short lines of
   fixed text, so every pixel past ~430 is padding inside the row rather than
   more tape — and once the stage stopped shrink-fitting, a .85fr tape claimed
   592px at 1920 and took that width straight out of the expiry grid, which is
   the one thing on this page that can genuinely use it. 430px is what the .85fr
   column already resolved to at 1440, so that layout is unchanged and every
   pixel gained above 1440 goes to the grid. */
/* THE LITTLE INSIDE BOXES. The overview fills the stage exactly and does not
   scroll it — the venue grid and the tape each own their own scrollTop, so
   reading down the tape never moves the board beside it, and vice versa. Every
   other section is a long document and keeps scrolling the stage instead. */
/* A flex column, because the overview view is no longer just the split — a
   `.board-all` banner is rendered after it. Giving the split height:100% made
   it claim the whole view and pushed that banner out of the shell, which put
   the scroll back on the stage. flex:1 lets the split take what is left. */
.view[data-view="overview"]{flex:1 1 auto;min-height:0;
  display:flex;flex-direction:column;gap:10px}
/* ONE column now, not two. The second track reserved 300-430px for the live
   tape, which has since moved into its own data-view="trades" section — so it
   became dead space: the screener pane measured 630px at 1280 and forced a
   horizontal scroll while 445px sat empty beside it. */
.view[data-view="overview"] > .split{display:grid;
  grid-template-columns:minmax(0,1fr);gap:10px;
  align-items:stretch;flex:1 1 auto;min-height:0}
/* The 300px second track went with the "Live fills" rail it existed for.
   Leaving the track behind reserved a quarter of a 1440px viewport for an
   element that is no longer in the DOM, so the board stopped at 1115px with
   dead space to its right. One column, full width. */
.view[data-view="overview"] > .split > .panel{min-height:0;display:flex;flex-direction:column}
/* The panel head stays put while its pane scrolls under it. */
.view[data-view="overview"] > .split > .panel > .panel-head{flex:none}
.hero-panel > .chart-wrap,
.hero-panel > #where-grid{flex:1 1 auto;min-height:0;overflow:auto;
  overscroll-behavior:contain}
.hero-panel > .legend,.hero-panel > .rowsub{flex:none}
.topright{display:flex;align-items:center;gap:12px}
.byline{font:10.5px/1 var(--mono);color:var(--ink3)}
.byline a{color:var(--ink2)}

/* ---- the tape ---- */
/* max-height is gone: the pane is sized by the shell now, so a hard 620px cap
   would leave dead space under it on a tall window and clip it on a short one.
   #tape below is the scrollport. */
.tape-panel{display:flex;flex-direction:column;min-height:0}
.tapecount{font:10px/1 var(--mono);color:var(--ink3);text-transform:uppercase;letter-spacing:.1em}
#tape{overflow-y:auto;overscroll-behavior:contain;flex:1 1 auto;min-height:0;padding:4px 0 6px}
.tape-rows{display:flex;flex-direction:column}
/* Two lines per fill. Line 1 says WHAT HAPPENED in plain language; line 2
   carries the qualifiers. The old single-row grid printed "4050 P" and left
   the reader to decode a contract code — the strike had no spot to sit
   against and no expiry, so it could not be acted on. */
.trow{
  display:flex;flex-direction:column;gap:2px;
  padding:6px 13px;border-bottom:1px solid var(--line);
  font:11px/1.35 var(--sans);font-variant-numeric:tabular-nums;
}
.trow:hover{background:var(--glass)}
.t-l1{display:flex;align-items:center;gap:7px;min-width:0}
.t-l2{display:flex;align-items:baseline;gap:8px;min-width:0;
  font:10px/1.3 var(--mono);color:var(--ink3)}
.t-age{color:var(--ink3);font:10px/1 var(--mono);flex:none;width:30px}
.t-asset{display:flex;align-items:center;gap:5px;flex:none}
.t-asset b{font-weight:700;font-size:10.5px;font-family:var(--mono)}
/* The verb is the row. It gets the weight and the colour, not the strike. */
.t-act{font-size:11.5px;font-weight:600;color:var(--ink);min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.t-act.put{color:var(--v1)}
.t-act.call{color:var(--v3)}
.t-act.sell{opacity:.82}
.t-ctx{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.t-cost{flex:none;color:var(--ink);font-weight:700}
.t-cost i{font-style:normal;font-weight:400;color:var(--ink3)}
.t-venue{flex:none;color:var(--ink3)}
.t-cp{flex:none;opacity:.7;cursor:help}
@keyframes flash{from{background:var(--mint-dim)}to{background:transparent}}
.trow.fresh{animation:flash 1.6s ease-out}
@media (prefers-reduced-motion:reduce){.trow.fresh{animation:none}}

.vname{display:inline-flex;align-items:center;gap:7px}
.mk{vertical-align:middle;flex:none}

@media (max-width:1100px){
  /* Stacked, so the split can no longer be one viewport tall — the stage takes
     the scroll back and each panel sizes to its content. */
  .view[data-view="overview"]{flex:0 0 auto}
  .view[data-view="overview"] > .split{grid-template-columns:1fr;height:auto}
  /* Was written when the tape was a 428px SIDEBAR next to the venue grid, where
     capping it stopped one column running away from the other. Trades is now
     its own full-width section, so this capped the entire view at 420px and
     left dead space below it — four rows visible out of forty-five. The cap
     survives only where the tape is still a side panel. */
  .view[data-view="overview"] .tape-panel{max-height:420px}
}

/* ---- coverage table: venues down, assets across --------------------------
   The first column is sticky: with 23 asset columns the venue name is the one
   label you cannot afford to scroll away from. */
#coverage-wrap{padding:14px 15px 6px;overflow:auto;max-height:70vh}
table.cov{border-collapse:separate;border-spacing:0;font:11px/1 var(--mono)}
table.cov th,table.cov td{padding:0}
.cov-corner{position:sticky;left:0;top:-1px;z-index:calc(var(--z-sticky) + 1);background:var(--bg)}
.cov thead th.cov-a{position:sticky;top:-1px;z-index:var(--z-sticky);background:var(--bg);
  height:74px;vertical-align:bottom;padding-bottom:6px}
.cov-ah{display:flex;align-items:center;gap:4px;transform:rotate(-60deg);
  transform-origin:left bottom;white-space:nowrap;font-weight:600;
  color:var(--ink2);letter-spacing:.02em}
.cov-ah .mk{flex:none}
/* NOT display:flex — a flex <th> leaves table layout and the row stops
   aligning with its cells. The flex lives on an inner span. */
th.cov-v{position:sticky;left:0;z-index:var(--z-raised);background:var(--bg);height:26px;
  padding-right:12px;color:var(--mint);cursor:pointer;white-space:nowrap;
  font-weight:600;text-align:left;vertical-align:middle}
th.cov-v span.vn{display:inline-flex;align-items:center;gap:6px}
th.cov-v:hover span.vn{text-decoration:underline}
td.cov-c{width:26px;height:26px;text-align:center;vertical-align:middle;
  border:1px solid transparent;border-radius:var(--r-mark);position:relative}
td.cov-c i{font-style:normal;font-size:10px;line-height:1;opacity:.85}
td.cov-c.traded{color:var(--bg);border-color:var(--ink)}
td.cov-c.traded i{opacity:1;font-weight:700;mix-blend-mode:luminosity}
td.cov-c.listed{border:1px dashed var(--empty);color:var(--ink3)}
td.cov-c.unknown{border:1px solid var(--empty);color:var(--ink3);
  background:repeating-linear-gradient(45deg,var(--s2),var(--s2) 2px,transparent 2px,transparent 6px)}
td.cov-c.none{background:none;border-color:transparent}
td.cov-c.stale{outline:1px dotted var(--warn);outline-offset:-2px}
td.cov-c:not(.none){cursor:pointer}
td.cov-c:not(.none):hover,td.cov-c:not(.none):focus-visible{
  outline:2px solid var(--mint);outline-offset:-1px;z-index:1}

/* ---- hover card ---------------------------------------------------------- */
#cov-card{position:fixed;z-index:var(--z-pop);width:330px;max-width:calc(100vw - 16px);
  background:var(--bg);border:1px solid var(--line2);border-radius:var(--r);
  box-shadow:0 10px 28px rgba(0,0,0,.16);padding:12px 13px;pointer-events:none;
  font:11.5px/1.45 var(--sans);color:var(--ink)}
#cov-card[hidden]{display:none}
.cc-h{display:flex;align-items:center;gap:6px;font-weight:700;font-size:12.5px;
  padding-bottom:7px;border-bottom:1px solid var(--line);margin-bottom:8px}
.cc-x{color:var(--ink3);font-weight:400}
.cc-v{margin:0 0 8px;font-size:11.5px;line-height:1.45}
.cc-v.traded{color:var(--pos)}
.cc-v.listed{color:var(--ink2)}
.cc-v.unknown{color:var(--warn)}
.cc-s{display:flex;flex-wrap:wrap;gap:4px 12px;font:10.5px/1 var(--mono);
  color:var(--ink2);margin-bottom:7px}
.cc-s b{color:var(--ink);font-size:11.5px}
.cc-m{color:var(--ink3);text-transform:uppercase;letter-spacing:.05em}
.cc-n{margin:0 0 6px;font-size:11px;color:var(--ink2)}
.cc-n code{font:10.5px var(--mono);color:var(--ink);background:var(--s1);
  padding:1px 4px;border-radius:var(--r-mark)}
.cc-stale{color:var(--warn)}
table.cc-t{width:100%;border-collapse:collapse;font:10.5px/1 var(--mono);margin-top:7px}
.cc-t th{text-align:right;color:var(--ink3);font-weight:500;padding:3px 0 5px;
  border-bottom:1px solid var(--line);text-transform:uppercase;letter-spacing:.04em;
  font-size:10px}
.cc-t th:first-child,.cc-t td:first-child{text-align:left}
.cc-t td{text-align:right;padding:3px 0;color:var(--ink)}
.cc-t td.muted{color:var(--ink3)}
.cc-more{margin:5px 0 0;font-size:10px;color:var(--ink3)}

/* ---- ranked table: money-first, DefiLlama-shaped ------------------------- */
#ranked tr.clickable{cursor:pointer}
#ranked tr.clickable:hover{background:var(--glass)}
/* An asset nobody publishes trades for is dimmed, never zeroed — the row is
   real, our visibility into it is not. */
#ranked tr.nofeed td{opacity:.62}
.rk-cat{font:10px/1 var(--mono);text-transform:uppercase;letter-spacing:.06em;
  color:var(--ink3);white-space:nowrap}
.rk-money{white-space:nowrap;min-width:132px}
.rk-money b{font-weight:700;font-size:11px;margin-left:7px;color:var(--ink)}
/* The bar IS the ranking: length is share of the largest premium, so the order
   reads without parsing a single number. */
.rk-bar{display:inline-block;width:56px;height:7px;border-radius:var(--r-mark);
  background:var(--s2);vertical-align:middle;overflow:hidden}
.rk-bar i{display:block;height:100%;background:var(--q4);border-radius:var(--r-mark)}
.rk-nofeed{color:var(--ink3);font-family:var(--mono)}

/* ---- where can I trade: one card per venue -------------------------------
   Replaced a table. A contract count is inventory; the card leads with the
   verdict and everything that used to be a column moved into the hover. */
.cw-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(232px,1fr));
  gap:11px;padding:14px 15px 6px}
.cw-card{border:1px solid var(--edge-1);border-radius:var(--r-card);background:var(--fill-1);
  padding:14px 15px;cursor:pointer;display:flex;flex-direction:column;gap:8px;
  transition:border-color var(--t),box-shadow var(--t)}
.cw-card:hover,.cw-card:focus-visible{border-color:var(--line2);
  box-shadow:0 2px 14px -6px rgba(26,26,24,.28);outline:none}
.cw-h{display:flex;align-items:center;gap:8px;font:600 13px/1.2 var(--sans)}
.cw-h b{color:var(--ink)}
.cw-best{margin-left:auto;font:8.5px/1 var(--mono);text-transform:uppercase;
  letter-spacing:.08em;color:var(--good);border:1px solid var(--good);
  border-radius:var(--r-mark);padding:3px 5px;white-space:nowrap}
.cw-net{margin:0;font:10px/1.3 var(--mono);color:var(--ink3);
  text-transform:uppercase;letter-spacing:.05em}
/* The verdict is the card. Colour carries state; the words carry the meaning,
   so it is never colour alone. */
.cw-verdict{margin:2px 0 0;display:flex;flex-direction:column;gap:2px}
.cw-verdict b{font:600 14px/1.2 var(--sans)}
.cw-verdict i{font-style:normal;font-size:10.5px;color:var(--ink3)}
.cw-verdict.ok b{color:var(--good)}
.cw-verdict.warn b{color:var(--warn)}
.cw-verdict.ind b{color:var(--ink2)}
.cw-verdict.dim b{color:var(--ink3)}
.cw-chips{display:flex;gap:4px;flex-wrap:nowrap;overflow:hidden}
.cw-chips.wrap{flex-wrap:wrap;margin-top:8px}
.cw-chip{font:10px/1 var(--mono);font-style:normal;padding:4px 6px;border-radius:var(--r-mark);
  background:var(--s2);color:var(--ink3);white-space:nowrap}
.cw-chip.on{background:var(--mint-dim);color:var(--mint);font-weight:700}
.cw-chip.more{background:none;border:1px dashed var(--line2)}

#cw-pop{position:fixed;z-index:var(--z-pop);width:310px;max-width:calc(100vw - 16px);
  background:var(--bg);border:1px solid var(--edge-1);border-radius:var(--r-card);
  box-shadow:0 10px 28px rgba(0,0,0,.16);padding:13px 14px;pointer-events:none;
  font:11.5px/1.45 var(--sans);color:var(--ink)}
#cw-pop[hidden]{display:none}
.cw-pop-h{display:flex;align-items:center;gap:7px;font-weight:700;font-size:12.5px;
  padding-bottom:8px;border-bottom:1px solid var(--line);margin-bottom:9px}
.cw-pop-h span{margin-left:auto;font:10px/1 var(--mono);color:var(--ink3);
  text-transform:uppercase;letter-spacing:.06em}
.cw-kv{display:flex;flex-direction:column;gap:6px}
.cw-kv div{display:flex;justify-content:space-between;gap:12px;font-size:11px}
.cw-kv span{color:var(--ink3)}
.cw-kv b{font-family:var(--mono);font-size:11px;text-align:right}
.cw-kv .cw-unk b,.cw-kv div.cw-unk b{color:var(--warn)}
.cw-pop-s{margin:9px 0 0;font:10px/1.3 var(--mono);color:var(--ink3);
  text-transform:uppercase;letter-spacing:.05em}

/* ---- the recording block ------------------------------------------------
   Was one line of grey fine print in the footer. It is the strongest claim on
   the site: this history cannot be bought, only started. */
/* In a locked shell the footer cannot scroll: it is a sibling of .app in
   index.html, so it can never sit inside the stage's scrollport without a
   wrapper element in markup this file does not own. At 256px it is far too
   tall to pin permanently — 28% of a 900px viewport spent on a colophon. So it
   collapses to its 43px bar, and the recording block is revealed by the "How
   this is measured" control already sitting in it. :has() rather than a class,
   because that toggle is app.js's and only ever sets [hidden] on #method —
   this reads that state without either file knowing about the other. */
footer{border-top:1px solid var(--edge-1);margin-top:0;
  /* padding:0 because the collapsed footer is exactly its .foot-bar, and the
     footer's own 16px/30px padding would otherwise fill the visible band with
     blank space and clip the bar itself. .foot-bar carries its own padding. */
  padding:0;max-height:44px;overflow:hidden}
/* `footer .rec`, not `.rec`. The .rec rule that sets display:grid is further
   down this file, so a bare .rec here loses on source order — it measured
   display:grid with the disclosure still closed, which put the recording block
   in the collapsed 44px band instead of the bar. */
footer .rec{display:none}
footer:has(#method:not([hidden])){
  max-height:min(70dvh,540px);overflow-y:auto;overscroll-behavior:contain}
footer:has(#method:not([hidden])) .rec{display:grid}
.rec{display:grid;grid-template-columns:minmax(180px,240px) minmax(0,1fr);
  gap:0;align-items:stretch}
.rec-num{display:flex;flex-direction:column;justify-content:center;gap:5px;
  min-height:104px;
  padding:26px 24px;border-right:1px solid var(--edge-1);
  background:linear-gradient(160deg,var(--s1),transparent 70%)}
/* Mono, not sans. This is the counter that climbs on every poll, and it is the
   single largest number on the page — proportional digits made it change width
   as it counted, which shifted the sentence beside it. */
.rec-num b{font:700 30px/1 var(--mono);letter-spacing:-.03em;color:var(--ink);
  font-variant-numeric:tabular-nums}
.rec-num span{font:10px/1.3 var(--mono);text-transform:uppercase;
  letter-spacing:.1em;color:var(--ink3)}
.rec-body{padding:24px 26px;display:flex;flex-direction:column;gap:9px;min-width:0}
.rec-lead{margin:0;font:14px/1.5 var(--sans);color:var(--ink);max-width:70ch}
.rec-lead b{font-weight:700}
.rec-why{margin:0;font:12px/1.6 var(--sans);color:var(--ink2);max-width:70ch}
.rec-why em{font-style:italic;color:var(--ink)}
.rec-meta{margin:2px 0 0;font:10px/1 var(--mono);color:var(--ink3);
  text-transform:uppercase;letter-spacing:.07em}
.foot-bar{display:flex;align-items:center;justify-content:space-between;gap:14px;
  padding:14px 24px;border-top:1px solid var(--line);flex-wrap:wrap}
@media (max-width:720px){ .rec{grid-template-columns:1fr}
  .rec-num{border-right:0;border-bottom:1px solid var(--line)} }


/* ── the display step in the chrome ────────────────────────── */
.trust .tr-count b{
  font:600 var(--fs-display)/1 var(--mono);
  font-variant-numeric:tabular-nums;letter-spacing:-.01em;
}
.rec-num b{
  font:600 var(--fs-display)/1 var(--mono);
  font-variant-numeric:tabular-nums;letter-spacing:-.01em;
}


/* ── "How this is measured" modal ──────────────────────────── */
.mm{position:fixed;inset:0;z-index:97;display:flex;align-items:center;
  justify-content:center;padding:20px;
  background:color-mix(in srgb, var(--bg) 70%, transparent);backdrop-filter:blur(6px)}
.mm-card{width:min(520px,94vw);background:var(--card,var(--bg));
  border:1px solid var(--line2);border-radius:var(--r-card);
  box-shadow:var(--elev,none);padding:28px 30px 20px}
.mm-eyebrow{font:600 10.5px/1 var(--mono);letter-spacing:.16em;
  text-transform:uppercase;color:var(--mint);margin:0 0 12px}
.mm-card p{margin:0 0 10px;font:400 12.5px/1.65 var(--sans);color:var(--ink2)}
.mm-card em{color:var(--ink);font-style:normal;font-weight:600}
.mm-x{margin-top:8px;background:var(--brand-fill);color:var(--on-brand);
  border:0;border-radius:var(--r-ctl);padding:8px 16px;
  font:600 12px/1 var(--sans);cursor:pointer}
@media (prefers-reduced-motion: no-preference){
  .mm-card{animation:ob-mm .24s cubic-bezier(.2,.6,.3,1)}
  @keyframes ob-mm{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
}
