/* ── KPI metrics strip - REDESIGN (docs/design_handoff_metrics_strip, Option A) ──
   Net-flow hero (wider) + 3 insight tiles with micro-viz, hairline-divided into
   ONE panel (no nested boxes). Desktop-only (hidden <=860px; mobile uses the story
   rail). Theme-aware: the comp's dark hexes map to our tokens so light theme holds.
   margin-right reserves the 300px Events sidebar so the strip lines up with the
   table. Every functional id/dataset/onclick is unchanged - only markup + CSS. */
#stats-strip.kpi-redux { display: flex; align-items: stretch; flex-wrap: wrap; margin: 6px 316px 0 16px; flex-shrink: 0; padding: 8px 18px; background: var(--bg-card); border: 1px solid var(--border); border-radius: 14px; }

/* ── Net-flow hero (flex 1.5 = the ~10% wider hero). Links to CEX Analytics. ── */
.kpi-flow { flex: 1.5 1 0; min-width: 320px; display: flex; flex-direction: column; gap: 6px; padding: 0 18px 0 0; border-right: 1px solid var(--border); justify-content: center; }
.kpi-flow-link { position: relative; cursor: pointer; }
.kpi-flow-link:focus-visible { outline: 2px solid var(--accent-border); outline-offset: 3px; border-radius: 8px; }
/* context label row: optional exchange chip + "ENTITY · NET FLOW · TF" */
.kpi-ctx { display: flex; align-items: center; gap: 8px; }
.kpi-cex-chip { width: 18px; height: 18px; border-radius: 5px; display: none; align-items: center; justify-content: center; font-family: var(--mono); font-size: 10px; font-weight: 700; background: var(--accent-dim, rgba(124,92,255,.14)); color: var(--accent-2, var(--accent)); }
.kpi-cex-chip.show { display: inline-flex; }
.kpi-cex-chip .mat-ic { font-size: 12px; }   /* "layers" glyph for the multi-CEX aggregate */
.kpi-ctx-lbl { font-family: var(--mono); font-size: 11px; letter-spacing: .8px; text-transform: uppercase; color: var(--text-lo); }
/* big value + verdict + sparkline */
.kpi-flow-head { display: flex; align-items: baseline; gap: 11px; }
.kpi-flow-net { display: flex; align-items: baseline; gap: 11px; min-width: 0; }
.kpi-flow-net .ark-stat-label { display: none; }   /* "Net Flow" now lives in the context line */
.kpi-flow .kpi-hero-val { font-family: var(--mono); font-size: 26px; font-weight: 700; line-height: 1; letter-spacing: -1px; color: var(--text-hi); }
.kpi-flow .kpi-hero-verdict { display: inline-flex; align-items: center; gap: 5px; font-family: var(--mono); font-size: 13px; font-weight: 600; }
.kpi-flow-spark { width: 128px; max-width: 38%; min-height: 26px; margin: 0 0 0 auto; opacity: .9; align-self: center; }
@media (prefers-reduced-motion: reduce) { .kpi-flow-spark { animation: none; } }
/* in/out bar (taller, rounded) + a white knob that rides the inflow/outflow boundary */
.kpi-tug-wrap { position: relative; width: 100%; }
.kpi-flow .kpi-tug { display: flex; width: 100%; height: 9px; border-radius: 6px; overflow: hidden; background: var(--bg-active, rgba(255,255,255,.06)); }
.kpi-flow .kpi-tug .kt-in  { background: var(--flow-in);  width: 50%; transition: width .5s cubic-bezier(.22,1,.36,1); }
.kpi-flow .kpi-tug .kt-out { background: var(--flow-out); width: 50%; transition: width .5s cubic-bezier(.22,1,.36,1); }
.kpi-tug-pulse { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 14px; height: 14px;
  border-radius: 50%; background: #fff; border: 3px solid var(--bg-card); box-shadow: 0 0 0 1px var(--flow-in);
  z-index: 3; pointer-events: none; transition: left .5s cubic-bezier(.22,1,.36,1); }
.kpi-tug-pulse.out  { box-shadow: 0 0 0 1px var(--flow-out); }
.kpi-tug-pulse.flat { box-shadow: 0 0 0 1px var(--text-lo); }
/* legs */
.kpi-flow-legs { display: flex; gap: 5px 18px; flex-wrap: wrap; font-family: var(--mono); }   /* tight row-gap: the legs stack at this hero width */
.kpi-leg { display: flex; align-items: baseline; gap: 6px; min-width: 0; font-size: 12.5px; }
.kpi-leg-lbl { display: inline-flex; align-items: center; gap: 5px; text-transform: uppercase; letter-spacing: .3px; color: var(--text-md); }
.kpi-leg-dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; }
.kpi-leg.in  .kpi-leg-dot { background: var(--flow-in); }
.kpi-leg.out .kpi-leg-dot { background: var(--flow-out); }
.kpi-leg-val { font-weight: 700; color: var(--text-hi); }
.kpi-leg-cnt { font-size: 11px; color: var(--text-lo); }

/* ── Insight tiles (top wallet / top mover / fresh whale) - hairline-divided,
   each a flex column so its micro-viz pins to the bottom (margin-top:auto). ── */
.kpi-tile { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; padding: 2px 16px 2px 16px; }
.kpi-tile.bordl { border-left: 1px solid var(--border); }
.kpi-tile.sig-click { cursor: pointer; border-radius: 10px; transition: background .14s; }
.kpi-tile.sig-click:hover { background: var(--accent-dim, rgba(124,92,255,.05)); }
.kpi-tile.sig-click:focus-visible { outline: 2px solid var(--accent-border); outline-offset: 2px; }
.kpi-tile-head { display: flex; align-items: center; gap: 7px; margin-bottom: 6px; }
.kpi-tile-ic { width: 24px; height: 24px; border-radius: 7px; display: flex; align-items: center; justify-content: center; flex: 0 0 auto; }
.kpi-tile-ic .mat-ic { font-size: 15px; }
.kpi-tile-ic.accent { background: var(--accent-dim, rgba(124,92,255,.14)); color: var(--accent-2, var(--accent)); }
.kpi-tile-ic.green  { background: rgba(16,185,129,.14); color: var(--flow-in); }
.kpi-tile-ic.orange { background: rgba(245,158,11,.14); color: var(--orange); }
.kpi-tile-lbl { font-family: var(--mono); font-size: 10px; letter-spacing: .7px; text-transform: uppercase; color: var(--text-lo); white-space: nowrap; }
.kpi-tile-open { margin-left: auto; display: inline-flex; align-items: center; gap: 1px; font-family: var(--mono); font-size: 10.5px; color: var(--accent-2, var(--accent)); opacity: 0; transition: opacity .14s; white-space: nowrap; }
.kpi-tile-open .mat-ic { font-size: 13px; }
.kpi-tile.sig-click:hover .kpi-tile-open, .kpi-tile.sig-click:focus-visible .kpi-tile-open { opacity: .9; }
.kpi-tile-val { font-family: var(--mono); font-size: 19px; font-weight: 700; letter-spacing: -.5px; color: var(--text-hi); line-height: 1.05; margin-bottom: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.kpi-tile-sub { font-family: var(--mono); font-size: 11px; color: var(--text-lo); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.kpi-tile-viz { margin-top: auto; padding-top: 6px; }
/* top mover: asset icon + symbol row, area sparkline at the bottom */
.kpi-mover-val { display: flex; align-items: center; gap: 8px; }
.kpi-mover-ic { width: 24px; height: 24px; border-radius: 7px; overflow: hidden; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; }
.kpi-mover-ic img, .kpi-mover-ic svg { width: 100%; height: 100%; object-fit: cover; }
.kpi-mover-spark { width: 100%; height: 24px; }
.kpi-mover-spark svg { width: 100%; height: 100%; display: block; }
/* top wallet: in/out split bar (the wallet's lean) + lean label + addr chip */
.kpi-split { display: flex; height: 6px; border-radius: 4px; overflow: hidden; background: var(--bg-active, rgba(255,255,255,.06)); margin-bottom: 6px; }
.kpi-split-in  { background: var(--flow-in); transition: width .5s cubic-bezier(.22,1,.36,1); }
.kpi-split-out { background: var(--flow-out); flex: 1 1 auto; }
.kpi-tile-foot { display: flex; align-items: center; justify-content: space-between; gap: 7px; }
.kpi-lean { font-family: var(--mono); font-size: 10.5px; color: var(--text-md); white-space: nowrap; }
.kpi-addr-chip { display: inline-flex; align-items: center; padding: 2px 7px; border-radius: 6px; background: var(--bg-active, rgba(255,255,255,.04)); border: 1px solid var(--border); font-family: var(--mono); font-size: 10.5px; color: var(--text-md); white-space: nowrap; }
/* fresh whale: radar "just appeared" ping + asset / NEW chips */
.kpi-radar { margin-left: auto; position: relative; display: flex; align-items: center; justify-content: center; width: 14px; height: 14px; }
.kpi-radar-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--orange); }
.kpi-radar-ring { position: absolute; width: 8px; height: 8px; border-radius: 50%; background: var(--orange); animation: kpi-radar-ping 1.8s ease-out infinite; }
.kpi-radar.idle .kpi-radar-ring { display: none; }   /* stops once the move isn't "fresh" */
@keyframes kpi-radar-ping { 0% { transform: scale(.7); opacity: .7; } 100% { transform: scale(1.8); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .kpi-radar-ring { animation: none; } }
.kpi-fw-chip { display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px; border-radius: 7px; font-family: var(--mono); font-size: 10.5px; font-weight: 600; }
.kpi-fw-chip.asset { background: rgba(247,147,26,.12); border: 1px solid rgba(247,147,26,.28); color: #f7931a; }
.kpi-fw-chip.new { background: var(--bg-active, rgba(255,255,255,.04)); border: 1px solid var(--border); color: var(--orange); }
.kpi-fw-ic { width: 13px; height: 13px; border-radius: 4px; overflow: hidden; display: inline-flex; flex: 0 0 auto; }
.kpi-fw-ic img, .kpi-fw-ic svg { width: 100%; height: 100%; object-fit: cover; }

/* tx-in-cache: a tiny tail, not a full row */
.kpi-sys { flex: 0 0 auto; display: flex; flex-direction: column; justify-content: center; gap: 1px; padding: 8px 12px; border-left: 1px solid var(--border); font-family: var(--mono); font-size: 9px; color: var(--text-lo); white-space: nowrap; }
.kpi-sys .mat-ic { display: none; }
.kpi-sys b { color: var(--text-md); font-weight: 700; font-size: 13px; }
.kpi-sys #s-cache-d { color: var(--green); }

/* ── Mobile Net-Flow Band (Variant C "Accent Hero") - docs/"Net-Flow Band -
   Refactor Spec.md". This IS the mobile "Pulse" widget: renderPulse() gives the
   #pulse-digest host class .nfb, so the band shows <=860px and is hidden on
   desktop (the KPI strip covers it there). The hero TONE flips with sign(net)
   via .nfb.dep / .nfb.wd driving CSS vars. Surfaces use theme tokens (light
   theme holds); the tones + spines use the app's --flow-in/--flow-out so admin
   colour overrides apply. Base + tones here; layout in the <=860px block. */
.nfb {
  display: none;
  --nfb-tint: var(--border);
  --nfb-hero-bg: linear-gradient(135deg, var(--accent-dim, rgba(124,92,255,.12)), transparent 72%);
  --nfb-kick: var(--text-lo); --nfb-num: var(--text-hi);
}
.nfb.dep {
  --nfb-tint: var(--flow-in-border);
  --nfb-hero-bg: linear-gradient(135deg, var(--flow-in-bg-strong), transparent 72%);
  --nfb-kick: var(--flow-in); --nfb-num: var(--flow-in);
}
.nfb.wd {
  --nfb-tint: var(--flow-out-border);
  --nfb-hero-bg: linear-gradient(135deg, var(--flow-out-bg-strong), transparent 72%);
  --nfb-kick: var(--flow-out); --nfb-num: var(--flow-out);
}

/* Desktop-narrow (860-980px, strip still visible): hero on its own line, the
   three tiles in a row beneath it (per the handoff's responsive note). */
@media (max-width: 980px) {
  .kpi-flow { flex-basis: 100%; border-right: 0; border-bottom: 1px solid var(--border); padding: 0 0 12px 0; margin-bottom: 2px; }
  .kpi-tile { padding-left: 0; padding-right: 14px; }
  .kpi-tile.bordl { border-left: 0; border-right: 0; padding-left: 14px; border-left: 1px solid var(--border); }
}

/* ── Additive "+ Filter" bar ──────────────────────────────────────────── */
.filter-off { display: none !important; }
.filter-add { position: relative; }
.filter-add-btn { display: inline-flex; align-items: center; gap: 5px; font-family: var(--mono); font-size: 11px; font-weight: 700; color: var(--text-md); background: transparent; border: 1px dashed var(--border2, rgba(255,255,255,.2)); border-radius: 6px; padding: 5px 11px 5px 8px; cursor: pointer; transition: all .12s; white-space: nowrap; }
.filter-add-btn .mat-ic { font-size: 15px; }
.filter-add-btn:hover { color: var(--text-hi); border-color: var(--accent-border, var(--accent)); background: var(--accent-dim); }
.filter-add.open .filter-add-btn { color: var(--accent-2); border-color: var(--accent-border, var(--accent)); border-style: solid; background: var(--accent-dim); }
.filter-add-menu { position: absolute; top: calc(100% + 6px); left: 0; z-index: 60; min-width: 170px; display: flex; flex-direction: column; padding: 5px; background: var(--bg-card2, var(--bg-card)); border: 1px solid var(--border2, var(--border)); border-radius: 9px; box-shadow: var(--shadow-2, 0 8px 28px rgba(0,0,0,.4)); }
.filter-add-menu[hidden] { display: none; }
.filter-add-item { display: flex; align-items: center; gap: 9px; font-family: var(--font); font-size: 12.5px; color: var(--text-md); background: transparent; border: 0; border-radius: 6px; padding: 7px 9px; cursor: pointer; text-align: left; transition: background .1s, color .1s; }
.filter-add-item .mat-ic { font-size: 16px; color: var(--text-lo); }
.filter-add-item:hover { background: var(--bg-hover); color: var(--text-hi); }
.filter-add-item:hover .mat-ic { color: var(--accent-2); }
.filter-add-item.used { display: none; }   /* already active → drop from the menu */

/* Custom dropdown (Time + Market cap) - our themed button + panel (reuses the filter-menu + chain
   filter tokens), NOT a native <select>, so the OPEN option list is themed too. Lives inside the
   Time / Market-cap filter chips. NOTE: font-family is LONGHAND on purpose - the `font:` shorthand
   with a var() family list parsed wrong on Windows and fell back to Arial (the documented gotcha). */
.mini-dd { position: relative; }
.mini-dd-trigger {
  appearance: none; -webkit-appearance: none;   /* kill the native button chrome (corrupted on Windows) */
  display: inline-flex; align-items: center; justify-content: space-between; gap: 8px;
  min-width: 104px; height: 28px; padding: 0 9px;
  background: var(--bg-hover); border: 1px solid var(--border); border-radius: 5px;
  color: var(--text-hi); font-family: var(--mono); font-size: 11px; font-weight: 600; line-height: 1;
  cursor: pointer; transition: border-color .12s;
}
.mini-dd-trigger:hover, .mini-dd.open .mini-dd-trigger { border-color: var(--accent-border); }
.mini-dd-trigger .caret { color: var(--text-lo); font-size: 9px; transition: transform .15s; }
.mini-dd.open .caret { transform: rotate(180deg); }
.mini-dd-panel {
  display: none; position: absolute; top: calc(100% + 5px); right: 0; z-index: 70; min-width: 116px;
  flex-direction: column; padding: 4px;
  background: var(--bg-card2, var(--bg-card)); border: 1px solid var(--border2, var(--border));
  border-radius: 8px; box-shadow: var(--shadow-2, 0 8px 28px rgba(0,0,0,.4));
}
.mini-dd.open .mini-dd-panel { display: flex; }
.mini-dd-opt {
  appearance: none; -webkit-appearance: none;
  text-align: left; font-family: var(--font); font-size: 12px; font-weight: 600; line-height: 1;
  color: var(--text-md); background: transparent; border: 0; border-radius: 5px; padding: 7px 9px;
  cursor: pointer; transition: background .1s, color .1s; width: 100%;
}
.mini-dd-opt:hover { background: var(--bg-hover); color: var(--text-hi); }
.mini-dd-opt.is-on { color: var(--accent-2); background: var(--accent-dim); }
.mini-dd-opt.locked::after { content: ' (Pro)'; color: var(--text-lo); font-size: 10px; font-weight: 700; }

/* Exchange (CEX) multiselect dropdown: the CEX chips (#quick-btns) render as a
   VERTICAL LIST inside the panel (one exchange per row, like the asset list) -
   anchored to the trigger's left so it opens rightward. The chips + their click
   handlers are unchanged; only their container moved into this dropdown. */
.cex-dd .mini-dd-trigger { min-width: 132px; }
.cex-dd-panel { left: 0; right: auto; min-width: 208px; max-width: 300px; padding: 5px; max-height: 340px; overflow-y: auto; }
.cex-dd-panel .seg-chips { display: flex; flex-direction: column; gap: 2px; overflow: visible; }
.cex-dd-panel .seg-chips::after, .cex-dd-panel .seg-chips::before { display: none; }
.cex-dd-panel .ark-seg-btn {
  width: 100%; justify-content: flex-start; text-align: left;
  border-radius: 5px; padding: 8px 10px; gap: 8px;
}

/* Active filter = a removable chip wrapping the existing control */
/* No redundant outer "holder": the field's own control IS the compact token, so
   the chip is just a transparent inline wrapper positioning the ✕ + optional label. */
.filter-chip { position: relative; display: inline-flex; align-items: center; gap: 6px; padding: 0 18px 0 0; background: transparent; border: 0; }
.filter-cex { padding-right: 0; }
.filter-chip .filter-chip-x { position: absolute; right: 0; top: 50%; transform: translateY(-50%); width: 16px; height: 16px; display: flex; align-items: center; justify-content: center; border-radius: 50%; border: 0; background: transparent; color: var(--text-lo); font-size: 14px; line-height: 1; cursor: pointer; opacity: 0; transition: opacity .12s, background .1s, color .1s; }
.filter-chip:hover .filter-chip-x { opacity: 1; }
.filter-chip .filter-chip-x:hover { background: var(--red-dim, rgba(231,76,92,.15)); color: var(--red); }
.filter-chip .ark-ctrl-label { margin: 0; font-family: var(--mono); font-size: 10px; font-weight: 700; letter-spacing: .4px; text-transform: uppercase; color: var(--text-lo); }
/* Each control is the single compact token: purple-tint surface + #2a2540 border. */
#tx-controls-bar .mini-dd-trigger,
#tx-controls-bar .amount-filter,
#tx-controls-bar .chain-select,
#tx-controls-bar #tag-select,
#tx-controls-bar .asset-picker-trigger {
  background: rgba(155,114,255,.08); border: 1px solid #2a2540; }
#tx-controls-bar .mini-dd-trigger:hover,
#tx-controls-bar .asset-picker-trigger:hover,
#tx-controls-bar .amount-filter:focus-within {
  border-color: color-mix(in srgb, var(--accent) 45%, #2a2540); }
.cex-dd .mini-dd-trigger { min-width: 0; }

@media (max-width: 860px) {
  #stats-strip.kpi-redux { margin: 8px 10px 0; }
  .kpi-flow { flex: 1 1 100%; border-right: 0; }
  /* The 3 secondary metrics collapse to ONE compact centered row (no tall
     sparklines) - they were eating too much vertical space as a 2x2 grid. */
  .kpi-grid { flex: 1 1 100%; border-top: 1px solid var(--border); }
  .kpi-cell { flex: 1 1 0; padding: 8px 6px; text-align: center; align-items: center; border-right: 1px solid var(--border); }
  .kpi-cell:last-child { border-right: 0; }
  .kpi-cell .ark-stat-spark { display: none; }
  .kpi-cell .kpi-cell-val { font-size: 15px; }
  .kpi-flow .kpi-hero-val { font-size: 22px; }
  /* ── De-cryptify the 3 signal tiles on mobile ──
     They were 9px low-contrast grey labels + 9.5px grey subs at 33% width -
     read as decorative noise, not the actionable insight cards they are.
     Brighter labels, a bigger value headline, readable sub, and a persistent
     ↗ tap hint (no hover on touch) so it's clearly tappable. */
  .kpi-cell.sig { gap: 4px; padding: 11px 7px 12px; }
  .kpi-cell.sig .sig-label { font-size: 9.5px; color: var(--text-md); justify-content: center; }
  .kpi-cell.sig .sig-label .mat-ic { font-size: 13px; color: var(--accent-2, #a98bff); }
  .kpi-cell.sig .sig-v { font-size: 19px; font-variant-numeric: tabular-nums; }
  .kpi-cell.sig .sig-sub { font-size: 10px; color: var(--text-md); }
  /* persistent affordance: a small corner arrow (the .disc-go hover hint is
     useless on touch) */
  .kpi-cell.sig.disc { position: relative; }
  .kpi-cell.sig.disc .disc-go { display: none; }
  .kpi-cell.sig.disc::after { content: "\2197"; position: absolute;   /* ↗ - no font dependency */
    top: 5px; right: 7px; font-size: 12px; color: var(--text-lo); opacity: .6; }
}

/* The Net-Flow Band (Variant C) IS the mobile Pulse widget - shown on phones,
   hidden on desktop via the .nfb base (display:none). */
@media (max-width: 860px) {
  .nfb {
    display: block; position: relative; margin: 2px 0 0;
    border: 1px solid var(--border); border-radius: 16px; overflow: hidden;
    background: var(--bg-card); -webkit-tap-highlight-color: transparent;
  }
  .nfb.tappable { cursor: pointer; }
  .nfb.tappable:active { border-color: var(--accent-border, var(--accent)); }
  .nfb-hero {
    position: relative; padding: 13px 15px 12px;
    background: var(--nfb-hero-bg); border-bottom: 1px solid var(--nfb-tint);
  }
  /* ambient sheen - decoration only, never a data cue (low opacity, slow, rests) */
  .nfb-hero::after {
    content: ""; position: absolute; inset: 0; pointer-events: none;
    background: linear-gradient(105deg, transparent 30%, rgba(255,255,255,.10) 48%, transparent 66%);
    transform: translateX(-70%) skewX(-18deg); opacity: 0;
    animation: nfbSheen 5s ease-in-out infinite;
  }
  .nfb-kicker {
    font-family: var(--mono); font-size: 10px; font-weight: 600;
    letter-spacing: 1px; text-transform: uppercase; color: var(--nfb-kick);
    margin-bottom: 5px;
  }
  .nfb-net { position: relative; display: flex; align-items: center; gap: 6px; }
  .nfb-arrow { font-size: 23px; color: var(--nfb-num); flex: none; }
  .nfb-num {
    font-family: var(--mono); font-size: 31px; font-weight: 700;
    letter-spacing: -1.1px; line-height: 1; color: var(--nfb-num);
    font-variant-numeric: tabular-nums;
  }
  .nfb-body { padding: 11px 15px 12px; }
  .nfb-split { display: flex; gap: 12px; }
  .nfb-leg { flex: 1 1 0; display: flex; align-items: center; gap: 9px; min-width: 0; }
  .nfb-spine { width: 7px; height: 28px; border-radius: 3px; flex: none; }
  .nfb-spine.in { background: var(--flow-in); }
  .nfb-spine.out { background: var(--flow-out); }
  .nfb-leg-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
  .nfb-leg-lbl {
    font-family: var(--mono); font-size: 10px; color: var(--text-lo);
    letter-spacing: .3px; text-transform: uppercase;
  }
  .nfb-leg-val {
    font-family: var(--mono); font-size: 15.5px; font-weight: 700; color: var(--text-hi);
    font-variant-numeric: tabular-nums;
  }
  .nfb-meta {
    display: flex; align-items: center; gap: 11px; margin-top: 12px;
    padding-top: 10px; border-top: 1px solid var(--border);
    font-family: var(--mono); font-size: 11px; color: var(--text-lo);
  }
  .nfb-meta .mat-ic { font-size: 13px; vertical-align: -2px; }
  .nfb-meta b { font-weight: 700; color: var(--text-hi); }   /* bright number, faint unit */
  .nfb-crit { display: inline-flex; align-items: center; gap: 3px; color: #f5a838; }
  .nfb-crit b { color: #f5a838; }                            /* crit stays warn-coloured */
  .nfb-ent { color: var(--text-md); font-weight: 600; max-width: 38%;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .nfb-sp { flex: 1 1 auto; }
  .nfb-view { display: inline-flex; align-items: center; gap: 2px; color: var(--accent-2); font-weight: 700; flex: none; }
  .nfb-view .mat-ic { font-size: 14px; transition: transform .14s; }
  .nfb.tappable:active .nfb-view .mat-ic { transform: translateX(3px); }
}
@keyframes nfbSheen {
  0%, 55%  { transform: translateX(-70%) skewX(-18deg); opacity: 0; }
  68%      { opacity: .10; }
  85%      { opacity: .10; }
  100%     { transform: translateX(180%) skewX(-18deg); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .nfb-hero::after { animation: none; opacity: 0; }
}

/* ── Admin-configurable promo strip (default = Whale Hunt) ───────────────────
   Thin brand-gradient bar ABOVE the nav (CMC-style): icon + message + CTA centred,
   dismiss (X) pinned right. Single line - never wraps (keeps the strip a slim
   constant height; long copy ellipsizes). Filled + toggled by js/pages/scanner.js
   from /api/config.scannerBanner; [hidden] until then. */
.scan-promo {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  gap: 9px; padding: 5px 44px; min-height: 30px;
  /* Brand gradient (the product purple, deep → light, with a faint diagonal
     sheen on top so the flat strip reads as a surface, not a block of colour). */
  background:
    linear-gradient(115deg, rgba(255,255,255,.12) 0%, rgba(255,255,255,0) 42%),
    linear-gradient(90deg, #5a37f0 0%, #7b5cff 54%, #9b7cff 100%);
  color: #fff; font-size: 12.5px; line-height: 1.25;
  border-bottom: 1px solid color-mix(in srgb, #5a37f0 60%, #000);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.14);
  flex-shrink: 0; overflow: hidden; white-space: nowrap;
}
.scan-promo[hidden] { display: none; }
.scan-promo-ic { font-size: 16px; opacity: .94; flex: none; }
.scan-promo-txt { font-weight: 600; letter-spacing: .1px; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.scan-promo-cta {
  flex: none; display: inline-flex; align-items: center; gap: 3px;
  padding: 3px 10px; border-radius: 999px;
  background: rgba(255,255,255,.18); color: #fff; text-decoration: none;
  font-weight: 700; font-size: 12px; white-space: nowrap;
  transition: background-color .14s, transform .08s;
}
.scan-promo-cta:hover { background: rgba(255,255,255,.30); }
.scan-promo-cta:active { transform: scale(.97); }
.scan-promo-cta .mat-ic { font-size: 14px; }
.scan-promo-x {
  appearance: none; -webkit-appearance: none;   /* kill native button chrome (corrupted on Windows) */
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  display: inline-grid; place-items: center; width: 24px; height: 24px; padding: 0;
  background: transparent; border: 0; border-radius: 7px; cursor: pointer;
  color: rgba(255,255,255,.82); transition: background-color .14s, color .14s;
}
.scan-promo-x:hover { background: rgba(255,255,255,.20); color: #fff; }
.scan-promo-x .mat-ic { font-size: 16px; }
/* Mobile: one tight single line (was a 3-line wrap); the message ellipsizes and
   uses the shorter mobile copy. ~15% taller than the first pass (it read too thin). */
@media (max-width: 600px) {
  .scan-promo { gap: 6px; padding: 5px 30px; min-height: 28px; font-size: 12.5px; }
  .scan-promo-ic { font-size: 15px; }
  .scan-promo-cta { padding: 3px 9px; font-size: 11.5px; }
  .scan-promo-cta .mat-ic { font-size: 13px; }
  .scan-promo-x { width: 22px; height: 22px; right: 5px; }
}

body { display:flex; flex-direction:column; height:100vh; overflow:hidden; }
.main { display:flex; flex:1; overflow:hidden; min-height:0; }
/* scrollbar-gutter:stable reserves the scrollbar's space so the transactions
   holder's right padding stays even with the left (no jump / lopsided gap when
   the vertical scrollbar appears). Trim the right padding since the gutter now
   carries that side. */
.content { flex:1; overflow-y:auto; padding:16px 16px 16px 16px; display:flex; flex-direction:column; gap:12px; scrollbar-width: none; }
/* Transactions column scrollbar HIDDEN completely (still scrollable via wheel / touch). No
   scrollbar-gutter now, and the right padding goes back to 16px to stay symmetric with the left. */
.content::-webkit-scrollbar { width: 0; height: 0; display: none; }
.sidebar { width:340px; flex-shrink:0; border-left:1px solid var(--border); display:flex; flex-direction:column; background:var(--bg-card); }
/* Mobile: stack the events sidebar below the transactions table + let the
   page scroll as one column (the .ark-sidebar width/border reset is shared
   in style.css). */
@media (max-width: 860px) {
  /* Full-page scroll on mobile: the WHOLE page scrolls (nav → metrics → table)
     instead of a fixed viewport with only an inner pane scrolling - so the wide
     table is reachable (esp. in landscape). The nav and the Transactions/Events
     switch stay pinned (sticky) while the active pane flows into the page scroll.
     Scoped to the scanner here so the chart's fixed-canvas layout (which shares
     [data-mobile-views] in style.css) is untouched. */
  /* Full-page scroll; nav + the Transactions/Events switch stay pinned. Scoped
     HERE (not shared) because the chart can't page-scroll - its canvas needs the
     fixed shell, and a sticky pane-switch broke its layout. */
  body { height: auto; min-height: 100vh; overflow-y: auto; }
  .ark-nav { position: sticky; top: 0; z-index: 100; }
  .main { flex-direction: column; overflow: visible; }
  .main > [data-mview].mview-on { overflow: visible; min-height: 0; }
  .main .mview-switch { position: sticky; top: 44px; z-index: 50; }
  .content { padding: 8px 6px; overflow-y: visible; gap: 8px; }
  /* Drop the outer panel frame on mobile - each tx is already its own card, so
     the wrapping .tx-panel border/background + padding just added gaps around
     the list (the "outside gaps"). Let the cards sit near edge-to-edge. */
  .tx-panel { border: 0; border-radius: 0; background: transparent; }
  .tx-panel > #tx-controls-bar.ark-controls { padding-left: 4px; padding-right: 4px; }
  /* Internal switch: drop the desktop right-push so it flows inline with the
     other controls when the bar wraps/collapses (no orphaned right-floated row). */
  .scan-internal-group { margin-left: 0; }
}

/* ─── Mobile: transactions render as dedicated compact CARDS (renderTxCard) ───
   On ≤860px the wide table is hidden and a per-tx <div> card is shown instead.
   A standalone card (not a restyled table row) can't be widened or clipped by
   the table's overflow-x scroll wrapper / flex min-content widths - which was
   the whole battle. Compact 3 lines: from→to · time / asset · amount · value /
   tag chips, coloured left edge by market signal. Desktop keeps the table. */
.tx-cards { display: none; }   /* desktop: table shown, cards hidden */
.story-rail, .story-rail-head { display: none; } /* desktop: KPI tiles carry these facts; rail is mobile-only */
/* Registered custom property so the signal ring's readiness arc can TWEEN
   between values (the chip rotates through forming signals every 3s) - an
   unregistered var in a conic-gradient just jumps. Must live at top level. */
@property --pct { syntax: '<number>'; inherits: true; initial-value: 0; }
@media (max-width: 860px) {
  /* Belt-and-suspenders: keep the page from exceeding the viewport. */
  html, body { overflow-x: hidden; max-width: 100%; }
  .main, .content, .tx-panel, #results-area, #results-area .ark-card { min-width: 0; max-width: 100%; }
  /* Swap: hide the table, show the card list. */
  #results-area .tx-table-wrap { display: none !important; }
  .tx-cards { display: flex; flex-direction: column; gap: 8px; padding: 8px 0; }
  .txc {
    display: block; text-decoration: none; color: inherit;
    border: 1px solid var(--border);
    border-left: 3px solid var(--accent, var(--border2));
    border-radius: 12px;
    padding: 12px 13px;
    background: var(--bg-card);
  }
  /* line 1: verdict (flow chip) + time */
  .txc-top { display: flex; align-items: center; gap: 8px; }
  .txc-dir { flex: 1; min-width: 0; display: inline-flex; }
  .txc-time { flex: 0 0 auto; font-size: 10.5px; color: var(--text-lo); font-family: var(--mono); }
  /* line 2: the money - big and first */
  .txc-mid { display: flex; align-items: baseline; gap: 8px; margin-top: 8px; min-width: 0; }
  .txc-val { flex: 0 0 auto; font-family: var(--mono); font-size: 20px; font-weight: 800; letter-spacing: -.5px; color: var(--text-hi); font-variant-numeric: tabular-nums; }
  .txc-asset { flex: 0 1 auto; min-width: 0; display: inline-flex; align-items: center; gap: 3px; overflow: hidden; font-family: var(--mono); font-size: 12px; font-weight: 600; color: var(--text-md); }
  .txc-amt { margin-left: auto; flex: 0 0 auto; font-family: var(--mono); font-size: 11.5px; color: var(--text-lo); }
  /* line 3: who (supporting) */
  .txc-route { margin-top: 6px; font-family: var(--mono); font-size: 11.5px; font-weight: 600; color: var(--text-md);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .txc-route .txc-arr { color: var(--text-lo); margin: 0 4px; }
  /* ONE swipeable line of tags - badges wrapping to 2-3 rows per card was the
     biggest "looks complex" signal on the mobile list. Overflow scrolls
     horizontally behind a soft right-edge fade (nothing is lost, just calm). */
  .txc-tags { display: flex; flex-wrap: nowrap; align-items: center; gap: 4px; margin-top: 7px;
    overflow-x: auto; overflow-y: hidden; scrollbar-width: none; -webkit-overflow-scrolling: touch;
    -webkit-mask-image: linear-gradient(90deg, #000 92%, transparent);
    mask-image: linear-gradient(90deg, #000 92%, transparent); }
  .txc-tags::-webkit-scrollbar { display: none; }
  .txc-tags > * { flex: 0 0 auto; }
  /* Counterparty wallet link, pushed to the right of the tag row. */
  .txc-wref { margin-left: auto; display: inline-flex; align-items: center; }
  .txc-wref .tx-wref { margin-right: 0; }

  /* ── Feed hierarchy: whale-sized moves carry visual WEIGHT ──
     Every card used to render at one size, so a $1.3M critical outflow was
     optically identical to $3K dust and the list read as a monotone wall.
     $1M+ (or critical) steps the money line up; $10M+ steps up again. */
  .txc-hero { border-left-width: 4px; background: linear-gradient(135deg, var(--bg-card), var(--bg-card2, var(--bg-card))); }
  .txc-hero .txc-val { font-size: 26px; }
  .txc-epic { border-left-width: 5px; border-color: var(--border2);
    background: linear-gradient(135deg, var(--bg-card), var(--bg-card2, var(--bg-card)));
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 22%, transparent),
                0 6px 18px rgba(0, 0, 0, .25); }
  .txc-epic .txc-val { font-size: 30px; }

  /* ── Redesigned single TX card (Variant C) - docs/"TX Card - Mobile Refactor
     Spec.md". Scoped under .txc-v2 so the group-digest + disco cards (which
     reuse the old .txc-* inner classes) stay untouched. Visual only: same data,
     same tag set, same system icons. Surfaces use theme tokens so light theme
     holds; only the semantic hues (direction / asset / tag) are literal. */
  .txc-v2 { padding: 14px 15px 13px 17px; border-radius: 14px; border-left-color: var(--accent); }
  /* row 1: value + token (left) | direction + time (right) */
  .txc-hd { display: flex; align-items: flex-start; gap: 10px; }
  .txc-hd-l { min-width: 0; flex: 1 1 auto; }
  .txc-hd-r { flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-end; gap: 3px; }
  .txc-valrow { display: flex; align-items: center; gap: 7px; min-width: 0; }
  .txc-v { font-family: var(--mono); font-size: 25px; font-weight: 700; letter-spacing: -.8px;
    color: var(--text-hi); font-variant-numeric: tabular-nums; }
  .txc-tok { flex: 0 0 auto; display: inline-flex; align-items: center;
    font-family: var(--mono); font-size: 12px; font-weight: 600;
    padding: 2px 7px 2px 5px; border-radius: 6px; color: var(--c, var(--text-md));
    background: color-mix(in srgb, var(--c, #8a8d9c) 12%, transparent);
    border: 1px solid color-mix(in srgb, var(--c, #8a8d9c) 25%, transparent); }
  .txc-tok img, .txc-tok span[style] { width: 14px; height: 14px; border-radius: 50%;
    object-fit: cover; margin: 0 4px 0 0 !important; flex: 0 0 auto; vertical-align: middle; }
  .txc-sub { margin-top: 3px; font-family: var(--mono); font-size: 11.5px; color: var(--text-lo); }
  .txc-flow { display: inline-flex; align-items: center; gap: 3px; font-family: var(--mono);
    font-size: 10.5px; font-weight: 600; letter-spacing: .3px; color: var(--c, var(--text-md)); white-space: nowrap; }
  .txc-flow .mat-ic { font-size: 13px; }
  .txc-when { font-family: var(--mono); font-size: 11px; color: var(--text-lo); }
  /* row 2: wrapping tag row, one chip style, hue per meaning */
  .txc-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; margin-top: 10px; }
  .txc-chip { display: inline-flex; align-items: center; gap: 3px; padding: 2px 7px; border-radius: 6px;
    font-family: var(--mono); font-size: 9.5px; font-weight: 600; letter-spacing: .3px;
    color: var(--c, #8a8d9c); white-space: nowrap;
    background: color-mix(in srgb, var(--c, #8a8d9c) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--c, #8a8d9c) 28%, transparent); }
  .txc-chip .mat-ic { font-size: 11px; }
  .txc-chip.more { color: var(--text-lo); background: transparent; border-color: transparent; }
  /* row 3: entity-typed route, hairline above */
  .txc-rt { display: flex; align-items: center; gap: 10px; margin-top: 12px; padding-top: 11px;
    border-top: 1px solid var(--border); }
  .txc-party { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; }
  .txc-party .txc-addr { display: flex; flex-direction: column; min-width: 0; text-decoration: none; }
  .txc-pname { display: flex; align-items: center; gap: 4px; min-width: 0;
    font-family: var(--font); font-size: 12.5px; font-weight: 600; color: var(--text-md); }
  .txc-pn { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .txc-venue { width: 14px; height: 14px; border-radius: 50%; object-fit: cover; flex: 0 0 auto; margin: 0; }
  .txc-psub { margin-top: 1px; font-family: var(--mono); font-size: 10.5px; color: var(--text-lo);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .txc-rarrow { flex: 0 0 auto; font-size: 16px; }
  .txc-chev { flex: 0 0 auto; font-size: 17px; color: var(--text-lo); transition: transform .14s, color .14s; }
  .txc-v2:active .txc-chev { transform: translateX(2px); color: var(--accent-2); }
  /* weight tiers for the redesigned card (self-contained; don't inherit the
     group-card-tuned .txc-hero/.txc-epic value sizes) */
  .txc-v2.txc-hero .txc-v { font-size: 28px; }
  .txc-v2.txc-epic .txc-v { font-size: 32px; }

  /* ── Group digest card (renderGroupCard) ──
     "12× · OUTFLOW · $2.1M total · Binance → many wallets", tap to expand. */
  .txc-grp-count { flex: 0 0 auto; font-family: var(--mono); font-size: 11px; font-weight: 700;
    color: var(--purple); background: rgba(155, 114, 255, .12);
    border: 1px solid rgba(155, 114, 255, .3); border-radius: 4px; padding: 1px 7px; }
  .txc-grp-members { margin: -2px 0 2px 14px; display: flex; flex-direction: column; gap: 6px;
    border-left: 1px solid var(--border); padding-left: 8px; }

  /* ── "Right now" story rail (renderStoryRail) ──
     The doors into the deep pages, in story form: top mover → /asset, top
     wallet → /wallet, forming signal → /intelligence, fresh whale → tx.
     It REPLACES the KPI signal tiles on mobile (the strip is hidden below) -
     same facts, but rendered as tappable destinations instead of statistics.
     Zero net new strips on the page. */
  #stats-strip { display: none !important; }   /* tiles' facts live in the rail + Pulse */
  /* Header spells out the interaction (the chips alone got zero taps). */
  .story-rail-head { display: flex; align-items: center; justify-content: space-between; padding: 9px 12px 1px; }
  .srh-eyebrow { display: inline-flex; align-items: center; gap: 6px; font: 800 10px var(--mono);
    letter-spacing: 1.2px; text-transform: uppercase; color: var(--text-md); }
  .srh-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--flow-in, #4fb38f);
    box-shadow: 0 0 7px var(--flow-in, #4fb38f); animation: srh-pulse 1.9s ease-out infinite; }
  @keyframes srh-pulse { 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--flow-in, #4fb38f) 55%, transparent); }
    70% { box-shadow: 0 0 0 7px transparent; } 100% { box-shadow: 0 0 0 0 transparent; } }
  .srh-hint { display: inline-flex; align-items: center; gap: 3px; font: 700 10.5px var(--font);
    color: var(--accent-2, #8b7bff); animation: srh-nudge 3.6s ease-in-out infinite; }
  .srh-hint .mat-ic { font-size: 13px; }
  @keyframes srh-nudge { 0%, 88%, 100% { transform: translateX(0); opacity: .85; } 94% { transform: translateX(3px); opacity: 1; } }
  .story-rail {
    /* space-evenly: the chips spread across the full width instead of
       clustering left (with overflow the free space is zero and it packs
       to the start, so the scroll behaviour is unchanged). */
    display: flex; gap: 6px; align-items: flex-start; justify-content: space-evenly;
    padding: 10px 8px 6px;
    overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch;
    -webkit-mask-image: linear-gradient(90deg, #000 94%, transparent);
    mask-image: linear-gradient(90deg, #000 94%, transparent);
  }
  .story-rail::-webkit-scrollbar { display: none; }
  /* assetIcon()/letter-badge HTML ships inline margin-right:4px +
     vertical-align for inline-text use - inside the centered ring that
     reads as the icon sitting up-left. Neutralize (hence !important). */
  .story-ring img, .story-ring span:not(.mat-ic) { margin: 0 !important; display: block; }
  .story-chip {
    flex: 0 0 auto; width: 66px;
    display: flex; flex-direction: column; align-items: center; gap: 4px;
    text-decoration: none; color: inherit;
    -webkit-tap-highlight-color: transparent;
  }
  .story-chip:active .story-ring { transform: scale(.88); filter: brightness(1.18); }
  .story-chip:active .story-lbl { color: var(--accent-2, #8b7bff); }
  /* Staggered pop-in on (re)render - reads as fresh, live, tappable content. */
  .story-rail .story-chip:not(.story-skel) { animation: story-pop-in .45s cubic-bezier(.2, .7, .2, 1) both; }
  .story-rail .story-chip:nth-child(2):not(.story-skel) { animation-delay: .06s; }
  .story-rail .story-chip:nth-child(3):not(.story-skel) { animation-delay: .12s; }
  .story-rail .story-chip:nth-child(4):not(.story-skel) { animation-delay: .18s; }
  @keyframes story-pop-in { from { opacity: 0; transform: translateY(7px) scale(.94); } to { opacity: 1; transform: none; } }
  @media (prefers-reduced-motion: reduce) { .story-rail .story-chip { animation: none !important; } }
  /* The STORY ring language: a vivid gradient ring with the classic gap
     before the disc - the pattern phones already read as "tap me, new
     content inside". V1 used a thin gray border + gray glyphs and tested
     as static info icons. Gradient stops are decorative brand colours
     (not flow surfaces, so no --flow-* requirement). */
  .story-ring {
    width: 52px; height: 52px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    border: 2px solid transparent;
    background:
      linear-gradient(var(--bg-card2, #151a26), var(--bg-card2, #151a26)) padding-box,
      conic-gradient(from 210deg, var(--accent-2, #8b7bff), #4a8eff, var(--accent-2, #8b7bff)) border-box;
    box-shadow: inset 0 0 0 2.5px var(--bg-page, #0b0e15);   /* the story gap */
    color: var(--text-hi);
    transition: transform .12s;
    animation: story-breathe 3.4s ease-in-out infinite;
  }
  .story-chip:nth-child(2) .story-ring { animation-delay: .5s; }
  .story-chip:nth-child(3) .story-ring { animation-delay: 1s; }
  .story-chip:nth-child(4) .story-ring { animation-delay: 1.5s; }
  @keyframes story-breathe {
    0%, 100% { filter: drop-shadow(0 0 0 rgba(124, 92, 255, 0)); }
    50%      { filter: drop-shadow(0 0 7px rgba(124, 92, 255, .38)); }
  }
  @media (prefers-reduced-motion: reduce) { .story-ring { animation: none; } }
  .story-ring img { border-radius: 50%; }
  .story-ring .mat-ic { font-size: 21px; }
  .story-ring.whale {
    background:
      linear-gradient(var(--bg-card2, #151a26), var(--bg-card2, #151a26)) padding-box,
      conic-gradient(from 210deg, #8b5cf6, #ec4899, #8b5cf6) border-box;
  }
  .story-ring.whale .mat-ic { color: var(--accent-2); }
  /* Signal ring keeps REAL data: conic arc = readiness %. Thicker + brighter
     than v1 so the arc reads as progress, not a faded border. */
  .story-ring.signal {
    position: relative;
    border: none; box-shadow: none;
    background: var(--bg-card2, #151a26);   /* centre disc; the progress RING is an SVG on top */
  }
  /* PROGRESS as an SVG stroke. The earlier conic + animated @property (--pct)
     approach does NOT animate on mobile Safari (only the transform-based comet
     showed - the bug you hit). SVG stroke-dashoffset animates on EVERY browser.
     A faint track circle + a bright progress circle that CONTINUOUSLY LOADS: the
     stroke fills 0 -> readiness % (--pct-target) then drains back, looping. The
     exact % is in the text label under the ring. */
  .story-ring.signal .sig-svg {
    position: absolute; inset: 0; width: 100%; height: 100%;
    transform: rotate(-90deg);   /* arc starts at 12 o'clock */
    pointer-events: none; z-index: 0;
  }
  .story-ring.signal .sig-track { fill: none; stroke: rgba(124, 92, 255, .15); stroke-width: 5; }
  .story-ring.signal .sig-prog {
    --circ: 147.65;   /* 2*pi*r, r=23.5 in the "0 0 52 52" viewBox */
    fill: none; stroke: var(--accent-2, #8b7bff); stroke-width: 5; stroke-linecap: round;
    stroke-dasharray: var(--circ);
    stroke-dashoffset: var(--circ);
    /* ease-in-out so the fill accelerates in and settles softly at the peak, then
       eases back - no sharp reversal like the old linear triangle = smoother. */
    animation: sig-load 2.8s ease-in-out infinite;
  }
  @keyframes sig-load {
    0%, 100% { stroke-dashoffset: var(--circ); }                                          /* empty */
    55%      { stroke-dashoffset: calc(var(--circ) * (1 - var(--pct-target, 0) / 100)); } /* filled to % */
  }
  /* The centre icon sits above the SVG, below the comet. */
  .story-ring.signal .sig-ico { position: relative; z-index: 1; display: inline-flex; align-items: center; justify-content: center; }
  /* Orbiting "scanning" comet ON TOP (kept - you like it): a bright highlight
     rides the rim nonstop. Masked to the rim so it never covers the centre icon. */
  .story-ring.signal::after {
    content: ''; position: absolute; inset: 0; border-radius: 50%; pointer-events: none; z-index: 2;
    background: conic-gradient(from 0deg, transparent 0 70%, color-mix(in srgb, var(--accent-2, #8b7bff) 55%, #fff) 86%, #fff 92%, transparent 97%);
    -webkit-mask: radial-gradient(closest-side, transparent calc(100% - 4px), #000 calc(100% - 4px));
            mask: radial-gradient(closest-side, transparent calc(100% - 4px), #000 calc(100% - 4px));
    animation: sig-orbit 1.4s linear infinite;
  }
  @keyframes sig-orbit { to { transform: rotate(1turn); } }
  @media (prefers-reduced-motion: reduce) {
    .story-ring.signal .sig-prog { animation: none; stroke-dashoffset: calc(var(--circ) * (1 - var(--pct-target, 0) / 100)); }
    .story-ring.signal::after { animation: none; opacity: 0; }
  }
  .story-ring.signal .mat-ic { color: var(--accent-2); }
  /* Signal carousel swap: a clean cross-fade POP - the asset icon shrinks + fades
     out, the next one springs back in (bouncy ease), and the label/sub fade-slide.
     The ring + its readiness arc stay put (its --pct keeps sweeping). NO rotateY
     snap - that read as janky. (startSignalRotation toggles .sig-out around the swap.) */
  .sig-rotate .story-lbl, .sig-rotate .story-sub,
  .sig-rotate .story-ring img, .sig-rotate .story-ring span:not(.mat-ic) {
    transition: opacity .2s ease, transform .28s cubic-bezier(.34, 1.45, .5, 1);
  }
  .sig-rotate.sig-out .story-lbl, .sig-rotate.sig-out .story-sub {
    opacity: 0; transform: translateY(4px);
  }
  .sig-rotate.sig-out .story-ring img, .sig-rotate.sig-out .story-ring span:not(.mat-ic) {
    opacity: 0; transform: scale(.55);
  }
  @media (prefers-reduced-motion: reduce) {
    .story-ring.signal { transition: none; }
    .sig-rotate .story-lbl, .sig-rotate .story-sub,
    .sig-rotate .story-ring img, .sig-rotate .story-ring span:not(.mat-ic) { transition: opacity .2s; transform: none !important; }
    .srh-dot, .srh-hint { animation: none; }
  }
  .story-ring.fresh {
    background:
      linear-gradient(var(--bg-card2, #151a26), var(--bg-card2, #151a26)) padding-box,
      conic-gradient(from 210deg, #f59e0b, #fb7185, #f59e0b) border-box;
  }
  .story-ring.fresh .mat-ic { color: var(--orange, #ffa657); }
  .story-lbl { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-size: 10.5px; font-weight: 700; color: var(--text-hi); }
  .story-sub { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-family: var(--mono); font-size: 9.5px; color: var(--text-lo); }

  /* ── Asset chips must LOOK tappable ──
     assetCell already wraps every ticker in an <a class="asset-link"> to the
     asset page, but it rendered as dead gray text - the most-wanted object on
     the screen (the user's coin) gave no hint it was a door. Accent + chevron.
     !important: the inner span carries an inline color from assetCell. */
  .txc .asset-link { text-decoration: none; display: inline-flex; align-items: center; gap: 2px; }
  .txc .asset-link span { color: var(--accent-2) !important; }
  .txc .asset-link::after { content: '›'; font-size: 12px; color: var(--accent-2); opacity: .65; }

  /* ── In-feed discovery card (injectDiscoveryCards) ──
     One per ~8 tx cards: top whale / forming signal / asset mover / analytics /
     Telegram. These are ATTRACT units, not data rows - they're meant to grab
     the eye and earn a tap, so they break the calm-card mould on purpose:
     a real coin logo (or themed glyph), a big hero number as the hook, a
     coloured glow, an oversized watermark, and a button-style CTA. Each TYPE
     owns a colour (--dc) set at the bottom. */
  .disco-card {
    --dc: var(--accent);                 /* per-type theme colour, set below */
    position: relative; overflow: hidden; isolation: isolate;
    display: block; text-decoration: none; color: inherit;
    padding: 15px 16px 16px;
    border: 1px solid color-mix(in srgb, var(--dc) 32%, var(--border));
    border-radius: 16px;
    background:
      radial-gradient(150% 120% at 100% 0%, color-mix(in srgb, var(--dc) 20%, transparent), transparent 55%),
      linear-gradient(180deg, color-mix(in srgb, var(--dc) 9%, var(--bg-card)), var(--bg-card) 60%);
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--dc) 14%, transparent),
                0 10px 26px -12px color-mix(in srgb, var(--dc) 55%, transparent);
    transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease;
  }
  /* Bold accent rail down the left edge, clipped to the rounded corner. */
  .disco-card::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 4px;
    background: linear-gradient(180deg, var(--dc), color-mix(in srgb, var(--dc) 20%, transparent));
    z-index: 3; }
  /* Watermark: the type's icon, oversized + rotated into the bottom-right.
     Bigger and a touch stronger than a data card would dare - it's an ad. */
  .disco-glyph { position: absolute; right: -16px; bottom: -22px; z-index: 0;
    font-size: 128px; line-height: 1; pointer-events: none;
    color: var(--dc); opacity: .10; transform: rotate(-10deg); }
  .disco-head, .disco-hero, .disco-sub, .disco-meter, .disco-cta { position: relative; z-index: 1; }

  /* Header row: badge + eyebrow + right-aligned chip. */
  .disco-head { display: flex; align-items: center; gap: 10px; }
  /* Badge: Material glyph in a tinted rounded tile... */
  .disco-badge { flex: none; width: 40px; height: 40px; border-radius: 12px;
    display: inline-flex; align-items: center; justify-content: center;
    background: color-mix(in srgb, var(--dc) 16%, transparent);
    border: 1px solid color-mix(in srgb, var(--dc) 32%, transparent); }
  .disco-badge > .mat-ic { font-size: 23px; color: var(--dc); }
  /* ...or a real coin logo with a coloured ring (asset cards). */
  .disco-badge.asset { background: none; border: none; width: 38px; height: 38px;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--dc) 30%, transparent); }
  /* !important: AssetIcons.icon bakes an inline margin-right:4px (for inline use),
     which beats a plain class rule and shoved the logo off-centre in the badge.
     The > span covers the letter-badge fallback (also carries the inline margin). */
  .disco-badge.asset img, .disco-badge.asset > span { width: 34px !important; height: 34px !important; margin: 0 !important; border-radius: 50%; }
  .disco-eyebrow { flex: 1; min-width: 0; display: inline-flex; align-items: center; gap: 6px;
    font-family: var(--mono); font-size: 10px; font-weight: 800; letter-spacing: .8px;
    text-transform: uppercase; color: var(--dc); white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis; }
  /* Right-side chip: rank (#1) or direction (BEARISH ↗). */
  .disco-chip { flex: none; display: inline-flex; align-items: center; gap: 2px;
    padding: 3px 8px; border-radius: 99px; font-family: var(--mono);
    font-size: 10px; font-weight: 800; letter-spacing: .4px; color: var(--dc);
    background: color-mix(in srgb, var(--dc) 15%, transparent);
    border: 1px solid color-mix(in srgb, var(--dc) 30%, transparent); }
  .disco-chip .mat-ic { font-size: 13px; }
  /* Live pulse - "happening right now" (whale card). */
  .disco-live { width: 6px; height: 6px; border-radius: 50%; background: var(--dc); flex: none;
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--dc) 60%, transparent);
    animation: disco-pulse 1.9s ease-out infinite; }
  @keyframes disco-pulse {
    0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--dc) 55%, transparent); }
    70%  { box-shadow: 0 0 0 7px transparent; }
    100% { box-shadow: 0 0 0 0 transparent; } }

  /* Hero: the hook. Big, heavy, tabular. `small` = the unit/label beside it. */
  .disco-hero { margin-top: 11px; font-family: var(--mono);
    font-size: 26px; font-weight: 800; letter-spacing: -.6px; line-height: 1.05;
    color: var(--text-hi); font-variant-numeric: tabular-nums; }
  .disco-hero small { font-size: 13px; font-weight: 700; letter-spacing: 0;
    color: var(--dc); margin-left: 4px; }
  .disco-sub { margin-top: 5px; font-size: 12px; line-height: 1.35; color: var(--text-md); }

  /* Readiness bar (signal card) - tinted track filled to `meter`%, with glow. */
  .disco-meter { margin-top: 12px; height: 5px; border-radius: 99px;
    background: color-mix(in srgb, var(--dc) 18%, transparent); overflow: hidden; }
  .disco-meter i { display: block; height: 100%; border-radius: 99px;
    background: linear-gradient(90deg, color-mix(in srgb, var(--dc) 55%, transparent), var(--dc));
    box-shadow: 0 0 8px color-mix(in srgb, var(--dc) 60%, transparent); }

  /* CTA reads as a button, not a text link - tinted pill, bold, arrow nudges. */
  .disco-cta { display: inline-flex; align-items: center; gap: 5px; margin-top: 13px;
    padding: 8px 13px; border-radius: 10px;
    background: color-mix(in srgb, var(--dc) 15%, transparent);
    font-family: var(--mono); font-size: 11.5px; font-weight: 800; letter-spacing: .2px;
    color: var(--dc); }
  .disco-cta .mat-ic { font-size: 15px; transition: transform .16s ease; }
  .disco-card:active { transform: scale(.985);
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--dc) 30%, transparent),
                0 6px 18px -10px color-mix(in srgb, var(--dc) 60%, transparent); }
  .disco-card:active .disco-cta .mat-ic { transform: translateX(3px); }

  /* ── Per-type identity ───────────────────────────────────────────── */
  .disco-whale    { --dc: #8b6dff; }                 /* signature violet */
  .disco-signal   { --dc: #8b6dff; }                 /* neutral until direction known */
  .disco-signal.bull { --dc: var(--flow-in); }       /* readiness leans green */
  .disco-signal.bear { --dc: var(--flow-out); }      /* ...or red */
  .disco-mover    { --dc: #e0a13c; }                 /* warm amber = momentum */
  .disco-flow     { --dc: #4d8af0; }                 /* analytic blue */
  .disco-telegram { --dc: #229ed9; }                 /* Telegram brand blue */
  /* Text-hero cards (no number to shout) - dial the hero down to a headline. */
  .disco-flow .disco-hero, .disco-telegram .disco-hero {
    font-family: var(--font); font-size: 17px; font-weight: 800; letter-spacing: -.2px;
    line-height: 1.25; }
  /* Telegram CTA = the one hard conversion - solid filled button. */
  .disco-telegram .disco-cta { background: var(--dc); color: #fff; }
}
/* ─── Mobile "Pulse" digest: one verdict, one driver, one risk line ───
   Leads the Transactions pane on phones so the FIRST thing a user reads is a
   plain-language answer ("so what"), not a dense table. Desktop: hidden (the
   KPI strip already covers it). Rendered by renderPulse() in scanner.js. */
.pulse { display: none; }
@media (max-width: 860px) {
  /* Ownership rule: the Pulse SPEAKS the net-flow verdict on mobile, so the
     strip's .kpi-flow card (same numbers, numeric form) hides here - the strip
     keeps only the signal tiles. One fact, one place. */
  #stats-strip .kpi-flow { display: none; }
  .pulse { display: flex; flex-direction: column; gap: 7px; padding: 13px 14px; margin: 2px 0 0;
    border: 1px solid var(--border); border-radius: 12px;
    background: linear-gradient(180deg, rgba(124,92,255,.07), transparent 70%), var(--bg-card); }
  .pulse.tappable { cursor: pointer; -webkit-tap-highlight-color: transparent; }
  .pulse.tappable:active { border-color: var(--accent-border, var(--accent)); }
  .pulse-row .sw { width: 7px; height: 7px; border-radius: 50%; flex: none; }
  /* Block flow, NOT flex: with flex-wrap a long verdict became a full-width
     item and pushed the icon and the timeframe tag onto their own rows -
     three rows for one sentence. Inline icon + trailing <small> wrap as one
     paragraph instead. */
  .pulse-hero { display: block;
    font-family: var(--font); font-size: 17.5px; font-weight: 800; letter-spacing: -.2px; line-height: 1.3; }
  .pulse-hero .mat-ic { font-size: 19px; vertical-align: -3px; margin-right: 6px; }
  .pulse-sub { font: 600 11px var(--mono); color: var(--text-lo); letter-spacing: .3px;
    margin-top: -1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  /* Exchange name is the emphasised part: bright, bold, a notch larger. */
  .pulse-sub .pulse-ent { color: var(--text-hi); font-weight: 800; font-size: 13px; letter-spacing: 0; }
  .pulse-row { display: flex; align-items: center; gap: 7px; min-width: 0;
    font-family: var(--font); font-size: 12.5px; font-weight: 500; color: var(--text-md); }
  .pulse-row .mat-ic { font-size: 15px; color: var(--text-lo); flex: none; }
  .pulse-row b { color: var(--text-hi); font-weight: 700; }
  .pulse-row.warn .mat-ic, .pulse-row.warn b { color: #ffba49; }
  .pulse-row.crit:not(.warn) .mat-ic { color: var(--flow-in); }
  .pulse-go { margin-left: auto; color: var(--accent-2); font-weight: 700; font-size: 11.5px; flex: none; }
}
/* scan-row removed */
.empty-state { display:flex; flex-direction:column; align-items:center; justify-content:center; gap:12px; padding:60px 20px; color:var(--text-lo); }
.empty-icon { font-size:36px; opacity:.4; }

/* "Internal" toggle - a modern switch pinned to the tail of the controls bar
   (same-family/desk rebalances hidden by default). The native checkbox is
   visually hidden; a CSS switch renders the on/off state and lights up purple
   (the INTERNAL flow colour) when ticked on. */
.scan-internal-group { margin-left:auto; }   /* push to the end of the bar */
/* A 2nd special toggle (Deep Scanning) sits RIGHT AFTER Internal, both clustered at the right end -
   without this each group would take margin-left:auto and get pushed apart (Internal ended up mid-bar). */
.scan-internal-group + .scan-internal-group { margin-left:14px; }
.scan-internal-check { display:inline-flex; align-items:center; gap:8px; font-size:12px;
  font-weight:600; letter-spacing:.2px; color:var(--text-md); cursor:pointer;
  user-select:none; white-space:nowrap; transition:color .15s; }
.scan-internal-check:hover { color:var(--text-hi); }
.scan-internal-check input { position:absolute; opacity:0; width:0; height:0; pointer-events:none; }
.scan-switch { position:relative; width:32px; height:18px; flex:none; border-radius:999px;
  background:var(--border); transition:background .18s ease; }
.scan-switch::after { content:''; position:absolute; top:2px; left:2px; width:14px; height:14px;
  border-radius:50%; background:#e9edf4; box-shadow:0 1px 2px rgba(0,0,0,.35); transition:transform .18s ease; }
.scan-internal-check:hover .scan-switch { background:var(--border-hi, #3a4252); }
.scan-internal-check input:checked ~ .scan-switch { background:var(--purple); }
.scan-internal-check input:checked ~ .scan-switch::after { transform:translateX(14px); background:#fff; }
.scan-internal-check input:checked ~ .scan-internal-lbl,
.scan-internal-check:has(input:checked) { color:var(--purple); }
.scan-internal-check input:focus-visible ~ .scan-switch { outline:2px solid var(--accent); outline-offset:2px; }

/* On scanner the CEX/MM filter and metrics strip are body-level siblings of
   .main, so they span the full body width. The table below only takes the
   .content column (Events sidebar is 340px on the right). Outer margin (not
   padding) so the row itself is narrower - the background/border floats
   inside the inset. Right margin = sidebar width exactly so the right edge
   of these rows meets the LEFT edge of the Events sidebar (matches the
   mockup; no visible page-gap between them). Mobile (≤860px) hides the
   sidebar and drops side margins. */
/* CEX/MM filter + metrics strip on scanner now look like the .tx-panel
   below - same card styling (bg, border, radius) so they read as a
   vertically stacked column of three matching panels: filter → metrics →
   table. Same width too: the table is inside .content (which has 16px
   padding) wrapped in .tx-panel, so its right edge sits at
   (screen-width - sidebar-300 - content-padding-16). Setting
   margin-right: 316px on these two rows lines them up exactly.
   Left edge already aligns at 16px (margin-left) which equals .content's
   16px padding-left. Backdrop-filter/glass background from the global
   .ark-controls/.ark-stats rules are turned off here so the card look
   reads as one solid surface.

   Note: scanner.css is loaded ONLY on scanner.html, so these `body >`
   rules don't leak into chart or other pages that use .ark-controls. */
body > .ark-controls,
body > #stats-strip {
  margin-right: 316px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--r-lg, 8px);
  /* Cancel the glass/blur/edge-borders from the global rules so the
     panel looks like a clean card matching .tx-panel below. */
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  box-shadow: none;
}

/* CEX always on row 1, MM always on row 2 - each filter group gets its
   own dedicated line, no vertical divider between them. We scope this to
   body > .ark-controls so it ONLY targets the top-level CEX/MM filter
   row, NOT the table's inner #tx-controls-bar (which also uses the
   .ark-controls class but should stay as a single horizontal row of
   Asset / Risk / Timeframe / Min-USD / Exclude-stables). */
body > .ark-controls {
  flex-direction: column;
  align-items: stretch;
  flex-wrap: nowrap;
  /* Vertical gap between the CEX row and the MM row - visible breathing
     space so the two filter groups read as distinct, without feeling
     disconnected. Bumped from 4px → 10px after the no-divider change
     left them feeling cramped together. */
  row-gap: 10px;
}
body > .ark-controls > .ark-divider { display: none; }
@media (max-width: 860px) {
  /* ONE tight, consistent side gutter for every stacked container on mobile so
     their left/right edges line up (filters bar, KPI strip, the Transactions/
     Events switch, and the tx list all share it). Matches the transactions
     content's minimal gutter. */
  body > .ark-controls { margin-left: 6px; margin-right: 6px; }
  body > #stats-strip {
    margin-left: 6px;
    margin-right: 6px;
    border-radius: 0;
    border-left: none;
    border-right: none;
  }
  /* Assets is its own top-level .mview tab on mobile now, so the Events
     sidebar's sub-tab header is redundant here - hide it (desktop keeps it). */
  .ark-sidebar-tabs { display: none; }
  /* Assets pane on mobile: spacious, tappable cards (not cramped list rows). */
  #tab-assets-mobile .ast-rail-row {
    padding: 13px 14px; margin: 0 8px 8px; gap: 12px;
    border: 1px solid var(--border); border-radius: 13px; background: var(--bg-card2);
  }
  #tab-assets-mobile .ast-rail-row.active { border-color: var(--accent-border, rgba(123,92,255,.45)); }
  #tab-assets-mobile .ast-rail-sym { font-size: 15px; }
  #tab-assets-mobile .ast-rail-sub { font-size: 12px; margin-top: 2px; }
  #tab-assets-mobile .ast-rail-price { font-size: 14px; }
  #tab-assets-mobile .ast-rail-head { padding: 8px; position: sticky; top: 0; z-index: 2; }
}
/* The scanner Assets rail is narrow (300px desktop / a phone pane), so drop the per-row watchlist
   star - it ate a whole grid column. The asset DETAIL page rail keeps its star (different host). */
#tab-assets-mobile .ast-rail-star { display: none; }
#tab-assets-mobile .ast-rail-row.has-star { grid-template-columns: auto 1fr auto; }

/* DESKTOP 3-column layout: the Assets list is its own fixed LEFT rail, MIRRORING the fixed Events
   rail on the right. The transactions table + the top filter / KPI cards clear BOTH rails (316px on
   each side). On mobile (<=861px) none of this applies - Assets stays a top-level .mview pane. */
@media (min-width: 861px) {
  /* #assets-pane-mobile already carries .ark-sidebar (fixed, 300px, full height) - just flip it to
     the LEFT edge and move its divider border to the right side. */
  #assets-pane-mobile { left: 0; right: auto; border-left: 0; border-right: 1px solid var(--border); }
  .main { padding-left: 300px; }                         /* reserve the left rail (mirrors padding-right) */
  body > .ark-controls { margin-left: 316px; }           /* CEX/MM filter card clears the left rail */
  #stats-strip.kpi-redux { margin-left: 316px; }         /* KPI strip clears the left rail */
  #tab-btn-assets { display: none; }                     /* Assets is its own rail now - drop the Events sub-tab */
}

/* Events sidebar runs the FULL HEIGHT of the page (below nav → bottom),
   not just below the CEX/metrics rows. Position fixed so it stays put
   while .content scrolls. The sidebar started life as a flex child of
   .main, which made its top edge align with the table (way below the
   nav). Pulling it out of flow and pinning it to the right gives the
   "full-height column" look from the mockup.

   .main needs padding-right to reserve the 300px now that the sidebar
   is out of flex flow - otherwise .content would slide under it. */
.ark-sidebar {
  position: fixed;
  /* Sit BELOW the nav with the same 6px breathing-room the CEX/MM + metrics
     cards get (margin-top: 6px), so the Events header top lines up with them.
     Nav is 51px (.ark-nav height) - the old 44px left the sidebar ~7px high.
     --promo-h is the live height of the admin promo strip (0 when hidden);
     js/pages/scanner.js sets it so the fixed rails drop below the banner instead
     of overlapping it (the banner is in normal flow, the rails are not). */
  top: calc(51px + 6px + var(--promo-h, 0px));
  right: 0;
  bottom: 0;
  width: 300px;
  z-index: 50;         /* below nav (z=100) but above page content */
  /* Top border closes the card on the top edge. Without it the sidebar
     reads as "open" at the top - the 8px gap to the nav looks like a
     leak rather than an intentional breathing-room. Left border comes
     from the global .ark-sidebar rule; this just adds the missing
     horizontal edge. */
  border-top: 1px solid var(--border);
  /* Round top corners to match the other cards (.tx-panel, the CEX/MM
     and metrics rows) - the sidebar should read as a card too, not a
     bar pinned to the edge. Bottom corners stay square because the
     sidebar runs to the bottom of the viewport. */
  border-top-left-radius: var(--r-lg, 8px);
  border-top-right-radius: var(--r-lg, 8px);
  overflow: hidden;    /* clip the inner background to the rounded corners */
}
.main { padding-right: 300px; }
@media (max-width: 860px) {
  /* On mobile the sidebar collapses into a pane-switch (mview), so the
     fixed positioning would break the responsive shell. Revert to flow. */
  .ark-sidebar { position: static; top: auto; right: auto; bottom: auto; width: 100%; z-index: auto; }
  .main { padding-right: 0; }
}


/* ─── Tx-panel: single cohesive component wrapping toolbar + table ───
   The Scanner's filter toolbar and results table used to sit as two
   separate sibling cards (each with own border + background), looking
   "not quite related" with a thin gap between them. .tx-panel owns
   the rounded card-style frame for the whole unit; its children
   (#tx-controls-bar and the inner .ark-card from renderResults) lose
   their own borders/radii so they merge into one block. */
.tx-panel {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--r-lg, 8px);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
}
.tx-panel > #tx-controls-bar.ark-controls {
  border-top: none;
  border-bottom: 1px solid var(--border);   /* divider only - no outer frame */
  border-radius: 0;
  margin: 0;                                /* kill margin too (was 6px from global .ark-controls) so the bar sits flush against .tx-panel's top edge */
  background: transparent;
  /* The global .ark-controls also paints a fake top hairline via
     `box-shadow: inset 0 1px 0 var(--hairline)`. border-top:none alone
     doesn't remove it - explicitly null the shadow so the bar truly
     has no top edge inside the tx-panel card. */
  box-shadow: none;
}
/* Proportional, fixed column layout. Previously From/To were pinned to width:1%
   (shrink-to-content), which dumped ALL the table's leftover width into the Tag
   column - the big empty gap on the right. table-layout:fixed makes these
   per-column percentages authoritative, so the slack is shared evenly. From/To
   ellipsis on overflow (long labels). The 8th-column !important neutralises the
   inline padding-left:54px the renderer sets on the Tag cell. */
#results-area .ark-table { table-layout: fixed; }
/* Discovery/feature promo woven into the desktop table - styled as a REGULAR tx
   row (same td padding + border, matching height) so it doesn't break the table
   rhythm. One line: real icon (asset badge where relevant) + category label +
   headline + context sub + a category chip + CTA arrow. Each category sets its own
   --txp-accent so the rows read as DIFFERENT things, not five identical ads.
   (#results-area scope wins the global tr:hover td bg.) */
#results-area .tx-promo-row > td { padding: 0; background: transparent; }
.tx-promo-row .txp {
  --txp-accent: var(--accent-2);
  display: flex; align-items: center; gap: 10px;
  padding: 6px 14px; min-height: 30px;
  text-decoration: none; color: var(--text-md);
  /* Directional fade off the accent rail (not a flat wash) - reads sleeker against the mono feed. */
  background: linear-gradient(90deg, color-mix(in srgb, var(--txp-accent) 13%, transparent) 0%, transparent 70%);
  box-shadow: inset 3px 0 0 0 color-mix(in srgb, var(--txp-accent) 75%, transparent);
  transition: box-shadow .12s, background-color .12s;
}
.tx-promo-row .txp:hover {
  background: linear-gradient(90deg, color-mix(in srgb, var(--txp-accent) 20%, transparent) 0%, transparent 70%);
  box-shadow: inset 3px 0 0 0 var(--txp-accent);
}
/* Per-category accent - what makes each promo read as its own thing. */
.txp-whale    { --txp-accent: var(--accent-2); }
.txp-bull     { --txp-accent: var(--green, #10b981); }
.txp-bear     { --txp-accent: var(--red, #e74c5c); }
.txp-mover    { --txp-accent: #4ea1ff; }
.txp-flow     { --txp-accent: var(--accent-2); }
.txp-telegram { --txp-accent: #2aa3e0; }
/* Icon: asset badge (img) scaled to a 22px chip, or a mat-ic glyph. */
.txp-ic { width: 22px; height: 22px; flex: none; display: inline-grid; place-items: center; color: var(--txp-accent); }
.txp-ic img, .txp-ic > span:not(.mat-ic) { width: 100% !important; height: 100% !important; border-radius: 50%; object-fit: cover; }
.txp-ic .mat-ic { font-size: 18px; }
.txp-body { display: flex; align-items: baseline; gap: 7px; min-width: 0; overflow: hidden; }
/* The text was a jumble: UPPERCASE eyebrow + Title/lower hero + lower sub, bold + regular, 10/12/13px -
   that "camelcase, bold-and-not, bigger-and-smaller" mix is what made it exhausting to read. Now it is
   UNIFORM: one font (sans), one size (12px), one weight (600), one case (sentence - no uppercase). It
   reads as a single calm line; ALL structure comes from COLOUR (accent for the label/chip/CTA, white for
   the content). The banner treatment (gradient + accent rail) is kept - that part reads well. */
.txp-eyebrow { font-family: var(--font); font-size: 12px; font-weight: 600; color: var(--txp-accent); flex: none; }
.txp-hero { font-family: var(--font); font-size: 12px; font-weight: 600; color: var(--text-hi); white-space: nowrap; flex: none; }
.txp-sub { font-family: var(--font); font-size: 12px; font-weight: 600; color: var(--text-hi); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.txp-chip { flex: none; font-family: var(--font); font-size: 11px; font-weight: 600; line-height: 1; display: inline-flex; align-items: center; gap: 2px; padding: 3px 7px; border-radius: 999px; background: color-mix(in srgb, var(--txp-accent) 16%, transparent); color: var(--txp-accent); }
.txp-chip .mat-ic { font-size: 11px; }
.txp-cta { margin-left: auto; display: inline-flex; align-items: center; gap: 3px; font-family: var(--font); font-size: 12px; font-weight: 600; color: var(--txp-accent); white-space: nowrap; flex: none; }
.txp-cta .mat-ic { font-size: 14px; transition: transform .12s; }
.tx-promo-row .txp:hover .txp-cta .mat-ic { transform: translateX(3px); }
@media (max-width: 600px) { .txp-eyebrow, .txp-sub { display: none; } }
#results-area .ark-table th:nth-child(1), #results-area .ark-table td:nth-child(1) { width: 7%; }   /* Time */
#results-area .ark-table th:nth-child(2), #results-area .ark-table td:nth-child(2) { width: 5%; }   /* Network */
#results-area .ark-table th:nth-child(3), #results-area .ark-table td:nth-child(3),
#results-area .ark-table th:nth-child(4), #results-area .ark-table td:nth-child(4) {
  width: 11%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;   /* From / To - tighter gap */
}
#results-area .ark-table th:nth-child(5), #results-area .ark-table td:nth-child(5) { width: 8%; padding-left: 16px; }  /* Asset */
#results-area .ark-table th:nth-child(6), #results-area .ark-table td:nth-child(6) { width: 11%; }  /* Amount */
#results-area .ark-table th:nth-child(7), #results-area .ark-table td:nth-child(7) { width: 10%; }  /* Value */
/* Tag is the WIDEST column: multiple tags (flow + MM + low-liq, ~3 max) must
   stay on ONE line. nowrap stops them stacking vertically (the collapse). */
#results-area .ark-table th:nth-child(8), #results-area .ark-table td:nth-child(8) {
  width: 26%; padding-left: 16px !important; white-space: nowrap;
}
#results-area .ark-table th:nth-child(9), #results-area .ark-table td:nth-child(9) { width: 5%; }   /* action */

/* Sortable column headers (Asset / Value) */
#results-area .ark-table th.th-sort { transition: color .12s; white-space: nowrap; }
#results-area .ark-table th.th-sort:hover { color: var(--text-hi); }
#results-area .ark-table th.th-sort:hover .th-sort-ic { opacity: .8; }
#results-area .ark-table th.th-sort.active { color: var(--accent-2); }
.th-sort-ic { font-size: 13px; vertical-align: -2px; margin-left: 3px; opacity: .38; transition: opacity .12s; }
#results-area .ark-table th.th-sort.active .th-sort-ic { opacity: 1; }

/* Right-side wallet reference (counterparty icon + your note/follow badge). */
#results-area .ark-table td.tx-arrow-cell { text-align: right; white-space: nowrap; position: relative; }
/* Discover badge on a tx row: the bare chevron becomes a "Details ↗" chip that
   slides in when the row highlights on hover (same language as the .disc chips). */
.tx-arrow-cell::after { content: 'Details \2197'; position: absolute; right: 6px; top: 50%;
  transform: translateY(-50%) translateX(6px); font-family: var(--mono); font-size: 8.5px; font-weight: 700;
  letter-spacing: .3px; text-transform: uppercase; white-space: nowrap; color: var(--accent-2);
  background: var(--bg-card2, var(--bg-card)); border: 1px solid var(--accent-border); border-radius: 6px;
  padding: 1px 7px; opacity: 0; transition: opacity .13s ease, transform .13s ease; pointer-events: none; }
.tx-row-clickable:hover .tx-arrow-cell::after { opacity: 1; transform: translateY(-50%) translateX(0); }
.tx-row-clickable:hover .tx-detail-arrow { opacity: 0; }   /* chip replaces the chevron on hover */

/* Hot-asset marker in the feed: the most-active / on-fire asset (HOT_SET, from /api/hot-assets) gets a
   small orange flame + warmer ticker wherever it is mentioned in a row, so the asset moving right now is
   visible in the stream, not just the rail. Orange (not a flow/price colour) - an activity signal, not
   direction. STATIC on purpose (it can appear on many rows; an animation here would repaint per row and
   fight the scroll - the single rail flame animates, the feed ones don't). */
.tx-asset-hot { display: inline-flex; align-items: center; gap: 4px; }
.tx-asset-fire { color: #ff7a3c; font-size: 13px !important; line-height: 1; flex: 0 0 auto; pointer-events: none; }
.tx-asset-cell.is-hot > span { color: #ff8a4c; }

/* Scroll-perf (the "feed scroll isn't smooth" report - Windows mouse-wheel especially): while the feed
   is actively scrolling, rows slide UNDER a static cursor and each fires :hover -> the Details-chip swap
   above repaints, per row. Dropping pointer-events on the rows during the gesture stops :hover matching
   at all (no recalc, no repaint); js/pages/scanner.js adds html.is-scrolling on .content scroll and
   clears it ~120ms after it stops. The wheel still scrolls .content normally. Invisible when idle. */
html.is-scrolling .tx-row-clickable { pointer-events: none; }
.tx-wref { display: inline-flex; align-items: center; gap: 3px; vertical-align: middle; margin-right: 12px; color: var(--text-lo); text-decoration: none; }
.tx-wref:hover { color: var(--accent-2); }
.tx-wref .wref-glyph { font-size: 17px; }
.tx-wref .wref-ico { width: 16px; height: 16px; border-radius: 4px; object-fit: cover; vertical-align: middle; }
.wref-mark.on .mat-ic { font-size: 14px; color: var(--accent-2); vertical-align: middle; }

.tx-panel > #results-area { background: transparent; }
.tx-panel > #results-area > .ark-card {
  border: none;
  border-radius: 0;
  background: transparent;
}

/* ─── Timeline (anomaly events stream) ─── */
.tl-wrap {
  flex: 1;
  overflow-y: auto;
  padding: 12px 14px 20px;
  scrollbar-width: thin;
}
.tl-controls {
  display: flex; gap: 6px; align-items: center;
  padding: 8px 14px 10px;
  border-bottom: 1px solid var(--border, #1c2c42);
  flex-wrap: wrap;
}
.tl-controls .lbl {
  font-size: 9px;
  color: var(--text-lo, #5f7a99);
  font-family: 'JetBrains Mono', monospace;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  margin-right: 4px;
}
.tl-controls .chip {
  background: transparent;
  border: 1px solid var(--border, #1c2c42);
  color: var(--text-md, #8fa8c8);
  font-size: 10px;
  padding: 4px 9px;
  border-radius: 100px;
  cursor: pointer;
  font-family: 'JetBrains Mono', monospace;
  transition: all .12s;
}
.tl-controls .chip:hover { border-color: var(--accent-border); color: var(--text-hi); }
.tl-controls .chip.active { background: var(--accent-dim); border-color: var(--accent-2); color: var(--accent-2); }
.tl-controls .chip.critical.active { background: color-mix(in srgb, var(--red) 12%, transparent); border-color: var(--red); color: var(--red); }
.tl-controls .chip.medium.active   { background: rgba(255, 153, 0,  0.12); border-color: #ff9900; color: #ff9900; }
.tl-controls .chip.info.active     { background: rgba(143, 168, 200,0.12); border-color: var(--text-md); color: var(--text-md); }

.tl-event {
  display: grid;
  grid-template-columns: 44px 14px 1fr;
  gap: 10px;
  align-items: flex-start;
  padding-bottom: 14px;
  position: relative;
  animation: tl-slide-in .35s ease-out;
}
.tl-event.new {
  animation: tl-slide-in-glow .8s ease-out;
}
@keyframes tl-slide-in {
  from { opacity: 0; transform: translateX(-6px); }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes tl-slide-in-glow {
  0%   { opacity: 0; transform: translateX(-6px); background: rgba(77,168,255,0.08); }
  50%  { background: rgba(77,168,255,0.04); }
  100% { opacity: 1; transform: translateX(0); background: transparent; }
}
.tl-event:not(:last-child)::after {
  content: '';
  position: absolute;
  left: 49px;
  top: 18px;
  bottom: -2px;
  width: 1px;
  background: var(--border, #1c2c42);
}
.tl-event .tl-time {
  color: var(--text-lo, #5f7a99);
  font-size: 9px;
  font-family: 'JetBrains Mono', monospace;
  padding-top: 1px;
  white-space: nowrap;
  text-align: right;
}
.tl-event .tl-marker {
  width: 12px; height: 12px;
  border-radius: 50%;
  background: var(--bg-card, #0b1220);
  border: 2px solid var(--text-lo, #5f7a99);
  margin-top: 1px;
  position: relative;
  z-index: 1;
  flex-shrink: 0;
}
.tl-event.critical .tl-marker {
  background: var(--red);
  border-color: var(--red);
  box-shadow: 0 0 8px color-mix(in srgb, var(--red) 60%, transparent);
}
.tl-event.medium .tl-marker {
  background: #ff9900;
  border-color: #ff9900;
  box-shadow: 0 0 6px rgba(255, 153, 0, 0.5);
}
.tl-event.info .tl-marker {
  background: var(--text-md);
  border-color: var(--text-md);
}
.tl-event .tl-marker.pulse::before {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  border: 2px solid currentColor;
  animation: tl-pulse 1.6s ease-out infinite;
  pointer-events: none;
}
.tl-event.critical .tl-marker.pulse { color: var(--red); }
.tl-event.medium   .tl-marker.pulse { color: #ff9900; }
@keyframes tl-pulse {
  0%   { transform: scale(0.8); opacity: 0.8; }
  100% { transform: scale(1.6); opacity: 0; }
}

.tl-event .tl-body {
  font-size: 11px;
  color: var(--text-md, #c4d2e2);
  line-height: 1.45;
  min-width: 0;
}
.tl-event .tl-title {
  font-weight: 600;
  color: var(--text-hi, #e8f0fa);
  margin-bottom: 2px;
}
.tl-event.critical .tl-title { color: var(--red); }
.tl-event.medium   .tl-title { color: #ffb84d; }
.tl-event .tl-detail {
  color: var(--text-md, #8fa8c8);
  font-size: 10px;
  font-family: 'JetBrains Mono', monospace;
}
.tl-event .tl-detail .red   { color: var(--red); }
.tl-event .tl-detail .green { color: var(--green); }
.tl-event .tl-detail b      { color: var(--text-hi, #e8f0fa); }
.tl-event .tl-link {
  display: inline-block; margin-top: 3px;
  font-size: 9px; color: var(--text-lo, #5f7a99);
  font-family: 'JetBrains Mono', monospace;
  text-decoration: none;
}
.tl-event .tl-link:hover { color: var(--accent-2); }

.tl-empty {
  text-align: center;
  color: var(--text-lo, #5f7a99);
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  padding: 40px 20px;
}

/* ─── Live update animations (WS-pushed new rows) ─── */
@keyframes flash-row-critical {
  0%   { background: color-mix(in srgb, var(--red) 20%, transparent); }
  50%  { background: color-mix(in srgb, var(--red) 10%, transparent); }
  100% { background: transparent; }
}
@keyframes flash-row-medium {
  0%   { background: rgba(255, 153, 0, 0.18); }
  50%  { background: rgba(255, 153, 0, 0.09); }
  100% { background: transparent; }
}
@keyframes flash-row-normal {
  0%   { background: rgba(77, 168, 255, 0.15); }
  50%  { background: rgba(77, 168, 255, 0.07); }
  100% { background: transparent; }
}
/* New rows ROLL in: a 3D flip-down from edge-on to flat - the "elite" entrance. flashNewRows sets a
   per-row animation-delay so they cascade one by one (top -> last added). A soft colour tint rides
   on top as a brief "new" highlight (not an alarm flash). */
@keyframes roll-in-row {
  0%   { opacity: 0; transform: perspective(760px) rotateX(-68deg) translateY(-7px); }
  55%  { opacity: 1; }
  100% { opacity: 1; transform: perspective(760px) rotateX(0deg) translateY(0); }
}
tr.new-tx-critical { animation: flash-row-critical 2.4s ease-out forwards, roll-in-row .72s cubic-bezier(.2,.7,.2,1) both; }
tr.new-tx-medium   { animation: flash-row-medium   2.2s ease-out forwards, roll-in-row .72s cubic-bezier(.2,.7,.2,1) both; }
tr.new-tx-normal   { animation: flash-row-normal   1.8s ease-out forwards, roll-in-row .72s cubic-bezier(.2,.7,.2,1) both; }

/* Anomaly card animation */
@keyframes pulse-card-critical {
  0%   {
    background: color-mix(in srgb, var(--red) 18%, transparent);
    border-color: color-mix(in srgb, var(--red) 60%, transparent);
    box-shadow: 0 0 20px color-mix(in srgb, var(--red) 35%, transparent);
  }
  60%  {
    background: color-mix(in srgb, var(--red) 8%, transparent);
    box-shadow: 0 0 12px color-mix(in srgb, var(--red) 18%, transparent);
  }
  100% {
    background: transparent;
    box-shadow: none;
  }
}
@keyframes pulse-card-medium {
  0%   {
    background: rgba(255, 153, 0, 0.15);
    border-color: rgba(255, 153, 0, 0.5);
    box-shadow: 0 0 16px rgba(255, 153, 0, 0.25);
  }
  100% {
    background: transparent;
    box-shadow: none;
  }
}
/* New event cards FLIP in, mirroring the table rows (staggered by flashNewRows). They hinge from the
   top edge for a "card dropping into the stack" feel. */
@keyframes roll-in-card {
  0%   { opacity: 0; transform: perspective(720px) rotateX(-72deg) translateY(-6px); }
  100% { opacity: 1; transform: perspective(720px) rotateX(0deg) translateY(0); }
}
.ark-anomaly.new-card-critical { transform-origin: center top; animation: pulse-card-critical 3.2s ease-out forwards, roll-in-card .72s cubic-bezier(.2,.7,.2,1) both; }
.ark-anomaly.new-card-medium   { transform-origin: center top; animation: pulse-card-medium   2.8s ease-out forwards, roll-in-card .72s cubic-bezier(.2,.7,.2,1) both; }

/* Respect users who prefer reduced motion - keep the highlight, drop the slide. */
@media (prefers-reduced-motion: reduce) {
  tr.new-tx-critical, tr.new-tx-medium, tr.new-tx-normal,
  .ark-anomaly.new-card-critical, .ark-anomaly.new-card-medium {
    animation-name: flash-row-normal;
    transform: none !important;
  }
}

/* Live-event toasts are rendered centrally by ws-client.js (coalesced,
   theme-aware, prefs-gated) - the old page-level .live-toast was removed to
   avoid double toasts for the same event. */
/* Smooth re-fetch (filter / pagination) - dim old content + spinner while the
   request is in flight, so the swap reads as "loading → done", not a freeze. */
#results-area.is-loading { position: relative; }
#results-area.is-loading > * { opacity: .4; pointer-events: none; transition: opacity .15s; }
#results-area.is-loading::before {
  content: ''; position: absolute; top: 60px; left: 50%; z-index: 3;
  width: 26px; height: 26px; margin-left: -13px;
  border: 2px solid transparent; border-top-color: var(--accent-2);
  border-right-color: var(--accent-border); border-radius: 50%;
  animation: view-spin .7s linear infinite;
}
@keyframes view-spin { to { transform: rotate(360deg); } }
#stats-strip.stats-loading, #anomaly-list.list-loading { opacity: .55; transition: opacity .15s; }

/* CEX selector uses the shared .ark-seg / .ark-seg-btn segmented control -
   identical to chart so the "selected" look matches across pages. The
   only scanner-specific bit is the 🔥 flame marking the top-volume CEX. */
#quick-btns .ark-seg-btn.is-top { padding-right: 8px; }
/* Fire icon on top-volume CEX is disabled - was making each button width
   uneven (flame added a few px to some labels and not others, so the row
   visually shifted as data refreshed). Volume-rank info still in tooltip. */
#quick-btns .ark-seg-btn .tab-flame { display: none; }
@keyframes tab-flame-flicker {
  0%, 100% { opacity: 1; transform: translateY(0); }
  50%      { opacity: 0.7; transform: translateY(-0.5px); }
}
/* CEX still warming up - dimmed + tiny spinner; stays clickable (shows a
   loader in the table until its warmup finishes, then auto-loads). */
#quick-btns .ark-seg-btn.warming { opacity: .5; }
#quick-btns .ark-seg-btn.warming::after {
  content: ''; width: 8px; height: 8px; margin-left: 6px;
  display: inline-block; vertical-align: middle; border-radius: 50%;
  border: 1.5px solid var(--border3); border-top-color: var(--accent-2);
  animation: loader-rotate .8s linear infinite;
}
/* CEX outside the current plan - dimmed with a lock. Still clickable: the
   click shows a polite upgrade card in the table (server returns locked). */
#quick-btns .ark-seg-btn.cex-locked,
#mm-filter-chips .ark-seg-btn.cex-locked { opacity: .55; }
#quick-btns .ark-seg-btn.cex-locked .cex-lock,
#mm-filter-chips .ark-seg-btn.cex-locked .cex-lock {
  font-size: 12px; margin-left: 5px; vertical-align: -2px; color: var(--text-lo, #888);
}

/* ── Asset picker (searchable dropdown) - mirrors chart's pair picker.
   Filters the Transactions table client-side; anomalies sidebar is
   untouched by design. Lifted from chart so the two pages share a
   visual idiom for "single-select with search". */
.asset-picker {
  position: relative;
  display: inline-flex;
  min-width: 160px;
}
.asset-picker-trigger {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--bg-hover);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 0 10px;
  cursor: pointer;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--text-hi);
  width: 100%;
  height: 28px;
  transition: border-color .12s;
}
.asset-picker-trigger:hover { border-color: var(--accent-border); }
.asset-picker-trigger .label { flex: 1; text-align: left; font-weight: 600; }
.asset-picker-trigger .caret { color: var(--text-lo); font-size: 9px; transition: transform .15s; }
.asset-picker.open .caret { transform: rotate(180deg); }

/* Chain filter - native <select> styled to match .asset-picker-trigger so the
   two filter dropdowns read as one component family. */
.chain-select {
  -webkit-appearance: none; appearance: none;
  height: 28px;
  padding: 0 28px 0 10px;
  background-color: var(--bg-hover);
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6' fill='none' stroke='%238b96a8' stroke-width='1.4'><path d='M1 1l4 4 4-4'/></svg>");
  background-repeat: no-repeat;
  background-position: right 9px center;
  border: 1px solid var(--border);
  border-radius: 5px;
  color: var(--text-hi);
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 600;
  cursor: pointer;
  transition: border-color .12s;
}
.chain-select:hover, .chain-select:focus { border-color: var(--accent-border); outline: none; }
.chain-select option { background: var(--bg-card); color: var(--text-hi); }
.asset-picker .ticker-icon {
  width: 16px; height: 16px;
  border-radius: 50%;
  background: linear-gradient(135deg, #4da8ff 0%, #9b72ff 100%);
  color: #fff;
  font-size: 8px; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.asset-panel {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  min-width: 260px;
  max-height: 400px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 6px;
  box-shadow: 0 12px 32px rgba(0,0,0,0.6);
  z-index: 500;
  display: none;
  flex-direction: column;
  overflow: hidden;
}
.asset-picker.open .asset-panel { display: flex; }
.asset-search { padding: 8px; border-bottom: 1px solid var(--border); }
.asset-search input {
  width: 100%; box-sizing: border-box;
  background: var(--bg-hover);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--text-hi);
  font-family: inherit;
  font-size: 12px;
  padding: 6px 8px;
  outline: none;
}
.asset-search input:focus { border-color: var(--accent-2); }
.asset-search input::placeholder { color: var(--text-lo); }
.asset-list { overflow-y: auto; flex: 1; padding: 4px 0; }
.asset-row {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 10px;
  cursor: pointer;
  font-family: var(--mono);
  font-size: 11px;
  transition: background .1s;
}
.asset-row:hover { background: var(--accent-dim); }
.asset-row.active { background: var(--accent-dim); color: var(--accent-2); }
/* Multi-select tick: assetSel is a Set, so several rows can be active at once
   (ETH family + an isolated wrapper). The check makes the toggle unambiguous. */
.asset-row .asset-check { margin-left: 6px; font-size: 16px; color: var(--accent-2); flex-shrink: 0; }
.asset-row .ticker-icon { width: 20px; height: 20px; font-size: 9px; }
/* Hybrid picker: a base row (ETH) expands to its wrapper sub-rows. The chevron
   toggles expansion only (stopPropagation), independent of the row's select. */
.asset-expand { margin-left: 4px; font-size: 18px; color: var(--text-lo); cursor: pointer;
  border-radius: 6px; transition: transform .15s, color .15s, background .15s; flex-shrink: 0; }
.asset-expand:hover { color: var(--text-hi); background: var(--accent-dim); }
.asset-expand.open { transform: rotate(180deg); color: var(--accent-2); }
.asset-row.asset-sub { padding-left: 26px; background: rgba(255,255,255,0.015); }
.asset-row.asset-sub .asset-symbol { font-weight: 500; }
.asset-sub-tag { font-size: 9px; font-weight: 600; color: var(--text-lo); border: 1px solid var(--border);
  border-radius: 4px; padding: 0 4px; margin-left: 6px; vertical-align: middle; letter-spacing: .04em; }
/* Real asset icons inside the picker (trigger + row) - AssetIcons.icon()
   emits an <img> with inline margin-right; neutralise it so the parent
   .asset-row gap controls spacing, and clip the corners so any letter
   badge fallback still reads as a circle. */
.asset-picker img { margin-right: 0 !important; border-radius: 50%; flex-shrink: 0; }
.asset-picker .ticker-icon { overflow: hidden; }
.asset-picker .ticker-icon img,
.asset-picker .ticker-icon > span { width: 100%; height: 100%; margin: 0 !important; }
.asset-row .asset-info { flex: 1; min-width: 0; }
.asset-row .asset-symbol { color: var(--text-hi); font-weight: 600; }
.asset-row .asset-meta { color: var(--text-lo); font-size: 9px; margin-top: 1px; }
.asset-row .asset-stats { color: var(--text-lo); font-size: 10px; text-align: right; font-variant-numeric: tabular-nums; }
.asset-empty {
  padding: 20px;
  text-align: center;
  color: var(--text-lo);
  font-size: 10px;
  font-family: var(--mono);
}

/* ── Amount filter (≥ USD input) - same 28px height as other controls,
   compact width. Accepts plain numbers and shorthand (1k / 100K / 1.5M / 2B).
   Active state (non-empty) lights up the border to signal an applied filter. */
.amount-filter {
  display: inline-flex;
  align-items: center;
  height: 28px;
  background: var(--bg-hover);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 0 4px 0 8px;
  font-family: var(--mono);
  transition: border-color .12s;
}
.amount-filter:focus-within { border-color: var(--accent-border); }
.amount-filter.active { border-color: color-mix(in srgb, var(--green) 45%, transparent); background: color-mix(in srgb, var(--green) 6%, transparent); }
.amount-filter .amount-prefix {
  color: var(--text-lo);
  font-size: 12px;
  font-weight: 600;
  margin-right: 4px;
}
.amount-filter input {
  width: 90px;
  background: transparent;
  border: none;
  outline: none;
  color: var(--text-hi);
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  padding: 0;
}
.amount-filter input::placeholder { color: var(--text-lo); font-weight: 400; }
.amount-clear {
  width: 18px; height: 18px;
  background: transparent;
  border: none;
  color: var(--text-lo);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  border-radius: 3px;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 0;
}
.amount-clear:hover { color: var(--text-hi); background: var(--bg-card); }
.amount-filter.active .amount-clear { display: inline-flex; }

/* Wallet address filter - same shell as Min USD, but a wider input (addresses are
   long) and an accent (not green) active tint, since green reads as inflow here. */
.addr-filter input { width: 172px; }
.addr-filter .amount-prefix { font-size: 15px; margin-right: 5px; }
.addr-filter.active { border-color: var(--accent-border); background: color-mix(in srgb, var(--accent) 7%, transparent); }
@media (max-width: 560px) { .addr-filter input { width: 128px; } }

/* .risk-pill lives in style.css now - shared so chart (MM chips +
   any future pill UI) renders identically. */

/* .ark-sidebar-tabs (the sidebar Events / OB-Walls pill header) lives in
   style.css now - shared so scanner + chart sidebars render identically. */

/* ── Calm first-run state (js/calm-state.js) ──────────────────────────
   Payoff-first first view shown ONCE to first-time guests. Overlays the
   dense scanner: body.calm-active hides the controls / KPI strip / main
   grid / mobile tab bar and reveals #calm-host. Mobile-first (90% of
   traffic). Removed (exit) the moment the guest opens the full scanner -
   which is already loading underneath. */
/* The calm state is a full-screen overlay. The scanner renders several surfaces
   at BODY level (controls, KPI strip, the #story-rail, #ws-toasts, the main grid,
   the mobile tab bar) - hiding only .main left the story rail + toasts bleeding
   through below the card. So hide EVERY body-level child and keep only the nav +
   the calm layer. The tx-peek modal, the deferred capture sheet and the auth
   modal are body-level too but belong to the calm flow, so they're excluded and
   stay visible when shown. */
body.calm-active > :not(.ark-nav):not(#scan-promo):not(#calm-host):not(#tx-modal):not(.calm-sheet-back):not(.am-backdrop):not(.cloak-overlay):not(script) {
  display: none !important;
}
/* The scanner pins body to a fixed-height, overflow:hidden app shell (the
   `body { height:100vh; overflow:hidden }` rule above). The calm state is a
   normal scrolling document that runs taller than the viewport on a phone - so
   without this override its bottom (the "Open the full scanner" button) gets
   clipped and can't be scrolled to. Restore normal page scrolling while it's up. */
body.calm-active {
  display: block;
  height: auto;
  min-height: 100vh;
  overflow-y: auto;
  --bottom-nav-h: 0px;
}

.calm-host { display: block; max-width: 640px; margin: 0 auto; padding: 16px 14px 40px; }
.calm-wrap { display: flex; flex-direction: column; gap: 14px; }

.calm-card {
  position: relative; overflow: hidden;
  background: var(--bg-card); border: 1px solid var(--border); border-radius: 14px;
  padding: 16px 16px 18px;
}
.calm-card::before { content: ''; position: absolute; top: 0; bottom: 0; left: 0; width: 3px; background: var(--text-lo); }
.calm-card.lean-sell::before { background: var(--flow-in); }
.calm-card.lean-buy::before  { background: var(--flow-out); }

.calm-card-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
.calm-card-title { font: 700 13px/1.3 var(--font); color: var(--text-md); }
.calm-pill {
  flex: none; white-space: nowrap;
  font: 800 10px/1 var(--mono); letter-spacing: .06em;
  padding: 5px 8px; border-radius: 999px; border: 1px solid var(--border); color: var(--text-md);
}
.calm-pill.sell { color: var(--flow-in);  border-color: color-mix(in srgb, var(--flow-in) 42%, transparent); }
.calm-pill.buy  { color: var(--flow-out); border-color: color-mix(in srgb, var(--flow-out) 42%, transparent); }
.calm-verdict { font: 600 16px/1.45 var(--font); color: var(--text-hi); }

.calm-rows { display: flex; flex-direction: column; gap: 8px; }
.calm-row {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 8px 10px;
  width: 100%; text-align: left; cursor: pointer; font: inherit; color: var(--text-hi);
  background: var(--bg-card); border: 1px solid var(--border);
  border-left: 3px solid var(--c, var(--text-lo)); border-radius: 10px; padding: 10px 12px;
}
.calm-row:hover { border-color: var(--accent); }
.calm-row-ic { grid-row: 1 / 3; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; }
.calm-row-ic img, .calm-row-ic svg { width: 30px; height: 30px; border-radius: 50%; }
.calm-row-amt { font: 800 15px/1 var(--mono); color: var(--text-hi); }
.calm-row-route {
  grid-column: 2 / 3; font: 500 12px/1.2 var(--font); color: var(--text-md);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.calm-row-chip { grid-column: 3 / 4; grid-row: 1 / 2; justify-self: end; font: 800 10px/1 var(--mono); letter-spacing: .04em; }
.calm-row-ago { grid-column: 3 / 4; grid-row: 2 / 3; justify-self: end; font: 500 11px/1 var(--mono); color: var(--text-lo); }

.calm-orient {
  display: flex; align-items: center; flex-wrap: wrap; gap: 4px 14px;
  font: 500 12px/1.4 var(--font); color: var(--text-md); padding: 2px 2px 0;
}
.calm-orient > span { display: inline-flex; align-items: center; }
.calm-dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; margin-right: 6px; }
.calm-dot.in  { background: var(--flow-in); }
.calm-dot.out { background: var(--flow-out); }

.calm-open {
  margin-top: 4px; width: 100%; border: 0; border-radius: 12px; padding: 14px;
  font: 800 14px/1 var(--font); color: #fff; cursor: pointer;
  background: var(--grad-accent, linear-gradient(135deg, #9171ff, #6a45f5));
}

.calm-skel { gap: 14px; }
.calm-skel .skel { width: 100%; border-radius: 12px; display: block; }

/* deferred capture bottom sheet */
.calm-sheet-back {
  position: fixed; inset: 0; z-index: 10000;
  display: flex; align-items: flex-end; justify-content: center;
  background: rgba(5, 7, 12, .6); backdrop-filter: blur(3px); animation: calm-fade .16s ease-out;
}
.calm-sheet {
  width: 100%; max-width: 520px; position: relative;
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: 16px 16px 0 0; padding: 22px 18px calc(20px + env(safe-area-inset-bottom));
  box-shadow: 0 -18px 60px rgba(0, 0, 0, .5); animation: calm-rise .22s cubic-bezier(.2, .8, .2, 1);
}
.calm-sheet-x {
  position: absolute; top: 12px; right: 12px; background: none; border: 0; cursor: pointer;
  color: var(--text-lo); font-size: 20px; line-height: 1; padding: 4px;
}
.calm-sheet-x:hover { color: var(--text-hi); }
.calm-sheet-title { font: 800 17px/1.3 var(--font); color: var(--text-hi); margin: 0 0 6px; padding-right: 24px; }
.calm-sheet-sub { font: 500 13px/1.5 var(--font); color: var(--text-md); margin: 0 0 16px; }
.calm-sheet-cta {
  width: 100%; border: 0; border-radius: 10px; padding: 13px; cursor: pointer;
  font: 800 14px/1 var(--font); color: #fff;
  background: var(--grad-accent, linear-gradient(135deg, #9171ff, #6a45f5));
}
.calm-sheet-later {
  width: 100%; background: none; border: 0; margin-top: 8px; padding: 10px; cursor: pointer;
  font: 600 13px/1 var(--font); color: var(--text-md);
}
@keyframes calm-fade { from { opacity: 0 } to { opacity: 1 } }
@keyframes calm-rise { from { transform: translateY(100%) } to { transform: translateY(0) } }

/* desktop: center the card column + the capture dialog instead of bottom-sheet */
@media (min-width: 721px) {
  .calm-host { padding-top: 26px; }
  .calm-sheet-back { align-items: center; }
  .calm-sheet { max-width: 420px; border-radius: 16px; box-shadow: 0 18px 60px rgba(0, 0, 0, .5); animation: calm-fade .16s ease-out; }
}

/* ===== Mobile Transactions Panel (docs/"CEXlens scanner redesign.pdf") =======
   The toolbar + list read as ONE table: a header zone (summary row + always-
   visible removable chip rail + the existing controls as an expandable drawer
   with Reset/Apply) over de-boxed rows. Filters keep their wiring; this is
   container chrome only. Desktop is untouched - .fp-* chrome is hidden there
   and the row de-box / panel overrides live inside @media (max-width:860px). */
.fp-head, .fp-foot { display: none; }

@media (max-width: 860px) {
  /* The panel is the single card (overrides the earlier edge-to-edge reset). */
  .tx-panel {
    border: 1px solid var(--border); border-radius: 18px; overflow: hidden;
    background: var(--bg-card); box-shadow: 0 16px 40px rgba(0, 0, 0, .38);
  }
  #results-area .ark-card { border: 0; border-radius: 0; background: transparent; }

  /* ---- Header zone: summary row + chip rail ---- */
  .fp-head { display: block; background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 7%, var(--bg-card)), var(--bg-card)); }
  .fp-summary { display: flex; align-items: center; gap: 9px; padding: 12px 13px 10px; cursor: pointer; -webkit-tap-highlight-color: transparent; }
  .fp-tune { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: 8px;
    background: var(--accent-dim, rgba(139,108,255,.12)); border: 1px solid var(--accent-border, rgba(139,108,255,.4)); color: var(--accent-2); flex: none; }
  .fp-tune .mat-ic { font-size: 17px; }
  .fp-title { font-family: var(--font); font-size: 14px; font-weight: 700; color: var(--text-hi); flex: none; }
  .fp-count { display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px; padding: 0 5px;
    border-radius: 9px; background: var(--accent); color: #0b0b12; font-family: var(--mono); font-size: 10.5px; font-weight: 700; flex: none; }
  .fp-count[hidden] { display: none; }
  .fp-digest { flex: 1 1 auto; min-width: 0; font-family: var(--mono); font-size: 11px; color: var(--text-lo);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .fp-result { flex: none; font-family: var(--mono); font-size: 11px; font-weight: 600; color: var(--text-md); }
  .fp-chev { flex: none; font-size: 20px; color: var(--text-lo); transition: transform .2s ease; }
  .tx-panel.fp-open .fp-chev { transform: rotate(180deg); }

  .fp-rail { display: flex; gap: 7px; padding: 0 13px 11px; overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
  .fp-rail::-webkit-scrollbar { display: none; }
  .fp-chip { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 4px; height: 26px; padding: 0 10px;
    border-radius: 999px; font-family: var(--mono); font-size: 11.5px; font-weight: 600; cursor: pointer;
    border: 1px solid var(--border); background: var(--bg-active, rgba(255,255,255,.04)); color: var(--text-md); white-space: nowrap; }
  .fp-chip.on { border-color: var(--accent-border, rgba(139,108,255,.55)); background: var(--accent-dim, rgba(139,108,255,.16)); color: var(--accent-2, #cdbcff); }
  .fp-chip .mat-ic { font-size: 13px; }
  .fp-chip.fp-add { border-style: dashed; background: transparent; color: var(--text-lo); }
  .fp-chip-tf { font-weight: 700; color: var(--text-md); }

  /* Collapsed = a single compact summary bar. The chip rail is a second row that
     just repeats the digest text already in the summary (and is only [tf][+Add]
     in the default state), so it's hidden until the drawer opens - the resting
     header is one tight row (~42px), not two (~65px). The removable chips come
     back the moment you tap to expand. */
  .fp-summary, .fp-tune { transition: padding .2s ease, width .2s ease, height .2s ease; }
  .tx-panel:not(.fp-open) .fp-rail { display: none; }
  .tx-panel:not(.fp-open) .fp-summary { padding: 9px 13px; }
  .tx-panel:not(.fp-open) .fp-tune { width: 24px; height: 24px; }
  .tx-panel:not(.fp-open) .fp-tune .mat-ic { font-size: 15px; }

  /* ---- Drawer = the existing controls bar, collapsed by default ---- */
  .tx-panel > #tx-controls-bar.ark-controls {
    max-height: 0; min-height: 0; overflow: hidden; opacity: 0; padding: 0 13px;
    border: 0; box-shadow: none; margin: 0; background: transparent;
    transition: max-height .3s ease, opacity .22s ease, padding .22s ease;
  }
  .tx-panel.fp-open > #tx-controls-bar.ark-controls {
    max-height: 1200px; overflow: visible; opacity: 1; padding: 11px 13px 0;
    border-top: 1px solid color-mix(in srgb, var(--accent) 14%, var(--border));
  }
  .fp-foot { display: flex; gap: 9px; padding: 12px 0 13px; }
  .fp-reset { flex: 0 0 auto; padding: 0 16px; height: 38px; border-radius: 10px; background: transparent;
    border: 1px solid var(--border); color: var(--text-md); font-family: var(--mono); font-size: 12.5px; font-weight: 600; cursor: pointer; }
  .fp-reset:active { background: var(--bg-hover); }
  .fp-apply { flex: 1 1 auto; height: 38px; border-radius: 10px; border: 0; cursor: pointer; color: #fff;
    background: linear-gradient(135deg, #9b7dff, #7b5cff); font-family: var(--mono); font-size: 13px; font-weight: 700;
    box-shadow: 0 4px 14px rgba(123,92,255,.35); display: inline-flex; align-items: center; justify-content: center; gap: 6px; }

  /* ---- Header/body seam ---- */
  #results-area { border-top: 1px solid var(--border); }

  /* Compact the empty / locked states on mobile: short content (loading spinner,
     locked-exchange upgrade card) was centered in a tall band, leaving a big empty
     gap under the filter header. Trim the padding + reserved height and top-align
     so it sits right under the header. */
  #results-area .empty-state { padding: 26px 18px; }
  #results-area .uc-standalone { min-height: 0; align-items: flex-start; padding: 18px 12px 20px; }

  /* ---- Body zone: de-box the rows (dividers only; spine kept, inset) ---- */
  .tx-cards { gap: 0; padding: 0; }
  .tx-cards > .txc-v2, .tx-cards > .txc-grp {
    border: 0; border-radius: 0; margin: 0; background: transparent; box-shadow: none;
    border-bottom: 1px solid color-mix(in srgb, var(--accent) 8%, var(--border)); position: relative;
  }
  .tx-cards > .txc-v2:last-child, .tx-cards > .txc-grp:last-child { border-bottom: 0; }
  .tx-cards > .txc-v2 { border-left: 0; padding-left: 17px; }
  .tx-cards > .txc-v2::before { content: ""; position: absolute; left: 0; top: 11px; bottom: 11px; width: 3px;
    border-radius: 0 3px 3px 0; background: var(--accent, var(--border2)); }
  .tx-cards > .txc-v2.txc-hero::before { width: 4px; }
  .tx-cards > .txc-v2.txc-epic::before { width: 5px; }
  .tx-cards > .txc-v2:active { background: color-mix(in srgb, var(--accent) 6%, transparent); }
}
