/* ───────────────────────────────────────────────────────────────
   Self-hosted Inter (variable, weights 100–900). Replaces the Google
   Fonts CDN link so the font is served from /static/vendor with the
   long-lived immutable cache (no extra DNS/TLS to fonts.gstatic.com).
   Latin + Cyrillic subsets — the UI is Russian. unicode-range lets the
   browser fetch only the subsets a glyph actually needs.
   ─────────────────────────────────────────────────────────────── */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url(vendor/inter-latin-wght-normal.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url(vendor/inter-latin-ext-wght-normal.woff2) format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url(vendor/inter-cyrillic-wght-normal.woff2) format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url(vendor/inter-cyrillic-ext-wght-normal.woff2) format("woff2");
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

/* ───────────────────────────────────────────────────────────────
   Mini App theme — dark + orange (mining-app inspired).
   Chart colours (--bg/--grid/--border/--up/--down/--tv) are read by
   app.js (lightweight-charts + canvas thumbs), so keep those var NAMES.
   ─────────────────────────────────────────────────────────────── */
:root {
  /* surfaces */
  --bg: #141414;           /* chart area + base (read by app.js) */
  --bg-top: #1e1e1e;       /* page gradient top */
  --surface: #1c1c1c;      /* cards */
  --surface-2: #242424;    /* chips / lifted controls */
  --grid: #1b1b1b;         /* chart grid — low-contrast so candles carry the eye (read by app.js) */
  --border: #2a2a2a;       /* hairline strokes (read by app.js) */
  --border-soft: #212121;

  /* brand */
  --accent: #ff8a00;       /* orange */
  --accent-2: #ffa224;     /* orange highlight (gradient top) */
  --accent-press: #e67c00;
  --on-accent: #ffffff;

  /* candles (read by app.js) */
  --up: #2bd47e;
  --down: #ff5466;

  /* text */
  --title: #ffffff;
  --text: #e8e8e8;
  --neutral: #d2d2d2;
  --muted: #8a8a8a;        /* axis ticks / labels (read by app.js) */
  --stat-header: #6a6a6a;
  --level: #b0b3c0;

  /* measure tool accent → orange (read by app.js as --tv) */
  --tv: #ff8a00;

  /* One radius scale used across the whole app: panels/cards · inputs/tiles ·
     controls/rows · small tags/tooltips · pills(999). Every border-radius in the
     app resolves to one of these — no off-scale one-offs. */
  --radius: 1rem;
  --radius-sm: 0.75rem;
  --radius-xs: 0.625rem;
  --radius-2xs: 0.5rem;

  /* glass surfaces — translucent + blur, reference-inspired */
  --glass: rgba(38, 38, 38, 0.55);          /* lifted controls (chips/buttons) */
  --glass-strong: rgba(26, 26, 26, 0.72);   /* menus / floating nav */
  --border-glass: rgba(255, 255, 255, 0.07);/* hairline light edge on glass */
  --shadow-soft: 0 10px 30px rgba(0, 0, 0, 0.45);

  /* ── Safe areas — Telegram fullscreen (Bot API 8.0) ──────────────────────────
     In fullscreen the client drops its own header and hands the app the WHOLE
     screen, drawing the ✕/⋮ controls straight over the page. Two independent
     insets have to be cleared and they STACK:
       --tg-safe-area-inset-*          device chrome (status bar, notch, gesture bar)
       --tg-content-safe-area-inset-*  Telegram's floating controls
     Both are published by the client as CSS vars; app.js mirrors the SDK objects
     into the same names so this formula stays the single source of truth on every
     client. Old clients leave them unset → every term falls back to 0px, i.e. the
     pre-fullscreen layout, byte for byte. env() keeps iOS correct when the page is
     opened outside Telegram (browser testing). */
  --sa-top: max(var(--tg-safe-area-inset-top, 0px), env(safe-area-inset-top, 0px));
  --sa-bottom: max(var(--tg-safe-area-inset-bottom, 0px), env(safe-area-inset-bottom, 0px));
  --sa-left: max(var(--tg-safe-area-inset-left, 0px), env(safe-area-inset-left, 0px));
  --sa-right: max(var(--tg-safe-area-inset-right, 0px), env(safe-area-inset-right, 0px));
  /* Floor for the Telegram-controls inset. Telegram DESKTOP reports 0 here yet still
     floats ✕/⋮ over the top-right — exactly where the screener parks its tool row —
     so fullscreen reserves a minimum strip regardless of what the client reports.
     max() means a client that reports a real (larger) inset still wins. */
  --fs-floor: 0px;
  --chrome-top: max(var(--tg-content-safe-area-inset-top, 0px), var(--fs-floor));
  --pad-top: calc(var(--sa-top) + var(--chrome-top));
}

/* Set by app.js from tg.isFullscreen (and kept in sync via fullscreenChanged). */
html[data-fullscreen="1"] { --fs-floor: 2.75rem; }

* {
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
  /* Kill the mobile long-press callout ("Открыть в браузере / Сохранить
     изображение …") across the whole app — a long press on any image, svg,
     canvas chart or label must never yank the user out into the browser. */
  -webkit-touch-callout: none;
}

/* A Mini App should feel native: no accidental text/image selection or drag on
   a long press. Selection is re-enabled only where the user actually types. */
#app, #app * {
  -webkit-user-select: none;
  user-select: none;
  -webkit-user-drag: none;
}
img, svg, canvas {
  -webkit-user-drag: none;
  user-drag: none;
}
input, textarea, [contenteditable="true"] {
  -webkit-user-select: text;
  user-select: text;
}

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow: hidden;
  /* ── Cross-device proportional scaling ──────────────────────────────────────
     The whole layout is expressed in rem, so this ONE fluid root font-size scales
     every element together as a single unit. It is keyed to viewport HEIGHT: the
     app is a full-height flex column with a greedy flex:1 chart, so on a taller
     device the fixed chrome would otherwise occupy a smaller share while the chart
     ballooned. Growing the root with height keeps the chrome/chart proportions
     constant across phones, tablets and desktop — independent of pixel density or
     resolution (CSS px already normalise DPR; rem removes the remaining aspect
     drift). The 16px FLOOR is the reference base: the Telegram *desktop* webview is
     short (~600px), where 2.3vh would fall below 16 — pinning the min at 16px keeps
     the PC render pixel-identical to the original, and nothing ever renders smaller
     than the reference. Taller phones scale UP toward the 19px cap so the fixed
     chrome grows and the greedy flex chart can't dominate. text-size-adjust pins
     text so Android's display-size/font-scale settings can't inflate the UI
     independently of the layout. */
  font-size: clamp(16px, 2.3vh, 19px);
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

#app {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
  /* Darker page gradient: --bg-top (== native header/bottom-bar chrome) fades into the
     --bg chart body. The top edge equals the native header colour so header→app is
     seamless; the body stays dark. Telegram's setBackgroundColor is set to the SAME
     --bg-top so the thin seam it paints under the header (and the desktop @bot strip)
     matches this gradient top instead of showing as a darker line. */
  /* Top edge == --bg-top == the native Telegram chrome tone (setBackgroundColor), so the
     header→app join is seamless; it fades into the darker --bg body. This exact match is
     load-bearing — do NOT lighten the 0% stop or a seam appears under the native header. */
  background: linear-gradient(180deg, var(--bg-top) 0%, var(--bg) 38%);
  /* Fullscreen: the app owns the status bar and the strip under Telegram's floating
     ✕/⋮ controls, so it has to inset itself — nothing else reserves that space. The
     gradient still paints across the padding box, which is what keeps the reserved
     strip the --bg-top chrome tone instead of a bare band. Horizontal insets matter
     in landscape fullscreen (notch on the side). Outside fullscreen every term is
     0px and this is a no-op. */
  padding-top: var(--pad-top);
  padding-left: var(--sa-left);
  padding-right: var(--sa-right);
}

.view {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.hidden { display: none !important; }

/* ── Top bar (feed) ──────────────────────────────────────── */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.875rem 1rem 0.75rem;
}

.topbar-title {
  font-size: 1.25rem;
  font-weight: 800;
  color: var(--title);
  letter-spacing: 0.0125rem;
}

/* ── Generic round icon button ───────────────────────────── */
.icon-btn {
  flex: 0 0 auto;
  width: 2.375rem;
  height: 2.375rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.25rem;
  line-height: 1;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, transform 0.08s;
}
.icon-btn:active { background: #303030; transform: scale(0.94); }

/* Back is secondary navigation — a borderless chevron, not a heavy 38px square that
   out-weighs the ticker. Tap target kept at 34px; the glyph is optically pulled toward
   the 16px page gutter since it no longer has a box. */
#back-btn {
  width: 2.125rem;
  height: 2.125rem;
  margin-left: -0.4375rem;
  font-size: 1.625rem;
  padding-bottom: 0.1875rem;
  background: transparent;
  border-color: transparent;
  color: var(--muted);
}
#back-btn:active { background: transparent; color: var(--text); }

/* ── Detail header ───────────────────────────────────────── */
/* Back chevron pinned to the left gutter, the ticker CENTERED in the row (like before).
   Nothing else lives here — the AI-разбор pill floats over the chart and the timeframe
   sits on the snapshot-strip tools row. */
.header {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  padding: 0.25rem 1rem 0;
  background: transparent;
}
/* Centered ticker title — plain, no chip frame. Takes the middle column; a spacer the
   same width as the back chevron balances the right so the symbol is TRULY centered.
   Truncates with «…» only if a very long symbol would otherwise collide with the edges. */
.detail-sym {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  overflow: hidden;
}
/* Zero the gutter-pull margin here so the back box is a clean 34px, symmetrical with the
   spacer below → the centred ticker lands on the true page centre. */
.header #back-btn { margin-left: 0; }
/* Invisible right-hand counterweight = back-btn box, so «justify-content:center» inside
   .detail-sym lands the ticker on the page's true centre. */
.header::after {
  content: "";
  flex: 0 0 auto;
  width: 2.125rem;
}
.detail-tools { display: flex; align-items: center; gap: 0.3125rem; flex: 0 0 auto; }

.symbol {
  font-size: 1.1875rem;
  font-weight: 800;
  letter-spacing: 0.0125rem;
  color: var(--title);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Role + outcome + recommendation badges — a tight LEFT-aligned row under the header,
   flat like the screener's chips. Its own horizontal padding now that it sits outside
   the header. */
/* All badges stay on ONE row — never wrap a pill down to a second line. If the set is
   wider than the screen it scrolls sideways (hidden scrollbar) instead of stacking. */
.title-badges {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  /* Badges sit CENTERED as a group — no empty gap stretched across the row. «safe center»
     keeps them centered when they fit and falls back to left-anchored (nothing clipped)
     when the set is wider than the screen, where it scrolls sideways instead. */
  justify-content: safe center;
  gap: 0.3125rem;
  padding: 0.375rem 1rem 0;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.title-badges::-webkit-scrollbar { display: none; }
/* Each pill keeps its natural width and shows its full text — never truncated with «…».
   If the whole set overflows, the row scrolls (above) rather than clipping any label. */
.title-badges .badge {
  flex: 0 0 auto;
  white-space: nowrap;
}

.badge {
  font-size: 0.6875rem;
  font-weight: 600;
  padding: 0.25rem 0.625rem;
  border-radius: 62.4375rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--muted);
  white-space: nowrap;
}
/* Detail header badges: slimmer, lighter pills (less «box») — equal height, a single
   pill radius and a soft hairline so the row reads as one clean, modern set. Kept compact
   so the plate stays small and the two pills sit evenly on one tidy line. */
.title-badges .badge {
  display: inline-flex;
  align-items: center;
  line-height: 1;
  font-size: 0.625rem;
  padding: 0.1875rem 0.4375rem;
  border-color: var(--border-soft);
}
.badge-dir { color: var(--text); }
/* Density outcome badge: green when the wall held (отскок), red on a пробой. */
.badge.up { color: var(--up); }
.badge.down { color: var(--down); }
.badge.skip { color: var(--accent); }

/* Badge hierarchy in the detail title row: the role (Уровень сопротивления) is a QUIET
   ghost chip — transparent fill, just a hairline — while the recommendation and the
   resolved outcome carry a soft tinted fill so the eye lands on the conclusion first. */
.title-badges .badge-dir {
  color: var(--neutral);
  background: transparent;
}
.title-badges #rec-call,
.title-badges #density-outcome { font-weight: 700; }
.title-badges #rec-call.up,
.title-badges #density-outcome.up {
  color: var(--up);
  background: rgba(43, 212, 126, 0.12);
  border-color: rgba(43, 212, 126, 0.26);
}
.title-badges #rec-call.down,
.title-badges #density-outcome.down {
  color: var(--down);
  background: rgba(255, 84, 102, 0.12);
  border-color: rgba(255, 84, 102, 0.28);
}
.title-badges #rec-call.skip {
  color: var(--accent);
  background: rgba(255, 138, 0, 0.12);
  border-color: rgba(255, 138, 0, 0.28);
}

/* ── Timeframe selector (segmented pills) ────────────────── */
.tfbar {
  display: flex;
  gap: 0.4375rem;
  margin-top: 0.875rem;
}

/* Detail bar: equal-width buttons filling the row. */
#tfbar .tf-btn {
  flex: 1 1 0;
  min-width: 0;
  padding-left: 0;
  padding-right: 0;
  text-align: center;
}

.tf-btn {
  flex: 0 0 auto;
  font-family: inherit;
  font-size: 0.75rem;
  font-weight: 600;
  padding: 0.4375rem 0.8125rem;
  border-radius: 62.4375rem;
  background: var(--glass);
  border: 1px solid var(--border-glass);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: var(--muted);
  cursor: pointer;
  transition: background 0.14s, color 0.14s, border-color 0.14s;
}
.tf-btn:hover { color: var(--text); }

.tf-btn.active {
  color: var(--on-accent);
  background: linear-gradient(180deg, var(--accent-2), var(--accent));
  border-color: transparent;
  box-shadow: 0 3px 12px rgba(255, 138, 0, 0.28);
}

/* ── Timeframe dropdown (compact selector) ───────────────── */
.tf-dd { position: relative; flex: 0 0 auto; }

.tf-dd-btn {
  display: flex;
  align-items: center;
  gap: 0.4375rem;
  font-family: inherit;
  font-size: 0.8125rem;
  font-weight: 700;
  padding: 0.4375rem 0.8125rem;
  border-radius: 62.4375rem;
  background: var(--glass);
  border: 1px solid var(--border-glass);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: var(--title);
  cursor: pointer;
  transition: border-color 0.14s;
}
.tf-dd-btn .tf-dd-caret {
  font-size: 0.5625rem;
  color: var(--accent);
  transition: transform 0.16s;
}
.tf-dd.open .tf-dd-btn { border-color: var(--accent); }
.tf-dd.open .tf-dd-caret { transform: rotate(180deg); }

.tf-dd-menu {
  display: none; /* opened by toggling .tf-dd.open (see below) */
  position: absolute;
  right: 0;
  top: calc(100% + 0.4375rem);
  z-index: 30;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.375rem;
  padding: 0.5625rem;
  min-width: 10.75rem;
  background: var(--glass-strong);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
}
.tf-dd.open .tf-dd-menu { display: grid; }

.tf-dd-item {
  font-family: inherit;
  font-size: 0.75rem;
  font-weight: 600;
  padding: 0.5625rem 0;
  border-radius: var(--radius-xs);
  background: var(--glass);
  border: 1px solid var(--border-glass);
  color: var(--text);
  cursor: pointer;
  text-align: center;
}
.tf-dd-item.active {
  color: var(--on-accent);
  background: linear-gradient(180deg, var(--accent-2), var(--accent));
  border-color: transparent;
}

/* «Статистика» period picker — a dedicated calendar-aware panel, not the flat TF grid.
   Flat + solid per the design system (no glass): quick presets that show their real dates,
   then a past-month grid. The panel animates open with opacity/translate (display can't
   transition), gated on .open; visibility keeps it out of the tab order when closed. */
#stats-range .stats-picker {
  display: block;
  grid-template-columns: none;
  right: 0;
  left: auto;
  width: min(21rem, calc(100vw - 1.5rem));
  min-width: 0;
  max-height: 74vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: 0.875rem;
  gap: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  visibility: hidden;
  opacity: 0;
  transform: translateY(-0.375rem);
  pointer-events: none;
  transition: opacity 0.18s ease, transform 0.2s cubic-bezier(0.22, 1, 0.36, 1);
}
#stats-range.tf-dd.open .stats-picker {
  visibility: visible;
  opacity: 1;
  transform: none;
  pointer-events: auto;
}
@media (prefers-reduced-motion: reduce) {
  #stats-range .stats-picker {
    transition: opacity 0.12s ease;
    transform: none;
  }
}

.stats-pick-eyebrow {
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--stat-header);
  margin-bottom: 0.5rem;
}
.stats-pick-eyebrow ~ .stats-pick-eyebrow,
.stats-pick-presets + .stats-pick-eyebrow {
  margin-top: 0.875rem;
}

.stats-pick-presets {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5rem;
}
.stats-pick-tile {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.1875rem;
  padding: 0.625rem 0.6875rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-xs);
  cursor: pointer;
  text-align: left;
  transition: border-color 0.14s, background 0.14s;
}
.stats-pick-name {
  font-size: 0.8125rem;
  font-weight: 700;
  color: var(--title);
}
.stats-pick-range {
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--muted);
}
.stats-pick-tile.active {
  background: linear-gradient(180deg, var(--accent-2), var(--accent));
  border-color: transparent;
  box-shadow: 0 3px 12px rgba(255, 138, 0, 0.28);
}
.stats-pick-tile.active .stats-pick-name { color: var(--on-accent); }
.stats-pick-tile.active .stats-pick-range { color: rgba(255, 255, 255, 0.85); }

.stats-pick-year {
  font-size: 0.6875rem;
  font-weight: 700;
  color: var(--muted);
  margin: 0.625rem 0 0.375rem;
}
.stats-pick-months {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.375rem;
}
.stats-pick-mchip {
  padding: 0.4375rem 0;
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-xs);
  cursor: pointer;
  text-align: center;
  transition: border-color 0.14s, background 0.14s;
}
.stats-pick-mchip.active {
  background: linear-gradient(180deg, var(--accent-2), var(--accent));
  border-color: transparent;
  color: var(--on-accent);
  box-shadow: 0 3px 12px rgba(255, 138, 0, 0.28);
}

.stats-pick-hint {
  margin-top: 0.875rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--border);
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--stat-header);
  line-height: 1.35;
}

@media (hover: hover) {
  .stats-pick-tile:not(.active):hover,
  .stats-pick-mchip:not(.active):hover { border-color: var(--accent); }
}

/* ── Stats / alert-moment snapshot ───────────────────────── */
/* Detail «Данные на момент алерта» — a slim strip flush above the chart, connected by
   a bottom hairline exactly like the screener's market-stats row. A quiet eyebrow
   caption sits on the left, the ruler + center controls on the right; the metric row
   below spans the full width (reusing the global .sstat typography). No card box. */
/* Structurally mirrors the screener head+stats: the caption/tools row is the "header"
   (hairline below it), and the metric row sits between two hairlines exactly like the
   screener's .screener-head → .screener-stats → chart stack. No outer box. */
.snap-strip {
  flex: 0 0 auto;
}
.snap-strip-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.625rem 1rem;
  border-bottom: 1px solid var(--border-soft);
}
.snap-strip-cap {
  font-size: 0.5625rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.0375rem;
  color: var(--stat-header);
  white-space: nowrap;
  /* The tools group on the right is fixed-width; let the caption shrink/ellipsize on
     narrow screens instead of pushing the tools off the row. */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.snap-tools { display: flex; align-items: center; gap: 0.3125rem; flex: 0 0 auto; }
/* TF pill on the snapshot strip: compact + solid so it sits level with the ruler square,
   matching the screener's flat timeframe pill. */
.snap-tools .tf-dd-btn {
  font-size: 0.75rem;
  padding: 0.375rem 0.6875rem;
  background: var(--surface-2);
  border-color: var(--border-soft);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.snap-tools .tf-dd.open .tf-dd-btn { border-color: var(--accent); }

/* The metric row is styled identically to the screener's .screener-stats: same padding,
   same bottom hairline (so it meets the chart exactly like the screener), 4 readouts
   spread evenly, tabular digits, horizontal-scroll fallback with no visible scrollbar. */
.stats {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: 0.375rem;
  padding: 0.5rem 1rem;
  border-bottom: 1px solid var(--border-soft);
  font-variant-numeric: tabular-nums;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.stats::-webkit-scrollbar { display: none; }

/* Feed-card snapshot — a quiet eyebrow caption + the SAME flat .sstat metric row used
   on the screener and the detail strip (no box/frame). */
.snap-cap {
  font-size: 0.5625rem;
  text-transform: uppercase;
  letter-spacing: 0.05rem;
  color: var(--stat-header);
  margin-bottom: 0.5625rem;
}
/* The metric row itself is the SAME .sstat readout the screener header uses (shared
   markup), so the plate looks identical everywhere; here it's spread evenly inside the
   snapshot card. Reuses the global .sstat / .sstat-l / .sstat-v typography. */
.snap-stats {
  display: flex;
  flex-direction: row;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.375rem;
  font-variant-numeric: tabular-nums;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.snap-stats::-webkit-scrollbar { display: none; }

/* ── Chart ───────────────────────────────────────────────── */
.chart { flex: 1; min-height: 0; position: relative; }
.chart.measuring { touch-action: none; }
/* Drawing/editing on the detail chart suspends touch-scroll the same way the screener
   does (.screener-chart.drawing/.editing) so a draw gesture isn't stolen by the page. */
.chart.drawing { touch-action: none; }
.chart.editing { touch-action: none; cursor: grabbing; }
.chart a { display: none !important; }

/* The ruler + center controls in .snap-tools reuse the screener's .tool-btn / .tool-ico
   styling verbatim, so they look identical to the screener's header tools. */

/* ── «ИИ-анализ»: floating pill + bottom sheet ───────────── */
/* «AI-разбор» — a small accent pill floating over the chart's top-right corner (like a
   charting app's tools button). Compact so it barely covers any candles; the solid accent
   fill alone carries it as the primary action — flat, no shadow (see Elevation in DESIGN.md). */
.ai-btn {
  position: absolute;
  top: 0.625rem;
  right: 0.625rem;
  z-index: 20;
  height: 1.625rem;
  padding: 0 0.625rem;
  border-radius: 62.4375rem;
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  font-size: 0.6875rem;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  color: var(--on-accent);
  background: var(--accent);
  border: 1px solid transparent;
  touch-action: manipulation;
}
.ai-btn:active { background: var(--accent-press); }

.ai-panel {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 8;
  /* Tall enough that the whole card (Рекомендация…Сумма входа) fits without an
     inner scroll on desktop, where the viewport runs a bit shorter than a phone. */
  max-height: 94%;
  display: flex;
  flex-direction: column;
  /* Flat solid sheet, one tone up from the chart base (see Elevation in DESIGN.md):
     the top hairline carries the separation — no shadow, no blur, no translucency. */
  background: var(--surface);
  border-top: 1px solid var(--border);
  border-radius: var(--radius) var(--radius) 0 0;
  animation: ai-slide-up 0.18s ease-out;
}
@keyframes ai-slide-up {
  from { transform: translateY(14px); opacity: 0; }
  to { transform: none; opacity: 1; }
}

/* The «ИИ-анализ» title is a quiet eyebrow — the recommendation below is the hero,
   not this header. A hairline separates the header from the content. */
.ai-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.75rem 1rem 0.625rem;
  border-bottom: 1px solid var(--border);
}
.ai-panel-title {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.03125rem;
  text-transform: uppercase;
  color: var(--muted);
}
.ai-close {
  width: 1.75rem; height: 1.75rem;
  border-radius: var(--radius-2xs);
  border: 1px solid var(--border);
  background: rgba(255, 255, 255, 0.04);
  color: var(--muted);
  font-size: 0.875rem;
  cursor: pointer;
}

.ai-body {
  overflow-y: auto;
  padding: 0.75rem 1rem 1rem;
  -webkit-overflow-scrolling: touch;
}
.ai-block { margin-bottom: 0; }

/* ── Recommendation hero — the visual center of the whole card ──
   Direction reads big (word + arrow, tone-coloured); the reason + reliability
   are the supporting lines under it. */
.ai-rec { margin-top: 0.125rem; }
.ai-rec-eyebrow {
  display: flex; align-items: center; gap: 0.3125rem;
  font-size: 0.625rem; font-weight: 600; letter-spacing: 0.025rem;
  text-transform: uppercase; color: var(--muted);
}
.ai-rec-ico { font-size: 0.6875rem; }
.ai-rec-dir { display: flex; align-items: baseline; gap: 0.4375rem; margin-top: 0.3125rem; }
.ai-rec-arrow { font-size: 1.25rem; font-weight: 800; line-height: 1; }
.ai-rec-word { font-size: 1.25rem; font-weight: 800; line-height: 1; letter-spacing: -0.025rem; }
.ai-rec-sub { font-size: 0.75rem; font-weight: 600; color: var(--muted); }
.ai-rec-up .ai-rec-arrow, .ai-rec-up .ai-rec-word { color: var(--up); }
.ai-rec-down .ai-rec-arrow, .ai-rec-down .ai-rec-word { color: var(--down); }
.ai-rec-mid .ai-rec-arrow, .ai-rec-mid .ai-rec-word { color: var(--accent); }

/* Reason line ("Пробой вниз — шорт.") sits under the hero direction */
.ai-lead { margin: 0.5rem 0 0; font-size: 0.8125rem; line-height: 1.4; font-weight: 600; color: var(--text); }

/* Labelled reliability line (replaces the cryptic lone «Средняя» chip) */
.ai-reliab {
  display: inline-flex; align-items: center; gap: 0.375rem;
  margin-top: 0.4375rem;
  font-size: 0.75rem; color: var(--muted);
}
.ai-reliab b { font-weight: 700; text-transform: capitalize; }
.ai-reliab-dot { width: 0.375rem; height: 0.375rem; border-radius: 50%; background: var(--muted); }
.ai-reliab-up b { color: var(--up); }
.ai-reliab-up .ai-reliab-dot { background: var(--up); }
.ai-reliab-mid b { color: var(--accent); }
.ai-reliab-mid .ai-reliab-dot { background: var(--accent); }
.ai-reliab-down b { color: var(--down); }
.ai-reliab-down .ai-reliab-dot { background: var(--down); }

/* ── Вход / Цель / Стоп — one unified tile group (numbers are the accent) ── */
.ai-targets { display: flex; gap: 0.4375rem; margin-top: 0.75rem; }
.ai-tgt {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 0.1875rem;
  padding: 0.5625rem 0.625rem;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
}
.ai-tgt-k { font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.0125rem; color: var(--muted); }
.ai-tgt-v { font-size: 0.875rem; font-weight: 800; letter-spacing: -0.01875rem; color: var(--title); }
.ai-tgt-v.up { color: var(--up); }
.ai-tgt-v.down { color: var(--down); }
.ai-tgt-p { font-size: 0.6875rem; font-weight: 700; }
.ai-tgt-p.up { color: var(--up); }
.ai-tgt-p.down { color: var(--down); }
.ai-tgt-rr { margin-top: 0.5625rem; font-size: 0.75rem; color: var(--muted); }
.ai-tgt-rr b { color: var(--text); font-weight: 700; }

/* ── «Сумма входа» — informational block, part of the design system (neutral surface,
   no heavy tinted fill) ── */
.ai-size {
  margin-top: 0.75rem;
  padding: 0.6875rem 0.75rem;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
}
.ai-size-head { display: flex; align-items: center; gap: 0.4375rem; }
.ai-size-ico { font-size: 0.8125rem; }
.ai-size-title { font-size: 0.8125rem; font-weight: 700; color: var(--title); }
.ai-size-lev {
  margin-left: auto;
  font-size: 0.6875rem;
  font-weight: 700;
  color: var(--accent);
  background: rgba(255, 138, 0, 0.14);
  padding: 0.125rem 0.5rem;
  border-radius: 62.4375rem;
}
.ai-size-lev-off { color: var(--muted); background: rgba(255, 255, 255, 0.06); }
.ai-size-text {
  margin-top: 0.4375rem;
  font-size: 0.8125rem; line-height: 1.5; color: var(--neutral);
}
.ai-size-text b { font-weight: 800; color: var(--title); }
.ai-size-text .ai-accent { color: var(--accent); }
.ai-size-text .ai-neg { color: var(--down); }

/* «Посчитать в деньгах» — a full, solid, tappable action button (highest-contrast CTA) */
.ai-calc-toggle {
  margin-top: 0.625rem;
  width: 100%;
  height: 2.25rem;
  display: inline-flex; align-items: center; justify-content: center; gap: 0.4375rem;
  border-radius: var(--radius-xs);
  /* Soft accent-tinted button (not a loud solid orange) so it sits calmly inside the
     surface-2 card instead of shouting over the numbers above it. */
  border: 1px solid rgba(255, 138, 0, 0.32);
  background: rgba(255, 138, 0, 0.12);
  color: var(--accent);
  font-size: 0.8125rem; font-weight: 700;
  cursor: pointer;
  touch-action: manipulation;
  transition: background 0.14s, border-color 0.14s;
}
.ai-calc-toggle:active { background: rgba(255, 138, 0, 0.2); border-color: rgba(255, 138, 0, 0.5); }
.ai-calc-ico { width: 0.9375rem; height: 0.9375rem; flex: 0 0 auto; }

/* Calculator (revealed on tap): deposit input + quick presets + result rows */
.ai-calc { margin-top: 0.625rem; }
.ai-calc-field {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5625rem 0.75rem;
  border-radius: var(--radius-xs);
  background: var(--bg);
  border: 1px solid var(--border);
}
.ai-calc-field:focus-within { border-color: var(--accent); }
.ai-calc-cur { font-size: 0.875rem; font-weight: 700; color: var(--muted); }
.ai-calc-input {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  color: var(--title);
  font-size: 1rem; /* ≥16px keeps iOS from zooming on focus */
  font-weight: 700;
}
.ai-calc-input::placeholder { color: var(--muted); font-weight: 500; font-size: 0.875rem; }
/* Hide the number-input spinners (Chrome/Safari + Firefox). */
.ai-calc-input::-webkit-outer-spin-button,
.ai-calc-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.ai-calc-input { -moz-appearance: textfield; }

.ai-calc-presets { display: flex; gap: 0.375rem; margin-top: 0.4375rem; }
.ai-calc-preset {
  flex: 1;
  height: 1.75rem;
  border-radius: var(--radius-2xs);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--neutral);
  font-size: 0.71875rem; font-weight: 700;
  cursor: pointer;
  touch-action: manipulation;
}
.ai-calc-preset:active,
.ai-calc-preset.active {
  border-color: rgba(255, 138, 0, 0.45);
  background: rgba(255, 138, 0, 0.12);
  color: var(--accent);
}

.ai-calc-out {
  margin-top: 0.625rem;
  display: flex; flex-direction: column; gap: 0.0625rem;
  border-radius: var(--radius-xs);
  overflow: hidden;
  border: 1px solid var(--border);
}
.ai-calc-row {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0.5625rem 0.75rem;
  background: var(--surface);
}
.ai-calc-row-k { font-size: 0.71875rem; color: var(--muted); }
.ai-calc-row-v { font-size: 0.84375rem; font-weight: 800; letter-spacing: -0.0125rem; color: var(--title); }
.ai-calc-row-v.ai-neg { color: var(--down); }
.ai-calc-row-v.ai-accent { color: var(--accent); }

.ai-foot { margin-top: 0.75rem; font-size: 0.6875rem; color: var(--muted); line-height: 1.4; }
.ai-disclaimer { margin-top: 0.5rem; font-size: 0.6875rem; color: var(--muted); opacity: 0.8; line-height: 1.4; }
.ai-tgt-alt {
  margin-top: 0.4375rem;
  padding-top: 0.4375rem;
  border-top: 1px solid var(--border);
  font-size: 0.6875rem;
  color: var(--muted);
  line-height: 1.4;
}

/* ── Статистика отработки ────────────────────────────────── */
.stats-page-title { font-size: 1.25rem; font-weight: 700; letter-spacing: 0.0125rem; color: var(--title); }
.stats-section-hd {
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--title);
  margin: 0.375rem 0.125rem 0.625rem;
}
.stats-page {
  flex: 1;
  overflow-y: auto;
  padding: 0.75rem 1rem 1.5rem;
  -webkit-overflow-scrolling: touch;
}
.stats-summary {
  text-align: center;
  padding: 0.875rem 0.75rem 1.25rem;
  margin-bottom: 0.5rem;
}
.stats-big { font-size: 2.875rem; font-weight: 800; line-height: 1; letter-spacing: -0.02em; }
.stats-sub { margin-top: 0.5rem; font-size: 0.75rem; color: var(--muted); }

.stats-pct-up { color: var(--up); }
.stats-pct-down { color: var(--down); }
.stats-pct-mid { color: var(--muted); }

/* The headline % is the hero by size and weight alone (2.875rem/800). It carries the
   same --up/--down as every other price figure: one green in the app, not two. */

.stats-meta { flex: 0 0 auto; font-size: 0.75rem; color: var(--muted); font-variant-numeric: tabular-nums; }

/* ── Headline facts strip ───────────────────────────────────────────────────
   One unified block under the profit %, split into three segments by hairlines
   (winrate · trades · тейк/стоп). Extra top margin gives the profit room to breathe
   and stay the single accent. */
.stats-facts {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  align-items: center;
  margin-top: 1.5rem;
  padding: 0.75rem 0.5rem;
  border-radius: var(--radius);
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--border-soft);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.035),
    0 0.375rem 1rem rgba(0, 0, 0, 0.22);
}
.stats-fact {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  padding: 0.125rem 0.5rem;
}
.stats-fact + .stats-fact { border-left: 1px solid var(--border); }
.stats-fact-v {
  font-size: 1rem;
  font-weight: 800;
  color: var(--title);
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.stats-fact-l { font-size: 0.6875rem; color: var(--muted); }
.stats-fact-wl { display: inline-flex; align-items: center; gap: 0.75rem; }
.stats-wl { display: inline-flex; align-items: center; gap: 0.25rem; color: var(--title); }
.stats-wl .stats-ic { width: 0.875rem; height: 0.875rem; }
.stats-wl-tp .stats-ic { color: var(--up); }
.stats-wl-sl .stats-ic { color: var(--down); }

.stats-note {
  text-align: center;
  font-size: 0.8125rem;
  color: var(--muted);
  padding: 1.25rem 0.75rem;
}

/* ── Per-strategy card (minimal, TradingView/Linear-flavoured) ──────────────
   Quiet surface, no border, generous vertical rhythm. Only three accent colours
   in play: green (profit), red (loss), muted grey (secondary); everything else
   is white. Blocks are separated by whitespace, not rules or boxes. */
.stats-row {
  padding: 1rem;
  margin-bottom: 0.5rem;
  border-radius: var(--radius);
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--border-soft);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.035),
    0 0.375rem 1rem rgba(0, 0, 0, 0.22);
}
.stats-row-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
}
/* Left identity block: rank medal + name + the «N сделок» count all on ONE line
   («Импульсы  33 сделки»), so the header reads as a single tidy leaderboard line
   instead of stacking the count as a subtitle below the name. */
.stats-id { display: flex; align-items: center; min-width: 0; flex: 1 1 auto; }
.stats-name-wrap { display: inline-flex; align-items: center; gap: 0.5rem; min-width: 0; }
.stats-name {
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--title);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 0 1 auto;
}
/* Rank: an SVG medal (gold/silver/bronze) for the top 3, a quiet #N below that. */
.stats-medal { display: inline-flex; line-height: 0; }
.stats-medal-ic { width: 1.1875rem; height: 1.1875rem; display: block; }
.stats-medal-1 { color: #f3c04a; }   /* gold   */
.stats-medal-2 { color: #c2c7d0; }   /* silver */
.stats-medal-3 { color: #cd8a54; }   /* bronze */
.stats-rank {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.1875rem;
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* The hero figure of the card: net result, coloured by sign. Anchored on the RIGHT of
   the header row (opposite the name) — a leaderboard line, so the number stays big and
   prominent without floating on its own lonely row. */
.stats-net {
  flex: 0 0 auto;
  /* Nudged up so the big figure's cap-height lines up with the strategy name to its left
     (the row is flex-start), reading as one leaderboard line rather than a floating number. */
  margin-top: -0.125rem;
  font-size: 1.75rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
}

/* Metrics grid spans the full card width, evenly distributed. Tighter top gap now that
   the number sits inline with the name (the card reads as one compact block). */
.stats-metrics {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.625rem;
  /* Faint hairline splits the header from the metrics — a quiet in-card divider «для
     красоты», sitting on the tighter 12px rhythm rather than a big empty gap. */
  margin-top: 0.75rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--border-glass);
}
.stats-metric { display: flex; flex-direction: column; gap: 0.1875rem; min-width: 0; }
.stats-metric-val {
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--title);
  font-variant-numeric: tabular-nums;
}
.stats-metric-lbl {
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
}

/* Outcome tally — decode of the winrate: тейк / стоп / без результата. Each item is an
   icon+count on top over a faint label below, the three spread evenly across the card so
   the counts are legible (not the cramped «✔14✖18⏳1») and self-explanatory. */
.stats-tally {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.625rem;
  /* Same faint in-card divider + tighter rhythm, pulling the тейк/стоп tally up snug
     under the metrics instead of floating far below. */
  margin-top: 0.75rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--border-glass);
  font-variant-numeric: tabular-nums;
}
.stats-tl {
  display: flex;
  flex-direction: column;
  gap: 0.1875rem;
  min-width: 0;
}
.stats-tl-top {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
}
.stats-tl-n {
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--title);
}
.stats-tl-lbl {
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.stats-ic { width: 1rem; height: 1rem; display: block; flex: none; }
.stats-tl-tp .stats-ic { color: var(--up); }
.stats-tl-sl .stats-ic { color: var(--down); }
.stats-tl-to .stats-ic { color: var(--muted); }

/* ── Equity curve (рост депозита по советам) ─────────────────────────────── */
.stats-equity-wrap {
  margin: 0 2px 14px;
  padding: 12px 12px 10px;
  border-radius: var(--radius);
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--border);
}
.eq-plot { position: relative; height: 150px; }
.eq-canvas {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  right: 38px;
  touch-action: pan-y;        /* horizontal drag scrubs the crosshair, vertical still scrolls */
  cursor: crosshair;
}
.stats-equity { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.eq-up { color: var(--up); }
.eq-down { color: var(--down); }
.eq-line {
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.eq-area { fill: currentColor; opacity: 0.13; }
.eq-grid { stroke: rgba(255, 255, 255, 0.06); stroke-width: 1; vector-effect: non-scaling-stroke; }
.eq-grid.eq-zero { stroke: var(--muted); stroke-dasharray: 3 4; opacity: 0.55; }

.eq-ylabel {
  position: absolute;
  right: 0;
  width: 36px;
  transform: translateY(-50%);
  padding-left: 5px;
  font-size: 10px;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
}
.eq-xaxis { position: relative; margin: 6px 38px 0 0; height: 13px; }
.eq-xlabel {
  position: absolute;
  top: 0;
  font-size: 10px;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
}

.eq-cursor {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  margin-left: -0.5px;
  background: rgba(255, 255, 255, 0.32);
  pointer-events: none;
}
.eq-dot {
  position: absolute;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: currentColor;
  border: 2px solid var(--bg);
  transform: translate(-50%, -50%);
  pointer-events: none;
}
.eq-tip {
  position: absolute;
  top: 4px;
  z-index: 3;
  pointer-events: none;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-2xs);
  padding: 5px 8px;
  white-space: nowrap;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
}
.eq-tip-val { font-size: 12px; font-weight: 800; }
.eq-tip-date { margin-top: 1px; font-size: 10px; color: var(--muted); }

.stats-equity-cap {
  margin-top: 10px;
  text-align: center;
  font-size: 12px;
  font-weight: 500;
  color: var(--muted);
  line-height: 1.45;
  padding: 0 6px;
}

/* ── Portfolio metrics grid ──────────────────────────────────────────────── */
.stats-overview {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.5rem;
  margin: 0 0.125rem 1rem;
}
.stats-ov-cell {
  padding: 0.75rem 0.875rem;
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--border);
}
.stats-ov-val { font-size: 1.25rem; font-weight: 800; color: var(--title); line-height: 1.1; }
.stats-ov-lbl {
  margin-top: 0.1875rem;
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--muted);
}
.stats-ov-note {
  margin: 0.125rem 0.25rem 1.125rem;
  font-size: 0.75rem;
  color: var(--muted);
  line-height: 1.45;
}

.measure-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 4;
  pointer-events: none;
  overflow: visible;
}
.m-cross {
  stroke: rgba(255, 255, 255, 0.45);
  stroke-width: 1;
  stroke-dasharray: 4 4;
}
.m-rect {
  fill: rgba(255, 138, 0, 0.12);
  stroke: rgba(255, 138, 0, 0.5);
  stroke-width: 1;
}
.m-arrow { stroke: var(--tv); stroke-width: 1.5; }

.measure-label {
  position: absolute;
  z-index: 6;
  pointer-events: none;
  transform: translate(-50%, calc(-100% - 8px));
  text-align: center;
  line-height: 1.32;
  font-size: 0.625rem;
  font-weight: 700;
  padding: 0.1875rem 0.4375rem;
  border-radius: var(--radius-2xs);
  white-space: nowrap;
  color: var(--on-accent);
  background: linear-gradient(180deg, var(--accent-2), var(--accent));
  box-shadow: 0 3px 10px rgba(255, 138, 0, 0.3);
}
.measure-label.below { transform: translate(-50%, 8px); }
.measure-label .ml-sub {
  font-weight: 600;
  color: rgba(255, 255, 255, 0.9);
  font-size: 0.5625rem;
}

/* ── Overlays / spinner ──────────────────────────────────── */
.overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.875rem;
  /* Loading/error screen fills the chart area with the same dark chart tone (--bg) the
     canvas uses, so it flows into the page gradient. Its top connects to the native
     header via the gradient (no black seam now that setBackgroundColor == --bg-top). */
  background: var(--bg);
  z-index: 10;
}
.overlay-text { color: var(--muted); font-size: 0.8125rem; max-width: 80%; text-align: center; }
.overlay-icon { font-size: 2.5rem; }
.overlay-hint { margin-top: 0.5rem; color: var(--accent); font-size: 0.75rem; }
#detail-error { cursor: pointer; }

/* Retry affordance shared by every failed-load state (home / stats / screener /
   detail): a message plus a solid orange «Обновить» button, so a slow or failed
   server never dead-ends the user without a way back. */
.load-error {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.875rem;
  padding: 2.5rem 1rem;
  text-align: center;
}
.load-error-msg { color: var(--muted); font-size: 0.8125rem; max-width: 20rem; }
.retry-btn {
  appearance: none;
  border: 0;
  cursor: pointer;
  background: var(--accent);
  color: var(--on-accent);
  font: inherit;
  font-weight: 700;
  font-size: 0.8125rem;
  padding: 0.625rem 1.375rem;
  border-radius: 62.4375rem;
  display: inline-flex;
  align-items: center;
  gap: 0.4375rem;
  transition: background 0.15s ease, transform 0.08s ease;
}
.retry-btn:active { background: var(--accent-press); transform: scale(0.97); }
.retry-btn svg {
  width: 1rem;
  height: 1rem;
  stroke: currentColor;
  stroke-width: 2;
  fill: none;
}

.spinner {
  width: 2.125rem;
  height: 2.125rem;
  border: 3px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ── Launch splash ──────────────────────────────────────────────────────────
   Brand logo centred on the app's dark gradient (+ a soft brand glow), shown from
   the first painted frame and faded out by app.js (.hiding → .gone) once the first
   view renders. Gives the Mini App a real "launching" moment instead of a bare
   spinner on black. z-index sits above every in-app overlay (loader/error z:10). */
#splash {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.75rem;
  background:
    radial-gradient(90% 55% at 50% 42%, rgba(255, 138, 0, 0.10) 0%, rgba(255, 138, 0, 0) 60%),
    linear-gradient(180deg, var(--bg-top) 0%, var(--bg) 70%);
  transition: opacity 0.45s ease;
}
#splash.hiding { opacity: 0; }
#splash.gone { display: none; }
.splash-logo {
  width: min(62vw, 15rem);
  max-width: 74%;
  height: auto;
  filter: drop-shadow(0 0.5rem 1.5rem rgba(0, 0, 0, 0.5));
  animation: splash-pop 0.5s cubic-bezier(0.2, 0.7, 0.2, 1) both;
}
/* Text wordmark shown only if the logo image is missing (onerror). */
.splash-word {
  font-size: 2rem;
  font-weight: 800;
  letter-spacing: 0.02em;
  color: var(--title);
  animation: splash-pop 0.5s cubic-bezier(0.2, 0.7, 0.2, 1) both;
}
.splash-word b { color: var(--accent); font-weight: 800; }
.splash-spin { opacity: 0.9; animation: spin 0.8s linear infinite, splash-fade-in 0.4s ease 0.2s both; }
@keyframes splash-pop {
  from { transform: scale(0.92); opacity: 0; }
  to   { transform: scale(1); opacity: 1; }
}
@keyframes splash-fade-in { from { opacity: 0; } to { opacity: 0.9; } }

/* The «Загружаем историю…» pill that used to sit here is gone: older pages are now
   prefetched before the view reaches the edge, so history just appears as you pan or
   zoom out and there is nothing to announce. */

/* ── Feed (list of situations) ───────────────────────────── */
.feed-tfbar {
  padding: 0.25rem 0.875rem 0.875rem;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.feed-tfbar::-webkit-scrollbar { display: none; }

.feed-list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 0.25rem 0.625rem 0.875rem 0.875rem;
  display: flex;
  flex-direction: column;
  gap: 0.875rem;
  /* Visible thin scrollbar so it's obvious the coin list scrolls (not only wheel). */
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.22) transparent;
}
.feed-list::-webkit-scrollbar { width: 0.375rem; }
.feed-list::-webkit-scrollbar-track { background: transparent; }
.feed-list::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.22);
  border-radius: 62.4375rem;
}
.feed-list::-webkit-scrollbar-thumb:active { background: rgba(255, 255, 255, 0.35); }

.card {
  flex: 0 0 auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  cursor: pointer;
  transition: border-color 0.14s, transform 0.08s;
}
.card:active { border-color: var(--accent); transform: scale(0.992); }

/* The chart is the hero of the card: taller, and set on the darker chart base so it
   reads as its own panel — no divider rule needed to separate it from the plate. */
.card-preview {
  position: relative;
  width: 100%;
  height: 14rem;
  background: var(--bg);
}
.card-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.card-spin { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
.card.loaded .card-spin { display: none; }

/* Outcome pill overlaid on the preview corner — absolute, so it never reflows the card. */
.card-outcome {
  position: absolute;
  top: 0.625rem;
  left: 0.625rem;
  z-index: 2;
  max-width: calc(100% - 1.25rem);
  padding: 0.25rem 0.625rem;
  border-radius: var(--radius-2xs);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.0125rem;
  line-height: 1.4;
  color: var(--neutral);
  background: rgba(10, 14, 19, 0.82);
  border: 1px solid var(--border-glass);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}
.card-outcome.oc-win { color: var(--up); border-color: rgba(43, 212, 126, 0.3); background: rgba(15, 33, 25, 0.85); }
.card-outcome.oc-loss { color: var(--down); border-color: rgba(255, 84, 102, 0.3); background: rgba(35, 16, 20, 0.85); }
.card-outcome.oc-skip { color: var(--accent); border-color: rgba(255, 138, 0, 0.3); background: rgba(35, 25, 12, 0.85); }
.card-outcome.oc-no_fill { color: var(--muted); }
.card-outcome.oc-timeout { color: var(--muted); }
.card-outcome.oc-pending { color: var(--muted); opacity: 0.85; }
.card-outcome.oc-no_data { color: var(--muted); opacity: 0.7; }

.card-plate {
  padding: 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
.card-row { display: flex; align-items: center; gap: 0.625rem; }
/* Coin name is the primary accent of the header row. */
.card-symbol {
  font-size: 1.0625rem;
  font-weight: 800;
  color: var(--title);
  letter-spacing: 0.0125rem;
}
/* Time meta on one baseline, evenly spaced and quiet: 1m · 09.07 09:03 · 4ч назад. */
.card-meta { font-size: 0.6875rem; color: var(--muted); align-items: center; gap: 0.625rem; }
.card-tf {
  padding: 0.125rem 0.5rem;
  border-radius: var(--radius-2xs);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--muted);
  font-weight: 600;
}
.card-date { color: var(--muted); }
.card-ago { margin-left: auto; color: var(--muted); }

/* Signal type is the secondary badge — pushed to the far side of the coin name. */
.card .badge-dir { margin-left: auto; }

/* Alert-moment snapshot — mirrors the detail page's .snap-strip 1:1 so the plate reads
   pixel-for-pixel like the chart page. Full-bleed to the card edges; a top hairline
   separates it from the meta row, then a padded 9px eyebrow caption sits over its OWN
   hairline (like .snap-strip-top), then the flat .sstat readout. 16px gutters line the
   numbers up under the ticker; the four readouts spread evenly and only fall back to a
   hidden horizontal scroll in the rare over-long case (same as the detail .stats row). */
.card-alert {
  /* Full-bleed left/right AND bottom (negative bottom margin cancels the plate's 16px
     bottom padding) → the snapshot becomes a clean footer strip flush to the card's
     bottom edge, with no big empty gap under the numbers. No fill: it reads as the same
     dark card surface, separated only by the thin top hairline. */
  margin: 0.25rem -1rem -1rem;
  border-top: 1px solid var(--border-soft);
}
.card-alert .snap-cap {
  margin: 0;
  padding: 0.4375rem 1rem 0.375rem;
  border-bottom: 1px solid var(--border-soft);
  letter-spacing: 0.0375rem;
}
/* Four readouts spread evenly edge-to-edge (16px gutters) so the row is balanced and
   centred across the width, not huddled to one side. Values a touch smaller so all four
   fit without overflow-scrolling. */
.card-alert .snap-stats {
  padding: 0.5rem 1rem 0.625rem;
  gap: 0.375rem;
  align-items: baseline;
  justify-content: space-between;
}
.card-alert .snap-stats .sstat-v { font-size: 0.6875rem; }

.feed-loader { display: flex; justify-content: center; padding: 0.5rem 0 1rem; }
.spinner-sm { width: 1.375rem; height: 1.375rem; border-width: 2px; }
.feed-empty { text-align: center; color: var(--muted); font-size: 0.8125rem; padding: 2.5rem 1rem; }

/* ── Home («Главная») — top take-profit wins over 24h ────── */
/* The view scrolls as ONE column. The header is content, not chrome: on a 740px Telegram
   viewport a pinned banner spent a third of the screen restating what the user already
   read, so it now yields the screen to the cards as soon as the user moves. */
.home-view {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.22) transparent;
}
.home-view::-webkit-scrollbar { width: 0.375rem; }
.home-view::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.22); border-radius: 62.4375rem; }

/* ── AI-assistant hero ──────────────────────────────────────────────
   The mascot is the emotional anchor of the landing view. Its glow, aura and
   accent copy all read from a single --mood set on [data-mood]; the whole card
   cross-fades between mood palettes. Eyes/accents recolour via hue-rotate of the
   baked-orange PNG (isolating just the eyes in a raster is impossible), so the
   robot appears to "glow the mood colour". */
.ai-hero {
  --mood: #3aa0ff;                        /* neutral (blue) default */
  --mood-soft: rgba(58, 160, 255, 0.22);
  --bot-hue: 180deg;                      /* orange (~35°) → mood hue */
  position: relative;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0.875rem 1rem 0.875rem;
  padding: 1.25rem 1.25rem 1.25rem 1.375rem;
  min-height: 9.5rem;
  border-radius: var(--radius);
  overflow: hidden;
  /* Directional light: the card is darkest at the left edge and lifts toward the bottom-right,
     where the robot stands — a mood-tinted wash plus a soft white bloom read as the mascot
     spilling light onto the panel, rather than a flat, evenly-lit rectangle. */
  background:
    radial-gradient(90% 95% at 100% 100%, var(--mood-soft) 0%, transparent 55%),
    radial-gradient(72% 82% at 93% 86%, rgba(255, 255, 255, 0.055) 0%, transparent 60%),
    linear-gradient(100deg, #131313 0%, #1a1a1a 52%, #242424 100%);
  border: 1px solid var(--border);
  /* Lifted off the page: a soft cast shadow for depth + a hairline top highlight so the
     panel catches light like a physical card. */
  box-shadow: 0 12px 28px -14px rgba(0, 0, 0, 0.7), inset 0 1px 0 rgba(255, 255, 255, 0.05);
  transition: background 0.6s ease;
  animation: cardRise 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
}
/* Mood → palette + hue-rotate amount. bull=green, bear=red, flat=yellow, neutral=blue. */
.ai-hero[data-mood="bull"]    { --mood: #2bd47e; --mood-soft: rgba(43, 212, 126, 0.24); --bot-hue: 108deg; }
.ai-hero[data-mood="bear"]    { --mood: #ff5466; --mood-soft: rgba(255, 84, 102, 0.24); --bot-hue: -42deg; }
.ai-hero[data-mood="flat"]    { --mood: #ffcf3a; --mood-soft: rgba(255, 207, 58, 0.22); --bot-hue: 16deg; }
.ai-hero[data-mood="neutral"] { --mood: #3aa0ff; --mood-soft: rgba(58, 160, 255, 0.22); --bot-hue: 180deg; }

/* Ambient wash behind the robot. Kept low-opacity, wide, and heavily blurred so it
   reads as light spilling off the mascot into the card — never a hard-edged disc that
   sits on top of the background. Anchored to the bot's corner (bottom-right), not the
   vertical centre. */
.ai-hero-aura {
  position: absolute;
  right: -5rem;
  bottom: -5.5rem;
  width: 21rem;
  height: 21rem;
  /* Wider, softer and fainter than before so it reads as ambient light spilling off the robot
     that melts into the card's own bottom-right lift — not a coloured disc laid on top of the
     background. The gradient fades to transparent very early (48%) and the whole layer is heavily
     blurred, so the glow dissolves gradually into the background with no perceptible edge. */
  background: radial-gradient(circle at 55% 55%, var(--mood) 0%, transparent 48%);
  opacity: 0.10;
  filter: blur(64px);
  pointer-events: none;
  animation: auraPulse 4.2s ease-in-out infinite;
  transition: background 0.6s ease;
}
.ai-hero-copy {
  position: relative;
  z-index: 1;
  flex: 1 1 auto;
  min-width: 0;
  /* Clearance for the mascot. The robot PNG's visible silhouette (measured: 19.5%–79.4% of its
     square box) reaches ~9.4rem in from the card's right edge, while the text column only used to
     reserve 7rem — so any headline that filled the clamp ran under the robot, and the collision
     grew on wider (desktop) viewports where the copy column stretches further right. This reserve
     is in rem and the robot is rem-sized against the right edge, so one value clears it at every
     width. */
  padding-right: 9.25rem;
}
.ai-hero-kicker {
  display: inline-flex;
  align-items: center;
  gap: 0.4375rem;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  white-space: nowrap;   /* «AI-ассистент» is one token — never let the narrow column break it */
  color: var(--mood);
  transition: color 0.6s ease;
}
.ai-hero-dot {
  width: 0.4375rem;
  height: 0.4375rem;
  border-radius: 50%;
  background: var(--mood);
  box-shadow: 0 0 8px var(--mood);
  animation: dotPulse 2s ease-in-out infinite;
  transition: background 0.6s ease, box-shadow 0.6s ease;
}
.ai-hero-headline {
  margin: 0.5rem 0 0;
  font-size: 0.875rem;
  font-weight: 800;
  line-height: 1.34;
  letter-spacing: -0.005em;
  color: var(--title);
  text-wrap: balance;
  /* A HARD-fixed 3-line box: the rotating phrase (reads of varying length) is clamped so the
     card NEVER resizes as the assistant «speaks» — and 3 lines keeps the card compact rather than
     reading like a paragraph of an article. Anything longer than 3 lines trails off with an
     ellipsis; the spoken copy is kept short enough that this is rare.
     height is locked, not just min-height, so a short line and a long one occupy the same space. */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  /* min-height reserves the full 3-line box (a hair above 3×line-height) so a short spoken line
     occupies the same space as a long one and the card never resizes — while staying just clear
     of the clamp boundary, so WebKit doesn't inject a phantom ellipsis on a phrase that fits. */
  min-height: 3.55rem;
  transition: opacity 0.22s ease;    /* soft crossfade as the spoken line rotates */
}
.ai-hero-headline.is-swapping { opacity: 0; }
.ai-hero-mood {
  display: inline-block;
  margin-top: 0.5625rem;
  font-size: 0.78125rem;
  font-weight: 700;
  letter-spacing: 0.005em;
  color: var(--mood);
  transition: color 0.6s ease;
}
/* The mascot is enlarged and seated into the card's bottom-right corner so it reads as if it
   is standing BEHIND the panel: the card's own `overflow: hidden` gives a clean straight cut
   across the hips (no mask, no fade/blur), and the box is sized + dropped far enough that the
   legs and feet fall below that edge and never show. Motion is layered for a natural, calm idle
   and runs on every device: it is NOT gated behind prefers-reduced-motion (users wanted the
   desktop robot alive without touching the Windows animation setting). */
.ai-hero-bot {
  position: absolute;
  right: -1.5rem;              /* seated a little further into the right corner so its raised arm clears the copy */
  bottom: -4.5rem;            /* dropped a touch less than before so the head gains a little top breathing room and the bust reads centred, not jammed at the top edge — hip-cut still hides the legs */
  z-index: 1;
  width: 13.5rem;            /* slightly smaller so it sits centred with headroom, still enough presence and clear of the text column */
  height: 13.5rem;
  /* Layer 1 — slow "breathing": a small vertical drift + micro-scale. */
  animation: botBreath 6s cubic-bezier(0.45, 0, 0.55, 1) infinite;
  will-change: transform;
}
.ai-bot-inner {
  position: relative;
  width: 100%;
  height: 100%;
  /* Layer 2 — a slow, tiny sway, out of phase with the breath so the compound motion never
     looks like one mechanical loop. */
  animation: botSway 9s ease-in-out infinite;
  transform-origin: 58% 46%;   /* pivot near the face so the head barely moves, body settles */
}
.ai-bot-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center top;   /* square image in a square box → exact fill, face is deterministic */
  filter:
    hue-rotate(var(--bot-hue)) saturate(1.12)
    /* grounding cast shadow (neutral) + a WIDE, low-opacity mood glow that dissolves into the
       card instead of hugging the silhouette as a hard coloured outline. */
    drop-shadow(0 5px 13px rgba(0, 0, 0, 0.5))
    drop-shadow(0 0 34px var(--mood-soft));
  transition: filter 0.6s ease;
  -webkit-user-drag: none;
  user-select: none;
  /* Purely decorative — nothing is bound to it. Taking it out of hit-testing means a
     long press lands on the hero card instead of on an <img>, so the webview has no
     image to offer a "save / copy link" menu for even if it ignores the
     contextmenu preventDefault in app.js. */
  pointer-events: none;
}
@keyframes botBreath {
  0%, 100% { transform: translateY(0) scale(1); }
  50%      { transform: translateY(-0.24rem) scale(1.012); }
}
@keyframes botSway {
  0%, 100% { transform: rotate(-0.9deg); }
  50%      { transform: rotate(0.9deg); }
}
@keyframes auraPulse {
  0%, 100% { opacity: 0.11; transform: scale(1); }
  50%      { opacity: 0.18; transform: scale(1.06); }
}
/* Panels ease up + fade in on first paint — a calm, staggered arrival that reads as
   "the page composing itself" rather than snapping in. Runs on desktop too. */
@keyframes cardRise {
  from { opacity: 0; transform: translateY(0.625rem); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes dotPulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.5; transform: scale(0.85); }
}

/* Day header — the section label above the top-winners board (hero now carries the
   day's mood). Smaller than the old page-title since the assistant leads the view. */
.home-head {
  flex: 0 0 auto;
  padding: 0.25rem 1rem 0.75rem;
}
.home-head-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
}
.home-title {
  margin: 0;
  font-size: 1.25rem;
  font-weight: 800;
  letter-spacing: 0.0125rem;
  color: var(--title);
  text-wrap: balance;
}
.home-updated {
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--stat-header);
  white-space: nowrap;
}
.home-lede {
  margin: 0.25rem 0 0;
  font-size: 0.8125rem;
  line-height: 1.35;
  color: var(--muted);
}
/* Four day-facts in a standalone card. Tabular so they don't jitter on refresh.
   A quiet caption fixes the «over what window?» ambiguity of the bare numbers. */
.home-kpis {
  display: flex;
  flex-direction: column;
  gap: 0.4375rem;
  margin: 0 1rem 0.875rem;
  /* Compact stat strip: the caption tucks tight above a single dense row of numbers so the card
     stays short and reads as a quick glance-line, not a heavy panel — but keeps the insights card's
     side inset so the two still read as one card system. */
  padding: 0.75rem 0.875rem 0.8125rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 8px 20px -16px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.035);
  animation: cardRise 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.06s both;
}
.home-kpis.hidden { display: none; }
.home-kpis-cap {
  padding: 0 0.125rem;
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--stat-header);
}
.home-kpi-row {
  display: flex;
}
.home-kpi {
  display: flex;
  flex-direction: column;
  gap: 0.1875rem;
  align-items: center;
  text-align: center;
  flex: 1 1 0;
  min-width: 0;
  padding: 0.0625rem 0.375rem;   /* air between the numbers and the column dividers */
}
.home-kpi + .home-kpi {
  border-left: 1px solid var(--border-soft);
}
.home-kpi-v {
  font-size: 1.125rem;
  font-weight: 800;
  line-height: 1.1;
  color: var(--title);
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
}
.home-kpi-v.up { color: var(--up); }
.home-kpi-v.down { color: var(--down); }
.home-kpi-l {
  /* Reserve two lines for EVERY label so a wrapping one («дошли до цели») can't make its
     column taller than its neighbours — the row of numbers stays on one optical baseline. */
  display: flex;
  align-items: flex-start;
  justify-content: center;
  min-height: 1.5em;
  font-size: 0.625rem;
  font-weight: 500;
  line-height: 1.15;
  color: var(--muted);
}

/* Daily insights — deterministic reads of the screener's own stats (no LLM). Icons are
   stroked glyphs from the sprite; the emoji bullets they replace rendered differently on
   every platform and read as chat, not instrumentation. */
.insights {
  flex: 0 0 auto;
  margin: 0 1rem 0.875rem;
  padding: 1.125rem 1.125rem 1.25rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 10px 24px -16px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.035);
  animation: cardRise 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.12s both;
}
.insights.hidden { display: none; }
/* Header doubles as the collapse toggle (button, reset to look like the old row). */
.insights-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0;
  background: none;
  border: 0;
  text-align: left;
  color: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.insights-title {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 0.875rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--title);
  white-space: nowrap;   /* keep «Что говорит ассистент» on one line — the updated chip shrinks first */
  overflow: hidden;
  text-overflow: ellipsis;
}
/* «обновлено …» — a quiet live chip: a soft accent dot + muted text on a faint surface pill.
   More noticeable than the old bare grey text, still calm enough not to compete with the title. */
.insights-updated {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 0.3125rem;
  padding: 0.1875rem 0.4375rem;
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
  background: var(--surface-2);
  border-radius: 62.4375rem;
}
.insights-updated:empty { display: none; }
.insights-updated::before {
  content: "";
  width: 0.375rem;
  height: 0.375rem;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 6px rgba(255, 138, 0, 0.6);
  animation: dotPulse 2s ease-in-out infinite;
}
.insights-chev {
  flex: 0 0 auto;
  width: 1.125rem;
  height: 1.125rem;
  stroke: var(--muted);
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 0.3s ease;
}
.insights.collapsed .insights-chev { transform: rotate(-90deg); }
/* Smooth "tab" open/close via grid-rows (0fr↔1fr) — no layout-thrashing height
   animation, and no arbitrary max-height cap that could clip a longer summary. */
.insights-body {
  display: grid;
  grid-template-rows: 1fr;
  opacity: 1;
  transition: grid-template-rows 0.35s ease, opacity 0.25s ease;
}
.insights.collapsed .insights-body {
  grid-template-rows: 0fr;
  opacity: 0;
}
.insights-list {
  list-style: none;
  margin: 0;
  padding: 0.875rem 0 0;
  min-height: 0;      /* let the grid track collapse to 0 */
  overflow: hidden;   /* clip bullets as the row closes */
  display: flex;
  flex-direction: column;
  gap: 0.875rem;
}
.insights-item {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  font-size: 0.8125rem;
  font-weight: 500;
  line-height: 1.45;
  color: var(--text);
}
/* Icon sits in a soft round chip so each read is a discrete recommendation, not a flat
   bullet in a paragraph. content-box keeps the glyph a fixed size inside the padded chip. */
.insights-ico {
  flex: 0 0 auto;
  box-sizing: content-box;
  width: 0.9375rem;
  height: 0.9375rem;
  padding: 0.375rem;
  border-radius: 50%;
  background: var(--surface-2);
  stroke: var(--muted);
  fill: none;
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Orange marks the one insight worth acting on (the freshest live setup). Its chip picks up a
   faint accent wash so the eye lands there first. */
.insights-ico.accent {
  stroke: var(--accent);
  background: rgba(255, 138, 0, 0.12);
}
.insights-ico.warn { stroke: var(--stat-header); }

/* The coin token inside «самый интересный сейчас» is a live link into its отработка. Kept as
   inline accent text with a soft underline — reads as tappable at a glance without the loud
   filled chip that used to shout over the rest of the calm insight list. The soft accent wash
   only appears on press, to confirm the tap. */
.insight-link {
  display: inline;
  padding: 0.02rem 0.12rem;
  border: none;
  border-radius: 0.375rem;
  font: inherit;
  font-weight: 800;
  letter-spacing: 0.01em;
  color: var(--accent);
  background: transparent;
  text-decoration: underline;
  text-decoration-color: rgba(255, 138, 0, 0.45);
  text-decoration-thickness: 0.08em;
  text-underline-offset: 0.15em;
  cursor: pointer;
  transition: background 0.15s ease, text-decoration-color 0.15s ease;
  -webkit-tap-highlight-color: transparent;
}
.insight-link:hover,
.insight-link:active {
  background: var(--accent-soft, rgba(255, 138, 0, 0.14));
  text-decoration-color: var(--accent);
}

.home-list {
  flex: 0 0 auto;
  padding: 0 1rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.home-card {
  flex: 0 0 auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  cursor: pointer;
  transition: border-color 0.14s, transform 0.08s;
  /* Entrance: a short staggered rise. --i is set per card by buildHomeCard. */
  animation: home-card-in 0.34s cubic-bezier(0.22, 1, 0.36, 1) both;
  animation-delay: calc(var(--i, 0) * 55ms);
}
@keyframes home-card-in {
  from { opacity: 0; transform: translateY(0.5rem); }
  to { opacity: 1; transform: none; }
}
.home-card:active { border-color: var(--accent); transform: scale(0.992); }

.home-top {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.875rem 1rem 0.75rem;
}
/* Rank marker: a numbered chip. #1 is lit orange («look here»); the rest stay muted —
   the medal emoji it replaces rendered as three different illustrations per platform. */
.home-rank {
  flex: 0 0 auto;
  width: 1.5rem;
  height: 1.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 62.4375rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--muted);
  font-size: 0.75rem;
  font-weight: 800;
  line-height: 1;
}
.home-rank.rank-1 {
  background: var(--accent);
  border-color: transparent;
  color: var(--on-accent);
}
.home-info { flex: 1 1 auto; min-width: 0; }
.home-sym {
  font-size: 1.0625rem;
  font-weight: 800;
  color: var(--title);
  letter-spacing: 0.0125rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.home-sub {
  margin-top: 0.125rem;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.home-pnl {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.1875rem;
}
.home-pnl-v {
  font-size: 1.25rem;
  font-weight: 800;
  line-height: 1;
  color: var(--up);
  letter-spacing: 0.0125rem;
}
.home-pnl-tag {
  display: flex;
  align-items: center;
  gap: 0.1875rem;
  font-size: 0.625rem;
  font-weight: 700;
  color: var(--up);
}
.home-pnl-ico {
  width: 0.6875rem;
  height: 0.6875rem;
  display: block;
  stroke: currentColor;
  fill: none;
  stroke-width: 2.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* The chart strip — the card's hero. It sits on the chart base, full-bleed to the card
   edges, and is tall enough for real candles: at 5.5rem the 690-bar outcome window
   collapsed into a 1px smear, which is why «отработку не видно». See fitCandles(). */
.home-preview {
  position: relative;
  width: 100%;
  height: 8.5rem;
  background: var(--bg);
  border-top: 1px solid var(--border-soft);
}
/* Skeleton sweep while the thumbnail fetches — the strip reads as loading, not broken. */
.home-preview::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.035), transparent);
  transform: translateX(-100%);
  animation: home-skeleton 1.5s ease-in-out infinite;
}
@keyframes home-skeleton { to { transform: translateX(100%); } }
.home-card.loaded .home-preview::after { display: none; }
.home-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  opacity: 0;
  transition: opacity 0.24s ease-out;
}
.home-card.loaded .home-canvas { opacity: 1; }

/* Empty state teaches the board instead of just saying «nothing here». */
.home-blank {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 2rem 1.5rem 2.5rem;
}
.home-blank-art { width: 6.5rem; height: auto; opacity: 0.5; margin-bottom: 0.875rem; }
.home-blank-t { font-size: 0.9375rem; font-weight: 800; color: var(--text); }
.home-blank-s {
  margin-top: 0.375rem;
  font-size: 0.8125rem;
  line-height: 1.4;
  color: var(--muted);
  max-width: 17rem;
}

@media (prefers-reduced-motion: reduce) {
  .home-card { animation: none; }
  .home-preview::after { animation: none; display: none; }
  .home-canvas { transition: none; }
  /* NOTE: .ai-hero-headline keeps its opacity crossfade even here — the rotating spoken line is
     the card's «alive» signal the user wants on desktop too (Windows toggles reduced-motion on by
     default), same rationale as the mascot + kicker dot below. */
  /* The staggered panel entrance and the "updated" live dot are motion-for-polish, not content —
     drop them for users who ask for reduced motion (leave the panels visible in their final state). */
  .ai-hero, .home-kpis, .insights { animation: none; }
  .insights-updated::before { animation: none; }
  /* NOTE: the mascot (breath + sway) AND the «AI-ассистент» kicker dot are deliberately NOT
     disabled here. They are self-contained, gentle idles the user explicitly asked to keep
     alive on desktop without depending on the Windows "show animations" setting — which is what
     drives prefers-reduced-motion on that platform. Only the wide ambient aura pulse stays off. */
  .ai-hero-aura { animation: none; }
}

/* ── Global bottom navigation (app sections) ─────────────── */
/* A floating, rounded, translucent glass panel detached from the screen edges
   (reference-inspired). Icon-over-label items; the active one glows orange with
   a small pill indicator. Stays in flow so it reserves its own height. */
.bottomnav {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-around;
  gap: 0.5rem;
  /* Flat, solid bar with a single top hairline — no glass blur, no drop shadow. */
  /* margin-top:auto keeps the bar glued to the bottom even before any view is
     un-hidden on first load — otherwise it's the only flex child of #app and
     floats up to the top during the initial loading frames. */
  margin: auto 0 0;
  /* Bottom padding clears the gesture bar: --sa-bottom is env() on iOS OUTSIDE
     Telegram and the client-reported inset in fullscreen, whichever is larger. */
  padding-top: 0.375rem;
  padding-bottom: calc(0.375rem + var(--sa-bottom));
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  border-radius: var(--radius) var(--radius) 0 0;
  /* --bg-top so the nav matches the native Telegram bottom bar (@bot), which
     applyThemeChrome() paints the same #1e1e1e → the two read as one seamless strip. */
  background: var(--bg-top);
  border-top: 1px solid var(--border);
}

.nav-item {
  /* Four tabs now (Главная · Скринер · Отработка · Статистика) — share the width
     equally so they always fit on narrow phones instead of overflowing. */
  flex: 1 1 0;
  min-width: 0;
  position: relative;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4375rem;
  padding: 0.25rem 0.375rem 0.3125rem;
  background: transparent;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--muted);
  font-family: inherit;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.00625rem;
  cursor: pointer;
  transition: color 0.14s;
}
.nav-ico { width: 1.5rem; height: 1.5rem; display: block; stroke: currentColor; fill: none; transition: transform 0.18s cubic-bezier(0.16, 1, 0.3, 1); }
.nav-item span { line-height: 1; white-space: nowrap; }

.nav-item.active { color: var(--accent); font-weight: 700; }
/* Active tab reads at a glance: the icon lifts a hair and a solid accent pill sits under it. */
.nav-item.active .nav-ico { transform: translateY(-0.0625rem); }
.nav-item.active::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 1.375rem;
  height: 0.1875rem;
  border-radius: 62.4375rem;
  background: var(--accent);
  box-shadow: 0 0 8px rgba(255, 138, 0, 0.45);
}
.nav-item:active { color: var(--text); }

/* ── Feed TF-group tabs (Все / Быстрые / Среднесрок / Долгосрок) ── */
.feed-groups {
  flex: 0 0 auto;
  display: flex;
  gap: 0.375rem;
  padding: 0 1rem;
  overflow-x: auto;
  /* No visible scroll indicator — it read like a system scrollbar and cheapened the
     bar. The row still scrolls (touch/wheel) if the chips ever overflow. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.feed-groups::-webkit-scrollbar { display: none; }

/* When the chips share the topbar row with the strategy dropdown they take the
   remaining width and scroll horizontally (min-width:0 lets the flex child
   shrink so overflow-x works); the topbar already supplies side padding. */
.topbar .feed-groups { flex: 1 1 auto; min-width: 0; padding: 0; }

/* Strategy picker — a compact dropdown pinned at the left of the feed header row.
   Its menu opens left-aligned (the shared tf-dd default aligns right) as a single
   vertical column listing every strategy. */
/* Top-align the strategy pill with the chip row: the chips' scroll container
   reserves horizontal-scrollbar height at its bottom, making it taller than the
   pill, so center-alignment never lines them up cleanly. Anchoring both to the top
   puts the pill and the chips on the same line (the scrollbar sits below both). */
/* Scrollbar gone → the chips and the strategy pill are the same height, so centre-align
   them cleanly on one line. */
.feed-topbar { padding-top: 0.75rem; padding-bottom: 0.75rem; align-items: center; }

/* Filter controls (strategy pill + group chips): solid surfaces, no glass blur, equal
   height, roomier horizontal padding, tighter inter-button gap. */
.feed-topbar #feed-strategy-dd .tf-dd-btn,
.feed-groups .tf-btn {
  font-size: 0.75rem;
  padding: 0.5rem 0.9375rem;
  background: var(--surface-2);
  border-color: var(--border);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
/* Selected filter: a solid, confident orange fill (no gradient, no glow) — clearly the
   active tab without the neon shimmer. */
.feed-groups .tf-btn.active {
  color: var(--on-accent);
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: none;
}
/* Strategy picker menu — flat solid panel per the design system (no glass/blur/shadow),
   matching the «Статистика» picker: solid --surface, 1px hairline, one orange accent on
   the active row. Rows are a single readable column with roomy tap targets. */
#feed-strategy-dd .tf-dd-menu {
  left: 0;
  right: auto;
  grid-template-columns: 1fr;
  gap: 0.25rem;
  min-width: 11.5rem;
  padding: 0.375rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
#feed-strategy-dd .tf-dd-item {
  text-align: left;
  padding: 0.625rem 0.75rem;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-xs);
  transition: background 0.14s, color 0.14s;
}
/* Active row = the system's lit-switch: orange gradient + accent glow, exactly like an
   active chip. Only one row is lit at a time. */
#feed-strategy-dd .tf-dd-item.active {
  color: var(--on-accent);
  background: linear-gradient(180deg, var(--accent-2), var(--accent));
  border-color: transparent;
  box-shadow: 0 3px 12px rgba(255, 138, 0, 0.28);
}
@media (hover: hover) {
  #feed-strategy-dd .tf-dd-item:not(.active):hover { background: var(--surface-2); }
}

/* ── Screener (home): coin strip + live chart ────────────── */
/* Top bar: a pinned search pill (always visible) + a horizontal, finger-
   scrollable coin strip (like the pair chips in binary-options apps). */
.screener-stripbar {
  flex: 0 0 auto;
  display: flex;
  align-items: stretch;
  gap: 0.5rem;
  padding: 0.625rem 1rem 0.375rem;
}

/* Search magnifier: a SQUARE that matches the coin-chip height (aspect-ratio 1 + stretch),
   not the old tall 38px-wide vertical bar. Sits level with the ticker chips. */
.coin-search-pill {
  flex: 0 0 auto;
  align-self: stretch;
  aspect-ratio: 1 / 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-xs);
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  color: var(--muted);
  cursor: pointer;
  transition: border-color 0.14s, background 0.14s, color 0.14s, transform 0.08s;
}
.coin-search-pill:active { transform: scale(0.94); border-color: var(--accent); color: var(--accent); }
.search-ico { width: 1.125rem; height: 1.125rem; display: block; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; }

.screener-strip {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  gap: 0.5rem;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  /* No scroll-snap: it made wheel/drag scrolling stutter as it tugged chips to a
     stop. Free scrolling reads smoother. */
  scroll-behavior: smooth;
  /* Scrollbar hidden — the chips are obviously swipeable, and the visible bar read like a
     system scrollbar. Drag/swipe still works. */
  scrollbar-width: none;
}
.screener-strip::-webkit-scrollbar { display: none; }
/* Favourite star pinned to each strip chip's top-right corner. Tapping it flips
   the favourite state in place (handled by the chip's click handler). */
.coin-fav-btn {
  position: absolute;
  top: 0.125rem;
  right: 0.125rem;
  width: 1.25rem;
  height: 1.25rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.75rem;
  line-height: 1;
  color: var(--muted);
  transition: color 0.12s, transform 0.08s;
}
.coin-fav-btn:active { transform: scale(0.85); }
.coin-fav-btn.on { color: var(--accent); }
.strip-hint {
  display: flex;
  align-items: center;
  font-size: 0.75rem;
  color: var(--muted);
  padding: 0.375rem 0.25rem;
  white-space: nowrap;
}

.coin-chip {
  position: relative; /* anchor for the top-right favourite star */
  scroll-snap-align: start;
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.125rem;
  min-width: 4.625rem;
  padding: 0.375rem 1.375rem 0.375rem 0.625rem; /* extra right room so the star clears the ticker */
  border-radius: var(--radius-xs);
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  color: var(--text);
  font-family: inherit;
  cursor: pointer;
  transition: background 0.14s, border-color 0.14s, transform 0.08s;
}
.coin-chip:active { transform: scale(0.96); }
.coin-chip.active {
  border-color: rgba(255, 138, 0, 0.5);
  background: rgba(255, 138, 0, 0.1);
}
.coin-sym { font-size: 0.75rem; font-weight: 800; color: var(--title); letter-spacing: 0.0125rem; }
/* tabular-nums = every digit the same width, so live price/percent updates don't
   reflow the chip (see fmtStripPrice, which also keeps the string length fixed). */
.coin-row { display: flex; align-items: baseline; gap: 0.375rem; font-variant-numeric: tabular-nums; }
.coin-px { font-size: 0.6875rem; font-weight: 600; color: var(--neutral); }
.coin-chg { font-size: 0.6875rem; font-weight: 700; color: var(--muted); }
.coin-chg.up { color: var(--up); }
.coin-chg.down { color: var(--down); }

.screener-head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  /* Tightened top padding pulls this row up closer to the coin strip above (the tools
     buttons already give it height), so the header block reads as one tight stack. */
  padding: 0.25rem 1rem 0.5rem;
  border-bottom: 1px solid var(--border-soft);
}
/* Symbol + price take the space the tools leave; the symbol truncates first so the
   price (котировка) is never clipped or pushed onto the tool squares. */
.screener-sym {
  display: flex;
  align-items: baseline;
  gap: 0.4375rem;
  margin-right: auto;
  min-width: 0;
  flex: 0 1 auto;
  overflow: hidden;
  /* Small right nudge so the bold ticker optically lines up with the stats numbers in
     the row below (24Н · Объём …) instead of jutting out past them to the left. */
  padding-left: 0.25rem;
}
.screener-symbol {
  font-size: 1rem;
  font-weight: 800;
  color: var(--title);
  letter-spacing: 0.0125rem;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.screener-price { font-size: 0.875rem; font-weight: 700; color: var(--neutral); flex: 0 0 auto; white-space: nowrap; }
.screener-price.up { color: var(--up); }
.screener-price.down { color: var(--down); }

/* Timeframe pill on the screener header: solid to match the flat tool squares
   (the shared glass .tf-dd-btn is only for the detail view). */
.screener-head .tf-dd-btn {
  font-size: 0.75rem;
  padding: 0.4375rem 0.75rem;
  background: var(--surface-2);
  border-color: var(--border-soft);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.screener-head .tf-dd.open .tf-dd-btn { border-color: var(--accent); }

.screener-feed-btn {
  flex: 0 0 auto;
  font-family: inherit;
  font-size: 0.75rem;
  font-weight: 700;
  padding: 0.4375rem 0.75rem;
  border-radius: 62.4375rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--accent);
  cursor: pointer;
  transition: background 0.14s, border-color 0.14s;
}
.screener-feed-btn:active { border-color: var(--accent); background: rgba(255, 138, 0, 0.14); }

/* Chart tool buttons (ruler / fit) grouped next to the timeframe selector. */
.tool-btn {
  flex: 0 0 auto;
  width: 1.875rem;
  height: 1.875rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.875rem;
  line-height: 1;
  border-radius: var(--radius-xs);
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  color: var(--muted);
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, color 0.12s, transform 0.08s;
}
.tool-btn:active { transform: scale(0.94); }
.tool-btn.active {
  color: var(--on-accent);
  border-color: transparent;
  background: linear-gradient(180deg, var(--accent-2), var(--accent));
}

.screener-chart { flex: 1; min-height: 0; position: relative; }
.screener-chart a { display: none !important; }
.screener-chart.measuring { touch-action: none; }
.screener-chart.drawing { touch-action: none; }
/* While dragging a drawn line: no page/chart pan, grab cursor. */
.screener-chart.editing { touch-action: none; cursor: grabbing; }

/* User-drawing overlay: trendlines projected in pixel space so their slope is exactly
   what the user drew (re-projected on every chart move). pointer-events:none so the
   chart still handles pan/zoom; we hit-test the shapes ourselves. */
.draw-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 3;
  pointer-events: none;
  overflow: hidden;
}
.draw-line { stroke: var(--accent); stroke-width: 2; stroke-linecap: round; }
.draw-rect { fill: rgba(255, 138, 0, 0.10); stroke: var(--accent); stroke-width: 1.5; }
/* Endpoint handles for the selected shape (TradingView-style): white dot with an
   accent ring. Purely visual — hit-testing is done in JS on pixel coords. */
.draw-handle { fill: #fff; stroke: var(--accent); stroke-width: 2; }

/* Volume-profile overlay: horizontal volume-by-price bars, below the drawings
   overlay so user shapes sit on top. pointer-events:none — non-interactive. */
.vp-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 2;
  pointer-events: none;
  overflow: hidden;
}
.vp-bar { fill: rgba(120, 144, 180, 0.28); }
.vp-bar.poc { fill: rgba(255, 138, 0, 0.42); }

/* Side readout panels — plain-language plates that replace the on-chart indicator
   curves so beginners read a verdict, not a plot. Stacked top-left in .chart-panels
   (clear of the right price axis and bottom time axis), non-interactive so they never
   steal a pan/draw gesture. Two of them: «Поток ордеров» (CVD) and «Индикаторы».
   Each is shown only while its source is on (.show is toggled in JS). */
.chart-panels {
  position: absolute;
  left: 8px;
  top: 8px;
  z-index: 5;
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-width: 66%;
  pointer-events: none;
}
.of-panel {
  min-width: 118px;
  padding: 6px 9px 7px;
  border-radius: var(--radius-2xs);
  background: rgba(18, 18, 18, 0.82);
  border: 1px solid var(--border);
  font-variant-numeric: tabular-nums;
  display: none;
}
.of-panel.show { display: block; }
.of-panel-title {
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 5px;
}
.of-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  line-height: 1.55;
}
.of-key { font-size: 10px; font-weight: 600; color: var(--muted); }
.of-val { font-size: 11px; font-weight: 700; white-space: nowrap; }
.of-val.pos { color: var(--up); }
.of-val.neg { color: var(--down); }
.of-val.neu { color: var(--title); }
/* The combined verdict row — set apart from the raw metric rows: it's the «куда лучше»
   headline, so it reads a touch bigger with a divider under it. */
.of-row.of-signal {
  margin-bottom: 3px;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--border);
}
.of-row.of-signal .of-key { color: var(--title); }
.of-row.of-signal .of-val { font-size: 12px; }

/* Центрировать — small labelled control tucked into the chart corner, clear of the
   right price axis and the bottom time axis so it never covers quotes or candles. */
.chart-fit-corner {
  position: absolute;
  left: 10px;      /* bottom-left: fully clear of the right price axis (the quotes) */
  bottom: 38px;    /* clear of the bottom time axis */
  z-index: 5;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 3px;
  padding: 2px 5px;
  border-radius: var(--radius-2xs);
  background: rgba(28, 28, 28, 0.7);
  border: 1px solid var(--border);
  color: var(--muted);
  opacity: 0.7;
  cursor: pointer;
  touch-action: manipulation;
  transition: opacity 0.12s, color 0.12s, border-color 0.12s;
}
.chart-fit-corner svg {
  width: 10px;
  height: 10px;
  stroke: currentColor;
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.chart-fit-corner .fit-label {
  font-size: 7px;
  font-weight: 700;
  letter-spacing: 0.01em;
  line-height: 1;
  white-space: nowrap;
}
.chart-fit-corner:active { opacity: 1; color: var(--title); border-color: var(--accent); background: rgba(255, 138, 0, 0.16); }

/* Header chart tools: standalone square buttons (Линейка · Магнит) + a pencil
   dropdown for the drawing tools. All share .tool-btn; the pencil menu opens like
   the timeframe selector — a vertical list of icon+label rows. */
.screener-tools { display: flex; align-items: center; gap: 0.3125rem; flex: 0 0 auto; }
.tools-dd { position: relative; flex: 0 0 auto; }
.tool-ico { width: 1.125rem; height: 1.125rem; display: block; stroke: currentColor; fill: none; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.tool-ico circle { fill: currentColor; stroke: none; }
/* Pencil button glows on open; the shared .tool-btn.active handles the armed state. */
.tools-dd.open .tool-btn { border-color: var(--accent); color: var(--accent); }
.tools-menu {
  display: none; /* opened by toggling .tools-dd.open */
  position: absolute;
  right: 0;
  top: calc(100% + 0.4375rem);
  z-index: 30;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.4375rem;
  min-width: 13.25rem;
  max-height: 72vh;
  overflow-y: auto;
  background: var(--glass-strong);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
}
.tools-dd.open .tools-menu { display: flex; }
/* The chart-type menu (Свечи / Линия) is the leftmost right-aligned tool, so the
   full 212px width opening leftward runs off the left edge of the screen. Its two
   labels are short — shrink it so it stays on-screen. */
#screener-charttype-dd .tools-menu,
#detail-charttype-dd .tools-menu { min-width: 8.25rem; }
.tools-item {
  display: flex;
  align-items: center;
  gap: 0.6875rem;
  width: 100%;
  font-family: inherit;
  font-size: 0.8125rem;
  font-weight: 600;
  padding: 0.625rem 0.75rem;
  border-radius: var(--radius-xs);
  background: transparent;
  border: 1px solid transparent;
  color: var(--text);
  cursor: pointer;
  text-align: left;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.tools-item:active { background: var(--glass); }
.tools-item.active { color: var(--accent); background: rgba(255, 138, 0, 0.10); border-color: rgba(255, 138, 0, 0.30); }
.tools-ico { width: 1.125rem; height: 1.125rem; flex: 0 0 auto; display: block; stroke: currentColor; fill: none; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.tools-ico circle { fill: currentColor; stroke: none; }
/* «?» help badge at the far right of each indicator row — a small round chip that opens
   the plain-language card. margin-left:auto pins it right of the label. */
.ind-help {
  margin-left: auto;
  flex: 0 0 auto;
  width: 1.125rem;
  height: 1.125rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.75rem;
  font-weight: 700;
  line-height: 1;
  color: var(--muted);
  background: var(--glass);
  border: 1px solid var(--border-glass);
  border-radius: 50%;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.ind-help:active { color: var(--accent); border-color: var(--accent); background: rgba(255, 138, 0, 0.14); }

/* Indicator help card — a centered modal opened by the «?» badge. Explains an indicator
   in plain words (same voice as the on-chart plates). Above everything, dismissed by the
   backdrop or the «Понятно» button. */
.ind-help-modal {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(0, 0, 0, 0.6);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  align-items: center;
  justify-content: center;
  padding: 1.25rem;
}
.ind-help-modal.show { display: flex; }
.ind-help-card {
  width: 100%;
  max-width: 20rem;
  max-height: 80vh;
  overflow-y: auto;
  padding: 1.125rem 1.25rem 1rem;
  background: var(--glass-strong);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
}
.ind-help-title {
  font-size: 1rem;
  font-weight: 800;
  color: var(--title);
  margin-bottom: 0.5rem;
}
.ind-help-body { font-size: 0.8125rem; line-height: 1.5; color: var(--text); }
.ind-help-body p { margin: 0 0 0.4rem; }
.ind-help-body p:last-child { margin-bottom: 0; }
.ind-help-body p.sp { height: 0.35rem; margin: 0; }
.ind-help-close {
  margin-top: 0.9rem;
  width: 100%;
  padding: 0.625rem;
  font-family: inherit;
  font-size: 0.8125rem;
  font-weight: 700;
  color: #141414;
  background: var(--accent);
  border: none;
  border-radius: var(--radius-xs);
  cursor: pointer;
}
.ind-help-close:active { opacity: 0.85; }

/* Market-stats readout — a slim row ABOVE the chart (no longer floating over the
   candles): 24h change · 24h volume · NATR, divider-spaced, tabular digits. */
.screener-stats {
  flex: 0 0 auto;
  display: flex;
  flex-direction: row;
  align-items: center;
  /* Spread the 4 readouts (24ч · Объём · NATR · OI) evenly across the width so the
     plate reads as an aligned row rather than a left-huddled cluster. */
  justify-content: space-between;
  gap: 0.375rem;
  padding: 0.5rem 1rem;
  border-bottom: 1px solid var(--border-soft);
  font-variant-numeric: tabular-nums;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.screener-stats::-webkit-scrollbar { display: none; }
.screener-stats:empty { display: none; }
.sstat {
  display: flex;
  align-items: baseline;
  gap: 0.3125rem;
  white-space: nowrap;
}
.sstat-l {
  color: var(--stat-header);
  font-size: 0.5625rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.sstat-v {
  font-weight: 700;
  font-size: 0.75rem;
  color: var(--title);
  font-variant-numeric: tabular-nums;
}
.sstat-v.up { color: var(--up); }
.sstat-v.down { color: var(--down); }

.screener-empty {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 8;
  color: var(--muted);
  font-size: 0.8125rem;
}

/* Home (‹) button sits at the far left of the feed top bar. */
.topbar-home { font-size: 1.625rem; padding-bottom: 0.1875rem; }

/* ── Screener search / filter panel (overlay) ────────────── */
.search-panel {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  /* position:fixed puts this OUTSIDE #app, so it inherits none of #app's safe-area
     padding and has to inset itself — otherwise the search field lands under the
     status bar and Telegram's ✕/⋮ in fullscreen. */
  padding-top: var(--pad-top);
  padding-bottom: var(--sa-bottom);
  padding-left: var(--sa-left);
  padding-right: var(--sa-right);
}

.search-head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.875rem 1rem 0.625rem;
}
.search-input {
  flex: 1 1 auto;
  min-width: 0;
  font-family: inherit;
  font-size: 0.9375rem;
  font-weight: 600;
  padding: 0.6875rem 0.875rem 0.6875rem 2.5rem;
  border-radius: var(--radius-sm);
  background: var(--surface) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' stroke='%238a8a8a' stroke-width='2' stroke-linecap='round'><circle cx='7' cy='7' r='5'/><line x1='10.5' y1='10.5' x2='15' y2='15'/></svg>") no-repeat 14px center;
  background-size: 16px 16px;
  border: 1px solid var(--border);
  color: var(--title);
  outline: none;
  transition: border-color 0.14s;
}
.search-input::placeholder { color: var(--muted); font-weight: 500; }
.search-input:focus { border-color: var(--accent); }
.search-close {
  flex: 0 0 auto;
  width: 2.375rem;
  height: 2.375rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1rem;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  cursor: pointer;
}
.search-close:active { background: #303030; }

/* «Добавить на главный экран» — a quiet, full-width brand pill tucked into the search
   panel (only surfaced by app.js on Bot API ≥8.0 clients where it isn't already added). */
.add-home-btn {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin: 0 1rem 0.5rem;
  padding: 0.625rem 0.875rem;
  font-family: inherit;
  font-size: 0.8125rem;
  font-weight: 600;
  border-radius: var(--radius-sm);
  background: rgba(255, 138, 0, 0.10);
  border: 1px solid rgba(255, 138, 0, 0.30);
  color: var(--accent);
  cursor: pointer;
  transition: background 0.14s, border-color 0.14s, transform 0.14s;
}
.add-home-btn:active { transform: scale(0.98); background: rgba(255, 138, 0, 0.18); border-color: var(--accent); }
.add-home-btn .ah-ico {
  flex: 0 0 auto;
  width: 1rem;
  height: 1rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.search-sorts {
  flex: 0 0 auto;
  display: flex;
  gap: 0.375rem;
  padding: 0.125rem 1rem 0.75rem;
}
.sort-chip {
  /* All four filters share the row equally so they fit in one line — no scrolling. */
  flex: 1 1 0;
  min-width: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.3125rem;
  font-family: inherit;
  font-size: 0.75rem;
  font-weight: 600;
  padding: 0.5rem 0.375rem;
  border-radius: 62.4375rem;
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  color: var(--muted);
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.14s, color 0.14s, border-color 0.14s;
}
.sort-lbl {
  min-width: 0;
  line-height: 1;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sort-chip:active { transform: scale(0.96); }
/* Icon carries a tone tint (green up / red down / amber star) so the row is
   scannable even before you read the labels. */
.sort-ico {
  flex: 0 0 auto;
  display: inline-flex;
  width: 0.875rem;
  height: 0.875rem;
  color: var(--muted);
}
.sort-ico svg {
  width: 0.875rem;
  height: 0.875rem;
  display: block;
  stroke: currentColor;
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.sort-ico.up { color: var(--up); }
.sort-ico.down { color: var(--down); }
.sort-ico.star { color: var(--accent-2); }
.sort-ico.star svg { fill: currentColor; stroke: none; }
.sort-chip.active {
  color: var(--on-accent);
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: none;
}
/* On the active (orange) chip the tinted icon would clash — flip it to white. */
.sort-chip.active .sort-ico { color: var(--on-accent); }

.search-list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 0 0.375rem 0.75rem;
}
.search-list::-webkit-scrollbar { display: none; }

.srow {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.6875rem 0.5rem;
  border-bottom: 1px solid var(--border-soft);
  border-radius: var(--radius-xs);
  cursor: pointer;
  transition: background 0.1s;
}
.srow:active { background: var(--surface); }
.srow.active { background: rgba(255, 138, 0, 0.08); }

.srow-star {
  flex: 0 0 auto;
  width: 1.875rem;
  height: 1.875rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1rem;
  line-height: 1;
  background: transparent;
  border: none;
  color: var(--muted);
  cursor: pointer;
}
.srow-star.on { color: var(--accent); }

/* Exchange-style watchlist row: ticker + venue/volume subtitle on the left,
   price over coloured %change stacked on the right. */
.srow-meta {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.1875rem;
}
.srow-line1 { display: flex; align-items: center; gap: 0.4375rem; min-width: 0; }
.srow-line2 { display: flex; align-items: center; gap: 0.4375rem; min-height: 0.9375rem; }
.srow-sym { font-size: 0.9375rem; font-weight: 800; color: var(--title); letter-spacing: 0.0125rem; }
.srow-vol { font-size: 0.75rem; font-weight: 600; color: var(--muted); font-variant-numeric: tabular-nums; }
.srow-right {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.125rem;
  text-align: right;
}
.srow-px { font-size: 0.875rem; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.srow-chg { font-size: 0.8125rem; font-weight: 700; color: var(--muted); font-variant-numeric: tabular-nums; }
.srow-chg.up { color: var(--up); }
.srow-chg.down { color: var(--down); }
/* Mini exchange logos under the ticker: which venues list this coin. */
.srow-ex { display: flex; align-items: center; gap: 0.3125rem; min-height: 0.9375rem; }
.ex-badge {
  display: inline-flex;
  width: 0.9375rem;
  height: 0.9375rem;
  line-height: 0;
  border-radius: 0.25rem;
  overflow: hidden;
}
.ex-badge svg { display: block; width: 0.9375rem; height: 0.9375rem; }
/* Bybit shows its full wordmark → a wider pill than the square venue tiles. */
.ex-badge--bybit { width: auto; }
.ex-badge--bybit svg { width: 2.375rem; height: 0.9375rem; }

.search-empty {
  flex: 0 0 auto;
  text-align: center;
  color: var(--muted);
  font-size: 0.8125rem;
  padding: 1.875rem 1rem;
}

/* ── Access denial screen (closed-channel gate) ──────────────────────────────
   Covers the whole app when the server refuses a session: the shell may already
   have painted, and a half-rendered screener behind a refusal reads as a bug.
   One card, one action — the same visual language as the rest of the app. */
.access-screen {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  background: linear-gradient(180deg, var(--bg-top) 0%, var(--bg) 38%);
}
.access-screen.hidden { display: none; }
.access-card {
  width: 100%;
  max-width: 22rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.75rem 1.25rem 1.5rem;
  text-align: center;
}
.access-icon { font-size: 2rem; line-height: 1; margin-bottom: 0.875rem; }
.access-title {
  color: var(--title);
  font-size: 1.0625rem;
  font-weight: 700;
  margin-bottom: 0.5rem;
}
.access-text {
  color: var(--muted);
  font-size: 0.875rem;
  line-height: 1.45;
}
.access-btn {
  display: block;
  width: 100%;
  margin-top: 1.25rem;
  padding: 0.8125rem 1rem;
  border: 0;
  border-radius: var(--radius);
  background: linear-gradient(180deg, var(--accent-2) 0%, var(--accent) 100%);
  color: var(--on-accent);
  font-size: 0.9375rem;
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
}
.access-btn:active { background: var(--accent-press); }
.access-btn:disabled { opacity: 0.6; }

/* Terms splash — the access card with a readable list instead of a paragraph.
   Left-aligned on purpose: the centred access copy is two lines, this is four
   bullets, and centred multi-line text gives every line a different left edge
   for the eye to find. */
.terms-card { text-align: left; }
.terms-card .access-icon,
.terms-card .access-title { text-align: center; }
.terms-list {
  margin: 0.875rem 0 0;
  padding-left: 1.125rem;
  color: var(--muted);
  font-size: 0.875rem;
  line-height: 1.45;
}
.terms-list li { margin-bottom: 0.5rem; }
.terms-link {
  display: block;
  margin-top: 0.875rem;
  color: var(--accent);
  font-size: 0.875rem;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}
.terms-note {
  margin-top: 0.75rem;
  color: var(--muted);
  font-size: 0.75rem;
  line-height: 1.4;
  text-align: center;
}
