/*
  layout.css — шапка, навигация, сетки, контейнеры.
  Брейкпоинты (значения дублируют tokens.css --bp-*, CSS не даёт переменные в @media):
  480 (бургер), 768 (планшет), 1280 (десктоп-база), 1440 (широкий десктоп).
*/

@layer layout {
  /* ---------- Skip-link «к содержимому» ---------- */
  .skip-link {
    position: absolute;
    top: -48px;
    left: var(--space-3);
    z-index: var(--z-toast);
    background: var(--color-accent);
    color: var(--color-text-on-accent);
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-md);
    text-decoration: none;
    font-weight: 600;
    transition: top var(--dur-base) var(--ease-out);
  }

  .skip-link:focus-visible {
    top: var(--space-3);
  }

  @media (prefers-reduced-motion: reduce) {
    .skip-link { transition: none; }
  }

  .container {
    width: 100%;
    max-width: var(--content-max);
    margin-inline: auto;
    padding-inline: clamp(16px, 4vw, 48px);
  }

  .container--thread {
    max-width: var(--thread-max);
  }

  .container--wide {
    max-width: 1520px;
  }

  /* ---------- Шапка: липкая, всегда видимая (решение хаба §2) ---------- */
  .app-header {
    position: sticky;
    top: 0;
    z-index: var(--z-sticky-header);
    display: flex;
    align-items: center;
    gap: var(--space-4);
    min-height: 60px;
    padding: var(--space-2) clamp(16px, 4vw, 48px);
    background: var(--color-header-bg);
    backdrop-filter: var(--glass-blur);
    -webkit-backdrop-filter: var(--glass-blur);
    border-bottom: var(--glass-border);
  }

  @supports not (backdrop-filter: blur(1px)) {
    .app-header {
      background: var(--color-surface);
    }
  }

  .app-header.is-scrolled {
    box-shadow: var(--shadow-sm);
  }

  .app-header__brand {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-weight: 700;
    font-size: var(--fs-md);
    color: var(--color-text);
    text-decoration: none;
    flex-shrink: 0;
  }

  .app-header__nav {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    flex: 1;
  }

  .app-header__nav-link {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0 var(--space-3);
    border-radius: var(--radius-md);
    color: var(--color-text-muted);
    text-decoration: none;
    font-size: var(--fs-sm);
    font-weight: 600;
    transition: background var(--dur-base) var(--ease-out), color var(--dur-base) var(--ease-out);
  }

  .app-header__nav-link:hover {
    background: var(--color-surface-sunken);
    color: var(--color-text);
  }

  .app-header__nav-link[aria-current="page"] {
    color: var(--color-accent);
    background: var(--color-accent-soft);
  }

  .app-header__actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-shrink: 0;
    margin-left: auto;
  }

  .app-header__burger {
    display: inline-flex;
    width: 44px;
    height: 44px;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-md);
  }

  .app-header__burger:hover {
    background: var(--color-surface-sunken);
  }

  /* Мобильная выезжающая панель навигации (≤480px).
     top+height, а не inset: top+bottom-стрейч у fixed-блока в этой сборке (замер
     показал computed height 32px вместо ожидаемых ~508px при top:60/bottom:0)
     не растягивал блок до низа экрана — явная высота через calc(100dvh - …) надёжнее
     и уже проверена на .chat-screen. */
  .app-header__panel {
    position: fixed;
    top: 60px;
    left: 0;
    right: 0;
    height: calc(100dvh - 60px);
    z-index: var(--z-overlay);
    background: var(--color-surface);
    padding: var(--space-4) clamp(16px, 4vw, 48px);
    overflow-y: auto;
  }

  .app-header__panel[hidden] {
    display: none;
  }

  /* Полное название пространства в бургер-панели (доработка 1, дефект 3) — обычный
     текст, не бейдж: здесь оно не соревнуется за ширину строки шапки. */
  .app-header__panel-workspace {
    margin: 0 0 var(--space-3) 0;
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--color-accent);
  }

  .app-header__panel .app-header__nav {
    flex-direction: column;
    align-items: stretch;
  }

  .app-header__panel .app-header__nav-link {
    width: 100%;
  }

  .app-header__panel-actions {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-3);
    margin-top: var(--space-4);
    padding-top: var(--space-4);
    border-top: 1px solid var(--color-border);
  }

  .app-header__panel-actions form {
    width: 100%;
  }

  @media (min-width: 480px) {
    .app-header__burger {
      display: none;
    }

    /* Без !important: у .app-header__panel нет конкурирующего авторского display —
       обычный display:none здесь ничего не проигрывает (§19 скилла не нарушается). */
    .app-header__panel {
      display: none;
    }
  }

  @media (max-width: 479.98px) {
    /* Только прямая навигация шапки — вложенная копия внутри .app-header__panel
       (класс тот же, .app-header__nav) должна остаться видимой, это и есть
       единственное место, где на такой ширине можно кликнуть «Чаты»/«Статистика». */
    .app-header > .app-header__nav {
      display: none;
    }

    .app-header {
      flex-wrap: nowrap;
      /* Доработка 1 (найдено при замере после дефекта 3): полный бейдж «FINANCE» +
         статус подключения + кнопка выхода + бургер вместе с «AiChat» текстом не
         помещались в 320px — шапка реально распирала страницу вбок (scrollWidth 342
         при innerWidth 320, поймано измерением). Уже стоящий компактный бейдж сам по
         себе не виноват — тесно всем вместе. Уже стоит вплотную к минимуму */
      gap: var(--space-2);
    }

    /* Текст «AiChat» прячем, иконка-логотип остаётся — экономит место активным
       элементам (бейдж пространства, статус, выход, бургер), которым сжиматься
       нельзя (тач-цели ≥44px, статус — не ellipsis). Логотип и на иконке остаётся
       ссылкой на «Чаты» — behaviour не меняется, страдает только текстовая подпись. */
    .app-header__brand span {
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip: rect(0 0 0 0);
      white-space: nowrap;
    }
  }

  /* Хайд theme-toggle/connection-status/badge--workspace для этой ширины — в components.css,
     рядом с базовыми правилами тех же классов (см. пояснение там): @layer в этом файле
     регистрируется раньше @layer components, поэтому правило display:none отсюда всё равно
     проиграло бы конкурирующему display:inline-flex из components.css независимо от
     специфичности и медиазапроса — приоритет слоя проверяется раньше специфичности. */

  /* ---------- Основная сетка страницы ---------- */
  .app-shell {
    display: flex;
    flex-direction: column;
    min-height: 100dvh;
  }

  /* Доработка 1 (2-й проход): явная height вместо min-height нужна ТОЛЬКО на ≥768px,
     и вот почему. .app-main — flex-item с flex-basis:0% (шорткат flex:1). Пока у
     .app-shell была только min-height, его главный размер оставался неопределённым, а по
     спеку Flexbox процентный flex-basis при неопределённом главном размере контейнера
     резолвится не в 0, а в «content» — .app-main переставал реально расти через flex-grow,
     а .chat-screen внутри (высота — явный calc(100dvh - var(--header-h))) добавлял ещё и
     высоту своего внутреннего контента поверх: .app-shell фактически рендерился на 64px
     выше вьюпорта (864 вместо 800 на 1280×800), и страница получала второй, страничный
     скроллбар вдобавок к внутреннему скроллу треда — «два скролла», которых дефект 2
     требует не допускать на ≥768px. Явная height делает главный размер определённым —
     flex-basis:0% резолвится как положено, .chat-screen точно занимает 100dvh-минус-шапка.
     ПОЧЕМУ НЕ ГЛОБАЛЬНО (это и была ошибка первого прохода): явная height на ЛЮБОЙ ширине
     ломает липкий композер на мобильном chat.html — .composer (position:sticky; bottom:0)
     считает своим «контейнером стики» ближайшего блочного предка с определённым главным
     размером; если .app-shell зафиксирован явной height в 100dvh, а его контент на ≤767px
     (тред без внутреннего скролла, страница растёт естественно и может быть в разы выше
     вьюпорта) реально выше этой высоты, браузер трактует .app-shell как границу stiky-диапазона
     и композер залипает у нижнего края вьюпорта ВСЕГДА, а не только когда пользователь
     реально проскроллил страницу до конца — на деле ведёт себя как position:fixed и на
     верхней части длинного треда перекрывает сообщения. Проверено: getBoundingClientRect()
     композера возвращал одно и то же (top:445/bottom:568 при вьюпорте 568) что при
     scrollY:0, что при scrollY на самом дне — с min-height такой подмены не происходит,
     потому что .app-shell честно растягивается вместе с контентом, и sticky-диапазон —
     это вся страница, как и должно быть. Поэтому явная height включается медиазапросом
     ровно там, где нужна (≥768px, где у chat.html вообще нет постраничного скролла —
     тред скроллится внутри себя, а не композер прилипает к длинной странице). */
  @media (min-width: 768px) {
    .app-shell {
      height: 100dvh;
    }
  }

  .app-main {
    flex: 1;
    padding-block: var(--space-6);
  }

  /* См. base.html — используется на chat.html, чтобы .chat-screen точно вписывался
     в 100dvh-минус-шапка без второго скролла на ≥768px (доработка 1, дефект 2). */
  .app-main--flush {
    padding-block: 0;
    min-height: 0;
  }

  .app-footer {
    padding: var(--space-4) clamp(16px, 4vw, 48px);
    text-align: center;
    color: var(--color-text-faint);
    font-size: var(--fs-xs);
    border-top: 1px solid var(--color-border);
  }

  /* ---------- Хлебные крошки ---------- */
  .breadcrumbs {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    flex-wrap: wrap;
    font-size: var(--fs-sm);
    color: var(--color-text-muted);
    margin-bottom: var(--space-4);
  }

  .breadcrumbs__sep {
    color: var(--color-text-faint);
  }

  .breadcrumbs a {
    color: var(--color-text-muted);
    text-decoration: none;
  }

  .breadcrumbs a:hover {
    color: var(--color-accent);
  }

  .breadcrumbs__current {
    color: var(--color-text);
    font-weight: 600;
  }

  /* ---------- Флеш-сообщения ---------- */
  /* Без сообщений регион не должен резервировать вертикальное место (доработка 1,
     дефект 1 — на 320px каждый лишний пиксель чужеродной высоты отъедает бюджет треда).
     :has() — Baseline widely available (Chrome/Firefox/Safari, §1 скилла), верхний
     паддинг появляется только когда внутри реально есть .flash. */
  .flash-region {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding-inline: clamp(16px, 4vw, 48px);
  }

  .flash-region:has(.flash) {
    padding-top: var(--space-4);
  }

  /* ---------- Сетки списков/карточек ---------- */
  .grid-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: var(--space-4);
  }

  .stack {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
  }

  @media (min-width: 768px) {
    /* :not(.app-main--flush), а не просто .app-main (доработка 1, найдено при замере
       на 1280px chat.html): при равной специфичности (один класс) это правило шло в
       источнике ПОСЛЕ .app-main--flush и на ≥768px перебивало его padding-block:0 обратно
       на 32px — .chat-screen (высота — явный calc(100dvh - var(--header-h))) с этой
       прибавкой уже не помещался в отведённую .app-main высоту и вылезал на 32px ниже
       вьюпорта, снова давая страничный скроллбар вдобавок к скроллу треда (дефект 2).
       :not() поднимает специфичность ровно настолько, чтобы .app-main--flush побеждал
       на любой ширине, как и задумано. */
    .app-main:not(.app-main--flush) {
      padding-block: var(--space-8);
    }
  }
}
