/* ==========================================================================
   Menu — Figma 10:898 (425), 10:517 (576), 10:583 (768),
          10:637 (1024), 10:765 (1280), 10:856 (2560)
   В макете это второй фрейм компонента Header — Subheader, поэтому разметка
   лежит внутри <header>, а стили вынесены сюда: раскладок две и они разные.

   До 1024 — оверлей во весь экран: две равные колонки (слева подпись раздела,
   справа пункты), секции идут одна под другой с шагом 40. Высота содержимого
   в макете 1080 при кадре 1058, то есть меню заведомо длиннее экрана —
   скроллится внутри себя, страница под ним заблокирована (js/menu.js).

   С 1024 — панель под шапкой на 342: те же 12 колонок с зазором 4, что и у
   шапки, разделы стоят ровно под своими пунктами меню (cases 7–8, services
   9–10, About 11–12), подписи разделов при этом скрыты — их роль играет сам
   пункт шапки. Слева (1–2) описание студии и «Work with us», в колонках 4–5 —
   Legals, под ним Dark.

   Типографика: в мобильном моде Figma Variables пункты набраны стилем
   Text xl (font-size/md = 16, lh 1.5 → 24), подписи — Text md (14 → 21);
   в десктопном моде тот же компонент берёт Text sm (font-size/xs = 14 → 21).
   Кнопки (View all, Dark, Work with us) — стили Uppercase с lh 1.4, поэтому
   их высота строки задана в px: 16 × 1.4 = 22.4 → 22, 14 × 1.4 = 19.6 → 20.
   ========================================================================== */

/* Оверлей выезжает сверху вниз за то же время, что и панель на 1024+, — оба
   идут по общим токенам движения (--duration-enter / --duration-leave /
   --ease-soft), тем же, что и плитка кейса.
   Механизм другой только потому, что разная природа высоты: на панели
   раскрывается строка грида по содержимому (grid-template-rows: 0fr → 1fr),
   а здесь меню всегда занимает остаток экрана, поэтому анимируется сама
   высота — от нуля до «экран минус шапка». Для transition обе величины
   должны быть заданы, поэтому не flex: 1 1 auto, а явный calc. */
.menu {
  display: block;
  block-size: 0;                                /* закрыто */
  overflow: hidden;
  /* Схлопнутого в ноль мало: содержимое остаётся в обходе с клавиатуры и в
     дереве доступности, и Tab по закрытой странице шестнадцать раз уходил в
     невидимое меню. С JS это снимает inert (js/menu.js), но именно там, где
     он не загрузился, закрытое меню и открыть-то нечем — а обойти его
     по-прежнему приходится. visibility убирает его у всех и без скрипта,
     включая Safari 15.4, где inert ещё нет.

     Переключается она в конце ухода — как у панели языка и списка бюджетов:
     пока меню закрывается, оно ещё на виду, а спрятанное — уже нет. */
  visibility: hidden;
  /* Те же токены движения, что у плитки кейса: одна кривая и одна пара
     длительностей на весь сайт, приход длиннее ухода */
  transition:
    block-size var(--duration-leave) var(--ease-soft),
    visibility 0s linear var(--duration-leave);
  width: 100%;
  background-color: var(--color-bg-base);

  /* локальная шкала блока: мобильный мод переменных */
  --menu-font-size: var(--font-size-md);        /* 16 */
  --menu-uppercase-line-height: 22px;           /* 16 × 1.4 = 22.4 → 22 */
  --menu-note-font-size: var(--font-size-sm);   /* 14 */
}

.header--open .menu {
  /* оверлей: шапка сверху, меню занимает остаток экрана и скроллится само */
  flex: 0 0 auto;
  block-size: calc(100dvh - var(--header-height));
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  visibility: visible;
  transition:
    block-size var(--duration-enter) var(--ease-soft),
    visibility 0s linear 0s;
}

.menu__viewport {
  width: 100%;
  min-height: 0;
}

.menu__inner {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-3xl);                              /* 40 между секциями */
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-block: var(--spacing-2xl) var(--spacing-lg);  /* 32 сверху, 16 снизу */
}

/* --- Секция: подпись слева, пункты справа ------------------------------- */

.menu__section {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--spacing-xl);
  align-items: start;
}

.menu__title {
  grid-column: 1;
  margin: 0;
  color: var(--color-text-subtle);
  font-size: var(--menu-note-font-size);
  font-weight: var(--font-weight-regular);
  line-height: var(--line-height-text-relaxed);
  letter-spacing: var(--letter-spacing-text);
}

/* В макете подпись Legals стоит во фрейме фиксированной высоты 16 — она
   единственная выровнена не по строке, а по своему боксу, и из-за этого
   сидит на 4px выше первого пункта. Высота задана буквально, как в макете,
   а высота строки прижата к ней же: с 21 текст вставал на 1px ниже, чем в
   Figma, — половинный интерлиньяж округляется в другую сторону. */
.menu__title--legals {
  display: flex;
  align-items: center;
  height: 16px;
  line-height: 16px;
}

.menu__group {
  grid-column: 2;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-xl);   /* 24 между списком и «View all» */
}

.menu__list {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-md);   /* 8 */
}

/* About — единственный список с шагом 4 (в макете 28 против 32) */
.menu__list--tight {
  gap: var(--spacing-sm);
}

.menu__link {
  display: block;
  color: var(--color-text-primary);
  font-size: var(--menu-font-size);
  line-height: var(--line-height-text-relaxed);
  letter-spacing: var(--letter-spacing-text);
}

/* Пункт, выключенный в админке: услуга или раздел, страницы которого ещё
   нет. В разметке он уже не ссылка, а <span> (tools/shared/page.mjs), так
   что курсор и клавиатура его не касаются и без pointer-events; правило
   оставлено на случай, если такой класс однажды повесят на ссылку. */
.menu__link--disabled {
  opacity: var(--opacity-disabled);
  pointer-events: none;
}

/* --- Кнопки (View all / Dark / Work with us) ---------------------------- */

.menu__actions {
  display: flex;
  align-items: flex-start;
  gap: var(--spacing-sm);
  margin: 0;
}

.menu__cta,
.menu__counter {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding-block-end: var(--spacing-sm);
  font-size: var(--menu-font-size);
  line-height: var(--menu-uppercase-line-height);
  letter-spacing: var(--letter-spacing-uppercase);
  text-transform: uppercase;
  text-align: center;
}

/* Счётчик «+N» рядом с «View all» — единственный, кому перенос запрещён: он и
   так в два-три знака, а разорванный смотрелся бы опечаткой. */
.menu__counter {
  white-space: nowrap;
}

.menu__counter {
  color: var(--color-text-subtle);
}

/* Подписи-кнопки переносятся, если не помещаются, — запрета переноса на них
   нет, хотя и был. Колонка раздела на 320 — это 124px, и русское «Работать с
   нами» (140) вылезало из неё на 16: в открытом меню появлялась горизонтальная
   прокрутка, которой в закрытом не было, и потому она не ловилась обычным
   прогоном. Английское «Work with us» туда помещается, и переносить ему нечего
   — на всех остальных языках и ширинах ничего не меняется. */

/* Подчёркивание тенью, а не рамкой — по той же причине, что и в шапке:
   в Figma обводка идёт внутрь фрейма и высоту бокса не меняет. */
.menu__cta--underline {
  box-shadow: inset 0 -1px 0 var(--color-border-primary);
}

/* Кнопка темы показывает, куда переключит, а не где стоишь: в светлой теме на
   ней «Dark», в тёмной — «Light». В разметке лежат обе подписи, меняется
   видимая — тот же приём, что у пары «Menu / Close» в шапке.
   Переключает их CSS, а не JS, поэтому подпись верна с первой отрисовки:
   действующую тему несёт data-theme на <html> (см. бутстрап в <head>).

   Иконка идёт за подписью: луна рядом с «Dark», солнце рядом с «Light», —
   иначе в тёмной теме получалась бы луна при подписи «Light». Глиф зависит
   от состояния, поэтому класса icon--moon в разметке нет: обе ссылки на файл
   стоят здесь, у .menu__icon, и переключает их тот же data-theme. */
.menu__toggle-label--to-light {
  display: none;
}

:root[data-theme="dark"] .menu__toggle-label--to-dark {
  display: none;
}

:root[data-theme="dark"] .menu__toggle-label--to-light {
  display: inline;
}

.menu__toggle {
  grid-column: 2;
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  margin: 0;
}

/* Иконка в макете лежит в боксе 12×16: 12×12 сверху и те же 4 снизу, что и
   у подчёркнутых кнопок, — так «Dark» и луна стоят на одной линии. Контур и
   цвет даёт общий механизм (.icon в css/icons.css), здесь только сам глиф,
   размер и это поле снизу.
   Начало координат маски — content-box: 12×12 приходятся на сам квадрат
   иконки, нижние 4 остаются полем. */
.menu__icon {
  --icon-size: 12px;
  --icon-url: url("../../assets/img/icons/moon.svg");
  box-sizing: content-box;
  padding-block-end: var(--spacing-sm);
  -webkit-mask-origin: content-box;
  mask-origin: content-box;
}

:root[data-theme="dark"] .menu__icon {
  --icon-url: url("../../assets/img/icons/sun.svg");
}

.menu__note {
  grid-column: 1;
  margin: 0;
  color: var(--color-text-subtle);
  font-size: var(--menu-note-font-size);
  line-height: var(--line-height-text-relaxed);
  letter-spacing: var(--letter-spacing-text);
}

.menu__section--cta {
  /* в макете это фрейм фиксированной высоты 92 при любом числе строк подписи */
  min-height: 92px;
}

.menu__section--cta > .menu__cta {
  grid-column: 2;
  justify-self: start;
}

/* --- 1024+ · Figma 10:637 (1024), 10:765 (1280), 10:856 (2560) ----------
   Панель под шапкой. Сетка та же, что у шапки: 12 колонок, зазор 4, предел
   ширины 1872 — поэтому разделы стоят ровно под своими пунктами меню.
   Строк две: в первой Legals, во второй Dark, прижатый к низу; высоту всей
   панели задаёт самый длинный раздел — cases (10 × 21 + 9 × 4 = 246, плюс 24
   до кнопки и сама кнопка 24 = 294). Отсюда 342 = 32 + 294 + 16.

   Шкала шрифта здесь другая: в десктопном моде переменных тот же компонент
   набран font-size/xs = 14, то есть пункты 14/21, кнопки 14/20. */
@media (min-width: 1024px) {
  .menu {
    display: grid;
    grid-template-rows: 0fr;                 /* закрыто */
    /* высота здесь идёт от содержимого, а не от экрана: раскрывается строка,
       а не сам блок (мобильный вариант — в начале файла) */
    block-size: auto;
    overflow: visible;
    /* visibility — та же, что и у мобильного варианта в начале файла:
       схлопнутая строка грида содержимое не прячет, и без неё закрытая
       панель осталась бы в обходе с клавиатуры. Переключается в конце ухода,
       поэтому и здесь своя дорожка с задержкой. */
    transition:
      grid-template-rows var(--duration-leave) var(--ease-soft),
      visibility 0s linear var(--duration-leave);

    --menu-font-size: var(--font-size-xs);   /* 14 */
    --menu-uppercase-line-height: 20px;      /* 14 × 1.4 = 19.6 → 20 */
    --menu-note-font-size: var(--font-size-xs);
  }

  .header--open .menu {
    display: grid;
    grid-template-rows: 1fr;                 /* открыто */
    flex: none;
    block-size: auto;
    overflow: visible;
    transition:
      grid-template-rows var(--duration-enter) var(--ease-soft),
      visibility 0s linear 0s;
  }

  /* схлопывание идёт по этому слою, поэтому вертикальные поля живут
     ниже — на .menu__inner: иначе панель не сжалась бы в ноль */
  .menu__viewport {
    overflow: hidden;
  }

  .menu__inner {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    grid-template-rows: auto 1fr;
    gap: 0 var(--spacing-sm);
  }

  /* подписи разделов не нужны: их роль играет пункт шапки над колонкой */
  .menu__title {
    display: none;
  }

  .menu__section {
    display: flex;
    flex-direction: column;
  }

  /* колонки макета: описание 1–2, Legals и Dark 4–5, cases 7–8,
     services 9–10, About 11–12 */
  .menu__section--cta {
    grid-column: 1 / span 2;
    grid-row: 1 / span 2;
    justify-content: space-between;
    align-items: flex-start;
    min-height: 0;
  }

  .menu__section--legals {
    grid-column: 4 / span 2;
    grid-row: 1;
  }

  .menu__section--theme {
    grid-column: 4;
    grid-row: 2;
    justify-content: flex-end;   /* Dark прижат к низу панели */
  }

  .menu__section--cases {
    grid-column: 7 / span 2;
    grid-row: 1 / span 2;
  }

  .menu__section--services {
    grid-column: 9 / span 2;
    grid-row: 1 / span 2;
  }

  .menu__section--about {
    grid-column: 11 / span 2;
    grid-row: 1 / span 2;
  }

  /* на десктопе все списки идут с шагом 4 (25 против 32 в мобильном макете) */
  .menu__list {
    gap: var(--spacing-sm);
  }
}

/* --- Появление: стаггер строк ------------------------------------------
   Строки проявляются сверху вниз, колонка за колонкой: задержка складывается
   из номера раздела (--menu-col) и номера строки внутри него (--menu-row).
   Порядок разделов свой на каждой раскладке — на мобильной он совпадает с
   разметкой, на десктопной идёт слева направо, как они стоят в панели.
   Закрытие без задержек: уходят все строки разом.

   Шаг стаггера ужат с 30/12 до 24/10 вместе с переходом на длинные токены:
   иначе на самой дальней строке задержка складывалась бы с приходом в 660мс,
   а меню теперь выпадает по наведению, а не по осознанному клику. При
   нынешних числах волна докатывается до последней строки за 616мс, и первые
   строки при этом стоят на месте с самого начала.

   Всё это живёт внутри prefers-reduced-motion: no-preference — при reduce
   правила не применяются вовсе и меню просто появляется, как в макете. */
@media (prefers-reduced-motion: no-preference) {
  .menu__title,
  .menu__list > li,
  .menu__actions,
  .menu__toggle,
  .menu__note,
  .menu__section--cta > .menu__cta {
    opacity: 0;
    /* приход собран из трёх мягких составляющих: непрозрачность, подъём и
       расфокус — по отдельности каждая почти не читается, вместе строка
       «проявляется», а не выскакивает. Подъём тот же, что у «View Case» в
       плитке кейса, — 6px. */
    translate: 0 6px;
    filter: blur(4px);
    transition: opacity var(--duration-leave) var(--ease-soft),
                translate var(--duration-leave) var(--ease-soft),
                filter var(--duration-leave) var(--ease-soft);
  }

  .header--open .menu__title,
  .header--open .menu__list > li,
  .header--open .menu__actions,
  .header--open .menu__toggle,
  .header--open .menu__note,
  .header--open .menu__section--cta > .menu__cta {
    opacity: 1;
    translate: 0 0;
    filter: blur(0);
    transition-duration: var(--duration-enter);
    transition-delay: calc(var(--menu-col, 0) * 24ms + var(--menu-row, 0) * 10ms);
  }

  .menu__section--cases    { --menu-col: 0; }
  .menu__section--services { --menu-col: 1; }
  .menu__section--about    { --menu-col: 2; }
  .menu__section--legals   { --menu-col: 3; }
  .menu__section--theme    { --menu-col: 4; }
  .menu__section--cta      { --menu-col: 5; }

  .menu__list > li:nth-child(1)  { --menu-row: 0; }
  .menu__list > li:nth-child(2)  { --menu-row: 1; }
  .menu__list > li:nth-child(3)  { --menu-row: 2; }
  .menu__list > li:nth-child(4)  { --menu-row: 3; }
  .menu__list > li:nth-child(5)  { --menu-row: 4; }
  .menu__list > li:nth-child(6)  { --menu-row: 5; }
  .menu__list > li:nth-child(7)  { --menu-row: 6; }
  .menu__list > li:nth-child(8)  { --menu-row: 7; }
  .menu__list > li:nth-child(9)  { --menu-row: 8; }
  .menu__list > li:nth-child(10) { --menu-row: 9; }

  .menu__actions { --menu-row: 10; }
  .menu__section--cta > .menu__cta { --menu-row: 1; }

  /* на панели порядок разделов — слева направо, как они стоят в макете */
  @media (min-width: 1024px) {
    .menu__section--cta      { --menu-col: 0; }
    .menu__section--legals   { --menu-col: 1; }
    .menu__section--theme    { --menu-col: 1; }
    .menu__section--cases    { --menu-col: 2; }
    .menu__section--services { --menu-col: 3; }
    .menu__section--about    { --menu-col: 4; }
  }
}

/* --- Приглушение страницы ----------------------------------------------
   При открытом меню содержимое страницы уходит в половинную непрозрачность.
   Значение снято с макетов: плитки кейсов под панелью на 1024, 1280 и 2560
   светлее исходных файлов ровно вдвое — при подложке #fafafa это даёт 0.5
   по всем трём каналам (проверка — в README, «Сверка с макетом»).

   В макете непрозрачность стоит на фрейме контента (hero + сетка); футер в
   кадр не попадает ни на одной ширине, но гасится вместе с ним — иначе при
   прокрутке с открытой панелью он остался бы единственным ярким пятном.

   До 1024 этого не видно вовсе: там меню перекрывает экран целиком. */
.page__main,
.footer {
  transition: opacity var(--duration-leave) var(--ease-soft);
}

.page--menu-open .page__main,
.page--menu-open .footer {
  opacity: var(--menu-dim-opacity);
  transition-duration: var(--duration-enter);
}

/* При reduce меню открывается сразу: и раскрытие панели, и приглушение
   страницы мгновенные — состояние читается по самому меню, а не по движению.

   Блок стоит в самом конце файла нарочно. Он перебивает правила той же
   специфичности (.menu — 0,1,0; .page__main — 0,0,1), а такие споры решает
   порядок: пока он лежал выше, приглушение страницы объявлялось после него и
   выигрывало — при reduce страница всё равно гасла с анимацией. */
@media (prefers-reduced-motion: reduce) {
  .menu {
    transition: none;
  }

  .page__main,
  .footer {
    transition: none;
  }
}
