/* ════════════════════════════════════════════════════════════════════
   AIR «Воздух» — каркас оболочки.

   Ядро — геометрия CONTRACT §2 (источник прошлых провалов):
   навигация = 100dvh при ЛЮБОМ контенте, скроллится КОНТЕНТ внутри своей
   колонки, а не страница. Поверх ядра — раскладка уровня ФинПо: колонка
   навигации, шапка 52, контентная зона, подвал, мобильный дровер.

   Значения — только токены var(--air-*). Литеральные px оставлены ровно там,
   где это геометрия иконки или hairline (правило пакета).
   Движение — только --air-t-* и --air-ease-*; у fixed-оверлеев анимируется
   ТОЛЬКО opacity (ловушка §5.2: transform занят позиционированием).
   ════════════════════════════════════════════════════════════════════ */

/* ── 1. Сетка каркаса ───────────────────────────────────────────────── */

/* Пакет не навязывает продукту глобальный reset, поэтому border-box объявлен
   точечно на своих узлах: иначе поля страницы и высота шапки 52 «поедут»
   ровно на величину padding/border (проверено — без этого шапка 53px). */
.air-shell,
.air-shell__nav,
.air-shell__logo,
.air-shell__nav-body,
.air-shell__header,
.air-shell__header-spacer,
.air-shell__actions,
.air-shell__topnav,
.air-shell__burger,
.air-content,
.air-shell__page,
.air-shell__footer,
.air-shell__skip,
.air-shell__scrim,
.air-shell__drawer,
.air-shell__drawer-head {
  box-sizing: border-box;
}

.air-shell {
  display: grid;
  height: 100dvh; /* НЕ min-height — иначе скроллится страница, а не контент */
  grid-template-columns: var(--air-nav-w) minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto; /* шапка · контент · подвал */
  grid-template-areas:
    'nav header'
    'nav main'
    'nav footer';
  background: var(--air-bg);
  color: var(--air-text);
}

/* Эффективная ширина колонки навигации. Атрибут ставит сам каркас: он знает и
   выбор пользователя, и ширину экрана, а tokens.css §10 знает только выбор
   (при деградации сайдбар→rail значение на <html> уже неверно). */
.air-shell[data-air-nav='sidebar'] {
  --air-nav-w: var(--air-sidebar-w);
}
.air-shell[data-air-nav='rail'] {
  --air-nav-w: var(--air-rail-w);
}

/* Топбар и мобильный режим — одна колонка: меню живёт в шапке / в дровере. */
.air-shell[data-air-nav='topbar'],
.air-shell[data-air-nav='mobile'],
.air-shell.air-shell--no-nav {
  --air-nav-w: 0px;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    'header'
    'main'
    'footer';
}

/* ── 2. Колонка навигации ───────────────────────────────────────────── */

.air-shell__nav {
  grid-area: nav;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  overflow: hidden; /* тень вложенного .air-nav клипается — не двоится */
  background: var(--air-surface);
  box-shadow: var(--air-shadow-float);
  z-index: var(--air-z-nav);
}

.air-shell__logo {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  min-width: 0;
  padding: var(--air-s-4) var(--air-s-4) var(--air-s-2);
}
/* Знак внутри шапки/дровера — без собственных полей, их даёт контейнер. */
.air-shell__logo--inline {
  padding: 0;
}

/* Обёртка слота nav. Ряд minmax(0,1fr) + stretch даёт вложенному меню полную
   высоту колонки (работает .air-nav__spacer, низ прижат), а собственный
   overflow-y гарантирует прокрутку длинного меню даже если компонент навигации
   не объявил её сам. Длинное меню скроллится здесь, страница — никогда. */
.air-shell__nav-body {
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
  display: grid;
  grid-template-rows: minmax(0, 1fr);
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* Эталон §2.2 — навигация как flex-колонка со спейсером (низ прижат). */
.air-nav {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--air-surface);
  box-shadow: var(--air-shadow-float);
  z-index: var(--air-z-nav);
}

.air-nav__spacer {
  flex: 1;
} /* всё после него прижато к низу */

/* ── 3. Шапка ───────────────────────────────────────────────────────── */

.air-shell__header {
  grid-area: header;
  z-index: var(--air-z-sticky);
  display: flex;
  align-items: center;
  gap: var(--air-s-2);
  min-width: 0;
  height: var(--air-topbar-h);
  padding: 0 var(--air-s-6);
  border-bottom: 1px solid var(--air-hairline);
  background: color-mix(in srgb, var(--air-bg) 80%, transparent);
  backdrop-filter: var(--air-blur-nav);
  -webkit-backdrop-filter: var(--air-blur-nav);
}

/* На узком экране поля шапки ужимаются: бургер и кластер справа важнее полей.
 *
 * И шапка ПЕРЕНОСИТСЯ НА ВТОРОЙ РЯД. До 06.08.2026 она была рядом жёсткой высоты со слотом
 * действий `flex-shrink: 0` — значит любой продукт, положивший в слот больше двух кнопок, уезжал
 * за правый край, и дотянуться до управления было нельзя. Замер Billy на 320px: кластеру нужно
 * 328px при доступных 176, и поштучным сжатием это не закрывается — считано, а не прикинуто.
 *
 * Перенос на второй ряд — не вольность, а НАЗВАННОЕ средство канона `interface-layout`: воздух
 * возвращают полями и переносами, и только не кеглем. Бюджет шапки в 56px канон задаёт для точки
 * отсчёта (ноутбук 1280), а не для экрана шириной с телефон; ряд сетки здесь и так `auto`, так что
 * расти шапке ничто не мешало, кроме собственной жёсткой высоты. */
@media (max-width: 639px) {
  .air-shell__header {
    height: auto;
    min-height: var(--air-topbar-h);
    flex-wrap: wrap;
    row-gap: var(--air-s-2);
    padding: var(--air-s-2);
  }
  /* Кластер шириной во всю строку сам начинает новую: знак с бургером остаются на первой, управление
     занимает вторую целиком. Распорку для этого трогать НЕ нужно — отдельной строкой она добавила бы
     пустой ряд и два лишних межстрочных зазора. */
  .air-shell__actions {
    flex-shrink: 1;
    flex-wrap: wrap;
    min-width: 0;
    width: 100%;
    justify-content: flex-end;
  }
}

.air-shell__header-spacer {
  flex: 1 1 auto;
  min-width: 0;
}

.air-shell__actions {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: var(--air-s-1);
}

/* Вариант topbar: меню лентой в шапке. Горизонтальная прокрутка — с закрытым
   вертикальным (ловушка §5.3) и скрытым скроллбаром: строка всего 52px. */
.air-shell__topnav {
  flex: 1 1 auto;
  min-width: 0;
  /* stretch + height:100% держат ленту РОВНО в строке шапки: слот, который
     нарисует вертикальный список, не расплывётся поверх контента. */
  align-self: stretch;
  height: 100%;
  display: flex;
  align-items: center;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.air-shell__topnav::-webkit-scrollbar {
  display: none;
  width: 0;
  height: 0;
}

/* Бургер и крестик дровера — тач-цель 48 (--air-control-h-lg). */
.air-shell__burger {
  flex-shrink: 0;
  display: grid;
  place-items: center;
  width: var(--air-control-h-lg);
  height: var(--air-control-h-lg);
  border: 0;
  border-radius: var(--air-r-pill);
  background: transparent;
  color: var(--air-text-soft);
  cursor: pointer;
  transition:
    background var(--air-t-fast) var(--air-ease-out),
    color var(--air-t-fast) var(--air-ease-out);
}
.air-shell__burger:hover {
  background: var(--air-tint-hover);
  color: var(--air-text-strong);
}
.air-shell__burger:active {
  background: var(--air-tint-press);
}
.air-shell__burger > svg {
  width: 20px; /* геометрия иконки */
  height: 20px;
}

/* ── 4. Контент ─────────────────────────────────────────────────────── */

.air-content {
  grid-area: main;
  overflow-y: auto;
  overscroll-behavior: contain; /* скролл не утекает на страницу */
  min-width: 0;
}
/* Цель skip-link фокусируется программно; кольцо вокруг всего экрана было бы
   шумом — подтверждение перехода даёт сам перескок фокуса. */
.air-content:focus {
  outline: none;
  box-shadow: none;
}

/* Страница: поля и центрирование. Продукту с полноширинной страницей достаточно
   поставить на своей ветке --air-page-max: none. */
.air-shell__page {
  width: 100%;
  max-width: var(--air-page-max, var(--air-content-max));
  min-width: 0;
  margin: 0 auto;
  padding: var(--air-pad-page);
  animation: air-fade-in var(--air-t-base) var(--air-ease-out) both;
}
@media (max-width: 1023px) {
  .air-shell__page {
    padding: var(--air-s-7);
  }
}
@media (max-width: 639px) {
  .air-shell__page {
    padding: var(--air-s-4);
  }
}

/* ── 5. Подвал ──────────────────────────────────────────────────────── */

.air-shell__footer {
  grid-area: footer;
  display: flex;
  align-items: center;
  gap: var(--air-s-3);
  min-width: 0;
  padding: var(--air-s-3) var(--air-s-6);
  border-top: 1px solid var(--air-hairline);
  background: var(--air-surface);
  color: var(--air-text-soft);
  font: var(--air-type-caption);
}
@media (max-width: 639px) {
  .air-shell__footer {
    padding: var(--air-s-3) var(--air-s-4);
  }
}

/* ── 6. Skip-link ───────────────────────────────────────────────────── */

/* Прячем сдвигом top за верхнюю кромку — БЕЗ transform, чтобы не заводить
   вторую роль у свойства, которым элемент позиционируется. */
.air-shell__skip {
  position: fixed;
  top: calc(-1 * var(--air-s-10));
  left: var(--air-s-4);
  z-index: var(--air-z-tooltip); /* выше дровера, скрима и тостов */
  padding: var(--air-s-2) var(--air-s-4);
  border-radius: var(--air-r-md);
  background: var(--air-accent);
  color: var(--air-on-accent);
  font: var(--air-type-body-sm);
  font-weight: var(--air-fw-strong);
  text-decoration: none;
  box-shadow: var(--air-shadow-float);
  transition: top var(--air-t-fast) var(--air-ease-out);
}
.air-shell__skip:focus,
.air-shell__skip:focus-visible {
  top: var(--air-s-2);
}

/* ── 7. Мобильный дровер ────────────────────────────────────────────── */

.air-shell__scrim {
  position: fixed;
  inset: 0;
  background: var(--air-scrim);
  z-index: var(--air-z-overlay);
  animation: air-fade-in var(--air-t-base) var(--air-ease-out);
}

/* Панель прижата к левой кромке через inset, ширина ограничена вьюпортом.
   Вход — только opacity: transform у позиционируемых оверлеев не анимируем. */
.air-shell__drawer {
  position: fixed;
  inset-block: 0;
  left: 0;
  width: min(85vw, var(--air-sidebar-w));
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
  background: var(--air-surface);
  border-radius: 0 var(--air-r-xl) var(--air-r-xl) 0;
  box-shadow: var(--air-shadow-modal);
  z-index: var(--air-z-modal);
  outline: none;
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
  padding-left: env(safe-area-inset-left);
  animation: air-fade-in var(--air-t-base) var(--air-ease-out);
}

.air-shell__drawer-head {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--air-s-2);
  padding: var(--air-s-3) var(--air-s-3) var(--air-s-2) var(--air-s-4);
}

/* ── 8. Движение ────────────────────────────────────────────────────── */

/* Дубль keyframes на случай, если каркас подключили без nav-батча;
   в tokens.css анимация та же — только opacity. */
@keyframes air-fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/* ── 9. Шапка страницы ──────────────────────────────────────────────── */

.air-page-head {
  display: flex;
  flex-direction: column;
  gap: var(--air-s-4);
  margin-bottom: var(--air-gap-section);
  animation: air-mist var(--air-t-base) var(--air-ease-out) both;
}
/* На широком действия встают справа и садятся на базовую линию заголовка, а не на его середину:
   заголовок здесь выше всего в блоке, и центрирование увело бы кнопки вверх от текста. */
@media (min-width: 640px) {
  .air-page-head {
    flex-direction: row;
    align-items: flex-end;
    justify-content: space-between;
  }
}

.air-page-head__text {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--air-s-1);
}

.air-page-head__eyebrow {
  margin: 0;
  font: var(--air-type-micro);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--air-text-soft);
}

.air-page-head__title {
  margin: 0;
  font: var(--air-type-h1);
  letter-spacing: -0.02em;
  color: var(--air-text-strong);
}
/* Дисплейный кегль появляется только там, где под него есть ширина: 30 пикселей на телефоне
   съедают две строки из трёх видимых. */
@media (min-width: 640px) {
  .air-page-head__title {
    font: var(--air-type-display);
    letter-spacing: -0.02em;
  }
}

.air-page-head__desc {
  margin: 0;
  font: var(--air-type-body);
  color: var(--air-text-soft);
}

.air-page-head__actions {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: var(--air-s-2);
}

@media (prefers-reduced-motion: reduce) {
  .air-page-head {
    animation: none;
  }
}
[data-motion='reduced'] .air-page-head,
[data-motion='off'] .air-page-head {
  animation: none;
}
