.tool-hub,
.tool-page {
  --tool-cyan-soft: color-mix(in srgb, var(--course-cyan) 11%, transparent);
}

/* ЭКРАН ХАБА СЧИТАЕТСЯ ВМЕСТЕ С ПОДВАЛОМ, А НЕ ПОМИМО НЕГО.
   Портальная .portal-main держит min-height: calc(100vh - шапка), то есть
   ровно экран, и юридический подвал в 177 px ложится ПОД него. Отсюда пол,
   ниже которого не опускается ни одна страница портала с подвалом: 64 + 836 +
   177 = 1077 px при экране 900, те самые 1,20 у /courses/ — и никакая правка
   содержимого их не снимает, потому что содержимое тут ни при чём. Замер
   /tools/ до этой строки: состояние «контракт» — 1077 px ровно, то есть
   упиралось в пол, а не в свой текст.

   Хаб выстраивается так же, как это уже сделано у хаба Workbench
   (static/css/workbench.css, .courses-body--workbench): страница — колонка,
   main забирает остаток высоты, подвал стоит на дне того же экрана. Пока
   содержимое помещается, документ ровно в экран; когда не помещается —
   прокручивается, как обычная страница, и подвал уезжает вниз сам.

   Через :has(), а не через класс на body: класс пришлось бы заводить в
   layouts/_default/baseof.html, то есть трогать шаблон всех 6 300 страниц
   ради одной. Приём в этом репозитории санкционирован и уже применён. */
body:has(> main.tool-hub) {
  display: flex;
  flex-direction: column;
  min-height: 100svh;
}

body:has(> main.tool-hub) > main.tool-hub {
  flex: 1 1 auto;
  min-height: 0;
  padding: 14px 0 20px;
}

.tool-hero {
  align-items: end;
  border-bottom: 1px solid var(--course-line);
  display: flex;
  gap: 32px;
  justify-content: space-between;
  padding: 28px 0 24px;
}

/* Шапка хаба — не обложка раздела, а строка адреса: под ней на том же экране
   обязан поместиться либо мастер целиком, либо каталог целиком. Замер
   1280×800: в прежнем виде (заголовок 48 px, отступы 40/24, описание в три
   строки) она съедала 221 px из 615, которые есть у содержимого вообще.

   Правила размеров стоят НИЖЕ, после .tool-hero h1 и .tool-hero p: у них та же
   специфичность, и здесь они молча ничего бы не делали. */
.tool-hero--hub {
  align-items: center;
  padding: 16px 0 14px;
}

.tool-hero h1 {
  color: var(--course-text);
  font-size: clamp(30px, 4vw, 48px);
  letter-spacing: -0.035em;
  line-height: 1.03;
  margin: 0;
}

.tool-hero p {
  color: var(--course-muted);
  font-size: 16px;
  line-height: 1.6;
  margin: 12px 0 0;
  max-width: 760px;
}

.tool-hero--hub h1 {
  font-size: clamp(23px, 2.3vw, 31px);
}

.tool-hero--hub p {
  font-size: 14px;
  line-height: 1.5;
  margin-top: 6px;
  max-width: 96ch;
}

.tool-local-badge {
  align-items: center;
  background: var(--course-panel);
  border: 1px solid var(--course-line);
  border-radius: var(--course-radius);
  color: var(--course-green);
  display: flex;
  flex: 0 0 auto;
  gap: 10px;
  max-width: 310px;
  padding: 11px 14px;
}

.tool-local-badge span {
  display: grid;
  gap: 2px;
}

.tool-local-badge strong {
  color: var(--course-text);
  font-size: 12.5px;
}

.tool-local-badge small {
  color: var(--course-subtle);
  font-size: 11.5px;
  line-height: 1.35;
}

/* КАТАЛОГ — ЭТО И ЕСТЬ СТРАНИЦА, И ПРЯТАТЬ ЕГО БОЛЬШЕ НЕЧЕМУ.
   Здесь стояли :has()-правила, убиравшие каталог, когда открыт мастер подбора,
   его цель или контракт: три списка подряд на одном экране — та самая портянка,
   от которой хаб уходил. Мастера нет (docs/sdd/portal-tools-hub.md,
   «Пересмотрено 18.08»), и прятать каталог стало нечем и незачем: он
   единственный список на странице, а контракт разворачивается ПОД ним, а не
   вместо него. */
.tool-catalog {
  padding: 20px 0 8px;
}

/* Шапка каталога — одна строка, а не три. Счёт, заголовок и выход на контракт
   стоят рядом: вертикально они занимали 88 px над сеткой, ради которой на
   страницу и приходят. */
.tool-catalog > .tool-section-heading {
  align-items: baseline;
  column-gap: 14px;
  display: flex;
  flex-wrap: wrap;
  row-gap: 2px;
}

.tool-catalog > .tool-section-heading h2 {
  margin: 0;
}

.tool-section-heading > span,
.tool-step {
  color: var(--course-cyan);
  font-family: var(--course-mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
}

.tool-section-heading h2,
.tool-workspace__intro h2,
.star-diagnostics h2 {
  color: var(--course-text);
  font-size: 21px;
  margin: 5px 0 0;
}

/* НОМЕР ШАГА СТОИТ В СТРОКУ С НАЗВАНИЕМ, А НЕ НАД НИМ.

   Отдельной строкой номер занимал 20 px (15 своих плюс 5 отступа сверху у h2)
   на КАЖДОМ шаге КАЖДОГО инструмента — при том, что рельс над панелью в этот
   момент показывает ровно то же самое: «04 Картина целиком» подсвечено. Двадцать
   пикселей чистого повтора там, где их не хватало содержимому: замер 3 сентября
   2026 (1280×800, /tools/cartesian-square/, шаг «Картина целиком») — заголовок
   61 px, а списку ответов при этом недоставало 166.

   Набрано тем же приёмом, что уже стоит выше по файлу у заголовка каталога
   (.tool-catalog > .tool-section-heading): flex по базовой линии. Подсказка
   переносится под них обоих — она третий элемент и переносу не мешает. */
.tool-wizard__panel > .tool-section-heading {
  align-items: baseline;
  column-gap: 12px;
  display: flex;
  flex-wrap: wrap;
  row-gap: 2px;
}

.tool-wizard__panel > .tool-section-heading h2 { margin: 0; }

.tool-wizard__panel > .tool-section-heading p {
  flex: 1 0 100%;
  margin-top: 4px;
}

.tool-section-heading p,
.tool-workspace__intro p,
.star-diagnostics > p {
  color: var(--course-muted);
  line-height: 1.55;
  margin: 7px 0 0;
  max-width: 760px;
}

/* Возврат из панели-цели: у каталога и у контракта он один и тот же, поэтому
   и класс один. */
.tool-panel-back {
  display: flex;
  flex-wrap: wrap;
  font-size: 13px;
  gap: 14px;
  margin: 0;
}

.tool-principles .tool-panel-back {
  margin-top: 10px;
}

/* Число колонок считает браузер, а не автор, и НИЖНЯЯ ГРАНИЦА КОЛОНКИ ПОДНЯТА
   215 → 230 px. Причина — подпись: она печатается целиком, и ширина колонки
   решает, во сколько строк она ляжет. Замер на собранной странице, десять
   карточек, высота документа (обрезанных подписей ни при одном значении нет):

     minmax   1440×900              1280×800
     215      6 колонок, 961 px     5 колонок, 867 px
     230      5 колонок, 900 px     5 колонок, 867 px
     260      5 колонок, 900 px     4 колонки, 993 px
     300      4 колонки, 917 px     3 колонки, 1059 px

   230 — единственное значение, дающее пять колонок на обеих ширинах: на 1440
   страница ровно в экран, на 1280 — 1,08 вместо прежних 1,24. Больше 230
   ломает 1280 (колонка шире, но их четыре, и подпись уходит вниз третьим
   рядом), меньше — 1440 (шесть узких колонок, подпись в пять строк). */
.tool-catalog__grid {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  margin-top: 14px;
}

/* КАРТОЧКА — ПЛИТКА НАБОК: ИКОНКА СЛЕВА, СПРАВА РУБРИКА, ИМЯ, ПОДПИСЬ И
   ДЕЙСТВИЕ. Раскладка была у модификатора .tool-card--compact, а сама
   .tool-card стояла вертикальной колонкой — второй вид нужен был панелям
   мастера подбора. Мастера нет, вид остался один, и модификатор снят: две
   раскладки, из которых применяется одна, расходятся молча.

   align-content: start — чтобы лишняя высота карточки (сетка тянет их до
   высоты самой длинной в ряду) собиралась внизу, а не размазывалась зазорами
   между рубрикой, именем и подписью. */
.tool-card {
  align-content: start;
  background: var(--course-panel);
  border: 1px solid var(--course-line);
  border-radius: var(--course-radius);
  color: var(--course-text);
  column-gap: 12px;
  display: grid;
  grid-template-areas:
    "icon eyebrow"
    "icon title"
    "icon text"
    "icon note"
    "icon open";
  grid-template-columns: auto minmax(0, 1fr);
  min-height: 0;
  padding: 12px 14px;
  position: relative;
  row-gap: 0;
  text-decoration: none;
  transition: border-color 0.15s ease, transform 0.15s ease;
}

/* АКЦЕНТ КАРТОЧКИ — ОДНА ПЕРЕМЕННАЯ, А НЕ БЛОК ПРАВИЛ НА КАЖДЫЙ ИНСТРУМЕНТ.
   Пока их было четыре, три блока-исключения читались. На девяти это тридцать
   строк, повторяющих друг друга в трёх местах — полоса сверху, подложка
   иконки и цвет ссылки, — и каждый новый инструмент требовал бы трёх правок
   вместо одной. Значение по умолчанию оставлено прежним: карточка без
   назначенного акцента выглядит собранной, а не сломанной. */
.tool-card {
  --tool-accent: var(--course-cyan);
}

.tool-card--architect { --tool-accent: var(--course-purple); }
.tool-card--workbench { --tool-accent: var(--course-blue); }
.tool-card--digit { --tool-accent: var(--course-green); }
.tool-card--digitwm { --tool-accent: var(--course-red); }
.tool-card--star { --tool-accent: var(--course-purple); }
.tool-card--score { --tool-accent: var(--course-orange); }
.tool-card--resume { --tool-accent: var(--course-blue); }
.tool-card--planner { --tool-accent: var(--course-green); }
.tool-card--digitdisk { --tool-accent: var(--course-orange); }

/* КАДРЫ И ОКНО ТЕРМИНАЛА ДЕЛЯТ ДВА РАЗДЕЛА — /digitdisk/ и /ouroboros/, — и
   делят они одни правила, а не две копии. Копия стоила бы здесь двухсот строк,
   которые разошлись бы молча: рамка, шапка с командой, огоньки, раскладка
   подписи сбоку и свёрнутый вывод буквами сняты замером панели и правятся
   вместе. Поэтому у каждого правила ниже просто два списка селекторов.

   ВРОЗЬ ИДЁТ РОВНО ТО, ЧТО У ИНСТРУМЕНТОВ РАЗНОЕ: подложка и высота окна
   (.digitdisk-term__screen против .ouroboros-term__screen — довод там же) и
   блок установки, которого у соседа нет вовсе. Всё остальное общее. */

/* Раздел digitdisk. Оболочка, рельс шагов и заголовки у него общие
   (.tool-workspace, .tool-wizard*, .tool-section-heading), поэтому здесь
   только то, чего нет больше нигде: пара карточек подкоманд, перечень границ и
   проза первой панели. Пять правил вместо своего файла — ровно потому, что
   раскладку держат общие части. */
.digitdisk-grid,
.ouroboros-grid {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin-top: 12px;
}

.digitdisk-card,
.ouroboros-card {
  background: var(--course-panel);
  border: 1px solid var(--course-line);
  min-width: 0;
  padding: 12px 14px;
}

.digitdisk-card code,
.ouroboros-card code {
  color: var(--course-cyan);
  font: 700 13px/1 var(--course-mono);
}

.digitdisk-card p,
.digitdisk-limits li,
.digitdisk-prose p,
.ouroboros-card p,
.ouroboros-limits li,
.ouroboros-prose p,
.ouroboros-note,
.ouroboros-install p {
  color: var(--course-muted);
  font-size: 13px;
  line-height: 1.55;
  margin: 8px 0 0;
}

.digitdisk-limits,
.ouroboros-limits {
  display: grid;
  gap: 8px;
  list-style: none;
  margin: 12px 0 0;
  padding: 0;
}

.digitdisk-limits li,
.ouroboros-limits li {
  border-left: 2px solid var(--course-line-strong);
  margin: 0;
  padding-left: 12px;
}

.digitdisk-prose > :first-child,
.ouroboros-prose > :first-child { margin-top: 12px; }

.digitdisk-source,
.ouroboros-source,
.ouroboros-course {
  font-size: 12.5px;
  margin: 14px 0 0;
}

.digitdisk-source a,
.ouroboros-source a,
.ouroboros-course a {
  align-items: center;
  color: var(--course-cyan);
  display: inline-flex;
  gap: 6px;
}

@media (max-width: 640px) {
  .digitdisk-grid,
  .ouroboros-grid { grid-template-columns: 1fr; }
}

/* ШАГ «В ДЕЛЕ»: СНИМКИ ЖИВОГО ЭКРАНА. Экран показан кадром (webp, отрисованный
   scripts/render-digitdisk-shots.py из data/digitdisk-output.json), а не
   строкой в абзаце: так же показаны сеанс /digitwm/ и шесть кадров консольного
   агента на /digit/. Тот же экран буквами лежит под кадром в <details> — на
   поиск по странице, скринридер и копирование. */
.digitdisk-shots,
.ouroboros-shots { margin-top: 8px; }

.digitdisk-shot,
.ouroboros-shot { margin: 0; }

.digitdisk-shot[hidden],
.ouroboros-shot[hidden] { display: none !important; }

/* Рельс снимков — младший брат рельса шагов: те же роли и та же клавиатура от
   portal-tabs.js, но ниже ростом и без нумерации. Нумеровать снимки нечем:
   прогоны — не шаги, следующие один за другим. */
.digitdisk-views,
.ouroboros-views {
  display: flex;
  gap: 4px;
  overflow-x: auto;
  scrollbar-width: thin;
}

.digitdisk-views a,
.ouroboros-views a {
  align-items: center;
  border: 1px solid var(--course-line);
  border-radius: var(--course-radius);
  color: var(--course-muted);
  display: inline-flex;
  flex: 0 0 auto;
  font-size: 12px;
  min-height: 30px;
  padding: 0 11px;
  text-decoration: none;
  white-space: nowrap;
}

.digitdisk-views a.is-active,
.digitdisk-views a[aria-selected="true"],
.ouroboros-views a.is-active,
.ouroboros-views a[aria-selected="true"] {
  background: var(--course-control-active);
  border-color: var(--course-cyan);
  color: var(--course-text);
}

/* ОКНО ТЕРМИНАЛА. Рамка, шапка с командой и три огонька — разметка и стили, а
   не часть картинки, и это решение. В кадре шапка уехала бы вверх вместе с
   выводом при первой же прокрутке — то есть окно «теряло» бы собственный
   заголовок, — а команда перестала бы копироваться: она текст и обязана им
   остаться, иначе перенести прогон в свой терминал можно только перепечатав. */
.digitdisk-term,
.ouroboros-term {
  border: 1px solid var(--course-line);
  border-radius: var(--course-radius);
  margin: 8px 0 0;
  overflow: hidden;
}

/* ⚠ ПОДЛОЖКА ЗАДАНА ЧИСЛОМ, А НЕ ТОКЕНОМ, И ЭТО НЕ НЕДОСМОТР. Терминал тёмный
   в обеих темах портала — как и кадры /digitwm/ и /digit/, снятые на тёмном и
   стоящие на светлой теме без подмены. Взять здесь --course-bg значило бы
   получить в светлой теме белое поле вокруг тёмной картинки: кадр уже своего
   окна (804 точки из 886), и разъехавшийся фон видно полосой справа. Число —
   это тот цвет, которым digitdisk заливает экран (SGR 48;2;5;8;13), и оно
   обязано совпадать с BG в scripts/render-digitdisk-shots.py: там же стоит
   встречный комментарий на этот файл. У «Уробороса» подложка своя и обязана
   совпадать с BG в scripts/render-ouroboros-shots.py.

   ОКНО РОВНО ПО КАДРУ, А ПОТОЛОК — ПО ЭКРАНУ. Раньше высота стояла числом:
   380 px у digitdisk (высота кадра обхода) и clamp(150px, 26vh, 220px) у
   «Уробороса». Числа эти платили дважды. Кадр короче окна оставлял под собой
   полосу пустой черноты: у «Уробороса» кадр «пара записей» — 96 точек в окне
   208, то есть 112 px чёрного ни за чем. Кадр выше окна прокручивался, хотя
   место на экране было: замер 1280×800 (headless chromium, дерево на
   0da9f59c) — «обмазать» 456 точек в окне 208, «трасса» 492 в том же 208.
   Жалоба владельца дословно: «надо скроллить код, мне кажется что можно высоту
   у блока кода сделать выше без скролла».

   Теперь высота — height: auto, то есть ровно кадр, а прокрутка появляется
   только там, где кадр выше ПОТОЛКА. Потолок считается от экрана, а не
   назначен: это всё, что остаётся панели после шапки портала, полосы чисел на
   телефоне, ряда ссылок внизу, шапки инструмента и постоянных частей самой
   панели (заголовок шага, рельс снимков, шапка окна, подпись-провенанс,
   «назад / дальше»). Постоянные части у двух разделов разной высоты — 250 px
   у digitdisk против 287 у «Уробороса», потому что провенанс там на строку
   длиннее, — поэтому вычитаемое у каждого своё, и оно снято замером, а не
   выведено на глаз.

   ЧЕГО ЭТО НЕ ОБЕЩАЕТ. «Совсем без прокрутки» — не обещает, и обещать нечем:
   свод «Уробороса» — 1428 точек, план уборки digitdisk — 596, а потолок при
   1280×800 равен 355 и 392. Чтобы план уборки встал целиком, окну браузера
   надо быть высотой 1007 px. Прокрутка внутри окна остаётся ровно там, где
   кадр физически выше экрана, и подвал самого кадра об этом говорит
   («строки 1–16 из 25»).

   Тянущееся окно на флексе пробовали до этого и отвергли: с min-height: 0 на
   звеньях панель считала, что всё поместилось, а подпись и провенанс лежали
   ПОВЕРХ кнопок «назад» и «дальше»; без min-height: 0 окно вырастало до
   1542 px внутри панели в 495. Потолок числом от 100dvh таких развилок не
   имеет: он не зависит от того, что внутри окна. */
.tool-page--digitdisk {
  --tool-appbar-extra: 0px;
  --tool-frame-room: calc(100dvh - var(--course-header-height) - var(--course-header-strip) - var(--tool-appbar-extra) - 344px);
}

.tool-page--ouroboros {
  --tool-appbar-extra: 0px;
  --tool-frame-room: calc(100dvh - var(--course-header-height) - var(--course-header-strip) - var(--tool-appbar-extra) - 381px);
}

/* ШАПКА С РЕЛЬСОМ РАСТЁТ СТУПЕНЯМИ, И СТУПЕНИ ЭТИ ИЗМЕРЕНЫ, А НЕ УГАДАНЫ.
   Рельс стоит в одну строку с заголовком, пока помещается; когда перестаёт —
   переносится под него и забирает свою высоту обратно. Сколько именно —
   печатает развёртка по ширине (headless chromium, обе редакции, оба раздела;
   число — высота .tool-appbar):

     ширина  1440 1366 1280 1200 1120 1024  900  860  820  760  700  390
     высота    69   69   69   69  113  113  113  113  ???  130  130   93

   На 820 высота зависит от редакции: 130 у английского «Уробороса», 113 у
   русского — то есть зависит от длины перевода, а не от вёрстки. Поэтому
   ступень взята по худшему из четырёх, с запасом: ниже 900 вычитается 61.
   Ошибка в запасе стоит нескольких точек высоты окна, ошибка в другую сторону —
   прокрутки всей панели, и цены эти несоизмеримы.

   Ниже 640 описание скрыто (.tool-appbar p { display: none }), и шапка снова
   ниже — 93 вместо 130. */
@media (max-width: 1199px) {
  .tool-page--digitdisk,
  .tool-page--ouroboros { --tool-appbar-extra: 44px; }
}

@media (max-width: 899px) {
  .tool-page--digitdisk,
  .tool-page--ouroboros { --tool-appbar-extra: 61px; }
}

@media (max-width: 640px) {
  .tool-page--digitdisk,
  .tool-page--ouroboros { --tool-appbar-extra: 24px; }
}

.digitdisk-term__screen,
.ouroboros-term__screen {
  height: auto;
  max-height: max(150px, var(--tool-frame-room));
  overflow: auto;
}

.digitdisk-term__screen { background: #05080d; }

.ouroboros-term__screen { background: #0b0e13; }

.digitdisk-term__screen img,
.ouroboros-term__screen img {
  display: block;
  max-width: none;
}

.digitdisk-term__screen:focus-visible,
.ouroboros-term__screen:focus-visible {
  outline: 2px solid var(--course-cyan);
  outline-offset: -2px;
}

.digitdisk-term__bar,
.ouroboros-term__bar {
  align-items: center;
  background: #151b24;
  border-bottom: 1px solid var(--course-line);
  display: flex;
  gap: 10px;
  margin: 0;
  min-width: 0;
  padding: 6px 10px;
}

.digitdisk-term__bar code,
.ouroboros-term__bar code {
  color: #7fe8dc;
  font: 600 12px/1.4 var(--course-mono);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Три огонька одним узлом: сам кружок плюс две тени со сдвигом. Отдельные
   элементы под них — три пустых span в разметке ради украшения, а разметка
   здесь и без того несёт окно, кадр и текст. */
.digitdisk-term__lights,
.ouroboros-term__lights {
  background: #ff7d86;
  border-radius: 50%;
  box-shadow: 13px 0 0 #ffad66, 26px 0 0 #75d69c;
  flex: 0 0 auto;
  height: 9px;
  margin-right: 28px;
  width: 9px;
}

.digitdisk-shot figcaption,
.digitdisk-shot__note,
.ouroboros-shot figcaption,
.ouroboros-shot__note {
  color: var(--course-muted);
  font-size: 12.5px;
  line-height: 1.5;
  margin: 8px 0 0;
}

.digitdisk-shot__note a,
.ouroboros-shot__note a { color: var(--course-cyan); }

/* ВЫВОД БУКВАМИ. Свёрнут по умолчанию не ради красоты, а ради правила одного
   экрана: развёрнутый он тянет 23–103 строки, и панель шага стала бы лентой.
   Свёрнутый он остаётся в DOM целиком — то есть достаётся скринридеру,
   поиску по странице (Chrome и Firefox раскрывают <details> при находке) и
   копированию в один щелчок. */
.digitdisk-shot__text,
.ouroboros-shot__text {
  font-size: 12.5px;
  margin: 8px 0 0;
}

.digitdisk-shot__text summary,
.ouroboros-shot__text summary {
  color: var(--course-cyan);
  cursor: pointer;
}

.digitdisk-shot__text pre,
.ouroboros-shot__text pre {
  background: var(--course-bg);
  border: 1px solid var(--course-line);
  border-radius: var(--course-radius);
  color: var(--course-text);
  font: 11.5px/1.5 var(--course-mono);
  margin: 8px 0 0;
  max-height: 240px;
  overflow: auto;
  padding: 10px 12px;
  white-space: pre;
}

/* НА ШИРОКОМ ЭКРАНЕ ПОДПИСЬ СТОИТ СБОКУ, А НЕ ПОД ОКНОМ. Кадр не шире
   собственного экрана — 804 точки у всех трёх снимков, снятых в терминале
   112 колонок, — а колонка шага на 1280×800 даёт 1184: справа остаётся место,
   и подпись под окном стоила бы панели двух лишних строк.

   СВЁРНУТЫЙ ВЫВОД БУКВАМИ НЕ СТОИТ ПАНЕЛИ НИ ОДНОЙ ТОЧКИ, и это не мелочь:
   отдельной строкой под окном он стоил ровно тех 27 px, которых панели не
   хватало. Окно занимает обе строки первой колонки, подпись — первую строку
   второй, а «весь вывод текстом» садится во вторую строку второй колонки, то
   есть в пустое место под подписью. Высоту фигуры задаёт окно, и свёрнутая
   строка в неё умещается даром. */
@media (min-width: 900px) {
  .digitdisk-shot,
  .ouroboros-shot {
    align-items: start;
    column-gap: 18px;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(200px, 280px);
  }

  .digitdisk-term,
  .ouroboros-term { grid-area: 1 / 1 / 3 / 2; }

  .digitdisk-shot figcaption,
  .ouroboros-shot figcaption { grid-area: 1 / 2 / 2 / 3; margin-top: 10px; }

  .digitdisk-shot__text,
  .ouroboros-shot__text { grid-area: 2 / 2 / 3 / 3; }

  /* РАЗВЁРНУТЫЙ — ВО ВСЮ ШИРИНУ: он повторяет кадр, а кадр шире колонки
     подписи (751 против 280), и читать его в колонку значило бы гонять
     горизонтальную прокрутку по каждой строке. Раскрытие переносит текст под
     окно на всю ширину, а окно возвращает в одну строку. Панель при этом
     перерастает свои 495 и прокручивается сама — это действие читателя, а не
     состояние страницы, и правило одного экрана меряет второе.
     :has() понимают все живые движки; где его нет, текст останется в колонке
     подписи — уже, но целиком читаемым и копируемым. */
  .digitdisk-shot:has(.digitdisk-shot__text[open]) .digitdisk-term,
  .ouroboros-shot:has(.ouroboros-shot__text[open]) .ouroboros-term {
    grid-area: 1 / 1 / 2 / 2;
  }

  .digitdisk-shot__text[open],
  .ouroboros-shot__text[open] { grid-area: 2 / 1 / 3 / 3; }
}

@media (max-width: 640px) {
  .digitdisk-shot__text pre,
  .ouroboros-shot__text pre { font-size: 11px; }
}

/* Три способа установки одним блоком — единственное, чего у соседнего раздела
   нет вовсе. Моноширинный, без подсветки и без прокрутки: три строки, которые
   копируют целиком. */
.ouroboros-install pre {
  background: var(--course-bg);
  border: 1px solid var(--course-line);
  border-radius: var(--course-radius);
  color: var(--course-text);
  font: 12px/1.5 var(--course-mono);
  margin: 6px 0 0;
  overflow-x: auto;
  padding: 6px 12px;
}

/* ОТСТУПЫ ЗДЕСЬ УЖАТЫ ПРОТИВ ОБЩИХ, И ЭТО НЕ ВКУС, А БЮДЖЕТ ПАНЕЛИ. С общими
   шаг «С чего начать» давал 516 px содержимого в боксе 495: панель заводила
   собственную прокрутку, и «назад / дальше» уезжали под нижний край — ровно
   там, где до конца не хватало двадцати одной точки. Прокрутка панели спекой
   разрешена, но платить ею за три отступа незачем. После правки содержимое шага
   не выходит за бокс ни в одной редакции: 495 из 495 при 1280×800. */
.ouroboros-install p { margin-top: 4px; }

.ouroboros-grid { margin-top: 8px; }

/* Planner lives on a separate origin. This surface is only its responsive
   launcher/status frame; it never receives or renders the person's data. */
.planner-surface {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  gap: 10px;
  min-height: 0;
  padding: 10px 0 0;
}

.planner-surface__heading,
.planner-surface__actions {
  align-items: center;
  display: flex;
  gap: 12px;
}

.planner-surface__heading {
  flex: 0 0 auto;
  justify-content: space-between;
  min-height: 42px;
}
.planner-surface__heading h1 {
  color: var(--course-text);
  font-size: clamp(18px, 1.8vw, 24px);
  line-height: 1.1;
  margin: 0;
}

.planner-surface__state {
  background: color-mix(in srgb, var(--course-green) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--course-green) 45%, var(--course-line));
  border-radius: 999px;
  color: var(--course-green);
  font: 600 12px/1 var(--course-mono);
  padding: 8px 10px;
  white-space: nowrap;
}

.planner-surface__state--pending {
  background: color-mix(in srgb, var(--course-orange) 10%, transparent);
  border-color: color-mix(in srgb, var(--course-orange) 40%, var(--course-line));
  color: var(--course-orange);
}

.planner-surface__actions { flex-wrap: wrap; margin-left: auto; }
.planner-surface__actions a,
.planner-surface__actions button {
  align-items: center;
  background: var(--course-surface);
  border: 1px solid var(--course-line-strong);
  border-radius: var(--course-radius);
  color: var(--course-text);
  display: inline-flex;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.2;
  gap: 7px;
  min-height: 36px;
  padding: 8px 11px;
  text-decoration: none;
}

.planner-surface__actions button:not(:disabled) { cursor: pointer; }
.planner-surface__actions--disabled button { cursor: not-allowed; opacity: .48; }

.planner-surface__frame {
  background: var(--course-surface);
  border: 1px solid var(--course-line);
  border-radius: var(--course-radius);
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}

.planner-surface__frame iframe {
  border: 0;
  display: block;
  height: 100%;
  min-height: 0;
  width: 100%;
}

/* Planner clean view only removes tool-local framing. The Courses header stays
   visible and remains the stable navigation/identity surface of the portal. */
html.planner-clean-view .source-links,
html.planner-clean-view .planner-surface__heading {
  display: none;
}

html.planner-clean-view .courses-body--tool .tool-page {
  height: calc(100dvh - var(--course-header-height));
}

html.planner-clean-view .planner-surface {
  gap: 0;
  padding: 0;
}

html.planner-clean-view .planner-surface__frame {
  border: 0;
  border-radius: 0;
}

.planner-surface__fallback {
  align-items: start;
  background: color-mix(in srgb, var(--course-orange) 6%, var(--course-surface));
  border: 1px solid var(--course-line);
  border-radius: var(--course-radius);
  display: grid;
  gap: 14px;
  grid-template-columns: auto minmax(0, 1fr);
  padding: clamp(18px, 3vw, 30px);
}

.planner-surface__fallback h3 { margin: 0 0 8px; }
.planner-surface__fallback p { color: var(--course-muted); margin: 0 0 12px; max-width: 70ch; }
.planner-surface__fallback-icon { color: var(--course-orange); }

@media (max-width: 620px) {
  .planner-surface { min-height: calc(100dvh - var(--course-header-height) - 25px); }
  .planner-surface__heading { align-items: stretch; flex-direction: column; }
  .planner-surface__actions { align-items: stretch; display: grid; }
  .planner-surface__actions a,
  .planner-surface__actions button { justify-content: center; width: 100%; }
  .planner-surface__fallback { grid-template-columns: 1fr; }
  .planner-surface__frame,
  .planner-surface__frame iframe { min-height: 560px; }
}

.tool-card::before {
  background: var(--tool-accent);
  content: "";
  height: 2px;
  inset: -1px -1px auto;
  opacity: 0.72;
  position: absolute;
}

.tool-card:hover,
.tool-card:focus-visible {
  border-color: var(--course-line-strong);
  color: var(--course-text);
  text-decoration: none;
  transform: translateY(-2px);
}

.tool-card__icon {
  align-items: center;
  align-self: start;
  background: color-mix(in srgb, var(--tool-accent) 11%, transparent);
  border: 1px solid color-mix(in srgb, var(--tool-accent) 38%, var(--course-line));
  border-radius: var(--course-radius);
  color: var(--tool-accent);
  display: inline-flex;
  grid-area: icon;
  height: 36px;
  justify-content: center;
  width: 36px;
}

.tool-card__eyebrow {
  color: var(--course-subtle);
  font-family: var(--course-mono);
  font-size: 10px;
  grid-area: eyebrow;
  letter-spacing: 0.08em;
  overflow: hidden;
  text-overflow: ellipsis;
  text-transform: uppercase;
  white-space: nowrap;
}

/* Имя переносится, а не обрезается. Здесь стоял -webkit-line-clamp: 2 — при
   колонке 215 px «S.C.O.R.E. · карта изменений» не помещалось в две строки и
   теряло хвост. Колонка теперь 230 px и шире, но дело не в этом: имя, не
   поместившееся в две строки, обязано перенестись, а не исчезнуть. */
.tool-card h3 {
  font-size: 16.5px;
  line-height: 1.25;
  grid-area: title;
  margin: 3px 0 0;
}

/* ПОДПИСЬ — ЦЕЛИКОМ, БЕЗ nowrap И БЕЗ МНОГОТОЧИЯ.
   Здесь стояло `white-space: nowrap; text-overflow: ellipsis` — одна строка на
   карточку. Замер до правки: на 1440×900 обрезаны ВСЕ десять подписей, на
   1280×800 — девять. Владелец: «caption у инструментов огрызается и не
   читается». Плата названа в docs/sdd/portal-tools-hub.md: страница выросла,
   и на 1280×800 она больше не в один экран. */
.tool-card p {
  color: var(--course-muted);
  font-size: 12.5px;
  grid-area: text;
  line-height: 1.45;
  margin: 4px 0 0;
}

/* Обещание про локальность остаётся В РАЗМЕТКЕ, но не на плитке: то же самое
   слово в слово стоит значком в шапке хаба и на самой странице инструмента, а
   в карточке оно повторяло бы его пять раз подряд. Из HTML не вынуто — его
   печатает бумага (@media print ниже) и читает поиск. */
.tool-card__privacy {
  align-items: center;
  color: var(--course-subtle);
  display: none;
  font-size: 11.5px;
  gap: 6px;
  grid-area: note;
}

.tool-card > strong {
  align-items: center;
  color: var(--tool-accent);
  display: inline-flex;
  font-size: 13px;
  gap: 7px;
  grid-area: open;
  margin-top: 8px;
}

/* Описание раздела в шапке — вводное, и нужно оно ровно до первого выбора:
   открывший контракт уже знает, куда попал, а 45 px описания стоят ему ряда
   каталога. Двух соседних правил — про открытый мастер и про открытую цель —
   здесь больше нет вместе с мастером. */
.tool-hub:has(#tools-contract:target) .tool-hero--hub p {
  display: none;
}

/* Каталог первым пунктом панели: он вход для того, кто не знает имён, и
   отделён от списка имён чертой, а не только порядком. */
.portal-nav__sub--catalogue {
  border-bottom: 1px solid var(--course-line);
  padding-bottom: 10px;
}

/* КОНТРАКТ — ТОЖЕ ЦЕЛЬ АДРЕСА, А НЕ ХВОСТ СТРАНИЦЫ.
   display здесь стоит none, а не grid, и это не описка. Пара правил
   «.tool-principles{display:none}» и «.tool-principles:target{display:grid}»
   стояла ВЫШЕ этого блока и молча им перебивалась: специфичность одна,
   побеждает последний. Контракт при этом печатался в КАЖДОМ состоянии страницы
   и добавлял 228 px (200 + отступ) к каждому замеру — в том числе к тем, ради
   которых блок и убирали. Раскрытие теперь стоит сразу за объявлением, и
   перебить его нечем. */
.tool-principles {
  align-items: start;
  border-top: 1px solid var(--course-line);
  display: none;
  gap: 36px;
  grid-template-columns: minmax(220px, .7fr) minmax(0, 1.3fr);
  margin-top: 20px;
  padding: 20px 0;
}

.tool-principles:target {
  display: grid;
}

.tool-principles h2 {
  color: var(--course-text);
  font-size: 20px;
  margin: 0;
}

.tool-principles ol {
  display: grid;
  gap: 8px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.tool-principles li {
  align-items: baseline;
  border-bottom: 1px solid var(--course-line);
  color: var(--course-muted);
  display: grid;
  font-size: 13.5px;
  gap: 13px;
  grid-template-columns: 30px 1fr;
  line-height: 1.5;
  padding: 9px 0 12px;
}

.tool-principles li strong {
  color: var(--course-cyan);
  font-family: var(--course-mono);
  font-size: 11px;
}

/* Интерактивные инструменты — будущие desktop-окна. Документ не движется:
   постоянные элементы занимают свои строки, прокрутка остаётся только внутри
   активного шага. Без JavaScript панели остаются обычным длинным документом. */
.js .courses-body--tool {
  height: 100dvh;
  overflow: hidden;
}

.courses-body--tool .tool-page {
  /* --course-header-strip: на телефоне шапка выше базовой на строку чисел
     активности (courses-v2.css, @media max-width: 619px). Без вычитания
     страница инструмента переставала помещаться в экран ровно на этот
     прирост: /digitdisk/ при 390×844 — 859 px документа вместо 844. */
  height: calc(100dvh - var(--course-header-height) - var(--course-header-strip) - 25px);
  min-height: 0;
  overflow: hidden;
  padding: 0;
}

.courses-body--tool .source-links {
  height: 25px;
  overflow: hidden;
}

.courses-body--tool .source-links__inner {
  align-items: center;
  flex-wrap: nowrap;
  height: 24px;
  padding-bottom: 0;
  padding-top: 0;
}

.courses-body--tool .source-links__inner a {
  font-size: 10px;
  white-space: nowrap;
}

.tool-page__shell {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}

/* ШАПКА СТРАНИЦЫ ИНСТРУМЕНТА — 69 px, А НЕ 97, И ВЫСОТУ ЕЙ ЗАДАЁТ ЗАГОЛОВОК.
   min-height: 86px здесь стояла не от вкуса: колонка с заголовком просит 66, а
   плашка «Локально и без аккаунта» — 76, и подпорка ровняла шапку по плашке.
   Плашки в шапке инструмента больше нет (довод — в tool-parts/appbar.html),
   вместе с ней ушли хлебные крошки, и подпирать стало нечего: 48 px заголовка
   с описанием плюс 10 + 10 отступа плюс черта = 69. Замер 1280×800: 97 → 69.

   flex-wrap нужен рельсу шагов, который теперь стоит справа (см. ниже): на
   узком экране он обязан переноситься под заголовок, а не сжимать его до
   переносов посреди слова. */
.tool-appbar {
  align-items: center;
  border-bottom: 1px solid var(--course-line);
  column-gap: 24px;
  display: flex;
  flex: 0 0 auto;
  flex-wrap: wrap;
  justify-content: space-between;
  padding: 10px 0;
  row-gap: 6px;
}

.tool-appbar > div { min-width: 0; }

.tool-appbar h1 {
  color: var(--course-text);
  font-size: 22px;
  line-height: 1.15;
  margin: 3px 0 0;
}

.tool-appbar p {
  color: var(--course-muted);
  font-size: 12px;
  line-height: 1.4;
  margin: 3px 0 0;
}

/* РЕЛЬС ШАГОВ В ШАПКЕ. Своей строки он здесь не занимает: у шапки справа
   ровно то место, которое освободила плашка, а рельс из четырёх переключателей
   занимает меньше половины ширины страницы. Черта и отступы снимаются — черту
   уже несёт сама шапка, а отступы удваивали бы её собственные.

   flex: 0 1 auto вместо 1 0 auto на переключателе: в отдельной строке рельс
   растягивал четыре кнопки на всю ширину, в шапке растягивать нечего — он
   стоит по правому краю и занимает ровно свою ширину. */
.tool-appbar .tool-wizard__steps {
  border-bottom: 0;
  flex: 0 1 auto;
  max-width: 100%;
  padding: 0;
}

.tool-appbar .tool-wizard__steps a { flex: 0 1 auto; }

.tool-wizard {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  min-height: 0;
  padding: 0;
}

.tool-wizard__steps {
  border-bottom: 1px solid var(--course-line);
  display: flex;
  flex: 0 0 auto;
  gap: 4px;
  overflow-x: auto;
  padding: 8px 0;
  scrollbar-width: thin;
}

.tool-wizard__steps a {
  align-items: center;
  border: 1px solid transparent;
  border-radius: var(--course-radius);
  color: var(--course-muted);
  display: inline-flex;
  flex: 1 0 auto;
  font-size: 12px;
  gap: 8px;
  justify-content: center;
  min-height: 38px;
  padding: 0 12px;
  text-decoration: none;
  white-space: nowrap;
}

.tool-wizard__steps a b {
  color: var(--course-subtle);
  font-family: var(--course-mono);
  font-size: 10px;
}

.tool-wizard__steps a.is-active,
.tool-wizard__steps a[aria-selected="true"] {
  background: var(--course-control-active);
  border-color: var(--course-cyan);
  color: var(--course-text);
}

.tool-wizard__steps a.is-active b,
.tool-wizard__steps a[aria-selected="true"] b { color: var(--course-cyan); }

.tool-wizard__panels,
.tool-wizard__panels > [data-resume-workbench] {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  min-height: 0;
}

.tool-wizard__panel {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 18px 2px 14px;
}

.tool-wizard__panel[hidden] { display: none !important; }

.tool-wizard__panel > .tool-section-heading {
  margin-bottom: 14px;
}

.tool-wizard__actions {
  align-items: center;
  display: flex;
  gap: 10px;
  justify-content: space-between;
  margin-top: 14px;
}

/* Шаги квадрата Декарта — колонка, у которой подвижна ровно одна часть.

   ЧТО БЫЛО. Замер 2 сентября 2026 (headless chromium, обе редакции):

     шаг                 экран      панель просила / показано
     «Картина целиком»   1280×800   665 / 495  — заполненный черновик
     «Картина целиком»   1280×800   544 / 495  — пустой
     «Картина целиком»   1024×768   737 / 456
     «Если сделать»       390×844   729 / 562
     «Если не сделать»    390×844   749 / 562

   На «Картине целиком» под эту прокрутку уходили ВСЕ ТРИ ДЕЙСТВИЯ инструмента
   («копировать выжимку», «скачать», «сбросить»), строка состояния и «назад» —
   на 1280×800 с заполненным черновиком в экране не было ни одной кнопки из
   трёх. Отказ был тихим: документ показывал 800/800 = 1,00, потому что
   страницу инструмента держит замок `.js .courses-body--tool { overflow:
   hidden }`. Переполнение за ним не рисует ни полосы, ни обрыва — оно просто
   исчезает вместе с кнопками.

   ЧТО СТАЛО. Прокрутка отдана содержимому шага — списку ответов и паре
   вопросов, — и только ему. Это честный случай: длину задаёт читатель (его же
   текст, любой длины), а высоту — окно. Постоянные части шага закреплены и
   видны всегда: заголовок, счётчик с подсказкой, три кнопки, строка состояния,
   «назад» и «дальше».

   ШАГА «РЕШЕНИЕ» В СПИСКЕ НЕТ намеренно: он помещается на всех пяти замеренных
   экранах (1.00 везде, включая 390×844) — там одно поле на одну фразу, и
   закреплять в нём нечего.

   Правила именные (`[data-tool-panel="…"]` под классом страницы), потому что
   шаг с именем `result` есть и у STAR, и у S.C.O.R.E., а внутри у них не
   список карточек, а поля ввода — им прокрутка панели по-прежнему верна. */
.tool-page--cartesian .tool-wizard__panel[data-tool-panel="happens"],
.tool-page--cartesian .tool-wizard__panel[data-tool-panel="not"],
.tool-page--cartesian .tool-wizard__panel[data-tool-panel="result"] {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.tool-page--cartesian .tool-wizard__panel > .tool-section-heading,
.tool-page--cartesian .tool-wizard__panel > .tool-result-bar,
.tool-page--cartesian .tool-wizard__panel > .tool-wizard__actions {
  flex: 0 0 auto;
}

/* КАРТИНА ЦЕЛИКОМ — КВАДРАТ 2×2, А НЕ ЛЕНТА В ОКОШКЕ.

   ЧТО БЫЛО ПОСЛЕ ПРЕДЫДУЩЕЙ ПРАВКИ (2ed9714c). Кнопки она вернула в экран
   честно, а содержимое — нет: прокрутку просто перенесли на шаг ниже, в сам
   список ответов. Замер 3 сентября 2026, 1280×800, черновик по три строки на
   сторону: списку нужно 498 px, показано 332 — из четырёх сторон видно две, и
   вторая пара обрезана посреди карточки. Полосы прокрутки на снимке нет:
   `.cartesian-review { overflow-y: auto }` рисует её только при наведении, а
   страницу держит замок `.js .courses-body--tool { overflow: hidden }`.

   Число 1,00 предыдущая правка получила потому, что мерила panel.scrollHeight
   при panel.clientHeight — а панель к тому моменту уже ничего не переполняла:
   переполнял вложенный бокс. Ровно та же ложь этажом ниже, что и с документом.

   ЧТО СТАЛО. Решение — строка над квадратом (оно подпись к сторонам, а не
   пятая сторона), четыре стороны — сетка 2×2, как и зовётся сам инструмент.
   Текст стороны сокращается ВИДИМО, многоточием, а не обрезается молча: это
   тот же приём, которым на портале уже решена та же задача у карты S.C.O.R.E.
   (.score-review__item p ниже по файлу). Полный текст никуда не делся — он на
   шагах 02 и 03, где его писали, и в «скопировать выжимку» и «скачать».

   Сколько строк показано — зависит от экрана, потому что от экрана зависит
   место: 6 строк на 800, 8 на 900, 10 на 1024. Числа не на глаз — это остаток
   высоты шага, поделённый на два ряда карточек: на 800 после заголовка, строки
   решения, счётчика и «назад» под квадрат остаётся 352 px, ряд при шести
   строках — 158. Черновик в три фразы на сторону не сокращается ни на одном из
   трёх экранов.

   align-content: start — иначе строки грида растянутся на всю высоту бокса, и
   карточки станут ширмами вместо квадрата. */
.cartesian-review {
  --cartesian-clamp: 6;
  align-content: start;
  display: grid;
  flex: 1 1 auto;
  gap: 8px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  min-height: 0;
  overflow: hidden;
}

@media (min-height: 880px) { .cartesian-review { --cartesian-clamp: 8; } }
@media (min-height: 1000px) { .cartesian-review { --cartesian-clamp: 10; } }

.cartesian-review article {
  background: var(--course-panel);
  border: 1px solid var(--course-line);
  border-radius: var(--course-radius);
  min-height: 0;
  padding: 11px 13px;
}

/* Решение: подпись и текст в одну строку, через всю ширину квадрата. */
.cartesian-review__decision {
  align-items: baseline;
  column-gap: 10px;
  display: flex;
  flex-wrap: wrap;
  grid-column: 1 / -1;
  row-gap: 2px;
}

.cartesian-review span { color: var(--course-subtle); font-size: 11px; }
.cartesian-review strong { color: var(--course-text); display: block; margin-top: 4px; }
.cartesian-review__decision strong { margin-top: 0; }

.cartesian-review p {
  -webkit-box-orient: vertical;
  -webkit-line-clamp: var(--cartesian-clamp);
  color: var(--course-muted);
  display: -webkit-box;
  font-size: 13px;
  line-height: 1.45;
  margin: 5px 0 0;
  overflow: hidden;
  white-space: pre-line;
}

/* ЗДЕСЬ СТОЯЛА `.tool-workspace { padding: 28px 0 36px }` — 64 px воздуха,
   которые уже пытались убрать и не смогли. `.tool-wizard { padding: 0 }` (выше
   по файлу) означала ровно это, но эта строка объявлена НИЖЕ при той же
   специфичности и молча выигрывала: у страниц инструментов, запертых в один
   экран, 28 px сверху и 36 снизу уходили в пустоту между чертой шапки и
   рельсом. Замер 1280×800 (headless chromium, дерево на 0da9f59c): рабочая
   область 614 px, из них 64 — этот отступ, панель шага 495.

   Правило снято целиком, а не переписано: класс .tool-workspace нигде не
   стоит без .tool-wizard (`grep -c 'class="tool-workspace' layouts/` — 7, и все
   семь с обоими), то есть отступ ему задавала соседняя строка и без него. */
.tool-workspace__intro {
  align-items: end;
  display: grid;
  gap: 28px;
  grid-template-columns: minmax(220px, .7fr) minmax(0, 1.3fr);
  margin-bottom: 18px;
}

.tool-field {
  color: var(--course-muted);
  display: grid;
  font-size: 12.5px;
  gap: 7px;
}

.tool-field input,
.tool-field textarea,
.tool-field select,
.cartesian-quadrant textarea,
.star-card textarea,
.score-card textarea,
.resume-tool__section input,
.resume-tool__section textarea {
  background: var(--course-control);
  border: 1px solid var(--course-line);
  border-radius: 6px;
  color: var(--course-text);
  font: 13.5px/1.55 var(--course-font);
  outline: none;
  padding: 10px 11px;
  resize: vertical;
  width: 100%;
}

.tool-field input:focus,
.tool-field textarea:focus,
.tool-field select:focus,
.cartesian-quadrant textarea:focus,
.star-card textarea:focus,
.score-card textarea:focus,
.resume-tool__section input:focus,
.resume-tool__section textarea:focus {
  border-color: var(--course-cyan);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--course-cyan) 18%, transparent);
}

.cartesian-square {
  /* align-content: start держит рабочий стол прежним: единственная строка
     квадранта остаётся ростом по содержимому (267 px), а не растягивается на
     всю панель. Прокрутка появляется только там, где пара вопросов и правда
     не помещается, — на телефоне, где квадрат становится одной колонкой. */
  align-content: start;
  display: grid;
  flex: 1 1 auto;
  gap: 1px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  min-height: 0;
  overflow: hidden auto;
  scrollbar-width: thin;
}

.cartesian-quadrant {
  background: var(--course-panel);
  border: 1px solid var(--course-line);
  display: flex;
  flex-direction: column;
  min-height: 250px;
  padding: 18px;
}

.cartesian-quadrant > span,
.star-card > span,
.score-card > span,
.score-review__item > span {
  align-items: center;
  color: var(--course-text);
  display: flex;
  font-size: 15px;
  font-weight: 650;
  gap: 9px;
}

.cartesian-quadrant b,
.star-card b,
.score-card b,
.score-review__item b {
  align-items: center;
  border: 1px solid currentColor;
  border-radius: 999px;
  color: var(--course-cyan);
  display: inline-flex;
  flex: 0 0 auto;
  font-family: var(--course-mono);
  font-size: 10px;
  height: 25px;
  justify-content: center;
  width: 25px;
}

.cartesian-quadrant small,
.star-card small,
.score-card small {
  color: var(--course-subtle);
  font-size: 11.5px;
  line-height: 1.4;
  margin: 7px 0 12px 34px;
}

.cartesian-quadrant textarea,
.star-card textarea,
.score-card textarea {
  flex: 1;
}

.cartesian-quadrant--status b,
.cartesian-quadrant--loss b { color: var(--course-purple); }

.cartesian-quadrant--cost b { color: var(--course-orange); }

.tool-result-bar {
  align-items: center;
  background: var(--course-panel);
  border: 1px solid var(--course-line);
  display: flex;
  flex-wrap: wrap;
  gap: 14px 24px;
  justify-content: space-between;
  margin-top: 14px;
  padding: 14px 16px;
}

.tool-result-bar > div:first-child {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  gap: 7px 12px;
}

.tool-result-bar span {
  color: var(--course-subtle);
  font-size: 11.5px;
}

.tool-result-bar strong {
  color: var(--course-text);
  font-family: var(--course-mono);
}

.tool-result-bar p {
  color: var(--course-muted);
  flex-basis: 100%;
  font-size: 12.5px;
  margin: 0;
}

.tool-actions,
.resume-import__actions {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
}

.tool-button,
.tool-file-button {
  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: 600 12.5px/1 var(--course-font);
  gap: 7px;
  min-height: 38px;
  padding: 0 13px;
}

.tool-button:hover,
.tool-file-button:hover {
  background: var(--course-control-active);
  border-color: var(--course-line-strong);
}

.tool-button--primary {
  background: var(--course-cyan);
  border-color: var(--course-cyan);
  color: var(--course-on-accent);
}

.tool-button--primary:hover {
  background: var(--course-cyan);
  opacity: .87;
}

.tool-button--ghost {
  background: transparent;
  color: var(--course-subtle);
}

.tool-status[data-state="success"] { color: var(--course-green); }
.tool-status[data-state="error"] { color: var(--course-red); }

.star-input,
.star-diagnostics,
.star-output,
.resume-import,
.resume-workbench {
  border-bottom: 1px solid var(--course-line);
  padding: 0 0 28px;
}

.star-input__footer,
.resume-import__actions {
  align-items: center;
  display: flex;
  gap: 20px;
  justify-content: space-between;
  margin-top: 12px;
}

.star-input__footer p {
  color: var(--course-subtle);
  font-size: 12px;
  margin: 0;
  max-width: 720px;
}

.star-diagnostics,
.star-output,
.resume-workbench {
  padding-top: 26px;
}

.star-diagnostics__terms {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 14px;
}

.star-diagnostics mark {
  background: color-mix(in srgb, var(--course-orange) 12%, var(--course-control));
  border: 1px solid color-mix(in srgb, var(--course-orange) 46%, var(--course-line));
  border-radius: 999px;
  color: var(--course-orange);
  font-size: 12px;
  padding: 4px 9px;
}

.tool-empty {
  color: var(--course-subtle);
  font-size: 12px;
  line-height: 1.45;
}

/* ШАГ С ГРАНЯМИ — КОЛОНКА, У КОТОРОЙ ПОДВИЖНА ОДНА ЧАСТЬ.

   Правило общее и написано один раз через :has(), а не по имени каждого
   инструмента: приём один и тот же везде, где шаг делится на грани, и копия на
   инструмент разошлась бы молча. :has() в этом репозитории санкционирован и уже
   применён (body:has(> main.tool-hub) выше по файлу).

   Закреплено всё постоянное — заголовок, рельс граней, счётчик с кнопками и
   «назад»; растягивается только активная грань. Это ровно то, чего не хватало
   итогу STAR: кнопки уезжали вниз потому, что их выталкивал второй ряд
   карточек. */
.tool-wizard__panel[data-tool-facets],
.tool-wizard__panel:has(> [data-tool-facets]),
.js .tool-wizard__panel:has(> [data-one-screen-scroll]) {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.tool-wizard__panel[data-tool-facets] > *,
.tool-wizard__panel:has(> [data-tool-facets]) > *,
.js .tool-wizard__panel:has(> [data-one-screen-scroll]) > * {
  flex: 0 0 auto;
}

.tool-wizard__panel[data-tool-facets] > .tool-facets__panels,
.tool-wizard__panel:has(> [data-tool-facets]) > .tool-facets__panels,
.tool-wizard__panel:has(> [data-tool-facets]) > [data-tool-facets],
.js .tool-wizard__panel > [data-one-screen-scroll] {
  flex: 1 1 auto;
  min-height: 0;
}

/* ОБЪЯВЛЕННАЯ ПРОКРУТКА — ОДНО ПРАВИЛО НА ВЕСЬ ПОРТАЛ.

   Бокс с data-one-screen-scroll — это окно в документ, длину которого задаёт не
   вёрстка, а данные: предпросмотр резюме, список разделов резюме, ходы для
   сторон верхней полосы, набор из всех утверждений. Такому боксу «уложиться в
   экран» нельзя иначе как спрятав часть данных, и честный ход — показать окно
   с видимой полосой, закрепив вокруг него всё остальное.

   Слово «объявленная» здесь не украшение: сторож
   features/browser/tool-one-screen.feature считает отказом ЛЮБУЮ прокрутку
   внутри шага, кроме помеченной этим атрибутом и кроме полей ввода. Так
   переполнение нельзя спрятать, переложив его в соседний бокс, — а именно так
   квадрат Декарта и получил своё 1,00 при обрезанной половине содержимого. */
.js [data-one-screen-scroll] {
  min-height: 0;
  overflow-y: auto;
  scrollbar-width: thin;
}

/* Поле внутри грани дышит вместе с ней: без min-height: 0 textarea упирается в
   свой rows и выпирает из шага вместо того, чтобы ужаться. */
.tool-facets__panels [data-tool-facet-panel] textarea {
  flex: 1 1 auto;
  min-height: 0;
}

/* ГРАНИ ШАГА — вкладки внутри одной панели мастера
   (partials/tool-parts/facets.html). Рельс набран теми же правилами, что рельс
   шагов: это тот же приём этажом ниже, и выглядеть он обязан так же, иначе
   читатель принимает вторую полосу переключателей за что-то другое. */
.tool-facets {
  display: flex;
  flex: 0 0 auto;
  gap: 4px;
  margin-top: 14px;
  overflow-x: auto;
  scrollbar-width: thin;
}

.tool-facets a {
  align-items: center;
  border: 1px solid var(--course-line);
  border-radius: var(--course-radius);
  color: var(--course-muted);
  display: inline-flex;
  flex: 0 1 auto;
  font-size: 12px;
  gap: 8px;
  justify-content: center;
  min-height: 34px;
  padding: 0 13px;
  text-decoration: none;
  white-space: nowrap;
}

.tool-facets a b {
  color: var(--course-subtle);
  font-family: var(--course-mono);
  font-size: 10px;
}

.tool-facets a.is-active,
.tool-facets a[aria-selected="true"] {
  background: var(--course-control-active);
  border-color: var(--course-cyan);
  color: var(--course-text);
}

.tool-facets a.is-active b,
.tool-facets a[aria-selected="true"] b { color: var(--course-cyan); }

/* Чего не хватает — видно с рельса, не открывая карточек по очереди: тот же
   оранжевый, что и на самой карточке (.star-card.is-missing ниже). */
.tool-facets a.is-missing {
  border-color: color-mix(in srgb, var(--course-orange) 52%, var(--course-line));
}

.tool-facets a.is-missing b { color: var(--course-orange); }

/* Пачка панелей забирает остаток высоты шага, активная разворачивается на неё
   целиком — ради этого грани и заводились. */
.tool-facets__panels {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  margin-top: 10px;
  min-height: 0;
}

.tool-facets__panels > [data-tool-facet-panel] {
  flex: 1 1 auto;
  min-height: 0;
}

/* Со скриптом грань — колонка: иначе то, что внутри неё объявлено
   прокручиваемым, забирать высоту не у чего, и оно вырастает во весь рост.
   Без скрипта правила нет: там грани стоят подряд обычными блоками. */
.js .tool-facets__panels > [data-tool-facet-panel] {
  display: flex;
  flex-direction: column;
}

/* Внутри грани подвижна РОВНО ОДНА часть — объявленное окно прокрутки; всё
   остальное закреплено. Правило нарочно узкое (только грани, где такое окно
   стоит прямым ребёнком): общее `> * { min-height: 0 }` тут уже стояло и сжало
   две сноски про ω до 6 px каждая — текст из них вылезал, а грань показывала
   342 px в боксе 331. Пример того, что «подвинуть всё» и «подвинуть нужное» —
   разные правила. */
.js .tool-facets__panels > [data-tool-facet-panel]:has(> [data-one-screen-scroll]) > * {
  flex: 0 0 auto;
}

.js .tool-facets__panels > [data-tool-facet-panel]:has(> [data-one-screen-scroll]) > [data-one-screen-scroll] {
  flex: 1 1 auto;
  min-height: 0;
}

.tool-facets__panels > [data-tool-facet-panel][hidden] { display: none !important; }

/* БЕЗ JAVASCRIPT грани остаются обычной лентой: скрипт не пришёл, hidden никто
   не навесил, и все четыре карточки просто стоят подряд — шаг длинный, но
   целый. Ради этого случая здесь и стоит зазор. */
.tool-facets__panels > [data-tool-facet-panel] + [data-tool-facet-panel] {
  margin-top: 10px;
}

/* Итог STAR — колонка: рельс граней и карточка, которая забирает остаток. */
.star-facets {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  min-height: 0;
}

.star-facets .star-card {
  /* Сетка держала карточку в 220 px; в одиночку она растёт по шагу, и разбор
     правится в поле нормального роста, а не в окошке на пять строк. */
  min-height: 0;
}

.star-grid {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin-top: 16px;
}

.star-card {
  background: var(--course-panel);
  border: 1px solid var(--course-line);
  display: flex;
  flex-direction: column;
  min-height: 220px;
  padding: 16px;
}

.star-card--t b { color: var(--course-blue); }
.star-card--a b { color: var(--course-purple); }
.star-card--r b { color: var(--course-green); }

.star-card.is-missing {
  border-color: color-mix(in srgb, var(--course-orange) 52%, var(--course-line));
}

.star-card.is-missing textarea {
  color: var(--course-orange);
}

.score-panel {
  border-bottom: 1px solid var(--course-line);
}

.score-fields {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.score-card {
  background: var(--course-panel);
  border: 1px solid var(--course-line);
  display: flex;
  flex-direction: column;
  min-height: 245px;
  padding: 16px;
}

.score-card--c b { color: var(--course-orange); }
.score-card--o b { color: var(--course-blue); }
.score-card--r b { color: var(--course-purple); }
.score-card--e b { color: var(--course-green); }

.score-caution {
  align-items: baseline;
  background: color-mix(in srgb, var(--course-orange) 7%, var(--course-panel));
  border-left: 2px solid var(--course-orange);
  color: var(--course-muted);
  display: flex;
  font-size: 12px;
  gap: 8px;
  margin-top: 10px;
  padding: 9px 12px;
}

.score-caution strong {
  color: var(--course-orange);
  white-space: nowrap;
}

.score-resources {
  min-height: 135px;
}

.score-review {
  display: grid;
  gap: 7px;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  margin-top: 10px;
}

.score-review__item {
  background: var(--course-panel);
  border: 1px solid var(--course-line);
  min-width: 0;
  padding: 10px;
}

.score-review__item > span {
  font-size: 11.5px;
  gap: 6px;
}

.score-review__item b {
  height: 21px;
  width: 21px;
}

.score-review__item--c b { color: var(--course-orange); }
.score-review__item--o b { color: var(--course-blue); }
.score-review__item--r b { color: var(--course-purple); }
.score-review__item--e b { color: var(--course-green); }

.score-review__item p {
  color: var(--course-muted);
  display: -webkit-box;
  font-size: 11px;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  line-height: 1.4;
  margin: 8px 0 0;
  overflow: hidden;
  white-space: pre-line;
}

.score-panel--bridge .tool-result-bar {
  margin-top: 10px;
}

/* Подсказка рядом со счётчиком. Правило было привязано к панели S.C.O.R.E.
   (`.score-panel--bridge …`), хотя печатает её общий партиал
   tool-parts/actions.html: второй инструмент со слотом insight — квадрат
   Декарта — получил бы её браузерным умолчанием, то есть другим кеглем и
   другим цветом. Поэтому селектор общий, как и разметка. */
.tool-result-bar > div:first-child small {
  color: var(--course-muted);
  font-size: 11.5px;
}

.tool-file-button {
  flex: 0 0 auto;
  position: relative;
}

.tool-file-button input {
  height: 1px;
  opacity: 0;
  overflow: hidden;
  position: absolute;
  width: 1px;
}

.resume-import__actions {
  justify-content: flex-start;
}

.resume-import__actions .tool-status {
  color: var(--course-muted);
  font-size: 12px;
  margin: 0 0 0 4px;
}

.resume-tool__grid {
  display: grid;
  gap: 18px;
  grid-template-columns: minmax(270px, 340px) minmax(0, 1fr);
  margin-top: 18px;
}

.resume-tool__controls {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* Оба шага конструктора резюме — колонки с закреплёнными краями: заголовок
   сверху, «назад»/«дальше» снизу, между ними ровно одна подвижная часть.
   Без этого правила .tool-wizard__panel { overflow: auto } прокручивал бы весь
   шаг целиком вместе с кнопками — то самое, из-за чего «назад» и «показать
   резюме» стояли на 1331 px при окне 800. */
.js .tool-wizard__panel.resume-workbench {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.js .tool-wizard__panel.resume-workbench > .tool-section-heading,
.js .tool-wizard__panel.resume-workbench > .tool-wizard__actions {
  flex: 0 0 auto;
}

/* Набор полей резюме показан вкладками (partials/tool-resume.html), поэтому
   трёх колонок рядом больше нет: колонка теперь ровно одна и занимает всю
   ширину шага. Правило оставлено для случая БЕЗ JavaScript — там вкладки не
   поднимаются, наборы стоят подряд, и три колонки рядом читаются лучше ленты. */
.js .resume-workbench .resume-tool__controls {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  min-height: 0;
}

/* Тело набора забирает остаток шага и прокручивается внутри себя — прокрутка
   объявлена в разметке (data-one-screen-scroll), см. довод там же. */
/* Ящик, внутри которого стоит объявленная прокрутка, сам обязан быть колонкой
   с нулевым минимумом — иначе прокручиваемому боксу нечего забирать и он
   вырастает во весь рост, вытесняя кнопки из экрана. */
.js .tool-facets__panels > [data-tool-facet-panel]:has(> [data-one-screen-scroll]),
.js .strengths-next__block:has(> [data-one-screen-scroll]),
.js [data-tool-facet-panel] > .strengths-next__block:has(> [data-one-screen-scroll]) {
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}

.js .tool-facets__panels > [data-tool-facet-panel] > [data-one-screen-scroll],
.js .strengths-next__block > [data-one-screen-scroll],
.js [data-tool-facet-panel] > .strengths-next__block {
  flex: 1 1 auto;
}

.js .resume-workbench .tool-facets__panels > .tool-panel {
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}

.resume-workbench .resume-tool__controls {
  display: grid;
  grid-template-columns: minmax(220px, .75fr) minmax(320px, 1.5fr) minmax(220px, .75fr);
}

.resume-tool__identity,
.resume-tool__filters {
  display: grid;
  gap: 9px;
}

/* Итог резюме — колонка: кнопки выгрузки и «назад» закреплены, прокручивается
   сам документ. max-height: none снят вместе с прежней раскладкой: высоту здесь
   задаёт остаток шага, а не число в пикселях. */
.js .resume-workbench .resume-tool__output {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  min-height: 0;
}

.resume-workbench .resume-tool__preview {
  max-height: none;
}

.tool-panel {
  background: var(--course-panel);
  border: 1px solid var(--course-line);
  border-radius: var(--course-radius);
  margin: 0;
  padding: 13px 14px 15px;
}

.tool-panel legend {
  color: var(--course-subtle);
  font-family: var(--course-mono);
  font-size: 10.5px;
  letter-spacing: .07em;
  padding: 0 4px;
  text-transform: uppercase;
}

.tool-panel .tool-field + .tool-field,
.tool-check {
  margin-top: 10px;
}

.tool-check {
  align-items: center;
  color: var(--course-muted);
  cursor: pointer;
  display: flex;
  font-size: 12.5px;
  gap: 8px;
}

.tool-check input,
.resume-tool__section input[type="checkbox"] {
  accent-color: var(--course-cyan);
}

.resume-tool__sections {
  display: grid;
  gap: 9px;
}

.resume-tool__section {
  border-bottom: 1px solid var(--course-line);
  display: grid;
  gap: 7px;
  padding-bottom: 10px;
}

.resume-tool__section:last-child {
  border-bottom: 0;
  padding-bottom: 0;
}

.resume-tool__section > div {
  align-items: center;
  display: grid;
  gap: 7px;
  grid-template-columns: auto 1fr;
}

.resume-tool__section input[type="text"] {
  padding: 6px 8px;
}

.resume-tool__section textarea {
  font-size: 12px;
  padding: 7px 8px;
}

.resume-tool__techs {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 12px;
}

.resume-tool__techs button {
  background: var(--course-control);
  border: 1px solid var(--course-line);
  border-radius: 999px;
  color: var(--course-muted);
  cursor: pointer;
  font: 11.5px var(--course-font);
  padding: 4px 9px;
}

.resume-tool__techs button[aria-pressed="true"] {
  background: var(--tool-cyan-soft);
  border-color: var(--course-cyan);
  color: var(--course-cyan);
}

.resume-tool__output {
  min-width: 0;
}

.resume-tool__bar {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: space-between;
}

.resume-tool__bar > p,
.resume-tool__save-note {
  color: var(--course-subtle);
  font-size: 11.5px;
  margin: 0;
}

.resume-tool__save-note {
  margin-top: 8px;
}

.resume-tool__preview {
  background: #fff;
  border: 1px solid var(--course-line);
  border-radius: var(--course-radius);
  margin-top: 12px;
  max-height: 780px;
  overflow: auto;
  padding: 30px 34px;
}

.resume-tool-doc {
  color: #16202a;
  font: 13.5px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

.resume-tool-doc h1 {
  color: #16202a;
  font-size: 25px;
  margin: 0;
}

.resume-tool-doc__role {
  color: #48606f;
  font-size: 15px;
  margin: 3px 0;
}

.resume-tool-doc__contacts,
.resume-tool-doc footer {
  color: #667b87;
  font-size: 12px;
}

.resume-tool-doc h2 {
  border-bottom: 1px solid #d6dee4;
  color: #35566d;
  font-size: 14px;
  letter-spacing: .06em;
  margin: 21px 0 9px;
  padding-bottom: 5px;
  text-transform: uppercase;
}

.resume-tool-doc p {
  margin: 0 0 9px;
}

.resume-tool-doc ul {
  margin: 5px 0 9px;
  padding-left: 20px;
}

.resume-tool-doc footer {
  border-top: 1px solid #d6dee4;
  margin-top: 28px;
  padding-top: 8px;
}

.tool-more {
  align-items: center;
  border-top: 1px solid var(--course-line);
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
  padding: 20px 0 6px;
}

.tool-more > span {
  color: var(--course-subtle);
  font-size: 11px;
  letter-spacing: .06em;
  text-transform: uppercase;
}

.tool-more a {
  align-items: center;
  color: var(--course-muted);
  display: inline-flex;
  font-size: 12.5px;
  gap: 5px;
  text-decoration: none;
}

.tool-more a:hover { color: var(--course-cyan); }

@media (max-width: 1120px) {
  .score-review { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Здесь у .tool-card стояло min-height: 230px — с тех пор, когда карточка была
   вертикальным столбиком и без подпорки выглядела рваной. Теперь она лежит
   набок, высоту задаёт её содержимое, и подпорка стала пустотой: замер 390×844
   до снятия — карточки по 230 px при содержимом на 150. */
@media (max-width: 900px) {
  .tool-principles,
  .tool-workspace__intro,
  .resume-tool__grid { grid-template-columns: 1fr; }
  .tool-workspace__intro { align-items: start; }
  .resume-tool__preview { max-height: 560px; }
  .resume-workbench .resume-tool__controls { grid-template-columns: 1fr; }
}

@media (max-width: 640px) {
  .tool-hero {
    align-items: stretch;
    flex-direction: column;
    gap: 18px;
    padding-top: 22px;
  }
  /* Значок «локально» в шапке хаба стоит 94 px из 844 и повторяет первую же
     строку врезки, которая на телефоне видна тут же. На страницах самих
     инструментов он остаётся: там врезки нет. Тот же приём, что этажом выше у
     .tool-appbar. */
  .tool-hero--hub { gap: 0; padding: 14px 0 12px; }
  .tool-hero--hub .tool-local-badge { display: none; }
  .tool-local-badge { max-width: none; }
  .cartesian-square,
  .star-grid,
  .score-fields,
  .score-review { grid-template-columns: 1fr; }
  /* Каталог на телефоне — две колонки плиток, а не десять рядов подряд. Решение
     проверено заново после того, как подпись стала печататься целиком: замер
     390×844 на собранной странице — одна колонка 2 134 px, две колонки с
     иконкой 2 053, две без иконки 1 791 (2,12 экрана). Подпись при этом нигде
     не обрезана: в колонке 174 px она просто занимает четыре-пять строк. */
  .tool-catalog__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* Иконка уступает место имени. В колонке 174 px она забирала 47 из 137, и
     имени оставалось 90: «Архитектор» ломался посреди слова, «Конструктор
     резюме» тоже. Опознавательный цвет при этом не теряется — его несёт
     полоска .tool-card::before поверх плитки. */
  .tool-card {
    column-gap: 0;
    grid-template-areas:
      "eyebrow"
      "title"
      "text"
      "note"
      "open";
    grid-template-columns: minmax(0, 1fr);
    padding: 10px 11px;
  }
  .tool-card__icon { display: none; }
  .cartesian-quadrant,
  .star-card,
  .score-card { min-height: 210px; }
  .score-caution { align-items: flex-start; flex-direction: column; }
  .star-input__footer { align-items: stretch; flex-direction: column; }
  .tool-result-bar { align-items: stretch; flex-direction: column; }
  .tool-actions { width: 100%; }
  .tool-actions .tool-button { flex: 1 1 auto; justify-content: center; }
  .resume-tool__preview { padding: 21px 18px; }
  .tool-appbar p { display: none; }
  .tool-wizard__steps a { flex: 0 0 auto; }
  .tool-wizard__steps a span { max-width: 120px; overflow: hidden; text-overflow: ellipsis; }
  .cartesian-review { grid-template-columns: 1fr; }
  .cartesian-review article:first-child { grid-column: auto; }
}

/* НА БУМАГЕ ХАБ — ОБЫЧНЫЙ ДОКУМЕНТ.
   Цели адреса на печати не работают: у листа нет ни адреса, ни :target, и
   контракт ушёл бы в никуда — а на экране его и прячут ради экрана, которого у
   бумаги нет. Так же поступает portal-tabs.css с хабами на JavaScript:
   «печатается длинный документ, то есть ровно та портянка, от которой уходим на
   экране» (docs/sdd/portal-one-viewport.md, «Цена одного экрана»).
   Список правил стал короче ровно на мастер подбора: печатать больше нечего.
   Обещание про локальность на бумаге печатается — там оно не повтор значка в
   шапке, а единственное место, где сказано. */
@media print {
  .tool-hub .tool-principles { display: grid !important; }
  .tool-hub .tool-hero--hub p { display: block !important; }
  .tool-hub .tool-card__privacy { display: inline-flex !important; }
}

@media print {
  .tool-page .portal-header,
  .tool-page .tool-hero,
  .tool-page .resume-import,
  .tool-page .resume-tool__controls,
  .tool-page .resume-tool__bar,
  .tool-page .resume-tool__save-note,
  .tool-page .tool-more,
  .tool-page .site-footer,
  .tool-page .source-links { display: none !important; }
  .tool-page,
  .tool-page .portal-main,
  .tool-page .resume-workbench,
  .tool-page .resume-tool__grid,
  .tool-page .resume-tool__output,
  .tool-page .resume-tool__preview {
    background: #fff;
    border: 0;
    display: block;
    margin: 0;
    max-height: none;
    overflow: visible;
    padding: 0;
  }
}
