/* global React */
(function () {
  const { useState, useCallback, useEffect, useRef } = React;
  const T   = window.SC_T;
  const Icon = window.SC_Icon;
  const Api  = window.SC_Api;
  const SRC_MAP = window.SC_SrcMap;

  const PAGE_SIZES = [100, 500, 1000, 0];

  // ── Вращающийся шеврон SVG ─────────────────────────────────────────────────

  function Chevron({ open }) {
    return (
      <svg width="11" height="11" viewBox="0 0 12 12" fill="none"
        style={{ display: 'block', flexShrink: 0, transition: 'transform .18s ease', transform: open ? 'rotate(90deg)' : 'rotate(0)' }}>
        <path d="M4.5 2.5 L9 6 L4.5 9.5" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
      </svg>
    );
  }

  // ── Утилита: найти ближайший scroll-контейнер ─────────────────────────────

  function getScrollParent(el) {
    let p = el.parentElement;
    while (p) {
      const { overflow, overflowY } = getComputedStyle(p);
      if (/auto|scroll/.test(overflow + overflowY)) return p;
      p = p.parentElement;
    }
    return document.documentElement;
  }

  // ── Группировка по первому слову ───────────────────────────────────────────

  function groupByFirstWord(items, sorted = false) {
    const map = {}, order = [];
    items.forEach((item) => {
      const w = (item.phrase || '').trim().split(/\s+/)[0].toLowerCase();
      if (!map[w]) { map[w] = []; order.push(w); }
      map[w].push(item);
    });
    if (sorted) {
      order.sort((a, b) => a.localeCompare(b, 'ru'));
      return order.map((w) => ({ word: w, items: map[w].sort((a, b) => (a.phrase || '').localeCompare(b.phrase || '', 'ru')) }));
    }
    return order.map((w) => ({ word: w, items: map[w] }));
  }

  // ── Заголовок группы ──────────────────────────────────────────────────────

  function GroupHeader({ word, count, open, onClick, action }) {
    const [hover, setHover] = useState(false);
    return (
      <div
        onClick={onClick}
        onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
        style={{
          display: 'flex', alignItems: 'center', gap: 8,
          padding: '7px 16px 7px 14px',
          borderBottom: `1px solid ${T.borderSoft}`,
          borderLeft: `3px solid ${open ? T.accent : 'transparent'}`,
          background: open ? T.bg : hover ? T.bg : T.surface,
          cursor: 'pointer', userSelect: 'none', transition: 'background .12s ease, border-color .12s ease',
          position: 'sticky', top: 0, zIndex: 2,
        }}
      >
        <span style={{ color: open ? T.accent : T.inkSubtle, display: 'flex' }}>
          <Chevron open={open} />
        </span>
        <span style={{ fontSize: 13, fontWeight: 600, color: T.ink, flex: 1 }}>{word}</span>
        <span style={{
          fontSize: 11, fontWeight: 700, padding: '2px 8px', borderRadius: 999,
          background: open ? T.accentSoft : '#F3F4F6',
          color: open ? T.accent : T.inkMuted,
          transition: 'all .15s',
          lineHeight: '16px',
        }}>
          {count}
        </span>
        {action && <div onClick={(e) => e.stopPropagation()}>{action}</div>}
      </div>
    );
  }

  // ── Группа фраз ───────────────────────────────────────────────────────────

  function PhraseGroup({ word, items, renderRow, onTransferAll, isRejected }) {
    const [open, setOpen] = useState(false);
    const ref = useRef(null);

    const toggle = useCallback(() => {
      const next = !open;
      setOpen(next);
      if (!next && ref.current) {
        const el = ref.current;
        const container = getScrollParent(el);
        const elTop    = el.getBoundingClientRect().top;
        const ctTop    = container === document.documentElement ? 0 : container.getBoundingClientRect().top;
        const ctBottom = container === document.documentElement ? window.innerHeight : container.getBoundingClientRect().bottom;
        if (elTop < ctTop || elTop >= ctBottom) {
          el.scrollIntoView({ behavior: 'smooth', block: 'start' });
        }
      }
    }, [open]);

    const transferAllBtn = onTransferAll ? (
      <button
        onClick={() => onTransferAll(items.map((i) => i.phrase))}
        style={{
          fontSize: 10, padding: '2px 8px', borderRadius: 5, fontFamily: 'inherit',
          border: `1px solid ${isRejected ? T.accent : T.err}`,
          color: isRejected ? T.accent : T.err,
          background: 'transparent', cursor: 'pointer', fontWeight: 700, whiteSpace: 'nowrap',
        }}
      >
        {isRejected ? '← вернуть все' : 'убрать все →'}
      </button>
    ) : null;

    if (items.length === 1) return renderRow(items[0]);
    return (
      <div ref={ref}>
        <GroupHeader word={word} count={items.length} open={open} onClick={toggle} action={transferAllBtn} />
        <div style={{
          display: 'grid',
          gridTemplateRows: open ? '1fr' : '0fr',
          transition: 'grid-template-rows 0.28s cubic-bezier(0.4, 0, 0.2, 1)',
        }}>
          <div style={{ overflow: 'hidden' }}>
            <div style={{ borderLeft: `3px solid ${T.accentSoft}` }}>
              {items.map((item, idx) => (
                <div key={item.phrase + idx} style={{ paddingLeft: 12 }}>
                  {renderRow(item)}
                </div>
              ))}
            </div>
          </div>
        </div>
      </div>
    );
  }

  // ── Список с группировкой ─────────────────────────────────────────────────

  function GroupedList({ items, renderRow, emptyText, sorted = false, onTransferAll, isRejected }) {
    const groups = groupByFirstWord(items, sorted);
    if (!groups.length) {
      return (
        <div style={{ padding: '36px 20px', textAlign: 'center', color: T.inkMuted, fontSize: 13 }}>
          {emptyText || 'Пусто'}
        </div>
      );
    }
    return groups.map(({ word, items: gi }) => (
      <PhraseGroup key={word} word={word} items={gi} renderRow={renderRow} onTransferAll={onTransferAll} isRejected={isRejected} />
    ));
  }

  // ── Строка в нормальном режиме ────────────────────────────────────────────

  function NormalRow({ row }) {
    const [hover, setHover] = useState(false);
    return (
      <div
        onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
        style={{
          display: 'grid', gridTemplateColumns: '1fr 130px 100px',
          columnGap: 20, padding: '9px 18px', borderBottom: `1px solid ${T.borderSoft}`,
          fontSize: 13, alignItems: 'center',
          background: hover ? T.accentSoft : 'transparent', transition: 'background .12s ease',
        }}
      >
        <div style={{ color: T.ink, fontWeight: 500, paddingRight: 12, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{row.phrase}</div>
        <div style={{ textAlign: 'right', fontVariantNumeric: 'tabular-nums', color: T.ink, fontWeight: 600 }}>{(row.freq || 0).toLocaleString('ru')}</div>
        <div><SourceBadge src={row.src || 'seed'} /></div>
      </div>
    );
  }

  // ── Строка в панели редактирования ────────────────────────────────────────

  function EditRow({ row, isRejected, onTransfer, transferring }) {
    const [hover, setHover] = useState(false);
    const busy  = !!transferring;
    const isMe  = transferring === row.phrase;
    const btnText  = isRejected ? '← В ядро' : 'Убрать →';
    const btnDir   = isRejected ? 'to_core'  : 'to_rejected';
    const btnColor = isRejected ? T.accent    : T.err;
    const rowHoverBg = isRejected ? T.accentSoft : '#FEF2F2';

    return (
      <div
        onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
        style={{
          display: 'flex', alignItems: 'center', gap: 8, minWidth: 0,
          padding: '8px 14px', borderBottom: `1px solid ${T.borderSoft}`,
          background: hover ? rowHoverBg : 'transparent', transition: 'background .1s ease',
        }}
      >
        <div style={{ flex: 1, fontSize: 13, fontWeight: 500, color: T.ink, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
          {row.phrase}
        </div>
        <SourceBadge src={row.src || (SRC_MAP && SRC_MAP[row.source]) || 'seed'} />
        <button
          onClick={() => !busy && onTransfer(row.phrase, btnDir)}
          style={{
            flexShrink: 0, padding: '3px 10px',
            border: `1px solid ${btnColor}`,
            borderRadius: 6, fontSize: 11, fontWeight: 700, fontFamily: 'inherit',
            cursor: busy ? (isMe ? 'wait' : 'not-allowed') : 'pointer',
            background: hover && !busy ? btnColor : 'transparent',
            color: hover && !busy ? '#fff' : btnColor,
            transition: 'all .12s ease', whiteSpace: 'nowrap',
            opacity: busy ? 0.45 : 1,
          }}
        >
          {isMe ? '…' : btnText}
        </button>
      </div>
    );
  }

  // ── Поисковое поле (переиспользуемое) ─────────────────────────────────────

  function SearchBox({ value, onChange, placeholder, maxWidth }) {
    return (
      <div style={{ position: 'relative', flex: 1, minWidth: 100, maxWidth: maxWidth || 'none' }}>
        <span style={{ position: 'absolute', left: 9, top: '50%', transform: 'translateY(-50%)', color: T.inkMuted, display: 'flex', pointerEvents: 'none' }}>
          {Icon.search(13)}
        </span>
        <input
          value={value} onChange={(e) => onChange(e.target.value)}
          placeholder={placeholder || 'Поиск…'}
          style={{ width: '100%', padding: '6px 10px 6px 28px', border: `1px solid ${T.border}`, borderRadius: 7, fontSize: 12.5, fontFamily: 'inherit', outline: 'none', background: T.surface }}
          onFocus={(e) => { e.target.style.borderColor = T.accent; e.target.style.boxShadow = `0 0 0 2px ${T.accentSoft}`; }}
          onBlur={(e)  => { e.target.style.borderColor = T.border;  e.target.style.boxShadow = 'none'; }}
        />
      </div>
    );
  }

  // ── Панель редактирования (одна колонка) ──────────────────────────────────

  function EditPanel({ title, count, items, isRejected, onTransfer, onTransferAll, transferring, emptyText, accentClr }) {
    const [search, setSearch] = useState('');
    const visible = search
      ? items.filter((r) => r.phrase.toLowerCase().includes(search.toLowerCase()))
      : items;

    return (
      <div style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0, overflow: 'hidden' }}>
        <div style={{
          padding: '11px 16px', borderBottom: `1px solid ${T.border}`, flexShrink: 0,
          display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap',
          background: accentClr + '09',
        }}>
          <span style={{ fontSize: 13, fontWeight: 700, color: T.ink, flexShrink: 0 }}>{title}</span>
          <span style={{
            fontSize: 11, fontWeight: 700, padding: '2px 8px', borderRadius: 999,
            background: accentClr + '22', color: accentClr, lineHeight: '16px', flexShrink: 0,
          }}>
            {count}
          </span>
          <SearchBox value={search} onChange={setSearch} placeholder="Поиск…" />
        </div>
        <div style={{ flex: 1, overflow: 'auto' }}>
          <GroupedList
            items={visible}
            renderRow={(row) => (
              <EditRow row={row} isRejected={isRejected} onTransfer={onTransfer} transferring={transferring} />
            )}
            emptyText={search ? 'Ничего не найдено' : emptyText}
            sorted
            onTransferAll={search ? null : onTransferAll}
            isRejected={isRejected}
          />
        </div>
      </div>
    );
  }

  // ── Статистика и кнопка переключения (вне ResultsScreen — стабильный тип) ──

  function StatsRow({ total, seedCount, aiCount, suggestCount, wordstatCount }) {
    return (
      <div style={{ display: 'flex', gap: 14, flexWrap: 'wrap', flexShrink: 0 }}>
        <StatCard label="Всего фраз"    value={total} active />
        <StatCard label="Затравки"      value={seedCount} />
        <StatCard label="AI расширение" value={aiCount} />
        <StatCard label="Suggest"       value={suggestCount} />
        <StatCard label="Wordstat"      value={wordstatCount} />
      </div>
    );
  }

  function ToggleBtn({ editMode, onToggle }) {
    return (
      <button onClick={onToggle} style={{
        padding: '8px 18px', border: `1px solid ${editMode ? T.accent : T.border}`,
        borderRadius: 8, cursor: 'pointer', fontSize: 13, fontWeight: 600,
        background: editMode ? T.accent : T.surface, color: editMode ? '#fff' : T.ink,
        fontFamily: 'inherit', transition: 'all .15s ease',
        display: 'inline-flex', alignItems: 'center', gap: 6,
      }}>{Icon.textCursor(16)}
        {editMode ? 'Выйти из режима редактирования' : 'Режим редактирования'}
      </button>
    );
  }

  // ── Основной компонент ────────────────────────────────────────────────────

  function ResultsScreen({
    rows, totalCount, seedCount, aiCount, suggestCount, wordstatCount,
    allLoaded, onLoadAll, jobId, onError, onAdGen, onRowsUpdate,
  }) {
    const [q,         setQ]         = useState('');
    const [srcFilter, setSrcFilter] = useState('all');
    const [sortCol,   setSortCol]   = useState('freq');
    const [sortAsc,   setSortAsc]   = useState(false);
    const [pageSize,  setPageSize]  = useState(500);

    const [editMode,        setEditMode]        = useState(false);
    const [rejectedPhrases, setRejectedPhrases] = useState([]);
    const [loadingEdit,     setLoadingEdit]     = useState(false);
    const [transferring,    setTransferring]    = useState(null);

    const [localRows, setLocalRows] = useState(rows);
    useEffect(() => { setLocalRows(rows); }, [rows]);

    // Включение / выключение режима редактирования
    const toggleEditMode = useCallback(async () => {
      const next = !editMode;
      setEditMode(next);
      if (!next) { setRejectedPhrases([]); return; }

      setLoadingEdit(true);
      try {
        await Promise.all([
          !allLoaded ? onLoadAll() : Promise.resolve(),
          (async () => {
            if (!jobId) return;
            try {
              const data = await Api.getJson(`/api/job/${jobId}/rejected`);
              setRejectedPhrases(Array.isArray(data) ? data : []);
            } catch { setRejectedPhrases([]); }
          })(),
        ]);
      } finally {
        setLoadingEdit(false);
      }
    }, [editMode, allLoaded, onLoadAll, jobId]);

    // Оптимистичный перенос — UI обновляется мгновенно, откат при ошибке
    const handleTransfer = useCallback(async (phrase, direction) => {
      if (!jobId || transferring) return;
      setTransferring(phrase);

      // Снапшот для отката
      const prevLocalRows = localRows;
      const prevRejected  = rejectedPhrases;

      // Вычисляем новый список заранее, чтобы передать родителю после успеха
      let newLocalRows = localRows;

      if (direction === 'to_rejected') {
        const moved = localRows.find((r) => r.phrase === phrase);
        newLocalRows = localRows.filter((r) => r.phrase !== phrase);
        setLocalRows(newLocalRows);
        if (moved) {
          setRejectedPhrases((prev) => [...prev, {
            id: Date.now(), phrase: moved.phrase,
            shows_per_month: moved.freq || 0, source: moved.src || 'suggest',
          }]);
        }
      } else {
        const moved = rejectedPhrases.find((r) => r.phrase === phrase);
        setRejectedPhrases((prev) => prev.filter((r) => r.phrase !== phrase));
        if (moved) {
          const src = (SRC_MAP && SRC_MAP[moved.source]) ? SRC_MAP[moved.source] : (moved.source || 'suggest');
          newLocalRows = [...localRows, { phrase: moved.phrase, freq: moved.shows_per_month || 0, src, cluster: '' }];
          setLocalRows(newLocalRows);
        }
      }

      try {
        await Api.postJson(`/api/job/${jobId}/transfer`, { phrase, direction });
        if (onRowsUpdate) onRowsUpdate(newLocalRows);
      } catch {
        // Откатываем UI к состоянию до клика
        setLocalRows(prevLocalRows);
        setRejectedPhrases(prevRejected);
        if (onError) onError('Не удалось перенести фразу — попробуйте ещё раз');
      } finally {
        setTransferring(null);
      }
    }, [jobId, transferring, localRows, rejectedPhrases, onError, onRowsUpdate]);

    // Перенос всей группы одним кликом (оптимистичный, откат при ошибке)
    const handleTransferGroup = useCallback(async (phrases, direction) => {
      if (!jobId || transferring) return;
      setTransferring('__group__');

      const prevLocalRows = localRows;
      const prevRejected  = rejectedPhrases;
      let newLocalRows = localRows;
      let newRejected  = rejectedPhrases;

      if (direction === 'to_rejected') {
        const moved = localRows.filter((r) => phrases.includes(r.phrase));
        newLocalRows = localRows.filter((r) => !phrases.includes(r.phrase));
        newRejected  = [...rejectedPhrases, ...moved.map((m) => ({ id: Date.now() + Math.random(), phrase: m.phrase, shows_per_month: m.freq || 0, source: m.src || 'suggest' }))];
      } else {
        const moved = rejectedPhrases.filter((r) => phrases.includes(r.phrase));
        newRejected  = rejectedPhrases.filter((r) => !phrases.includes(r.phrase));
        newLocalRows = [...localRows, ...moved.map((m) => { const src = (SRC_MAP && SRC_MAP[m.source]) ? SRC_MAP[m.source] : (m.source || 'suggest'); return { phrase: m.phrase, freq: m.shows_per_month || 0, src, cluster: '' }; })];
      }

      setLocalRows(newLocalRows);
      setRejectedPhrases(newRejected);

      let failed = false;
      try { await Api.postJson(`/api/job/${jobId}/transfer/batch`, { phrases, direction }); }
      catch { failed = true; }

      if (failed) {
        setLocalRows(prevLocalRows);
        setRejectedPhrases(prevRejected);
        if (onError) onError('Не удалось перенести группу — попробуйте ещё раз');
      } else {
        if (onRowsUpdate) onRowsUpdate(newLocalRows);
      }
      setTransferring(null);
    }, [jobId, transferring, localRows, rejectedPhrases, onError, onRowsUpdate]);

    // ── Фильтрация / сортировка (нормальный режим) ─────────────────────────

    const filtered = localRows
      .filter((r) =>
        (srcFilter === 'all' || r.src === srcFilter) &&
        (!q || r.phrase.toLowerCase().includes(q.toLowerCase()))
      )
      .sort((a, b) => {
        const va = a[sortCol] ?? 0, vb = b[sortCol] ?? 0;
        const rv = typeof va === 'number' ? va - vb : String(va).localeCompare(String(vb), 'ru');
        return sortAsc ? rv : -rv;
      });

    const displayed = pageSize === 0 ? filtered : filtered.slice(0, pageSize);
    const onSort = (col) => { if (sortCol === col) setSortAsc(!sortAsc); else { setSortCol(col); setSortAsc(col !== 'freq'); } };
    const handlePageSize = async (s) => {
      setPageSize(s);
      if (!allLoaded && (s === 0 || s > localRows.length)) await onLoadAll();
    };

    // ── Данные для edit-панелей ─────────────────────────────────────────────

    const rejectedForEdit = rejectedPhrases.map((r) => ({
      phrase: r.phrase,
      freq: r.shows_per_month || 0,
      src: (SRC_MAP && SRC_MAP[r.source]) ? SRC_MAP[r.source] : (r.source || 'suggest'),
      source: r.source,
    }));

    // ── РЕЖИМ РЕДАКТИРОВАНИЯ — split-layout ────────────────────────────────

    if (editMode) {
      return (
        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', minHeight: 0, overflow: 'hidden' }}>
          <div style={{ padding: '20px 28px 16px', flexShrink: 0 }}>
            <StatsRow total={localRows.length} seedCount={seedCount} aiCount={aiCount} suggestCount={suggestCount} wordstatCount={wordstatCount} />
          </div>

          {loadingEdit ? (
            <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', color: T.inkMuted, fontSize: 13, gap: 8 }}>
              <span>Загрузка данных…</span>
            </div>
          ) : (
            <div style={{
              flex: 1, display: 'flex', margin: '0 28px', minHeight: 0,
              border: `1px solid ${T.border}`, borderRadius: 14, overflow: 'hidden', background: T.surface,
            }}>
              {/* Левая панель — Семантическое ядро */}
              <EditPanel
                title="Семантическое ядро"
                count={localRows.length}
                items={localRows}
                isRejected={false}
                onTransfer={handleTransfer}
                onTransferAll={(phrases) => handleTransferGroup(phrases, 'to_rejected')}
                transferring={transferring}
                emptyText="Семантическое ядро пусто"
                accentClr={T.accent}
              />
              {/* Разделитель */}
              <div style={{ width: 1, background: T.border, flexShrink: 0 }} />
              {/* Правая панель — Отклонённые */}
              <EditPanel
                title="Отклонённые нейросетью"
                count={rejectedForEdit.length}
                items={rejectedForEdit}
                isRejected={true}
                onTransfer={handleTransfer}
                onTransferAll={(phrases) => handleTransferGroup(phrases, 'to_core')}
                transferring={transferring}
                emptyText="Нет отклонённых фраз"
                accentClr={T.err}
              />
            </div>
          )}

          <div style={{ padding: '12px 28px 20px', flexShrink: 0, display: 'flex', justifyContent: 'flex-end' }}>
            <ToggleBtn editMode={editMode} onToggle={toggleEditMode} />
          </div>
        </div>
      );
    }

    // ── НОРМАЛЬНЫЙ РЕЖИМ ──────────────────────────────────────────────────

    const srcOpts = [
      { v: 'all', l: 'Все' }, { v: 'seed', l: 'Seed' }, { v: 'ai', l: 'AI' },
      { v: 'suggest', l: 'Suggest' }, { v: 'wordstat', l: 'Wordstat' },
    ];

    return (
      <div style={{ flex: 1, padding: '24px 28px', overflow: 'auto', display: 'flex', flexDirection: 'column', gap: 18, minHeight: 0 }}>
        <StatsRow total={totalCount} seedCount={seedCount} aiCount={aiCount} suggestCount={suggestCount} wordstatCount={wordstatCount} />

        <div style={{ background: T.surface, border: `1px solid ${T.border}`, borderRadius: 14, overflow: 'hidden', display: 'flex', flexDirection: 'column', flex: 1, minHeight: 0 }}>

          {/* Toolbar */}
          <div style={{ padding: '12px 16px', borderBottom: `1px solid ${T.borderSoft}`, display: 'flex', alignItems: 'center', gap: 10, flexShrink: 0, flexWrap: 'wrap' }}>
            <div style={{ position: 'relative', flex: 1, minWidth: 200, maxWidth: 360 }}>
              <span style={{ position: 'absolute', left: 11, top: '50%', transform: 'translateY(-50%)', color: T.inkMuted, display: 'flex', pointerEvents: 'none' }}>
                {Icon.search(14)}
              </span>
              <input
                value={q} onChange={(e) => setQ(e.target.value)}
                placeholder="Поиск по фразам и кластерам..."
                style={{ width: '100%', padding: '7px 12px 7px 32px', border: `1px solid ${T.border}`, borderRadius: 8, fontSize: 13, fontFamily: 'inherit', outline: 'none', background: T.surface }}
                onFocus={(e) => { e.target.style.borderColor = T.accent; e.target.style.boxShadow = `0 0 0 3px ${T.accentSoft}`; }}
                onBlur={(e)  => { e.target.style.borderColor = T.border;  e.target.style.boxShadow = 'none'; }}
              />
            </div>

            <div style={{ display: 'inline-flex', padding: 3, background: '#F3F4F6', borderRadius: 8, gap: 2 }}>
              {srcOpts.map((o) => (
                <button key={o.v} onClick={() => setSrcFilter(o.v)} style={{
                  padding: '5px 11px', border: 'none', borderRadius: 6, cursor: 'pointer',
                  background: srcFilter === o.v ? T.surface : 'transparent',
                  color: srcFilter === o.v ? T.ink : T.inkMuted,
                  fontSize: 12, fontWeight: 600, fontFamily: 'inherit',
                  boxShadow: srcFilter === o.v ? '0 1px 2px rgba(17,24,39,0.08)' : 'none',
                  transition: 'all .12s ease',
                }}>{o.l}</button>
              ))}
            </div>

            <div style={{ display: 'inline-flex', padding: 3, background: '#F3F4F6', borderRadius: 8, gap: 2 }}>
              {PAGE_SIZES.map((s) => (
                <button key={s} onClick={() => handlePageSize(s)} style={{
                  padding: '5px 10px', border: 'none', borderRadius: 6, cursor: 'pointer',
                  background: pageSize === s ? T.surface : 'transparent',
                  color: pageSize === s ? T.ink : T.inkMuted,
                  fontSize: 12, fontWeight: 600, fontFamily: 'inherit',
                  boxShadow: pageSize === s ? '0 1px 2px rgba(17,24,39,0.08)' : 'none',
                  transition: 'all .12s ease',
                }}>
                  {s === 0 ? 'Все' : s.toLocaleString('ru')}
                </button>
              ))}
            </div>

            <span style={{ fontSize: 12, color: T.inkMuted, fontWeight: 500, marginLeft: 'auto', whiteSpace: 'nowrap' }}>
              {displayed.length.toLocaleString('ru')}
              {` / ${(q || srcFilter !== 'all' ? filtered.length : totalCount).toLocaleString('ru')}`}
            </span>
          </div>

          {/* Заголовок колонок — вне scroll-контейнера, sticky в outer-контексте */}
          <div style={{
            display: 'grid', gridTemplateColumns: '1fr 130px 100px',
            columnGap: 20, padding: '10px 18px', background: '#FAFBFC',
            borderBottom: `1px solid ${T.borderSoft}`,
            fontSize: 11, fontWeight: 600, letterSpacing: 0.8, textTransform: 'uppercase',
            color: T.inkMuted, flexShrink: 0, position: 'sticky', top: 0, zIndex: 3,
          }}>
            {[['phrase', 'Фраза'], ['freq', 'Кол-во поисков'], ['src', 'Источник']].map(([k, l]) => (
              <div key={k} onClick={() => onSort(k)} style={{
                cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 4, userSelect: 'none',
                justifyContent: k === 'freq' ? 'flex-end' : 'flex-start',
              }}>
                {l}{sortCol === k && <span style={{ fontSize: 9, opacity: 0.7 }}>{sortAsc ? '▲' : '▼'}</span>}
              </div>
            ))}
          </div>

          {/* Строки — отдельный scroll-контекст */}
          <div style={{ flex: 1, overflow: 'auto', minHeight: 0 }}>
            <GroupedList
              items={displayed}
              renderRow={(row) => <NormalRow row={row} />}
              emptyText="Ничего не нашлось"
            />
          </div>

          {/* Нижняя панель */}
          <div style={{ padding: '12px 18px', borderTop: `1px solid ${T.borderSoft}`, display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 10 }}>
            {onAdGen && (
              <button onClick={() => onAdGen()} style={{
                padding: '8px 18px', border: `1px solid ${T.border}`,
                borderRadius: 8, cursor: 'pointer', fontSize: 13, fontWeight: 600,
                background: T.surface, color: T.ink, fontFamily: 'inherit',
                transition: 'all .15s ease', display: 'inline-flex', alignItems: 'center', gap: 6,
              }}
              onMouseEnter={(e) => { e.currentTarget.style.borderColor = T.accent; e.currentTarget.style.color = T.accent; }}
              onMouseLeave={(e) => { e.currentTarget.style.borderColor = T.border;  e.currentTarget.style.color = T.ink; }}
              >{Icon.astroid(16)}
                Сгенерировать рекламу
              </button>
            )}
            <ToggleBtn editMode={editMode} onToggle={toggleEditMode} />
          </div>
        </div>
      </div>
    );
  }

  window.ResultsScreen = ResultsScreen;
})();
