/* ONE CONTROL LAYER.
 *
 * Audited before this file existed: 16 distinct control classes, each styled
 * independently as its surface was built — .op-b, .tl-btn, .tl-tab, .dt-asset,
 * .dt-lv button, .py-chip, .cs-btn, .theme-btn, .cw-card, .rl-item, .seg,
 * .chip-q, .linkish and more. Of twenty control rules, SIX had a transition and
 * the border radii ranged over 1, 2, 3, 4, 5, 6px, 50% and var(--r).
 *
 * That is what "the buttons are not smooth" is: not any single control being
 * wrong, but no two of them agreeing on how a control behaves. A click felt
 * different depending on which part of the app you were in.
 *
 * This file does NOT restyle those controls — their colour, size and layout are
 * still owned by the file that built them, so nothing shifts. It normalises the
 * four things that make a set of controls feel like one system:
 *
 *   1. the same timing curve on every state change
 *   2. the same press feedback
 *   3. the same focus ring, on everything, always
 *   4. the same radius scale
 *
 * It loads LAST so it wins the cascade without needing !important anywhere.
 */

:root {
  /* One curve, one duration.
     180ms, up from 130ms. The reference this app is being measured against
     runs 150ms on links and 200ms on buttons and reads as expensive; 130ms
     measured as a cut rather than a fade on the dense surfaces, where a hover
     crosses several rows in the time one transition should take. 180ms sits
     inside that band and stays ONE number, because the finding being copied is
     that perceived smoothness comes from every control agreeing, not from the
     curve. Two durations would forfeit exactly that.
     Anything not a control keeps its own timing on purpose — the ticker's
     arrival fade is .5s in tools.css and is a data signal, not an affordance. */
  /* Duration and easing are ALSO exposed separately. tools.css needed a
     `transition-duration` and could not use --t, because a full shorthand is
     legal in `transition` and illegal in `transition-duration` — the
     declaration is dropped silently and that dim snapped instead of fading. It
     had to hard-code 130ms to work around this file, which is how the app got a
     third duration back after this token was raised to 180ms. Anything needing
     a bare duration should use --t-dur and stay on the ladder. */
  --t-dur: 180ms;
  --t-ease: cubic-bezier(.2, .6, .3, 1);
  --t: var(--t-dur) var(--t-ease);
  /* Cards move FASTER than buttons, not slower. A card is a large surface and
     the eye reads its whole edge at once, so the same 180ms on a card lags
     visibly behind the cursor that triggered it; a button is small enough that
     180ms reads as weight. Three steps, and every control still shares ONE of
     them — the ladder is a hierarchy, not a licence for a fourth value. */
  --t-card-dur: 150ms;
  --t-card: var(--t-card-dur) var(--t-ease);
  --t-slow-dur: 260ms;
  --t-slow: var(--t-slow-dur) var(--t-ease);
}

/* ---- every control, one behaviour --------------------------------------- */
/* Every selector below was checked against the live DOM before shipping. The
   first draft carried `.seg button`, `.chip-q` and `.dt-lad-r.val`, which
   matched ZERO elements — two orphaned from surfaces that no longer exist, one
   simply invented. A rule that matches nothing fails silently, which is how an
   earlier rule in this app targeted `.tl-tabs` when the real class was
   `.tl-rail` and did nothing at all until it was measured. */
.op-b,
.tl-btn,
.tl-tab,
.py-chip,
.dt-asset,
.dt-lv button,
.cs-btn,
.theme-btn,
.rl-item,
.cw-card,
.linkish,
.tr-link,
.next-step,
.dt-lad-r,
.op-mx-c.val,
.fn-r,
/* Added after a second live census. Counts, measured with the surface that
   renders them actually on screen: .cs-opt 24 (asset picker open), .fl-t rect
   15 (flow tiles, which carried the app's last hard-coded .12s), .sum-v 1,
   .controls select 1. The four .dt-* selectors above still read 0 on load and
   are NOT dead: opening a venue renders .dt-asset 9, .dt-lv button 3,
   .dt-lad-r 10, so a load-time census would have deleted live rules. */
.cs-opt,
.fl-t rect,
.sum-v,
.controls select,
/* Handed over by the Strategies agent, which built them in opps.js and left
   them out of this file rather than editing a file it does not own. Counts are
   live, and three of them only exist AFTER an interaction, which is why they
   read 0 on a plain load: .op-cell 34 and .op-pin 34 on arrival, .op-chip 5,
   then .op-xb 5 and .op-t-x 1 once a cell is opened.
   .op-l-b was handed over with them and is NOT here: it matches nothing in any
   state, and does not appear anywhere in the DOM's op-* class list. Adding it
   would have been precisely the rule-that-matches-nothing this file exists to
   stop shipping. */
.op-chip,
.op-xb,
.op-cell,
.tw-x-b,
.op-pin,
.op-t-x {
  transition:
    background-color var(--t),
    border-color var(--t),
    color var(--t),
    /* outline-color so the pre-reserved focus ring COLOURS IN from transparent
       rather than appearing at full strength. */
    outline-color var(--t),
    opacity var(--t),
    fill var(--t),
    stroke var(--t),
    box-shadow var(--t),
    transform var(--t);
}

/* Press. A control that does not move under the finger reads as an image of a
   button rather than a button. */
.op-b:active,
.tl-btn:active,
.tl-tab:active,
.py-chip:active,
.dt-asset:active,
.dt-lv button:active,
.cs-btn:active,
.theme-btn:active,
.op-chip:active,
.op-xb:active,
.op-cell:active,
.tw-x-b:active,
.op-pin:active,
.op-t-x:active {
  transform: translateY(1px);
}

/* Cards travel the other way — they lift toward the cursor, because the click
   opens something rather than toggling a value. */
.cw-card, .tl-card, .op-card {
  transition:
    background-color var(--t-card), border-color var(--t-card),
    color var(--t-card), box-shadow var(--t-card), transform var(--t-card);
}
.cw-card { cursor: pointer; }
.cw-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 20px -8px rgba(0, 0, 0, .22);
  border-color: var(--edge-2);
}
.cw-card:active { transform: translateY(0); }

/* ---- a button behaves like a button, not like text ----------------------- */
/* Measured: 120 of 120 buttons had selectable labels and 0 had appearance:none.
   Double-clicking a chip on the strategies board selected its label, and a
   second click on the same control selected the row around it — the app looked
   like a document being highlighted rather than a control being pressed.
   This is deliberately on the ELEMENT, not on the class list: the class list
   only knows the controls that existed when it was written, and the audit found
   one button (.sum-v, rendered by grid.js) that no stylesheet claimed at all
   and was still wearing UA chrome. */
button {
  -webkit-user-select: none;
  user-select: none;
  -webkit-appearance: none;
  appearance: none;
}

/* Label stability. Every figure on these controls is repolled, so a label CAN
   change under the reader. nowrap is the load-bearing half: a wrapped label is
   what actually resizes a control, and it resizes it vertically, which moves
   everything below it. min-height is set to the smallest height these controls
   already render at (measured: .op-b 26, .py-chip 27, .tl-btn 29, .cs-btn and
   .theme-btn 30, .next-step 31), so it changes nothing today and only stops a
   shorter label collapsing one later.
   .linkish and .tr-link are excluded on purpose — they are text that acts as a
   control, they render at 14px and 20px, and padding them to 26px would open a
   gap in the middle of the footer sentence and the trust bar they sit in. */
.op-b, .tl-btn, .tl-tab, .py-chip, .dt-asset, .dt-lv button,
.cs-btn, .theme-btn, .next-step, .op-chip, .op-xb, .op-pin, .op-t-x {
  white-space: nowrap;
  min-height: 26px;
}
/* .op-cell is excluded from min-height on purpose: it is a matrix CELL, sized
   by its grid row, and a floor under it would push every row of the board. It
   still takes nowrap, which is the half that matters for a repolled figure. */
.op-cell { white-space: nowrap; }

/* ---- one focus ring, on everything -------------------------------------- */
/* The audit found ZERO :focus-visible rules among the matched controls. A
   keyboard user could not see where they were anywhere in the app. */
/* The ring is PRE-RESERVED: every control below ships a transparent outline at
   the exact width and offset the focused ring uses, so focus fades the colour
   in. Without this the browser has no `outline-color` to interpolate FROM and
   the transition above is dead on arrival — the ring snaps on at full strength,
   which is the one control state in the app that was still a cut. */
.op-b,
.tl-btn,
.tl-tab,
.py-chip,
.dt-asset,
.dt-lv button,
.cs-btn,
.theme-btn,
.rl-item,
.cw-card,
.linkish,
.tr-link,
.next-step,
.op-mx-c.val,
.cs-opt,
.sum-v,
.controls select,
.op-chip,
.op-xb,
.op-cell,
.op-pin,
.op-t-x,
summary,
/* ...and the element itself, for the same reason as user-select above: the
   class list covers 41 of the app's 120 buttons, and the ticker fills alone are
   another 30 that a reader can tab into. A transparent outline is invisible and
   costs no layout, so there is no reason to reserve it selectively. */
button {
  outline: 2px solid transparent;
  outline-offset: 2px;
}

.op-b:focus-visible,
.tl-btn:focus-visible,
.tl-tab:focus-visible,
.py-chip:focus-visible,
.dt-asset:focus-visible,
.dt-lv button:focus-visible,
.cs-btn:focus-visible,
.theme-btn:focus-visible,
.rl-item:focus-visible,
.cw-card:focus-visible,
.linkish:focus-visible,
.tr-link:focus-visible,
.next-step:focus-visible,
.op-mx-c.val:focus-visible,
.cs-opt:focus-visible,
.sum-v:focus-visible,
.controls select:focus-visible,
.op-chip:focus-visible,
.op-xb:focus-visible,
.op-cell:focus-visible,
.tw-x-b:focus-visible,
.op-pin:focus-visible,
.op-t-x:focus-visible,
summary:focus-visible,
/* Paired with the reserved ring on `button` below-cascade: reserving a
   transparent outline without colouring it back in would have LOST the ring on
   the 79 buttons the class list does not name. */
button:focus-visible {
  /* The full shorthand, not outline-color alone. Width and offset here are
     IDENTICAL to the reserved ring, so nothing about the box changes when focus
     lands — but they have to be restated: opps.css and nav.css each set their
     own `outline: 1.5px ...; outline-offset: 1px` at :focus-visible, at the
     same specificity and earlier in the cascade, so a colour-only rule here
     would have left .op-b and .rl-item ringing at 1.5px/1px and jumping off
     the reserved 2px/2px. */
  outline: 2px solid var(--mint2);
  outline-offset: 2px;
  border-radius: var(--r-ctl);
}

/* ---- one radius scale ---------------------------------------------------- */
/* Driven from the scale in style.css so this file cannot start a fresh one —
   which is exactly what it did before: it introduced 6px and 10px as literals
   next to the eight radii already live, and became part of the problem it was
   written to fix. */
.op-b, .tl-btn, .tl-tab, .py-chip, .dt-asset, .dt-lv button,
.cs-btn, .cs-opt, .sum-v, .controls select, .fn-r, .op-mx-c.val,
.rl-item, .next-step, .linkish, .tr-link,
.op-chip, .op-xb, .op-cell, .op-pin, .op-t-x { border-radius: var(--r-ctl); }
.cw-card, .tl-card, .op-card, .panel { border-radius: var(--r-card); }

/* ---- views arrive, they do not appear ------------------------------------ */
/* nav.js swaps [hidden], which is instant: the old section vanishes and the new
   one is simply there, mid-scroll, with no sense that one led to the other.
   A short rise-and-fade is the whole difference between "a page changed" and
   "I went somewhere". */
@keyframes view-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
.view:not([hidden]) { animation: view-in var(--t-slow) both; }

/* The venue panel is opened by a click on a card elsewhere on the page, so it
   gets the same arrival — otherwise it materialises under a smooth scroll. */
#detail-root .dt { animation: view-in var(--t-slow) both; }

/* ---- the funnel step ----------------------------------------------------- */
/* The one control that carries a reader from one section to the next. It is
   deliberately the most affordant thing on the page after the venue cards. */
.next-step {
  display: inline-flex; align-items: center; gap: 7px;
  margin-top: 10px; padding: 8px 14px;
  border: 1px solid var(--edge-2); border-radius: var(--r-ctl);
  background: var(--fill-2); color: var(--ink2);
  font: 12.5px/1 var(--sans); text-decoration: none; cursor: pointer;
}
.next-step b { color: var(--mint); transition: transform var(--t); }
.next-step:hover { color: var(--ink); border-color: var(--mint2); background: var(--fill-1); }
.next-step:hover b { transform: translateX(3px); }

/* ---- the click affordance on a venue card -------------------------------- */
.cw-go {
  display: block; margin-top: 10px; padding-top: 9px;
  border-top: 1px solid var(--line);
  font: 11px/1 var(--mono); color: var(--ink3);
}
.cw-go b { color: var(--mint); display: inline-block; transition: transform var(--t); }
.cw-card:hover .cw-go { color: var(--ink2); }
.cw-card:hover .cw-go b { transform: translateX(3px); }

/* ---- the picker, promoted ------------------------------------------------ */
.pick-lead {
  font: 10.5px/1 var(--mono); text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink3);
}
.pick-why { font-size: 11px; color: var(--ink3); }

/* ---- the recording counter ----------------------------------------------
   This strip used to carry four claims — the counter, the date, a venue count
   and "nothing modelled". Four claims in 40px means none of them lands. It now
   carries one, and the one it carries is the only claim that is ours alone:
   every free venue API returns "now" and nothing else, so this history cannot
   be bought or backfilled. It exists because the recording started.

   The figure is therefore allowed to be the largest thing in the chrome. */
.trust {
  display: flex; align-items: baseline; gap: 14px;
  padding: 10px 20px 11px;
  background: var(--bg); border-bottom: 1px solid var(--line);
  font: 12px/1 var(--mono);
}
.tr-count { display: inline-flex; align-items: baseline; gap: 9px; min-width: 0; }
.tr-count b {
  font: 700 22px/1 var(--mono); color: var(--mint);
  /* Tabular figures, or the digits jitter sideways while it counts. */
  font-variant-numeric: tabular-nums; letter-spacing: -.02em;
}
.tr-count i { font-style: normal; color: var(--ink2); }
.tr-since { color: var(--ink3); }
.tr-since b { color: var(--ink2); font-weight: 600; }

.tr-link {
  margin-left: auto; background: none; border: 0; cursor: pointer;
  color: var(--ink2); font: 11.5px/1 var(--mono);
  text-decoration: underline; text-underline-offset: 3px;
  text-decoration-color: var(--line2); padding: 4px 2px;
}
.tr-link:hover { color: var(--mint); text-decoration-color: var(--mint2); }

@media (max-width: 860px) {
  .trust { gap: 3px 14px; padding: 0 12px; font-size: 10.5px; }
  /* The claim is the load-bearing half; the counts can wrap away first. */
  .tr-link { margin-left: 0; }
}

/* ---- respect the setting ------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* The earlier 15px .tr-count rule lived here and, being later in the file,
   silently beat the 22px one above it — same specificity, so source order
   decided, and the counter rendered at 15px while the rule that was supposed
   to size it looked correct. Deleted rather than reordered: two rules for one
   element is how that happens in the first place. */


/* ---- every number is a tabular number ------------------------------------
 * Measured across the five sections: 275 elements render a digit, 262 are
 * already in the mono family — but only 148 carried tabular-nums. The cause is
 * not a missing declaration, it is the `font:` SHORTHAND. Every rule written as
 * `font: 11.5px/1 var(--mono)` resets font-variant-numeric to normal, and this
 * app is written almost entirely in that shorthand, so body's tabular-nums was
 * being switched back off on the exact elements that most need it: .tk-p,
 * .tk-age and .tk-d in the ticker all measured mono=true tabular=false.
 *
 * That is why this is the one !important in the file. The shorthands live in
 * five stylesheets, three of which this file may not touch, and every one of
 * them would silently re-break it. Proportional digits are never wanted
 * anywhere in a screener: a price going 9 -> 1 must not move the column.
 * Scoped to * rather than to a number class because no number class exists —
 * the digits are bare text nodes rendered by six different JS files. */
* { font-variant-numeric: tabular-nums !important; }

/* ---- live numbers may not resize their container -------------------------
 * These three all repoll: the recording counter climbs, the trust counts and
 * the stat cards re-render. Fixing the ROW, not the number, is what stops the
 * jitter — the digits are already tabular above, so the only remaining way a
 * changing value moves the page is by wrapping onto a second line or by a
 * sub-line appearing.
 * The venue panel's "Key numbers" block and the Strategies matrix cells have
 * the same problem and are NOT fixable from here — detail.css and opps.css own
 * them. They are written up in the report instead. */
.trust { min-height: 34px; }
.tr-i, .tr-link { white-space: nowrap; }
/* flex-none as well: in a nowrap row the link was the only shrinkable item, so
   at 1280 it took the whole squeeze and broke "How this is measured" over two
   lines inside a band that is now exactly 40px tall. */
.tr-link { flex: none; }
.cards { grid-auto-rows: minmax(74px, auto); }


/* ---- scrollbars ----------------------------------------------------------
 * Now that panes scroll instead of the document, these are on screen
 * constantly rather than once at the edge of the window, so the default
 * full-bleed thumb reads as a system artefact sitting inside our surface.
 *
 * The thumb is inset by a BORDER in the track colour, not by a margin — a
 * scrollbar thumb does not accept margin, and the border is the only way to
 * make it read as a floating pill with room around it. background-clip keeps
 * the border transparent to the thumb's own fill.
 *
 * Both tokens follow the theme: --edge-2 over the app's ground in either mode,
 * never a fixed grey. Firefox gets the two-keyword form, which is all it
 * supports, and lands in the same place. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--edge-2) transparent;
}
::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background-color: var(--edge-2);
  border: 3px solid transparent;
  background-clip: content-box;
  border-radius: var(--r-pill);
}
::-webkit-scrollbar-thumb:hover { background-color: var(--ink3); }
::-webkit-scrollbar-corner { background: transparent; }

/* ---- section arrival ------------------------------------------------------
 * The DexScreener pass. nav.js switches views by toggling [hidden], and a
 * display:none -> block flip restarts CSS animations for free — so the arrival
 * choreography can live entirely here, with zero JS and zero per-view code.
 * One animation, on the view container only: animating every card separately
 * was tried and reads as the page assembling itself rather than responding.
 * 6px of rise, not more — the stage is a scrollport and a larger travel makes
 * its own scrollbar flicker. Runs on the slow step of the existing ladder so
 * a section change reads as one deliberate gesture, not a cut. */
.stage .view:not([hidden]) {
  animation: view-in var(--t-slow-dur) var(--t-ease) backwards;
}
@keyframes view-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* Press, second half. translateY(1px) alone reads as the label slipping; the
 * 1% scale makes the whole control give under the finger, which is the "click
 * like a game" half of the brief. Scale, not shadow: these controls are flat
 * by design and a press shadow would be their first one. Composited, so it
 * costs nothing on the dense boards. */
.op-b:active, .tl-btn:active, .tl-tab:active, .py-chip:active,
.dt-asset:active, .dt-lv button:active, .cs-btn:active, .theme-btn:active,
.op-chip:active, .op-xb:active, .tw-x-b:active, .op-pin:active, .op-t-x:active {
  transform: translateY(1px) scale(.985);
}

/* Every choreography in this file is decoration over state that is already
 * legible without it. Vestibular users get the state changes with no travel. */
@media (prefers-reduced-motion: reduce) {
  .stage .view:not([hidden]) { animation: none; }
  .op-b:active, .tl-btn:active, .tl-tab:active, .py-chip:active,
  .dt-asset:active, .dt-lv button:active, .cs-btn:active, .theme-btn:active,
  .op-chip:active, .op-xb:active, .tw-x-b:active, .op-pin:active, .op-t-x:active {
    transform: none;
  }
}

/* ── NUMBER VOICE (owner call, 2026-08-05) ─────────────────────────────────
   Every number in the product wears the same face as the venue page's key
   tiles (.dt-tv): Inter, tabular numerals, tight tracking. Mono remains the
   voice of LABELS (small caps column heads, chips, code, tickers) — the
   split is: mono says what a thing is, Inter says how much of it there is.
   Family-only overrides: each rule keeps its own size and weight, so no
   measured column or tile rewraps. ui.css loads last and wins by order. */
.trust .tr-count b, .rec-num b { font-family: var(--sans); letter-spacing: -.02em; }
.card .value { font-family: var(--sans); }
.answer-strip b { font-family: var(--sans); }
.ms-v,                       /* buyers-vs-sellers mosaic values      */
.dt-n-k,                     /* venue page "what traded" numbers     */
.fn-ret {                    /* Data & API demo return value         */
  font-family: var(--sans); letter-spacing: -.02em;
}
.sc .cell,                   /* board rows: values and their sublines */
.sc .cell i,
.pp-n,                       /* row preview numbers                   */
.op-cell b,                  /* strategies matrix cell values         */
.op-t-v, .op-x-val,          /* compare tray + expansion prices       */
.tw-body .tw-c,              /* tape body cells (headers stay mono)   */
.tl-tape td,                 /* mini tape table                       */
table,                       /* venue compare + expiry tables         */
.status {                    /* live pill: contracts count            */
  font-family: var(--sans);
}


/* ── BINANCE CHROME (2026-08-05 direction) ─────────────────────────────────
   An exchange draws no outlines: secondary controls are SURFACES (one grey
   level up), primaries are the yellow fill, and elevation is never a border.
   Family-wide sweep here because ui.css wins the cascade; per-file colour
   rules keep their sizes and paddings. */
.cs-btn, .theme-btn, .controls select, .closebtn,
.op-b, .op-chip, .op-adv, .py-chip, .tl-copy, .tw-x-b, .tw-fmode, .tw-clear,
#col-mode, .dt-asset, .dt-lv button {
  border-color: transparent;
  background-color: var(--s2);
}
.cs-btn:hover, .theme-btn:hover, .controls select:hover, .closebtn:hover,
.op-b:hover, .op-chip:hover, .op-adv:hover, .py-chip:hover, .tl-copy:hover,
.tw-x-b:hover, #col-mode:hover, .dt-asset:hover, .dt-lv button:hover {
  border-color: transparent;
  background-color: var(--s3);
  color: var(--ink);
}
/* Selected state: the accent tint, still borderless. */
.op-b.on, .op-chip.on, .seg button.on, .tw-tab[aria-selected="true"],
.dt-asset.on, .dt-lv button.on {
  border-color: transparent;
  background-color: var(--mint-dim);
  color: var(--mint);
}
/* "firm first, always" and the zero-chip lose their dashed outlines. */
.op-lock, .op-chip.fact { border-style: none; background-color: var(--s1); }

.tr-kpis{
  margin-left: 6px; padding-left: 14px;
  border-left: 1px solid var(--line2);
  font: 11px/1 var(--mono); color: var(--ink3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
@media (max-width: 900px){ .tr-kpis{ display:none; } }
