/* responsive.css — implements TERMINAL_DOCTRINE §4.5 (5-tier system).
 * Loaded LAST after every other CSS file so it can correct earlier rules.
 *
 * Doctrine tiers (viewport-width based):
 *   mobile    < 900px   ui-scale 0.9   single-column
 *   laptop    900-1599  ui-scale 1.0   12-column (default)
 *   desk      1600-2399 ui-scale 1.05  12-column, denser
 *   wall-75   2400-3599 ui-scale 1.55  12-column, content halved
 *   wall-100  >= 3600   ui-scale 2.0   12-column, content halved-and-again
 */

:root {
  --ui-scale: 1;
  --touch-min: 32px;
  /* Scaled tokens — components that read these get viewport-aware sizing for free. */
  --type-hero-s: calc(28px * var(--ui-scale));
  --type-num-s:  calc(15px * var(--ui-scale));
  --type-ui-s:   calc(13px * var(--ui-scale));
  --type-lbl-s:  calc(10px * var(--ui-scale));
  --gap-s:       calc(12px * var(--ui-scale));
  --row-h-s:     calc(28px * var(--ui-scale));
  /* Hairline must NEVER scale (doctrine §4.5.2). */
  --hairline:    1px;
}

@media (max-width: 899px), (hover: none) and (pointer: coarse)  { :root { --ui-scale: 0.9;  --touch-min: 44px; } }
@media (min-width: 1600px) { :root { --ui-scale: 1.05; } }
@media (min-width: 2400px) { :root { --ui-scale: 1.55; } }
@media (min-width: 3600px) { :root { --ui-scale: 2.0;  } }

/* ─── Tape sentiment dot — small color pip on the left edge of each news row ─ */
.tape2__row--up::before,
.tape2__row--dn::before,
.tape2__row--neutral::before {
  content: '';
  display: inline-block;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  margin-right: 6px;
  flex-shrink: 0;
}
.tape2__row--up::before      { background: #34D399; box-shadow: 0 0 4px rgba(52,211,153,0.55); }
.tape2__row--dn::before      { background: #F87171; box-shadow: 0 0 4px rgba(248,113,113,0.55); }
.tape2__row--neutral::before { background: rgba(255,255,255,0.18); }

/* Make mover cards visibly clickable (already wire to entity pulse via data-entity) */
.movers2__card { cursor: pointer; transition: transform 140ms ease-out, border-color 140ms ease-out; }
.movers2__card:hover { transform: translateY(-1px); border-color: rgba(201,168,106,0.45); }

/* ─── Top bar — guard against TERMINAL/COCKPIT overlap at narrow desktop widths.
 * The static "TERMINAL · v2.11.0" label only meaningfully fits at desk tier (1600+).
 * Below that, hide it cleanly. */
@media (max-width: 1599px) {
  .topbar__product { display: none !important; }
  .topbar__brand .ver { display: none !important; }
  .topbar__sep { display: none !important; }
}
/* Also: at any width, ensure brand column has a hard minimum padding to its right
 * so even if media query timing is off, the nav can't visually touch TERMINAL. */
.topbar__brand { padding-right: 16px; }

/* Chrome auto-translate prompt — disable for this page */
html { translate: no; }

/* ─── Hero strip — 4 cards + AI take line. Desktop: equal grid. Mobile: swipe carousel. ─
 * Grid layout: 4 columns × 2 rows.
 *   Row 1 (1fr): the 4 quote cards — fills available space
 *   Row 2 (auto): the AI take line — content-sized
 * min-height ensures cards have enough room for top + price + spark + chg row
 * even on short laptops. The AI take takes its content height on top of that. */
.hero-strip {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: 1fr auto;  /* cards fill, ai take auto-sized */
  gap: 8px;
  height: 100%;
  min-height: 170px;  /* card 130 + ai take 30 + gap ≈ 170 desktop default */
  overflow: hidden;
}
.hero-strip__ai-take { grid-column: 1 / -1; }  /* AI take always spans all 4 cols */

@media (min-width: 900px) and (max-height: 800px) {
  /* Short laptop — card 110 + ai 25 + gap = 145 */
  .hero-strip { min-height: 150px; }
}
@media (min-width: 900px) and (max-height: 730px) {
  /* Ultra-short (300%+ Windows scaling, or 250% on 4K laptops like ASUS H7606WX
   * which produces 1422×701 CSS viewport — just above the doctrine ≤700 line
   * but visually identical squeeze). Threshold widened 700 → 730 in v45 to
   * catch the user's primary device. Card 100 + ai 22 + gap = 130 */
  .hero-strip { min-height: 135px; gap: 6px; }
  .hero-strip__ai-take { font-size: 11px; line-height: 1.3; padding: 4px 8px; }
  .quote { gap: 1px; padding: 4px 8px 6px; }
  .quote__sym { font-size: 16px; }
  .quote__name { font-size: 8.5px; }
  .quote__price { font-size: 20px; line-height: 1; }
  .quote__chg-row { font-size: 9.5px; gap: 4px; line-height: 1.1; }
  /* v47 — hide BID·ASK spread row at ≤730h: card budget ~104px can't fit
   * sym(13) + price(20) + chg(11) + spread(13) + tps(10) + 4 gaps = 71+ with
   * spread row partially clipped per user screenshot. Spread info is also in
   * the top ticker tape; hiding it preserves the primary price+chg signal. */
  .quote__spread { display: none !important; }
  .quote__tps { font-size: 8.5px; }
  /* Brief panel further compression — and HIDE brief2__foot (info duplicated
   * by PanelHeartbeat in panel header). Same doctrine pattern as stance__foot
   * hide at ≤800h: at narrow heights, drop optional metadata, don't shrink
   * essential content. responsive.css:1076 sets .brief2__foot to
   * `display: flex !important`; the !important here is required to override. */
  .brief2 { padding: 6px 12px; gap: 3px; }
  .brief2__headline { font-size: 15px; line-height: 1.2; padding-bottom: 2px; }
  .brief2__paras { font-size: 11.5px; line-height: 1.45; flex: 1 1 32px !important; min-height: 32px !important; gap: 4px; }
  .brief2__watch { padding-top: 2px; }
  .brief2__watch-h { font-size: 8px; }
  .brief2__watch-row { font-size: 10px; padding: 1px 0; }
  /* .brief2__foot { display: none } lives later (after L1086) to win source
   * order over the base `display: flex !important` rule. */
}
.hero-strip__card {
  min-width: 0;
  /* Card stretches to fill the hero-strip cell, but never beyond — internal
     overflow:hidden on .quote clips any chg-row / spread that doesn't fit. */
  min-height: 0;
  overflow: hidden;
}

/* ══════════════════════════════════════════════════════════════════════════
 * §4.5.6 — Narrow viewport heights · UNIFIED COMPRESSION BLOCK
 * ══════════════════════════════════════════════════════════════════════════
 * Applies to every page across the terminal (Cockpit, Crypto, Defi, Markets,
 * Macro, Flows, Options, Predict, News, AI).
 *
 * Triggered when the user's effective CSS viewport height is short:
 *   tier-A:   max-height ≤ 800px — laptops 1080p / 1440p with light scaling
 *   tier-B:   max-height ≤ 700px — 4K / OLED at 250-300% Windows scaling
 *                                  (ASUS H7606WX class, user's primary device)
 *
 * Strategy: compress each dense data row's padding and gap so the same row
 * count fits in the constrained vertical row of the base 4-row .main grid
 * (1.18fr / 1.55fr / 1.55fr / auto) without clipping or scrolling.
 *
 * Rules touch ONLY row/gap/cell padding and panel chrome — never font-size
 * floors (legibility) or grid-template (per-page identity).
 * ════════════════════════════════════════════════════════════════════════ */

@media (min-width: 900px) and (max-height: 800px) {
  /* ── Panel header + AI-synth-line: shave ~6px off vertical chrome ── */
  .panel__h { padding: 4px 12px; min-height: 28px; }
  .ai-synth-line { padding: 3px 12px 4px; }

  /* ── Tables (CRYPTO funding, DEFI, MARKETS, MACRO, FLOWS, OPTIONS, NEWS) ── */
  .ftable td, .ftable th { padding: 4px 6px; }
  .defi-tbl tbody td, .defi-tbl thead th { padding: 3px 10px; }
  .yield-tbl td, .yield-tbl th { padding: 3px 10px; }
  .borrow-tbl td, .borrow-tbl th { padding: 3px 10px; }
  .ytable td { padding: 4px 4px; }
  .ytable th { padding: 3px 4px; }
  .fxtable__row { padding: 4px 12px; }
  .cbrates__row { padding: 4.5px 12px; }
  .etf-tbl tbody td, .etf-tbl thead th { padding: 5px 12px; }
  .options-tbl td, .options-tbl th,
  .chain tbody td, .chain thead th { padding: 4px 6px; }
  .econ-tbl tbody td, .econ-tbl thead th { padding: 3px 10px; }
  .earn-tbl tbody td, .earn-tbl thead th { padding: 3px 10px; }
  .ivsurf tbody td, .ivsurf thead th, .ivsurf tbody th { padding: 4px 6px; }

  /* ── Row-based widgets (cross-page) ── */
  .altmov__row, .megacap__row, .single-movers__row,
  .lst__row, .feerev__row, .dexvol__row,
  .stab-sup__row, .exnet__row, .holders__row, .whales__row,
  .simple__row, .movers__row, .hdl__row, .plats__row,
  .sessions__row, .maxpain__row, .blocks__row, .topoi__row,
  .wire__row, .desk-feed__row, .revs__row, .agents__row,
  .prompts__row, .ctx-pool__row, .threads__row,
  .radar__row { padding: 4px 12px; }

  .chain-tvl { gap: 3px; }
  .chain-tvl__row { font-size: 11.5px; }
  .stable-row { padding: 2px 0; }
  .oibars { gap: 5px; }

  /* ── Grid-cell widgets ── */
  .idx-cell, .breadth__cell, .comgrid__cell, .mkey__cell,
  .gex-cell { padding: 6px 9px; }
  .idx-cell__val { font-size: 14px; }
  .mkey__val { font-size: 16px; line-height: 1.15; }
  .breadth__val, .gex-cell__v { font-size: 14px; }

  /* v56 — mkey grid floor at ≤800h too (was only ≤730h before).
   * Without this, 731-800h viewports get default `height: 100%` collapse
   * because Macro Row 4 vital-signs strip steals height as `auto`.
   * 38px × 2 rows + gap = 78px body fits within Macro Row 1 ~110px (chrome 30). */
  .mkey { gap: 4px; grid-template-rows: minmax(38px, 1fr) minmax(38px, 1fr) !important; grid-auto-rows: minmax(38px, auto) !important; }
  .mkey__lbl { min-height: 11px; }
  .mkey__sub { min-height: 11px; }
  .main--macro {
    grid-template-rows:
      minmax(110px, 1.18fr)
      minmax(0, 1.5fr)
      minmax(0, 1.5fr)
      minmax(60px, auto);
  }

  .onchain { gap: 2px; }
  .onchain__cell { padding: 3px 8px; }
  .onchain__val { font-size: 12px; }

  /* ── Depth ladder (CRYPTO) ── */
  .depth__row { padding: 2px 12px; }
  .depth__mid { padding: 4px 12px; }

  /* ── News hero ── */
  .news-hero { gap: 6px; }
  .news-hero__head { font-size: 22px; line-height: 1.15; }
  .news-hero__dek { font-size: 12px; min-height: 32px; -webkit-line-clamp: 2; }

  /* ── TVL / Vol hero (DEFI / OPTIONS) ── */
  .tvl-hero { gap: 5px; }
  .tvl-hero__sym { font-size: 32px; }
  .tvl-hero__rule { margin: 2px 0; }
  .vol-hero { gap: 5px; }
  .vol-hero__big { font-size: 28px; }

  /* ── PREDICT timeline ── */
  .tline { gap: 4px; }
  .tline__big { font-size: 28px; }

  /* ── AI page hero shrink ──
   * AI.jsx:12 sets inline padding `20px 24px` on .panel--gold .panel__body.
   * At ≤800h that 40px of vertical padding + 101px ai-hero__l content > 111px
   * panel body height → 10px overflow. Override the inline padding with
   * !important + shrink the field to compress hero content. */
  .panel--gold > .panel__body { padding: 10px 18px !important; }
  .ai-hero__head { font-size: 26px; }
  .ai-hero__dek { font-size: 12.5px; }
  .ai-hero__field { padding: 8px 12px; }
  .ai-hero__brand { margin-bottom: 8px; }
  .ai-tele__cell { padding: 3px 10px; }
  .ai-tele__val { font-size: 13.5px; }
  .caps__card { padding: 10px 12px; gap: 5px; }
  .caps__title { font-size: 15px; }
  .caps__desc { font-size: 11px; line-height: 1.42; }
}

/* v53 — threshold widened 700 → 730 because user's actual Chrome viewport
 * oscillates 637-701px depending on Chrome chrome state (URL bar, bookmarks
 * bar visibility, devtools). v52 mkey fix was firing at 637 (≤700 match) but
 * NOT at 701 (just above), so Macro values were invisible at 701 even after
 * cache-bust deploy. Use same 730 cap as the brief2/hero-strip block above. */
@media (min-width: 900px) and (max-height: 730px) {
  /* ── Panel header + AI-synth-line: aggressive shave ── */
  .panel__h { padding: 3px 12px; min-height: 26px; }
  .panel__title { font-size: 10.5px; }
  .panel__sub { font-size: 9.5px; }
  .ai-synth-line { padding: 2px 12px 3px; font-size: 10.5px; }

  /* ── Brief2 panel — further compression at the very narrow heights that
   * Chrome's chrome (URL bar + tabs + bookmarks) on Windows HiDPI creates.
   * Verified on prod at user's actual viewport 1280×631 (resize_window 1422×701
   * minus 142px×70px chrome overhead): brief2 panel body is only ~88px and
   * watch was overflowing by 8-9px. Hide brief2__watch-h (header label
   * "WATCH TODAY") and reduce paras flex to 24, gap to 2 so watch list itself
   * still fits as a 2-3 line ticker preview at the panel bottom. */
  .brief2 { padding: 4px 12px; gap: 2px; }
  .brief2__headline { font-size: 14px; line-height: 1.18; padding-bottom: 1px; }
  .brief2__paras { flex: 1 1 24px !important; min-height: 24px !important; font-size: 11px; line-height: 1.4; gap: 3px; }
  .brief2__watch { padding-top: 1px; gap: 2px 10px; }
  .brief2__watch-h { display: none; }
  .brief2__watch-row { font-size: 9.5px; padding: 0; }

  /* ── Tables ── */
  .ftable td, .ftable th { padding: 3px 5px; }
  .ftable { font-size: 11px; }
  .ftable th { font-size: 9px; }
  .defi-tbl tbody td, .defi-tbl thead th { padding: 2px 9px; }
  .defi-tbl { font-size: 11px; }
  .yield-tbl td { padding: 2px 9px; }
  .borrow-tbl td { padding: 2px 9px; }
  .ytable td { padding: 3px 4px; font-size: 10.5px; }
  .ytable th { padding: 2px 4px; }
  .fxtable__row { padding: 3px 12px; font-size: 10.5px; }
  .cbrates__row { padding: 3px 12px; }
  .etf-tbl tbody td, .etf-tbl thead th { padding: 3.5px 12px; font-size: 11px; }
  .options-tbl td, .options-tbl th,
  .chain tbody td, .chain thead th { padding: 3px 6px; font-size: 10px; }
  .econ-tbl tbody td, .econ-tbl thead th { padding: 2px 10px; }
  .earn-tbl tbody td, .earn-tbl thead th { padding: 2px 10px; }
  .ivsurf tbody td, .ivsurf thead th, .ivsurf tbody th { padding: 3px 6px; font-size: 10.5px; }

  /* ── Row-based widgets ── */
  .altmov__row, .megacap__row, .single-movers__row,
  .lst__row, .feerev__row, .dexvol__row,
  .stab-sup__row, .exnet__row, .holders__row, .whales__row,
  .simple__row, .movers__row, .hdl__row, .plats__row,
  .sessions__row, .maxpain__row, .blocks__row, .topoi__row,
  .wire__row, .desk-feed__row, .revs__row, .agents__row,
  .prompts__row, .ctx-pool__row, .threads__row,
  .radar__row { padding: 2px 12px; }

  .altmov__rk { font-size: 9.5px; }
  .megacap__nm, .single-movers__rk { font-size: 9.5px; }
  .chain-tvl { gap: 2px; }
  .chain-tvl__row { font-size: 11px; }
  .stable-row { padding: 1px 0; }
  .stable-row__sym { font-size: 11px; }
  .oibars { gap: 4px; font-size: 10.5px; }

  /* ── Grid-cell widgets ── */
  .idx-cell, .breadth__cell, .comgrid__cell, .mkey__cell,
  .gex-cell { padding: 4px 8px; gap: 1px; }
  .idx-cell__val { font-size: 13px; }
  .mkey__val { font-size: 14px; line-height: 1.1; }
  .breadth__val, .gex-cell__v { font-size: 13px; }
  .idx-cell__ytd { padding-top: 1px; margin-top: 1px; font-size: 9px; }

  /* v48/v49/v51 — Macro mkey grid rows floor lifted 28→33 to give cells enough
   * room for lbl (10) + val (14, line-height 1.1) + 4px×2 padding = 32 content
   * (cell needs 33px to fully render val without 8px clip residual seen in v50).
   * Combined with main--macro Row 1 minmax(118) below.
   * v49: hide .mkey__sub (chg secondary line) — primary lbl + val read fine. */
  .mkey { gap: 3px; grid-auto-rows: minmax(33px, auto) !important; grid-template-rows: minmax(33px, 1fr) minmax(33px, 1fr) !important; }
  .mkey__cell { padding: 3px 8px; }
  .mkey__lbl { font-size: 8.5px; min-height: 10px; }
  .mkey__sub { display: none; }
  .mkey__val { font-size: 13px; line-height: 1.1; }

  .onchain { gap: 1px; }
  .onchain__cell { padding: 2px 7px; }
  .onchain__lbl { font-size: 8px; }
  .onchain__val { font-size: 11px; }
  .onchain__sub { font-size: 9px; }

  /* ── Depth ladder ── */
  .depth__row { padding: 1.5px 12px; font-size: 10.5px; }
  .depth__mid { padding: 3px 12px; font-size: 9.5px; }
  .depth__mark { font-size: 12px; }

  /* ── News hero ──
   * v48/v49: relax dek min-height (28 → 0) + hide impact chips at narrow h.
   * Header + 1-line dek + 1-line flag/tag/src + minimum padding fits ~70px. */
  .news-hero { gap: 2px; }
  .news-hero__head { font-size: 16px; line-height: 1.1; }
  .news-hero__dek { font-size: 10.5px; min-height: 0; -webkit-line-clamp: 1; line-height: 1.25; }
  .news-hero__impact { display: none; }
  .news-hero__chip { padding: 1px 6px; font-size: 9px; }
  .news-hero__flag, .news-hero__tag, .news-hero__src { font-size: 9px; }

  /* ── Options 25Δ Risk Reversal skew chart ──
   * v48/v49: 6 strikes × 14px = 84px content vs 66px body. v48 track 14→11
   * still left 7px overflow. v49: 9px track + hide hd (axis is self-evident)
   * → 6 × 9 + 5 gaps × 2 = 64px. */
  .skew { gap: 2px; }
  .skew__hd { display: none; }
  .skew__track { height: 9px; }
  .skew__v { line-height: 9px; font-size: 8.5px; }
  .skew__ten { font-size: 9px; }
  .skew__foot { display: none; }

  /* ── TVL / Vol hero ── */
  .tvl-hero { gap: 4px; }
  .tvl-hero__sym { font-size: 26px; }
  .tvl-hero__rule { margin: 1px 0; }
  .tvl-hero__grid { gap: 3px 14px; }
  .tvl-hero__grid > div { font-size: 11px; }
  .vol-hero { gap: 4px; }
  .vol-hero__big { font-size: 24px; }
  .vol-hero__grid { gap: 3px 14px; }
  .vol-hero__grid > div { font-size: 10.5px; }

  /* ── PREDICT timeline ── */
  .tline { gap: 3px; }
  .tline__big { font-size: 24px; }
  .tline__chart { min-height: 50px; }

  /* ── AI page ── */
  .ai-hero { gap: 18px; }
  .ai-hero__head { font-size: 21px; line-height: 1.12; margin: 0 0 6px; }
  .ai-hero__dek { font-size: 11.5px; line-height: 1.45; }
  .ai-hero__field { padding: 8px 12px; }
  .ai-hero__field input { font-size: 12.5px; }
  .ai-tele__cell { padding: 2px 9px; }
  .ai-tele__lbl { font-size: 8.5px; }
  .ai-tele__val { font-size: 12px; }
  .caps { gap: 8px; }
  .caps__card { padding: 8px 10px; gap: 4px; }
  .caps__title { font-size: 13.5px; }
  .caps__desc { font-size: 10.5px; line-height: 1.38; }
}

/* ── 5-row pages (FLOWS / OPTIONS / PREDICT) ────────────────────────────
 * These pages cram 5 grid rows into the same vertical space the default
 * 4-row .main consumes. At narrow heights, each row is ~20% shorter than
 * default — so the page-specific grid templates need to drop the 70-86px
 * `auto` footer track to a smaller minimum, freeing height for content rows. */
@media (min-width: 900px) and (max-height: 800px) {
  .main--flows {
    grid-template-rows:
      minmax(0, 1fr)
      minmax(0, 1fr)
      minmax(0, 1fr)
      minmax(0, 1fr)
      minmax(56px, auto);
  }
  .main--options {
    grid-template-rows:
      minmax(0, 0.95fr)
      minmax(0, 1.05fr)
      minmax(0, 1.05fr)
      minmax(0, 0.95fr)
      minmax(56px, auto);
  }
  .main--predict {
    grid-template-rows:
      minmax(0, 1fr)
      minmax(0, 1fr)
      minmax(0, 1fr)
      minmax(0, 1fr)
      minmax(72px, 80px) !important;
  }
}
/* v53 — same 730 widening: page-level grid template overrides for narrow heights.
 * Was 700 → user's actual 701h viewport missed these critical Row 1 rescues
 * (Macro/News/Options), causing values invisible / chart clip persistence. */
@media (min-width: 900px) and (max-height: 730px) {
  .main--flows {
    grid-template-rows:
      minmax(0, 1fr)
      minmax(0, 1fr)
      minmax(0, 1fr)
      minmax(0, 1fr)
      minmax(48px, auto);
  }
  .main--options {
    /* v50 — Row 1 forced to minmax(108px, 0.95fr) so the 4-card hero strip
     * (BTC Vol / ETH Vol / Term / Risk Reversal) gets enough body height for
     * the 6-strike skew chart to fit. Was 75px → 7px hidden clip. */
    grid-template-rows:
      minmax(108px, 0.95fr)
      minmax(0, 1.05fr)
      minmax(0, 1.05fr)
      minmax(0, 0.95fr)
      minmax(48px, auto);
  }
  .main--predict {
    grid-template-rows:
      minmax(0, 1fr)
      minmax(0, 1fr)
      minmax(0, 1fr)
      minmax(0, 1fr)
      minmax(60px, 70px) !important;
  }
  /* v50 — News Row 1 budget rescue. Same pattern as Macro/Options:
   * Default 1.18fr Row 1 at ≤700h gives col-8 Top Story panel only ~72px,
   * body 28px after panel header eats most. Top Story hero (head + dek +
   * source line) needs ~67px → 11px hidden. Force minmax(108px) so body
   * grows to ~64px, plenty for compressed hero. */
  .main--news {
    grid-template-rows:
      minmax(108px, 1.18fr)
      minmax(0, 1.4fr)
      minmax(0, 1.4fr)
      minmax(60px, auto);
  }

  /* v48 — Macro Row 1 budget rescue.
   * Default `.main grid-template-rows` gives Row 1 1.18fr ÷ (1.18+1.55+1.55+auto)
   * which at ≤700h shrinks to ~84px because Row 4 "auto" claims 130-150px for
   * its vital-signs strip. Result: Macro Row 1 col-8 panel only 84px → mkey
   * 8 cells × 2 rows × 14.5px → values invisible (cell 9-13px, content needs 50-71px).
   * Cap Row 4 to 100px so Row 1 grows to ~108px → mkey rows ~28px each → values visible. */
  .main--macro {
    /* v51: Row 1 bumped 108→118px to accommodate mkey 2×33px + gap 3 + chrome
     * 40 + body padding 16 = 109 min. Adds 10px buffer for ai-synth line. */
    grid-template-rows:
      minmax(118px, 1.18fr)
      minmax(0, 1.35fr)
      minmax(0, 1.35fr)
      minmax(60px, 100px);
  }
}

/* ── Narrow-height row caps (v46) — truncate, don't inner-scroll ─────────
 * At user's actual 1422×639 viewport (Windows HiDPI 4K + Chrome chrome eating
 * ~80px vertical), Cockpit panels with 8-row tape / 5-row movers / 3-card
 * predict were showing only 1-2 items + inner scroll. Doctrine §4.5.3 says
 * narrow viewports should DROP content, not scroll. Hide-N-and-beyond is
 * less janky than letting the panel inner-scroll. Display-none preserves
 * data in DOM (cmd menu still finds entities) but skips visual render.
 *
 * Triggers at ≤700h (4K @ 250-300% Windows scaling). At ≥730h the full
 * row count shows.
 *
 * Same pattern as the existing mobile cap at L708-711 in this file. */
@media (min-width: 900px) and (max-height: 730px) {
  /* Cockpit Row 2 PinnedMovers (col-5) — cap to 3 of 5 cards + per-card compress.
   * v47: at 135px row budget - 38px chrome = 97px body, 3 cards × 32px each
   * needed. Compress padding + clamp headline to 1 line + hide detail row.
   * Detail is redundant with headline narrative in this tight viewport. */
  .movers2 .movers2__card:nth-child(n+4) { display: none !important; }
  .movers2__card { padding: 3px 12px; }
  .movers2__headline { font-size: 11px; line-height: 1.25; -webkit-line-clamp: 1; }
  .movers2__sub { font-size: 9px; }
  .movers2__detail { display: none; }
  .movers2__tag { font-size: 9px; padding: 1px 5px; }

  /* Cockpit Row 3 UnifiedTape (col-5) — cap to 4 of 8 rows + per-row compress.
   * v47: v46 cap=5 still overflowed because default .tape2__row 21px × 5 =
   * 105px > 97px body. Reduce to 4 rows × 18px = 72px (safe in 97px). */
  .tape2 .tape2__row:nth-child(n+5) { display: none !important; }
  .tape2__row { padding: 3px 12px; font-size: 11px; line-height: 1.3; }
  .tape2__t, .tape2__src, .tape2__kind { font-size: 9.5px; }

  /* Cockpit Row 3 TopPredict (col-3) — cap to 2 of 3 markets (v46 unchanged) */
  .pred2 .pred2__card:nth-child(n+3),
  .pred2 > a:nth-child(n+3) { display: none !important; }

  /* Cockpit Row 3 TodaysCalendar (col-4) — cap to 4 of 6 events + compress.
   * v47 CRITICAL FIX: changed nth-of-type → nth-child. cal2__body contains
   * mixed <div> + <a> children (CockpitWidgets switches on rowUrl). nth-of-type
   * counts per-tag-type so it never reached the 5th div, leaving all 6 rows
   * visible. nth-child counts all element siblings so it correctly hides 5+. */
  .cal2 .cal2__row:nth-child(n+5) { display: none !important; }
  .cal2__row { padding: 4px 12px; font-size: 11px; }
  .cal2__t, .cal2__cat, .cal2__imp { font-size: 9.5px; }
}

/* Mobile: convert to horizontal scroll-snap carousel + indicator dots */
@media (max-width: 899px), (hover: none) and (pointer: coarse) {
  .hero-strip {
    display: flex !important;
    grid-template-columns: none !important;
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    -ms-overflow-style: none;
    padding: 0 6vw 4px;        /* lets first/last card center via snap */
    gap: 6px;
  }
  .hero-strip::-webkit-scrollbar { display: none; }
  .hero-strip__card {
    flex: 0 0 88vw;
    max-width: 88vw;
    scroll-snap-align: center;
    height: 160px;             /* compact mobile hero — 970px → 160px total */
  }
  .hero-strip__card .quote { height: 100%; min-height: 0; }
}

/* ─── Mobile — single column, iOS HIG typography, swipe-able hero ─────── */
@media (max-width: 899px), (hover: none) and (pointer: coarse) {
  html, body { -webkit-text-size-adjust: 100%; touch-action: manipulation; }

  /* iOS HIG mainstream typography (body ≥14px, caption ≥11px) */
  :root {
    --type-hero: 22px;
    --type-num: 15px;
    --type-ui: 14px;
    --type-lbl: 12px;
  }
  .panel__title { font-size: 12.5px !important; }
  .panel__sub { font-size: 11.5px !important; }
  .panel__h-r, .panel__h-r * { font-size: 11.5px !important; }
  .row, .row__body, .tape2__body, .cal2__ev, .brief2__paras p,
  .stance__thesis, .hdl__q, .movers2__detail { font-size: 14px !important; line-height: 1.5 !important; }
  .quote__sym { font-size: 18px !important; }
  .quote__price { font-size: 30px !important; }
  .quote__name { font-size: 11px !important; letter-spacing: 0.15em !important; }
  .quote__cat { font-size: 9.5px !important; letter-spacing: 0.18em !important; padding: 1px 6px !important; }
  .quote__chg, .quote__chg-abs { font-size: 11px !important; }
  .ticker, .ticker-tape, .ticker__row { font-size: 13px !important; }
  /* v34.12: mobile ticker tape scroll speed (was 35s, before that 90s).
   * On mobile narrow viewport only 1-2 items visible at a time. 35s still
   * felt slow — bumped to 16s. Items roll through ~5.6× faster than the
   * desktop original, keeping the "live wire" feel readable on a thumb-sized
   * viewport. Doctrine §3.4: still data-driven (real prices), not idle deco. */
  .ticker__track { animation-duration: 16s !important; }

  /* Grid — single column except hero (carousel).
   * Critical: override desktop grid-template-rows to auto so each panel can grow
   * to fit its content. Without this, mobile inherits the desktop's 4 fr-sized rows
   * and panels 1-4 get crushed (brief2__paras collapsed to 0 — observed at 375px). */
  .main {
    grid-template-columns: 1fr !important;
    grid-template-rows: none !important;
    grid-auto-rows: minmax(0, auto) !important;
    gap: 8px !important;
    padding: 8px !important;
  }
  .col-1, .col-2, .col-3, .col-4, .col-5, .col-6,
  .col-7, .col-8, .col-9, .col-10, .col-11, .col-12 {
    grid-column: 1 / -1 !important;
  }

  /* Hero strip — wrap into horizontal swipe carousel.
   * The hero is 4 × .col-3 wrapping a .quote. On mobile we make each col-3
   * snap-align inside a flex row inside an implicit carousel via :nth-child(1).parent.
   * Simpler: render all 4 col-3 hero in a flex row, container scrolls horizontally. */
  .main > .col-3 {
    flex: 0 0 88vw !important;
    scroll-snap-align: center;
  }
  .main {
    /* When hero exists, change main to flex-wrap so hero rows scroll horizontally.
     * Use a wrapping technique: hero cards have data-screen-label nope — they're
     * just .col-3 children. We use grid-row 1 / span 1 on hero, scroll inside.
     * Easier: target hero specifically via :has() on the .quote child. */
  }
  /* Hero carousel container — wraps the 4 hero col-3 in a horizontal scroll row */
  .main.main--has-hero-carousel { /* set by JS or by always-on @media */ }

  /* Touch targets per iOS HIG */
  .chip, .row__action, button, .tb-burger, .tb-dapp--mobile,
  .tb-drawer__nav-btn, .cmdline__input {
    min-height: 44px;
    min-width: 44px;
  }
  /* Mobile: hide inline nav strip entirely — navigation lives in the burger drawer */
  .topbar__nav { display: none !important; }

  /* ─── Mobile topbar — clean iOS pattern: logo · HH:MM UTC · ● ☰ ─── */
  /* Hide everything that belongs in the drawer */
  .tb-lang, .tb-tray, .tb-dapp--desktop { display: none !important; }
  .tb-dapp--mobile { display: none !important; }                   /* DAPP moves to drawer */
  .session-pill { display: none !important; }                      /* Session moves to drawer */
  .topbar__product { display: none !important; }                   /* "TERMINAL" static text */
  .topbar__sep { display: none !important; }                       /* "·" separator after logo */
  .topbar__brand .ver { display: none !important; }                /* "v2.11.0" — desktop debug only */
  .topbar__clock .sub { display: none !important; }                /* hide date sub-line */
  .topbar__clock .time__ss { display: none !important; }           /* hide ":SS" — minute precision enough */
  .live-dot__lbl { display: none !important; }                     /* hide "LIVE" text, keep pulse dot */

  /* Topbar container — clean iOS spacing + sticky pin (v34.3).
   * Sticky behavior chosen over fixed: keeps document flow intact, plays nice
   * with iOS Safari URL-bar collapse, and inherits safe-area handling from
   * the browser chrome. backdrop-filter (already on .topbar in terminal.css)
   * gives the floating-over-content look automatically. */
  .topbar {
    display: flex !important;
    align-items: center;
    justify-content: space-between;
    padding: 0 14px !important;
    min-height: 48px !important;
    gap: 0;
    position: sticky !important;
    top: 0 !important;
    z-index: 100 !important;
  }
  .topbar__brand { gap: 8px; padding: 0; cursor: pointer; }
  /* v34.3 logo fix: preserve natural aspect ratio (4493×1478 wordmark = ratio 3.04).
   * Previously width:28 + height:28 squashed it into a square. */
  .topbar__logo { width: auto; height: 22px; object-fit: contain; }
  .topbar__right { display: flex; align-items: center; gap: 14px; }

  /* Clock: just HH:MM UTC, gold, tabular */
  .topbar__clock {
    display: inline-flex; align-items: baseline; gap: 0;
    font-family: var(--font-mono);
  }
  .topbar__clock .time {
    font-size: 14px !important;
    letter-spacing: 0.05em;
    color: var(--gold-bright, #e6c98f);
    font-variant-numeric: tabular-nums;
    display: inline-flex; align-items: baseline; gap: 0;
  }
  .topbar__clock .time__zone {
    font-size: 9.5px;
    letter-spacing: 0.18em;
    color: var(--txt-3, rgba(255,255,255,0.5));
    margin-left: 5px;
    font-weight: 500;
  }

  /* Live indicator: pulse-only, 44px touch target */
  .live-dot {
    display: inline-flex; align-items: center; justify-content: center;
    width: 28px; height: 28px;
    padding: 0; gap: 0;
    background: none; border: none;
  }
  .live-dot .pulse {
    width: 9px; height: 9px;
    background: var(--up, #34D399);
  }

  /* Hamburger — bigger touch target + clearer border */
  .tb-burger {
    display: inline-flex !important;
    width: 40px !important; height: 40px !important;
    border-color: rgba(201,168,106,0.40) !important;
  }

  /* Command line — pin to bottom */
  .cmdline {
    position: sticky;
    bottom: 0;
    z-index: 50;
    border-top: 1px solid var(--bd, rgba(201,168,106,0.18));
    background: var(--bg, #0a0a0a);
  }
  .cmdline__input { font-size: 14px !important; }   /* iOS HIG body min */

  /* AI sheet — full screen */
  .ai-sheet, .aisheet, [class*="ai-sheet"] {
    width: 100vw !important;
    max-width: 100vw !important;
    left: 0 !important;
    right: 0 !important;
  }
  /* Pinned strip — horizontal scroll */
  .pinned-strip, .pinned__strip { overflow-x: auto; scrollbar-width: none; }
  .pinned-strip::-webkit-scrollbar { display: none; }
  .help-overlay button, .help__close { min-height: 44px; min-width: 44px; }

  /* Hero quote details — keep compact, never wrap */
  .quote__chg-row { flex-wrap: wrap; gap: 8px !important; }
  .quote__chg-abs { font-size: 10.5px !important; }
  .quote__tps { font-size: 9px !important; }
  .quote__spread { font-size: 9px !important; padding-top: 4px !important; }

  /* Panel header chrome shrink */
  .panel__h { padding: 5px 10px !important; min-height: 30px !important; }
  .panel__body { padding: 8px 10px !important; }

  /* v34.5 mobile thermal-load fix (iPhone 16 PM heat report).
   * Kill *purely decorative* idle animations that have no data-driver.
   * All data-driven animations (TickCell flash, PanelHeartbeat pip, entityPulse,
   * panel--tick, tapeSlideIn, thresholdFlash, pulse-ring on .dot--live) are
   * preserved verbatim — the "live data" feel is unchanged.
   * Goal: ~30% GPU/CPU reduction per second of idle browsing. */

  /* a) Reduce topbar backdrop-filter blur radius (GPU cost ∝ blur²).
   *    20px → 8px keeps the floating-glass look at ~16% the cost. */
  .topbar {
    backdrop-filter: blur(8px) !important;
    -webkit-backdrop-filter: blur(8px) !important;
  }

  /* b) Hero quote price — kill idle opacity breath (gmgPriceBreath 4s ∞).
   *    Real price changes still trigger TickCell flash; the breathing was
   *    pure "alive" decoration without informational content. */
  .quote__price { animation: none !important; }

  /* c) Stance 4-card scanning light (.gmg-scanning 8s ∞) — Cockpit only,
   *    pure decoration, no data driver. */
  .gmg-scanning,
  .gmg-scanning::before,
  .gmg-scanning::after { animation: none !important; }

  /* d) Long-list roving spotlight (gmgRovingHL random 2.4-3.5s) — decoration. */
  .tape2__row--spotlight,
  .cal2__row--spotlight,
  .desk__row--spotlight,
  .wire__row--spotlight,
  [class*="--spotlight"] { animation: none !important; background: transparent !important; }

  /* e) Slow down (don't kill) brand/icon idle breaths — keep the "alive" hint
   *    but at half cadence so GPU repaints fewer times per minute. */
  .brief2__icon { animation-duration: 6s !important; }
  .tb-dapp::after { display: none !important; }   /* gold shimmer 4s loop */
  .cat, .kind, .tag, .badge,
  .session-pill,
  .ticker-tape__src,
  .stance__conv { animation-duration: 7s !important; }

  /* f) Replace box-shadow-driven heartbeat with opacity flash (GPU-composited).
   *    box-shadow keyframes force a CPU repaint each frame; opacity is
   *    on the compositor thread. Visually near-identical. */
  .panel-hb__pip {
    animation-duration: 3s !important;
  }

  /* g) Page Visibility pause: when html.gmg-paused is set by JS, halt all
   *    CSS animations. Saves 100% of animation cost while tab is backgrounded
   *    or screen is locked. Resumes instantly on visibilitychange. */
}
html.gmg-paused *,
html.gmg-paused *::before,
html.gmg-paused *::after {
  animation-play-state: paused !important;
}

@media (max-width: 899px), (hover: none) and (pointer: coarse) {
  /* v34.7 iOS scroll-trap fix — BROAD remediation (replaces v34.4 narrow list).
   *
   * Root cause (v34.4 attempt incomplete): I guessed inner widget class names
   * (.yields-tbl-wrap, .chain-tvl-wrap, etc.) but actual DEFI widget classes
   * are .yield-tbl, .defi-tbl, .borrow-tbl, .stable-list, .chain-tvl__row, etc.
   * iOS Safari traps vertical pan when ANY ancestor under finger has an
   * inner scroll context with horizontal overflow (common with tables).
   *
   * Broad fix: every descendant inside main on mobile renounces its scroll
   * context. Single column at 375px doesn't need any inner scroll anyway.
   * `* { overflow: visible }` is intentionally nuclear. */
  /* v34.7 → revised v34.10: scope-narrowed nuclear.
   * Only zero out overflow-Y on descendants (kills scroll-trap).
   * Overflow-X stays per-element default — main keeps hidden (no page-wide
   * horizontal scroll), hero-strip keeps auto (swipe carousel needs it). */
  main *,
  .panel__body, .panel__body--scroll {
    overflow-y: visible !important;
    max-height: none !important;
  }
  /* main: clip horizontally so widget content can't push the page wider than
   * the viewport. This was the v34.10 bug: Brief headline + inline code spans
   * extended past viewport because v34.7 nuked overflow-x globally. */
  main {
    overflow-x: hidden !important;
    overflow-y: visible !important;
    max-width: 100vw !important;
  }
  /* Hero strip MUST keep horizontal swipe — explicit re-grant. */
  .hero-strip {
    overflow-x: auto !important;
    overflow-y: hidden !important;
  }
  /* Inline code in Brief (and anywhere else) must wrap so backtick spans
   * like `7,501` don't push the line off-screen on narrow viewports. */
  main code, main .brief2__paras code, main .brief2__headline code {
    white-space: normal !important;
    word-break: break-word !important;
    overflow-wrap: anywhere !important;
  }
  /* Defense in depth: every panel + immediate widget root caps at 100% width. */
  main section, main section *,
  main .col-12, main .col-7, main .col-5, main .col-4, main .col-3 {
    max-width: 100% !important;
    min-width: 0 !important;
  }
  /* Exceptions: tiny decorative bars (chain-tvl__bar, dexvol__bar, etc.) MUST
   * keep overflow: hidden to clip their fill — these are <6px tall, can't
   * possibly be scroll-trap candidates. Re-grant them. */
  .chain-tvl__bar, .dexvol__bar, .lst__bar, .util-bar, .feerev__bar,
  .bridges__track, .exnet__track, .oibars__bar-wrap,
  [class*="__bar"], [class*="__track"], [class*="__fill"] {
    overflow: hidden !important;
  }
  /* Tables: prevent intrinsic horizontal overflow by allowing word-wrap. */
  main table { table-layout: fixed; width: 100% !important; }
  main td, main th { word-break: break-word; overflow-wrap: anywhere; }

  /* iOS rubber-band: revert v34.5 overscroll-behavior so iOS can scroll
   * smoothly with native momentum. `contain` was suppressing the natural
   * scroll handoff. Drop touch-action too — let iOS default handle. */
  html, body {
    touch-action: auto !important;
    overscroll-behavior: auto !important;
  }

  /* Sticky topbar backdrop-filter is a known iOS Safari scroll-jank source.
   * Replace blur with semi-opaque solid bg — visually similar, GPU-free. */
  .topbar {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
    background: rgba(10, 9, 7, 0.97) !important;
  }

  /* v34.6 Mobile Cockpit density optimization (no data sacrifice).
   * Diagnostic audit found 3 sources of "chaotic" feeling at 375px:
   *   1. Unified Tape — 11 mixed-kind rows = wall of text
   *   2. Stance — 4 similar gold-bordered cards stacked vertically = repetition
   *   3. Calendar — 6 rows = long after a 390px Brief panel
   * Fix: cap tape & calendar rows, switch Stance to 2x2 grid.
   * All data still in DOM (just visually clipped) — no information loss. */

  /* Tape: cap to 6 newest rows */
  .tape2 .tape2__row:nth-child(n+7) { display: none !important; }
  /* Calendar: cap to 4 rows (use nth-of-type to ignore non-row sibling elements) */
  .cal2__row:nth-of-type(n+5),
  .cal2 > .cal2__row:nth-child(n+5) { display: none !important; }
  /* Stance: 4 cards → 2×2 grid (much better visual rhythm than vertical stack) */
  .stance,
  .stance__grid {
    display: grid !important;
    grid-template-columns: 1fr 1fr !important;
    gap: 8px !important;
  }
  .stance__card { padding: 8px 10px !important; }

  /* v34.6 CRITICAL: Brief2 mobile layout fix — fixes text-overlap bug.
   * Root cause: .brief2 has `height: 100%; overflow: hidden` (desktop expects
   * fixed panel height). On mobile panels grow with content (auto rows), so
   * brief2's 100% height becomes ambiguous. Combined with .brief2__paras
   * `flex: 1 1 64px` and .brief2__watch `margin-top: auto`, the paragraphs
   * physically OVERLAP the watch row when narrative exceeds 64px.
   *
   * Mobile fix: brief2 grows to content, paras stops flex-growing, watch
   * uses explicit gap instead of margin-top: auto. Natural top-to-bottom flow. */
  .brief2 {
    height: auto !important;
    overflow: visible !important;
  }
  .brief2__paras {
    flex: 0 0 auto !important;
    min-height: 0 !important;
    overflow: visible !important;
    max-height: none !important;
  }
  .brief2__watch {
    margin-top: 10px !important;
  }

  /* v34.3 — Market Brief mobile typography fix.
   * At 375px, the 22px serif headline + 12.5px inline code blocks made the
   * brief panel feel cramped: headline forced to 3 lines, inline code blocks
   * with backgrounds broke prose visual flow. Soften both. */
  .brief2__headline {
    font-size: 17px !important;
    line-height: 1.3 !important;
    padding-bottom: 6px !important;
  }
  .brief2__paras {
    font-size: 14px !important;
    line-height: 1.55 !important;
  }
  .brief2__paras code,
  .brief2__headline code {
    /* Match surrounding text size + tone down chip styling so numbers flow with prose */
    font-size: 0.92em !important;
    padding: 0 3px !important;
    border-width: 0 !important;
    background: rgba(201,165,92,0.06) !important;
  }
  .brief2__watch {
    flex-wrap: wrap;
    gap: 6px 12px !important;
  }
  .brief2__watch-row {
    font-size: 12px !important;
  }
}

/* ─── Hover-dependent UI off on touch ─────────────────────────────────── */
@media (hover: none) {
  .hover-spark, .hover-tooltip, .ctx-menu { display: none !important; }
}

/* ─── Wall tiers — heatmap & ticker density drop per doctrine §4.5.3 ──── */
@media (min-width: 2400px) {
  .heatmap-grid {
    grid-template-columns: repeat(6, 1fr) !important;
  }
  .heatmap-cell:nth-child(n+31) { display: none; }
  .ticker, .ticker-tape, .ticker__row { font-size: 18px !important; }
}

@media (min-width: 3600px) {
  .heatmap-grid {
    grid-template-columns: repeat(5, 1fr) !important;
  }
  .heatmap-cell:nth-child(n+21) { display: none; }
  .ticker, .ticker-tape, .ticker__row { font-size: 22px !important; }
}

/* ─── Hero tick direction arrow + bid/ask spread strip + TPS indicator ── */
.quote__price { position: relative; display: flex; align-items: baseline; gap: 8px; }
@keyframes gmgTickArrowFade {
  0%   { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(-2px); }
}
.quote__tick-arrow {
  font-size: 14px;
  line-height: 1;
  animation: gmgTickArrowFade 700ms ease-out forwards;
  font-family: var(--font-mono);
}
.quote__tick-arrow--up { color: var(--up, #34D399); }
.quote__tick-arrow--dn { color: var(--dn, #F87171); }

.quote__tps {
  font-size: 9px;
  letter-spacing: 0.10em;
  color: var(--gold, #C9A86A);
  text-transform: uppercase;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-left: auto;
}
.quote__tps .dot--live { width: 4px; height: 4px; }

.quote__spread {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 9.5px;
  letter-spacing: 0.04em;
  color: var(--txt-3, rgba(255,255,255,0.5));
  padding-top: 2px;
  border-top: 1px solid rgba(201,168,106,0.10);
  margin-top: 2px;
  font-family: var(--font-mono);
}
.quote__spread-bid { color: rgba(248,113,113,0.85); }   /* bid = where sellers hit */
.quote__spread-ask { color: rgba(52,211,153,0.85); }    /* ask = where buyers lift */
.quote__spread-gap { opacity: 0.4; }
.quote__spread-bps { margin-left: auto; opacity: 0.55; }

/* ─── Cockpit panel layout sizing ──────────────────────────────────── */
/* Brief panel ITSELF stays static — only .brief2__paras scrolls. The previous
 * overflow-y:auto here made the entire brief panel a scroll container which,
 * combined with constrained grid row heights, collapsed brief2__paras to 0px
 * because the brief expanded to its intrinsic content and overflow gated visibility. */
.brief2 {
  flex: 1 1 auto;
  min-height: 0;
  /* No overflow: paras has its own scroll. */
}
/* Top + bottom fade mask on all scrollable panel bodies — Bloomberg-style */
.panel__body--scroll {
  -webkit-mask-image: linear-gradient(to bottom, black 0, black calc(100% - 12px), transparent 100%);
          mask-image: linear-gradient(to bottom, black 0, black calc(100% - 12px), transparent 100%);
}

/* ─── Top-bar hardening — keep lang switcher above DAPP halo ──────────── */
/* Bulletproof burger hide on desktop (Phase A救命) */
@media (min-width: 900px) {
  .tb-burger { display: none !important; }
}
/* On wide screens the gold halo from .tb-dapp's box-shadow visually bled onto
 * the EN ▾ button. Shrink the halo at wall tiers so the lang switcher stays clean.
 * (Strict doctrine §3.4 — halo is allowed, just contained.) */
@media (min-width: 2400px) {
  .tb-dapp {
    box-shadow:
      inset 0 1px 0 rgba(255,231,166,0.45),
      inset 0 -1px 0 rgba(139,107,47,0.30),
      0 2px 6px rgba(201,168,106,0.22) !important;
  }
}
@media (min-width: 3600px) {
  .tb-dapp {
    box-shadow:
      inset 0 1px 0 rgba(255,231,166,0.45),
      inset 0 -1px 0 rgba(139,107,47,0.30),
      0 2px 4px rgba(201,168,106,0.18) !important;
  }
}

/* ────────────────────────────────────────────────────────────────────────
 * Phase C · Bloomberg-quiet micro-animations (TERMINAL_DOCTRINE §3.4 compliant)
 * All use opacity/transform only (GPU-accelerated). No spring physics, no
 * ambient loops, no decorative gradient sweeps, no icon bounces.
 * ──────────────────────────────────────────────────────────────────────── */

/* C2 · tape / news / unified-feed slide-in (200ms, prefers-reduced-motion safe) */
@keyframes gmgTapeSlideIn {
  from { opacity: 0; transform: translateX(-3px); }
  to   { opacity: 1; transform: translateX(0); }
}
.tape2__row, .news-row, .nf__row, .agents__row {
  animation: gmgTapeSlideIn 220ms ease-out;
  animation-fill-mode: backwards;
}
.tape2__row:nth-child(1) { animation-delay: 0ms; }
.tape2__row:nth-child(2) { animation-delay: 30ms; }
.tape2__row:nth-child(3) { animation-delay: 60ms; }
.tape2__row:nth-child(4) { animation-delay: 90ms; }
.tape2__row:nth-child(5) { animation-delay: 120ms; }
.tape2__row:nth-child(6) { animation-delay: 150ms; }

/* C3 · Sparkline path morph — applied as a CSS transition on the path's d
 * (Chrome 120+ supports d transitions). On older browsers, transition is a no-op. */
.spark-path {
  transition: d 380ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* C4 · Heatmap threshold flash — for cells where chg crosses ±2% or ±5% */
@keyframes gmgThresholdFlashUp {
  0%   { background-color: rgba(52, 211, 153, 0.0); box-shadow: 0 0 0 rgba(52, 211, 153, 0.0); }
  20%  { background-color: rgba(52, 211, 153, 0.38); box-shadow: 0 0 18px rgba(52, 211, 153, 0.55); }
  100% { background-color: rgba(52, 211, 153, 0.0); box-shadow: 0 0 0 rgba(52, 211, 153, 0.0); }
}
@keyframes gmgThresholdFlashDn {
  0%   { background-color: rgba(248, 113, 113, 0.0); box-shadow: 0 0 0 rgba(248, 113, 113, 0.0); }
  20%  { background-color: rgba(248, 113, 113, 0.38); box-shadow: 0 0 18px rgba(248, 113, 113, 0.55); }
  100% { background-color: rgba(248, 113, 113, 0.0); box-shadow: 0 0 0 rgba(248, 113, 113, 0.0); }
}
.flash-threshold-up { animation: gmgThresholdFlashUp 1200ms ease-out; }
.flash-threshold-dn { animation: gmgThresholdFlashDn 1200ms ease-out; }

/* ─────────────────────────────────────────────────────────────────────────
 *  Comprehensive breathing system — every "indicator" element pulses
 *  (TERMINAL_DOCTRINE §3.4: breathing pip on panel headers is the brand signature)
 * ──────────────────────────────────────────────────────────────────────── */

/* The green LIVE indicator dot — breathes opacity + halo on a 2s cycle */
@keyframes gmgLiveBreath {
  0%, 100% { opacity: 0.55; box-shadow: 0 0 4px rgba(52,211,153,0.45); }
  50%      { opacity: 1.0;  box-shadow: 0 0 12px rgba(52,211,153,0.9); }
}
.live-dot .pulse {
  animation: gmgLiveBreath 2s ease-in-out infinite;
}
.live-dot {
  animation: gmgLiveBreath 2s ease-in-out infinite;
  animation-name: gmgLiveTextBreath;
}
@keyframes gmgLiveTextBreath {
  0%, 100% { color: rgba(52,211,153,0.72); }
  50%      { color: rgba(52,211,153,1.0); }
}

/* Generic .dot--live (used in panel headers + brief footer + session pill) */
@keyframes gmgPipBreath {
  0%, 100% { opacity: 0.55; box-shadow: 0 0 0 0 rgba(52,211,153,0.0); }
  50%      { opacity: 1.0;  box-shadow: 0 0 0 3px rgba(52,211,153,0.35); }
}
.dot--live {
  animation: gmgPipBreath 2.2s ease-in-out infinite;
}

/* Gold-toned dots (session pill closed state, etc.) — breathe gold instead of green */
@keyframes gmgGoldPipBreath {
  0%, 100% { opacity: 0.5;  box-shadow: 0 0 0 0 rgba(201,168,106,0.0); }
  50%      { opacity: 1.0;  box-shadow: 0 0 0 3px rgba(201,168,106,0.32); }
}
.session-pill > .dot:not(.dot--live) {
  animation: gmgGoldPipBreath 2.6s ease-in-out infinite;
}

/* DAPP CTA — subtle gold halo breath (gentler than other pulses, big object) */
@keyframes gmgDappBreath {
  0%, 100% {
    box-shadow:
      inset 0 1px 0 rgba(255,231,166,0.45),
      inset 0 -1px 0 rgba(139,107,47,0.30),
      0 2px 8px rgba(201,168,106,0.22);
  }
  50% {
    box-shadow:
      inset 0 1px 0 rgba(255,231,166,0.55),
      inset 0 -1px 0 rgba(139,107,47,0.30),
      0 3px 16px rgba(201,168,106,0.45);
  }
}
.tb-dapp {
  animation: gmgDappBreath 3.2s ease-in-out infinite;
}

/* Brief panel diamond icon ◆ — slow gold breath */
@keyframes gmgIconBreath {
  0%, 100% { opacity: 0.75; text-shadow: 0 0 4px rgba(201,168,106,0.4); }
  50%      { opacity: 1.0;  text-shadow: 0 0 10px rgba(201,168,106,0.85); }
}
.brief2__icon {
  animation: gmgIconBreath 2.8s ease-in-out infinite;
  display: inline-block;
}

/* Category badges — subtle border breathe (catches the eye) */
@keyframes gmgBadgeBreath {
  0%, 100% { border-color: rgba(201,168,106,0.18); }
  50%      { border-color: rgba(201,168,106,0.55); }
}
.quote__cat,
.tape2__kind,
.movers2__tag,
.cal2__cat,
.hdl__cat,
.stance__conv {
  animation: gmgBadgeBreath 3.6s ease-in-out infinite;
}
/* Pause badge breathing when an entity is being hovered/pinned — prevents conflict with pulse */
.quote.gmg-entity-pulse .quote__cat,
.cal2__row.gmg-roving-hl .cal2__cat,
.tape2__row.gmg-roving-hl .tape2__kind { animation-play-state: paused; }

/* Command-line caret — subtle blink */
@keyframes gmgCaretBlink {
  0%, 100% { opacity: 0.85; }
  50%      { opacity: 0.35; }
}
.cmdline__caret { animation: gmgCaretBlink 1.4s ease-in-out infinite; }

/* Quote price pulse — when no tick-flash is active, very subtle 4s opacity breath */
@keyframes gmgPriceBreath {
  0%, 100% { opacity: 0.95; }
  50%      { opacity: 1.0; }
}
.quote__price { animation: gmgPriceBreath 4s ease-in-out infinite; }

/* Brief countdown dot — green breath */
.brief2__countdown .dot--live { animation: gmgPipBreath 2.2s ease-in-out infinite; }

/* Quote TPS dot */
.quote__tps .dot--live { animation: gmgPipBreath 1.6s ease-in-out infinite; }

/* Respect reduced-motion: kill all the new breathing animations */
@media (prefers-reduced-motion: reduce) {
  .live-dot, .live-dot .pulse, .dot--live,
  .session-pill > .dot, .tb-dapp, .brief2__icon,
  .quote__cat, .tape2__kind, .movers2__tag, .cal2__cat, .hdl__cat, .stance__conv,
  .cmdline__caret, .quote__price {
    animation: none !important;
  }
}

/* Roving spotlight — gentle gold left-border that travels through rows every 3s.
 * Mimics Bloomberg's "currently computing" scan. Cathedral-quiet, doctrine-compliant. */
@keyframes gmgRovingHL {
  0%   { box-shadow: inset 3px 0 0 rgba(201,168,106,0);   background-color: transparent; }
  18%  { box-shadow: inset 3px 0 0 rgba(201,168,106,0.85); background-color: rgba(201,168,106,0.05); }
  100% { box-shadow: inset 3px 0 0 rgba(201,168,106,0);   background-color: transparent; }
}
.gmg-roving-hl { animation: gmgRovingHL 1400ms ease-out; }

/* Last-tick age pip — tiny mono number that increments each second */
.tape2__age, .cal2__age, .hdl__age, .pred2__age {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.04em;
  color: rgba(201, 168, 106, 0.42);
  margin-left: 6px;
  white-space: nowrap;
}

/* Scanning highlight for Stance — same idea, 1 of 4 cards highlighted at a time */
.stance__card.gmg-scanning {
  box-shadow: inset 3px 0 0 rgba(201, 168, 106, 0.55);
  background: linear-gradient(90deg, rgba(201, 168, 106, 0.04), transparent 40%);
  transition: box-shadow 400ms ease, background 400ms ease;
}

/* Session pill enhanced with countdown sub */
.session-pill__sub {
  font-size: 9px;
  letter-spacing: 0.10em;
  color: rgba(255, 255, 255, 0.42);
  margin-left: 6px;
  font-family: var(--font-mono);
  font-weight: 400;
}

.brief2__foot {
  display: flex !important;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
}
/* Ultra-short height: hide the foot (UPDATED · NEXT SYNTH metadata is already
 * shown by PanelHeartbeat in the panel header). Must live AFTER L1086 above
 * so source order beats the `display: flex !important` on the base rule. */
@media (min-width: 900px) and (max-height: 730px) {
  .brief2__foot { display: none !important; }
}
.brief2__countdown {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 9.5px;
  letter-spacing: 0.12em;
  color: rgba(201, 168, 106, 0.62);
  text-transform: uppercase;
}
.brief2__countdown .dot--live { width: 5px; height: 5px; }

/* C5 · Calendar countdown pulse — gold halo on the live dot when an event is imminent */
@keyframes gmgCalPulse {
  0%, 100% { box-shadow: 0 0 0 rgba(201, 168, 106, 0.0); }
  50%      { box-shadow: 0 0 16px rgba(201, 168, 106, 0.6); }
}
.dot--live.cal-imminent {
  animation: gmgCalPulse 1.6s ease-in-out 2;
}
/* Calendar row whose event fires within the next 30 minutes — gold halo pulse
 * along the left edge of the row (§3.4.1 registered, 1.6s × 2). */
.cal2__row--imminent {
  animation: gmgCalPulse 1.6s ease-in-out 2;
  background: linear-gradient(90deg, rgba(201,168,106,0.06), transparent 30%);
}
.cal2__row--imminent .cal2__t { color: var(--gold-bright); }

/* Stance card — clickable, signals it via cursor + faint underline of asset label.
 * Doctrine §3 gold-only. */
.stance__card { cursor: pointer; transition: transform 120ms ease, background 160ms ease; }
.stance__card:hover { background: rgba(201,168,106,0.04); }
.stance__card:hover .stance__asset { color: var(--gold-bright); }
.stance__card:focus-visible { outline: 1px solid rgba(201,168,106,0.45); outline-offset: -1px; }

/* Ticker tape — when an item is wrapped in <a>, preserve the inline layout. */
.ticker__item--linked {
  display: inline-flex; align-items: center; gap: 6px;
  color: inherit; text-decoration: none;
  transition: color 120ms ease;
}
.ticker__item--linked:hover .ticker__sym { color: var(--gold-bright); }

/* ── AI Synth Line — gold one-line AI commentary directly under panel header.
 * Doctrine §3.4.0 — the "AI 结论" surface. Fades in 220ms on mount or text change. */
@keyframes gmgAISynthFade {
  from { opacity: 0; transform: translateY(-2px); }
  to   { opacity: 1; transform: translateY(0); }
}
.ai-synth-line {
  display: flex; align-items: baseline; gap: 6px;
  padding: 4px 12px 6px;
  border-bottom: 1px solid rgba(201,168,106,0.10);
  font-size: 11.5px; line-height: 1.45;
  color: rgba(255,231,166,0.78);
  font-style: italic;
  background: linear-gradient(180deg, rgba(201,168,106,0.04) 0%, transparent 100%);
  animation: gmgAISynthFade 220ms ease-out;
}
.ai-synth-line__pip {
  color: var(--gold-bright);
  font-style: normal;
  font-size: 9.5px;
  flex-shrink: 0;
  animation: gmgIconBreath 2.8s ease-in-out infinite;
}
.ai-synth-line__txt {
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
@media (prefers-reduced-motion: reduce) {
  .ai-synth-line { animation: none !important; }
  .ai-synth-line__pip { animation: none !important; }
}

/* ── Microspark — inline 10-14px sparkline. Tone classes set stroke color via SVG attr,
 * this just sets containing flex behavior and a subtle hover brighten. */
.microspark { display: inline-block; vertical-align: middle; flex-shrink: 0; transition: filter 120ms ease; }
.microspark:hover { filter: brightness(1.25); }

/* ── PanelHeartbeat — Xs-ago + status pip + countdown ring at right of panel header.
 * Status colors map to data freshness; ring sweep is gold cumulative progress to next refresh. */
.panel-hb {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--txt-3);
}
.panel-hb__age { letter-spacing: 0.04em; }
.panel-hb__pip {
  width: 6px; height: 6px; border-radius: 50%;
  flex-shrink: 0;
  display: inline-block;
}
.panel-hb__pip--live  { background: var(--up);   box-shadow: 0 0 6px rgba(52,211,153,0.65); animation: gmgPipBreath 2.2s ease-in-out infinite; }
.panel-hb__pip--fresh { background: var(--gold); box-shadow: 0 0 4px rgba(201,168,106,0.55); }
.panel-hb__pip--aging { background: rgba(255,255,255,0.22); }
.panel-hb__pip--stale { background: var(--dn);   box-shadow: 0 0 4px rgba(248,113,113,0.55); }
.panel-hb__ring { flex-shrink: 0; }
@media (prefers-reduced-motion: reduce) {
  .panel-hb__pip--live { animation: none !important; }
}

/* ── TickCell upgrades — directional arrow + freshness fade.
 * Doctrine §3.4.0: cells static > staleMs dim to 70% opacity to focus eye on active ones. */
.tick-cell--stale { opacity: 0.7; transition: opacity 600ms ease; }
.tick-cell { transition: opacity 220ms ease; }
@keyframes gmgTickArrowSlideUp {
  from { opacity: 0; transform: translate(0, 4px); }
  to   { opacity: 1; transform: translate(0, 0); }
}
@keyframes gmgTickArrowSlideDn {
  from { opacity: 0; transform: translate(0, -4px); }
  to   { opacity: 1; transform: translate(0, 0); }
}
.tick-cell__arrow {
  display: inline-block;
  margin-left: 3px;
  font-size: 0.7em;
  vertical-align: super;
  opacity: 0;
}
.tick-cell__arrow--up { color: var(--up); animation: gmgTickArrowSlideUp 200ms ease-out forwards, gmgTickArrowFade 700ms 300ms ease-in forwards; }
.tick-cell__arrow--dn { color: var(--dn); animation: gmgTickArrowSlideDn 200ms ease-out forwards, gmgTickArrowFade 700ms 300ms ease-in forwards; }
@media (prefers-reduced-motion: reduce) {
  .tick-cell__arrow--up, .tick-cell__arrow--dn { animation: none !important; opacity: 1; }
}

/* ── Hot/Cold zone — z-score > 2σ cells get inset glow that slow-breathes 1.6s.
 * Apply with `data-zscore="hot-up|hot-dn"` on any element. */
@keyframes gmgHotZoneUp {
  0%, 100% { box-shadow: inset 0 0 6px rgba(52,211,153,0); }
  50%      { box-shadow: inset 0 0 6px rgba(52,211,153,0.42); }
}
@keyframes gmgHotZoneDn {
  0%, 100% { box-shadow: inset 0 0 6px rgba(248,113,113,0); }
  50%      { box-shadow: inset 0 0 6px rgba(248,113,113,0.42); }
}
[data-zscore="hot-up"] { animation: gmgHotZoneUp 1.6s ease-in-out infinite; }
[data-zscore="hot-dn"] { animation: gmgHotZoneDn 1.6s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
  [data-zscore="hot-up"], [data-zscore="hot-dn"] { animation: none !important; }
}

/* ── Panel data-tick flash — fires a 240ms gold border highlight on every successful
 * REST poll OR WS message. Doctrine §3.4.0 / §3.4.1 — real data event → real visual
 * event. Even when values don't change, this confirms the data pipeline is alive.
 * Trigger: live-data.js adds .panel--tick class on tick, removes after 240ms. */
@keyframes gmgPanelTick {
  0%   { box-shadow: 0 0 0 0 rgba(201,168,106,0.45); border-color: rgba(201,168,106,0.55); }
  100% { box-shadow: 0 0 0 4px rgba(201,168,106,0); border-color: var(--panel-border, rgba(255,255,255,0.06)); }
}
.panel--tick {
  animation: gmgPanelTick 240ms ease-out;
}
@media (prefers-reduced-motion: reduce) {
  .panel--tick { animation: none !important; }
}

/* ── Mobile typography catch-all (doctrine §6 caption ≥11px) ─────────────
 * Pages register specific class overrides in their own CSS. This generic
 * sweeper catches any text below 11px on phones for classes that slipped
 * through (mute / SPAN with .up/.dn/.gold). Specificity is intentionally
 * low so per-page rules can still override when needed.   */
@media (max-width: 899px), (hover: none) and (pointer: coarse) {
  main .mute,
  main .panel__body .up,
  main .panel__body .dn,
  main .panel__body .gold,
  main .panel__body small { font-size: 11px !important; line-height: 1.2 !important; }
}

/* ── Mobile motion calming (doctrine §6 + GMG_STANDARDS §5) ────────────────
 * On narrow screens the panel--tick border flash and the roving spotlight feel
 * visually busy because each panel takes 100% of the viewport width. Tone down. */
@media (max-width: 899px), (hover: none) and (pointer: coarse) {
  /* Softer panel-tick border — less bright in vertical stack */
  @keyframes gmgPanelTickMobile {
    0%   { box-shadow: 0 0 0 0 rgba(201,168,106,0.25); border-color: rgba(201,168,106,0.30); }
    100% { box-shadow: 0 0 0 2px rgba(201,168,106,0); border-color: var(--panel-border, rgba(255,255,255,0.06)); }
  }
  .panel--tick { animation: gmgPanelTickMobile 320ms ease-out; }

  /* Roving spotlight — no row-edge bleed, lower intensity */
  .defi-spot::before {
    inset: 0;
    background: linear-gradient(90deg, transparent 0%, rgba(201,168,106,0.06) 50%, transparent 100%);
    opacity: 0.75;
  }
}

/* Stance — "Awaiting synthesis" skeleton when AI is offline. Quiet gold-dashed
 * cards so the row keeps its proportions but no fake editorial fills the space. */
.stance--skel { opacity: 0.62; }
.stance__card--skel {
  border-style: dashed;
  border-color: rgba(201,168,106,0.22);
  cursor: default;
}
.stance__card--skel:hover { background: transparent; }
.stance__conv--skel { color: var(--txt-3); letter-spacing: 0.12em; }
.stance__stance--skel {
  color: var(--gold);
  letter-spacing: 0.18em;
  font-size: 11px;
  font-family: var(--font-mono);
}
.stance__thesis--skel { color: var(--txt-3); font-size: 11px; line-height: 1.4; }

/* Context menu — "Open source" row picks up gold accent because it leaves the app. */
.ctxmenu__row--source { color: var(--gold); }
.ctxmenu__row--source .ctxmenu__i { color: var(--gold-bright); }

/* Respect reduced-motion preference — kill all phase C animations */
@media (prefers-reduced-motion: reduce) {
  .tape2__row, .news-row, .nf__row, .agents__row { animation: none !important; }
  .spark-path { transition: none !important; }
  .flash-threshold-up, .flash-threshold-dn { animation: none !important; }
  .dot--live.cal-imminent { animation: none !important; }
}

/* ─── Tier badge (dev mode only — hidden corner indicator) ────────────── */
.gmg-tier-badge {
  position: fixed;
  right: 6px;
  bottom: 6px;
  font: 10px/14px var(--font-mono, monospace);
  color: var(--gold, #C9A86A);
  opacity: 0.35;
  background: rgba(10, 10, 10, 0.7);
  padding: 2px 6px;
  border: 1px solid rgba(201, 168, 106, 0.2);
  border-radius: 2px;
  z-index: 9998;
  pointer-events: none;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* ════════════════════════════════════════════════════════════════════════
 * PAGE-SPEC RUBRIC — News golden standard applied to every page
 * ════════════════════════════════════════════════════════════════════════
 * The News page is the agreed visual / density / mobile reference. These
 * rules apply the same standard to every other page WITHOUT touching
 * per-page CSS files (so this section can be rolled back as one unit).
 *
 * 1. Hover gold-tint on every data row
 * 2. Table thead bg darker for separation
 * 3. Hairline borders unified to rgba(255,255,255,.04)
 * 4. Mobile typography floor: body ≥14px, caption ≥11.5px, mono ≥11px
 * 5. Register the gmg-roving-hl animation primitive (was used but undeclared)
 * ════════════════════════════════════════════════════════════════════════ */

/* 1 · Unified hover — all data rows across all pages get gold tint */
.panel__body tbody tr:hover,
.panel__body .row:hover,
.ftable tbody tr:hover,
.altmov__row:hover,
.liqmap__row:hover,
.defi-tbl tbody tr:hover,
.yield-tbl tbody tr:hover,
.borrow-tbl tbody tr:hover,
.dexvol__row:hover,
.lst__row:hover,
.idx-cell:hover,
.megacap__row:hover,
.sectors__row:hover,
.ytable tbody tr:hover,
.fxtable tbody tr:hover,
.cbrates tbody tr:hover,
.etf-tbl tbody tr:hover,
.stab-sup__row:hover,
.exnet__row:hover,
.holders__row:hover,
.whales__row:hover,
.simple__row:hover,
.movers__row:hover,
.hdl__row:hover {
  background: rgba(201, 168, 106, 0.04);
  transition: background 120ms ease-out;
}

/* 2 · Unified table thead — slightly darker bg for separation */
.ftable thead,
.defi-tbl thead,
.yield-tbl thead,
.borrow-tbl thead,
.ytable thead,
.fxtable thead,
.cbrates thead,
.etf-tbl thead,
.holders thead,
.whales thead,
.options-tbl thead,
.chain thead {
  background: rgba(0, 0, 0, 0.2);
}

/* 3 · Unified hairline border — match News rgba(255,255,255,.04) where overspecified */
.ftable tbody td,
.defi-tbl tbody td,
.yield-tbl tbody td,
.borrow-tbl tbody td,
.ytable tbody td,
.fxtable tbody td,
.cbrates tbody td,
.etf-tbl tbody td,
.dexvol__row,
.lst__row,
.altmov__row,
.exnet__row,
.holders__row,
.whales__row,
.stab-sup__row,
.simple__row,
.movers__row,
.hdl__row {
  border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}

/* 5 · Register gmg-roving-hl — was used in CockpitWidgets.useRovingSpotlight()
 *      but undeclared. Map to TERMINAL_DOCTRINE §3.4.1 registered primitive. */
@keyframes gmgRovingHL {
  0%   { background: transparent; box-shadow: none; }
  50%  { background: rgba(201, 168, 106, 0.10); box-shadow: inset 3px 0 0 rgba(201, 168, 106, 0.55); }
  100% { background: transparent; box-shadow: none; }
}
.gmg-roving-hl {
  animation: gmgRovingHL 1400ms cubic-bezier(.4, 0, .2, 1) both;
}

/* 4 · Mobile typography floor — applied across ALL pages, not just Cockpit */
@media (max-width: 899px), (hover: none) and (pointer: coarse) {
  /* Body text ≥14px (iOS HIG minimum) across every list/table cell */
  .ftable, .ftable th, .ftable td,
  .defi-tbl, .defi-tbl th, .defi-tbl td,
  .yield-tbl, .yield-tbl th, .yield-tbl td,
  .borrow-tbl, .borrow-tbl th, .borrow-tbl td,
  .ytable, .ytable th, .ytable td,
  .fxtable, .fxtable th, .fxtable td,
  .cbrates, .cbrates th, .cbrates td,
  .etf-tbl, .etf-tbl th, .etf-tbl td,
  .dexvol__row,
  .lst__row,
  .altmov__row,
  .exnet__row,
  .holders__row,
  .whales__row,
  .stab-sup__row,
  .simple__row,
  .movers__row,
  .hdl__row,
  .hdl__txt,
  .wire__body,
  .desk-feed__head,
  .econ-tbl tbody td,
  .earn-tbl tbody td,
  .brief2__paras,
  .brief2__paras p,
  .cal2__row,
  .tape2__row,
  .pred2__q,
  .prompts__q {
    font-size: 14px !important;
    line-height: 1.5 !important;
  }
  /* Caption / mono labels ≥11px (iOS HIG caption minimum) */
  .ftable th, .defi-tbl th, .yield-tbl th, .borrow-tbl th,
  .ytable th, .fxtable th, .cbrates th, .etf-tbl th,
  .holders th, .whales th, .options-tbl th, .chain th,
  .ftable .mono, .defi-tbl .mono,
  .panel__h-r, .panel__h-r .mono,
  .desk-feed__tag,
  .cc-pill,
  .news-hero__chip .mono,
  .wire__cat,
  .lbl, .micro {
    font-size: 11px !important;
  }
  /* Chip / badge minimum (still small but tappable region 44px) */
  .chip, .badge, .pill,
  .desk-feed__tag, .cc-pill,
  .news-hero__chip, .prompts__cat,
  .quote__cat {
    font-size: 11px !important;
    min-height: 22px;
    padding: 3px 8px !important;
  }
  /* Touch target enforcement — any clickable row ≥44px tall */
  .panel__body tbody tr,
  .panel__body .row,
  .row.is-clickable,
  .simple__row,
  .movers__row,
  .hdl__row,
  .prompts__row,
  a.row, a.panel__body > div {
    min-height: 44px;
  }
}
@media (max-width: 899px), (hover: none) and (pointer: coarse) { .gmg-tier-badge { right: 4px; bottom: 64px; } }

/* ─────────────────────────────────────────────────────────────────────────
 * §6.M — Mobile typography floor (v34, GMG_STANDARDS §5 enforcement)
 *
 * Per [GMG_STANDARDS.md §5](GMG_STANDARDS.md) mobile caption floor is 11px.
 * The blocks above set selective sub-11px values for visual density on
 * desktop-derived rules. At ≤899px we enforce the floor as a final pass —
 * source order wins, so any class listed here ends up ≥ 11px on mobile.
 *
 * Reached via /loop mobile-polish round (v33 deploy, 375 viewport sweep
 * found 16 classes <11px and decorative ↗ arrow at 8.05px).
 * ───────────────────────────────────────────────────────────────────────── */
@media (max-width: 899px), (hover: none) and (pointer: coarse) {
  /* Caption / metadata floor — 11px per §5 */
  .quote__chg-abs,
  .quote__tps,
  .quote__spread,
  .depth__mid, .depth__spread,
  .wire, .wire__row, .wire__t, .wire__src, .wire__cat, .wire__body,
  .cal, .cal__row, .cal__t, .cal__ev,
  .chain, .chain__row, .chain__cell,
  .whales__path,
  .movers__delta,
  .vol-hero__lbl,
  .news-hero__flag, .news-hero__tag, .news-hero__src,
  .empty-state__hint,
  .caps__k,
  .agents__t,
  .ai-hero__hint, .ctx-pool__lbl,
  .ai-tele__lbl,
  .tline__chgs,
  .brief2__ask, .brief2__watch-h, .brief2__countdown,
  .oibars__legend,
  .altmov, .altmov__rk,
  .topoi__row,
  .termstr__axis,
  .econ-tbl, .econ-tbl th, .econ-tbl td, .econ-tbl tr,
  .earn-tbl, .earn-tbl th, .earn-tbl td, .earn-tbl tr,
  .src-strip__lbl {
    font-size: 11px !important;
    line-height: 1.3 !important;
  }

  /* Source-link decorative ↗ arrow — too small to read on mobile, hide.
   * The full link area remains tappable; the icon was always decorative. */
  .source-link__arrow { display: none !important; }
}

/* ════════════════════════════════════════════════════════════════════════
 * §6.M2 — Mobile UI / button / typography deep-pass (audit 2026-05-15)
 * ════════════════════════════════════════════════════════════════════════
 * Audit findings at 375/390/430/768 (preview MCP + window.gmgAudit):
 *   ✓ 0 horizontal overflow / 0 panel-escape / 0 zero-height (P0 clean)
 *   ✗ ~25 widget row classes < 32px tall (touch targets below floor)
 *   ✗ 16 SVG text labels in MACRO YieldCurve at 8.0/8.5px (caption floor 11)
 *   ✗ CRYPTO `.lsgauge__ratio` 10.5px (crypto.css L92 beats terminal.css :899)
 *   ✗ CRYPTO oibars venue links 17×25 / 25×42 (OKX, BYBIT, BITFINEX)
 *
 * Strategy: tiered floors (Bloomberg density vs HIG). All overrides scoped
 * to ≤899px or pointer:coarse so desktop density is untouched. Each rule
 * traces to a specific finding in AUDIT-FINDINGS.md.
 * ════════════════════════════════════════════════════════════════════════ */
@media (max-width: 899px), (hover: none) and (pointer: coarse) {
  /* ── Tier-A: Severely-dense data rows (was 14-21px). Floor at 32px ──
   * Density-first lists where 36/44 would balloon page height. 32px keeps
   * the dense feel but lifts above the iOS HIG sub-target threshold. */
  .skew__row,
  .sectors__row,
  .oibars__row,
  .bridges__row {
    min-height: 32px !important;
  }

  /* ── Table rows ignore min-height (display: table-row).
   *    Enforce via cell padding instead. Content ~12px + 10+10 = 32px ── */
  .chain tbody td,
  .options-tbl tbody td {
    padding-top: 10px !important;
    padding-bottom: 10px !important;
  }

  /* ── Tier-B: Standard data rows (was 22-32px). Floor at 36px ──
   * The bulk of widget rows. 36px is the Bloomberg-density compromise:
   * tappable but still scannable at 4-6 rows per viewport. */
  .altmov__row,
  .megacap__row,
  .single-movers__row,
  .chain-tvl__row,
  .dexvol__row,
  .feerev__row,
  .stable-row,
  .stab-sup__row,
  .lst__row,
  .exnet__row,
  .holders__row,
  .whales__row,
  .cbrates__row,
  .fxtable__row,
  .maxpain__row,
  .topoi__row,
  .agents__row,
  .desk-feed__row,
  .ctx-pool__row,
  .threads__row,
  .revs__row,
  .blocks__row,
  .depth__mid,
  /* Cockpit-specific row tap targets (tape + calendar are <a> elements with
   * outbound hrefs — primary affordance, must clear HIG floor). */
  .tape2__row,
  .cal2__row,
  /* DeFi tokens live panel row — was 31px on mobile (below Tier-B floor). */
  .defi-token,
  /* Predict-specific row tap targets (platforms / movers / headlines list) */
  .plats__row,
  .simple__row,
  .movers__row,
  .hdl__row {
    min-height: 36px !important;
  }

  /* Table-row floors via td padding (display: table-row ignores min-height) */
  .etf-tbl tbody td,
  .ftable tbody td,
  .econ-tbl tbody td,
  .earn-tbl tbody td {
    padding-top: 10px !important;
    padding-bottom: 10px !important;
  }

  /* ── Depth ladder special case ──
   * .depth__row × 40 rows in CRYPTO. Bumping to 36px would push other
   * widgets off the viewport. Keep 21px height but pad source-link inside
   * to expand hit area to ~32px (negative margin keeps layout flow). */
  .depth__row {
    min-height: 24px !important;
  }
  .depth__row > .source-link,
  .depth__side > .source-link {
    display: block;
    padding: 6px 0;
    margin: -6px 0;
  }

  /* ── CRYPTO funding rate (.lsgauge__ratio) ──
   * crypto.css L92 sets 10.5px; terminal.css L975 sets 13px under :899
   * but loses specificity race because crypto.css loads later. Re-assert. */
  .lsgauge__ratio,
  .lsgauge__ratio.tick-cell,
  .lsgauge__head .tick-cell { font-size: 12px !important; }
  .lsgauge__pct { font-size: 11px !important; }

  /* ── CRYPTO oibars venue links (OKX / BYBIT / BITFINEX) ──
   * 17×25 inline anchors. Expand hit area without changing layout flow. */
  .oibars__row a,
  .ftable__v a {
    display: inline-block;
    min-height: 32px;
    line-height: 32px;
    padding: 0 4px;
  }

  /* ── MACRO YieldCurve SVG text labels ──
   * MacroWidgets.jsx:97-98 hardcodes fontSize="8" / "8.5" in viewBox units.
   * SVG accepts CSS font-size; bump to 11 viewBox units = ~14% of 110-unit
   * viewBox height = ~15px on a 110px tall panel. Readable + still
   * proportional to chart. */
  .ycurve svg text { font-size: 11px; }

  /* ── DEFI / MARKETS chain & sector inline source-links ──
   * Tiny ticker labels (USDT, IBIT, AAPL, XLK 14-22px tall) inside row
   * cells are SECONDARY affordances — primary action is parent row click.
   * Don't visually grow the label, but expand its tap area inline so a
   * thumb tap on the label reliably hits the link. */
  main .panel__body a.source-link[as="span"],
  .stable-row__l .source-link,
  .megacap__row .source-link,
  .single-movers__row .source-link,
  .holders__row .source-link,
  .etf-tbl__sym .source-link,
  .lst__l .source-link,
  .chain-tvl__row .source-link,
  .feerev__row .source-link,
  .dexvol__row .source-link,
  .sectors__row .source-link,
  .bridges__row .source-link,
  .stab-sup__l .source-link {
    display: inline-block;
    padding: 4px 2px;
    margin: -4px -2px;
  }

  /* ── Vertical centering for rows that just got a min-height bump ──
   * Without this, content sticks to the top of the new tall row and the
   * extra space looks like a layout bug rather than a touch-target buffer. */
  .altmov__row,
  .megacap__row,
  .single-movers__row,
  .chain-tvl__row,
  .dexvol__row,
  .feerev__row,
  .stable-row,
  .stab-sup__row,
  .exnet__row,
  .holders__row,
  .whales__row,
  .cbrates__row,
  .fxtable__row,
  .maxpain__row,
  .topoi__row,
  .desk-feed__row,
  .blocks__row,
  .skew__row,
  .sectors__row,
  .oibars__row,
  .bridges__row,
  .tape2__row,
  .cal2__row,
  .defi-token,
  .plats__row,
  .simple__row,
  .movers__row,
  .hdl__row {
    align-items: center;
  }

  /* ── CRYPTO onchain cells — bump from 43 → 44 to clear HIG floor ──
   * grid-display cells at 3px vertical padding land at 43px due to mono
   * content height. Single-px bump via padding lands cleanly at 45. */
  .onchain__cell {
    padding-top: 4px !important;
    padding-bottom: 4px !important;
  }

  /* ── Cockpit mover-card + hero-quote source-link primary affordance ──
   * `.movers2__card` is the full mover card (337×39-41px) — the primary tap
   * affordance per CockpitWidgets.jsx:88. Bumped to 44px clean HIG floor.
   * `.quote__top-l` is the small source label on each hero quote card —
   * width 58-66px x height 39px; expand vertical padding to clear floor
   * without changing layout flow (inline-block hack same as ::1645). */
  .movers2__card.source-link,
  a.movers2__card {
    min-height: 44px !important;
    display: flex !important;
    align-items: center;
  }
  .quote__top-l.source-link,
  a.quote__top-l {
    display: inline-flex !important;
    align-items: center;
    min-height: 44px;
    padding-top: 6px;
    padding-bottom: 6px;
    margin-top: -6px;
    margin-bottom: -6px;
  }

  /* ── Long chain / protocol labels — single-line ellipsis truncation ──
   * "ETHEREUM+OPMAINNET" etc. cannot fit in narrow mobile cells. Wrapping
   * shreds words into per-char fragments ("ETHE/REUM"); ellipsis is
   * cleaner Bloomberg-style and full text is still readable via tap/source.
   * Applies to chain/protocol labels in DEFI's dexvol, feerev, lst, stable
   * rows where the cell width is constrained by the grid template. */
  .dexvol__chain,
  .feerev__hd,
  .lst__proto,
  .stable-row__chain {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
  }
}
