// ============================================================
// Раздел «Рынки» — терминал рынка: Обзор / Крипта / Новости / Фондовый.
// Пузыри (canvas-физика, hover, свечение), теплокарта, гейдж страха/жадности,
// широта рынка, секторы, топ-муверы, пульс-таблица, лента новостей, тикер.
// Общий для desktop+mobile. Экспортирует window.MarketsView({ embed }).
// Данные: window.MobileAPI.loadMarkets{Crypto,Stocks,Global,Sectors,News}.
// ============================================================
(function () {
  // ---------- форматтеры ----------
  const fmtBig = (n) => {
    if (n == null) return "—";
    const a = Math.abs(n);
    if (a >= 1e12) return "$" + (n / 1e12).toFixed(2) + "T";
    if (a >= 1e9) return "$" + (n / 1e9).toFixed(2) + "B";
    if (a >= 1e6) return "$" + (n / 1e6).toFixed(2) + "M";
    if (a >= 1e3) return "$" + (n / 1e3).toFixed(1) + "K";
    return "$" + Number(n).toFixed(0);
  };
  const fmtPrice = (n) => n == null ? "—" :
    n >= 1000 ? Number(n).toLocaleString("en-US", { maximumFractionDigits: 0 }) :
    n >= 1 ? Number(n).toFixed(2) : n >= 0.01 ? Number(n).toFixed(4) : Number(n).toPrecision(3);
  const sgn = (n, d = 2) => n == null ? "—" : (n >= 0 ? "+" : "") + Number(n).toFixed(d) + "%";
  const chCol = (ch) => (ch || 0) >= 0 ? "var(--long,#16b979)" : "var(--short,#ef4444)";
  const rgba = (ch, a) => (ch || 0) >= 0 ? `rgba(22,185,121,${a})` : `rgba(239,68,68,${a})`;
  const heatBg = (ch) => rgba(ch, 0.12 + Math.min(Math.abs(ch || 0) / 14, 1) * 0.34);
  const timeAgo = (ms) => {
    if (!ms) return "";
    const s = Math.max(0, (Date.now() - ms) / 1000);
    if (s < 60) return "только что";
    if (s < 3600) return Math.floor(s / 60) + "м назад";
    if (s < 86400) return Math.floor(s / 3600) + "ч назад";
    return Math.floor(s / 86400) + "д назад";
  };
  const fngRu = (l) => ({ "Extreme Fear": "Крайний страх", "Fear": "Страх", "Neutral": "Нейтрально", "Greed": "Жадность", "Extreme Greed": "Крайняя жадность" }[l] || l || "");
  const fngColor = (v) => v == null ? "#888" : v < 25 ? "#ef4444" : v < 45 ? "#f59e0b" : v < 55 ? "#eab308" : v < 75 ? "#84cc16" : "#16b979";
  const CAT_RU = { crypto: "Крипто", stocks: "Акции", general: "Общее", forex: "Форекс" };
  const SRC_COLOR = { Cointelegraph: "#f5b301", CoinDesk: "#3b82f6", Decrypt: "#a855f7", ForkLog: "#16b979", BeInCrypto: "#ef7f1a", Incrypted: "#00c2b8", "РБК": "#e4002b" };
  const srcColor = (s) => SRC_COLOR[s] || "var(--accent,#3b82f6)";
  const langBadge = (l) => l === "ru" ? "RU" : l === "en" ? "EN" : (l || "").toUpperCase();

  // ---------- одноразовый инжект анимаций/классов ----------
  function useStyleOnce() {
    useEffect(() => {
      if (document.getElementById("mk-style")) return;
      const el = document.createElement("style");
      el.id = "mk-style";
      el.textContent = `
      @keyframes mkMarquee { from{transform:translateX(0)} to{transform:translateX(-50%)} }
      @keyframes mkPulse { 0%,100%{opacity:.35} 50%{opacity:1} }
      @keyframes mkUp { from{opacity:0;transform:translateY(6px)} to{opacity:1;transform:none} }
      .mk-live{width:7px;height:7px;border-radius:50%;background:var(--long,#16b979);box-shadow:0 0 8px var(--long,#16b979);animation:mkPulse 1.6s ease-in-out infinite;display:inline-block}
      .mk-card{background:var(--bg-1,#0f0f10);border:1px solid var(--border,#222);border-radius:14px;transition:border-color .18s,transform .18s,box-shadow .18s}
      .mk-card:hover{border-color:var(--accent-dim,rgba(59,130,246,.4))}
      .mk-row{transition:background .12s}
      .mk-row:hover{background:var(--bg-2,rgba(255,255,255,.03))}
      .mk-news:hover{transform:translateY(-2px);box-shadow:0 8px 22px rgba(0,0,0,.35);border-color:var(--accent,#3b82f6)!important}
      .mk-tab{position:relative;border:none;background:none;cursor:pointer;font-weight:700;letter-spacing:.02em;padding:9px 2px;transition:color .15s}
      .mk-tab::after{content:"";position:absolute;left:0;right:0;bottom:0;height:2px;border-radius:2px;background:var(--accent,#3b82f6);transform:scaleX(0);transition:transform .2s}
      .mk-tab.on::after{transform:scaleX(1)}
      .mk-scroll::-webkit-scrollbar{height:6px;width:6px}
      .mk-scroll::-webkit-scrollbar-thumb{background:var(--border,#333);border-radius:6px}
      `;
      document.head.appendChild(el);
    }, []);
  }

  // ============================================================
  // Пузыри — canvas: физика + drag + hover-подсказка + свечение
  // ============================================================
  function Bubbles({ items, field, sizeBy, embed, onPick }) {
    const wrapRef = useRef(); const canvasRef = useRef();
    const S = useRef({ bubbles: [], drag: null, raf: 0, w: 0, h: 0, mx: -1, my: -1, hover: null, t: 0 }).current;

    useEffect(() => {
      const list = (items || []).filter(c => c[field] != null);
      const sizeVal = (c) => sizeBy === "cap" ? (c.market_cap || Math.abs(c[field]) * 1e6 || 1) : (Math.abs(c[field]) || 0.01);
      const maxV = Math.max(1, ...list.map(sizeVal));
      const rMin = embed === "mobile" ? 13 : 17, rMax = embed === "mobile" ? 40 : 72;
      const prev = {}; S.bubbles.forEach(b => { prev[b.id] = b; });
      S.bubbles = list.slice(0, embed === "mobile" ? 70 : 150).map(c => {
        const r = rMin + Math.sqrt(sizeVal(c) / maxV) * (rMax - rMin);
        const p = prev[c.id];
        return {
          id: c.id || c.symbol, sym: c.symbol, ch: c[field], price: c.price, name: c.name, mcap: c.market_cap,
          r, tr: r,
          x: p ? p.x : (S.w ? Math.random() * S.w : 40 + Math.random() * 260),
          y: p ? p.y : (S.h ? Math.random() * S.h : 40 + Math.random() * 260),
          vx: p ? p.vx : 0, vy: p ? p.vy : 0, ph: Math.random() * 6.28,
        };
      });
    }, [items, field, sizeBy, embed]);

    useEffect(() => {
      const cv = canvasRef.current, wrap = wrapRef.current;
      if (!cv || !wrap) return;
      const ctx = cv.getContext("2d");
      const resize = () => {
        const dpr = window.devicePixelRatio || 1;
        S.w = wrap.clientWidth; S.h = wrap.clientHeight;
        cv.width = S.w * dpr; cv.height = S.h * dpr;
        cv.style.width = S.w + "px"; cv.style.height = S.h + "px";
        ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
      };
      resize();
      const ro = new ResizeObserver(resize); ro.observe(wrap);
      const pos = (e) => { const r = cv.getBoundingClientRect(); const t = e.touches ? e.touches[0] : e; return { x: t.clientX - r.left, y: t.clientY - r.top }; };
      const hit = (x, y) => { for (let i = S.bubbles.length - 1; i >= 0; i--) { const b = S.bubbles[i]; if ((x - b.x) ** 2 + (y - b.y) ** 2 <= b.r * b.r) return b; } return null; };
      const down = (e) => { const { x, y } = pos(e); const b = hit(x, y); if (b) { S.drag = { b, lx: x, ly: y, moved: false }; b.vx = 0; b.vy = 0; } };
      const move = (e) => {
        const { x, y } = pos(e); S.mx = x; S.my = y;
        if (!S.drag) return; if (e.cancelable) e.preventDefault();
        const d = S.drag; if (Math.abs(x - d.lx) + Math.abs(y - d.ly) > 2) d.moved = true;
        d.b.x = x; d.b.y = y; d.b.vx = x - d.lx; d.b.vy = y - d.ly; d.lx = x; d.ly = y;
      };
      const up = () => { if (S.drag && !S.drag.moved && onPick) onPick(S.drag.b); S.drag = null; };
      const leave = () => { S.mx = -1; S.my = -1; };
      cv.addEventListener("mousedown", down); window.addEventListener("mousemove", move); window.addEventListener("mouseup", up); cv.addEventListener("mouseleave", leave);
      cv.addEventListener("touchstart", down, { passive: false }); cv.addEventListener("touchmove", move, { passive: false }); window.addEventListener("touchend", up);

      const roundRect = (x, y, w, h, r) => { ctx.beginPath(); ctx.moveTo(x + r, y); ctx.arcTo(x + w, y, x + w, y + h, r); ctx.arcTo(x + w, y + h, x, y + h, r); ctx.arcTo(x, y + h, x, y, r); ctx.arcTo(x, y, x + w, y, r); ctx.closePath(); };

      const step = () => {
        S.t += 0.016;
        const B = S.bubbles, cx = S.w / 2, cy = S.h / 2;
        for (const b of B) {
          b.r += (b.tr - b.r) * 0.12;                                  // плавный ресайз
          if (S.drag && S.drag.b === b) continue;
          b.vx += (cx - b.x) * 0.0009; b.vy += (cy - b.y) * 0.0009;
          b.vx += Math.cos(S.t + b.ph) * 0.010;                        // лёгкое «дыхание»
          b.vy += Math.sin(S.t * 0.9 + b.ph) * 0.010;
          b.vx *= 0.9; b.vy *= 0.9; b.x += b.vx; b.y += b.vy;
          if (b.x < b.r) { b.x = b.r; b.vx *= -0.5; } if (b.x > S.w - b.r) { b.x = S.w - b.r; b.vx *= -0.5; }
          if (b.y < b.r) { b.y = b.r; b.vy *= -0.5; } if (b.y > S.h - b.r) { b.y = S.h - b.r; b.vy *= -0.5; }
        }
        for (let i = 0; i < B.length; i++) for (let j = i + 1; j < B.length; j++) {
          const a = B[i], b = B[j]; const dx = b.x - a.x, dy = b.y - a.y; const d = Math.hypot(dx, dy) || 0.01; const min = a.r + b.r + 1.5;
          if (d < min) { const ov = (min - d) / 2, nx = dx / d, ny = dy / d; if (!(S.drag && S.drag.b === a)) { a.x -= nx * ov; a.y -= ny * ov; } if (!(S.drag && S.drag.b === b)) { b.x += nx * ov; b.y += ny * ov; } }
        }
        const hov = (S.mx >= 0) ? hit(S.mx, S.my) : null; S.hover = hov;
        ctx.clearRect(0, 0, S.w, S.h);
        for (const b of B) {
          const big = Math.abs(b.ch) >= 8, isHov = b === hov;
          const grad = ctx.createRadialGradient(b.x - b.r * 0.3, b.y - b.r * 0.35, b.r * 0.1, b.x, b.y, b.r);
          grad.addColorStop(0, rgba(b.ch, isHov ? 0.62 : 0.42));
          grad.addColorStop(1, rgba(b.ch, isHov ? 0.30 : 0.14));
          if (big || isHov) { ctx.shadowColor = rgba(b.ch, 0.9); ctx.shadowBlur = isHov ? 22 : 12; } else ctx.shadowBlur = 0;
          ctx.beginPath(); ctx.arc(b.x, b.y, b.r, 0, 6.2832); ctx.fillStyle = grad; ctx.fill();
          ctx.shadowBlur = 0;
          ctx.lineWidth = isHov ? 2.4 : 1.5; ctx.strokeStyle = rgba(b.ch, isHov ? 1 : 0.8); ctx.stroke();
          if (b.r >= 16) {
            ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillStyle = "#fff";
            const fs = Math.max(9, Math.min(b.r * 0.44, 22));
            ctx.font = `800 ${fs}px Geist,system-ui,sans-serif`;
            ctx.fillText(b.sym, b.x, b.y - (b.r >= 26 ? fs * 0.42 : 0));
            if (b.r >= 26) { ctx.font = `600 ${fs * 0.7}px ui-monospace,Menlo,monospace`; ctx.fillStyle = "rgba(255,255,255,.92)"; ctx.fillText(sgn(b.ch, 1), b.x, b.y + fs * 0.78); }
          }
        }
        if (hov) {                                                     // подсказка
          const lines = [hov.name || hov.sym, "Цена " + fmtPrice(hov.price), "24ч " + sgn(hov.ch, 2), hov.mcap ? "Кап " + fmtBig(hov.mcap) : null].filter(Boolean);
          ctx.font = "600 12px ui-monospace,Menlo,monospace";
          const wd = Math.max(...lines.map(l => ctx.measureText(l).width)) + 20;
          const hh = lines.length * 16 + 10;
          let tx = hov.x + hov.r + 10, ty = hov.y - hh / 2;
          if (tx + wd > S.w) tx = hov.x - hov.r - 10 - wd;
          if (ty < 4) ty = 4; if (ty + hh > S.h) ty = S.h - hh - 4;
          ctx.fillStyle = "rgba(10,10,12,.94)"; ctx.strokeStyle = rgba(hov.ch, 0.8); ctx.lineWidth = 1;
          roundRect(tx, ty, wd, hh, 8); ctx.fill(); ctx.stroke();
          ctx.textAlign = "left"; ctx.textBaseline = "top";
          lines.forEach((l, i) => { ctx.fillStyle = i === 0 ? "#fff" : (i === 2 ? chCol(hov.ch) : "rgba(255,255,255,.7)"); ctx.font = (i === 0 ? "800 12.5px" : "600 12px") + " ui-monospace,Menlo,monospace"; ctx.fillText(l, tx + 10, ty + 6 + i * 16); });
        }
        S.raf = requestAnimationFrame(step);
      };
      S.raf = requestAnimationFrame(step);
      return () => { cancelAnimationFrame(S.raf); ro.disconnect(); cv.removeEventListener("mousedown", down); window.removeEventListener("mousemove", move); window.removeEventListener("mouseup", up); cv.removeEventListener("mouseleave", leave); cv.removeEventListener("touchstart", down); cv.removeEventListener("touchmove", move); window.removeEventListener("touchend", up); };
    }, []);

    return (
      <div ref={wrapRef} style={{ position: "relative", width: "100%", height: embed === "mobile" ? "60vh" : "min(64vh, 640px)", minHeight: 340, borderRadius: 14, overflow: "hidden", background: "radial-gradient(120% 120% at 50% 40%, var(--bg-1,#0e0e10), var(--bg,#080809))", border: "1px solid var(--border,#222)", touchAction: "none", cursor: "grab" }}>
        <canvas ref={canvasRef} style={{ display: "block" }} />
      </div>
    );
  }

  // ============================================================
  // Теплокарта — плитки
  // ============================================================
  function Heat({ items, field, embed, onPick }) {
    const min = embed === "mobile" ? 88 : 112;
    return (
      <div style={{ display: "grid", gridTemplateColumns: `repeat(auto-fill, minmax(${min}px, 1fr))`, gap: 6 }}>
        {(items || []).map((c, i) => {
          const ch = c[field];
          return (
            <div key={c.id || c.symbol || i} className="mk-row" onClick={() => onPick && onPick({ sym: c.symbol, name: c.name, ch, price: c.price, mcap: c.market_cap })}
              style={{ borderRadius: 10, padding: "9px 10px", cursor: "pointer", background: heatBg(ch), border: "1px solid " + rgba(ch, 0.5), minHeight: 62, display: "flex", flexDirection: "column", justifyContent: "center" }}>
              <div style={{ fontWeight: 800, fontSize: 13, color: "#fff" }}>{c.symbol}</div>
              <div className="mono" style={{ fontSize: 10.5, color: "rgba(255,255,255,.72)" }}>{fmtPrice(c.price)}</div>
              <div className="mono" style={{ fontWeight: 700, fontSize: 12.5, color: (ch || 0) >= 0 ? "#7ef0bf" : "#ff9a9a", marginTop: 2 }}>{sgn(ch)}</div>
            </div>
          );
        })}
      </div>
    );
  }

  // ============================================================
  // Гейдж «Страх и жадность» (SVG)
  // ============================================================
  function Gauge({ value, label }) {
    const v = value == null ? 50 : Math.max(0, Math.min(100, value));
    const cx = 110, cy = 104, R = 84;
    const pol = (deg, r) => [cx + r * Math.cos(deg * Math.PI / 180), cy + r * Math.sin(deg * Math.PI / 180)];
    const arc = (a0, a1, r) => { const [x0, y0] = pol(a0, r), [x1, y1] = pol(a1, r); return `M ${x0} ${y0} A ${r} ${r} 0 0 1 ${x1} ${y1}`; };
    const val2ang = (val) => 180 + val / 100 * 180;      // 180°(лево)→360°(право)
    const segs = [[0, 25, "#ef4444"], [25, 45, "#f59e0b"], [45, 55, "#eab308"], [55, 75, "#84cc16"], [75, 100, "#16b979"]];
    const na = val2ang(v); const [nx, ny] = pol(na, R - 12);
    return (
      <div style={{ display: "flex", flexDirection: "column", alignItems: "center" }}>
        <svg width="220" height="128" viewBox="0 0 220 128">
          {segs.map(([a, b, col], i) => (
            <path key={i} d={arc(val2ang(a), val2ang(b), R)} fill="none" stroke={col} strokeWidth="13" strokeLinecap="butt" opacity="0.92" />
          ))}
          <line x1={cx} y1={cy} x2={nx} y2={ny} stroke="var(--text,#eee)" strokeWidth="3" strokeLinecap="round" />
          <circle cx={cx} cy={cy} r="6" fill="var(--text,#eee)" />
          <text x={pol(180, R + 10)[0]} y={pol(180, R + 10)[1]} fill="#ef4444" fontSize="9" textAnchor="start">0</text>
          <text x={cx} y="18" fill="#eab308" fontSize="9" textAnchor="middle">50</text>
          <text x={pol(360, R + 10)[0]} y={pol(360, R + 10)[1]} fill="#16b979" fontSize="9" textAnchor="end">100</text>
        </svg>
        <div style={{ marginTop: -8, textAlign: "center" }}>
          <div className="mono" style={{ fontSize: 30, fontWeight: 800, color: fngColor(value), lineHeight: 1 }}>{value == null ? "—" : value}</div>
          <div style={{ fontSize: 12, color: "var(--text-2,#aaa)", marginTop: 2 }}>{fngRu(label)}</div>
        </div>
      </div>
    );
  }

  // ============================================================
  // Мелкие панели
  // ============================================================
  function Panel({ title, right, children, style }) {
    return (
      <div className="mk-card" style={{ padding: 14, ...style }}>
        {title && (
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 10 }}>
            <div style={{ fontSize: 11, fontWeight: 800, letterSpacing: ".08em", textTransform: "uppercase", color: "var(--text-2,#bbb)" }}>{title}</div>
            {right != null && <div style={{ fontSize: 10.5, color: "var(--text-3,#888)" }}>{right}</div>}
          </div>
        )}
        {children}
      </div>
    );
  }

  function Stat({ label, value, sub, pos, accent }) {
    return (
      <div className="mk-card" style={{ padding: "11px 13px", minWidth: 0, overflow: "hidden" }}>
        <div style={{ fontSize: 10, color: "var(--text-3,#888)", textTransform: "uppercase", letterSpacing: ".05em", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{label}</div>
        <div className="mono" style={{ fontSize: 18, fontWeight: 800, marginTop: 3, color: accent || undefined, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{value}</div>
        {sub ? <div className="mono" style={{ fontSize: 11, marginTop: 1, color: pos != null ? (pos ? "var(--long,#16b979)" : "var(--short,#ef4444)") : "var(--text-3,#888)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{sub}</div> : null}
      </div>
    );
  }

  // широта рынка: доля растущих по таймфреймам
  function Breadth({ coins }) {
    const rows = [["ch1h", "1ч"], ["ch24h", "24ч"], ["ch7d", "7д"]];
    return (
      <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
        {rows.map(([f, l]) => {
          const arr = coins.filter(c => c[f] != null);
          const up = arr.filter(c => c[f] >= 0).length, dn = arr.length - up;
          const pctUp = arr.length ? up / arr.length * 100 : 0;
          return (
            <div key={f}>
              <div style={{ display: "flex", justifyContent: "space-between", fontSize: 11.5, marginBottom: 4 }}>
                <span style={{ color: "var(--text-2,#aaa)" }}>{l}</span>
                <span className="mono"><span style={{ color: "var(--long,#16b979)" }}>↑{up}</span> <span style={{ color: "var(--short,#ef4444)" }}>↓{dn}</span> · {pctUp.toFixed(0)}%</span>
              </div>
              <div style={{ display: "flex", height: 8, borderRadius: 5, overflow: "hidden", background: "var(--short,#ef4444)" }}>
                <div style={{ width: pctUp + "%", background: "var(--long,#16b979)" }} />
              </div>
            </div>
          );
        })}
      </div>
    );
  }

  function SectorBars({ sectors, embed }) {
    const rows = [...(sectors || [])].filter(s => s.ch24h != null).sort((a, b) => Math.abs(b.ch24h) - Math.abs(a.ch24h)).slice(0, embed === "mobile" ? 8 : 11);
    const mx = Math.max(1, ...rows.map(r => Math.abs(r.ch24h)));
    return (
      <div style={{ display: "flex", flexDirection: "column", gap: 7 }}>
        {rows.map((s, i) => (
          <div key={i} style={{ display: "grid", gridTemplateColumns: "1fr 84px 52px", alignItems: "center", gap: 8, fontSize: 12 }}>
            <span style={{ color: "var(--text-2,#bbb)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} title={s.name}>{s.name}</span>
            <div style={{ position: "relative", height: 8, borderRadius: 5, background: "var(--bg-2,#1a1a1a)" }}>
              <div style={{ position: "absolute", left: s.ch24h >= 0 ? "50%" : undefined, right: s.ch24h < 0 ? "50%" : undefined, width: (Math.abs(s.ch24h) / mx * 50) + "%", height: "100%", background: chCol(s.ch24h), borderRadius: 5 }} />
              <div style={{ position: "absolute", left: "50%", top: -1, bottom: -1, width: 1, background: "var(--border,#333)" }} />
            </div>
            <span className="mono" style={{ textAlign: "right", color: chCol(s.ch24h) }}>{sgn(s.ch24h, 1)}</span>
          </div>
        ))}
        {!rows.length && <div style={{ fontSize: 12, color: "var(--text-3,#888)" }}>Нет данных по секторам.</div>}
      </div>
    );
  }

  function Movers({ coins, field, onPick }) {
    const arr = coins.filter(c => c[field] != null);
    const gain = [...arr].sort((a, b) => b[field] - a[field]).slice(0, 6);
    const lose = [...arr].sort((a, b) => a[field] - b[field]).slice(0, 6);
    const Col = ({ title, list, up }) => (
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 10.5, fontWeight: 700, letterSpacing: ".05em", color: up ? "var(--long,#16b979)" : "var(--short,#ef4444)", marginBottom: 6 }}>{title}</div>
        {list.map((c, i) => (
          <div key={i} className="mk-row" onClick={() => onPick && onPick({ sym: c.symbol, name: c.name, ch: c[field], price: c.price, mcap: c.market_cap })}
            style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "4px 6px", borderRadius: 6, cursor: "pointer", fontSize: 12 }}>
            <span style={{ display: "flex", alignItems: "center", gap: 6, minWidth: 0 }}>
              {c.image && <img src={c.image} width="15" height="15" style={{ borderRadius: "50%" }} alt="" />}
              <b style={{ overflow: "hidden", textOverflow: "ellipsis" }}>{c.symbol}</b>
            </span>
            <span className="mono" style={{ color: chCol(c[field]) }}>{sgn(c[field], 1)}</span>
          </div>
        ))}
      </div>
    );
    return (
      <div style={{ display: "flex", gap: 14 }}>
        <Col title="ЛИДЕРЫ РОСТА" list={gain} up />
        <Col title="ЛИДЕРЫ ПАДЕНИЯ" list={lose} />
      </div>
    );
  }

  function PulseTable({ coins, embed, onPick }) {
    const rows = coins.slice(0, embed === "mobile" ? 15 : 20);
    return (
      <div className="mk-scroll" style={{ overflowX: "auto" }}>
        <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 12.5 }}>
          <thead>
            <tr style={{ color: "var(--text-3,#888)", fontSize: 10.5, textAlign: "right" }}>
              <th style={{ textAlign: "left", padding: "4px 6px", fontWeight: 600 }}>#</th>
              <th style={{ textAlign: "left", padding: "4px 6px", fontWeight: 600 }}>Монета</th>
              <th style={{ padding: "4px 6px", fontWeight: 600 }}>Цена</th>
              <th style={{ padding: "4px 6px", fontWeight: 600 }}>1ч</th>
              <th style={{ padding: "4px 6px", fontWeight: 600 }}>24ч</th>
              {embed !== "mobile" && <th style={{ padding: "4px 6px", fontWeight: 600 }}>7д</th>}
              <th style={{ padding: "4px 6px", fontWeight: 600 }}>Объём 24ч</th>
            </tr>
          </thead>
          <tbody>
            {rows.map((c, i) => (
              <tr key={c.id || i} className="mk-row" onClick={() => onPick && onPick({ sym: c.symbol, name: c.name, ch: c.ch24h, price: c.price, mcap: c.market_cap })}
                style={{ cursor: "pointer", borderTop: "1px solid var(--border,#1c1c1c)", textAlign: "right" }}>
                <td className="mono" style={{ textAlign: "left", padding: "6px", color: "var(--text-3,#888)" }}>{c.rank || i + 1}</td>
                <td style={{ textAlign: "left", padding: "6px" }}><span style={{ display: "flex", alignItems: "center", gap: 7 }}>{c.image && <img src={c.image} width="17" height="17" style={{ borderRadius: "50%" }} alt="" />}<b>{c.symbol}</b><span style={{ color: "var(--text-3,#888)", fontSize: 11, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", maxWidth: embed === "mobile" ? 60 : 120 }}>{c.name}</span></span></td>
                <td className="mono" style={{ padding: "6px" }}>{fmtPrice(c.price)}</td>
                <td className="mono" style={{ padding: "6px", color: chCol(c.ch1h) }}>{sgn(c.ch1h, 1)}</td>
                <td className="mono" style={{ padding: "6px", color: chCol(c.ch24h) }}>{sgn(c.ch24h, 1)}</td>
                {embed !== "mobile" && <td className="mono" style={{ padding: "6px", color: chCol(c.ch7d) }}>{sgn(c.ch7d, 1)}</td>}
                <td className="mono" style={{ padding: "6px", color: "var(--text-2,#aaa)" }}>{fmtBig(c.volume)}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    );
  }

  function NewsCard({ n, i, featured }) {
    const col = srcColor(n.source);
    return (
      <a href={n.url} target="_blank" rel="noopener noreferrer" className="mk-card mk-news"
        style={{ position: "relative", display: "block", padding: featured ? 18 : 13, paddingLeft: featured ? 20 : 15, textDecoration: "none", color: "inherit", overflow: "hidden", animation: "mkUp .3s ease both", animationDelay: Math.min(i * 18, 300) + "ms" }}>
        <span style={{ position: "absolute", left: 0, top: 0, bottom: 0, width: 3, background: col }} />
        <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: featured ? 9 : 7, fontSize: 10.5, flexWrap: "wrap" }}>
          <span style={{ display: "inline-flex", alignItems: "center", gap: 5, fontWeight: 700, letterSpacing: ".03em", color: col, textTransform: "uppercase" }}>
            <span style={{ width: 6, height: 6, borderRadius: "50%", background: col, boxShadow: `0 0 6px ${col}` }} />{n.source}
          </span>
          <span style={{ padding: "1px 6px", borderRadius: 4, background: "var(--bg-2,#1a1a1a)", color: "var(--text-3,#999)" }}>{CAT_RU[n.category] || n.category}</span>
          <span style={{ padding: "1px 5px", borderRadius: 4, border: "1px solid var(--border,#333)", color: "var(--text-3,#999)", fontWeight: 700, fontSize: 9.5 }}>{langBadge(n.lang)}</span>
          <span style={{ marginLeft: "auto", color: "var(--text-4,#666)" }}>{timeAgo(n.ts_ms)}</span>
        </div>
        <div style={{ fontSize: featured ? 17 : 13.5, fontWeight: 700, lineHeight: 1.32, marginBottom: 5 }}>{n.title}</div>
        {n.summary && <div style={{ fontSize: featured ? 13 : 12, color: "var(--text-3,#999)", lineHeight: 1.45, display: "-webkit-box", WebkitLineClamp: featured ? 3 : 2, WebkitBoxOrient: "vertical", overflow: "hidden" }}>{n.summary}</div>}
      </a>
    );
  }

  function NewsList({ news, embed }) {
    const [lang, setLang] = useState("all");
    const list = (news || []).filter(n => lang === "all" || n.lang === lang);
    const langs = [["all", "🌐 Все"], ["ru", "🇷🇺 Русские"], ["en", "🇬🇧 English"]];
    const mob = embed === "mobile";
    const Chip = ({ active, onClick, children }) => (
      <button onClick={onClick} style={{ border: "1px solid " + (active ? "var(--accent,#3b82f6)" : "var(--border,#333)"), background: active ? "var(--accent,#3b82f6)" : "transparent", color: active ? "#fff" : "var(--text-2,#aaa)", borderRadius: 8, padding: "6px 14px", fontSize: 12.5, fontWeight: 600, cursor: "pointer", transition: "all .15s" }}>{children}</button>
    );
    return (
      <div>
        <div style={{ display: "flex", gap: 8, marginBottom: 14, flexWrap: "wrap", alignItems: "center" }}>
          <div style={{ display: "flex", gap: 6, padding: 3, borderRadius: 10, background: "var(--bg-2,#161616)", border: "1px solid var(--border,#222)" }}>
            {langs.map(([k, l]) => <Chip key={k} active={lang === k} onClick={() => setLang(k)}>{l}</Chip>)}
          </div>
          <span style={{ marginLeft: "auto", fontSize: 11, color: "var(--text-4,#666)" }}>{list.length} новостей</span>
        </div>
        {!list.length ? (
          <div style={{ padding: 30, textAlign: "center", color: "var(--text-3,#888)" }}>{news ? "По фильтру ничего нет — смени язык/тему." : "Загружаем ленту…"}</div>
        ) : (
          <>
            {!mob && list[0] && (
              <div style={{ display: "grid", gridTemplateColumns: "1.5fr 1fr 1fr", gap: 12, marginBottom: 12 }}>
                <NewsCard n={list[0]} i={0} featured />
                {list.slice(1, 3).map((n, i) => <NewsCard key={i} n={n} i={i + 1} />)}
              </div>
            )}
            <div style={{ display: "grid", gridTemplateColumns: mob ? "1fr" : "repeat(auto-fill,minmax(300px,1fr))", gap: 10 }}>
              {list.slice(mob ? 0 : 3).map((n, i) => <NewsCard key={i} n={n} i={i} />)}
            </div>
          </>
        )}
      </div>
    );
  }

  function Ticker({ coins }) {
    const row = (coins || []).slice(0, 26);
    if (!row.length) return null;
    const Item = ({ c }) => (
      <span style={{ display: "inline-flex", alignItems: "center", gap: 6, padding: "0 16px", fontSize: 12 }}>
        <b>{c.symbol}</b>
        <span className="mono" style={{ color: "var(--text-2,#bbb)" }}>{fmtPrice(c.price)}</span>
        <span className="mono" style={{ color: chCol(c.ch24h) }}>{sgn(c.ch24h, 2)}</span>
      </span>
    );
    return (
      <div style={{ overflow: "hidden", borderTop: "1px solid var(--border,#222)", borderBottom: "1px solid var(--border,#222)", background: "var(--bg-1,#0d0d0e)", padding: "8px 0", position: "relative" }}>
        <div style={{ display: "inline-flex", whiteSpace: "nowrap", animation: "mkMarquee 60s linear infinite", willChange: "transform" }}>
          {row.map((c, i) => <Item key={"a" + i} c={c} />)}
          {row.map((c, i) => <Item key={"b" + i} c={c} />)}
        </div>
      </div>
    );
  }

  function SubTabs({ tab, onChange }) {
    const tabs = [["overview", "Обзор"], ["crypto", "Крипта"], ["news", "Новости"], ["stocks", "Фондовый"]];
    return (
      <div className="mk-scroll" style={{ display: "flex", gap: 20, borderBottom: "1px solid var(--border,#222)", overflowX: "auto" }}>
        {tabs.map(([k, l]) => (
          <button key={k} className={"mk-tab" + (tab === k ? " on" : "")} onClick={() => onChange(k)} style={{ color: tab === k ? "var(--text,#fff)" : "var(--text-3,#888)", whiteSpace: "nowrap", fontSize: 13.5 }}>{l}</button>
        ))}
      </div>
    );
  }

  function Seg({ opts, val, onChange, embed }) {
    return (
      <div style={{ display: "inline-flex", gap: 2, padding: 2, borderRadius: 10, background: "var(--bg-2,#161616)", border: "1px solid var(--border,#222)" }}>
        {opts.map(([k, l]) => (
          <button key={k} onClick={() => onChange(k)} style={{ border: "none", cursor: "pointer", whiteSpace: "nowrap", borderRadius: 8, padding: embed === "mobile" ? "6px 10px" : "6px 13px", fontSize: 12.5, fontWeight: 600, background: val === k ? "var(--accent,#3b82f6)" : "transparent", color: val === k ? "#fff" : "var(--text-2,#aaa)" }}>{l}</button>
        ))}
      </div>
    );
  }

  // ============================================================
  // Главный компонент
  // ============================================================
  function MarketsView({ embed = "desktop" }) {
    useStyleOnce();
    const [tab, setTab] = useState("overview");   // overview | crypto | news | stocks
    const [view, setView] = useState("bubbles");  // bubbles | heat
    const [tf, setTf] = useState("ch24h");
    const [sizeBy, setSizeBy] = useState("cap");
    const [crypto, setCrypto] = useState(null);
    const [stocks, setStocks] = useState(null);
    const [glob, setGlob] = useState(null);
    const [sectors, setSectors] = useState(null);
    const [news, setNews] = useState(null);
    const [loading, setLoading] = useState(true);
    const [pick, setPick] = useState(null);

    const loadAll = async () => {
      const A = window.MobileAPI;
      try {
        if (tab === "stocks") {
          const s = await A.loadMarketsStocks(); setStocks((s && s.stocks) || []);
        } else if (tab === "news") {
          const n = await A.loadMarketsNews().catch(() => null); if (n) setNews(n.news || []);
        } else {
          const [c, g, se] = await Promise.all([
            A.loadMarketsCrypto(),
            A.loadMarketsGlobal().catch(() => null),
            A.loadMarketsSectors().catch(() => null),
          ]);
          setCrypto((c && c.coins) || []); if (g) setGlob(g); if (se) setSectors(se.sectors || []);
          if (tab === "overview") { const n = await A.loadMarketsNews().catch(() => null); if (n) setNews(n.news || []); }
        }
      } catch (e) { /* stale */ }
      setLoading(false);
    };
    useEffect(() => { setLoading(true); loadAll(); const t = setInterval(loadAll, 60000); return () => clearInterval(t); /* eslint-disable-next-line */ }, [tab]);

    const cField = tab === "stocks" ? "ch24h" : tf;
    const cRaw = tab === "stocks" ? (stocks || []) : (crypto || []);
    const byCap = [...(crypto || [])].filter(c => c[cField] != null).sort((a, b) => (b.market_cap || 0) - (a.market_cap || 0));
    const items = [...cRaw].filter(c => c[cField] != null).sort((a, b) => (b.market_cap || Math.abs(b[cField]) || 0) - (a.market_cap || Math.abs(a[cField]) || 0));
    const pad = embed === "mobile" ? 12 : 22;
    const mob = embed === "mobile";

    // производные метрики для «Обзора»
    const up24 = (crypto || []).filter(c => c.ch24h >= 0).length;
    const tot24 = (crypto || []).filter(c => c.ch24h != null).length || 1;
    const avg24 = (crypto || []).reduce((s, c) => s + (c.ch24h || 0), 0) / tot24;
    const regime = avg24 > 1.2 ? ["БЫЧИЙ", "var(--long,#16b979)"] : avg24 < -1.2 ? ["МЕДВЕЖИЙ", "var(--short,#ef4444)"] : ["НЕЙТРАЛЬНЫЙ", "#eab308"];
    const topSector = [...(sectors || [])].filter(s => s.ch24h != null).sort((a, b) => b.ch24h - a.ch24h)[0];
    const topGainer = [...(crypto || [])].filter(c => c.ch24h != null).sort((a, b) => b.ch24h - a.ch24h)[0];

    const statRow = glob && (
      <div style={{ display: "grid", gridTemplateColumns: mob ? "1fr 1fr" : "repeat(4,1fr)", gap: 10 }}>
        <Stat label="Капитализация" value={fmtBig(glob.total_mcap_usd)} sub={glob.mcap_change_24h != null ? sgn(glob.mcap_change_24h) + " / 24ч" : ""} pos={glob.mcap_change_24h >= 0} />
        <Stat label="Доминация BTC" value={glob.btc_dominance != null ? glob.btc_dominance.toFixed(1) + "%" : "—"} sub={glob.eth_dominance != null ? "ETH " + glob.eth_dominance.toFixed(1) + "%" : ""} />
        <Stat label="Страх / Жадность" value={glob.fng_value != null ? String(glob.fng_value) : "—"} sub={fngRu(glob.fng_label)} accent={fngColor(glob.fng_value)} />
        <Stat label="Активов" value={glob.active_cryptos != null ? glob.active_cryptos.toLocaleString("ru-RU") : "—"} sub="в обзоре топ-120" />
      </div>
    );

    return (
      <div style={{ display: "flex", flexDirection: "column" }}>
        <div style={{ padding: pad, display: "flex", flexDirection: "column", gap: 14 }}>
          <SubTabs tab={tab} onChange={setTab} />

          {loading && !items.length && tab !== "news" && <div style={{ padding: 40, textAlign: "center", color: "var(--text-3,#888)" }}>Загружаем рынок…</div>}

          {/* ---------- ОБЗОР ---------- */}
          {tab === "overview" && (
            <>
              {/* «сегодня» strip — на мобиле горизонтальный скролл, чтобы текст не резался */}
              <div className="mk-card mk-scroll" style={{ padding: mob ? "10px 12px" : "13px 16px", display: "flex", gap: mob ? 18 : 26, alignItems: "center", flexWrap: mob ? "nowrap" : "wrap", overflowX: mob ? "auto" : "visible" }}>
                <span style={{ display: "flex", alignItems: "center", gap: 8, flexShrink: 0 }}><span className="mk-live" /><span style={{ fontSize: 11, letterSpacing: ".08em", color: "var(--text-3,#888)" }}>СЕГОДНЯ</span></span>
                <Mini label="Режим" value={regime[0]} color={regime[1]} sub={"ср. 24ч " + sgn(avg24, 1)} />
                {topSector && <Mini label="Лучший сектор" value={topSector.name} color="var(--long,#16b979)" sub={sgn(topSector.ch24h, 1)} />}
                {topGainer && <Mini label="Топ монета" value={topGainer.symbol} color="var(--long,#16b979)" sub={sgn(topGainer.ch24h, 1)} />}
                <Mini label="Широта 24ч" value={(up24 / tot24 * 100).toFixed(0) + "%"} color={up24 / tot24 >= 0.5 ? "var(--long,#16b979)" : "var(--short,#ef4444)"} sub={up24 + "↑ / " + (tot24 - up24) + "↓"} />
              </div>

              {statRow}

              <div style={{ display: "grid", gridTemplateColumns: mob ? "1fr" : "1.4fr 1fr 1fr", gap: 12 }}>
                <Panel title="Тепловая карта" right="топ-24 · 24ч">
                  <Heat items={byCap.slice(0, 24)} field="ch24h" embed={embed} onPick={setPick} />
                </Panel>
                <Panel title="Страх и жадность" right={glob ? fngRu(glob.fng_label) : ""}>
                  <Gauge value={glob && glob.fng_value} label={glob && glob.fng_label} />
                  <div style={{ marginTop: 8 }}><Breadth coins={crypto || []} /></div>
                </Panel>
                <Panel title="Секторы · 24ч">
                  <SectorBars sectors={sectors} embed={embed} />
                </Panel>
              </div>

              <div style={{ display: "grid", gridTemplateColumns: mob ? "1fr" : "1fr 2fr", gap: 12 }}>
                <Panel title="Движение · 24ч"><Movers coins={crypto || []} field="ch24h" onPick={setPick} /></Panel>
                <Panel title="Лента новостей" right={<span onClick={() => setTab("news")} style={{ cursor: "pointer", color: "var(--accent,#3b82f6)" }}>все →</span>}>
                  <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
                    {(news || []).slice(0, 7).map((n, i) => (
                      <a key={i} href={n.url} target="_blank" rel="noopener noreferrer" className="mk-row" style={{ display: "flex", gap: 9, alignItems: "center", padding: "6px 6px", borderRadius: 8, textDecoration: "none", color: "inherit" }}>
                        <span style={{ width: 7, height: 7, borderRadius: "50%", background: srcColor(n.source), boxShadow: `0 0 6px ${srcColor(n.source)}`, flexShrink: 0 }} />
                        <span style={{ fontSize: 10, fontWeight: 700, color: srcColor(n.source), minWidth: 70, textTransform: "uppercase", whiteSpace: "nowrap" }}>{n.source}</span>
                        <span style={{ fontSize: 12.5, fontWeight: 600, lineHeight: 1.3, flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{n.title}</span>
                        <span style={{ fontSize: 10.5, color: "var(--text-4,#666)", whiteSpace: "nowrap" }}>{timeAgo(n.ts_ms)}</span>
                      </a>
                    ))}
                    {!news && <div style={{ fontSize: 12, color: "var(--text-3,#888)" }}>Загружаем новости…</div>}
                  </div>
                </Panel>
              </div>
            </>
          )}

          {/* ---------- КРИПТА ---------- */}
          {tab === "crypto" && (
            <>
              {statRow}
              <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
                <Seg opts={[["bubbles", "Пузыри"], ["heat", "Теплокарта"]]} val={view} onChange={setView} embed={embed} />
                <Seg opts={[["ch1h", "1ч"], ["ch24h", "24ч"], ["ch7d", "7д"]]} val={tf} onChange={setTf} embed={embed} />
                {view === "bubbles" && <Seg opts={[["cap", "Размер: капит."], ["move", "Размер: движение"]]} val={sizeBy} onChange={setSizeBy} embed={embed} />}
              </div>
              {!!items.length && (view === "bubbles"
                ? <Bubbles items={items} field={cField} sizeBy={sizeBy} embed={embed} onPick={setPick} />
                : <Heat items={items} field={cField} embed={embed} onPick={setPick} />)}
              <div style={{ display: "grid", gridTemplateColumns: mob ? "1fr" : "2fr 1fr", gap: 12 }}>
                <Panel title="Пульс рынка · топ" right={<span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}><span className="mk-live" />live</span>}><PulseTable coins={byCap} embed={embed} onPick={setPick} /></Panel>
                <Panel title="Движение · 24ч"><Movers coins={crypto || []} field="ch24h" onPick={setPick} /></Panel>
              </div>
            </>
          )}

          {/* ---------- НОВОСТИ ---------- */}
          {tab === "news" && <Panel title="Лента новостей крипто и финансов" right={<span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}><span className="mk-live" />обновление ~5 мин</span>}><NewsList news={news} embed={embed} /></Panel>}

          {/* ---------- ФОНДОВЫЙ ---------- */}
          {tab === "stocks" && (
            <>
              <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
                <Seg opts={[["bubbles", "Пузыри"], ["heat", "Теплокарта"]]} val={view} onChange={setView} embed={embed} />
              </div>
              {!!items.length && (view === "bubbles"
                ? <Bubbles items={items} field="ch24h" sizeBy="move" embed={embed} onPick={setPick} />
                : <Heat items={items} field="ch24h" embed={embed} onPick={setPick} />)}
              {!loading && !items.length && <div style={{ padding: 20, textAlign: "center", color: "var(--text-3,#888)", fontSize: 13 }}>Данные фондового рынка временно недоступны. Обновится автоматически.</div>}
            </>
          )}

          <div style={{ fontSize: 10.5, color: "var(--text-4,#666)" }}>
            Источники: CoinGecko · alternative.me · Yahoo Finance · RSS (Cointelegraph/CoinDesk/Decrypt). Данные справочные, не инвестрекомендация.
          </div>
        </div>

        {/* Тикер снизу */}
        {(tab !== "stocks") && <Ticker coins={byCap} />}

        {/* Инфо о выбранном */}
        {pick && (
          <div onClick={() => setPick(null)} style={{ position: "fixed", inset: 0, zIndex: 200, background: "rgba(0,0,0,.55)", display: "flex", alignItems: mob ? "flex-end" : "center", justifyContent: "center" }}>
            <div onClick={e => e.stopPropagation()} style={{ width: "100%", maxWidth: 430, background: "var(--bg-1,#111)", borderRadius: mob ? "16px 16px 0 0" : 16, border: "1px solid var(--border,#222)", padding: 18, animation: "mkUp .2s ease" }}>
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
                <div style={{ fontWeight: 800, fontSize: 20 }}>{pick.sym}</div>
                <div onClick={() => setPick(null)} style={{ cursor: "pointer", color: "var(--text-3)", fontSize: 20 }}>✕</div>
              </div>
              {pick.name && <div style={{ color: "var(--text-3)", fontSize: 12.5 }}>{pick.name}</div>}
              <div style={{ display: "flex", gap: 20, marginTop: 14, flexWrap: "wrap" }}>
                <div><div style={{ fontSize: 10, color: "var(--text-3)" }}>ЦЕНА</div><div className="mono" style={{ fontSize: 17, fontWeight: 700 }}>{fmtPrice(pick.price)}</div></div>
                <div><div style={{ fontSize: 10, color: "var(--text-3)" }}>ИЗМЕНЕНИЕ</div><div className="mono" style={{ fontSize: 17, fontWeight: 700, color: chCol(pick.ch) }}>{sgn(pick.ch)}</div></div>
                {pick.mcap && <div><div style={{ fontSize: 10, color: "var(--text-3)" }}>КАПИТАЛИЗАЦИЯ</div><div className="mono" style={{ fontSize: 17, fontWeight: 700 }}>{fmtBig(pick.mcap)}</div></div>}
              </div>
            </div>
          </div>
        )}
      </div>
    );
  }

  function Mini({ label, value, color, sub }) {
    return (
      <div style={{ flexShrink: 0, maxWidth: 150 }}>
        <div style={{ fontSize: 9.5, color: "var(--text-4,#777)", textTransform: "uppercase", letterSpacing: ".05em", whiteSpace: "nowrap" }}>{label}</div>
        <div style={{ fontSize: 14, fontWeight: 800, color: color || "var(--text,#fff)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{value}</div>
        {sub && <div className="mono" style={{ fontSize: 10.5, color: "var(--text-3,#888)", whiteSpace: "nowrap" }}>{sub}</div>}
      </div>
    );
  }

  window.MarketsView = MarketsView;
})();
