.frame-wrap {
  position: relative;
  flex: 1;
  min-height: 0;
  background: var(--surface);
}
/* Слот экрана консоли, а не встроенного отчёта. Отчёт живёт в `<iframe>`,
   который прокручивается сам; экран рисует консоль, и прокручиваться должен он
   сам — у его корня (`.sp-panel`) для этого уже есть `overflow-y: auto`.
   Работает это ТОЛЬКО в ограниченной по высоте flex-колонке: в обычном блоке
   `flex: 1` инертен, панель растёт по содержимому, вылезает за рамку и
   срезается `main { overflow: hidden }` — без полосы прокрутки, молча. Именно
   так «Разбор звонков» показывал первые шесть строк таблицы из двадцати
   восьми. `position: absolute; inset: 0` повторяет геометрию `.state`, чтобы
   переключение режимов слота не меняло рамку.

   Слот опознаётся по содержимому (`:has`), а НЕ по классу, который вешал бы
   скрипт. Довод не стилистический: разметку экрана рисует `dashboards.js`, а
   он ES-модуль по неверсионированному адресу и вполне может лежать у зрителя в
   кэше вчерашним. Стили же приезжают с отпечатком содержимого в адресе
   (`app/assets.py`), то есть гарантированно свежими, — и правило, которому
   нечего ждать от скрипта, начинает действовать сразу. Заодно исчезает и
   переключение режимов руками: под отчётом в слоте лежит `.state`, `:has(>
   .sp-panel)` там просто не совпадает, и делать `position: absolute` пустому
   слоту поверх живого `<iframe>` (он перехватывал бы клики) уже некому.

   Селектор знает про `.sp-panel` — корень речевой аналитики, единственного
   сегодняшнего экрана. Появится второй — его корень дописывается сюда же
   отдельным правилом, а не общим списком: список селекторов целиком
   отбрасывается браузером, если хоть один в нём непонятен. */
.frame-wrap > div:has(> .sp-panel) {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* Всё, кроме прокручиваемой панели, стоит по своей высоте. Правило несущее, а
   не косметическое: ряд вкладок экрана несёт класс `.tabs`, а тот объявлен
   `flex: 1` для ГОРИЗОНТАЛЬНОЙ раскладки (`.tabs-row`). В колонке это же
   значение растянуло бы полосу вкладок по вертикали и отобрало высоту у
   панели. Панель исключена явно — её собственный `flex: 1` и должен победить. */
.frame-wrap > div:has(> .sp-panel) > :not(.sp-panel) {
  flex: none;
}

.frame-wrap iframe {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}

/* Полоса вкладок — СВОЯ строка под шапкой раздела, как в макете. Пока она жила
   внутри `.page-head` вместе с кнопками, у них не было общего базиса: `.tabs`
   тянется (`flex: 1`) и несёт `margin-bottom: -1px` под свою подчёркивающую
   линию, а `.btn` — обычный inline-flex. Кнопки «Открыть в Metabase» и
   «Обновить» из-за этого вставали то по центру полосы, то переносились
   на вторую строку. */
.tabs-row {
  display: flex;
  align-items: flex-end;
  gap: 1rem;
  flex: none;
  padding: 0 1.1rem;
  border-bottom: 1px solid var(--border);
}

/* packages/ui/dashboards.css — раздел «Дашборды» (console-core-dashboards, Шаг 3).
   Переносит из analytics-dashboards/portal/backend/app/static/index.html только то,
   чего ещё нет в packages/ui/shell.css (.page-head/.head-actions/.btn/.tabs/.search) и
   packages/ui/states.css (.state/.spinner/.empty): раскладку iframe внутри .frame-wrap.
   .state/.spinner/.empty здесь намеренно НЕ переопределяются — они принадлежат
   states.css, чей класс state уже задаёт абсолютное позиционирование поверх всей
   области .frame-wrap, что и даёт портальное поведение слота состояния. */

/* Заметка о ненастроенных дашбордах. `flex: none` + `nowrap` обязательны:
   она делит строку с полосой вкладок, а та — единственный узел, которому
   позволено сжиматься и прокручиваться. */
.dash-note {
  flex: none;
  white-space: nowrap;
  font-size: .82rem;
  color: var(--ink-3);
  padding-bottom: .55rem;
}
