/* bulldial.com — the design system.
 *
 * One token layer and one set of primitives for every page. Before this,
 * index.html was a dark instrument panel and markets.html was a light
 * table, and the two shared nothing; a visitor crossing between them
 * changed products. They now share this file and differ only in content.
 *
 * Theme. Dark is the default face — the instrument reads as an
 * instrument against black. A visitor whose system asks for light gets
 * light, and the header switch overrides either by writing
 * data-theme onto <html>. Every colour below is written once, with
 * light-dark(): the pair is (light value, dark value), resolved from the
 * color-scheme in force. Nothing here is duplicated per theme.
 *
 * Numerals are tabular everywhere. A price that changes must not move
 * the column it sits in.
 */

/* ═══════════════════════════════════════════════════════════ faces ═══
   Two subsets, each with a unicode-range: a page of Latin never
   downloads the extended file, and a coin name with an accent pulls it
   in on its own. font-display:swap so a slow network shows the page in
   a fallback rather than showing nothing. */

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url("/fonts/inter-latin-wght-normal.woff2") format("woff2-variations");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url("/fonts/inter-latin-ext-wght-normal.woff2") format("woff2-variations");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ═══════════════════════════════════════════════════════════ tokens ═══ */

:root {
  color-scheme: dark;

  /* -- surface ------------------------------------------------------- */
  --bg:        light-dark(#ffffff, #08090c);
  --bg-sunk:   light-dark(#f6f8fb, #050608);
  --surface:   light-dark(#ffffff, #0e1015);
  --surface-2: light-dark(#f7f9fc, #14171e);
  --surface-3: light-dark(#eef2f8, #1a1e27);
  --overlay:   light-dark(rgba(255,255,255,.86), rgba(12,14,18,.86));
  --scrim:     light-dark(rgba(16,20,28,.34), rgba(0,0,0,.62));

  /* -- line ---------------------------------------------------------- */
  --line:      light-dark(#e9edf3, #1b1f27);
  --line-2:    light-dark(#dbe1ea, #262b35);
  --line-3:    light-dark(#c6cede, #343a46);

  /* -- ink ----------------------------------------------------------- */
  --ink:       light-dark(#0b111c, #f2f4f8);
  --ink-2:     light-dark(#4d5a6e, #99a2b1);
  --ink-3:     light-dark(#78859a, #69717f);
  --ink-on-accent: #ffffff;

  /* -- brand + semantics --------------------------------------------- */
  --accent:      light-dark(#2f62f5, #4b8dff);
  --accent-2:    light-dark(#1c48d0, #78abff);
  --accent-sunk: light-dark(rgba(47,98,245,.09), rgba(75,141,255,.14));
  --up:          light-dark(#12a670, #2ebd85);
  --up-sunk:     light-dark(rgba(18,166,112,.10), rgba(46,189,133,.14));
  --down:        light-dark(#e0344a, #f6465d);
  --down-sunk:   light-dark(rgba(224,52,74,.10), rgba(246,70,93,.14));
  --warn:        light-dark(#c8830a, #f0a92e);
  --warn-sunk:   light-dark(rgba(200,131,10,.10), rgba(240,169,46,.14));

  /* -- sector ramp ---------------------------------------------------
     Sequential, not a traffic light: magnitude, never judgement. Amber
     closes the ramp because amber is the limit marking on a graduated
     scale, not because 90 is bad. */
  --s1: light-dark(#8494ad, #465569);
  --s2: light-dark(#4b7fd6, #35659c);
  --s3: light-dark(#2f62f5, #4b8dff);
  --s4: light-dark(#6ba3f7, #8fc0ff);
  --s5: light-dark(#e09612, #f0a92e);

  /* -- type ----------------------------------------------------------
     Inter, self-hosted. The system stack was serving SF Pro on Apple
     hardware and something else everywhere else, so the site had no
     typography of its own — it borrowed the reader's. Inter is the
     face financial data UIs converged on for a reason: unambiguous
     0/O and 1/l/I, even colour at 11px, and true tabular lining
     figures, which is what lets a price column change without the
     column moving.

     One variable file per subset carries every weight, so 350, 500 and
     620 all come from the same 48KB the browser already has. */
  --font: "Inter", -apple-system, BlinkMacSystemFont, system-ui,
          "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-num: "Inter", ui-monospace, "SF Mono", Menlo, monospace;

  --t-display: clamp(2.1rem, 1.2rem + 3.4vw, 3.6rem);
  --t-h1:      clamp(1.6rem, 1.15rem + 1.6vw, 2.15rem);
  --t-h2:      1.16rem;
  --t-h3:      0.985rem;
  --t-body:    0.94rem;
  --t-sm:      0.85rem;
  --t-xs:      0.78rem;
  --t-label:   0.695rem;
  --track:     0.13em;

  /* -- space ---------------------------------------------------------- */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --sp-8: 72px;

  /* -- radius --------------------------------------------------------- */
  --r-1: 6px; --r-2: 10px; --r-3: 14px; --r-4: 20px; --r-full: 999px;

  /* -- elevation -----------------------------------------------------
     Low-contrast and wide. A shadow here says "this floats", never
     "this is important". */
  --e-1: light-dark(0 1px 2px rgba(14,20,32,.05), 0 1px 2px rgba(0,0,0,.5));
  --e-2: light-dark(0 2px 4px rgba(14,20,32,.05), 0 2px 6px rgba(0,0,0,.45)),
         light-dark(0 12px 28px -16px rgba(14,20,32,.22), 0 14px 34px -18px rgba(0,0,0,.7));
  --e-3: light-dark(0 24px 60px -22px rgba(14,20,32,.30), 0 26px 64px -20px rgba(0,0,0,.8));

  /* -- motion --------------------------------------------------------
     Four durations and three curves, used everywhere. `spring` is the
     one that carries anything a finger started. */
  --d-1: 110ms; --d-2: 190ms; --d-3: 300ms; --d-4: 520ms;
  --ease:   cubic-bezier(.32, .72, 0, 1);
  --spring: cubic-bezier(.34, 1.4, .5, 1);
  --smooth: cubic-bezier(.4, 0, .2, 1);

  /* -- layout --------------------------------------------------------- */
  --wide: 1280px;
  --mid: 1080px;
  --bar-h: 56px;
  --tabbar-h: 58px;
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme]) { color-scheme: light; }
}
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"]  { color-scheme: dark; }

/* ══════════════════════════════════════════════════════════════ base ═══ */

*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--bar-h) + var(--safe-t) + 16px);
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  background: var(--bg);
  color: var(--ink);
  font: 400 var(--t-body)/1.55 var(--font);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1, "cv05" 1, "ss03" 1;   /* tabular, l with tail, curved r */
  letter-spacing: -0.011em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overscroll-behavior-y: none;      /* no rubber-band white flash in a shell */

  /* A column so a short page still puts its footer at the bottom of the
     window rather than halfway up it, with the empty space below. */
  min-height: 100svh;
  display: flex;
  flex-direction: column;
}
body > main { flex: 1 0 auto; }
body > footer { flex: none; }

/* In a standalone (installed / Capacitor) window the page is the app: the
   whole viewport is ours, and nothing should select or long-press-drag
   like a document. */
@media (display-mode: standalone) {
  body {
    -webkit-user-select: none; user-select: none;
    -webkit-touch-callout: none;
  }
  input, textarea, [contenteditable], .selectable {
    -webkit-user-select: text; user-select: text;
  }
}

a { color: inherit; text-decoration: none; }
button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
svg { display: block; }
img { max-width: 100%; }
table { border-collapse: collapse; width: 100%; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-1);
}
::selection { background: var(--accent); color: var(--ink-on-accent); }

/* Scrollbars, where the platform draws them at all. */
* { scrollbar-width: thin; scrollbar-color: var(--line-3) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: var(--line-2); border-radius: var(--r-full);
  border: 3px solid transparent; background-clip: content-box;
}
*::-webkit-scrollbar-thumb:hover { background: var(--line-3); background-clip: content-box; }

/* ═══════════════════════════════════════════════════════════ utility ═══ */

.vh {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.grow { flex: 1 1 auto; min-width: 0; }
.num { font-variant-numeric: tabular-nums; }
.up { color: var(--up); }
.down { color: var(--down); }
.flat { color: var(--ink-3); }
.mono { font-family: var(--font-num); letter-spacing: -0.02em; }
.nowrap { white-space: nowrap; }

.label {
  font-size: var(--t-label); font-weight: 600; letter-spacing: var(--track);
  text-transform: uppercase; color: var(--ink-3);
}

.page {
  width: 100%;
  max-width: var(--wide);
  margin: 0 auto;
  padding: var(--sp-5) max(var(--sp-5), var(--safe-l)) calc(var(--sp-8) + var(--safe-b));
}
.page.mid { max-width: var(--mid); }
@media (max-width: 720px) {
  .page { padding: var(--sp-4) var(--sp-4) calc(var(--sp-7) + var(--safe-b)); }
}
/* When the mobile tab bar is showing, the last thing on the page must
   still clear it. */
@media (max-width: 860px) {
  body.has-tabbar .page { padding-bottom: calc(var(--tabbar-h) + var(--safe-b) + var(--sp-6)); }
}

/* ════════════════════════════════════════════════════════════ shell ═══ */

/* -- top bar ---------------------------------------------------------- */

.bd-bar {
  position: sticky; top: 0; z-index: 60;
  padding-top: var(--safe-t);
  background: var(--overlay);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid transparent;
  transition: border-color var(--d-2) var(--smooth),
              background var(--d-2) var(--smooth);
}
/* The rule appears only once the page has moved under it — a border on a
   bar sitting at the top of an unscrolled page is drawing a line for
   nothing. */
.bd-bar.is-stuck { border-bottom-color: var(--line); }

.bd-bar-inner {
  max-width: var(--wide); margin: 0 auto;
  padding: 0 max(var(--sp-5), var(--safe-l)) 0 max(var(--sp-5), var(--safe-r));
  height: var(--bar-h);
  display: flex; align-items: center; gap: var(--sp-5);
}
@media (max-width: 720px) { .bd-bar-inner { padding: 0 var(--sp-4); gap: var(--sp-3); } }

.bd-brand {
  display: inline-flex; align-items: center; gap: 9px; flex: none;
  font-weight: 700; font-size: 0.95rem; letter-spacing: -0.02em;
  color: var(--ink);
  transition: opacity var(--d-1) var(--smooth);
}
.bd-brand:active { opacity: .6; }
.bd-brand svg { width: 24px; height: 24px; color: var(--accent); flex: none; }
.bd-brand b { font-weight: 700; }

.bd-nav { display: flex; align-items: center; gap: 2px; }
.bd-nav a {
  position: relative;
  padding: 7px 11px; border-radius: var(--r-2);
  font-size: var(--t-sm); font-weight: 550; color: var(--ink-2);
  transition: color var(--d-1) var(--smooth), background var(--d-1) var(--smooth);
}
.bd-nav a:hover { color: var(--ink); background: var(--surface-2); }
.bd-nav a.on { color: var(--ink); }
.bd-nav a.on::after {
  content: ""; position: absolute; left: 11px; right: 11px; bottom: -1px;
  height: 2px; border-radius: 2px 2px 0 0; background: var(--accent);
}
@media (max-width: 860px) { .bd-nav { display: none; } }

/* -- mobile tab bar ---------------------------------------------------
   The one element that most makes a site feel like an app on a phone:
   destinations sit under the thumb, not behind a hamburger. */

.bd-tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  display: none;
  padding: 6px max(6px, var(--safe-l)) calc(6px + var(--safe-b)) max(6px, var(--safe-r));
  background: var(--overlay);
  -webkit-backdrop-filter: saturate(180%) blur(24px);
  backdrop-filter: saturate(180%) blur(24px);
  border-top: 1px solid var(--line);
}
@media (max-width: 860px) { .bd-tabbar { display: flex; } }

.bd-tabbar a {
  flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 6px 2px; border-radius: var(--r-2);
  font-size: 0.63rem; font-weight: 600; letter-spacing: 0.02em;
  color: var(--ink-3);
  -webkit-tap-highlight-color: transparent;
  transition: color var(--d-1) var(--smooth), transform var(--d-1) var(--spring);
}
.bd-tabbar a svg { width: 21px; height: 21px; stroke-width: 1.9; }
.bd-tabbar a.on { color: var(--accent); }
.bd-tabbar a:active { transform: scale(.9); }

/* -- route progress ---------------------------------------------------
   A 2px thread at the top edge while the next document is on the wire.
   It exists so a tap is never followed by nothing. */

.bd-progress {
  position: fixed; top: 0; left: 0; right: 0; height: 2px; z-index: 90;
  transform: scaleX(0); transform-origin: 0 50%;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  opacity: 0; pointer-events: none;
}
.bd-progress.run { opacity: 1; animation: bd-progress 1.4s var(--ease) forwards; }
.bd-progress.done { opacity: 0; transform: scaleX(1); transition: opacity 260ms 80ms; }
@keyframes bd-progress {
  0%   { transform: scaleX(0); }
  55%  { transform: scaleX(.72); }
  100% { transform: scaleX(.94); }
}

/* ═══════════════════════════════════════════════════════ primitives ═══ */

/* -- button ------------------------------------------------------------ */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 9px 15px; border-radius: var(--r-2);
  font-size: var(--t-sm); font-weight: 600; letter-spacing: -0.005em;
  white-space: nowrap; border: 1px solid transparent;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--d-1) var(--smooth), border-color var(--d-1) var(--smooth),
              color var(--d-1) var(--smooth), transform var(--d-1) var(--spring),
              opacity var(--d-1) var(--smooth);
}
.btn:active { transform: scale(.965); }
.btn svg { width: 16px; height: 16px; flex: none; }
.btn[disabled], .btn[aria-disabled="true"] { opacity: .45; pointer-events: none; }

.btn.primary { background: var(--accent); color: var(--ink-on-accent); }
.btn.primary:hover { background: var(--accent-2); }
.btn.ghost { border-color: var(--line-2); color: var(--ink); background: var(--surface); }
.btn.ghost:hover { background: var(--surface-2); border-color: var(--line-3); }
.btn.quiet { color: var(--ink-2); }
.btn.quiet:hover { background: var(--surface-2); color: var(--ink); }
.btn.danger { color: var(--down); border-color: var(--line-2); background: var(--surface); }
.btn.danger:hover { background: var(--down-sunk); border-color: var(--down); }
.btn.sm { padding: 6px 11px; font-size: var(--t-xs); border-radius: var(--r-1); }
.btn.lg { padding: 13px 22px; font-size: var(--t-body); border-radius: var(--r-3); }
.btn.block { width: 100%; }

/* An icon-only control: square, centred, no text metrics involved. */
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: var(--r-2);
  color: var(--ink-2); flex: none;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--d-1) var(--smooth), color var(--d-1) var(--smooth),
              transform var(--d-1) var(--spring);
}
.icon-btn:hover { background: var(--surface-2); color: var(--ink); }
.icon-btn:active { transform: scale(.9); }
.icon-btn svg { width: 18px; height: 18px; }

/* -- chip -------------------------------------------------------------- */

.chip {
  padding: 7px 13px; border-radius: var(--r-full);
  background: var(--surface-2); border: 1px solid transparent;
  font-size: var(--t-xs); font-weight: 600; color: var(--ink-2);
  white-space: nowrap; -webkit-tap-highlight-color: transparent;
  transition: background var(--d-1) var(--smooth), color var(--d-1) var(--smooth),
              transform var(--d-1) var(--spring);
}
.chip:hover { color: var(--ink); }
.chip:active { transform: scale(.95); }
.chip.on { background: var(--ink); color: var(--bg); }

.chips {
  display: flex; gap: var(--sp-2); overflow-x: auto;
  scrollbar-width: none; -webkit-overflow-scrolling: touch;
  scroll-snap-type: x proximity;
}
.chips::-webkit-scrollbar { display: none; }
.chips > * { scroll-snap-align: start; }

/* -- badge ------------------------------------------------------------- */

.badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 8px; border-radius: var(--r-1);
  font-size: var(--t-label); font-weight: 700; letter-spacing: 0.04em;
  text-transform: uppercase;
}
.badge.up   { background: var(--up-sunk);   color: var(--up); }
.badge.down { background: var(--down-sunk); color: var(--down); }
.badge.warn { background: var(--warn-sunk); color: var(--warn); }
.badge.neutral { background: var(--surface-3); color: var(--ink-2); }
.badge.accent { background: var(--accent-sunk); color: var(--accent); }

/* -- card -------------------------------------------------------------- */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-3);
  padding: var(--sp-4);
  box-shadow: var(--e-1);
}
.card.pad-lg { padding: var(--sp-5); }
.card-h {
  display: flex; align-items: center; gap: var(--sp-2);
  font-size: var(--t-h3); font-weight: 650; letter-spacing: -0.01em;
  margin-bottom: var(--sp-3);
}
.card-h .more {
  margin-left: auto; font-size: var(--t-xs); font-weight: 550; color: var(--ink-3);
  transition: color var(--d-1) var(--smooth);
}
.card-h .more:hover { color: var(--accent); }

/* A card that is also a link or button: it should lift on hover and
   press on tap, so the whole rectangle reads as one target. */
.card.tappable {
  cursor: pointer;
  transition: transform var(--d-2) var(--ease), box-shadow var(--d-2) var(--ease),
              border-color var(--d-2) var(--ease);
}
.card.tappable:hover { transform: translateY(-2px); box-shadow: var(--e-2); border-color: var(--line-2); }
.card.tappable:active { transform: translateY(0) scale(.99); }

/* -- field ------------------------------------------------------------- */

.field {
  width: 100%; padding: 11px 13px;
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--line-2); border-radius: var(--r-2);
  font-size: var(--t-body);
  transition: border-color var(--d-1) var(--smooth), background var(--d-1) var(--smooth);
}
.field::placeholder { color: var(--ink-3); }
.field:focus { outline: none; border-color: var(--accent); background: var(--surface); }
.field:focus-visible { outline: none; }

.search-box {
  display: flex; align-items: center; gap: 8px;
  background: var(--surface-2); border: 1px solid var(--line-2);
  border-radius: var(--r-2); padding: 0 12px; height: 36px;
  color: var(--ink-3); min-width: 200px;
  transition: border-color var(--d-1) var(--smooth), background var(--d-1) var(--smooth);
}
.search-box:focus-within { border-color: var(--accent); background: var(--surface); }
.search-box svg { width: 16px; height: 16px; flex: none; }
.search-box input {
  border: 0; background: transparent; outline: 0; width: 100%;
  font-size: var(--t-sm); color: var(--ink);
}

/* -- table -------------------------------------------------------------
   Sticky head, hairline rows, no vertical rules. Density comes from
   line-height, not from boxes. */

.tablewrap {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-3); overflow: hidden;
}
.scroller { overflow-x: auto; -webkit-overflow-scrolling: touch; }

table.data { font-size: var(--t-sm); }
table.data thead th {
  position: sticky; top: calc(var(--bar-h) + var(--safe-t)); z-index: 2;
  background: var(--surface);
  font-size: var(--t-xs); font-weight: 600; color: var(--ink-3);
  text-align: right; white-space: nowrap;
  padding: 11px 12px;
  border-bottom: 1px solid var(--line);
}
table.data thead th:first-child, table.data thead th.left { text-align: left; }
table.data tbody td {
  padding: 13px 12px; text-align: right; white-space: nowrap;
  border-bottom: 1px solid var(--line);
}
table.data tbody td:first-child, table.data tbody td.left { text-align: left; }
table.data tbody tr:last-child td { border-bottom: 0; }
table.data tbody tr { transition: background var(--d-1) var(--smooth); }
table.data tbody tr:hover { background: var(--surface-2); }

/* -- sheet / modal ------------------------------------------------------
   One component, two presentations: a centred dialog on a pointer, a
   bottom sheet on a phone. The phone case is what an app does. */

.sheet-scrim {
  position: fixed; inset: 0; z-index: 80;
  background: var(--scrim);
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
  display: flex; align-items: center; justify-content: center;
  padding: var(--sp-4);
  animation: bd-fade var(--d-2) var(--smooth);
}
.sheet {
  width: 100%; max-width: 440px; max-height: min(84svh, 720px);
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  background: var(--surface); border: 1px solid var(--line-2);
  border-radius: var(--r-4); box-shadow: var(--e-3);
  padding: var(--sp-5);
  animation: bd-pop var(--d-3) var(--spring);
}
.sheet h2, .sheet h3 { font-size: var(--t-h2); font-weight: 650; letter-spacing: -0.02em; }
.sheet-actions { display: flex; gap: var(--sp-2); justify-content: flex-end; margin-top: var(--sp-5); }
.sheet-grip { display: none; }

@media (max-width: 620px) {
  .sheet-scrim { align-items: flex-end; padding: 0; }
  .sheet {
    max-width: none; max-height: 88svh;
    border-radius: var(--r-4) var(--r-4) 0 0;
    border-bottom: 0;
    padding: var(--sp-3) var(--sp-4) calc(var(--sp-5) + var(--safe-b));
    animation: bd-sheet-up var(--d-3) var(--ease);
  }
  .sheet-grip {
    display: block; width: 38px; height: 4px; border-radius: var(--r-full);
    background: var(--line-3); margin: 0 auto var(--sp-4);
  }
  .sheet-actions { flex-direction: column-reverse; }
  .sheet-actions .btn { width: 100%; padding: 13px; }
}

/* -- toast -------------------------------------------------------------- */

.bd-toasts {
  position: fixed; z-index: 100; left: 50%; transform: translateX(-50%);
  bottom: calc(var(--safe-b) + var(--sp-5));
  display: flex; flex-direction: column; gap: var(--sp-2);
  pointer-events: none; width: min(420px, calc(100vw - 32px));
}
@media (max-width: 860px) {
  body.has-tabbar .bd-toasts { bottom: calc(var(--tabbar-h) + var(--safe-b) + var(--sp-3)); }
}
.bd-toast {
  pointer-events: auto;
  display: flex; align-items: center; gap: var(--sp-2);
  padding: 11px 15px; border-radius: var(--r-3);
  background: var(--surface-3); border: 1px solid var(--line-2);
  box-shadow: var(--e-2); font-size: var(--t-sm);
  animation: bd-toast-in var(--d-3) var(--spring);
}
.bd-toast.out { animation: bd-toast-out var(--d-2) var(--smooth) forwards; }
.bd-toast .dot { width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--accent); }
.bd-toast.ok .dot { background: var(--up); }
.bd-toast.err .dot { background: var(--down); }

/* -- skeleton -----------------------------------------------------------
   Shown instead of an em dash while data is on the wire. The shape of
   the page is known before the numbers are, so it is drawn first and
   nothing jumps when they land. */

.sk {
  display: block; border-radius: var(--r-1);
  background: linear-gradient(90deg,
    var(--surface-2) 8%, var(--surface-3) 20%, var(--surface-2) 34%);
  background-size: 800px 100%;
  animation: bd-shimmer 1.5s linear infinite;
  color: transparent !important; user-select: none;
}
.sk.text { height: 0.75em; width: 100%; }
.sk.w-3 { width: 30%; } .sk.w-4 { width: 42%; } .sk.w-6 { width: 60%; } .sk.w-8 { width: 78%; }

/* -- meter ---------------------------------------------------------------
   The dial's 0–100 scale, repeated linearly wherever a component needs
   to show where it sits on the same scale. */

.meter { position: relative; height: 6px; border-radius: var(--r-full); background: var(--surface-3); overflow: hidden; }
.meter-fill {
  position: absolute; inset: 0 auto 0 0; border-radius: var(--r-full);
  background: var(--accent);
  transition: width var(--d-4) var(--ease);
}
.meter-mark {
  position: absolute; top: -3px; width: 2px; height: 12px; border-radius: 1px;
  background: var(--ink); transform: translateX(-1px);
  transition: left var(--d-4) var(--ease);
}

/* -- live dot ------------------------------------------------------------ */

.live-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--up); flex: none; position: relative; }
.live-dot::after {
  content: ""; position: absolute; inset: -3px; border-radius: 50%;
  border: 1.5px solid var(--up); animation: bd-ping 2s var(--ease) infinite;
}
.live-dot.stale { background: var(--warn); }
.live-dot.stale::after { border-color: var(--warn); animation: none; opacity: .3; }
.live-dot.off { background: var(--ink-3); }
.live-dot.off::after { display: none; }

/* -- coin avatar ---------------------------------------------------------- */

.ava {
  width: 26px; height: 26px; border-radius: 50%; flex: none;
  object-fit: cover; background: var(--surface-3);
}
.ava.fallback {
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 10px; font-weight: 700; color: #fff; letter-spacing: -0.02em;
}

/* -- empty state ----------------------------------------------------------- */

.empty {
  text-align: center; padding: var(--sp-7) var(--sp-4);
  color: var(--ink-3); font-size: var(--t-sm);
}
.empty svg { width: 34px; height: 34px; margin: 0 auto var(--sp-3); opacity: .5; }

/* ══════════════════════════════════════════════════════════ motion ═══ */

@keyframes bd-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes bd-pop { from { opacity: 0; transform: translateY(10px) scale(.97); } to { opacity: 1; transform: none; } }
@keyframes bd-sheet-up { from { transform: translateY(100%); } to { transform: none; } }
@keyframes bd-shimmer { to { background-position: 800px 0; } }
@keyframes bd-ping { 0% { transform: scale(.7); opacity: .9; } 70%, 100% { transform: scale(1.7); opacity: 0; } }
@keyframes bd-toast-in { from { opacity: 0; transform: translateY(14px) scale(.96); } to { opacity: 1; transform: none; } }
@keyframes bd-toast-out { to { opacity: 0; transform: translateY(8px) scale(.98); } }

/* Content arriving after first paint fades up rather than appearing. The
   stagger is capped: past a dozen items it stops being a reveal and
   starts being a wait. */
@keyframes bd-rise { from { opacity: 0; transform: translateY(9px); } to { opacity: 1; transform: none; } }
.rise { animation: bd-rise var(--d-3) var(--ease) both; }
.rise-1 { animation-delay: 30ms; } .rise-2 { animation-delay: 60ms; }
.rise-3 { animation-delay: 90ms; } .rise-4 { animation-delay: 120ms; }
.rise-5 { animation-delay: 150ms; } .rise-6 { animation-delay: 180ms; }

/* A value that just changed gets a one-shot tint, so a moving table can
   be read by where the eye is drawn rather than by re-reading it. */
@keyframes bd-flash-up   { 0% { background: var(--up-sunk); }   100% { background: transparent; } }
@keyframes bd-flash-down { 0% { background: var(--down-sunk); } 100% { background: transparent; } }
.tick-up   { animation: bd-flash-up 800ms var(--smooth); }
.tick-down { animation: bd-flash-down 800ms var(--smooth); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important; animation-iteration-count: 1 !important;
    transition-duration: 1ms !important; scroll-behavior: auto !important;
  }
}

/* ══════════════════════════════════════════════════════════════ tape ═══
   A price strip between the bar and the page, on any page that carries
   a #tape-track. Scrolls sideways under a finger; never animates on its
   own — a marquee is decoration, and the numbers are not. */

.tape {
  border-bottom: 1px solid var(--line);
  background: var(--bg);
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.tape::-webkit-scrollbar { display: none; }
.tape-track {
  display: flex; align-items: stretch;
  max-width: var(--wide); margin: 0 auto;
  padding: 0 max(var(--sp-5), var(--safe-l));
  min-height: 38px;
}
@media (max-width: 720px) { .tape-track { padding: 0 var(--sp-4); } }
.tape a {
  display: inline-flex; align-items: center; gap: 7px; flex: none;
  padding: 0 var(--sp-4); border-right: 1px solid var(--line);
  font-size: var(--t-xs); white-space: nowrap;
  transition: background var(--d-1) var(--smooth);
}
.tape a:first-child { padding-left: 0; }
.tape a:hover { background: var(--surface-2); }
.tape .sym { font-weight: 650; letter-spacing: 0.01em; }
.tape .px  { font-family: var(--font-num); color: var(--ink-2); }
.tape .chg { font-family: var(--font-num); }

/* ════════════════════════════════════════════════════ cluster rail ═══
   The site's annunciator strip: every live reading under the bar on
   every page, in the colour of the sector each reading sits in. The
   trailing entry is source health — the dot reads green only when every
   source answered its last collection pass. Labels and values, nothing
   else; it is signage, not commentary. */

.cluster-rail {
  border-bottom: 1px solid var(--line);
  background: var(--bg);
  overflow-x: auto; scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.cluster-rail::-webkit-scrollbar { display: none; }
.cr-track {
  display: flex; align-items: center; gap: var(--sp-5);
  max-width: var(--wide); margin: 0 auto;
  padding: 0 max(var(--sp-5), var(--safe-l));
  min-height: 36px;
}
@media (max-width: 720px) { .cr-track { gap: var(--sp-4); padding: 0 var(--sp-4); } }
.cluster-rail a {
  display: inline-flex; align-items: center; gap: 7px; flex: none;
  padding: 8px 0; font-size: var(--t-xs); white-space: nowrap;
  color: var(--ink-2);
}
.cluster-rail a:hover .cr-v { text-decoration: underline; text-underline-offset: 3px; }
.cr-k {
  font-size: var(--t-label); font-weight: 650; letter-spacing: var(--track);
  text-transform: uppercase; color: var(--ink-3);
}
.cr-v { font-weight: 650; color: var(--ink); }
.cr-b {
  font-size: var(--t-label); letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-3);
}
.cr-sys {
  margin-left: auto; font-size: var(--t-label); font-weight: 600;
  letter-spacing: var(--track); text-transform: uppercase; color: var(--ink-3);
}

/* ═════════════════════════════════════════════════ view transitions ═══
   Cross-document transitions: navigating between our own pages
   cross-fades the body and holds the shell still, which is the single
   cheapest thing that makes a multi-page site feel like one app. The
   bar and tab bar are named so the browser morphs them instead of
   fading them out and back in. */

@view-transition { navigation: auto; }

::view-transition-old(root) { animation: bd-vt-out 130ms var(--smooth) both; }
::view-transition-new(root) { animation: bd-vt-in 240ms var(--ease) both; }
@keyframes bd-vt-out { to { opacity: 0; } }
@keyframes bd-vt-in { from { opacity: 0; transform: translateY(6px); } }

.bd-bar { view-transition-name: bd-bar; }
.bd-tabbar { view-transition-name: bd-tabbar; }
::view-transition-group(bd-bar), ::view-transition-group(bd-tabbar) { animation-duration: 0s; }

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
}

/* ══════════════════════════════════════════════════════════ print ═══ */

@media print {
  .bd-bar, .bd-tabbar, .bd-toasts, .bd-progress { display: none !important; }
  body { background: #fff; color: #000; }
}

/* ══════════════════════════════════════════════ bar-resident controls ═══ */

/* The search affordance is a button, not an input: it opens the sheet,
   and a real input in the bar would compete with the sheet's own for
   focus on a phone. */
.bd-searchbtn {
  height: 34px; min-width: 210px; cursor: text;
  justify-content: flex-start; font-size: var(--t-sm); color: var(--ink-3);
}
.bd-searchbtn kbd {
  margin-left: auto; font: 600 10px/1 var(--font); letter-spacing: .04em;
  color: var(--ink-3); background: var(--surface-3);
  border: 1px solid var(--line-2); border-radius: 5px; padding: 4px 5px;
}
.bd-searchbtn-sm { display: none; }
@media (max-width: 1000px) {
  .bd-searchbtn { display: none; }
  .bd-searchbtn-sm { display: inline-flex; }
}

.authnav { display: inline-flex; align-items: center; gap: var(--sp-2); flex: none; }
.authnav-login {
  display: inline-flex; align-items: center; padding: 8px 14px; border-radius: var(--r-2);
  background: var(--accent); color: var(--ink-on-accent);
  font-size: var(--t-sm); font-weight: 600;
  transition: background var(--d-1) var(--smooth), transform var(--d-1) var(--spring);
}
.authnav-login:hover { background: var(--accent-2); }
.authnav-login:active { transform: scale(.96); }
.authnav-email {
  font-size: var(--t-xs); color: var(--ink-2); max-width: 15ch;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.authnav-avatar {
  width: 30px; height: 30px; border-radius: 50%; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-sunk); color: var(--accent);
  font-size: 11px; font-weight: 700; text-transform: uppercase;
  border: 1px solid var(--line-2);
  transition: transform var(--d-1) var(--spring);
}
.authnav-avatar:active { transform: scale(.9); }
@media (max-width: 720px) { .authnav-email { display: none; } }

/* -- search results ----------------------------------------------------- */

.bd-results { margin-top: var(--sp-3); max-height: 52svh; overflow-y: auto; }
.bd-result {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: 9px 10px; border-radius: var(--r-2);
  transition: background var(--d-1) var(--smooth);
}
.bd-result:hover { background: var(--surface-2); }
.bd-result-name { font-size: var(--t-sm); font-weight: 600; flex: 1; min-width: 0; }
.bd-result-name em { font-style: normal; color: var(--ink-3); font-weight: 500; margin-left: 6px; font-size: var(--t-xs); }
.bd-result-price { font-size: var(--t-sm); color: var(--ink-2); }

/* ══════════════════════════════════════════════════════════════ footer ═══
   One footer, on every page, at body level so it sits on its own band
   and a short page still puts it at the bottom of the window. */

.site-foot {
  border-top: 1px solid var(--line);
  background: var(--surface-2);
  margin-top: var(--sp-8);
}
.site-foot .page { padding-block: var(--sp-6) var(--sp-5); }

.fgrid {
  display: grid; grid-template-columns: 1.6fr repeat(3, 1fr);
  gap: var(--sp-5); padding-bottom: var(--sp-5);
}
@media (max-width: 760px) { .fgrid { grid-template-columns: 1fr 1fr; gap: var(--sp-4); } }

.fbrand p {
  color: var(--ink-3); font-size: var(--t-xs); margin-top: 10px;
  max-width: 30ch; line-height: 1.6;
}
.fbrand .btn { margin-top: var(--sp-3); }

.fcol h4 {
  font-size: var(--t-label); font-weight: 650; letter-spacing: var(--track);
  text-transform: uppercase; color: var(--ink-3); margin-bottom: 10px;
}
.fcol a { display: block; font-size: var(--t-xs); color: var(--ink-2); padding: 4px 0; }
.fcol a:hover { color: var(--accent); }

.fbottom {
  border-top: 1px solid var(--line); padding-top: var(--sp-4);
  display: flex; gap: var(--sp-4); align-items: baseline; flex-wrap: wrap;
}
/* The one paragraph on the page that cannot be cut: it is a regulatory
   statement, not an explanation. */
.fbottom p { font-size: var(--t-label); color: var(--ink-3); line-height: 1.7; max-width: 76ch; flex: 1; }
.fbottom .stamp { font-size: var(--t-label); color: var(--ink-3); white-space: nowrap; }
