/* Стрим-страница: шапка по центру + две колонки (история | доска).
   Без внутренних скроллов: история обрезается по низу экрана (JS), список удлиняет страницу. */

body.stream-mode {
  background: var(--bg);
  color: var(--text);
  margin: 0;
  min-height: 100vh;
}

main.app.stream-app {
  max-width: 100%;            /* на всю ширину: доска центрируется по экрану (см. .stream-cols) */
  width: 100%;
  min-height: 100vh;
  margin: 0;
  padding: 12px 16px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* ===== Шапка ===== */
.stream-header {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  flex: 0 0 auto;
}
.stream-header .tw-logo { display: inline-flex; align-items: center; }
.stream-header .tw-logo svg { display: block; }
/* Аватарка стримера (канала) в шапке. */
.stream-header .tw-streamer-ava {
  width: 26px; height: 26px; border-radius: 50%;
  object-fit: cover; flex: 0 0 auto;
  background: var(--surface, rgba(255,255,255,0.06));
}
.stream-header .tw-streamer-ava[hidden] { display: none; }
.stream-header .tw-input {
  width: auto;                 /* ширина по size-атрибуту (адаптивно, min 10 символов) */
  height: 34px;
  padding: 0 10px;
  border: 1px solid var(--border-strong, rgba(255,255,255,0.18));
  border-radius: 8px;
  background: var(--surface, rgba(255,255,255,0.05));
  color: var(--text);
  font-size: 15px;
  outline: none;
}
.stream-header .tw-input:focus { border-color: #9146FF; }
.stream-header .tw-clear {
  width: 30px; height: 30px;
  display: inline-flex; align-items: center; justify-content: center;
  border: none; background: transparent;
  color: var(--muted); font-size: 16px; font-weight: 700; line-height: 1; cursor: pointer;
  border-radius: 8px;
}
.stream-header .tw-clear:hover { background: var(--surface-hover, rgba(255,255,255,0.10)); color: var(--text); }
.stream-header .tw-status {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--muted); flex: 0 0 auto;
}
/* Подключено — фиолетовая (Twitch), а не зелёная. */
.stream-header .tw-status[data-state="connected"]   { background: #9146FF; box-shadow: 0 0 6px #9146FF; }
.stream-header .tw-status[data-state="connecting"]   { background: var(--mid, #ffa94d); }
.stream-header .tw-status[data-state="disconnected"] { background: var(--far, #ff5c5c); }
.stream-header .tw-status[data-state="idle"]         { background: var(--muted); }
.stream-header .brand {
  display: inline-flex;
  align-items: center;
  gap: 5px;                 /* фиолетовая точка вплотную к названию */
  margin-left: 6px;
  line-height: 1;
  font-weight: 800;
  letter-spacing: 0.3px;
  font-size: 18px;
  white-space: nowrap;
}
.stream-header .brand-name { line-height: 1; }
/* Точка между Векторно и рф — обычная (белый текст, базовая линия), как в лого. */
.stream-header .brand-dot { color: var(--text); margin: 0; font-weight: inherit; }

/* ===== Колонки =====
   Доска (правый столбец) центрируется по ЭКРАНУ (средний трек grid),
   история — слева от неё (левый трек, прижата к доске), справа пусто. */
.stream-cols {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-columns: 1fr minmax(0, 440px) 1fr;
  gap: 16px;
  align-items: start;
}
.stream-cols .col {
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.stream-cols .col-history { grid-column: 1; justify-self: end; width: 100%; max-width: 440px; }
.stream-cols .col-board   { grid-column: 2; }
/* Без внутреннего скролла — история обрезается JS-ом, список растит страницу. */
.stream-history,
.col-board .board {
  flex: 1 1 auto;
  overflow: visible;
  display: flex;
  flex-direction: column;
  gap: var(--gap-sm, 6px);
}
.col-board .board { margin-top: 6px; }
.col-board .list { display: flex; flex-direction: column; gap: var(--gap-sm, 6px); }

/* ===== Блок «актуального слова» + строки истории — плитки .active-guess =====
   Стили самой плитки в components/game/ActiveGuess/ActiveGuess.css. Здесь только
   снимаем верхний отступ оригинала (в списке зазор даёт gap контейнера). */
#active-word { margin-top: 0; }
.stream-history .active-guess { margin-top: 0; }

/* ===== board-tabs (ростер + сорт) ===== */
.col-board .board-tabs {
  display: flex; align-items: center; gap: 8px;
  margin-top: 8px; flex: 0 0 auto;
}

/* ===== Ростер: префикс слева, эмодзи по центру оставшегося места, счётчик справа ===== */
.col-board #players-roster { display: flex; align-items: center; gap: 6px; }
.col-board #players-roster .pl-prefix,
.col-board #players-roster .pl-count { flex: 0 0 auto; }
.col-board #players-roster .pl-emos {
  flex: 1 1 auto; min-width: 0;
  display: flex; justify-content: center; align-items: center; gap: 2px;
  overflow: visible;   /* кап в renderBoard гарантирует, что влезает — не обрезаем края */
}
.col-board #players-roster .pl-more {
  flex: 0 0 auto;
  font-size: 12px; color: var(--muted); margin-left: 2px;
}

/* ===== Аватарки как эмодзи ===== */
.sh-ava {
  width: 18px; height: 18px; border-radius: 50%;
  object-fit: cover; vertical-align: middle;
  background: var(--surface, rgba(255,255,255,0.06));
}
.sh-av { display: inline-flex; align-items: center; vertical-align: middle; }
.nick .sh-av { margin-right: 3px; }
.pl-emo .pl-ava {
  width: 18px; height: 18px; border-radius: 50%;
  object-fit: cover; vertical-align: middle;
  background: var(--surface, rgba(255,255,255,0.06));
}
/* Placeholder-кружок (силуэт) того же размера — пока грузится реальная аватарка. */
.sh-ava-ph, .pl-ava-ph {
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface-strong, rgba(255,255,255,0.12));
  color: var(--muted);
}
.sh-ava-ph svg, .pl-ava-ph svg { width: 64%; height: 64%; display: block; }

/* Smooth fade-in реальной twitch-аватарки поверх placeholder'а — в #active-word, streamHistory,
   списке слов (.row .w). Структура: <span class="sh-ava-wrap"><span class="sh-ava-ph">…</span>
   <img class="sh-ava-img"></span>. Placeholder ВСЕГДА под img; img opacity 0→1 на load.
   На error — img display:none, placeholder остаётся. Аналог .pl-ava-wrap из ростера. */
.sh-ava-wrap {
  position: relative;
  width: 18px; height: 18px;
  display: inline-flex; vertical-align: middle;
}
.sh-ava-wrap > .sh-ava-ph,
.sh-ava-wrap > .sh-ava-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  border-radius: 50%;
}
.sh-ava-img {
  opacity: 0;
  transition: opacity 250ms ease-out;
  object-fit: cover;
  background: var(--surface, rgba(255,255,255,0.06));
}
.sh-ava-img.loaded { opacity: 1; }
/* Эмодзи ванильного игрока — заполняет кружок как аватарка (тот же размер). */
.sh-emo-ava, .pl-emo-ava {
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 15px; line-height: 1;
}

/* (Победный тост .stream-winner удалён — теперь используется WinBanner + StatsList, как в игре.) */

/* ===== Победа: порядок как в игре — плашка → ростер → итоги → список слов =====
   #win/#stats смонтированы в .col-board (flex-колонка), их и ростер/список упорядочиваем
   через flex `order` при body.stream-won (вкл. в onWin, снят в applyNewGame). Активное слово
   скрыто; список .board (order последним, flex:1) заполняет остаток высоты. */
.stream-won .col-board #win         { order: 0; }
.stream-won .col-board .board-tabs  { order: 1; }   /* ростер */
.stream-won .col-board #stats       { order: 2; }   /* итоги */
.stream-won .col-board .board       { order: 3; }   /* список — заполняет остаток */
.stream-won .col-board #active-word { display: none; }

@media (max-width: 720px) {
  .stream-cols { grid-template-columns: 1fr; }
  .stream-cols .col-history { grid-column: 1; justify-self: stretch; max-width: none; }
  .stream-cols .col-board { grid-column: 1; }
}

/* ===== OBS-оверлей (html.obs-mode, /{login}/obs или ?obs=1) =====
   Заполняет заданный в OBS бокс (ширина = вьюпорт, список слов заполняет высоту),
   прозрачный (настраиваемый ?bg) фон, коллаб-шапка по центру, читаемость поверх
   геймплея. Включается из stream.html pre-script. Размер/позицию задаёт сам OBS. */

/* Прозрачность: убираем непрозрачный фон-слой base.css (body::before — fixed, var(--bg))
   и задаём фон body по альфе из ?bg (0 = насквозь [дефолт], 1 = сплошная тёмная подложка). */
html.obs-mode body::before { display: none !important; }
html.obs-mode,
html.obs-mode body.stream-mode {
  background: rgba(8, 11, 18, var(--obs-bg-alpha, 0)) !important;
}
html.obs-mode { overflow: hidden; }                 /* без скроллбара в источнике */
html.obs-mode body.stream-mode { min-height: auto; }

/* Адаптив: оверлей заполняет заданный в OBS бокс. Ширина = вьюпорт (300→300, 500→500),
   высота фиксирована → список слов flex-заполняет остаток и обрезается по высоте бокса. */
html.obs-mode main.app.stream-app {
  /* Масштаб через transform: scale (надёжнее в OBS Chromium, чем zoom).
     CSS-размер main = (физический viewport) / scale → после scale ровно покрывает viewport.
     position: absolute + top:0/left:0 → main лежит поверх body, не зависит от body-flow.
     display: flex column — иначе cascading height (main → stream-cols → col-board → .board)
     не работает, .board растёт по контенту и выталкивает .overlay-actions за низ viewport.
     min-height: 0 — отменяет базовое `main.app.stream-app { min-height: 100vh }`, иначе
     main растягивается на 100vh CSS, после transform визуально становится 100vh * scale
     (~1980px на 720px viewport) и нижняя часть улетает за viewport. */
  position: absolute;
  top: 0; left: 0;
  width: calc(100% / var(--obs-scale, 1));
  height: calc(100vh / var(--obs-scale, 1));
  min-height: 0;
  transform: scale(var(--obs-scale, 1));
  transform-origin: top left;
  display: flex;
  flex-direction: column;
  margin: 0; padding: 8px;
  box-sizing: border-box;
  overflow: hidden;            /* клип контента по границам OBS-бокса */
}
/* Прячем у body скролл — overflow main у нас уже clip'ит, body не должен прокручиваться. */
html.obs-mode, html.obs-mode body { overflow: hidden; margin: 0; padding: 0; }
/* Колонки на всю высоту (история скрыта → доска тянется на всю ширину и высоту).
   min-width:0 по всей цепочке — иначе flex не сжимается ниже ширины контента и слова
   вылезают за узкий бокс (300px). */
html.obs-mode .stream-cols {
  display: flex; flex-direction: column;
  flex: 1 1 auto; min-height: 0; min-width: 0;
  align-items: stretch;   /* перебить базовый align-items:start (для grid) — иначе доска не тянется по ширине */
}
html.obs-mode .col-board { flex: 1 1 auto; min-height: 0; min-width: 0; }
html.obs-mode .col-board .list { min-width: 0; }
/* Актуальное слово и ростер не сжимаются (flex-заполняет только список .board ниже);
   высота актуального слова = как у строк списка (.row min-height:44px). */
html.obs-mode #active-word,
html.obs-mode .board-tabs { flex: 0 0 auto; }
html.obs-mode .active-guess {
  min-height: 44px; box-sizing: border-box;
  /* Подложка-стекло как у .row (список слов) и #players-roster — единый визуальный язык
     overlay'я в OBS. ::before-заливка по рангу (opacity 0.30 + saturate 1.3 в OBS, см. ниже)
     рисуется поверх этого фона. */
  background: var(--panel-solid);
  -webkit-backdrop-filter: var(--blur-light);
          backdrop-filter: var(--blur-light);
}
/* Список слов клипается по высоте бокса жёстко, ЦЕЛЫМИ строками: mask-fade убран —
   JS-trim в renderBoard() (trimOverflowingListRows) скрывает row-элементы, чьи bottom
   выходят за нижний край .board. Последняя видимая строка показывается целиком,
   частичная и все последующие — display:none. */
html.obs-mode .col-board .board {
  flex: 1 1 auto; min-height: 0; min-width: 0;
  overflow: hidden;
}

/* Лог слева скрыт ВРЕМЕННО (вернуть — удалить это правило). */
html.obs-mode .col-history { display: none; }

/* Шапка-коллаб по центру: [twitch] [ава] Ник ✕ •(фиолет) Векторно.рф
   align-self:center → шапка по ширине контента и центрирована (не тянется на всю ширину). */
html.obs-mode .stream-header { align-self: center; justify-content: center; min-width: 0; max-width: 100%; }
html.obs-mode .stream-header > * { min-width: 0; }
/* Значок твича в оверлее не нужен (шапка: [ава] Ник ✕ •(фиолет) Векторно.рф). */
html.obs-mode .stream-header .tw-logo { display: none; }
/* Ник стримера — метка, не форма поверх видео. */
html.obs-mode .stream-header .tw-input {
  border: none; background: transparent; padding: 0;
  width: auto; max-width: 60vw;
  field-sizing: content;        /* ширина по тексту — убирает пустой хвост от size=10 перед ✕ */
  pointer-events: none;
  font-size: 15px; font-weight: 800;   /* чуть меньше текстового лого «Векторно.рф» (.brand 18px) */
  text-overflow: ellipsis; overflow: hidden; white-space: nowrap;
}
/* ✕ — немой разделитель-коллаб между стримером и Векторно (не кнопка). Отрицательные
   margin стягивают соседей (ник/лого) ближе — меньше отступ слева/справа от ✕. */
html.obs-mode .stream-header .tw-clear {
  display: inline-flex;
  pointer-events: none;
  background: transparent !important;
  color: var(--muted); font-weight: 400;
  margin: 0 -4px;
}
/* «Фиолетовый шарик» статуса в оверлее не нужен — убираем (зрителю статус не показываем). */
html.obs-mode .stream-header .tw-status { display: none; }
/* «Векторно.рф» — чуть меньше в оверлее (было 18px). */
html.obs-mode .stream-header .brand { font-size: 16px; }

/* Тень текста для читаемости на любом фоне видео. */
html.obs-mode .stream-cols,
html.obs-mode .stream-header,
html.obs-mode #players-roster {
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.75);
}
/* Заливки/фоны строк — КАК В ОРИГИНАЛЕ (тон + стеклянность .row: var(--panel-solid)
   + backdrop-filter). Активное слово (.active-guess) и ростер (#players-roster) в OBS
   получают такую же подложку-стекло (см. правила выше/ниже) — единый визуальный язык.
   ::before-заливка ранга opacity 0.30 + saturate 1.3 (см. ниже) рисуется поверх фона. */
/* Сорт-кнопку убираем — ростер занимает всю ширину .board-tabs (он уже flex:1). */
html.obs-mode #sort-toggle { display: none; }
/* Аватар+ник стримера в ростере не нужны (шапка их уже показывает) — скрываем префикс. */
html.obs-mode #players-roster .pl-prefix { display: none; }
/* Ростер — подложка-стекло как у .row и .active-guess (единый визуальный язык overlay'я).
   Высота 44px = .row / .active-guess (единообразие трёх «полос» overlay'я). */
html.obs-mode #players-roster {
  background: var(--panel-solid);
  -webkit-backdrop-filter: var(--blur-light);
          backdrop-filter: var(--blur-light);
  border-radius: 10px;
  padding: 6px 12px;
  min-height: 44px;
  box-sizing: border-box;
}
/* Единые отступы overlay в OBS: 6px между active-word / ростером / списком (как gap в .list).
   Перебивает базовые .board-tabs { margin-top: 8px } и .col-board .board { margin-top: 6px }. */
html.obs-mode .col-board .board-tabs { margin-top: 6px; }
/* Аватарки центрированы по вертикали: padding-top = padding-bottom (5px из PlayersRoster.css)
   — даёт 13/13 visual symmetry внутри ростера (min-height 44 + padding 6+6 + content 28). */
html.obs-mode #players-roster .pl-emo,
html.obs-mode #players-roster .pl-stream-head { padding-top: 5px; }
/* Аватарки заполняют ширину слева направо; overflow:hidden обрезает последнюю частичную
   (renderBoard рендерит на 1 больше, чем влезает) — без пустых полей по бокам. */
html.obs-mode #players-roster .pl-emos { justify-content: flex-start; }
/* Между ростером и списком слов — 0 (ростер вплотную). Если есть визуальный зазор —
   это от внутреннего padding-bottom ростера (6px root padding-bottom). */
html.obs-mode .col-board .board { margin-top: 0; }
/* BoardTabs.css даёт margin-bottom: 8px ростеру (.board-tabs) — в OBS это многовато,
   но 4px оказалось слишком тесно. 6px = единый ритм с базовым `gap-sm` overlay'я. */
html.obs-mode .col-board .board-tabs { margin-bottom: 6px; }
/* Заливки слов (зелёная/оранжевая/красная) насыщеннее, но всё ещё с прозрачностью. */
html.obs-mode .active-guess::before,
html.obs-mode .col-board .row::before { opacity: 0.30; filter: saturate(1.3); }
/* Flash активного слова на OBS-оверлее: финальный кадр МАТЧИТ base выше
   (opacity 0.30 + saturate(1.3)) — иначе после снятия .is-new opacity скачком
   возвращается с 0.16 (keyframe из ActiveGuess.css) на 0.30 base → визуально
   «загорается заново». Старт делаем ещё ярче, чтобы вспышка читалась — пользователь
   попросил «загореться и потухнуть» без передёргиваний по цвету. */
html.obs-mode .active-guess.is-new::before { animation-name: activeGuessBarFlashObs; }
@keyframes activeGuessBarFlashObs {
  0%   { opacity: 0.55; filter: brightness(1.5) saturate(1.7); }
  100% { opacity: 0.30; filter: saturate(1.3); }
}
/* Ники игроков — жирные (строки списка + активное слово). */
html.obs-mode .row .w .nick .name,
html.obs-mode .active-guess .word .nick .name { font-weight: 700; }
/* Победа в obs: компактнее отступы. Итоги по высоте НЕ ограничиваем — пусть выталкивают
   список слов (как в обычной игре); лишнее обрежется по низу бокса (main overflow:hidden). */
/* Плашку #win НЕ сжимать флексом — иначе .win{overflow:hidden} обрежет отсчёт/попытки. */
html.obs-mode .stream-won #win { flex: 0 0 auto; }
/* Итоги #stats сжимаемы: при scale>1 (ширина >500px) CSS-высота бокса мала, и сумма
   несжимаемых блоков (#win + ростер + #stats + .overlay-actions) переполняет низ — список
   .board уже схлопнут в 0, и хлопушка .oa-cheer вылазит за край и клипается. Отдаём лишние
   px за счёт #stats (overflow:hidden обрежет итоги снизу внутри своего бокса, без вылезания),
   чтобы хлопушка целиком осталась в боксе. При достатке высоты #stats сохраняет натуральный
   размер, а список .board (flex:1) по-прежнему заполняет остаток. */
html.obs-mode .stream-won #stats { flex: 0 1 auto; min-height: 0; overflow: hidden; }
html.obs-mode .stream-won #win   { margin-top: 4px; padding: 10px 16px; }  /* меньше отступ слова до верха (было 18px) */
html.obs-mode .stream-won #stats { margin-top: 6px; }
/* Между #stats (итогами) и .board (списком слов) при Won — отступ. Иначе примыкают визуально.
   Базовое obs-mode правило `html.obs-mode .col-board .board { margin-top: 0 }` (выше) корректно
   только для пары «ростер → список»; в stream-won сверху от .board идёт #stats, нужен зазор. */
html.obs-mode .stream-won .col-board .board { margin-top: 6px; }
/* `.board-tabs` (ростер) имеет base margin-bottom: 8px (BoardTabs.css:7) → суммарно с
   `#stats { margin-top: 6px }` зазор между ростером и итогами становится 14px (слишком большой).
   Обнуляем, чтобы зазор был ровно как везде в overlay — 6px. */
html.obs-mode .stream-won .col-board .board-tabs { margin-bottom: 0; }
/* Строки «попыток: N» и «новая игра через N сек» — читаемо поверх видео (в игре .muted small
   на сплошной карточке, на прозрачном оверлее тусклый серый теряется). */
html.obs-mode .win-stats,
html.obs-mode .win-wait { color: var(--text); font-size: 13px; }

/* OBS не кликает — снимаем выделение/курсор. */
html.obs-mode * { -webkit-user-select: none; user-select: none; cursor: default; }

/* ===== Не-твич публичный OBS-оверлей (html.obs-mode.public-mode, /{guid}/obs) =====
   Шапка не должна показывать твич-метку (аватарка стримера, ник, ✕-разделитель), потому
   что комната не твич-связана. Оставляем только бренд «Векторно.рф» по центру; публичный
   номер игры виден в ростере (префикс `🎲 #N`). */
html.obs-mode.public-mode .stream-header .tw-streamer-ava,
html.obs-mode.public-mode .stream-header .tw-input,
html.obs-mode.public-mode .stream-header .tw-clear { display: none; }
