/*
  Хаб «Библиотека» и визард каталога — DGT-UX-02 и DGT-UX-03,
  см. docs/sdd/portal-one-viewport.md §1 и §4.

  Правила лежат отдельным файлом и подключаются из layouts/courses/list.html:
  вкладки хаба, карточки треков и рельс ступеней есть ровно на одной странице
  портала, а css/courses-v2.css весит 143 КБ и едет на все.

  Базовое оформление каталога (.course-card, .courses-jump, .courses-stage) и
  сетки карточек (.material-grid, .video-grid) остаётся там. Здесь — только то,
  что делает из портянки хаб с визардом внутри:

    • полоса вкладок Курсы / Материалы / Видео над всем остальным;
    • шаг 1 — рельс ступеней слева вместо ряда «чипсов» над каталогом;
    • шаг 2 — сетка треков одной ступени, карточка ужата до ~120 px;
    • шаг 3 — выбранная карточка разворачивается в карту трека с главами;
    • деградация: без JavaScript всё это снова становится длинным каталогом.

  Файл подключён после courses-v2.css, поэтому переопределения работают без
  !important — специфичности .courses-wizard .foo хватает.
*/

/* --------------------------------------------- Хаб: полоса вкладок */

/*
  Полоса повторяет приём хаба Workbench: ссылки в <nav>, подчёркивание
  активной, цель нажатия ≥ 36 px. Роли навешивает js/portal-tabs.js.
*/
.library-hub__tabs {
  border-bottom: 1px solid var(--course-line);
  display: flex;
  gap: 2px;
  /* Полоса стоит вплотную к шапке страницы: каждый пиксель здесь уходит из
     запаса панели «Курсы», где 16 карточек самой длинной ступени и без того
     кончаются в паре десятков пикселей от сгиба. */
  margin-top: 10px;
  /*
    На узком экране полоса прокручивается вбок со scroll-snap, а не
    сворачивается в <select> (§«Мобильный экран» спеки): три вкладки должны
    быть видны как вкладки. Прокрутка здесь не спорит с правилом «одна
    прокрутка на документ» — она горизонтальная и живёт на полосе управления,
    а не на содержимом; вертикально полоса не прокручивается никогда.
  */
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  scrollbar-width: none;
  scroll-snap-type: x proximity;
}

.library-hub__tabs::-webkit-scrollbar {
  display: none;
}

.library-hub__tabs a {
  align-items: baseline;
  border-bottom: 2px solid transparent;
  color: var(--course-muted);
  display: inline-flex;
  font: 650 13px/1 var(--course-font);
  gap: 7px;
  justify-content: center;
  min-height: 40px;
  padding: 0 14px;
  scroll-snap-align: start;
  text-decoration: none;
  white-space: nowrap;
}

.library-hub__tabs a small {
  color: var(--course-subtle);
  font: 600 11px/1 var(--course-mono);
}

.library-hub__tabs a:hover {
  color: var(--course-text);
  text-decoration: none;
}

.library-hub__tabs a[aria-selected="true"] {
  border-bottom-color: var(--course-cyan);
  color: var(--course-text);
}

.library-hub__tabs a[aria-selected="true"] small {
  color: var(--course-cyan);
}

.library-hub__tabs a:focus-visible {
  outline: 2px solid var(--course-cyan);
  outline-offset: -3px;
}

/*
  Пока portal-tabs.js не применил состояние, страница успевает нарисовать все
  три панели подряд — та же защита от мигания, что у рельса ступеней и у хаба
  Workbench. Атрибут data-portal-panels-ready ставит модуль вкладок.
*/
.js .library-hub__panels:not([data-portal-panels-ready]) > .library-panel:not(:first-of-type) {
  display: none;
}

/* Якорь-цель панели: прыжок по #materials не должен прятать заголовок под
   липкой шапкой портала. */
.library-panel {
  scroll-margin-top: calc(var(--course-header-height, 64px) + 12px);
}

.no-js .library-panel + .library-panel {
  border-top: 1px solid var(--course-line);
  margin-top: 26px;
  padding-top: 22px;
}

/* ----------------------------------- Хаб: панели «Материалы» и «Видео» */

.library-panel__head {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
  margin: 16px 0 12px;
}

.library-panel__head h2 {
  font: 800 19px/1.2 var(--course-font);
  margin: 0;
}

.library-panel__head p {
  color: var(--course-muted);
  flex: 1 1 320px;
  font-size: 13px;
  line-height: 1.5;
  margin: 0;
}

/*
  Шесть карточек фиксированной сеткой, а не auto-fill из courses-v2.css: число
  колонок здесь не украшение, а то самое «влезает в экран». Замеры на текущей
  разметке (панель начинается на 284 px от верха):

    1440×900  запас 616 px   3×2 = 520 px   6×1 = 304 px
    1280×720  запас 436 px   3×2 = 520 px   6×1 = 304 px
     901×900  запас 616 px   3×2 = 520 px   6×1 карточка в 137 px — нечитаемо

  Отсюда и порог 1100 px: широкий экран получает полосу из шести карточек в
  один ряд (единственная раскладка, влезающая в 720 px высоты), узкий — три
  колонки в два ряда, где 616 px высоты хватает. Ниже 900 px «влезает» не
  обещается вовсе, там работает обычный auto-fill.
*/
.material-grid--library,
.video-grid--library {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

@media (min-width: 1100px) {
  .video-grid--library {
    grid-template-columns: repeat(6, minmax(0, 1fr));
  }
}

/*
  Карточка-превью короче своей канонической: описание материала и описание
  видео остаются в разметке (без JavaScript и в поиске они видны), но в панели
  хаба скрыты. Тот же приём и по той же причине, что у карточки трека на шаге 2
  визарда: шесть карточек с абзацем описания дают два ряда по 250 px и
  возвращают ту же портянку, от которой уходим.
*/
.material-grid--library .material-card p,
.video-grid--library .video-card__summary {
  display: none;
}

/*
  min-height карточек снимается намеренно: 330 px у видео и 188 px у материала —
  это их рост на канонических /video/ и /post/, где ряд карточек последний на
  экране и под ним пагинатор. В панели хаба под сеткой стоит ещё ссылка, а над
  ней — шапка страницы и полоса вкладок, и два ряда по 330 px дают 765 px при
  запасе в 620.
*/
.video-grid--library .video-card,
.material-grid--library .material-card {
  min-height: 0;
}

/* Обложка кадрируется по высоте, а не по соотношению сторон: 16/9 от колонки
   в 435 px — это 245 px одной картинки, больше половины запаса панели. */
.video-grid--library .video-card__thumb {
  aspect-ratio: auto;
  height: 104px;
}

.library-panel__all {
  margin: 14px 0 0;
}

.library-panel__all a {
  align-items: center;
  color: var(--course-cyan);
  display: inline-flex;
  font-size: 13px;
  font-weight: 650;
  gap: 6px;
}

@media (max-width: 900px) {
  .material-grid--library,
  .video-grid--library {
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  }
}

/* ------------------------------------------------ Шаг 1: рельс ступеней */

.courses-wizard {
  display: grid;
  gap: 22px;
  grid-template-columns: minmax(190px, 232px) minmax(0, 1fr);
  margin-top: 12px;
}

/*
  Тот же .courses-jump, что и раньше: без JavaScript это по-прежнему якорная
  навигация по семи секциям, и сценарий «Быстрый переход к ступени работает»
  проверяет ровно её. Со скриптом те же ссылки становятся вкладками —
  role="tablist" навешивает portal-tabs.js.
*/
.courses-wizard .courses-jump {
  align-content: start;
  display: grid;
  gap: 5px;
  margin: 0;
  position: sticky;
  top: calc(var(--course-header-height, 64px) + 14px);
}

/* Цель нажатия ≥ 36 px — требование спеки, а не вкусовщина. */
.courses-wizard .courses-jump a {
  border-radius: 8px;
  gap: 9px;
  justify-content: flex-start;
  line-height: 1.3;
  min-height: 38px;
  padding: 7px 12px;
  text-align: left;
}

.courses-wizard .courses-jump a[aria-selected="true"] {
  background: var(--course-control-active);
  border-color: var(--course-cyan);
  color: var(--course-text);
}

.courses-wizard .courses-jump a[aria-selected="true"] span {
  color: var(--course-cyan);
}

.courses-wizard .courses-jump a:focus-visible {
  outline: 2px solid var(--course-cyan);
  outline-offset: 2px;
}

/* ------------------------------------------------- Шаг 2: сетка треков */

.courses-wizard__panels {
  min-width: 0;
}

/* Рамка сверху нужна только там, где панели идут подряд, — то есть без
   JavaScript. Со скриптом видна ровно одна ступень, и линия над ней
   отделяла бы её от пустоты. */
.courses-wizard .courses-stage {
  border-top: 0;
  padding: 0 0 6px;
}

.no-js .courses-wizard .courses-stage + .courses-stage {
  border-top: 1px solid var(--course-line);
  margin-top: 24px;
  padding-top: 24px;
}

/*
  Пока portal-tabs.js не применил состояние, страница успевает нарисовать все
  семь ступеней подряд. Отложенный скрипт выполняется быстро, но «быстро» —
  не «никогда». Атрибут data-portal-panels-ready ставит модуль вкладок.
*/
.js .courses-wizard__panels:not([data-portal-panels-ready]) > .courses-stage:not(:first-of-type) {
  display: none;
}

.courses-wizard .courses-stage__head h2 {
  font-size: 19px;
}

.courses-wizard .courses-stage__head p {
  font-size: 13.5px;
  margin-top: 4px;
}

.courses-wizard .courses-stage__grid {
  gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(238px, 1fr));
  margin-top: 14px;
}

/*
  Карточка шага 2 — компактная: иконка, заголовок, две метрики, «Главы» и
  «Начать». Описание трека остаётся в разметке (без JavaScript и в поиске оно
  видно), но в сетке скрыто: полтора десятка карточек самой большой ступени
  иначе выстраиваются в пять рядов по 190 px и возвращают ту же портянку,
  только в рамке.
*/
/* Высота карточки — не вкусовщина, а бюджет: у самой длинной ступени их
   шестнадцать, и на 1440×900 они кончаются в двух десятках пикселей от сгиба.
   Каждый лишний пиксель здесь умножается на число рядов. */
.courses-wizard .course-card {
  align-content: start;
  display: grid;
  gap: 6px 10px;
  grid-template-columns: minmax(0, 1fr) auto;
  padding: 9px 12px;
}

.courses-wizard .course-card__head {
  align-items: center;
  gap: 10px;
  grid-column: 1 / -1;
}

.courses-wizard .course-card__icon {
  height: 26px;
  width: 26px;
}

.courses-wizard .course-card__head h3 {
  font-size: 15px;
  line-height: 1.25;
}

/*
  Заголовок карточки — ссылка на саму карточку (#track-<трек>). Без
  JavaScript это обычный якорь, со скриптом — вход в шаг 3.

  Растягивать эту ссылку невидимым слоем на всю карточку заманчиво (вся
  карточка становится целью нажатия), но нельзя: слой перехватывает нажатия
  по «Программе трека», по «Начать» и по кнопке «Повторить» в упавшей
  загрузке. Проверено сценариями — три из них покраснели ровно на этом.
*/
.course-card__open {
  color: var(--course-text);
  display: block;
  text-decoration: none;
}

/* Шеврон нарисован рамками, а не символом «›»: пустой content не попадает в
   дерево доступности, и скринридер не зачитывает украшение после названия
   трека. Заодно это те же байты, что и у символа, но без шрифтовых сюрпризов. */
.course-card__open::after {
  border-bottom: 1.5px solid var(--course-subtle);
  border-right: 1.5px solid var(--course-subtle);
  content: "";
  display: inline-block;
  height: 6px;
  margin-bottom: 1px;
  margin-left: 8px;
  transform: rotate(-45deg);
  width: 6px;
}

.course-card__open:hover,
.course-card__open:focus-visible {
  color: var(--track-accent);
  text-decoration: none;
}

.courses-wizard .course-card:hover {
  border-color: var(--course-line-strong);
}

.courses-wizard .course-card__head p {
  display: none;
}

/*
  Свёрнутый трек — строка, а не карточка (DGT-UX-16).

  Ужимать карточку было больше некуда, а арифметика не сходилась: у самой
  длинной ступени («Ремесло») пятнадцать треков, и на 901px сетка даёт всего
  две колонки — восемь рядов по 111px. Замер нижней границы панели против
  window.innerHeight (chromium прогона):

               901×900   1000×900   1120×900   1280×720   1440×900
    Ремесло     +461       +438       +121       +218      влезает
    Языки       +351       +340       +106       +199      влезает
    Продукт     +119        +75      влезает      +45      влезает

  Колонками это не лечится: на 901px под сетку 608px, и третья колонка режет
  карточку до 196px, четвёртая — до 144px, где название трека уже переносится
  и отыгрывает высоту обратно. Спека (§4) называла второй выход — собственная
  прокрутка панели, — но он закрыт правилом владельца «одновременно
  прокручиваемых контейнеров быть не должно ни одного».

  Остался третий, названный там же: менять подачу ступени. Свёрнутый трек
  показывает то же, что показывал бы пункт списка, — иконку и название со
  шевроном; метрики, «Программу трека» и «Начать» забирает раскрытый трек,
  где для них есть место. Приём не новый: строкой выше ровно так же и ровно
  по той же причине спрятано описание трека (`.course-card__head p`).

  Из разметки ничего не убрано — только `display: none`, — поэтому
  покарточный потолок в 1843 байта и страничный в 180 КБ остаются те же, а
  счётчик материалов и ссылка «Начать» по-прежнему читаются из HTML
  (features/courses-catalog.feature читает их cheerio, а не глазами).

  Правило висит на `role="tabpanel"`, а не на `.js`, и это не придирка.
  Каталог обязан оставаться полным — с метриками и рабочей ссылкой на
  /courses/programs/ — не только когда скрипт выключен, но и когда он не
  поднялся: свернуть трек в строку, не дав его раскрыть, хуже, чем не
  сворачивать вовсе. Класс `.js` этого не различает — его ставит head.html при
  любом живом JavaScript, — а `data-portal-panels-ready` тем более: его
  courses-wizard.js ставит и на аварийных выходах, чтобы вернуть читателю все
  ступени. Роль `tabpanel` печатает только portal-tabs.js и только после
  удачной разметки набора; сервер её не печатает вовсе (это стережёт сценарий
  «Без JavaScript набор врёт скринридеру про вкладки»). То есть правило
  включается ровно тогда, когда трек есть чем раскрыть.

  Раскрытый трек исключён через `:not(.is-open)` — ему ничего восстанавливать
  не нужно, он просто не попадает под правило.

  Стало: карточка 46px вместо 111px (58px там, где название в две строки), и
  каждая ступень влезает на всех пяти размерах — худшая «Ремесло» кончается за
  57px до сгиба на 901×900 и за 52px на 1280×720.
*/
.courses-stage[role="tabpanel"] .course-card:not(.is-open) .course-card__meta,
.courses-stage[role="tabpanel"] .course-card:not(.is-open) .course-card__list,
.courses-stage[role="tabpanel"] .course-card:not(.is-open) .course-card__cta {
  display: none;
}

.courses-wizard .course-card__meta {
  font-size: 12px;
  gap: 4px 10px;
  grid-column: 1 / -1;
}

/* Точка между метриками рисуется в CSS: два <svg><use> на карточку стоили
   470 байт, на всём каталоге — 30 КБ HTML при пороге страницы в 200 КБ. */
.courses-wizard .course-card__meta span + span::before {
  color: var(--course-line-strong);
  content: "·";
  margin-right: 5px;
}

/*
  Третья метрика — дата обновления трека (DGT-UX-23) — на телефоне уходит на
  свою строку целиком и без точки.

  Почему это отдельное правило, а не «пусть переносится само». Разделитель
  нарисован как ::before у следующего элемента, и при переносе он открывает
  новую строку: «18 материалов · ~257 мин» / «· обновлён 07.08.2026». Точка в
  начале строки читается как опечатка, а не как разделитель, и CSS не умеет
  различать «начало строки» — поэтому перенос назначается явно.

  Порог измерен, а не выбран на глаз: самой длинной метрике каталога
  («20 материалов · ~456 мин · обновлён 08.08.2026») нужно 319 точек, а
  доступная карточке ширина — это ширина окна минус 83. То есть в строку
  метрики перестают влезать около 402 точек; 480 берётся с запасом на рост
  чисел (счётчик глав и минут растёт с каждым новым материалом), и выше этого
  порога правило ничего не меняет.
*/
@media (max-width: 480px) {
  .courses-wizard .course-card__meta [data-updated] {
    flex-basis: 100%;
  }

  .courses-wizard .course-card__meta [data-updated]::before {
    content: none;
  }
}

.courses-wizard .course-card__list {
  grid-column: 1;
  min-width: 0;
}

.courses-wizard .course-card__list summary {
  font-size: 12.5px;
  padding: 0;
}

.courses-wizard .course-card__cta {
  align-self: start;
  font-size: 12.5px;
  grid-column: 2;
  margin-top: 0;
  padding: 7px 12px;
}

/* Та же стрелка, что раньше приезжала <svg><use> на 235 байт в каждой
   карточке каталога. Рисуется рамками и молчит для скринридера. */
.courses-wizard .course-card__cta::after {
  border-bottom: 1.5px solid currentcolor;
  border-right: 1.5px solid currentcolor;
  content: "";
  display: inline-block;
  height: 6px;
  margin-left: 8px;
  transform: rotate(-45deg);
  width: 6px;
}

.courses-wizard .course-card__empty {
  grid-column: 1 / -1;
}

/* --------------------------------------------- Шаг 3: карта трека */

/*
  Открытый трек занимает панель целиком, соседние карточки убираются. Это не
  «ещё одна вкладка»: карточка та же самая, просто развёрнутая, поэтому в
  DOM ничего не дублируется и вес страницы от шага 3 не растёт.
*/
.courses-stage[data-wizard-open] .courses-stage__grid {
  display: block;
}

.courses-stage[data-wizard-open] .course-card:not(.is-open) {
  display: none;
}

.courses-wizard .course-card.is-open {
  gap: 12px;
  grid-template-columns: minmax(0, 1fr);
  padding: 16px 18px;
}

.courses-wizard .course-card.is-open .course-card__head {
  align-items: start;
}

.courses-wizard .course-card.is-open .course-card__head h3 {
  font-size: 18px;
}

.courses-wizard .course-card.is-open .course-card__head p {
  display: block;
}

/* В развёрнутой карточке шеврон ни к чему: разворачивать больше нечего. */
.courses-wizard .course-card.is-open .course-card__open::after {
  content: none;
}

.courses-wizard .course-card.is-open .course-card__list,
.courses-wizard .course-card.is-open .course-card__cta {
  grid-column: 1;
}

.courses-wizard .course-card.is-open .course-card__cta {
  justify-self: start;
}

/* Главы трека в две-три колонки: шестнадцать глав в один столбец — та же
   портянка, ради ухода от которой всё и затевалось. */
.courses-wizard .course-card.is-open .course-card__list ol {
  columns: 3 210px;
  gap: 20px;
  padding-left: 18px;
}

.courses-wizard .course-card.is-open .course-card__list li {
  break-inside: avoid;
  margin-bottom: 3px;
}

.courses-wizard__back {
  align-items: center;
  background: var(--course-control);
  border: 1px solid var(--course-line);
  border-radius: 6px;
  color: var(--course-text);
  cursor: pointer;
  display: inline-flex;
  font: inherit;
  font-size: 12.5px;
  gap: 8px;
  margin: 14px 0 0;
  min-height: 36px;
  padding: 7px 12px;
}

.courses-wizard__back::before {
  border-bottom: 1.5px solid currentcolor;
  border-left: 1.5px solid currentcolor;
  content: "";
  display: inline-block;
  height: 6px;
  transform: rotate(45deg);
  width: 6px;
}

.courses-wizard__back:hover,
.courses-wizard__back:focus-visible {
  background: var(--course-control-active);
  border-color: var(--course-cyan);
}

/* ------------------------------------------------------- Узкий экран */

@media (max-width: 900px) {
  .courses-wizard {
    gap: 14px;
    grid-template-columns: minmax(0, 1fr);
  }

  /*
    Рельс превращается в горизонтальную ленту со scroll-snap, а не в <select>:
    семь ступеней должны оставаться видимыми как ступени. Горизонтального
    переполнения у документа при этом нет — лента прокручивается сама.
  */
  .courses-wizard .courses-jump {
    grid-auto-columns: max-content;
    grid-auto-flow: column;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    padding-bottom: 4px;
    position: static;
    scroll-snap-type: x proximity;
  }

  .courses-wizard .courses-jump a {
    scroll-snap-align: start;
    white-space: nowrap;
  }

  .courses-wizard .courses-stage__grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .courses-wizard .course-card.is-open .course-card__list ol {
    columns: auto;
  }
}

/* ----------------------------------------------------------- Печать */

/*
  На бумаге вкладок нет: печатается длинный каталог — ровно та портянка, от
  которой уходим на экране, и на бумаге она уместна. !important нужен, чтобы
  перебить UA-правило [hidden] { display: none }.
*/
@media print {
  [data-wizard-panel][hidden] {
    display: block !important;
  }

  .courses-stage[data-wizard-open] .course-card:not(.is-open) {
    display: grid !important;
  }

  /* На бумаге шага 2 нет, поэтому и прятать описание трека не от кого:
     печатается полная карточка. */
  .courses-wizard .course-card__head p {
    display: block !important;
  }

  .courses-wizard__back {
    display: none !important;
  }
}

/* -------------------------------------------- prefers-reduced-motion */

@media (prefers-reduced-motion: reduce) {
  .courses-wizard .courses-jump a,
  .courses-wizard .course-card {
    transition: none;
  }
}
