:root {
  --bg: #0e1116;
  --panel: #161b22;
  --border: #2a3139;
  --text: #e6edf3;
  --muted: #8b949e;
  --accent: #4ade80;
  --danger: #f87171;
}
* { box-sizing: border-box; }
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  background: var(--bg);
  color: var(--text);
  /* Body scrolls naturally (preserves mobile pull-to-refresh).
     Header + footer use position: sticky to stay pinned while the body
     scrolls between them. No flex container wrapping the whole page. */
}
header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 24px;
  border-bottom: 1px solid var(--border);
  background: var(--bg);
  position: sticky;
  top: 0;
  z-index: 10;
}
header h1 { font-size: 18px; margin: 0; font-weight: 600; }
.badge {
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 12px;
  background: #333;
  color: var(--muted);
}
.badge.ok { background: #0f3a22; color: var(--accent); }
.badge.status-cluster {
  padding: 2px 6px;
  font-size: 10px;
  letter-spacing: 2px;
  background: transparent;
}
.badge.status-cluster span { cursor: help; }

#diagnostic-footer {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 10px;
  padding: 8px 16px 12px;
  color: var(--muted);
  font-size: 10px;
  border-top: 1px solid var(--border);
  background: var(--bg);
  position: sticky;
  bottom: 0;
  z-index: 10;
}
#diagnostic-footer #version {
  font-family: ui-monospace, monospace;
  color: var(--muted);
}
.badge.bad { background: #3a0f0f; color: var(--danger); }
main {
  max-width: 3200px;
  margin: 0 auto;
  padding: 12px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  width: 100%;
}
main .card {
  width: 100%;
  padding: 12px 16px;
  /* 16:9 aspect ratio — scales height to card width automatically.
     Min-height floor keeps very narrow single-column layouts readable. */
  aspect-ratio: 16 / 9;
  min-height: 240px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
main .card > .scanner-grid,
main .card > .suggest,
main .card > table,
main .card > .chart-wrap {
  flex: 1;
  min-height: 0;
}
/* Small text hints at the bottom of a card shouldn't grow */
main .card > .hint,
main .card > #feeDetail,
main .card > #tradeSummary,
main .card > #scannerHint {
  flex-shrink: 0;
}
/* Chart wrapper gives Chart.js a stable box to measure and render into */
.chart-wrap {
  position: relative;
  width: 100%;
  min-height: 0;
}
.chart-wrap canvas {
  position: absolute;
  inset: 0;
  width: 100% !important;
  height: 100% !important;
}
main .card[data-card-id="trades"] > table {
  flex: 1;
  display: block;
  overflow: hidden;
  min-height: 0;
}
main .card[data-card-id="trades"] > table tbody {
  display: table;
  width: 100%;
}
main .card[data-card-id="trades"] td {
  padding: 4px 0;
  border-bottom: 1px solid var(--border);
}
main .card[data-card-id="trades"] > #tradeSummary,
main .card[data-card-id="journal"] > #journalStats,
main .card[data-card-id="tradeLog"] > #tradeLogStats {
  flex-shrink: 0;
  margin-top: 8px;
}
main .card[data-card-id="journal"] > table,
main .card[data-card-id="tradeLog"] > table {
  flex: 1;
  display: block;
  overflow-y: auto;
  min-height: 0;
  font-size: 11px;
}
main .card[data-card-id="journal"] td,
main .card[data-card-id="tradeLog"] td {
  padding: 3px 4px;
  border-bottom: 1px solid var(--border);
}
/* Column-count → aspect ratio curve. Each extra column makes cards shorter
   vertically so they fit more densely across. Widths per column roughly
   halve as columns double, so aspect ratio widens to compensate and keep
   content readable. Steps are clean whole ratios.
     1 col → 4:3  (tallest; width/height = 1.33)
     2 col → 3:2  (width/height = 1.50)
     3 col → 16:9 (width/height = 1.78) — default
     4 col → 2:1  (width/height = 2.00; shortest) */
@media (min-width: 2200px) {
  main { grid-template-columns: repeat(4, 1fr); }
  main .card { aspect-ratio: 2 / 1; }
}
/* Default (701-2200px): 3 columns + 16:9 */
@media (max-width: 1400px) {
  main { grid-template-columns: repeat(2, 1fr); }
  main .card { aspect-ratio: 3 / 2; }
}
@media (max-width: 700px) {
  main { grid-template-columns: 1fr; }
  main .card { aspect-ratio: 4 / 3; }
}
main .card[hidden] { display: none; }
.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 16px 20px;
  position: relative;
  transition: opacity 0.15s, border-color 0.15s;
}
.card.dragging { opacity: 0.4; }
.card.drag-over { border-color: var(--accent); }
.grip {
  position: absolute;
  top: 8px;
  right: 12px;
  color: var(--muted);
  font-size: 14px;
  cursor: grab;
  user-select: none;
  padding: 4px 6px;
  border-radius: 4px;
  letter-spacing: -2px;
}
.grip:hover { color: var(--text); background: #1f2937; }
.grip:active { cursor: grabbing; }
.card h2 { font-size: 14px; color: var(--muted); margin: 0 0 12px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.5px; flex-shrink: 0; }
.price { display: grid; grid-template-columns: auto 1fr auto 1fr auto 1fr; gap: 8px 16px; align-items: baseline; }
.price .label { color: var(--muted); font-size: 12px; }
.price span:not(.label) { font-variant-numeric: tabular-nums; font-size: 18px; }
table { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
td { padding: 6px 0; border-bottom: 1px solid var(--border); }
td:last-child { text-align: right; }
.hint { color: var(--muted); font-size: 12px; margin: 8px 0 0; }

.suggest-head { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
.pill {
  padding: 4px 12px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  background: #333;
  color: var(--muted);
}
.pill.small { font-size: 11px; padding: 3px 8px; }
.pill.buy { background: #0f3a22; color: var(--accent); }
.pill.sell { background: #3a0f0f; color: var(--danger); }
.pill.hold { background: #1f2937; color: #9ca3af; }
.pill.yes { background: #0f3a22; color: var(--accent); }
.pill.no { background: #3a2a0f; color: #fbbf24; }
/* Tier pills — shared across Buy and Sell windows.
   Color scheme is risk/gain-based (not direction-based):
     red    = at-loss (Sell window only — realized loss if sold now)
     orange = weak or moderate (uncertain territory)
     gray   = flat/neutral (breakeven)
     green  = strong (clearly actionable)
     pulsing green = prime / at-target (fire now) */
.pill.tier-loss { background: #3a0f0f; color: var(--danger); }         /* red — at loss */
.pill.tier-weak { background: #3a2308; color: #fb923c; }                /* orange */
.pill.tier-moderate { background: #3a2308; color: #fb923c; }           /* orange (same as weak) */
.pill.tier-flat { background: #1f2937; color: #9ca3af; }               /* gray */
.pill.tier-strong { background: #0f3a22; color: var(--accent); }       /* green */
.pill.tier-target {
  background: #0f3a22;
  color: var(--accent);
  animation: tierPulse 1.2s ease-in-out infinite;
  box-shadow: 0 0 6px rgba(74, 222, 128, 0.5);
}
@keyframes tierPulse {
  0%, 100% { box-shadow: 0 0 6px rgba(74, 222, 128, 0.4); opacity: 1; }
  50% { box-shadow: 0 0 14px rgba(74, 222, 128, 0.9); opacity: 0.85; }
}
.reason { margin: 4px 0 10px; font-size: 13px; }
.stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px 16px; font-variant-numeric: tabular-nums; }
.stats > div { display: flex; flex-direction: column; gap: 2px; }
.stats .label { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
@media (max-width: 500px) { .stats { grid-template-columns: repeat(2, 1fr); } }

.btn {
  margin-top: 12px;
  background: #1f2937;
  border: 1px solid var(--border);
  color: var(--text);
  padding: 6px 14px;
  border-radius: 6px;
  font-size: 13px;
  cursor: pointer;
}
.btn:hover { background: #2a3139; }
.btn.primary { background: #0f3a22; border-color: #1a6b3e; color: var(--accent); }
.btn.primary:hover:not(:disabled) { background: #155a33; }
.btn.primary:disabled, .btn:disabled { opacity: 0.4; cursor: not-allowed; }
.btn.sell { background: #3a0f0f; border-color: #6b1a1a; color: var(--danger); }
.actions { display: flex; gap: 8px; align-items: center; margin-top: 8px; }

dialog {
  background: var(--panel);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 24px;
  max-width: 420px;
}
dialog::backdrop { background: rgba(0, 0, 0, 0.6); }
dialog h3 { margin: 0 0 12px; font-size: 16px; }
.dialog-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 16px; }

.pnl-head {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 12px;
  margin-bottom: 10px;
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}
.pnl-head > div { display: flex; flex-direction: column; gap: 4px; }
.pnl-head .label { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
.pnl-head span:not(.label) { font-size: 16px; font-weight: 500; }
.pnl-val.up { color: var(--accent); }
.pnl-val.down { color: var(--danger); }
@media (max-width: 700px) {
  .pnl-head { gap: 4px; }
  .pnl-head span:not(.label) { font-size: 13px; }
  .pnl-head .label { font-size: 9px; }
}

.compare-legend { display: flex; flex-wrap: wrap; gap: 8px 14px; margin-top: 8px; font-size: 11px; }
.compare-legend span { display: flex; align-items: center; gap: 4px; }
.compare-legend .swatch { width: 10px; height: 3px; border-radius: 1px; }

.scanner-grid {
  display: flex;
  flex-direction: column;
  gap: 0;
  flex: 1;
  justify-content: flex-start;
  /* Rows beyond the card's available height become scrollable rather than
     overlapping the hint below. Keeps row 1 always at the top (so the best
     rarity drop / highest-profit position is instantly visible), and any
     extras are one scroll-wheel tick away. Uses the browser's native
     scrollbar styling so it's clearly visible — the earlier 6px thin
     scheme was nearly invisible and made the scroll affordance easy to miss.
  */
  overflow-y: auto;
  min-height: 0;
}
.scanner-row {
  display: grid;
  /* Three proportional columns, all left-aligned. Content within each
     column hugs the left edge; remaining space fills rightward. */
  grid-template-columns: 2fr 1fr 1.5fr;
  gap: 8px;
  align-items: center;
  padding: 0 8px;
  border-radius: 3px;
  cursor: pointer;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  height: 28px;
  box-sizing: border-box;
}
.scanner-row > span { text-align: left; }
.scanner-row .col-action .btn,
.scanner-row .col-action .scan-buy,
.scanner-row .col-action .scan-sell {
  line-height: 1.1;
  white-space: nowrap;
}
.scanner-row:hover { background: #1f2937; }
.scanner-row.active { background: #1a2332; }

/* Mobile: compress the first column (full coin name is too wide on phones and
   pushes the action button off-screen). Price/middle columns also shrink, and
   the last column takes whatever's left. */
@media (max-width: 700px) {
  .scanner-row {
    grid-template-columns: minmax(0, 1fr) auto auto;
    gap: 6px;
    padding: 0 4px;
    font-size: 11px;
  }
  .scanner-row .col-coin,
  .scanner-row .col-rsi {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
  }
  .scanner-row .col-action { justify-self: end; }
}

.open-trade { background: #1f2937; border-radius: 6px; padding: 10px 12px; margin-bottom: 12px; font-size: 13px; }
.open-trade .label { color: var(--muted); font-size: 11px; text-transform: uppercase; }
.trade-pnl.up { color: var(--accent); }
.trade-pnl.down { color: var(--danger); }

/* Read-only public view — hide all action buttons */
body.readonly .scan-buy,
body.readonly .scan-sell,
body.readonly #executeBtn,
body.readonly #depositBtn,
body.readonly #autoTradeBtn,
body.readonly #savePublicBtn,
body.readonly #reloadBtn { display: none !important; }
body.readonly header h1::after {
  content: " — shared view";
  color: var(--muted);
  font-size: 11px;
  font-weight: 400;
  margin-left: 8px;
}

.btn.auto-on { background: #0f3a22; border-color: #1a6b3e; color: var(--accent); }
.btn.auto-off { background: #1f2937; }

.stop-loss-status { display: flex; align-items: center; gap: 8px; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }

.card-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; flex-wrap: wrap; gap: 6px; flex-shrink: 0; }
.card-head h2 { margin: 0; }
.range-buttons { display: flex; gap: 4px; flex-wrap: wrap; margin-right: 36px; }
.range-btn {
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--border);
  padding: 3px 10px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 600;
  cursor: pointer;
}
.range-btn:hover { background: #1f2937; color: var(--text); }
.range-btn.active { background: #1f2937; color: var(--text); border-color: var(--text); }

/* Achievements */
.ach-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(38px, 1fr));
  gap: 3px;
  overflow: hidden;
  flex: 1;
  min-height: 0;
  padding: 1px;
  align-content: start;
}
.ach-tile {
  aspect-ratio: 1;
  border-radius: 5px;
  border: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
  cursor: help;
  background: #0f1419;
  filter: grayscale(1);
  opacity: 0.35;
  transition: transform 0.15s, opacity 0.3s, filter 0.3s;
  position: relative;
}
.ach-tile.earned { filter: none; opacity: 1; background: #1a2332; }
.ach-tile.earned.bronze { border-color: #a97142; box-shadow: 0 0 0 1px #a97142 inset; }
.ach-tile.earned.silver { border-color: #9ca3af; box-shadow: 0 0 0 1px #9ca3af inset; }
.ach-tile.earned.gold { border-color: #fbbf24; box-shadow: 0 0 0 1px #fbbf24 inset; }
.ach-tile.earned.platinum { border-color: #60a5fa; box-shadow: 0 0 0 2px #60a5fa inset; }
.ach-tile:hover { transform: scale(1.1); z-index: 2; }
.ach-tile.just-earned {
  animation: achPop 1.2s ease-out;
}
/* Progress bar inside locked tiles */
.ach-tile-bar {
  position: absolute;
  bottom: 0;
  left: 0;
  height: 3px;
  border-radius: 0 0 4px 4px;
  background: var(--accent);
  opacity: 0.75;
  transition: width 0.4s ease;
  pointer-events: none;
}
/* On-deck section below the achievement grid */
.ach-ondeck {
  padding: 6px 0 0;
  border-top: 1px solid var(--border);
  margin-top: 4px;
}
.ach-ondeck-title {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
  margin-bottom: 4px;
}
.ach-ondeck-row {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 3px 0;
  font-size: 11px;
}
.ach-ondeck-emoji { font-size: 14px; flex-shrink: 0; }
.ach-ondeck-name {
  flex-shrink: 0;
  color: var(--text);
  min-width: 80px;
  max-width: 80px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ach-ondeck-bar-wrap {
  flex: 1;
  height: 5px;
  background: var(--border);
  border-radius: 3px;
  overflow: hidden;
}
.ach-ondeck-bar-fill {
  height: 100%;
  border-radius: 3px;
  background: var(--accent);
  transition: width 0.4s ease;
}
.ach-ondeck-pct {
  font-size: 9px;
  color: var(--muted);
  flex-shrink: 0;
  min-width: 30px;
  text-align: right;
}
@keyframes achPop {
  0% { transform: scale(0.3); filter: brightness(3); }
  50% { transform: scale(1.4); filter: brightness(2); }
  100% { transform: scale(1); filter: none; }
}

/* Signal calibration chart */
.cal-title {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
  margin-bottom: 5px;
}
.cal-band {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-bottom: 2px;
  font-size: 10px;
}
.cal-label {
  width: 54px;
  color: var(--muted);
  flex-shrink: 0;
}
.cal-bar-wrap {
  flex: 1;
  height: 6px;
  background: var(--border);
  border-radius: 3px;
  overflow: hidden;
}
.cal-bar-fill {
  height: 100%;
  border-radius: 3px;
  transition: width 0.4s ease;
}
.cal-wr {
  width: 32px;
  text-align: right;
  flex-shrink: 0;
}
.cal-n {
  width: 40px;
  color: var(--muted);
  font-size: 9px;
  flex-shrink: 0;
  text-align: right;
}
.cal-insight {
  font-size: 10px;
  margin-top: 5px;
  padding: 4px 6px;
  border-radius: 4px;
  border-left: 2px solid;
}
.cal-insight.inverted {
  background: rgba(251,191,36,0.08);
  border-color: #fbbf24;
  color: #fbbf24;
}
.cal-insight.normal {
  background: rgba(74,222,128,0.06);
  border-color: var(--accent);
  color: var(--accent);
}

/* ── Weekly Digest panel ─────────────────────────────── */
.digest-title {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--muted);
  margin-bottom: 8px;
}
.digest-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px 12px;
  margin-bottom: 10px;
}
.digest-cell { display: flex; flex-direction: column; gap: 1px; }
.digest-cell .d-label { font-size: 10px; color: var(--muted); }
.digest-cell .d-val   { font-size: 15px; font-weight: 600; }
.digest-cell .d-val.pos { color: var(--accent); }
.digest-cell .d-val.neg { color: var(--danger); }
.digest-cell .d-val.neu { color: var(--text); }
.digest-ach {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 4px;
}
.digest-ach-chip {
  font-size: 11px;
  padding: 2px 7px;
  border-radius: 12px;
  background: rgba(99,102,241,.15);
  border: 1px solid rgba(99,102,241,.3);
  color: #a5b4fc;
  white-space: nowrap;
}
.digest-best-day {
  font-size: 11px;
  color: var(--muted);
  margin-top: 6px;
}
.digest-best-day span { color: var(--accent); font-weight: 600; }

/* Achievement unlock toast */
.ach-toast {
  position: fixed;
  top: 20px;
  left: 50%;
  transform: translateX(-50%) translateY(-120%);
  background: linear-gradient(135deg, #0f3a22, #155a33);
  border: 2px solid #fbbf24;
  color: #fff;
  padding: 16px 28px;
  border-radius: 12px;
  box-shadow: 0 10px 40px rgba(0,0,0,0.6), 0 0 30px rgba(251, 191, 36, 0.4);
  z-index: 10000;
  display: flex;
  align-items: center;
  gap: 16px;
  font-family: inherit;
  min-width: 320px;
  transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
  pointer-events: none;
}
.ach-toast.show { transform: translateX(-50%) translateY(0); }
.ach-toast .emoji { font-size: 40px; }
.ach-toast .meta { display: flex; flex-direction: column; gap: 2px; }
.ach-toast .title-line { font-size: 11px; color: #fbbf24; text-transform: uppercase; letter-spacing: 1px; font-weight: 700; }
.ach-toast .name { font-size: 16px; font-weight: 600; }

/* ═══════════════════════════════════════════════════════════════════════════
   LIVE ANIMATION LAYER  ·  v2.2.98
   Video-game-style feedback: trade stamps, screen flash, live chart dots,
   net-% row flashes, abort button.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1. Full-screen flash on trade fill ─────────────────────────────────── */
.trade-flash-overlay {
  position: fixed; inset: 0; z-index: 9998; pointer-events: none;
  opacity: 0; transition: opacity 80ms ease-in; will-change: opacity;
}
.trade-flash-overlay.active { opacity: 0.16; }
.trade-flash-overlay.buy      { background: radial-gradient(ellipse at center, #4ade80 0%, transparent 65%); }
.trade-flash-overlay.sell     { background: radial-gradient(ellipse at center, #60a5fa 0%, transparent 65%); }
.trade-flash-overlay.stoploss { background: radial-gradient(ellipse at center, #f87171 0%, transparent 65%); }

/* ── 2. Trade stamp ─────────────────────────────────────────────────────── */
.trade-stamp {
  position: fixed; top: 45%; left: 50%;
  transform: translate(-50%, -50%) scale(0.2);
  z-index: 9999; pointer-events: none;
  background: linear-gradient(160deg, #161b22, #0d1117);
  border-radius: 14px; padding: 22px 36px;
  text-align: center; font-family: inherit;
  animation: stampPop 2.6s cubic-bezier(0.34,1.56,0.64,1) forwards;
}
.trade-stamp.buy  { border: 2px solid #4ade80; box-shadow: 0 8px 48px rgba(0,0,0,0.85), 0 0 36px rgba(74,222,128,0.4); }
.trade-stamp.sell { border: 2px solid #60a5fa; box-shadow: 0 8px 48px rgba(0,0,0,0.85), 0 0 36px rgba(96,165,250,0.4); }
.trade-stamp.stoploss { border: 2px solid #f87171; box-shadow: 0 8px 48px rgba(0,0,0,0.85), 0 0 36px rgba(248,113,113,0.4); }
.trade-stamp .stamp-side {
  font-size: 11px; letter-spacing: 3px; font-weight: 800;
  text-transform: uppercase; margin-bottom: 8px;
}
.trade-stamp.buy      .stamp-side { color: #4ade80; }
.trade-stamp.sell     .stamp-side { color: #60a5fa; }
.trade-stamp.stoploss .stamp-side { color: #f87171; }
.trade-stamp .stamp-coin  { font-size: 30px; font-weight: 700; color: #e6edf3; line-height: 1.1; }
.trade-stamp .stamp-price { font-size: 12px; color: #8b949e; margin-top: 4px; }
.trade-stamp .stamp-amt   { font-size: 11px; color: #6e7681; margin-top: 2px; }
.trade-stamp .stamp-pnl   { font-size: 16px; font-weight: 700; margin-top: 10px; letter-spacing: 0.5px; }
.trade-stamp .stamp-pnl.pos { color: #4ade80; }
.trade-stamp .stamp-pnl.neg { color: #f87171; }
@keyframes stampPop {
  0%   { transform: translate(-50%,-65%) scale(0.2); opacity: 0; }
  12%  { transform: translate(-50%,-50%) scale(1.07); opacity: 1; }
  22%  { transform: translate(-50%,-50%) scale(0.97); opacity: 1; }
  30%  { transform: translate(-50%,-50%) scale(1);    opacity: 1; }
  72%  { transform: translate(-50%,-50%) scale(1);    opacity: 1; }
  100% { transform: translate(-50%,-38%) scale(0.88); opacity: 0; }
}

/* ── 3. Scanner sell-row net-% flash on price change ───────────────────── */
@keyframes netPctUp {
  0%   { background: rgba(74,222,128,0.28); border-radius: 4px; }
  100% { background: transparent; }
}
@keyframes netPctDown {
  0%   { background: rgba(248,113,113,0.28); border-radius: 4px; }
  100% { background: transparent; }
}
.net-tick-up   { animation: netPctUp   1.1s ease-out forwards; }
.net-tick-down { animation: netPctDown 1.1s ease-out forwards; }

/* ── 4. Compare-chart live dot (DOM overlay) ────────────────────────────── */
.chart-live-dot-wrap {
  position: absolute; top: 0; left: 0; width: 100%; height: 100%;
  pointer-events: none; overflow: hidden; z-index: 5;
}
.chart-live-dot {
  position: absolute; width: 7px; height: 7px;
  border-radius: 50%; transform: translate(-50%, -50%);
  pointer-events: none; will-change: transform;
}
.chart-live-dot::after {
  content: ''; position: absolute; inset: -7px; border-radius: 50%;
  border: 1.5px solid currentColor; opacity: 0;
  animation: liveDotRing 2.2s ease-out infinite;
}
@keyframes liveDotRing {
  0%   { transform: scale(0.4); opacity: 0.8; }
  100% { transform: scale(3.2); opacity: 0; }
}

/* ── 5. Chart container heartbeat on SSE data arrival ───────────────────── */
@keyframes chartHeartbeat {
  0%   { box-shadow: 0 0 0 0 rgba(74,222,128,0); }
  25%  { box-shadow: 0 0 0 3px rgba(74,222,128,0.4); }
  100% { box-shadow: 0 0 0 0 rgba(74,222,128,0); }
}
.chart-beat { animation: chartHeartbeat 0.7s ease-out; }

/* ── 6. Global abort button ──────────────────────────────────────────────── */
.global-abort-btn {
  position: fixed; bottom: 24px; left: 24px; z-index: 9000;
  padding: 9px 18px; background: rgba(239,68,68,0.12);
  border: 1.5px solid #ef4444; color: #f87171;
  border-radius: 8px; font-size: 11px; font-weight: 700;
  letter-spacing: 1.5px; cursor: pointer; font-family: inherit;
  text-transform: uppercase; transition: background 0.15s, color 0.15s, box-shadow 0.15s;
  animation: abortGlow 1.8s ease-in-out infinite; user-select: none;
}
.global-abort-btn:hover {
  background: rgba(239,68,68,0.32); color: #fff; animation: none;
  box-shadow: 0 0 20px rgba(239,68,68,0.55);
}
.global-abort-btn .abort-count {
  display: inline-block; background: #ef4444; color: #fff;
  border-radius: 10px; padding: 0 5px; font-size: 10px;
  margin-left: 6px; vertical-align: 1px; line-height: 1.5;
}
@keyframes abortGlow {
  0%, 100% { box-shadow: 0 0 5px rgba(239,68,68,0.3); }
  50%       { box-shadow: 0 0 16px rgba(239,68,68,0.7); }
}
.ach-toast .desc { font-size: 12px; color: #cbd5e1; }
