/* Утверждённый редизайн: монохром + ОДИН приглушённый акцент (охра), работающий
   только на интерактивных состояниях — ховер, фокус, активный пункт оглавления,
   нумерация тезисов. База остаётся чёрно-белой. Это осознанный отход от строчки
   «строго монохром» spec §5: образец (polka.academy/books) тоже не монохромен,
   владелец отход утвердил.
   По умолчанию — светлая тема для ЛЮБОГО посетителя без сохранённого выбора,
   независимо от системной/браузерной prefers-color-scheme: инлайновый скрипт
   в layout.twig ставит data-theme="light" на <html> синхронно, до первой
   отрисовки, если в localStorage ничего не лежит. Явный клик по переключателю
   сохраняет выбор и переопределяет тему в обе стороны через data-theme.
   Медиазапрос ниже на обычной загрузке уже недостижим (data-theme="light"
   стоит всегда) и остаётся резервным путём на случай, если тот инлайновый
   скрипт не выполнился (см. app.js, effectiveTheme()).
   Правило :root[data-theme="dark"] стоит ПОСЛЕ медиазапроса и имеет равную с
   ним специфичность, поэтому побеждает по порядку. Менять порядок нельзя. */
:root {
  --bg: #ffffff;
  --fg: #111111;
  --muted: #6f6f6f;
  --rule: #dcdcdc;          /* тонкая линейка: границы шапки, подвала, разделители */
  --rule-strong: #b4b4b4;   /* линейка поплотнее: рамки, засечки, знак подвала */
  --accent: #9c7b3d;        /* приглушённая охра — только интерактив, не заливки */
  /* Та же охра, но пригодная для НАБОРА, а не для линеек и состояний. На белом
     #9c7b3d даёт с фоном 3,95:1 — графическому элементу этого хватает (порог
     3:1), обычному тексту нет (порог 4,5:1), а плакат впервые красит акцентом
     именно текст: эпиграф, последнюю строку коды, номер в оглавлении. Более
     тёмный тон той же охры даёт 6:1 и проходит AA. В тёмной теме разделения
     не нужно — там охра и так ~9:1, обе переменные совпадают. */
  --accent-ink: #7a5f2c;
  /* Цифра-подложка раздела («плакат-манифест»): не элемент палитры, а фактура —
     минимальный сдвиг от --bg, едва отличимый от фона. Значение своё в каждой
     теме: на белом уходим темнее, на чёрном светлее, дельта примерно равная. */
  --watermark: #efeee9;

  /* Три шрифтовые роли, только системные семейства: ни веб-шрифтов, ни внешних
     запросов (spec §5.1). Дисплейная антиква — крупные заголовки; геометричный
     гротеск — капс, мета, навигация; читаемая антиква — основной текст. */
  --f-display: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, "Times New Roman", serif;
  --f-sans: "Avenir Next", Avenir, Futura, "Trebuchet MS", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --f-body: Georgia, "Times New Roman", serif;

  /* Ручная шкала отступов, кратная 15 (не 8px-сетка) — как у образца. */
  --s1: 15px; --s2: 30px; --s3: 45px; --s4: 60px; --s6: 90px; --s8: 120px;

  /* Композиция во всю ширину: поля растут вместе с экраном, но общая раскладка
     не растекается бесконечно. Колонка прозы ограничена отдельно (68ch). */
  --pad-x: clamp(18px, 5.2vw, 120px);
  --shell: 2200px;

  /* Собственный рисованный разделитель вместо <hr>: две линейки со скобочными
     засечками и ромб посередине. Хранится маской, а не картинкой, чтобы цвет
     брался из токенов темы; фон под маской — градиент, дающий ромбу --fg, а
     линейкам --rule-strong. */
  --flourish: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 128 10'%3E%3Cg fill='%23000'%3E%3Crect x='0' y='4.5' width='46' height='1'/%3E%3Crect x='0' y='1.5' width='1' height='7'/%3E%3Crect x='82' y='4.5' width='46' height='1'/%3E%3Crect x='127' y='1.5' width='1' height='7'/%3E%3C/g%3E%3Cpath d='M64 0.5L68.5 5L64 9.5L59.5 5Z' fill='none' stroke='%23000' stroke-width='1'/%3E%3C/svg%3E");
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0c0c0d; --fg: #f0efec; --muted: #98968f;
    --rule: #2c2c2e; --rule-strong: #46464a; --accent: #c6a462;
    --accent-ink: #c6a462; --watermark: #1c1c22;
  }
}
:root[data-theme="dark"] {
  --bg: #0c0c0d; --fg: #f0efec; --muted: #98968f;
  --rule: #2c2c2e; --rule-strong: #46464a; --accent: #c6a462;
  --accent-ink: #c6a462; --watermark: #1c1c22;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { transition: none !important; }
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font: 400 19px/1.72 var(--f-body);
}

/* Фокус виден всегда и одинаково — пунктир акцентом, с отбивкой. */
a:focus-visible, button:focus-visible, summary:focus-visible {
  outline: 1px dashed var(--accent);
  outline-offset: 5px;
}

/* Приём-подпись: подчёркивание, растущее ИЗ ЦЕНТРА при наведении. Сделано
   фоновым градиентом, а не псевдоэлементом: ::before/::after у ссылок заняты
   скобочным мотивом, а разметку оглавления генерирует pandoc — вставить туда
   лишний <span> нельзя. */
/* .author__links в этом списке больше нет: там полноразмерные фразы-предложения,
   а не короткие навигационные ярлыки, и приём «подчёркивание только при
   наведении» оставлял их неотличимыми от обычного текста, пока на них не
   наведёшь (прямая претензия автора). Они переведены на прозовый приём —
   подчёркивание в покое, как у ссылок внутри манифеста (.manifest a). */
.brand__name, .toc a, .mini-header__links a, .author__more a,
.masthead__nav a, .masthead__social a, .footer__nav a, .footer__social a {
  background-image: linear-gradient(var(--accent), var(--accent));
  background-repeat: no-repeat;
  background-position: 50% 100%;
  background-size: 0 1px;
  padding-bottom: 5px;
  transition: background-size .3s ease-out .05s, color .25s ease-out;
}
.masthead__home:hover .brand__name,
.masthead__home:focus-visible .brand__name,
.toc a:hover, .toc a:focus-visible,
.mini-header__links a:hover, .mini-header__links a:focus-visible,
.author__more a:hover, .author__more a:focus-visible,
.masthead__nav a:hover, .masthead__nav a:focus-visible,
.masthead__social a:hover, .masthead__social a:focus-visible,
.footer__nav a:hover, .footer__nav a:focus-visible,
.footer__social a:hover, .footer__social a:focus-visible { background-size: 100% 1px; }

/* Видимая подпись над служебным блоком ссылок («Скачать книгу», «Наши
   соцсети»): та же гарнитура, кегль и разрядка, что у самих ярлыков рядом, —
   отличается только отсутствием скобок, которыми на этом сайте помечено
   «нажимаемое». Двоеточие рисуется здесь, а не в разметке: в DOM остаются
   только слова. */
.nav-label {
  margin: 0;
  font-family: var(--f-sans); font-size: 11px; letter-spacing: .2em; line-height: 1;
  text-transform: uppercase; color: var(--muted);
}
.nav-label::after { content: ":"; }

/* --- Шапка -------------------------------------------------------------- */
.masthead {
  border-bottom: 1px solid var(--rule);
  padding: var(--s2) var(--pad-x);
  font-family: var(--f-sans);
}
.masthead__inner {
  max-width: var(--shell); margin: 0 auto;
  display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between;
  gap: var(--s2);
}

.brand {
  --mark-h: 56px;
  --mark-w: 60px;                       /* 56 × 1040/970 — пропорция кадра логотипа */
  display: flex; flex-wrap: wrap; align-items: center;
  column-gap: var(--s1); row-gap: 7px; min-width: 0;
}
.brand__title { margin: 0; font: inherit; }   /* <h1> без собственной типографики */
.masthead__home {
  display: inline-flex; align-items: center; gap: var(--s1);
  color: var(--fg); text-decoration: none;
}

/* Знак кадрируется средствами CSS. Исходник квадратный (1280×1277) и содержит
   вшитую надпись «ТЕОЭСТЕТИКА» внизу: на высоте 56 px она была бы ~5 px мыла.
   Показываем прямоугольник 1040×970 исходника со смещением 120/120 — только
   рисунок. Проценты повторяют арифметику background-position: точка X% картинки
   совмещается с точкой X% рамки, поэтому left/top + обратный translate дают
   ровно тот же кадр, что background-size: 123.08% / position: 50% 39.1%.
   Сам файл логотипа не тронут — он остаётся полным и пригодным для og:image. */
.brand__mark {
  flex: none; display: block; position: relative; overflow: hidden;
  width: var(--mark-w); height: var(--mark-h);
}
.brand__mark img {
  position: absolute; left: 50%; top: 39.1%;
  width: 123.08%; height: auto;         /* 1280 / 1040 */
  transform: translate(-50%, -39.1%);
  mix-blend-mode: multiply;             /* белый фон JPEG растворяется в светлой теме */
}
/* Тёмная тема: знак чёрно-белый, поэтому инвертируем его целиком, а ставший
   чёрным фон гасим режимом screen — тёмного прямоугольника вокруг не возникает.
   Порядок тот же, что у токенов: явный выбор объявлен последним. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .brand__mark img { filter: invert(1); mix-blend-mode: screen; }
}
:root[data-theme="dark"] .brand__mark img { filter: invert(1); mix-blend-mode: screen; }

/* Скобочный мотив — псевдоэлементами, а не литералами в разметке: скринридер
   читает «Теоэстетика», а не «левая квадратная скобка …» (spec §5.1).
   Регистр тоже задан стилем: в DOM живёт естественное «Теоэстетика». */
.brand__name {
  display: inline-flex; align-items: baseline; gap: 2px;
  font-family: var(--f-sans); font-size: 32px; letter-spacing: .1em; line-height: 1;
  text-transform: uppercase;
}
.brand__name::before { content: "["; font-weight: 500; }
.brand__name::after { content: "]"; font-weight: 500; }

.brand__tagline {
  flex-basis: 100%; margin: 0;
  padding-left: calc(var(--mark-w) + var(--s1));   /* выключка под вордмарк */
  font-family: var(--f-sans); font-size: 12.5px; letter-spacing: .17em;
  text-transform: uppercase; color: var(--muted); line-height: 1.6;
}

/* Абзаца-объяснения «что это за проект» (.brand__about) под теглайном больше
   нет: он был добавлен по просьбе автора и им же отменён. Правил под него тоже
   не осталось — заметка стоит здесь, чтобы следующая правка не начиналась с
   «а почему объяснения нет». */

/* Ссылки на скачивание — в шапке рядом с брендом, в подвале над подписью
   (spec §5.1, §8.4). Разметка и правила общие для обоих мест: .footer__nav
   отвечает только за расположение в сетке подвала (ниже), типографика и
   скобки — здесь, одним селектором на оба класса.
   Скобки — псевдоэлементами (::before/::after у этих ссылок заняты скобками,
   поэтому подчёркивание — тем же градиентным приёмом, что у
   .mini-header__links a, а не через сами эти псевдоэлементы). */
.masthead__nav, .footer__nav {
  display: flex; flex-wrap: wrap; align-items: center;
  column-gap: var(--s2); row-gap: 9px;
}
.masthead__nav a, .footer__nav a, .manifest__audio-label a {
  display: inline-flex; align-items: baseline; gap: 2px;
  font-size: 11px; letter-spacing: .2em; line-height: 1;
  text-transform: uppercase; color: var(--muted); text-decoration: none;
}
.masthead__nav a::before, .footer__nav a::before, .manifest__audio-label a::before { content: "["; }
.masthead__nav a::after, .footer__nav a::after, .manifest__audio-label a::after { content: "]"; }
.masthead__nav a:hover, .masthead__nav a:focus-visible,
.footer__nav a:hover, .footer__nav a:focus-visible,
.manifest__audio-label a:hover, .manifest__audio-label a:focus-visible { color: var(--accent); }

.download { position: relative; }
.download__btn {
  display: inline-flex; align-items: baseline; gap: 9px;
  padding: 12px 18px; border: 1px solid var(--fg); background: transparent; color: var(--fg); cursor: pointer;
  font-family: var(--f-sans); font-size: 12.5px; letter-spacing: .18em; line-height: 1; text-transform: uppercase;
  transition: color .25s ease-out, border-color .25s ease-out;
  list-style: none;
}
.download__btn::-webkit-details-marker { display: none; }
.download__btn::before { content: "[+]"; }
.download[open] > .download__btn::before { content: "[–]"; }
.download__btn:hover, .download__btn:focus-visible { color: var(--accent); border-color: var(--accent); }
.download__formats {
  display: flex; flex-wrap: wrap;
  column-gap: var(--s2); row-gap: 9px; padding-top: 15px;
}

/* Плеер находится в верхней композиции страницы, сразу под титулом, и не
   дублирует общий список загрузок из шапки. */
.manifest__audio { margin-top: var(--s2); }
.manifest__audio-label {
  margin: 0 0 9px;
  font-family: var(--f-sans); font-size: 11px; letter-spacing: .2em;
  line-height: 1; text-transform: uppercase; color: var(--muted);
}
.manifest__audio audio { display: block; width: min(100%, 560px); max-width: 100%; }
.manifest__audio audio:focus-visible { outline: 1px dashed var(--accent); outline-offset: 5px; }
.manifest__audio audio a { color: var(--fg); }

/* Соцсети и поддержка в шапке — собственная строка во всю ширину под брендом и
   скачиванием (flex-basis: 100% тем же приёмом, что у .brand__tagline). Автор
   попросил не заставлять листать страницу до подвала ради Telegram и YouTube.
   Видимой подписи «Наши соцсети» у строки больше нет (её имя живёт в aria-label
   самого <nav>), поэтому первым в строке идёт сразу канал. Скобочного мотива у
   этих ссылок нет намеренно: скобки на сайте означают команду (скачать,
   оглавление), а здесь — названия каналов. */
.masthead__social {
  flex-basis: 100%;
  display: flex; flex-wrap: wrap; align-items: baseline;
  column-gap: var(--s2); row-gap: 9px;
}
.masthead__social a {
  font-size: 12px; letter-spacing: .17em; line-height: 1;
  text-transform: uppercase; color: var(--muted); text-decoration: none;
}
.masthead__social a:hover, .masthead__social a:focus-visible { color: var(--accent); }

/* «Помощь проекту» — единственное действие в этой строке, у которого есть вес:
   поддержка стоит третьей в иерархии автора (прочитать → скачать → поддержать →
   соцсети), поэтому у неё рамка, а не просто строчка, но рамка тише, чем у
   чёрной кнопки скачивания выше: охряная и мелкая. Правила переехали сюда из
   липкой строки вместе с самой кнопкой (та сведена к бренду и контакту).
   Прижата к правому краю тем же margin-left: auto, что и контакт в липкой
   строке. Селектор с двумя классами — чтобы перебить .masthead__social a;
   растущее подчёркивание кнопке не нужно, у неё есть граница. */
.masthead__social a.masthead__support {
  margin-left: auto;
  background-image: none; padding: 6px 11px;
  border: 1px solid var(--accent); color: var(--accent);
  transition: color .25s ease-out, background-color .25s ease-out;
}
.masthead__social a.masthead__support:hover,
.masthead__social a.masthead__support:focus-visible {
  background: var(--accent); color: var(--bg);
}
/* Правый край имеет смысл, только пока строка укладывается в одну линию: на
   переносе margin-left: auto оставляет кнопку в одиночестве у правого края
   отдельной строкой — ровно та же ловушка, из-за которой контакт в липкой
   строке отпускает свой auto на узком экране. Порог не «на глаз»: пять
   названий каналов плюс кнопка перестают помещаться в линию ниже 740px
   (замерено в браузере — 740px ещё одна линия, 735px уже две), поэтому здесь
   собственный брейкпоинт, а не ближайший шкальный 700px, до которого кнопка
   успела бы повисеть отдельной строкой. Ниже порога она просто идёт следом за
   «Донатом», как обычный пункт ряда. */
@media (max-width: 739px) {
  .masthead__social a.masthead__support { margin-left: 0; }
}

/* Липкая строка: имя сайта слева, контакт автора справа — и всё. Ссылки на
   каналы и кнопка поддержки, которые какое-то время жили здесь, переехали в
   шапку (.masthead__social) и остались в подвале. Отбивка 22px, а не шкальные
   30, — наследство прежнего плотного ряда; двум пунктам она не мешает, а
   перенос на узком экране по-прежнему разводит их по строкам сам.
   Про вертикальное выравнивание строки — почему padding: 5px 0 у обоих флекс-элементов.
   Общий приём растущего подчёркивания (правило выше, .brand__name, .toc a,
   .mini-header__links a, …) даёт каждой ссылке padding-bottom: 5px — место, в котором
   рисуется линия. Дальше два элемента этой строки ведут себя по-разному:
   .mini-header__home сам является <a> И одновременно флекс-элементом, поэтому он
   блокифицируется и его нижний паддинг попадает в высоту рамки (11px текста + 5px = 16px);
   .mini-header__contact — это <span>, паддинг достаётся только вложенным в него
   ссылкам @gaginsky и e-mail, а они инлайновые, и нижний паддинг инлайн-элемента в
   высоту строки не входит (рамка 11px). Две рамки разной высоты, текст в каждой
   прижат к своему верху — и любое выравнивание по поперечной оси врёт: center
   разводил текст на 2.5px (половину разницы), а baseline хоть и сводил текст двух
   элементов между собой, но всю строку целиком оставлял на те же 2.5px выше
   оптического центра переключателя темы (внешний .mini-header центрирует именно
   рамку в 16px, а текст сидит в её верхних 11px).
   Лечится не выбором align-items, а симметрией: padding: 5px 0 вместо одного лишь
   padding-bottom делает рамку каждого элемента симметричной (5 + 11 + 5), текст
   оказывается в её геометрическом центре, и center с baseline дают побайтово
   одинаковый результат — проверено в WebKit и Chromium на 320/375/390/414/1200px,
   в обоих состояниях прокрутки: home−@gaginsky = 0.00px, @gaginsky−e-mail = 0.00px,
   центр строки − центр переключателя = 0.00px. То есть правильность больше не
   зависит от того, как конкретный движок считает базовую линию флекс-элемента, —
   ровно это и было целью, потому что прежняя правка проверялась только в Chromium.
   Высоту полоски симметричный паддинг не меняет: рамка ссылок вырастает с 16 до 21px
   (с 18 до 23px на десктопе), но полоску по-прежнему задаёт переключатель темы
   (26.6px на телефоне, 30.4px на десктопе) — .mini-header остаётся 41.59/49.39px.
   Подчёркивание на месте: background-position: 50% 100% отсчитывается от низа
   padding-box, а низ не сдвинулся.
   align-items оставлен baseline (а не center) единственно из-за вырожденного случая:
   если контакт всё же переносится внутри себя на две строки, baseline сводит
   [Теоэстетика] с первой из них, а center — ни с одной. */
.mini-header__links {
  flex: 1; min-width: 0;
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: flex-start;
  column-gap: 22px; row-gap: 9px;
}
.mini-header__links a, .mini-header__contact {
  font-size: 13px; letter-spacing: .17em; line-height: 1;
  text-transform: uppercase; color: var(--muted); text-decoration: none;
  padding: 5px 0;
}
.mini-header__links a:hover, .mini-header__links a:focus-visible { color: var(--accent); }

/* Без подписи «По сотрудничеству» — только сами контакты, прижаты к правому
   краю строки (margin-left: auto внутри растянутого flex-контейнера). */
.mini-header__contact { margin-left: auto; text-align: right; }

/* Мобильная посадка строки. E-mail гасится на телефоне уже не всегда, а по
   состоянию прокрутки (просьба владельца): наверху страницы, пока имя сайта
   в этой строке ещё не показалось (.mini-header__home без .is-visible), контакт
   на телефоне виден целиком — «@gaginsky · algaginsky@gmail.com». Как только app.js добавляет
   .mini-header__home.is-visible (масштхед ушёл за верхний край экрана и
   [Теоэстетика] встало в строку рядом), места на узком экране на оба пункта
   уже не хватает — тогда e-mail гасится тем же .mini-header__mail, как и
   раньше, и остаётся только @gaginsky, по-прежнему прижатый вправо
   (margin-left: auto; text-align: right — строка 312, общая для обоих
   состояний, здесь не меняется).
   Селектор сиблинговый (~), не потомковый: .mini-header__home и
   .mini-header__contact — соседи внутри .mini-header__links (разметка в
   mini-header.twig), .mini-header__home стоит в DOM первым, поэтому его
   .is-visible достаёт соседа через ~ без каких-либо правок в app.js — класс
   там уже переключается тем же IntersectionObserver, что показывает
   [Теоэстетика].
   Раньше здесь ещё и снимался правый прижим (margin-left: 0; text-align:
   left) — из опасения, что на переносе margin-left: auto оставит @gaginsky
   одиноким пунктом у правого края лишней второй строки. Проверено в живом
   браузере на 320/375/390/414/480/700px (владелец сообщил о переносе на
   телефоне, десктоп был в порядке): с двумя короткими пунктами и погашенным
   e-mail строка укладывается в одну линию с большим запасом на каждой из
   этих ширин, перенос не наступает нигде. Опасение не подтвердилось — правило
   убрано, базовый прижим (строка 312) работает на мобильном без изменений.
   Правил .mini-header__extra / __wide / __narrow / __support здесь больше нет:
   они обслуживали ссылки на каналы и кнопку поддержки, а те переехали в шапку
   (.masthead__social) — гасить и подменять подписи стало нечему.
   Отбивка между строкой ссылок и переключателем темы (.mini-header { gap })
   ужимается там же, где остальная мобильная посадка шапки, — в блоке 700px
   в конце файла: базовое правило .mini-header объявлено ниже по файлу, и
   переопределить его отсюда, с равной специфичностью, было бы нельзя. */
@media (max-width: 700px) {
  .mini-header__home.is-visible ~ .mini-header__contact .mini-header__mail { display: none; }
  /* Невидимая [Теоэстетика] не должна занимать место в строке. opacity/visibility
     гасят её ВИДИМОСТЬ, но флекс-элементом она остаётся — и её ширина (101.8px на
     телефоне) продолжает участвовать в раскладке. Наверху страницы, где e-mail ещё
     показан, на 375px это давало 101.8 + 19 (column-gap) + 249.5 (контакт) = 370px
     при доступных 294px: flex-wrap: wrap уводил контакт на ВТОРУЮ флекс-строку, а на
     ширинах ≤360px не помещался и сам контакт — тогда « · algaginsky@gmail.com»
     переносился под «@gaginsky» уже внутри него. Отсюда обе жалобы автора: «@gaginsky
     и e-mail не выровнены друг с другом» и «[Теоэстетика] и @gaginsky не выровнены» —
     элементы просто стояли на разных строках, а такое не чинится ничем на поперечной
     оси: align-items выравнивает внутри строки, а не между строками. Именно поэтому
     прежняя проверка (baseline, Chromium, getBoundingClientRect) показывала 0.00px и
     была бесполезна — она измеряла состояние ПОСЛЕ прокрутки, где переноса нет, и не
     воспроизводила верх страницы, который автор и видит первым. Движок был ни при чём:
     WebKit и Chromium расходятся здесь не более чем на 0.3px.
     position: absolute убирает невидимую ссылку из потока флекс-строки, не трогая
     ни разметку, ни app.js: содержащий блок — сам .mini-header (у него position:
     sticky), место для абсолютной ссылки не резервируется, а как только приходит
     .is-visible, она возвращается в поток обычным static. Скачка это не добавляет:
     тот же класс в этот момент гасит e-mail, то есть ширина контакта меняется и так,
     а сам контакт прижат вправо (margin-left: auto) и потому не ездит.
     Только для ≤700px: на десктопе строка помещается в одну линию с запасом,
     и резерв места там по-прежнему безвреден. */
  .mini-header__home:not(.is-visible) { position: absolute; }
}

/* Самые узкие телефоны. Контакт целиком — «@GAGINSKY · ALGAGINSKY@GMAIL.COM» —
   занимает 249.5px при кегле 11px. Доступная строке ширина считается как
   W − 36 (боковые поля, --pad-x упирается в свои 18px уже на 346px) − 15 (gap до
   переключателя) − 26.6 (сам переключатель) = W − 77.6, то есть на одну линию
   контакт встаёт начиная с W ≈ 327px. Ниже этого он переносится внутри себя,
   как ни выравнивай строку, поэтому здесь e-mail гасится независимо от прокрутки —
   тем же механизмом .mini-header__mail, что и в блоке выше. Порог взят 360px,
   а не 327px, чтобы остался запас на другую отрисовку шрифта; все актуальные
   iPhone (375px и шире) e-mail наверху страницы по-прежнему показывают. */
@media (max-width: 360px) {
  .mini-header__mail { display: none; }
}

/* --- Титул документа ----------------------------------------------------- */
/* Это НЕ <h1>: единственный заголовок первого уровня на странице — имя сайта в
   шапке. Здесь название документа, набранное крупно, но вне структуры
   заголовков, чтобы не спорить с ним за первый уровень. */
/* «Плакат-манифест»: титул выключен влево и набран тяжело — он открывает
   левую ось, вдоль которой дальше идут цифры-подложки разделов и линейка коды.
   Узкого начертания у системных антикв нет (font-stretch на них — молчаливая
   пустышка, а font-weight: 900 отдаёт тот же жир, что и 700), поэтому
   плотность набирается тем, что реально работает: полужирным, отрицательным
   трекингом, прописными и крупным кеглем. */
.doctitle {
  padding: clamp(var(--s4), 8vw, 150px) var(--pad-x) var(--s6);
  text-align: left;
  border-bottom: 1px solid var(--rule);
}
.doctitle__inner { max-width: var(--shell); margin: 0 auto; }
.doctitle__name {
  font-family: var(--f-display); font-weight: 700;
  font-size: clamp(36px, 8.6vw, 128px); line-height: .93;
  letter-spacing: -.025em; margin: 0;
  text-transform: uppercase;
  /* Страховка: даже если системная гарнитура окажется шире расчётной, слово
     переносится внутри себя, а не выпирает за край и не даёт горизонтальный
     скролл на узком экране. */
  overflow-wrap: break-word;
}
.doctitle__name span { display: block; }

/* --- Разделитель --------------------------------------------------------- */
.flourish,
.manifest section:not(:last-of-type)::after {
  display: block; width: 128px; height: 10px;
  background-image: linear-gradient(90deg,
    var(--rule-strong) 0 59px, var(--fg) 59px 69px, var(--rule-strong) 69px 100%);
}
@supports (mask-image: none) or (-webkit-mask-image: none) {
  .flourish,
  .manifest section:not(:last-of-type)::after {
    -webkit-mask-image: var(--flourish); mask-image: var(--flourish);
    -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
    -webkit-mask-position: center; mask-position: center;
    -webkit-mask-size: 128px 10px; mask-size: 128px 10px;
  }
}
/* Без поддержки масок из разделителя получилась бы сплошная плашка — лучше
   ничего не показывать вовсе, разделы и так разведены отступами. */
@supports not ((mask-image: none) or (-webkit-mask-image: none)) {
  .flourish,
  .manifest section:not(:last-of-type)::after { display: none; }
}
/* Обе виньетки прижаты влево — к той же оси, что титул, заголовки и коды.
   Ромб под титулом — единственная во всём макете «декоративная» охра: в
   образце-макете акцентная виньетка была ровно одна, и размножать её на шесть
   межразделных линеек значило бы превратить экономный акцент в заливку. */
.flourish {
  margin: var(--s2) 0 0;
  background-image: linear-gradient(90deg,
    var(--rule-strong) 0 59px, var(--accent) 59px 69px, var(--rule-strong) 69px 100%);
}
.manifest section:not(:last-of-type)::after { content: ""; margin: var(--s6) 0; }

/* --- Видео автора --------------------------------------------------------- */
/* Ролик автора о манифесте — за раскрывающимся блоком «Пояснения автора»
   (App\Support\ManifestBody::insertVideoIntro()), между разделом 0 и разделом
   1, свёрнут по умолчанию. .video-intro — обёртка вокруг iframe внутри
   .disclosure__body, а не самостоятельная полоса во всю ширину страницы, как
   было раньше: горизонтальные поля здесь по-прежнему --pad-x (см. .doctitle
   выше), потому что раскрывающийся блок сидит в общей колонке манифеста, а не
   растягивается на всю ширину экрана. Ширина внутреннего блока — не 68ch
   (мера прозы) и не --shell (мера титула/страницы целиком): оба варианта не
   подошли бы видео — первый слишком узкий, второй растянул бы ролик во весь
   экран на широком десктопе, — поэтому здесь собственное, «видео-уместное»
   значение. */
.video-intro {
  padding: var(--s4) var(--pad-x);
  border-bottom: 1px solid var(--rule);
}
.video-intro__inner { max-width: 900px; margin: 0 auto; }
/* Отдельного :root[data-theme="dark"]-блока здесь не нужно: --rule-strong,
   --watermark и --muted уже переключаются в теме сами (см. токены вверху
   файла), рамке и фону остаётся только на них ссылаться. */
.video-intro__frame { border: 1px solid var(--rule-strong); }
/* Вкладки — тот же скобочный мотив, что у .masthead__nav a/.footer__nav a
   (кнопка вместо ссылки: переключение ничего не открывает по адресу), и тот же
   капс с разрядкой, что у .disclosure__summary рядом. Активная вкладка не
   просто темнее — у неё ещё и нижняя линия акцентом; margin-bottom: -1px
   у самой кнопки укладывает эту линию поверх border-bottom контейнера, а не
   вторым слоем под ним. min-height держит зону нажатия на телефоне не меньше
   44px даже при мелком 11px-кегле. */
.video-intro__tabs {
  display: flex; flex-wrap: wrap;
  border-bottom: 1px solid var(--rule-strong);
}
.video-intro__tab {
  cursor: pointer; appearance: none;
  background: none; border: 0; margin: 0 0 -1px; padding: 0 var(--s1);
  min-height: 44px;
  display: inline-flex; align-items: center; gap: 2px;
  font-family: var(--f-sans); font-size: 11px; letter-spacing: .2em; line-height: 1;
  text-transform: uppercase; color: var(--muted);
  border-bottom: 2px solid transparent;
  transition: color .25s ease-out, border-color .25s ease-out;
}
.video-intro__tab::before { content: "["; }
.video-intro__tab::after { content: "]"; }
.video-intro__tab:hover, .video-intro__tab:focus-visible { color: var(--accent); }
.video-intro__tab.is-active { color: var(--fg); border-bottom-color: var(--accent); }
/* Панель — та же 16:9-рамка с фоном-«водяным знаком», что раньше держала
   рамка целиком: теперь их две, но видна всегда ровно одна, вторая скрыта
   штатным атрибутом hidden (переключает app.js). Отдельного правила для
   iframe[hidden] здесь уже не нужно: hidden стоит на .video-intro__panel, а не
   на самом iframe, и у неё нет своего display, который перебивал бы
   браузерное [hidden] { display: none }. */
.video-intro__panel {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--watermark);
}
.video-intro__panel iframe { display: block; width: 100%; height: 100%; border: 0; }
/* Виден только без JS (внутри <noscript>): проверить доступность YouTube там
   нечем, поэтому читателю просто даётся прямая ссылка на ролик во ВКонтакте.
   Набор — как у прочих служебных подписей: мелкий шрифт, приглушённый цвет. */
.video-intro__fallback {
  margin: 11px 0 0;
  font-family: var(--f-sans); font-size: 12.5px; line-height: 1.5;
  color: var(--muted);
}
.video-intro__fallback a { color: inherit; }
/* Якорь оглавления «Видео» ведёт на сам <details>, а не на <section> — общее
   .manifest section { scroll-margin-top: 96px; } (см. ниже) до него не
   дотягивается по тегу, поэтому то же значение задано явно тем же числом, что
   и у #author, — иначе переход по ссылке подвёл бы блок под липкую шапку.

   Ниже — две поправки на то, что этот <details> сидит МЕЖДУ разделами (после
   </section> раздела 0, перед <section> раздела 1) и потому не попадает ни под
   одно из правил, которыми разведены сами разделы:

   1. Отбивка снизу. Разделы разведены не своими полями, а виньеткой-разделителем
      ВНУТРИ предыдущего раздела (.manifest section:not(:last-of-type)::after,
      margin: var(--s6) 0), а у самих <section> верхнего поля нет вовсе. Пока
      разделы шли подряд, нижние 90px этой виньетки и были воздухом над
      следующим заголовком. С видео посередине они схлопнулись в отступ НАД
      блоком (margin-top: var(--s3) у .disclosure сливается с ними в те же
      90px), а между </details> и разделом 1 не осталось ничего — ноль пикселей.
      Возвращаем ту же величину явно, чтобы «раздел 0 → видео → раздел 1» дышало
      так же, как обычный переход между разделами.

   2. Слой. Цифра-подложка раздела (.manifest section:not(.author)::before)
      абсолютна, поднята над разделом на top: -.2em своего кегля (12–41px в
      зависимости от ширины) и лежит в z-index: 0. Содержимое раздела над ней
      поднято правилом .manifest section:not(.author) > * — но видео разделу не
      потомок, а сосед, и как обычный непозиционированный блок рисуется НИЖЕ
      любого позиционированного с z-index: 0. Поэтому цифра «1» закрашивала
      подпись «Пояснения автора» целиком, а не просто теснила её. Поднимаем блок
      в тот же слой, что и содержимое разделов: отбивка выше и так убирает
      пересечение, но слой снимает саму возможность перекрытия — и в раскрытом
      состоянии тоже, где цифра иначе наползала на нижнюю линейку ролика.

   Правила намеренно висят на #video, а не на .disclosure: тем же механизмом
   набраны «Подробнее об авторе» и «Публикации», но те лежат ВНУТРИ
   section.author, где ни подложки, ни этой стыковки нет. */
#video {
  scroll-margin-top: 96px;
  margin-bottom: var(--s6);
  position: relative; z-index: 1;
}

/* --- Каркас страницы: рельс оглавления + колонка текста ------------------ */
.page {
  max-width: var(--shell); margin: 0 auto;
  padding: var(--s6) var(--pad-x) var(--s8);
  display: grid;
  grid-template-columns: minmax(220px, 300px) minmax(0, 1fr);
  column-gap: clamp(var(--s3), 6vw, var(--s8));
}

/* --- Оглавление ---------------------------------------------------------- */
.toc {
  position: sticky; top: var(--s2); align-self: start;
  max-height: 100vh; overflow: auto;
  font-family: var(--f-sans);
}
.toc ul { list-style: none; margin: 0; padding: 0; }
/* Отступ слева — место под левую скобку: она позиционируется наружу ссылки, а
   .toc обрезает переполнение (overflow: auto). */
.toc li { margin: 0 0 19px; padding-left: 16px; }
.toc a {
  display: inline-block; position: relative;
  color: var(--muted); text-decoration: none;
  font-size: 15px; letter-spacing: .04em;
}
.toc a:hover, .toc a:focus-visible { color: var(--accent); }
.toc a.is-active { color: var(--fg); }
/* Номер раздела в рельсе — тот же плакатный приём, что у подложки: цифра
   отдельной величиной, а не частью названия. Обёртку ставит
   App\Support\ManifestBody (в артефакте pandoc это просто текст «0. »). */
.toc__num {
  font-weight: 700; color: var(--accent-ink);
  font-variant-numeric: tabular-nums; letter-spacing: .02em;
}
/* Скобки: тот же приём, что в шапке — псевдоэлементами, вне потока, чтобы не
   раздвигать текст и не растягивать подчёркивание. В покое сдвинуты внутрь и
   прозрачны, при наведении и на активном пункте разъезжаются и проявляются. */
.toc a::before, .toc a::after {
  /* Центр берётся по содержимому без нижнего поля под подчёркивание (5px),
     иначе скобки съезжают вниз относительно строки. */
  position: absolute; top: calc((100% - 5px) / 2);
  font-size: 15px; line-height: 1; color: var(--accent); opacity: 0;
  transition: opacity .25s ease-out .05s, transform .25s ease-out .05s;
}
.toc a::before { content: "["; right: 100%; margin-right: 4px; transform: translate(7px, -50%); }
.toc a::after { content: "]"; left: 100%; margin-left: 4px; transform: translate(-7px, -50%); }
.toc a:hover::before, .toc a:focus-visible::before, .toc a.is-active::before,
.toc a:hover::after, .toc a:focus-visible::after, .toc a.is-active::after {
  opacity: 1; transform: translate(0, -50%);
}

/* Свёрнутое оглавление — единственный вход к главам на телефоне, и он выглядел
   ровно как декоративный ярлык раздела: капс с разрядкой, приглушённый цвет,
   линейка снизу и ни одного признака нажимаемости. Автор так и прочитал —
   «названия глав не кликаются» (проверено в браузере: и раскрытие, и переход
   по главе работают на всех ширинах, сломана была подсказка, а не ссылки).
   Теперь у строки есть знак раскрытия [+]/[–] тем же приёмом, что у
   библиографии автора (.disclosure__summary): охра, скобочный мотив, смена
   знака при открытии. display: flex заодно убирает штатный треугольник
   <summary> в движках, где list-style: none не срабатывает. */
.toc__summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: baseline; gap: 9px;
  font-size: 11px; letter-spacing: .2em; text-transform: uppercase; color: var(--muted);
  margin: 0 0 var(--s2); padding-bottom: 13px; border-bottom: 1px solid var(--rule);
  transition: color .25s ease-out;
}
.toc__summary::-webkit-details-marker { display: none; }
.toc__summary::before { content: "[+]"; color: var(--accent); letter-spacing: 0; }
.toc__box[open] > .toc__summary::before { content: "[–]"; }
.toc__summary:hover { color: var(--accent); }

/* Вторая половина той же претензии. Весь «я ссылка» язык оглавления — охра,
   скобки, растущее подчёркивание — живёт на :hover, а на телефоне ховера нет
   вовсе: раскрыв список, читатель видит просто серый текст с цифрами. Поэтому
   на указателе без ховера подчёркивание показывается сразу, в покое; скобки и
   цвет остаются за активным пунктом, чтобы «где я сейчас» по-прежнему
   отличалось от «сюда можно перейти». */
@media (hover: none) {
  .toc a { background-size: 100% 1px; }
}

/* --- Текст манифеста ----------------------------------------------------- */
/* Счётчик начинается с −1, чтобы первый раздел получил номер 00 — нумерация
   разделов в тексте нулевая (s0…s5). */
.manifest { min-width: 0; counter-reset: manifest-section -1; }
.manifest section {
  position: relative; counter-increment: manifest-section;
  /* Липкая полоска (.mini-header) держится над контентом при прокрутке — без
     запаса под её высоту переход по якорю оглавления подводит заголовок
     раздела впритык к ней (визуально «обрезает» заголовок). Прежние 64px были
     рассчитаны на строку в одну линию; теперь она несёт шесть каналов и кнопку
     поддержки и на разных ширинах занимает от одной до трёх линий (~50–85px).
     Значение взято одно на все ширины и с запасом по самой высокой из них:
     набор рассчитанных под каждый брейкпоинт чисел разъехался бы с вёрсткой
     при первой же правке подписей, а лишний воздух над заголовком не мешает —
     там и так стоит цифра-подложка. */
  max-width: 68ch; scroll-margin-top: 96px;
  /* Небольшой запас сверху: цифра-подложка выступает над первой строкой
     заголовка, и без него она подрезалась бы соседним блоком. */
  padding-top: .2em;
}

/* Первый абзац вставленного pandoc-фрагмента — «МАНИФЕСТ ТЕОЭСТЕТИКИ», то же
   название документа, что стоит выше в .doctitle живым текстом. Убираем именно
   display: none, а не visually-hidden: строка не несёт ничего сверх видимого
   титула, и оставленная в дереве доступности она читалась бы вторым подряд
   «Манифест теоэстетики». Сам артефакт generated/ не трогаем (spec §4.4).
   Селектор точный: у .manifest только два абзаца верхнего уровня — этот и
   эпиграф, всё остальное лежит внутри <section>. */
.manifest > p:first-child { display: none; }
/* Это эпиграф-обращение, реальный текст манифеста — не пункт меню и не подпись
   к разделу, поэтому набран той же прозовой гарнитурой, что и текст, курсивом
   (как и было в исходнике: *Всем христианам!...*), без капса/разрядки. */
/* В плакате обращение набрано охрой: это второе из четырёх мест, где акцент
   вообще появляется (эпиграф, последняя строка коды, номер в оглавлении,
   состояния фокуса) — больше нигде, заливок акцентом по-прежнему нет. */
.manifest > p:first-child + p {
  font-family: var(--f-body); font-style: italic; font-size: 20px;
  color: var(--accent-ink); line-height: 1.6;
  margin: 0 0 var(--s4); max-width: 68ch; text-indent: 0;
}
/* Перенос строки внутри эпиграфа («Всем христианам! / Всем небезразличным!»)
   нужен только на узких телефонах — на десктопе это одна строка. Раз навсегда
   вставленный в источник <br> по умолчанию скрыт (идёт как обычный пробел
   перед ним же в тексте) и включается настоящим переносом только в брейкпоинте
   480px ниже. */
.epigraph__break { display: none; }

/* Заголовок раздела в плакатном ключе: та же дисплейная антиква, но тяжёлым
   начертанием, капсом и почти без интерлиньяжа. Кегль намеренно меньше
   макетных 66 px: в макете заголовок был узким (font-stretch 62.5%), а
   системная антиква узкого начертания не имеет — при равном кегле строка
   вышла бы в полтора раза шире и рассыпалась на три-четыре строки. 54 px
   полного начертания дают примерно ту же длину строки, что 66 px узкого. */
.manifest h2 {
  font-family: var(--f-display); font-weight: 700;
  font-size: clamp(26px, 4.4vw, 54px); line-height: 1.04;
  letter-spacing: .012em; margin: 0 0 var(--s2);
  text-transform: uppercase;
  overflow-wrap: break-word;
}
/* Номер раздела живёт в тексте заголовка («0. Предварительные тезисы»), а в
   плакате он показан подложкой — цифрой ЗА заголовком. Дважды одно число не
   показываем, но и не выбрасываем: подложке явно задано пустое альтернативное
   имя (см. ::before ниже — «/ ""»), сама она по умолчанию скринридером
   озвучилась бы наравне с обычным текстом. Поэтому здесь не display: none,
   а обычное визуальное скрытие. Обёртку ставит App\Support\ManifestBody. */
.manifest .sec-num {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.manifest p { margin: 0 0 var(--s1); text-align: justify; hyphens: auto; -webkit-hyphens: auto; }
/* Абзацный отступ вместо пустой строки (явная просьба владельца): проза идёт
   сплошным блоком, красная строка — единственный знак нового абзаца. Правило
   намеренно узкое — только абзацы верхнего уровня внутри разделов манифеста:
   тезисы-списки, эпиграф, кода и справочные абзацы «Об авторе» набираются
   иначе и своих отступов не теряют. */
.manifest section:not(.author) > p { margin: 0; text-indent: 1.5em; }
/* Первый абзац после заголовка — без красной строки: отступать не от чего. */
.manifest section:not(.author) > h2 + p { text-indent: 0; }
.manifest em { font-style: italic; }

/* Цифра-подложка — сердце плаката: номер раздела уходит ЗА заголовок гигантской
   цифрой цвета, едва отличимого от фона. Счётчиком, а не разметкой: вставить
   что-либо в generated/ нельзя (spec §4.4).
   Гарнитура здесь гротеск, а не дисплейная антиква (как в остальном плакате):
   у Georgia и Iowan Old Style цифры минускульные — с выносными элементами и
   разной высотой, — и в кегле 250 px такая цифра читается как случайная
   загогулина, а не как номер. У Avenir Next / Futura / Trebuchet цифры
   маюскульные, одной высоты, с плотным геометрическим силуэтом; заодно
   контраст «гротескная цифра — антиквенный заголовок поверх неё» и есть
   плакатный приём. */
.manifest section:not(.author)::before {
  content: counter(manifest-section);
  position: absolute; left: -.05em; top: -.2em; z-index: 0;
  font-family: var(--f-sans); font-weight: 700;
  font-size: clamp(110px, 16vw, 240px); line-height: .7;
  letter-spacing: -.04em; color: var(--watermark);
  pointer-events: none; user-select: none; -webkit-user-select: none;
}
/* content псевдоэлемента по умолчанию скринридер озвучивает наравне с обычным
   текстом — без этого числу здесь пришлось бы звучать дважды, вторым же
   числом следом за заголовком. «Alt text» для content — синтаксис content:
   … / "" — даёт подложке пустое альтернативное имя, гасящее озвучивание,
   сохраняя видимый счётчик (Chrome 105+, Safari 17.4+, Firefox 137+). Тот же
   приём деградации, что у масок разделителя выше: базовое правило работает
   везде, @supports лишь докручивает его там, где движок понимает синтаксис;
   на прочих движках подложка звучит дважды — как до этого фикса, не хуже. */
@supports (content: "a" / "b") {
  .manifest section:not(.author)::before {
    content: counter(manifest-section) / "";
  }
}
/* Содержимое раздела ложится ПОВЕРХ подложки. Служебный раздел «Об авторе»
   исключён: подложки у него нет, а поднимать в отдельный слой лайтбокс и
   раскрывающуюся библиографию незачем. */
.manifest section:not(.author) > * { position: relative; z-index: 1; }

/* Кода — короткий рефрен в конце раздела. В плакате это отдельная реплика:
   крупно, в столбик, влево, без выключки и переносов, с акцентной линейкой
   слева; последняя строка — охрой. Обёртку ставит App\Support\ManifestBody
   (см. там же признак, по которому кода опознаётся). Кегль по той же логике,
   что у заголовка: 36 px полного начертания ≈ 58 px узкого из макета. */
.coda {
  margin: var(--s3) 0 0;
  border-left: 2px solid var(--accent);
  padding: 4px 0 4px 22px;
}
.coda p {
  margin: 0 0 2px; text-indent: 0;
  font-family: var(--f-display); font-weight: 700;
  font-size: clamp(22px, 3.1vw, 36px); line-height: 1.08;
  letter-spacing: .012em; text-transform: uppercase;
  text-align: left; hyphens: none; -webkit-hyphens: none;
  overflow-wrap: break-word;
}
.coda p:last-child { margin-bottom: 0; color: var(--accent-ink); }

/* Выделенные места манифеста — ключевые тезисы, которые автор попросил вынуть
   из потока прозы (в источнике это обычные цитатные блоки `> …`, поэтому
   выделение доезжает и до EPUB/FB2/MOBI без единого своего механизма).
   Приём тот же левоосевой, что у коды и у цифры-подложки: линейка охрой слева.
   Отличие от коды сделано типографикой, а не цветом, чтобы два выделения не
   спорили: кода — тяжёлый капс дисплейной антиквы в столбик, реплика в конце
   раздела; здесь та же антиква, но обычным начертанием, строчными и заметно
   мельче — это по-прежнему предложение, которое читают, а не лозунг.
   Цвет текста остаётся --fg намеренно: охрой на этом сайте набраны ровно
   четыре вещи (эпиграф, последняя строка коды, номер в оглавлении, состояния
   фокуса), и пятой она здесь не становится — акцент отдан только линейке,
   то есть графике, как и у коды.
   Выключка левая и без переносов: короткая фраза по формату рвётся «реками»,
   а перенос в тезисе, набранном крупнее прозы, читается как опечатка. */
.manifest blockquote {
  margin: var(--s2) 0;
  border-left: 2px solid var(--accent);
  padding: 2px 0 2px 22px;
}
.manifest blockquote p {
  margin: 0 0 var(--s1); text-indent: 0;
  font-family: var(--f-display);
  font-size: clamp(20px, 1.85vw, 25px); line-height: 1.34;
  text-align: left; hyphens: none; -webkit-hyphens: none;
}
.manifest blockquote p:last-child { margin-bottom: 0; }

/* Тезисы: маркер списка заменён капсом-номером акцентом и короткой засечкой. */
.manifest ol { list-style: none; margin: var(--s2) 0; padding: 0; counter-reset: thesis; }
.manifest ol > li {
  counter-increment: thesis; position: relative;
  padding-left: 52px; margin: 0 0 var(--s2);
}
.manifest ol > li::before {
  content: counter(thesis, decimal-leading-zero);
  position: absolute; left: 0; top: 4px;
  font-family: var(--f-sans); font-size: 11px; letter-spacing: .1em;
  color: var(--accent-ink); font-variant-numeric: tabular-nums;
}
.manifest ol > li::after {
  content: ""; position: absolute; left: 0; top: 24px;
  width: 26px; height: 1px; background: var(--rule);
}
.manifest ol > li > p:last-child { margin-bottom: 0; }

/* Строчные ссылки в тексте: подчёркнуты по умолчанию, при наведении
   подчёркивание ИСЧЕЗАЕТ — обратный приём к навигационному. */
.manifest a {
  color: inherit; text-decoration: underline;
  text-decoration-thickness: 1px; text-underline-offset: 3px;
  text-decoration-color: var(--rule-strong);
  transition: text-decoration-color .25s ease-out, color .25s ease-out;
}
.manifest a:hover, .manifest a:focus-visible { text-decoration-color: transparent; color: var(--accent); }

/* --- Об авторе ----------------------------------------------------------- */
/* Раздел стоит внутри .manifest и потому уже получил её меру набора, отступы и
   виньетку сверху. Нумерация правого рельса ему не положена — селектор номера
   исключает .author, а счётчик здесь не наращивается, чтобы будущие разделы
   манифеста не сбились. Весь справочный аппарат (факты, идентификаторы,
   библиография) набран гротеском: это не проза манифеста, а картотека, и
   смена гарнитуры отделяет одно от другого. */
.manifest .author { counter-increment: none; }
.author__head {
  display: grid; grid-template-columns: 340px minmax(0, 1fr);
  column-gap: var(--s2); row-gap: var(--s2);
  /* Карточка укорочена (наукометрические идентификаторы убраны, основной текст
     ушёл под «Подробнее»), и правая колонка стала заметно короче портрета.
     align-items: start оставлял под ней пустое поле высотой в половину фото —
     читалось как недоделанная вёрстка. center вместо этого ставит короткую
     колонку по центру фотографии, а не топит её у верхнего края. */
  align-items: center; margin: 0 0 var(--s3);
}
.author__figure { margin: 0; }
.author__photo { display: block; width: 100%; height: auto; }
/* Кнопка вместо простого <img>: фото кликабельно, открывает то же изображение
   крупнее в <dialog>. Сброс кнопочного оформления — сама фотография и есть
   кликабельная область, рамка/фон браузерной кнопки здесь не нужны. */
.author__photo-btn {
  display: block; width: 100%; padding: 0; border: 0; background: none; cursor: zoom-in;
}
.author__photo-btn:hover .author__photo,
.author__photo-btn:focus-visible .author__photo { opacity: .88; }
.author__photo { transition: opacity .2s ease-out; }

/* Лайтбокс — стандартный <dialog>: центрирование через ::backdrop у браузера
   бесплатно на большинстве движков, но явные top/margin надёжнее кросс-браузерно. */
.lightbox {
  border: 0; padding: 0; background: none; max-width: min(92vw, 900px); max-height: 92vh;
}
.lightbox[open] { display: flex; }
.lightbox img { display: block; width: 100%; height: auto; max-height: 92vh; object-fit: contain; }
.lightbox::backdrop { background: rgba(0, 0, 0, .82); }
.lightbox__close {
  position: absolute; top: -34px; right: 0;
  width: 30px; height: 30px; padding: 0; cursor: pointer;
  background: none; border: 1px solid #fff; color: #fff; font-size: 15px; line-height: 1;
}
.lightbox__close:hover, .lightbox__close:focus-visible { background: #fff; color: #000; }
/* Тёмная тема: снимок чёрно-белый и на светлом фоне — в темноте он бьёт по
   глазам белым прямоугольником. Инвертировать портрет нельзя (в отличие от
   знака), поэтому просто чуть приглушаем. Порядок правил тот же, что у токенов:
   явный выбор объявлен последним. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .author__photo { filter: brightness(.86) contrast(1.02); }
}
:root[data-theme="dark"] .author__photo { filter: brightness(.86) contrast(1.02); }

.author__links { list-style: none; margin: 0 0 var(--s2); padding: 0; }
.author__links li { margin: 0 0 11px; }
/* Это не ярлыки навигации, а целые фразы («Профиль на сайте Московской духовной
   академии»), и цвет у них — обычный --fg. С общим приёмом шапки (подчёркивание
   растёт из центра только при наведении) они в покое ничем не отличались от
   простого текста: автор прямо сказал, что не понял, что это ссылки. Поэтому
   здесь второй, уже существующий на сайте приём — прозовый, как у ссылок внутри
   манифеста (.manifest a): подчёркивание есть всегда, при наведении оно, наоборот,
   гаснет, а текст уходит в охру. На телефоне, где ховера нет, подчёркивание
   остаётся видимым — в отличие от прежнего варианта. */
.author__links a {
  display: inline-block; color: var(--fg);
  text-decoration: underline;
  text-decoration-thickness: 1px; text-underline-offset: 3px;
  text-decoration-color: var(--rule-strong);
  font-family: var(--f-sans); font-size: 15px; letter-spacing: .04em; line-height: 1.5;
  transition: text-decoration-color .25s ease-out, color .25s ease-out;
}
.author__links a:hover, .author__links a:focus-visible {
  color: var(--accent); text-decoration-color: transparent;
}
.author__more a:hover, .author__more a:focus-visible { color: var(--accent); }

/* Примечание к монографии и «Далее »» — проза внутри карточной библиографии:
   та же типографика списка (гротеск, ~14.5px, приглушённый цвет), а не
   манифестный Georgia-корпус, который иначе наследовался бы от body. */
.author__note, .author__more {
  margin: var(--s1) 0 0;
  font-family: var(--f-sans); font-size: 16px; letter-spacing: .01em; line-height: 1.6;
  color: var(--muted);
}
.author__more a { display: inline-block; color: var(--fg); text-decoration: none; }

/* Ярлыки — та же служебная типографика, что у заголовка рельса оглавления и
   подписей подвала: гротеск, капс, крупная разрядка, приглушённый цвет. */
.author__ids dt, .author__facts dt, .disclosure__body h3 {
  font-family: var(--f-sans); font-weight: 400; font-size: 11.5px;
  letter-spacing: .24em; text-transform: uppercase; color: var(--muted);
}
.author__ids, .author__facts { margin: 0; }
.author__ids dt { margin: 0 0 4px; }
.author__ids dd { margin: 0 0 13px; }
.author__ids dd:last-child { margin-bottom: 0; }
.author__facts dt {
  margin: 0; padding-top: 13px; border-top: 1px solid var(--rule);
}
.author__facts dd { margin: 11px 0 var(--s2); }
.author__facts dd:last-child { margin-bottom: 0; }
.author__ids dd, .author__facts dd, .author__list li, .author__facts dd li {
  font-family: var(--f-sans); font-size: 16px; letter-spacing: .01em; line-height: 1.6;
  color: var(--fg);
}
.author__ids dd { font-variant-numeric: tabular-nums; }

/* Маркер списков — не точка, а короткая засечка линейкой: тот же приём, что у
   тезисов манифеста (.manifest ol > li::after). */
.author__facts dd ul, .author__list { list-style: none; margin: 0; padding: 0; }
.author__facts dd li, .author__list li { position: relative; padding-left: 22px; margin: 0 0 11px; }
.author__facts dd li:last-child, .author__list li:last-child { margin-bottom: 0; }
.author__facts dd li::before, .author__list li::before {
  content: ""; position: absolute; left: 0; top: 12px;
  width: 12px; height: 1px; background: var(--rule-strong);
}

/* Сворачиваемый блок — тот же механизм и тот же вид, что у мобильного
   оглавления: штатный маркер убран (в том числе вебкитовский), вместо него
   скобочный знак акцентом, меняющийся при раскрытии. */
.disclosure { border-top: 1px solid var(--rule); margin-top: var(--s3); }
.disclosure__summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: baseline; gap: 9px;
  font-family: var(--f-sans); font-size: 11px; letter-spacing: .2em;
  text-transform: uppercase; color: var(--muted);
  padding: 13px 0;
  transition: color .25s ease-out;
}
.disclosure__summary::-webkit-details-marker { display: none; }
.disclosure__summary::before { content: "[+]"; color: var(--accent); letter-spacing: 0; }
.disclosure[open] > .disclosure__summary::before { content: "[–]"; }
.disclosure__summary:hover { color: var(--accent); }
.disclosure__body { padding: var(--s1) 0 var(--s2); }
.disclosure__body h3 {
  margin: var(--s2) 0 var(--s1); padding-bottom: 11px;
  border-bottom: 1px solid var(--rule);
}
.disclosure__body > h3:first-child { margin-top: 0; }

/* --- Тонкая полоска под шапкой (липнет при прокрутке) --------------------- */
/* Раньше переключатель темы был fixed у правого края экрана и полз за прокруткой
   поверх текста, отъедая и без того тесное место на мобильном (владелец попросил
   убрать). Теперь у него своя тонкая строка сразу под шапкой: в потоке, пока
   страница вверху, и sticky-прилипает к верху при прокрутке — имя сайта и
   переключатель остаются под рукой, но в собственной полосе с фоном, а не
   поверх абзаца. */
.mini-header {
  position: sticky; top: 0; z-index: 4;
  display: flex; align-items: center; justify-content: space-between; gap: var(--s2);
  background: var(--bg); border-bottom: 1px solid var(--rule);
  padding: 9px var(--pad-x);
  font-family: var(--f-sans);
}
/* Скрыт по умолчанию: показываем только когда настоящее имя сайта в полной
   шапке уже ушло за верхний край экрана (класс ставит app.js через
   IntersectionObserver) — иначе строка дублирует то, что и так видно сверху.
   opacity/visibility, а не display: место в строке остаётся зарезервировано,
   соседние ссылки не дёргаются при появлении/исчезновении. */
.mini-header__home {
  font-size: 12px; letter-spacing: .18em; line-height: 1; text-transform: uppercase;
  color: var(--muted); text-decoration: none;
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .2s ease-out, color .25s ease-out;
}
.mini-header__home.is-visible { opacity: 1; visibility: visible; pointer-events: auto; }
.mini-header__home:hover, .mini-header__home:focus-visible { color: var(--accent); }

/* --- Переключатель темы -------------------------------------------------- */
/* Механизм переключения (data-theme, localStorage) не менялся — только место
   (внутри .mini-header) и форма (обычная квадратная кнопка в потоке). */
.theme-toggle {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--bg); color: var(--muted);
  border: 1px solid var(--rule-strong);
  width: 1.9em; height: 1.9em; padding: 0; cursor: pointer; font-size: 16px;
  font-family: var(--f-sans); line-height: 1;
  transition: color .25s ease-out, border-color .25s ease-out;
}
.theme-toggle:hover { color: var(--accent); border-color: var(--accent); }

/* --- Подвал -------------------------------------------------------------- */
.footer {
  border-top: 1px solid var(--rule);
  padding: var(--s4) var(--pad-x);
  font-family: var(--f-sans); color: var(--muted);
}
/* Третья колонка (auto) держала декоративный знак «[ ]» в правом нижнем углу.
   Знак убран (он ничего не сообщал и сбивал читателя с толку — см. footer.twig),
   вместе с ним ушла и колонка: подпись и место занимают две равные половины. */
.footer__inner {
  max-width: var(--shell); margin: 0 auto;
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--s2);
  align-items: end;
}
/* Четвёртый элемент сетки: собственная строка над подписью, а не четвёртая
   колонка — три существующие колонки (подпись/место/знак) не трогаем. */
.footer__nav { grid-column: 1 / -1; }
/* Пятая строка подвала: соцсети и способы поддержки — та же скромная
   типографика, что у .mini-header__links (без скобочного мотива download-нава:
   это контакты, а не команда «скачать»), и показана всегда, на любой ширине,
   без единого media-скрытия (явное требование владельца). */
.footer__social {
  grid-column: 1 / -1;
  display: flex; flex-wrap: wrap; align-items: center;
  column-gap: var(--s2); row-gap: 9px;
}
.footer__social a {
  font-size: 13px; letter-spacing: .17em; line-height: 1;
  text-transform: uppercase; color: var(--muted); text-decoration: none;
}
.footer__social a:hover, .footer__social a:focus-visible { color: var(--accent); }
.footer__sign { font-family: var(--f-display); font-size: 21px; color: var(--fg); margin: 0; }
.footer__place {
  font-size: 11px; letter-spacing: .18em; text-transform: uppercase;
  color: var(--muted); margin: 0;
}

/* --- Страница ошибки ----------------------------------------------------- */
/* error.twig не наследует layout.twig, поэтому здесь и шапки, и подвала нет —
   только колонка текста в общей палитре. */
.page--error { display: block; max-width: 68ch; margin: 0 auto; }
.error__title {
  font-family: var(--f-display); font-weight: 400;
  font-size: clamp(30px, 5vw, 56px); line-height: 1.05;
  letter-spacing: -.01em; margin: 0 0 var(--s2);
}
.error__lead { margin: 0 0 var(--s2); }
.error__back { margin: 0 0 var(--s3); }
.error__back a { color: var(--fg); text-decoration: none; }
.error__back a:hover, .error__back a:focus-visible { color: var(--accent); }
.error__code {
  color: var(--muted); letter-spacing: 2px;
  font-family: var(--f-sans); font-size: 11px; text-transform: uppercase; margin: 0;
}

/* ==========================================================================
   Адаптив
   ========================================================================== */

/* Большие экраны: шире поля, крупнее кегль, шире рельс оглавления. */
@media (min-width: 1920px) {
  body { font-size: 20px; }
  :root { --shell: 2600px; }
  .page { grid-template-columns: minmax(260px, 340px) minmax(0, 1fr); }
}

@media (max-width: 1240px) {
  .page { grid-template-columns: minmax(190px, 240px) minmax(0, 1fr); }
}

/* Планшет / узкий лэптоп: одна колонка, оглавление уезжает наверх строкой.
   Порог 1024 — от макета; порог 901 ниже — контракт с app.js, который
   принудительно раскрывает <details> на широких экранах (spec §5.3). В зазоре
   902–1024 оглавление уже горизонтальное, но ещё раскрыто — как в макете. */
@media (max-width: 1024px) {
  .page {
    grid-template-columns: minmax(0, 1fr);
    row-gap: var(--s4); padding-top: var(--s4);
  }
  .toc { position: static; max-height: none; overflow: visible; }
  .doctitle { padding-bottom: var(--s4); }
  /* Первый шаг усмирения подложки. Дальше их ещё два (700 и 480): чем уже
     колонка, тем меньше цифре позволено спорить с заголовком — на телефоне
     заголовок обязан читаться первым, это прямое требование владельца. */
  .manifest section:not(.author)::before { font-size: clamp(96px, 14vw, 150px); }
  /* Свёрнутая шапка оглавления остаётся под рукой при прокрутке — не весь список
     (это было бы навязчиво), а только строка «Оглавление»: коснулся — открылось.
     Выше 900px эта строка скрыта отдельным правилом ниже, туда это не попадает. */
  .toc__summary { position: sticky; top: 0; z-index: 3; background: var(--bg); }
}

/* Планшет / узкий лэптоп: два списка (манифест + «Об авторе») сведены в одну
   горизонтальную строку-рельс — см. комментарий в toc.twig. На настоящем
   телефоне это, наоборот, лишнее: пункты должны идти один под другим обычным
   списком, поэтому flex-раскладка ограничена диапазоном планшета. */
@media (min-width: 701px) and (max-width: 1024px) {
  /* Оглавление собрано из двух списков: сгенерированного pandoc и одного пункта
     «Об авторе» рядом с ним. Строкой они должны идти как один список, поэтому
     контейнером-раскладкой становится обёртка, а сами <ul> отдают свои пункты
     наружу через display: contents — вставить пункт внутрь артефакта нельзя.
     Смысл списка в дереве доступности при этом теряется, но у <nav> есть
     aria-label, а ссылки остаются ссылками. */
  .toc__lists { display: flex; flex-wrap: wrap; gap: 13px var(--s2); }
  .toc__lists ul { display: contents; }
  .toc li { margin: 0; }
}

@media (min-width: 901px) {
  .toc__summary { display: none; }
  /* На широком экране summary спрятан, а заголовок рельсу всё-таки нужен.
     Текст декоративный: у <nav> уже есть aria-label «Оглавление». */
  .toc__box::before {
    content: "Оглавление"; display: block;
    font-size: 11px; letter-spacing: .24em; text-transform: uppercase; color: var(--muted);
    margin: 0 0 var(--s2); padding-bottom: 13px; border-bottom: 1px solid var(--rule);
  }
}

/* Телефон и узкий планшет: оглавление перестаёт быть блоком в начале страницы и
   становится закреплённой кнопкой в углу экрана. Прямая просьба автора: манифест
   длинный, и до глав нужно дотягиваться в любой момент чтения, а не прокручивать
   текст обратно наверх. Разметка та же самая — один <details> из toc.twig, ничего
   не продублировано, — меняется только раскладка.
   Ключевой приём — flex-direction: column-reverse: в DOM <summary> идёт первым
   (иначе <details> просто не работает), а видимо он оказывается внизу, и список
   раскрывается ВВЕРХ, а не за нижний край экрана. Порог 900 не выдуман: выше него
   app.js принудительно раскрывает <details> (контракт spec §5.3), и сворачиваемой
   кнопки там нет вовсе — так что этот блок и правило min-width: 901 выше не
   пересекаются ни на одной ширине.
   Блок стоит ПОСЛЕ диапазона 701–1024 намеренно: там оглавление разложено
   горизонтальным рельсом, в узкой панели это ни к чему, и раскладка возвращается
   к обычному списку. */
@media (max-width: 900px) {
  .toc {
    position: fixed; z-index: 6;
    /* top: auto обязателен: базовое правило .toc задаёт top: var(--s2) для
       sticky-рельса, и при fixed с заданными и top, и bottom выигрывает top —
       кнопка уехала бы под шапку вместо нижнего угла. */
    top: auto; right: var(--pad-x); bottom: 19px; left: auto;
    width: max-content; max-width: calc(100vw - 2 * var(--pad-x));
    max-height: none; overflow: visible;
  }
  .toc__box {
    display: flex; flex-direction: column-reverse;
    background: var(--bg); border: 1px solid var(--rule-strong);
  }
  .toc__summary {
    position: static; margin: 0; padding: 12px 15px;
    border-bottom: 0; background: none;
  }
  /* Список прокручивается сам, если глав больше, чем помещается на экране, —
     страница под панелью при этом не уезжает. */
  .toc__lists {
    display: block;
    max-height: min(56vh, 420px); overflow: auto;
    padding: 15px 15px 0; border-bottom: 1px solid var(--rule);
  }
  .toc li { margin: 0 0 17px; padding-left: 16px; }
  /* Кнопка висит поверх страницы, и в самом низу это подвал: даём ему запас,
     иначе она закрывает последнюю строку. Значение перебивает и var(--s3) из
     блока 480px ниже — тот теперь задаёт только верхний отступ. */
  .footer { padding-bottom: 90px; }
}

/* Узкий планшет: шапка и подвал складываются в колонку. */
@media (max-width: 700px) {
  .brand { --mark-h: 45px; --mark-w: 48px; }
  .brand__name { font-size: 19px; }
  /* Отбивка до переключателя темы. 15px вместо 30 остались от плотного ряда
     ссылок; строка с тех пор сведена к бренду и контакту и укладывается в одну
     линию, но и на 375px, где перенос всё же случается, эти 15 пикселей ей не
     мешают — переключатель просто не липнет к ссылкам. */
  .mini-header { gap: 15px; }
  .mini-header__links { column-gap: 19px; }
  /* flex-basis: 100% — тем же приёмом, что у .brand__tagline: блок скачивания
     всегда уходит на свою строку под брендом, а не пытается втиснуться рядом
     с ним вперемешку с усечёнными ссылками. */
  .masthead__nav { flex-basis: 100%; }
  .masthead__nav, .masthead__social, .footer__nav, .footer__social { column-gap: 19px; }
  .footer__inner { grid-template-columns: 1fr; gap: 19px; }
  /* Портрет уезжает над текстом: рядом с ним колонка сведений становится уже
     ~150 px, и в ней ломаются и ссылки, и сами идентификаторы. */
  .author__head { grid-template-columns: minmax(0, 1fr); }
  .author__figure { max-width: 300px; }
  .manifest section:not(.author)::before { font-size: clamp(72px, 13vw, 98px); }
  .coda { padding-left: 17px; }
  .manifest blockquote { padding-left: 17px; }
}

/* Узкие телефоны: боковые поля и так сжаты до ~19px через clamp у --pad-x. */
@media (max-width: 480px) {
  body { font-size: 17.5px; line-height: 1.68; }
  .masthead { padding-top: 19px; padding-bottom: 19px; }
  .brand { --mark-h: 39px; --mark-w: 42px; column-gap: 11px; }
  .masthead__home { gap: 11px; }
  .brand__name { font-size: 18px; }
  .brand__tagline { padding-left: 0; font-size: 11.5px; letter-spacing: .13em; }
  /* Кегль 11px, разрядка .1em и отбивки ниже унаследованы от прежней раскладки
     с шестью пунктами. Теперь строка несёт ровно два — «[Теоэстетика]» и
     «@gaginsky», — так что запас только вырос; пересчёт под короткий набор не
     требуется, и уменьшать его тоже незачем: контакт остаётся мелким служебным
     текстом, а не второй строкой имени. */
  .mini-header__links { column-gap: 10px; row-gap: 6px; }
  .mini-header__links a, .mini-header__contact { font-size: 11px; letter-spacing: .1em; }
  .footer__social a { letter-spacing: .13em; }
  .masthead__nav, .masthead__social, .footer__nav, .footer__social { column-gap: 15px; row-gap: 7px; }
  .masthead__nav a, .footer__nav a { letter-spacing: .16em; }
  .masthead__social a { font-size: 11px; letter-spacing: .13em; }
  .doctitle { padding-top: var(--s3); padding-bottom: var(--s3); }
  .video-intro { padding-top: var(--s3); padding-bottom: var(--s3); }
  /* Собственный clamp вместо фиксированного кегля: у самой узкой колонки (320 px
     минус поля) «ТЕОЭСТЕТИКИ» полужирным капсом занимает ровно ширину строки,
     поэтому нижняя граница подобрана по ней, а не «на глаз». */
  .doctitle__name { font-size: clamp(35px, 10.5vw, 46px); }
  .page { padding-top: var(--s2); padding-bottom: var(--s4); }
  .manifest h2 { font-size: 24px; }
  /* Последний и самый жёсткий шаг подложки. На 375 px цифра выходит ~64 px —
     менее чем втрое крупнее заголовка, а не вчетверо-впятеро, как на десктопе:
     силуэт плаката сохраняется, но за внимание с текстом цифра уже не борется.
     Единственный шаг из макета (280 → 220 px на 1024 px) — планшетный, для
     телефона он заведомо мал: 220 px цифры на колонке в 336 px закрыли бы
     заголовок целиком. */
  .manifest section:not(.author)::before { font-size: clamp(60px, 17vw, 80px); }
  /* Выключка по формату на колонке в ~38 знаков даёт «реки» даже с переносами:
     владелец в курсе этого риска и осознанно решил оставить justify и на
     телефоне — здесь просто наследуется базовое правило `.manifest p`
     (строка ~736), никакого переопределения на этой ширине больше нет. */
  .epigraph__break { display: inline; }
  .manifest ol > li { padding-left: 0; padding-top: 26px; }
  .manifest ol > li::after { top: 19px; }
  .manifest section:not(:last-of-type)::after { margin: var(--s3) auto; }
  /* Отбивка под видео повторяет виньетку строкой выше: на телефоне межразделный
     воздух ужат с 90px до 45px, и переход «видео → раздел 1» ужимается вместе с
     ним, иначе один этот стык остался бы вдвое просторнее всех соседних. */
  #video { margin-bottom: var(--s3); }
  /* Только верхний отступ: нижний держит блок 900px выше — там под закреплённую
     кнопку оглавления зарезервировано место. */
  .footer { padding-top: var(--s3); }
  .mini-header { padding: 7px var(--pad-x); }
  .masthead__social a.masthead__support { padding: 5px 9px; }
   .manifest__audio { margin-top: var(--s1); }
   .manifest__audio audio { width: 100%; }
  .theme-toggle { font-size: 14px; }
}
