/* Оформление площадки — перенос вида «Заметок» love.ngs.ru.
 *
 * Мера тут не «красиво», а «человек не заметил переезда»: колонка автора слева
 * с квадратным аватаром и ником под ним, нумерованная постраничка, дата вида
 * 14.08.2026, 18:30:04, дерево с отступами по уровням. Числа и цвета взяты из
 * настоящих стилей сайта (index.css/main.css, снято 18.08.2026), а не на глаз:
 * разделитель #c7d3d9, текст #3d4952, служебное #999, ник женский #ef4e4f,
 * мужской #448dc8, аватар 100×100, комментарий с отступом слева на 115px.
 *
 * Чего из оригинала нет и не будет: лайки, жалобы, «онлайн»-огонёк, боковая
 * колонка новостей НГС. Первые три — функции, которых у площадки нет по
 * решению (эпик E), последняя — чужой раздел.
 *
 * Тема живёт в токенах :root, а какая именно — говорит атрибут data-theme.
 * Стоит он на КАЖДОЙ странице: не выбиравший получает тему по умолчанию
 * (web.defaultTheme, «Витраж»), поэтому prefers-color-scheme площадка не
 * спрашивает нигде. Палитр шесть, и они трёх родов:
 * «Светлая» (она же голый :root) и «Тёмная» — это НГС, их числа с сайта и
 * закреплены fidelity_test; «Бумага» и «Графит» — наши одиночные, и мера у них
 * другая, читаемость вместо соответствия; «Витраж» и «Гримуар» — наша ПАРА,
 * одно оформление в двух яркостях. Правило набора одно, и оплачено оно
 * сокращением с четырёх тем до двух: КАЖДАЯ кнопка обязана показывать то, чего
 * не показывает другая, — прежние «Классика» и «Светлая» отдавали одну и ту же
 * палитру, и нажатие это не подтверждало.
 *
 * ОГРАНИЧЕНИЕ: CSP без 'unsafe-inline' запрещает атрибут style, поэтому
 * отступ ветки задаётся классом d1…d12, а расстояние считает calc(). На самом
 * НГС это inline padding-left — нам так нельзя. */

/* color-scheme — не украшение: он говорит браузеру, какими рисовать ПОЛОСЫ
   ПРОКРУТКИ, галочки и поле выбора файла. Раньше это решала система читателя,
   и на тёмной теме со светлой системой полоса прокрутки оставалась белой; с
   тех пор как тёмная тема стала умолчанием, такой она была бы у КАЖДОГО гостя.
   Стоит внутри палитры, потому что это её собственное свойство, а не отдельная
   настройка рядом. */
:root {
  color-scheme: light;
  --page: #e9edf0;
  --card: #fff;
  --ink: #3d4952;
  --meta: #999;
  --rule: #c7d3d9;
  --link: #448dc8;
  --male: #448dc8;
  --female: #ef4e4f;
  --anon: #4a545c;
  --nav: #4ba3dd;
  --nav-ink: #fff;
  --accent: #82b541;
  --btn: #f4f6f7;
  --bb-red: red;
  --bb-green: green;
  --bb-blue: blue;
  --bb-purple: purple;
  --bb-orange: orange;
  --bb-pink: pink;
  /* Цвета старой записи «[color:cyan]» — ими писали до 2011 года. На белом
     листе они те же, что были на сайте: жёлтый и на нём читался еле-еле.
     Белый и чёрный не свои, а тёмы: «белым» на сайте прятали текст в фоне,
     «чёрным» писали цветом обычного текста, и взятые буквально они на тёмной
     теме сделали бы ровно обратное. */
  --bb-cyan: cyan;
  --bb-brown: brown;
  --bb-yellow: yellow;
  --bb-grey: grey;
  --bb-white: var(--card);
  --bb-black: var(--ink);
  --hl: #f3f6f9;
  --step: 14px;
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --page: #15181c;
  --card: #1d2126;
  --ink: #dbe2e8;
  --meta: #8c959d;
  --rule: #2e353c;
  --link: #6fadde;
  --male: #6fadde;
  --female: #ef6f70;
  --anon: #a8b1b9;
  --nav: #23527a;
  --btn: #242a30;
  --bb-red: #ef6f70;
  --bb-green: #7fbf5a;
  --bb-blue: #6fadde;
  --bb-purple: #b48ae6;
  --bb-orange: #e39a45;
  --bb-pink: #f0a0c0;
  --bb-cyan: #5fc9c9;
  --bb-brown: #c08a5e;
  --bb-yellow: #d8c455;
  --bb-grey: #9aa3ab;
  --hl: #232a31;
  --sil-fade: .72;
}

/* «Бумага» — не перенос, а своё оформление, и потому единственное, чем оно
   обязано себя оправдывать, — тем, что показывает не то же самое: тёплый лист
   вместо голубоватого экрана НГС, тёмная шапка вместо синей, глубокая бирюза
   вместо салатового акцента. Мера здесь не «как на сайте», а читаемость, и
   числа лучше исходных: служебный текст 4,6:1 против 2,85:1 у НГС, белая
   подпись на акценте 6,0:1 против 2,4:1 (расчёт WCAG, 18.08.2026).
   Цвета разметки ранних лет достаются от светлой намеренно: лист почти белый,
   и «розовый» здесь виден ровно так же еле-еле, как автору в 2013-м. */
:root[data-theme="paper"] {
  color-scheme: light;
  --page: #f4f1ec;
  --card: #fffdf9;
  --ink: #26231e;
  --meta: #7a7367;
  --rule: #e3ded4;
  --link: #1a6b86;
  --male: #2b6f9e;
  --female: #b23a63;
  --anon: #6b6459;
  --nav: #2b2822;
  --accent: #1a6b86;
  --btn: #efece5;
  --hl: #fdf2d8;
}

/* «Графит» — тоже своё: тёмная тема НГС уходит в чёрный и оставляет синюю
   шапку сайта, а эта стоит на нейтральном сланце, шапку красит в тот же
   графит и берёт бирюзовый акцент. Текст приглушён (#cfd6dd, а не белый):
   на тёмном фоне чистый белый даёт ореол вокруг букв.
   Цвета разметки ПРИХОДИТСЯ повторить: наследуются они от голого :root, где
   стоят словами автора (blue, purple), а на тёмной карточке эти слова не
   читаются вовсе — ровно та причина, по которой их поднимает тёмная тема.
   Акцент выбран между двумя своими работами: он и фон под белой подписью
   (3,9:1), и сам текст на карточке (3,7:1) — палитры, где обе роли брали бы
   4,5:1, не существует, и размен сделан в пользу равенства. */
:root[data-theme="graphite"] {
  color-scheme: dark;
  --page: #1c2126;
  --card: #252b32;
  --ink: #cfd6dd;
  --meta: #8a949e;
  --rule: #39424b;
  --link: #7ab8e8;
  --male: #7ab8e8;
  --female: #ef8ba4;
  --anon: #a3adb7;
  --nav: #2f363e;
  --nav-ink: #e6ebf0;
  --accent: #348f84;
  --btn: #2d343b;
  --bb-red: #ef6f70;
  --bb-green: #7fbf5a;
  --bb-blue: #6fadde;
  --bb-purple: #b48ae6;
  --bb-orange: #e39a45;
  --bb-pink: #f0a0c0;
  --bb-cyan: #5fc9c9;
  --bb-brown: #c08a5e;
  --bb-yellow: #d8c455;
  --bb-grey: #9aa3ab;
  --hl: #2f3841;
  --sil-fade: .72;
}

/* «Гримуар» и «Витраж» — пятая и шестая, и они ПАРА: средневековый фасад
   (антиква, латунь, витраж) на киберпанковом двигателе (моноширинное
   служебное, дорожка платы, свечения). Пара нужна потому, что правило набора
   требует от кнопки показывать то, чего не показывает другая, а «тёмная с
   загадкой» и «светлая с загадкой» — разные ответы на один вопрос читателя.

   Мера у них та же, что у «Бумаги» и «Графита»: читаемость, а не соответствие
   НГС. Числа проверены как контраст к --card (#12161d):
     --ink  #d8d3c6 → 11,4:1
     --meta #8b909b → 4,6:1  (у НГС на этом месте 2,85:1)
     --link #a99cf0 → 6,8:1
     --brass #c2a061 → 6,4:1 — на --nav (#0e1117) 7,6:1
   У светлой сестры считано к --card (#f7f3ea):
     --ink #23262e → 13,2:1, --meta #6e6a62 → 5,3:1, --link #5b4bb5 → 7,0:1.

   Четыре имени заведены сверх общего набора, и своих значений у остальных
   палитр для них нет — значит всякое их употребление обязано нести fallback,
   иначе правило, случайно вышедшее за пределы темы, подсветит «Бумагу»
   фиолетовым:
     --brass — латунь: рамка зеркала, линия над карточкой, капители служебных
               заголовков. Второй акцент, «средневековый».
     --glow  — цвет свечения. Живёт только в box-shadow/text-shadow.
     --pane, --pane2 — грани витража в фоне шапки, две прозрачности. */
:root[data-theme="grimoire"] {
  color-scheme: dark;
  --page: #0b0e13;
  --card: #12161d;
  --ink: #d8d3c6;
  --meta: #8b909b;
  --rule: #252d3a;
  --link: #a99cf0;
  --male: #74b6e6;
  --female: #e98ea6;
  --anon: #a9aeb8;
  --nav: #0e1117;
  --nav-ink: #e6e1d4;
  --accent: #9d8fe0;
  --btn: #181d26;
  --hl: #1d1a2e;
  --bb-red: #ef6f70;
  --bb-green: #7fbf5a;
  --bb-blue: #6fadde;
  --bb-purple: #b48ae6;
  --bb-orange: #e39a45;
  --bb-pink: #f0a0c0;
  --bb-cyan: #5fc9c9;
  --bb-brown: #c08a5e;
  --bb-yellow: #d8c455;
  --bb-grey: #9aa3ab;
  --sil-fade: .72;
  --brass: #c2a061;
  --glow: rgba(157, 143, 224, .45);
  --pane: rgba(157, 143, 224, .07);
  --pane2: rgba(194, 160, 97, .06);
}

/* «Витраж» — светлая сестра: тёплый пергамент вместо экрана, чернильная шапка,
   тот же фиолетовый акцент, приглушённый до читаемого на светлом. Цвета
   разметки наследуются от голого :root намеренно — они и рассчитаны на светлый
   лист, повторять их здесь значило бы завести второе место, где их правят. */
:root[data-theme="stained"] {
  color-scheme: light;
  --page: #e6e1d6;
  --card: #f7f3ea;
  --ink: #23262e;
  --meta: #6e6a62;
  --rule: #d6cebd;
  --link: #5b4bb5;
  --male: #2b6f9e;
  --female: #b23a63;
  --anon: #5f5a52;
  --nav: #1c1a24;
  --nav-ink: #ece6d8;
  --accent: #5b4bb5;
  --btn: #efe9dc;
  --hl: #ece4f5;
  --brass: #8c6b2a;
  --glow: rgba(91, 75, 181, .3);
  --pane: rgba(157, 143, 224, .12);
  --pane2: rgba(194, 160, 97, .1);
}

/* Шрифты пары. Заголовки — антиква (фасад), служебное — моноширинное
   (двигатель); тело НЕ трогается ни в одной теме: заметку в 600 реплик читают
   телом, а не заголовками, и 14px Arial остаётся как было.

   Файлы наши, с нашего домена (см. assets.go): чужой шрифтовый хост не пускает
   CSP, да и запрос к нему рассказал бы постороннему сервису, кто открыл
   страницу. @font-face стои́т вне блоков тем — правило это описание файла, а не
   палитра, — но качается шрифт только тогда, когда им что-то нарисовано. */
@font-face {
  font-family: "Grim Serif";
  src: url("/assets/font/ebgaramond-500.106fa66d.woff2") format("woff2");
  font-weight: 500;
  font-display: swap;
}
@font-face {
  font-family: "Grim Mono";
  src: url("/assets/font/plexmono-400.9125ba43.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

/* Подвал прижат к низу окна. На короткой странице — а самая частая короткая
   это заметка без комментариев — он иначе повисает посередине, и под ним
   остаётся полэкрана пустоты, будто страница не догрузилась.
   Тянется при этом СОДЕРЖИМОЕ (.main), а не распорка: карточка с текстом
   доходит до подвала, и низ страницы выглядит законченным, а не оборванным.
   dvh — ради телефонов, где адресная строка ест часть vh; vh оставлен ниже
   первым, он и есть запасной вариант для браузеров без dvh. */
body {
  margin: 0;
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--page);
  color: var(--ink);
  font: 14px/1.45 Arial, "Helvetica Neue", Helvetica, sans-serif;
}

a { color: var(--link); text-decoration: none; }
a:hover { text-decoration: underline; }

.wrap { max-width: 910px; margin: 0 auto; padding: 0 10px; }

/* ---------------------------------------------------------------- шапка */

/* Шапка ЛИПКАЯ (23.08.2026, просьба владельца: «из треда неудобно мотать наверх
   к меню, чтобы куда-то перейти»). Тред у нас показывается ЦЕЛИКОМ — 891 реплика
   на самой длинной заметке зеркала, — и дорога до навигации из его середины
   измеряется экранами.
   Липкость ничего не стоит: шапка это одна строка в 44px, своё место в потоке
   она сохраняет, а `position: sticky` остаётся позиционированным элементом —
   выпадающее меню участника, привязанное к ней, стоит где стояло. */
.top { background: var(--nav); position: sticky; top: 0; z-index: 20; }
/* Якорь под липкой шапкой. Без этого отступа реплика, на которую пришли по
   ссылке «#», встаёт ПОД шапкой — а именно её человек и шёл читать. */
.c, .cttl, .notebox, .replyrow { scroll-margin-top: 60px; }
/* flex-wrap обязателен: без него на узком экране вкладки шапки наезжают на
   название, а «Вход» уезжает за край (жалоба 18.08.2026). */
.topin { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 20px; min-height: 44px; padding: 5px 0; }
/* Название площадки — ЗНАКОМ и словом. Знак стоит всегда, слово уходит на узком
   экране (правило при 460px ниже): «Зазеркалье» занимает там четверть строки
   шапки. Знак это зеркало — рамка с бликом; называет он себя сам, title и
   aria-label стоят на ссылке. */
.brand {
  display: inline-flex; align-items: center; gap: 8px;
  color: var(--nav-ink); font-size: 17px; font-weight: 700;
}
.brand:hover { color: var(--nav-ink); text-decoration: none; }
/* Знак не ужимается вместе со строкой: он и есть то последнее, что от названия
   остаётся. */
.brand .mark { flex: none; }
.tab {
  color: var(--nav-ink); background: rgba(255, 255, 255, .18);
  padding: 5px 14px; border-radius: 3px; font-size: 14px;
}

/* Пары шапки живут ОДНИМ блоком с перегородкой: «Лента / Справка» — один
   переключатель разделов, колокольчик с меню участника — один угол «про меня».
   Порознь они читались как четыре несвязанные кнопки (жалоба владельца
   23.08.2026).

   Фон красит сам блок, а плашки внутри становятся прозрачными, поэтому
   перегородка — одна линия, а не стык двух прямоугольников. overflow: hidden
   тут нельзя: под .acct выпадает меню, и блок обрезал бы его. Значит скругление
   ставится КРАЙНИМ элементам, а красит их та же поверхность, что и hover. */
.grp {
  display: flex; align-items: stretch;
  background: rgba(255, 255, 255, .18); border-radius: 3px;
}
.grp > *, .grp > .acct > .acctbtn { background: none; border-radius: 0; }
.grp > * + * { border-left: 1px solid rgba(255, 255, 255, .22); }
.grp > :first-child, .grp > :first-child > .acctbtn { border-radius: 3px 0 0 3px; }
.grp > :last-child, .grp > :last-child > .acctbtn { border-radius: 0 3px 3px 0; }
.grp > a:hover, .grp > .acct > .acctbtn:hover, .grp > .acct[open] > .acctbtn {
  background: rgba(255, 255, 255, .2);
}
/* Автоматическое поле переезжает на блок целиком: у двух элементов подряд их
   было бы два, и flex поделил бы свободное место пополам — угол уехал бы в
   середину шапки. */
.uctl { margin-left: auto; }
.grp .acct, .grp .bell { margin-left: 0; }

/* Вход — в правом верхнем углу, как на НГС. margin-left:auto прижимает его к
 * краю без распорок; на узком экране flex перенесёт его строкой ниже, а не
 * выдавит наружу. Ссылка и кнопка выглядят одинаково: выход обязан быть POST
 * (GET-ссылку браузер нажимает сам в префетче), но человеку это знать незачем. */
.acct { position: relative; margin-left: auto; }
.acct form { margin: 0; }

/* Меню участника: значок в углу, под ним список. Раскрывает его сам браузер
   (details/summary), поэтому оно работает и без скрипта.
   Треугольник-маркер снимаем: на НГС в этом углу только значок. */
.acctbtn {
  display: flex; align-items: center; gap: 7px; cursor: pointer;
  color: var(--nav-ink); background: rgba(255, 255, 255, .18);
  padding: 5px 12px; border-radius: 3px; list-style: none;
}
.acctbtn::-webkit-details-marker { display: none; }
.acctbtn:hover, .acct[open] .acctbtn { background: rgba(255, 255, 255, .3); }
.acctbtn .who { font-weight: 600; }

/* Список выпадает ПОД кнопкой и поверх страницы, прижатый к правому краю —
   как на НГС. z-index нужен вместе с ним и на самой шапке: без него список
   уезжает под первую же заметку. */
.acctmenu {
  position: absolute; top: calc(100% + 4px); right: 0; z-index: 30;
  min-width: 190px; background: var(--card);
  border: 1px solid var(--rule); border-radius: 3px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .18);
  /* Потолок и прокрутка — с шестой темой (12.09.2026): кнопок оформления стало
     шесть, то есть три ряда, и у администратора меню доросло до ~355px. В
     портрет телефона это влезает, в АЛЬБОМНУЮ ориентацию — нет, а обрезанное
     меню не даёт добраться до «Выхода». dvh, а не vh, по той же причине, что у
     body: адресная строка ест часть экрана. */
  max-height: calc(100dvh - 60px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
/* :not(.tbtn) — не придирка. Пункт меню занимает всю ширину и несёт
   разделитель сверху; кнопки оформления внутри одного пункта — ряд, и под этим
   правилом они вытянулись бы в шесть полос с шестью линиями, а hover пункта
   стёр бы подсветку выбранной темы. Исключение стоит здесь, чтобы весь набор
   правил про .tbtn остался прежним и общим. */
.acctmenu a, .acctmenu button:not(.tbtn) {
  display: block; width: 100%; text-align: left; font: inherit;
  padding: 12px 18px; color: var(--ink); background: none;
  border: 0; border-top: 1px solid var(--rule); cursor: pointer;
}
.acctmenu > :first-child, .acctmenu > form:first-child button { border-top: 0; }
.acctmenu a:hover, .acctmenu button:not(.tbtn):hover { background: var(--btn); text-decoration: none; }

/* Оформление — пункт меню участника, а не подвал (решение владельца
   23.08.2026): тема это настройка «про меня». Ряд кнопок — ОДИН пункт, поэтому
   разделитель ставится блоку, а не каждой кнопке; две колонки, потому что в
   строку не встают ни четыре имени, ни шесть — ширину задаёт выпадашка, а не
   набор. Шесть кнопок ложатся тремя рядами, и порядок набора (web.themes) для
   них не случаен: слева светлые, справа тёмные. */
.themes {
  display: grid; grid-template-columns: 1fr 1fr; gap: 6px;
  padding: 10px 18px 12px; border-top: 1px solid var(--rule);
}
.themes .lbl { grid-column: 1 / -1; color: var(--meta); font-size: 12px; }
.tbtn {
  font: inherit; font-size: 12px; padding: 5px 8px; cursor: pointer; text-align: center;
  background: var(--btn); color: var(--ink); border: 1px solid var(--rule); border-radius: 3px;
}
.tbtn:hover { border-color: var(--meta); }
/* Подсвечена та тема, которая сейчас на экране, и называет её РАЗМЕТКА:
   класс on ставит шаблон. Пока умолчания не было, «сейчас на экране» знал
   один браузер — сервер prefers-color-scheme не видит, — и ту же подсветку
   приходилось повторять отдельными правилами для страницы без атрибута. С
   темой по умолчанию (30.08.2026) сервер знает её всегда, и повторять стало
   нечего. */
.tbtn.on { background: var(--accent); border-color: var(--accent); color: #fff; }

.enter {
  display: inline-block; font: inherit; font-size: 14px; line-height: 1.45;
  color: var(--nav-ink); background: rgba(255, 255, 255, .18);
  padding: 5px 14px; border: 0; border-radius: 3px; cursor: pointer;
}
.enter:hover {
  color: var(--nav-ink); text-decoration: none; background: rgba(255, 255, 255, .3);
}

/* Колокольчик — единственный элемент шапки, которого на НГС не было вовсе, и
 * поэтому единственный, который приходится объяснять (правило Ш5з о собственных
 * метках: title здесь, слова в /help).
 *
 * margin-left:auto переезжает на него, а у соседнего .acct снимается: два
 * автоматических поля во flex делят свободное место ПОПОЛАМ, и колокольчик уехал
 * бы в середину шапки. Когда колокольчика нет (гость, шина не подключена),
 * правило не срабатывает и .acct прижимается к краю сам, как раньше.
 *
 * Значок стоит и при нуле: элемент, пропадающий с шапки, дёргает вёрстку на
 * каждом переходе. */
/* Счётчик реплик в шапке — спутник липкости: до заголовка «Комментарии N» из
   середины треда уже не домотать. Значком с числом, а не словом: ширина строки
   на телефоне кончается раньше всего, а объяснение живёт в title. */
.hcount {
  display: flex; align-items: center; gap: 5px;
  color: var(--nav-ink); background: rgba(255, 255, 255, .18);
  padding: 5px 10px; border-radius: 3px; font-size: 13px; font-weight: 700;
}
.hcount:hover { color: var(--nav-ink); text-decoration: none; background: rgba(255, 255, 255, .3); }

.bell {
  position: relative; display: flex; align-items: center; margin-left: auto;
  color: var(--nav-ink); background: rgba(255, 255, 255, .18);
  padding: 5px 12px; border-radius: 3px;
}
.bell:hover { color: var(--nav-ink); text-decoration: none; background: rgba(255, 255, 255, .3); }
.bell + .acct { margin-left: 0; }
/* Число непрочитанного. Кружок накладывается на значок, а не встаёт рядом:
   ширина шапки на телефоне и без того спорная. */
.bell .cnt {
  position: absolute; top: 0; right: 2px;
  min-width: 15px; padding: 0 3px; border-radius: 8px;
  background: var(--accent); color: #fff;
  font-size: 10px; line-height: 15px; text-align: center; font-weight: 700;
}

/* Страница «События». Список, а не таблица: строки разной длины, и читают их
   сверху вниз, а не сравнивают между собой. */
.evs { list-style: none; margin: 0; padding: 0; }
.ev {
  padding: 12px 0 14px; border-bottom: 1px solid var(--rule);
}
.ev:last-child { border-bottom: 0; }
/* Непрочитанное помечено полосой слева, а не фоном: фон на карточке с текстом
   спорит с самим текстом, а полоса видна и в тёмной палитре. */
.ev.new { border-left: 3px solid var(--accent); padding-left: 11px; }
.evt { font-weight: 600; }
.evx { margin: 6px 0 0; color: var(--ink); }
.evr { margin: 6px 0 0; color: var(--meta); font-size: 12px; }
.evx.gone { color: var(--meta); font-style: italic; }
.readall { margin: 0 0 14px; }

/* Письма (эпик L). Вид взят у событий, а не придуман заново: это такой же
   список строк с непрочитанным, и разнобой между двумя личными разделами
   читатель заметил бы раньше, чем объяснил. Пузырей и колонок здесь нет
   намеренно — переписка двоих остаётся страницей площадки, а не чатом. */
.dlgs, .lts { list-style: none; margin: 0; padding: 0; }
.dlg {
  display: flex; gap: 10px;
  padding: 12px 0 14px; border-bottom: 1px solid var(--rule);
}
.dlg:last-child { border-bottom: 0; }
.dlg.new { border-left: 3px solid var(--accent); padding-left: 11px; }
/* Лицо в списке мельче авторского: строка здесь про РАЗГОВОР, а не про
   человека, и стопка стопятидесятых съела бы список — тот же довод, по которому
   в мордоленте 70, а не 100. */
.dlgava .ava { width: 48px; height: 48px; }
/* min-width: 0 обязателен: без него длинное слово в выдержке распирает
   flex-строку и ломает ширину всей страницы на телефоне. */
.dlgb { flex: 1; min-width: 0; }
.dlgtop { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.dlgwho { font-weight: 600; }
.dlgn {
  background: var(--accent); color: #fff; border-radius: 9px;
  padding: 0 6px; font-size: 12px; font-weight: 600;
}
.dlgx, .ltx { margin: 4px 0 0; overflow-wrap: anywhere; }
.dlgx.gone, .ltx.gone { color: var(--meta); font-style: italic; }
.dlghead {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin: 0 0 14px;
}
.dlgacts { display: flex; gap: 8px; flex-wrap: wrap; }
/* Закрытая переписка на «моей странице»: ряд с кнопкой справа — тот же, что у
   дверей входа и у согласий. */
.blocked, .follows { list-style: none; margin: 0; padding: 0; }
.blocked li, .follows li {
  border-top: 1px solid var(--rule); padding: 12px 0;
  display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
}
.blocked form, .follows form { margin-left: auto; }
/* Подписки (эпик F, F7): тот же ряд «строка — действие справа». Начало заметки
   в списке может быть длинным, и без min-width: 0 оно распирало бы ряд на
   телефоне так же, как выдержка письма. */
.follows li > a { flex: 1 1 14rem; min-width: 0; overflow-wrap: anywhere; }
/* Кнопка «следить» под заголовком комментариев и на странице человека —
   маленькая и приглушённая: это не призыв, а возможность. */
.follow { margin: 4px 0 10px; }
.follow button { color: var(--meta); }
.follow button.on { color: var(--ink); }
.evsettings { margin: 0 0 14px; }
.mutes { display: flex; flex-direction: column; gap: 8px; margin: 0 0 12px; }
.mutes .chk { font-size: 14px; color: var(--ink); }
.wordadd { display: flex; gap: 8px; flex-wrap: wrap; margin: 12px 0 0; }
.wordadd input[type=text] { font: inherit; padding: 5px 8px; min-width: 12rem; }
/* «Пожаловаться» у письма — с краю и приглушённо: это дверь на крайний случай, а
   не действие, которым пользуются каждый день. */
.lth .ltrep { margin-left: auto; color: var(--meta); }

/* РАЗГОВОР ПРОКРУЧИВАЕТСЯ ВНУТРИ СЕБЯ (12.09.2026, жалоба владельца: «когда в
   личной переписке много сообщений, страница становится простынёй»). Писем на
   страницу полсотни, и это несколько экранов: форма ответа уезжала за нижний
   край разговора — чтобы написать слово, надо было промотать всю переписку, а
   промотав до формы, потерять из вида то, на что отвечаешь.
   Потолок — тот же 60vh, что у главной иллюстрации заметки, и по тому же
   доводу: одна вещь на странице не съедает экран целиком. Именно max-height, а
   не height: у разговора в три письма рамка обнимает три письма, пустой высоты
   не бывает, и прокрутки у него не появляется вовсе.
   Рамка не украшение: без неё письмо, обрезанное на полуслове нижним краем,
   читается как поломка вёрстки, — а overscroll-behavior: contain держит
   страницу на месте, когда разговор дочитан до конца и палец продолжает тянуть.
   Модификатор, а не сам `.lts`, потому что список писем рисуют ДВОЕ: здесь
   разговор, а на странице жалобы — цитата в одно письмо, которой прокручиваться
   некуда и незачем.
   Прокрутку В КОНЕЦ ставит скрипт (app.js): свойства «открой блок с последней
   строки» у браузеров нет, и без JS разговор открывается началом страницы —
   ровно тем, что человек видел до этой правки. Сдвиг от дописанного письма
   разбирает сам браузер (overflow-anchor: auto по умолчанию), поэтому его здесь
   никто не отключает — то же правило, что у ленты и у треда.

   СЦЕПКУ ПРОКРУТОК ОТНИМАТЬ НЕЛЬЗЯ, и это оплачено в тот же вечер, что и сама
   рамка (жалоба владельца 12.09.2026: «по полю с сообщениями вниз не
   проскроллить, нужно попасть по тонкой полоске страницы»). Стояло здесь
   overscroll-behavior: contain с доводом «держит страницу на месте, когда
   разговор дочитан до конца и палец продолжает тянуть», — а скрипт открывает
   рамку СРАЗУ на последнем письме, то есть тянуть внутри неё уже некуда с
   первого жеста. Выходило, что единственная дорога из рамки к форме ответа —
   узкие поля страницы по краям, и та же правка, что берегла человека от лишнего
   рывка, запирала его в разговоре. Умолчание браузера (сцепка) эту дорогу
   возвращает: дочитал разговор — тянешь дальше, и едет страница. */
.lts.talk {
  max-height: 60vh; overflow-y: auto;
  border: 1px solid var(--rule); border-radius: 3px; padding: 0 12px;
}
/* Зазор под формой ответа, когда страницу подводят к ней скриптом (app.js,
   talkToEnd): без него кнопка отправки встаёт вплотную к нижнему краю экрана,
   а на телефоне это ещё и полоса системных кнопок. Свойство то же самое, каким
   якоря отбиваются от липкой шапки, только с другой стороны. */
.wbox { scroll-margin-bottom: 12px; }

/* КОД ЛИЧНОГО КОНТАКТА в справке (qr.go): картинка вместо строки в семьдесят
   шесть знаков. Белое поле и тихая зона у него СВОИ, внутри картинки, — код
   обязан быть чёрным по белому при любой из шести палитр, инвертированный
   читают не все камеры. Рамка отделяет его белый квадрат от светлой страницы,
   иначе на «Бумаге» он висит в пустоте без границ.
   Ссылка БЛОЧНАЯ (шириной по картинке), а не строчная: строчной она встаёт в
   поток строки и утаскивает её вверх — код тогда рисуется выше собственного
   пункта, а рядом с соседним зияет пустая строка в его высоту (поймано снимком
   12.09.2026). line-height: 0 — чтобы под картинкой не оставалось полстроки
   пустоты: браузер добавляет её под хвосты букв, которых тут нет. */
.qr { display: block; width: max-content; margin: 8px 0 2px; line-height: 0; }
.qr svg { display: block; border: 1px solid var(--rule); border-radius: 3px; }
.lt { padding: 10px 0 12px; border-bottom: 1px solid var(--rule); }
.lt:last-child { border-bottom: 0; }
/* СВОЁ письмо отмечено полосой слева — той же приметой, что непрочитанный
   повод, только серой: это не «посмотри сюда», а «это сказал ты». */
.lt.mine { border-left: 3px solid var(--rule); padding-left: 11px; }
.lth { display: flex; gap: 6px; align-items: baseline; color: var(--meta); font-size: 12px; }
.ltwho { font-weight: 600; color: var(--ink); }
/* Число писем у пункта меню. Не кружок-значок: пункт и без того строка текста,
   а второй значок в меню спорил бы с колокольчиком в шапке. */
.acctmenu .mn { color: var(--accent); font-weight: 600; }
.uact { margin: 8px 0 0; }

/* Живое дописывание (fresh.go, app.js). Новая реплика или заметка появляется
   сама, без перезагрузки и без нажатий, — и отличается от соседних ровно
   одним: короткой подсветкой, которая гаснет.
   Цвет тот же --hl, что и у подсветки по анкору, а не свой: «на это стоит
   посмотреть» на странице значит одно и то же, откуда бы указание ни пришло.
   Гасит подсветку САМА анимация (both), поэтому скрипту не нужен таймер, а
   классу — снятие: свою работу он к тому времени уже сделал.
   Сдвиг страницы, когда строка встаёт ВЫШЕ прочитанного, разбирает браузер сам
   (overflow-anchor: auto по умолчанию) — и именно поэтому его здесь никто не
   отключает. */
@keyframes fresh-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}
@keyframes fresh-glow {
  from { background-color: var(--hl); }
  to   { background-color: transparent; }
}
.c.fresh, .note.fresh, .lt.fresh { animation: fresh-in .28s ease-out, fresh-glow 6s ease-out .28s both; }

/* Кому движение мешает — тому только подсветка: она не движение, а указание,
   и без неё новая строка появлялась бы вовсе незаметно. */
@media (prefers-reduced-motion: reduce) {
  .c.fresh, .note.fresh, .lt.fresh { animation: fresh-glow 6s linear both; }
}

/* ПЕРЕЕЗД: строка стояла в угаданной ветке, обход мобильной версии заменил
   догадку настоящим ребром, и она встала на своё место (X-Fresh-Moved, app.js).
   Метка отличается от `fresh` намеренно: там появилось НОВОЕ и подсветка зовёт
   прочесть, здесь текст прежний, и вопрос у читателя другой — «почему вокруг
   всё поменялось». Поэтому не заливка, а рамка, и гаснет она вчетверо быстрее:
   строку человек уже читал, звать его к ней второй раз незачем.
   Движения тут нет вовсе (одна рамка), поэтому и ветки prefers-reduced-motion
   у неё нет: гасить нечего. */
@keyframes moved-mark {
  from { box-shadow: inset 0 0 0 2px var(--accent); }
  to   { box-shadow: inset 0 0 0 2px transparent; }
}
.c.moved { animation: moved-mark 1.6s ease-out both; }


/* Ширина карточки постоянная и от содержимого не зависит — как на НГС.
   width:100% здесь обязателен ВМЕСТЕ с `margin: … auto`: у flex-элемента с
   автоматическими полями поперёк оси растяжение отменяется, и карточка
   съёживается по содержимому. На ленте этого не видно (текст заметок и так
   шире 910px), а страница новой заметки схлопнулась в колонку 266px вокруг
   пустого поля ввода — по ширине textarea в 20 знаков (экран владельца,
   18.08.2026). */
.main {
  flex: 1 0 auto; width: 100%; max-width: 910px; margin: 10px auto;
  background: var(--card); padding: 16px 20px 28px;
}
.ttl { font-size: 30px; font-weight: 400; margin: 0 0 16px; color: var(--ink); }

/* ---------------------------------------------------------------- лента */

.notes { list-style: none; margin: 0; padding: 0; }

.note {
  border-top: 1px solid var(--rule);
  padding: 20px 0 15px;
  min-height: 135px;
  display: flex;
  gap: 20px;
  position: relative;
}
.notes .note:last-child { border-bottom: 1px solid var(--rule); }
.notebox { border-top: 0; padding-top: 6px; }
/* Под переключателем вида текст не идёт: на НГС он тоже стоит в своём углу.
   Резерв 76px — под ДВЕ ИКОНКИ; словами переключатель требовал 150px, и на
   узком экране от колонки текста не оставалось ничего. */
.nblock { position: relative; }
.notebox .nbody { padding-right: 76px; }

/* Колонка автора: 100px в ширину, ник под аватаром и по центру.

   ДЛИННЫЙ НИК ПЕРЕНОСИТСЯ ПО СЛОГАМ (19.09.2026, жалоба владельца на
   «Дискурсмонге / р»): прежде его рвал только overflow-wrap — посреди слога и
   с одной буквой на второй строке. Слоги знает браузер (lang="ru" стоит на
   <html>), у Chrome на Windows словарь докачивается сам, а где его нет —
   остаётся прежний разрыв где попало, то есть хуже, чем было, не станет.
   Порог 6 3 3 оплачен замером: без него «Хатуль мадан» выходило «ма- / дан» —
   короткое слово, которому целиком место на следующей строке. Стоит всё это
   на колонке, а не на нике, — у ника своей коробки больше нет (ниже). */
.author {
  width: 100px; flex: 0 0 100px; text-align: center;
  overflow-wrap: anywhere; -webkit-hyphens: auto; hyphens: auto;
  hyphenate-limit-chars: 6 3 3;
}
.ava {
  width: 100px; height: 100px; display: block;
  object-fit: cover; border: 1px solid var(--rule); border-radius: 3px;
  background: var(--btn);
}
/* Силуэт — это ОТСУТСТВИЕ фото, и светить он не должен: фото у площадки далеко
   не у всех (байты приносило зеркало, у теней архива их нет), так что в тёмной
   теме страница иначе превращается в решётку светлых квадратов ярче собственного
   текста. Гасится он только там: --sil-fade объявлен в тёмных палитрах, у
   светлых его нет вовсе, и картинка сайта показывается ровно такой, какая она
   на НГС. Настоящее фото не трогаем никогда — оно содержание, а не фон. */
.ava.sil { opacity: var(--sil-fade, 1); }
/* Ник в колонке — СТРОЧНЫЙ, без своей коробки, и зазор под аватаром даёт сам
   аватар. До 19.09.2026 ник был inline-block ради этих пяти пикселей, и за это
   платила запятая перед возрастом: вокруг атомарного элемента браузер обязан
   допускать перенос (CSS Text, §5.1 — даже там, где знак его запрещает), и
   «, 45 лет» уезжала на новую строку, начиная её с запятой. У строчного ника
   запятая прилипает к последней букве сама — перед запятой строку не рвут. */
.author > .ava:not(:last-child) { margin-bottom: 5px; }

/* Ник полужирный, 14px — .lv-note .lv-people__nickname { font-weight:600 }
   в стилях сайта. Он тут главная опора взгляда: по нику ищут своих в треде. */
.nick { color: var(--link); font-weight: 600; }
.nick.anon { color: var(--anon); }
/* Цвет ника по полу — как на НГС. Пол приехал даром вместе с обходом дерева
   ответов (Ш6): он стоит в разметке десктопной страницы комментариев, обход
   анкет для него не нужен. Неизвестен — ник остаётся ссылочно-синим. */
.nick._female { color: var(--female); }
.nick._male { color: var(--male); }

/* Возраст после ника — как в мобильной версии НГС: «Ягода, 48 лет». Цветом он
   служебный, а не второй ник: искать своих в треде по-прежнему по имени.

   Колонка автора узкая, и «48» от «лет» не отрывается НЕРАЗРЫВНЫМ ПРОБЕЛОМ
   (ageWords): врозь они читаются как опечатка. nowrap на всём span здесь стоял
   до 19.09.2026 и был неверен — запятая лежит в том же span, и строку можно
   было рвать только ПЕРЕД ней. Теперь рвётся после «, »: запятая остаётся при
   нике, возраст целиком уходит на следующую строку. */
.age { color: var(--meta); font-weight: 400; }
/* В заголовке страницы участника он приписка к имени, а не второе имя. */
.ttl .age { font-size: .6em; }

.nbody { flex: 1 1 auto; min-width: 0; }
.text { overflow-wrap: anywhere; }
.text p { margin: 0 0 .8em; }
.text p:last-child { margin-bottom: 0; }

/* Разметка ранних лет НГС: [b], [i], [u], [s], [color=…] в текстах архива.
   Цвет приезжает КЛАССОМ, а не атрибутом style — CSP без 'unsafe-inline'
   его запрещает (та же причина, что у отступа ветки d1…d12).
   В светлой теме стоит ровно то ключевое слово, которое назвал автор: сайт
   был светлым, и «розовый» там был так же еле виден, как здесь, — чужой
   выбор мы не поправляем. В тёмной те же цвета подняты по светлоте: на
   #1d2126 blue и purple не читаются вовсе. */
.bb-red { color: var(--bb-red); }
.bb-green { color: var(--bb-green); }
.bb-blue { color: var(--bb-blue); }
.bb-purple { color: var(--bb-purple); }
.bb-orange { color: var(--bb-orange); }
.bb-pink { color: var(--bb-pink); }
.bb-cyan { color: var(--bb-cyan); }
.bb-brown { color: var(--bb-brown); }
.bb-yellow { color: var(--bb-yellow); }
.bb-grey { color: var(--bb-grey); }
.bb-white { color: var(--bb-white); }
.bb-black { color: var(--bb-black); }

/* Смайлы НГС в тексте (:::popcorn:::). Размер у каждого свой (от 15×15 до
   100×20) и приезжает атрибутами width/height из самого файла, поэтому стилю
   остаётся немного: не дать картинке распереть узкий экран. Выравнивание
   оставлено браузерное, по базовой линии, — таким его давал <img> без класса,
   а проверить, как ровно смайл стоял на НГС, больше негде: сайт эти коды не
   показывает с 2017 года. */
.sm { max-width: 100%; height: auto; }

/* ---------------------------------------------------------------- реакции */

/* Кнопки-фишки под заметкой и под репликой. Смайлы в наборе разнокалиберные
   (15×15 … 100×20), поэтому в фишке задана высота, а ширина считается: иначе
   ряд фишек прыгал бы по высоте вслед за картинками. */
/* ОДНОЙ СТРОКОЙ с «Ответить» и полоской модератора — inline-flex, а не flex
   (жалоба владельца 02.09.2026: «„+“ на добавление эмоции занимает целую
   лишнюю строку»). Под репликой без реакций в этом ряду стоит один «плюс», и
   блочный флексбокс отдавал ему всю ширину карточки. На телефоне правило
   стояло с 23.08.2026 и по тому же доводу; десктоп получил его последним,
   потому что там ширины хватало на всё, кроме здравого смысла. */
.rx { display: inline-flex; vertical-align: middle; flex-wrap: wrap; gap: 4px; align-items: center; margin: 8px 12px 0 0; }
.rxb {
  display: inline-flex; align-items: center; gap: 4px; padding: 1px 7px;
  border: 1px solid var(--rule); border-radius: 11px; background: var(--btn);
  color: var(--ink); font: inherit; font-size: 12px; line-height: 20px; cursor: pointer;
}
.rxb .sm { height: 18px; width: auto; }
/* Реакции с 15.09.2026 — эмодзи: символу задаётся кегль, а не высота, и строка
   фишки остаётся той же двадцаткой. Старые смайлы (.sm) живут только в старых
   поводах на странице событий. */
.rxb .em { font-size: 16px; line-height: 20px; }
.rxb b { font-weight: 600; }
/* Своя реакция подсвечена рамкой, а не заливкой: заливка спорит со смайлом. */
.rxb.mine { border-color: var(--accent); }
span.rxb { cursor: default; }
.rxadd {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 22px; border: 1px solid var(--rule); border-radius: 11px;
  background: var(--btn); color: var(--meta); text-decoration: none; font-size: 14px;
}
.rxpick { display: inline-flex; flex-wrap: wrap; gap: 4px; align-items: center; }
/* «Отмена» в раскрытом ряду — тот же кружок, что у «+»: это его пара, и закрыть
   ряд надо там же, где его открыли. */
.rxclose {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 22px; border: 1px solid var(--rule); border-radius: 11px;
  background: var(--btn); color: var(--meta); text-decoration: none; font-size: 12px;
}
.rxclose:hover, .rxadd:hover { border-color: var(--meta); text-decoration: none; }
/* Пока сервер отвечает, коробка приглушена: второе нажатие в этот момент скрипт
   не отправляет, и видно это должно быть, а не угадываться. */
form.rx[data-busy] { opacity: .55; }

/* Выбиралка смайлов под формой. Закрыта по умолчанию: пришли писать, а не
   разглядывать три сотни картинок. Сетка на flex-wrap, потому что знаки
   разнокалиберные (от 12×25 до 100×20) и колонка по самому широкому оставила
   бы половину панели пустой. */
.smbox { margin: 6px 0 0; }
.smbox summary { cursor: pointer; color: var(--link); font-size: 13px; }
/* Наборы внутри панели — вкладки В РЯД, а не столбиком: вопрос у них один
   («какой набор»), и три строки одна под другой читались бы как три панели.
   Каждый набор — свой details, а сетка открытого живёт ВНУТРИ него, поэтому
   ряд собирается так: details растворяется (display: contents), его ярлык и
   содержимое становятся соседями во флекс-строке, и содержимое уходит в конец
   (order) на всю ширину. Растворять details умеют только браузеры, у которых
   есть ::details-content, — у прочих вкладки стоят столбиком над открытой
   сеткой, и это рабочий вид, а не поломка. */
@supports selector(::details-content) {
  .smpacks { display: flex; flex-wrap: wrap; align-items: center; }
  .smpack { display: contents; }
  .smpack::details-content { order: 1; flex-basis: 100%; }
}
.smpack > summary {
  display: inline-block; margin: 8px 6px 0 0; padding: 2px 10px;
  border: 1px solid var(--rule); border-radius: 11px; background: var(--btn);
  color: var(--ink); font-size: 12px; list-style: none;
}
.smpack > summary::-webkit-details-marker { display: none; }
.smpack[open] > summary { border-color: var(--accent); color: var(--link); }
.smgrid {
  display: flex; flex-wrap: wrap; gap: 4px; align-items: center;
  margin-top: 8px; padding: 8px; border: 1px solid var(--rule); border-radius: 4px;
  background: var(--btn); max-height: 220px; overflow-y: auto;
}
/* Без скрипта это справочник: картинка и код рядом, код набирается руками.
   Со скриптом код прячется, остаётся кнопка. */
.smgrid .smi {
  display: inline-flex; align-items: center; gap: 3px; padding: 2px 4px;
  border: 1px solid transparent; border-radius: 4px; font-size: 11px; color: var(--meta);
}
.smgrid button.smi { background: var(--card); border-color: var(--rule); cursor: pointer; }
.smgrid button.smi:hover { border-color: var(--accent); }
.smgrid .smi .sm { height: 20px; width: auto; }
/* Эмодзи — символ, а не картинка: ему задаётся кегль, а не высота, и ставится
   он той же двадцаткой, что картинки соседних наборов. */
.smgrid .smi .em { font-size: 20px; line-height: 20px; }
/* Цветной шрифт эмодзи назван прямо: у тем со своей гарнитурой («Гримуар»)
   символ иначе мог бы взяться из неё — чёрно-белым контуром. */
.em { font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif; font-style: normal; }

/* Справочник разметки под той же кнопкой-раскладушкой. Список, а не сетка:
   строк пять, и в каждой две части — как набирают и что выйдет. Код набран
   моноширинным, чтобы скобки не сливались с образцом рядом. */
.mkref {
  margin: 8px 0 0; padding: 8px 8px 8px 24px; list-style: disc;
  border: 1px solid var(--rule); border-radius: 4px; background: var(--btn);
  font-size: 13px; color: var(--meta);
}
.mkref li { margin: 2px 0; }
/* Кнопки разметки (со скриптом). Сервер рисует их скрытыми, и [hidden] названо
   явно: display: flex сильнее правила из таблицы агента. */
.mkbtns {
  display: flex; flex-wrap: wrap; gap: 4px; margin-top: 8px; padding: 6px;
  border: 1px solid var(--rule); border-radius: 4px; background: var(--btn);
}
.mkbtns[hidden] { display: none; }
.mkb {
  min-width: 32px; height: 30px; padding: 0 8px; cursor: pointer;
  border: 1px solid var(--rule); border-radius: 4px; background: var(--card);
  color: var(--ink); font: inherit; font-size: 15px; line-height: 1;
}
.mkb:hover { border-color: var(--accent); }

/* «Смайлы», «Разметка» и кнопка отправки — ОДНОЙ СТРОКОЙ под полем (15.09.2026,
   жалоба владельца: «ни к чему такое расстояние между кнопкой и полем»).
   Раскрытая панель уходит ПОД строку на всю ширину, а не раздвигает её: details
   растворяется (display: contents), ярлык остаётся в строке, содержимое уходит
   в конец порядком — тот же приём, что у вкладок наборов смайлов. Кнопка
   прижата вправо (margin-left: auto) и на узком экране, не поместившись,
   переносится сама, оставаясь у правого края. Ярлыки выровнены по базовой
   линии надписи на кнопке, а не по середине: под кнопкой ещё подсказка
   клавиш, и середина пары ушла бы ниже кнопки.
   Без ::details-content строка не собирается, и форма стоит столбиком, как
   прежде, — рабочий вид, а не поломка. */
.wbar { display: flex; flex-direction: column; gap: 10px; }
@supports selector(::details-content) {
  .wbar { flex-direction: row; flex-wrap: wrap; align-items: baseline; column-gap: 18px; row-gap: 0; }
  .wbar > .smbox { display: contents; }
  .wbar > .smbox::details-content { order: 1; flex-basis: 100%; }
  .wbar > .mkbox::details-content { order: 2; }
  .wbar > .wact.send { margin-left: auto; column-gap: 0; }
}
.mkref code { font-size: 12px; color: var(--fg); }

/* ДИКТОВКА (эпик N, web/voice.go, app.js — voicePanel). В строке под полем
   рядом со «Смайлами» и «Разметкой», и на десктопе выглядит так же — ссылкой
   со словом: главное действие в строке одно, «Отправить». Идёт запись — слово
   и значок красные, значок становится квадратом «стоп», рядом бежит время.

   На телефоне кнопка другая (решение владельца 29.09.2026: «и вид, и жест»):
   значок 40×40 под палец, без слова — оно уходит в .sr-only-приём и остаётся
   именем кнопки для читалки. Её ДЕРЖАТ, пока говорят, поэтому touch-action:
   none (иначе браузер заберёт жест под прокрутку и пришлёт pointercancel), а
   выделение и системное меню долгого нажатия сняты.

   [hidden] назван явно: display у .vbox перебил бы атрибут, и кнопка без
   скрипта стояла бы на странице мёртвой. */
.vbox { display: inline-flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.vbox[hidden] { display: none; }
.vbtn {
  display: inline-flex; align-items: center; gap: 4px; padding: 0;
  border: 0; background: none; color: var(--link); font: inherit; font-size: 13px;
  cursor: pointer;
}
.vbtn .ico { width: 15px; height: 15px; }
.vbtn .vstop { display: none; }
.vbox.rec .vbtn { color: var(--female); }
.vbox.rec .vmic { display: none; }
.vbox.rec .vstop { display: inline-flex; }
.vbox.wait .vbtn { opacity: .6; cursor: progress; }
.vstat { font-size: 12px; color: var(--meta); }
.vbox.rec .vstat::before { content: "CF"; color: var(--female); margin-right: 4px; animation: vblink 1s steps(2, start) infinite; }
@keyframes vblink { to { visibility: hidden; } }
@media (prefers-reduced-motion: reduce) { .vbox.rec .vstat::before { animation: none; } }
.vconsent { margin: 14px 0 0; }
/* Строка состояния — СВОЕЙ строкой под рядом, а не рядом с кнопкой. Рядом она
   не помещалась на телефоне и уводила кнопку строкой ниже в тот самый миг,
   когда её держит палец (снимок 29.09.2026); на десктопе длинный отказ так же
   столкнул бы «Ответить». Поэтому .vbox растворяется в ряду, а состояние уходит
   в конец порядком — тот же приём, что у раскрытых панелей смайлов и разметки.
   :not([hidden]) — чтобы растворение не перебило скрытую кнопку. */
@supports selector(::details-content) {
  .wbar > .vbox:not([hidden]) { display: contents; }
  .wbar .vstat { order: 3; flex-basis: 100%; }
  .wbar .vstat:not(:empty) { margin-top: 6px; }
}
/* Телефонная ветка кнопки — в конце файла, своим блоком: первым блоком
   «max-width: 700px» обязан оставаться основной, его читают тесты верности
   вида (fidelity_test). */

/* Закреплённая заметка. Отличается меткой и полосой слева, а не цветом фона:
   лента и так пёстрая от аватаров, а закреплённых бывает не больше пяти. */
.pin {
  margin: 0 0 6px; font-size: 12px; font-weight: 600; color: var(--accent);
  text-transform: uppercase; letter-spacing: .04em;
}
.note.pinned { border-left: 3px solid var(--accent); padding-left: 10px; }

.nfoot { margin-top: 10px; color: var(--meta); font-size: 12px; }
/* Реакции идут ТОЙ ЖЕ СТРОКОЙ, что и дата (жалоба владельца 11.09.2026:
   «целая строка на „+“»), поэтому верхнего поля у них здесь нет: 8px были
   отступом от текста заметки, а внутри строки они разгоняли её высоту. Слева
   те же 14px, что у «Поправить», — соседи по ряду ходят одним шагом. */
.nfoot .rx { margin: 0 0 0 14px; }
.date { color: var(--meta); }
/* Время, снятое зеркалом, а не отданное сайтом: настоящего НГС не публикует.
   Пунктир — единственный видимый след, чтобы вид не расходился с правдой. */
.date._approx { border-bottom: 1px dotted var(--meta); cursor: help; }
/* «Исправлено» — метка площадки, которой на НГС не было: там заметку не правил
   никто. Стоит рядом с датой и тем же тихим цветом, потому что это уточнение к
   ней, а не отдельная новость; объясняет себя заголовком при наведении. */
.edited { margin-left: 10px; font-size: 11px; color: var(--meta); cursor: help; }

/* Метка происхождения (origin.go, правило Ш5з) — ЗНАЧКОМ, а не словом
   (решение владельца 26.08.2026): подпись печатала имя чужого сайта на каждой
   карточке ленты, а вопрос «откуда это» задают редко. Своё — знак самой
   площадки, чужое — два листа, знак копии; имя остаётся в заголовке и в
   спрятанной подписи для читалки. Тихим цветом, потому что спорить с датой за
   внимание ей по-прежнему незачем; пунктир снизу снят — подчёркивать больше
   нечего. Правил для ссылки здесь больше нет: с 27.08.2026 метка никуда не
   ведёт, и курсор у неё один — «help». */
/* Стоит она ПЕРВОЙ в строке и крупнее прежнего (решение владельца 02.09.2026).
   Прежние 14px выбирались, когда метка была припиской в хвосте: там ей и
   полагалось не спорить с датой. Первой в строке она отвечает на вопрос
   «откуда это» ДО того, как читатель начал читать запись, — и вопрос этот
   стоит того, чтобы знак был виден: стрелку внутри зеркала в 14 точек не
   разглядеть (замер владельца по живой странице).

   Отступ задаёт МЕСТО, а не сама метка: в строке даты у реплики это gap
   флексбокса, в подвале заметки — правое поле. Своего margin-left у метки
   больше нет вовсе — он был у хвостовой и в начале строки означал бы отступ
   от края карточки.

   vertical-align: middle, а не поправка в пикселях: 18-точечный знак рядом с
   12-точечной датой браузер ставит по половине x-height, и число подгонять не
   надо — оно уедет вслед за кеглем. */
.orig { color: var(--meta); display: inline-flex; align-items: center; vertical-align: middle; cursor: help; }
.orig .ico { width: 18px; height: 18px; }
.nfoot .orig { margin-right: 10px; }
.clink { margin-left: 18px; }
/* «Добавить комментарий» стоит той же строкой, что и ссылка на обсуждение, и
   тем же кеглем: это второй вход в тот же разговор, а не отдельное действие. */
.addc { margin-left: 18px; }
.cnt { font-size: 10px; color: var(--meta); }

/* ------------------------------------------------ иллюстрации заметки

   Главная крупно, остальные полосой под ней, любая — в просмотрщике во весь
   экран. Довод и устройство — в web/shots.go; здесь только вид.

   Полоса прижата ВЛЕВО и не растягивается: ряд из двух картинок, размазанный по
   ширине карточки, читался бы как две отдельные иллюстрации, а не как «вот
   остальные». Переносится она сама (flex-wrap) — числа картинок мы не знаем, а
   горизонтальной прокрутки здесь быть не должно: у мордоленты она оправдана
   десятками лиц, у заметки картинок единицы. */
.shots { margin-top: 12px; scroll-margin-top: 60px; }
.shots img { max-width: 100%; height: auto; display: block; border: 1px solid var(--rule); }
/* Потолок высоты у ГЛАВНОЙ, и появился он вместе с просмотрщиком: снимок с
   телефона (портретный, 3:4) при ширине карточки вырастает почти до девятисот
   точек и отодвигает обсуждение на экран вниз — ровно та беда, из-за которой
   01.09.2026 здесь же начали сворачивать длинный текст. Раньше платить за это
   приходилось, потому что другого способа увидеть картинку не было; теперь она
   в один щелчок целиком. Мера в vh, а не в точках: отнимает потолок место у
   ЭКРАНА, а он у всех разный. Широкой картинки правило не касается вовсе — её
   высоту и без того держит ширина карточки. */
.shotmain img { max-height: 60vh; width: auto; }
.shotrow { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
/* Квадрат со срезанным по месту содержимым: картинки в полосе разной формы, и
   без object-fit ряд превращался бы в лесенку. 92 точки — меньше лица в
   мордоленте не берём (70 там выбрано замером с НГС), но и не вровень с
   главной: полоса обязана читаться как приложение к ней. */
/* flex: 0 0 auto — чтобы ряд ПЕРЕНОСИЛСЯ, а не ужимался: у сжатой ссылки
   картинка внутри остаётся своей ширины и вылезает наружу, а на узком экране
   это горизонтальная прокрутка страницы. */
.shotthumb { flex: 0 0 auto; }
.shotthumb img { width: 92px; height: 69px; object-fit: cover; }

/* ------------------------------------------------ просмотрщик

   Слой поверх всей страницы, открываемый анкором (`:target`) — без скрипта,
   потому что без JS страница обязана работать, а строгий CSP не пускает сюда ни
   inline-стилей, ни чужих библиотек.

   Ground у него тёмный ВО ВСЕХ палитрах и токеном не задаётся: это не часть
   вида страницы, а подложка под фотографию, и белый лист вокруг снимка врёт про
   его собственные цвета одинаково в светлой теме и в тёмной.

   z-index выше липкой шапки (20) и её выпадающего меню (30): просмотрщик
   накрывает страницу целиком, и шапка, проступающая сквозь него, читалась бы
   как незакрывшийся слой. */
.lbox { display: none; }
.lbox:target {
  display: flex; align-items: center; justify-content: center;
  position: fixed; inset: 0; z-index: 40; background: rgba(0, 0, 0, .93);
}
/* Подложка ловит нажатие мимо картинки. Стои́т первой в разметке, поэтому всё
   остальное внутри слоя рисуется поверх неё и нажатия себе забирает. */
.lbunder { position: absolute; inset: 0; }
.lbimg { position: relative; max-width: 96vw; max-height: 92vh; width: auto; height: auto; }
/* Стрелки и подпись лежат ПОВЕРХ снимка, а снимок бывает любой — белое по
   белому не читается вовсе. Поэтому под ними своя подложка, а не одна прозрачность:
   светлый угол фотографии не должен решать, видно кнопку или нет. */
.lbnav {
  position: absolute; top: 50%; transform: translateY(-50%);
  padding: 8px 14px; font-size: 40px; line-height: 1;
  color: #fff; text-decoration: none; opacity: .8;
  background: rgba(0, 0, 0, .45); border-radius: 4px;
}
.lbnav:hover { opacity: 1; background: rgba(0, 0, 0, .65); }
.lbprev { left: 4px; }
.lbnext { right: 4px; }
.lbbar {
  position: absolute; top: 8px; right: 8px; display: flex; align-items: center; gap: 16px;
  padding: 4px 10px; background: rgba(0, 0, 0, .45); border-radius: 4px;
}
.lbfile, .lbx { color: #fff; text-decoration: none; opacity: .8; }
.lbfile { font-size: 13px; }
.lbx { font-size: 22px; line-height: 1; }
.lbfile:hover, .lbx:hover { opacity: 1; }
/* На телефоне стрелки и подпись ужимаются: экран узкий, а картинка на нём и так
   занимает почти всю ширину — крупная стрелка отъедала бы её край. */
@media (max-width: 560px) {
  .lbnav { font-size: 30px; padding: 8px; }
  .lbfile { font-size: 12px; }
}

/* Иллюстрация в ЛЕНТЕ. Потолок по высоте, а не по ширине: в ленте двадцать
   карточек, и вертикальный снимок с телефона иначе занимает экран целиком —
   ровно то, из-за чего длинный текст здесь сворачивается показом.

   Файл при этом один и тот же, полного размера: второй, уменьшенной копии
   площадка не хранит. Цена названа честно — читатель ленты качает картинку
   целиком, — и она принята: своих картинок сотни, а не миллионы, а вторая
   копия это второй файл на диске без уборки и второй проход ffmpeg. */
.thumb { display: block; margin-top: 12px; }
.thumb img {
  max-width: 100%; max-height: 320px; width: auto; height: auto;
  display: block; border: 1px solid var(--rule);
}

/* Картинка к РЕПЛИКЕ (16.09.2026): миниатюра, а целиком — в просмотрщике выше.
   Потолок ниже, чем у ленты: реплика — строка разговора, и картинка в ней не
   должна отодвигать соседние реплики на экран. Файл тот же, полного размера,
   по той же причине, что в ленте. width: fit-content — чтобы нажималась сама
   картинка, а не пустая полоса справа от неё. */
.cshot { display: block; width: fit-content; margin-top: 8px; scroll-margin-top: 60px; }
.cshot img {
  max-width: 100%; max-height: 160px; width: auto; height: auto;
  display: block; border: 1px solid var(--rule);
}

/* ------------------------------------------------------------------- ролик

   Карточка ролика: наше превью, значок «play» и адрес. Плеера тут нет и не
   будет — довод в web/video.go, он про согласие, а не про вёрстку.

   Кадр держит aspect-ratio, а не width/height у картинки: превью приезжает с
   разных площадок и разной формы (у YouTube это 480×360 с чёрными полями), и
   единственный способ не дать странице прыгать при загрузке — задать место
   заранее, а саму картинку вписать кадрированием. Поля при этом срезаются, что
   и нужно. */

.vid {
  display: block; max-width: 420px; margin: 12px 0 0;
  border: 1px solid var(--rule); border-radius: 3px; overflow: hidden;
  background: var(--card); color: var(--ink);
}
.vid:hover { text-decoration: none; border-color: var(--link); }
/* Фон чёрный, а не по теме: пока превью не загрузилось, на его месте обязан
   быть кадр, а не дыра цвета карточки. */
.vidpic { position: relative; display: block; background: #000; aspect-ratio: 16 / 9; }
.vidpic img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* Значок нарисован рамками, а не картинкой: файл ради треугольника — это ещё
   один запрос и ещё одно место, где однажды разойдутся тема и содержимое. */
.vidplay {
  position: absolute; left: 50%; top: 50%; width: 54px; height: 38px;
  margin: -19px 0 0 -27px; border-radius: 8px;
  background: rgba(0, 0, 0, .62); border: 1px solid rgba(255, 255, 255, .35);
}
.vidplay::after {
  content: ""; position: absolute; left: 20px; top: 10px;
  border: 8px solid transparent; border-left: 13px solid #fff; border-right: 0;
}
.vidfoot {
  display: flex; gap: 8px; align-items: baseline; overflow: hidden;
  padding: 7px 10px; font-size: 12px;
}
.vidfoot b { flex: 0 0 auto; }
.vidurl { color: var(--meta); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.empty { color: var(--meta); padding: 20px 0; }

/* ---------------------------------------------------------------- постраничка */

.pages { margin: 14px 0; }
.pg {
  display: inline-block; min-width: 26px; padding: 5px 9px; margin: 0 3px 4px 0;
  text-align: center; border: 1px solid var(--rule); border-radius: 3px;
  background: var(--btn); color: var(--ink);
}
.pg:hover { text-decoration: none; border-color: var(--link); color: var(--link); }
.pg.cur { background: var(--accent); border-color: var(--accent); color: #fff; }
.pg.gap { border-color: transparent; background: none; }
.pg.off { color: var(--meta); background: none; border-color: transparent; }
/* Многоточие для узкого экрана: на широком его место занимают сами номера. */
.pg.gap.mob { display: none; }

/* ---------------------------------------------------------------- тред */

/* Переключатель вида — иконками, как на НГС (там два пустых <a> с title, а
   картинку рисует CSS). Цвет значка = currentColor, поэтому «выбрано» и смена
   темы работают одним свойством. */
/* СВОЙ СЛОЙ, и это не украшение. Карточка заметки ТОЖЕ позиционирована
   (.note { position: relative }) и стоит в разметке ПОСЛЕ переключателя, а два
   позиционированных элемента с z-index: auto рисуются в порядке разметки — то
   есть карточка ложится ПОВЕРХ значков, и отведённые под них 76px правого
   отступа оказываются не пустым местом, а слоем, съедающим нажатие. Заметить
   это глазом нечем: значки нарисованы целиком, они просто перестают отвечать
   (жалоба владельца 03.09.2026 «будто её какой-то слой перекрывает»). На
   телефоне переключатель возвращается в поток (position: static ниже), поэтому
   дефект жил только на десктопе — и потому же его не поймал ни один снимок.

   Единица, а не число побольше: над липкой шапкой (z-index: 20) переключателю
   быть нельзя — он проступил бы сквозь неё при прокрутке. */
.switch { position: absolute; z-index: 1; top: 12px; right: 0; display: flex; gap: 4px; }
.switch .sw {
  display: block; padding: 5px 7px; line-height: 0; color: var(--meta);
  border: 1px solid transparent; border-radius: 3px;
}
.switch .sw:hover { color: var(--link); }
.switch .sw.on { color: var(--ink); border-color: var(--rule); background: var(--btn); }

.cttl { font-size: 20px; font-weight: 700; margin: 22px 0 4px; }
.cttl .cnt { font-size: 13px; font-weight: 400; }
.cttl .closed { font-size: 13px; font-weight: 400; color: var(--meta); margin-left: 14px; }
.foldall { float: right; font-weight: 400; margin-top: 4px; }

.thread { list-style: none; margin: 0; padding: 0; }

/* Отступ ветки: класс несёт только НОМЕР уровня, расстояние считает calc.
   На НГС это inline padding-left, но inline-стиль запрещён нашим CSP. */
.d1 { --d: 0 } .d2 { --d: 1 } .d3 { --d: 2 } .d4 { --d: 3 }
.d5 { --d: 4 } .d6 { --d: 5 } .d7 { --d: 6 } .d8 { --d: 7 }
.d9 { --d: 8 } .d10 { --d: 9 } .d11 { --d: 10 } .d12 { --d: 11 }

.c {
  display: flex; gap: 15px;
  padding: 14px 0 12px;
  margin-left: calc(var(--d, 0) * var(--step));
  border-bottom: 1px solid var(--rule);
}
/* Свёрнутая ветка убирается атрибутом hidden, и правило для него нужно СВОЁ:
   браузерное `[hidden] { display: none }` живёт в таблице агента, а любой
   авторский display её перебивает — с `display: flex` выше сворачивание не
   работало вовсе (жалоба 18.08.2026: «не работает „Свернуть все ответы“»).
   Селектор с атрибутом весит больше `.c`, поэтому !important не нужен. */
.c[hidden] { display: none; }
.c:first-child { border-top: 1px solid var(--rule); }
/* Подсветка комментария, на который пришли по анкору. Цвет — токеном: он
   свой у каждой палитры, а селектор на тему держал бы его только у той, про
   которую вспомнили, — светлая подсветка на тёмной странице засвечивает
   реплику целиком. */
.c:target { background: var(--hl); }
.thread.linear .c { margin-left: 0; }

/* Строка треда с формой ответа. Отступ — как у реплики, которой отвечают: форма
   про неё, и вровень с корнем ветки она читалась бы как ответ не тому. В
   линейном виде отступов нет вовсе, значит нет и здесь. */
.replyrow { margin-left: calc(var(--d, 0) * var(--step)); list-style: none; }
.thread.linear .replyrow { margin-left: 0; }

/* Аватар комментария такой же, как у заметки: .lv-comment__avatar
   { width:100px; height:100px }, а тело — с отступом 115px. Мельче он делает
   тред плотнее, но лица в ленте реплик — это половина узнавания собеседника. */
.cava { flex: 0 0 100px; }
.cava .ava { width: 100px; height: 100px; }
.cbody { flex: 1 1 auto; min-width: 0; }
/* Ник, под ним дата — как в комментариях на сайте. */
.chead { margin-bottom: 6px; }
/* Строка даты — РЯД: в ней стоят метка происхождения и сама дата, и метка
   идёт первой. Флексбокс, а не инлайн с поправкой: знак вдвое выше текста, и
   выравнивать их по базовой линии значило бы подбирать число заново после
   каждой правки кегля. */
.cdate { font-size: 12px; margin-top: 1px; display: flex; align-items: center; gap: 8px; }
.anchor { color: var(--meta); font-size: 12px; margin-left: 8px; visibility: hidden; }
.c:hover .anchor, .c:target .anchor { visibility: visible; }

/* Простыня (реплика длиннее longBodyRunes) показывается началом, остальное — по
   кнопке. Ограничение стоит ПОКАЗОМ, а не запретом писать: в базе 10,7 млн
   зеркальных реплик, переписать их нельзя, а живому автору длину мысли мы не
   назначаем.

   Разворачивает галочка, а не скрипт: без JS страница обязана оставаться
   рабочей. Сама галочка спрятана размером в пиксель, но НЕ display:none — иначе
   она выпала бы и из порядка обхода клавиатурой, и из дерева доступности вместе
   со своей подписью. */
.expand {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; border: 0; overflow: hidden; clip-path: inset(50%);
}
.clip { max-height: 20em; overflow: hidden; position: relative; }
/* Затухание вниз говорит «текст продолжается» — обрыв на полуслове читается как
   поломка. Цвет — та поверхность, на которой текст ЛЕЖИТ, а лежит он на
   карточке: фон красит .main, у самой реплики и у заметки своего фона нет.
   Было --page (фон за карточкой), и в светлой теме затухание уходило в серую
   полосу поверх белого листа. */
.clip::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 3.5em;
  background: linear-gradient(to bottom, transparent, var(--card)); pointer-events: none;
}
.expand:checked ~ .clip { max-height: none; }
.expand:checked ~ .clip::after { display: none; }
.more { display: inline-block; margin-top: 6px; font-size: 12px; color: var(--link); cursor: pointer; }
.more:hover { text-decoration: underline; }
.more .mc { display: none; }
.expand:checked ~ .more .mo { display: none; }
.expand:checked ~ .more .mc { display: inline; }
/* Фокус виден на ПОДПИСИ: сама галочка размером в пиксель, и обводка на ней
   означала бы, что клавиатурой страница проходится вслепую. */
.expand:focus-visible ~ .more { outline: 2px solid var(--link); outline-offset: 2px; }
/* Пришли по ссылке на саму реплику (анкор, уведомление, ответ) — она
   разворачивается сразу: человек шёл читать именно её, и лишнее нажатие тут
   было бы издевательством. */
.c:target .clip { max-height: none; }
.c:target .clip::after { display: none; }
.c:target .more { display: none; }

/* Обращение «Ник, » — ребро, а не часть текста: в базе его нет, здесь оно
   дорисовано из ТЕКУЩЕГО ника адресата. На сайте оно выделено жирным. */
.to { font-weight: 700; color: var(--ink); }
.to:hover { text-decoration: underline; }

/* Подвал комментария: «Ответить» и число ответов в одной строке. «Ответить»
   первым — на сайте оно тоже слева и это главное действие; «Ответы N» скрипт
   подменяет кнопкой сворачивания, поэтому вид у них общий. */
.cact { margin: 8px 12px 0 0; font-size: 12px; color: var(--meta); display: inline-flex; vertical-align: middle; gap: 14px; }
.rcount { cursor: default; }
.rep { color: var(--link); }
.rep:hover { text-decoration: underline; }
.fold {
  font: inherit; font-size: 12px; color: var(--link);
  background: none; border: 0; padding: 0; cursor: pointer;
}
.fold:hover { text-decoration: underline; }

/* ---------------------------------------------------------------- запись */

/* Заголовок ленты и «Написать заметку» в одной строке: на НГС кнопка публикации
   стоит над лентой, справа, и это первое, что ищет глазами вернувшийся автор. */
.feedhead { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.newnote {
  font-size: 13px; padding: 7px 16px; border-radius: 3px;
  background: var(--accent); color: #fff; white-space: nowrap;
}
.newnote:hover { text-decoration: none; opacity: .9; }
.editlink { font-size: 12px; margin-left: 14px; }

/* Мордолента — полоса лиц жителей НАД карточкой ленты (faces.go; имя не
   выдумано, так её звали на НГС). Вид снят с записанной ленты сайта, а не
   придуман: там это section.lv-top-tape перед блоком содержимого, внутри —
   div.lv-top-tape__visible-part с одним рядом ul.lv-top-tape__list, фото
   70×70 (замер по index.css НГС, см. ниже) и БЕЗ подписей: имя всплывает
   при наведении (на сайте это qtip, у нас title — он же работает без JS).

   Ряд НЕ ПЕРЕНОСИТСЯ на второй этаж: тридцать лиц в три ряда над лентой — уже
   не лента, а стена. Листается он при этом по-настоящему — пальцем на
   телефоне, тачпадом и стрелками на десктопе, — а вот ПОЛОСЫ ПРОКРУТКИ не
   видно: она разрезала бы ряд лиц пополам, и на НГС её тоже не было (там ряд
   двигали тень со стрелкой). Прячется полоса тремя разными свойствами, потому
   что браузеры договориться о ней так и не смогли.

   Ширина и поля — как у .main, потому что полоса ему СОСЕДКА, а не часть: на
   сайте она тоже стоит над блоком, а не внутри него. */
.faceband { position: relative; width: 100%; max-width: 910px; margin: 10px auto 0; }
.faces {
  display: flex; gap: 0; list-style: none; margin: 0; padding: 0;
  overflow-x: auto; overflow-y: hidden;
  scrollbar-width: none; -ms-overflow-style: none;
  overscroll-behavior-x: contain;
}
.faces::-webkit-scrollbar { width: 0; height: 0; }
.faces li { flex: 0 0 auto; padding: 5px; }
.faces a { display: block; line-height: 0; }

/* РАЗМЕР ЛИЦА В ПОЛОСЕ — 70×70, и это ЗАМЕР, а не глазомер (31.08.2026,
   замечание владельца «в оригинальной мордоленте аватары меньше аватаров
   авторов заметок»). Снято с живого index.css НГС, где полоса описана так:

     .lv-top-tape__visible-part { height: 82px }
     .lv-top-tape__item         { height: 82px; width: 82px }
     .lv-top-tape__link         { padding: 5px; height: 70px; width: 70px }
     .lv-top-tape__userpic      { width: 100%; height: 100%; border-radius: 3px }
     .lv-top-tape__link:hover   { padding: 0; width: 80px; height: 80px }

   Стояло у нас 100×100 — размер САМОГО ФАЙЛА (аватары НГС лежат как
   `..._100_100_c.jpg`), и записанная страница называет именно его. Отсюда и
   ошибка: разметка говорит про файл, размер показа задаёт таблица стилей, и
   первого без второго мало. Полоса от этого весила ровно столько же, сколько
   колонка автора, — то есть переставала быть полосой.

   Отступ 5px у гнезда и НУЛЕВОЙ зазор дают те же 10px между лицами, что и
   раньше, а заодно место для роста при наведении: лицо занимает собственные
   поля и НЕ двигает соседей — так это и сделано на сайте. */
.faces .ava { width: 70px; height: 70px; }
.faces a:hover .ava { width: 80px; height: 80px; margin: -5px; }

/* КАРТОЧКА ДВОЙНИКА — смежного обсуждения (parts/synth.gohtml).

   Знак площадки занимает место аватара: у двойника нет автора-человека, и
   квадрат ровно того же размера держит колонку, чтобы соседние карточки ленты
   не разъезжались. Ник под ним — имя площадки, приглушённое и без ссылки: это
   не тот, к кому идут знакомиться.

   Цитата — линейка слева и приглушённая шапка, как у всякой цитаты: она обязана
   читаться как ЧУЖИЕ слова, а не как текст этой записи. Отступ у текста тот же,
   что у линейки, чтобы вложенная в цитату разметка не липла к ней. */
.sitemark {
  width: 100px; height: 100px; display: flex;
  align-items: center; justify-content: center;
  border: 1px solid var(--rule); border-radius: 3px;
  background: var(--btn); color: var(--meta);
}
/* Значок — ДОЛЯ коробки, а не сорок четыре точки. Коробка ужимается в четырёх
   местах (телефон, узкий телефон, колонка автора, колонка реплики), и жёсткий
   размер держался, пока знак жил в одной заметке: у реплики на узком экране
   коробка 48, и 44 точки прибивали значок к самой рамке. Доля переезжает вместе
   с коробкой сама и второго числа рядом с первым не заводит. */
.sitemark .ico { width: 44%; height: 44%; }
.nick._site { color: var(--meta); font-weight: 600; }
.synthcap { margin: 0 0 10px; color: var(--meta); font-size: 13px; }
.synthq { margin: 0; padding: 0 0 0 12px; border-left: 3px solid var(--rule); }
.synthq .qhead { margin: 0 0 6px; color: var(--meta); font-size: 13px; }
.synthq .qnick { font-weight: 600; }
.synthq .date { color: inherit; }

/* Стрелки листания — те самые lv-top-tape__nav с сайта: тень и стрелка у края.
   Рисует их СКРИПТ, а не сервер, и по общему правилу площадки: без JS ряд
   листается пальцем и тачпадом, а мёртвых кнопок на странице не бывает.
   Показываются они по одной и только когда есть куда ехать.

   [hidden] названо явно, потому что display: flex сильнее правила из таблицы
   браузера — та же грабля, что со свёрнутой веткой в треде. */
.tnav {
  position: absolute; top: 0; bottom: 0; width: 44px; border: 0; padding: 0;
  display: flex; align-items: center; justify-content: center;
  font: inherit; font-size: 26px; line-height: 1; cursor: pointer; color: var(--ink);
}
.tnav[hidden] { display: none; }
/* Та же причина, что строкой выше: форма отметки стоит в .dlgacts всегда и
   прячется, пока отмечать нечего, — правило названо явно, чтобы будущий
   `display` у форм полоски не отменил его молча. */
.mkread[hidden] { display: none; }
.tprev { left: 0; background: linear-gradient(to right, var(--page) 55%, transparent); }
.tnext { right: 0; background: linear-gradient(to left, var(--page) 55%, transparent); }

/* Форма записи — одна на всё: новая заметка, правка, ответ в тред. */
.wform { display: flex; flex-direction: column; gap: 10px; margin: 12px 0 0; }
.wform textarea {
  font: inherit; width: 100%; padding: 10px 12px; resize: vertical;
  border: 1px solid var(--rule); border-radius: 3px;
  background: var(--card); color: var(--ink);
}
.wact { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; }
.wact button {
  font: inherit; padding: 8px 24px; cursor: pointer; border: 0; border-radius: 3px;
  background: var(--accent); color: #fff;
}
.wact .alt { color: var(--meta); font-size: 13px; }
/* Форма, где ПИШУТ, отправляется кнопкой в правом углу (15.09.2026, просьба
   владельца): так устроены почта и мессенджеры, и взгляд, дописав текст, ищет
   кнопку справа внизу, под концом строки, а не в начале следующей. «Отмена»
   встаёт слева от неё, подсказка клавиш — под ней. Сетка, а не flex с
   обратным порядком: переставляется только ПОКАЗ, а порядок в разметке (кнопка,
   «Отмена», подсказка) остаётся тем, в котором по форме ходят Tab'ом и читает
   экранный диктор. У формы отзыва согласия класса нет намеренно — там не пишут,
   и опасная кнопка остаётся на прежнем месте. */
.wact.send {
  display: grid; grid-template-columns: 1fr auto; column-gap: 14px; row-gap: 4px;
  align-items: center;
}
.wact.send button { grid-column: 2; grid-row: 1; }
.wact.send .alt { grid-column: 1; grid-row: 1; justify-self: end; }
.sendkey { grid-column: 2; grid-row: 2; justify-self: center; font-size: 11px; color: var(--meta); }
.sendkey kbd {
  font: inherit; padding: 0 4px; border: 1px solid var(--rule); border-radius: 3px;
  background: var(--btn); color: var(--ink);
}
/* На сенсорном экране без мыши клавиатуры обычно нет, и подсказка — лишняя
   строка под кнопкой. Сочетание при этом работает: подключённой клавиатуре
   скрипт не отказывает. */
@media (hover: none) and (pointer: coarse) { .sendkey { display: none; } }
.chk { font-size: 13px; color: var(--meta); display: flex; gap: 6px; align-items: center; }

/* Приложенная картинка: полоса закачки и карточка с превью (shotdraft.go).
   Обе строки нарисованы сервером заранее и скрыты — скрипт их только
   показывает, поэтому без JS они молчат и ничего не занимают.

   Превью НЕ во всю ширину формы: это не иллюстрация, а подтверждение выбора, и
   рядом с ним обязаны помещаться слова о том, что уйдёт с заметкой. Отсюда же
   потолок по высоте — вертикальный снимок с телефона иначе выдавил бы кнопку
   «Опубликовать» за нижний край экрана. */
.shotwait { font-size: 13px; color: var(--meta); display: flex; gap: 10px; align-items: center; margin: 0; }
/* [hidden] названо явно по тому же доводу, что у .c и .tnav: display: flex
   сильнее правила из таблицы браузера, и полоса «Картинка загружается…» стояла
   на форме ВСЕГДА, ещё до выбора файла (замечено 16.09.2026, когда то же поле
   встало в форму ответа). */
.shotwait[hidden] { display: none; }
.shotwait progress { flex: 1; max-width: 16rem; height: 8px; }
.shotbox:empty { display: none; }
.shotbox { display: flex; gap: 12px; align-items: flex-start; flex-wrap: wrap; }
.shotbox .fine { margin: 0; flex: 1; min-width: 12rem; }
.shotprev {
  max-width: 220px; max-height: 220px; width: auto; height: auto;
  border: 1px solid var(--rule); border-radius: 3px; display: block;
}
.shotdrop {
  font: inherit; font-size: 13px; padding: 2px 10px; margin-left: 6px; cursor: pointer;
  border: 1px solid var(--rule); border-radius: 3px;
  background: var(--card); color: var(--meta);
}

.compose { max-width: 44rem; }
.compose .bad, .replybox .bad { margin: 10px 0 0; }

/* Форма ответа. Внизу треда она отвечает ЗАМЕТКЕ, а под репликой (.replyrow) —
   своему адресату; отступ сверху меньше в треде, где она стоит вплотную к
   реплике, — большой зазор там читался бы как конец разговора. */
.replybox { margin: 22px 0 0; padding-top: 16px; border-top: 1px solid var(--rule); }
.replyrow .replybox { margin-top: 0; padding-bottom: 16px; }
.repto { margin: 0; font-size: 13px; color: var(--meta); }
.repto a { color: var(--link); }

.nickform { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin: 20px 0 0; }
.nickform label { color: var(--meta); font-size: 13px; }
.nickform input {
  font: inherit; padding: 7px 10px; min-width: 14rem;
  border: 1px solid var(--rule); border-radius: 3px; background: var(--card); color: var(--ink);
}
.nickform button {
  font: inherit; padding: 7px 18px; cursor: pointer;
  border: 1px solid var(--rule); border-radius: 3px; background: var(--btn); color: var(--ink);
}

/* ---------------------------------------------------------------- вход и ошибки */

.enterbox, .oops { max-width: 34rem; margin: 5vh auto; text-align: center; }
.enterbox h1, .doctext h1 { margin: 0 0 12px; font-weight: 400; font-size: 26px; }
.enterbox .lead { color: var(--ink); margin: 0 0 18px; }
.enterbox .alt { margin-top: 26px; }
.enterbox .step { color: var(--meta); font-size: 12px; margin: 0 0 6px; text-transform: uppercase; }

/* Код показывается один раз и его переписывают руками — отсюда моноширинный
 * шрифт, крупный кегль и разрядка: «0 или O» человек не должен решать. */
.code {
  font: 700 24px/1.3 "Cascadia Mono", Consolas, "DejaVu Sans Mono", monospace;
  letter-spacing: 2px; margin: 18px 0; padding: 14px 10px;
  background: var(--btn); border: 1px dashed var(--rule); border-radius: 3px;
  user-select: all;
}

/* Шаги входа названы заранее: человеку идти на НГС, и он должен знать это до
 * того, как начнёт. */
.steps { text-align: left; margin: 18px auto; padding-left: 22px; max-width: 26rem; color: var(--ink); }
.steps li { margin: 6px 0; }

.whoami { display: flex; gap: 15px; align-items: center; justify-content: center; margin: 18px 0; }
.whoami .nick { display: block; font-size: 18px; font-weight: 600; }
.whoami .fine { display: block; }
/* Имя в шапке настроек. Шапка там ОБЩАЯ со страницей участника (.uhead), а имя
 * заголовком быть не может — заголовок страницы называет её саму
 * («Настройки»), и два крупных имени подряд читаются как две страницы. */
.umeta .nick { display: block; font-size: 18px; font-weight: 600; margin: 0; }

/* «Подробнее» — свёртка объяснения. Настройки отвечают сразу на десяток
 * вопросов, и распечатанные подряд абзацы прятали САМИ настройки: до нижних
 * приходилось мотать через полтора экрана прозы. details/summary, а не скрипт
 * (без JS страница обязана работать), и по одному на настройку, а не одна на
 * всю страницу: открывают то объяснение, которое сейчас нужно. */
.why { margin: 6px 0 0; }
.why > summary {
  cursor: pointer; color: var(--link); font-size: 13px; width: fit-content;
}
.why > summary:hover { text-decoration: underline; }
.why > p { margin: 8px 0 0; }
/* Альбом в настройках уже, чем на странице участника: там он колонка в 220
 * точек, а здесь лёг бы во всю ширину текста и перевесил бы собой раздел. */

/* «Обновить аватар» стоит под ником, у самой карточки: она про то, что видно
 * рядом, — а не в общем ряду кнопок страницы. Мельче кнопки смены ника, потому
 * что нажимают её изредка. */
/* Настройка «про меня» на /me: слева название и текущее состояние, справа
 * кнопка. Ряд, а не форма со «сохранить», потому что состояний два и нажатие
 * само и есть выбор: экрана подтверждения тут не бывает, промах отменяется
 * вторым нажатием. */
.pref {
  display: flex; gap: 12px; align-items: center; justify-content: space-between;
  flex-wrap: wrap; margin: 20px 0 0;
}
.pref .fine { display: block; margin: 2px 0 0; }
.pref button {
  font: inherit; padding: 7px 18px; cursor: pointer;
  border: 1px solid var(--rule); border-radius: 3px; background: var(--btn); color: var(--ink);
}
/* Выбор набора смайлов: три кнопки рядом, выбранная подсвечена тем же .tbtn.on,
   что тема в меню участника. */
.pref .packs { display: flex; gap: 6px; flex-wrap: wrap; }
.pref .packs .tbtn { padding: 7px 14px; }

/* Кнопки про фото стоят В РЯД, а не столбиком: они про одно и то же место на
 * странице, и разнесённые по строкам читались бы как две разные настройки. На
 * узком экране ряд переносится — тем же flex-wrap, что в шапке. */
.avarow { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-top: 6px; }
/* Свёрнутая правка ника стои́т в этом же ряду — «как меня зовут и как я
   выгляжу» это один вопрос. Раскрытая занимает всю строку: форма с полем и
   двумя абзацами в колонку кнопки не встаёт. */
.avarow details { margin: 0; }
.avarow details[open] { flex: 1 1 100%; }
.avaform button {
  font: inherit; font-size: 13px; padding: 4px 12px; cursor: pointer;
  border: 1px solid var(--rule); border-radius: 3px; background: var(--btn); color: var(--ink);
}

.warn {
  text-align: left; margin: 14px 0; padding: 10px 12px;
  border-left: 3px solid var(--accent); background: var(--btn);
}

/* Документ согласия читают, а не проглядывают: текст слева, обычной длины
 * строки, без сворачивания. */
.doc { max-width: 44rem; text-align: left; }
.doctext h2 { font-size: 17px; font-weight: 700; margin: 22px 0 6px; }
.doctext p { margin: 8px 0; }

/* Документы отдельными страницами (соглашения, политика, отказ от
 * ответственности). Та же колонка, что на экране входа, но без «карточки»:
 * это не шаг, который проходят, а текст, который читают подряд.
 *
 * Соглашений на странице ДВА, и каждое остаётся собой — со своим заголовком
 * первой строки, как в экране входа: сводить их под один заголовок значило бы
 * показывать вместо двух документов один пересказ. Разделяет их линия. */
.docpage { max-width: 44rem; margin: 0 auto; }
.docpage .lead { margin: 0 0 22px; }
.docsec + .docsec { margin-top: 34px; padding-top: 26px; border-top: 1px solid var(--rule); }
.agree { margin: 24px 0 8px; }
.agree button {
  font: inherit; font-size: 16px; padding: 10px 30px; cursor: pointer;
  border: 0; border-radius: 3px; background: var(--accent); color: #fff;
}
.refuse { margin: 0 0 18px; }
.linkish {
  font: inherit; background: none; border: 0; color: var(--meta);
  cursor: pointer; text-decoration: underline; padding: 4px;
}

.sub { font-size: 20px; font-weight: 400; margin: 26px 0 10px; }
/* ДВЕРИ — чем человек может войти. Список устроен как согласия рядом (строка,
 * разделитель, действие справа): вопрос у них соседний, и разная разметка
 * читалась бы как разные по важности разделы. */
.doors { list-style: none; margin: 0; padding: 0; }
.doors li {
  border-top: 1px solid var(--rule); padding: 12px 0;
  display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
}
.doors form { margin-left: auto; }
.doors button.danger { border-color: var(--female); color: var(--female); }
/* Ссылка, которая ведёт себя как кнопка: привязка — это переход на отдельный
 * экран с документом, а не POST, поэтому форма тут была бы неправдой. */
.btnlink {
  display: inline-block; font-size: 13px; padding: 5px 14px; text-decoration: none;
  border: 1px solid var(--rule); border-radius: 3px; background: var(--btn); color: var(--ink);
}
.btnlink:hover { border-color: var(--accent); }
.consents { list-style: none; margin: 0; padding: 0; }
.consent {
  border-top: 1px solid var(--rule); padding: 12px 0;
  display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
}
.consent .cname { flex: 1 1 20rem; }
.consent button {
  font: inherit; padding: 6px 16px; cursor: pointer;
  border: 1px solid var(--rule); border-radius: 3px; background: var(--btn); color: var(--ink);
}
.consent button.danger { border-color: var(--female); color: var(--female); }
.keyform { display: flex; flex-direction: column; gap: 8px; align-items: center; margin: 18px 0; }
.keyform label { color: var(--meta); }
.keyform input {
  font: inherit; width: 100%; max-width: 22rem; padding: 8px 10px;
  border: 1px solid var(--rule); border-radius: 3px; background: var(--card); color: var(--ink);
}
.keyform button, .rowform button, .oops a.back, a.door {
  font: inherit; padding: 8px 22px; cursor: pointer; border: 0; border-radius: 3px;
  background: var(--link); color: #fff;
}
/* Дверь наружу — ССЫЛКА, а выглядеть обязана кнопкой: рядом стоит настоящая
 * кнопка формы, и две одинаковые по смыслу двери не должны выглядеть
 * по-разному. Почему не форма — в login.gohtml. */
a.door { display: inline-block; text-decoration: none; }
a.door:hover { text-decoration: none; background: var(--accent); }
.bad { color: var(--female); }
.oops .code { font-size: 40px; margin: 0; color: var(--meta); }
.oops .msg { margin: 4px 0 18px; }

/* ---------------------------------------------------------------- модерация */

/* Полоска действий под публикацией. Мелкая и приглушённая намеренно: она стоит
   под КАЖДОЙ репликой, и если её видно так же, как текст, страница перестаёт
   быть разговором и становится пультом. */
.modbar { margin: 8px 12px 0 0; display: inline-flex; vertical-align: middle; flex-wrap: wrap; gap: 8px; align-items: center; }
.minline { display: inline-flex; gap: 6px; align-items: center; margin: 6px 0 0; }
.mbtn {
  font: inherit; font-size: 12px; padding: 3px 10px; cursor: pointer;
  border: 1px solid var(--rule); border-radius: 3px; background: var(--btn); color: var(--ink);
}
.mbtn.danger { border-color: var(--female); color: var(--female); }
.mlink { font-size: 12px; color: var(--meta); }
/* Значки полоски действий и значок реплики отправляются НА ТЕЛЕФОН, а на
   десктопе не показываются: там места хватает, и слово точнее знака. Разметка
   несёт и то и другое, потому что ширины экрана сервер не знает, — тот же
   приём, что у постранички, где лишние номера прячет CSS.

   .dlgacts — полоска над личной перепиской (12.09.2026): та же задача и потому
   тот же селектор, а не своя пара правил рядом. Разъехались бы они молча. */
.modbar .ico, .clink .ico, .dlgacts .ico { display: none; }
/* Два тихих слова подряд читаются как ОДНА фраза: «Поправить Автор» — это
   заметил владелец на снимке 26.08.2026, и на десктопе оно выглядит так же.
   Разделяет их точка, а не подчёркивание: ссылки на НГС не подчёркнуты, и
   заводить подчёркивание ради двух слов значило бы менять язык страницы. На
   телефоне точка снимается — там у ссылок рамка, и разделять уже нечего. */
.mlink + .mlink::before { content: "·"; margin-right: 8px; color: var(--rule); }
.mtag {
  font-size: 11px; color: var(--meta); border: 1px solid var(--rule);
  border-radius: 3px; padding: 1px 6px;
}
.mtag.warnbadge { border-color: var(--accent); color: var(--accent); }

/* Скрытое модерацией видно ТОЛЬКО модератору (запрос отдаёт его лишь ему), и
   отличаться оно обязано с одного взгляда — иначе он вернёт скрытое, приняв за
   живое, или скроет дважды. Отсюда ЗАТЕНЕНИЕ: не убрано, но и не в разговоре.
   У ЗАМЕТКИ гасится содержимое, а не строка целиком, и это не придирка к виду:
   opacity заводит свой слой, и вернуть полоске действий полную силу изнутри
   потом нечем, — а ею скрытое и возвращают. У реплики полоска короче и правило
   осталось прежним, на всю строку. */
.chid { opacity: .55; }
.nhid .author, .nhid .nbody > :not(.modbar) { opacity: .55; }
.chid .text, .nhid .text { text-decoration-line: line-through; text-decoration-color: var(--rule); }

.modsum { display: flex; flex-wrap: wrap; gap: 14px; align-items: baseline; margin: 12px 0 18px; }
.modq, .myhid { list-style: none; margin: 0; padding: 0; }
.mitem { border-top: 1px solid var(--rule); padding: 12px 0; }
.mitem.mhid { opacity: .8; }

/* Посетители на странице администратора (visits.go). Таблица, а не список: это
   числа, и читают их столбцами. Кегль мелкий намеренно — пять колонок обязаны
   уложиться в 288 точек, на самом узком экране площадки. */
.vnow { margin: 10px 0 0; }
.vtab { border-collapse: collapse; width: 100%; max-width: 440px; margin: 12px 0 0; font-size: 13px; }
.vtab th, .vtab td { border-bottom: 1px solid var(--rule); padding: 4px 6px; text-align: right; }
.vtab th[scope="col"] { color: var(--meta); font-size: 11px; font-weight: 400; }
.vtab th[scope="row"] { text-align: left; font-weight: 400; }
.mhead { display: flex; flex-wrap: wrap; gap: 10px; align-items: baseline; }
.mwhy { margin: 6px 0 0; }
.mquote {
  margin: 6px 0; padding: 4px 10px; border-left: 3px solid var(--female);
  color: var(--meta);
}
.mrep { margin: 4px 0 0; color: var(--meta); font-size: 13px; }
/* Текст показывается ЦЕЛИКОМ и с сохранением переносов: решать по обрезанной
   цитате — ровно та ошибка, ради которой кнопки продублированы на странице
   заметки. */
.mtext { margin: 6px 0 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.mdec { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin: 10px 0 0; }
/* Текст заметки в форме, где его не правят: администратор у ЗЕРКАЛЬНОЙ заметки
   ставит одну картинку, а видеть, к чему он её ставит, обязан. Не textarea и не
   поле ввода: править нечего, и вид должен об этом говорить сам — тот же лист,
   что и на странице, только приглушённой рамкой. */
.lockedtext { border: 1px solid var(--rule); border-radius: 3px; padding: 10px 12px;
  background: var(--card); color: var(--ink); max-height: 40vh; overflow-y: auto; }
.lockedtext p { margin: 0 0 10px; }
.lockedtext p:last-child { margin-bottom: 0; }
.mreason, .mdays, .mrole {
  font: inherit; font-size: 13px; padding: 5px 8px; flex: 1 1 16rem;
  border: 1px solid var(--rule); border-radius: 3px; background: var(--card); color: var(--ink);
}
.mdays { flex: 0 0 5rem; }
.mnum { flex: 0 0 8rem; }
.mrole { flex: 0 0 12rem; }

/* Выданный код. Показывается ОДИН раз, поэтому отделён рамкой от всего
   остального на странице: пропустить его глазами нельзя — второго показа не
   будет. Числа те же, что у экрана входа (.code): его тоже переписывают руками. */
.issued {
  margin: 14px 0 22px; padding: 2px 16px 10px;
  border: 1px solid var(--rule); border-radius: 3px; background: var(--card);
}
.issued .code { text-align: center; }

/* ---------------------------------------------------------------- страница участника

   Шапка повторяет колонку автора у заметки: то же квадратное фото слева, то же
   имя рядом. Своего вида профиль не заводит намеренно — иначе он оказался бы
   чужим той самой странице, с которой на него приходят.

   Список публикаций — строки, а не карточки: это «чем человек занят», а не
   вторая лента, и карточка с телом заметки превратила бы двадцать строк в
   двадцать экранов. */

.uhead {
  display: flex; gap: 20px; flex-wrap: wrap; align-items: flex-end;
  margin: 0 0 18px; padding-bottom: 18px; border-bottom: 1px solid var(--rule);
}
.umeta { min-width: 0; flex: 1 1 14rem; }
/* Служебная строка стои́т НАД именем: «кто это и под каким номером» человек
   сверяет, а не читает, — и место такому у ярлыка, а не под заголовком. */
.umeta .fine { margin: 0 0 4px; }
.umeta .ttl { margin: 0; }
.urole { margin: 5px 0 0; color: var(--meta); }
/* Кнопка прижата к правому краю шапки: действие одно, и место у него своё, а
   не в столбце сведений. auto слева, а не justify-content, — иначе на узком
   экране, где .umeta переносится, она уехала бы в середину. */
.uhead .uact { margin: 0 0 2px auto; }
.uhead .fine + .avaform { margin-left: auto; }

/* Одна колонка (хендофф 13.09.2026). Две колонки прожили сутки: справочной
   части у живого участника мало — город и занятие это уже щедро, — а записей
   много, и левый столбец в 220 точек оставался пустым рядом с длинной лентой.
   Геометрия стои́т ЗДЕСЬ, а не в блоке темы: разметка страницы одна на все
   шесть палитр, и тема меняет цвет, а не устройство. */

/* Факты о себе — строкой: два-три значения в столбце определений читаются как
   недозаполненная анкета, в строке — как подпись. */
.ufacts {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 8px 18px; font-size: 13px; margin: 0 0 14px;
}
.ufacts > span { display: inline-flex; align-items: baseline; gap: 7px; overflow-wrap: anywhere; }
/* Ярлык — капитель, а не заголовок ленты. Шрифт ему меняет тема, размер и
   регистр общие. */
.ufacts .lbl {
  font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(--meta);
}
/* Альбом — в правом конце той же строки, миниатюрами по 40 точек: страница
   отвечает на «кто это», а сам снимок открывается по нажатию. */
.ufacts .ualbum { align-items: center; gap: 6px; margin-left: auto; }
.ualbum a { display: block; line-height: 0; }
.ualbum img {
  width: 40px; height: 40px; object-fit: cover; display: block;
  border: 1px solid var(--rule); border-radius: 3px;
}
.ualbum a:hover img { border-color: var(--accent); }
/* Плашка рассказа о себе. Коробка общая для всех палитр — она отделяет рассказ
   от списка записей, и это устройство страницы; тема добавляет ей только
   подложку. */
.ubio {
  margin: 0 0 14px; padding: 12px 14px; border-radius: 3px;
  border: 1px solid var(--rule); border-left: 2px solid var(--accent);
}
.ubio .text { margin: 8px 0 0; }

/* Служебный ящик модератора — пунктирной рамкой: это служебное, а не часть
   профиля, и свёрнут он до нажатия. */
.uadm {
  margin: 0 0 14px; border: 1px dashed var(--rule); border-radius: 3px;
  background: var(--pane, transparent);
}
.uadm > summary {
  cursor: pointer; padding: 10px 14px; list-style: none;
  display: flex; align-items: center; gap: 10px;
  font-size: 12px; letter-spacing: .08em; text-transform: uppercase; color: var(--meta);
}
.uadm > summary::-webkit-details-marker { display: none; }
.uadm > summary::before { content: "▸"; color: var(--accent); }
.uadm[open] > summary::before { content: "▾"; }
/* Подсказка о содержимом — справа, тихим цветом; у открытого ящика она
   прячется: содержимое уже перед глазами. */
.uadm > summary .fine { margin: 0 0 0 auto; text-transform: none; letter-spacing: 0; }
.uadm[open] > summary .fine { display: none; }
.uadm .body { padding: 12px 14px 14px; border-top: 1px solid var(--rule); }
.mdecs { display: flex; flex-direction: column; gap: 14px; }
.mdecs fieldset { margin: 0; padding: 0; border: 0; min-width: 0; }
.mdecs legend {
  padding: 0; font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(--meta);
}
.mdecs .mdec { margin-top: 6px; }
.mdecs > .fine { margin: 0; }

/* Записи — закладками. Закладка остаётся ССЫЛКОЙ (?t=), открытая помечена
   чертой снизу: полоска, из которой текущая кнопка исчезает, читается как
   другая полоска. */
.utabs {
  display: flex; flex-wrap: wrap; align-items: flex-end;
  border-bottom: 1px solid var(--rule); margin: 22px 0 0;
  scroll-margin-top: 60px;
}
.utabs a {
  display: flex; align-items: baseline; gap: 9px; padding: 9px 16px;
  font-size: 17px; color: var(--meta); text-decoration: none;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.utabs a:hover { color: var(--ink); }
.utabs a[aria-current] { color: var(--ink); background: var(--btn); border-bottom-color: var(--accent); }
.utabs .cnt { font-size: 12px; color: var(--meta); }

.upub { list-style: none; margin: 0; padding: 0; }
/* Строка в два уровня: текст, под ним дата. В одну строку длинная выдержка
   отжимала дату за край. */
.upub li { border-bottom: 1px solid var(--rule); padding: 11px 0; }
.upub li .fine { display: block; margin-top: 4px; overflow-wrap: anywhere; }
.upub a { overflow-wrap: anywhere; line-height: 1.5; }
/* «← К последним» слева, «Раньше →» справа: направление ссылки совпадает с
   её стрелкой. */
.upubnav { display: flex; flex-wrap: wrap; gap: 8px 18px; font-size: 13px; margin: 14px 0 0; }
.upubnav .older { margin-left: auto; }
.sub .cnt { font-size: 13px; font-weight: 400; }

/* ------------------------------------------------- своя страница: вкладки и правка

   Вкладок две — «Профиль» и «Настройки», — а страница одна, и ярлык у полоски
   называет её вслух: «Моей страницей» зовут /me все пять опубликованных
   согласий («отозвать можно на „Моей странице"»), редакции неизменяемы, и
   пришедший по документу обязан увидеть на экране те же слова. */
.metabs { display: flex; gap: 6px; align-items: baseline; flex-wrap: wrap; margin: 0 0 18px; }
.metabs .lbl {
  font-size: 12px; text-transform: uppercase; letter-spacing: .1em;
  color: var(--meta); margin-right: 8px;
}
.metabs a {
  padding: 4px 12px; border: 1px solid var(--rule); border-radius: 3px;
  text-decoration: none; font-size: 14px;
}
.metabs a:hover { border-color: var(--accent); }
/* Открытая вкладка остаётся ССЫЛКОЙ и лишь помечена заливкой: полоска, из
   которой текущая кнопка исчезает, читается как другая полоска. */
.metabs a.on { background: var(--btn); color: var(--ink); border-color: var(--accent); }
.metabs + .lead { margin: 0 0 22px; color: var(--meta); }

/* Правка на месте: свёрнутая форма рядом с тем, что она правит. Пунктирная
   рамка у свёрнутой — чтобы её не путали с кнопкой действия: она ничего не
   делает, она открывает. */
.edit { margin: 0; }
.edit > summary {
  cursor: pointer; color: var(--link); font-size: 14px;
  display: inline-block; padding: 4px 12px;
  border: 1px dashed var(--rule); border-radius: 3px;
}
.edit > summary:hover { border-color: var(--accent); }
.edit[open] > summary { margin-bottom: 12px; border-style: solid; }
.edit > form, .edit > p, .edit > div { margin-top: 10px; }
/* Формы своей страницы — рядком свёрнутых строк: раскрытая встаёт своей
   строкой во всю ширину, свёрнутые стоят бок о бок. */
.uedits { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-start; margin: 0 0 14px; }
.uedits > .edit[open] { flex: 1 1 100%; }

/* Альбом ХОЗЯИНА — в ящике правки, квадратами покрупнее миниатюр: под каждым
   две кнопки, и строкой справа они уехали бы к краю и перестали читаться как
   кнопки ЭТОЙ фотографии. */
.uphotos { display: grid; grid-template-columns: repeat(3, minmax(0, 110px)); gap: 8px; }
.uphotos figure { margin: 0; }
.uphotos a {
  display: block; aspect-ratio: 1; border: 1px solid var(--rule); border-radius: 3px;
  overflow: hidden;
}
/* Пол на случай, если aspect-ratio не понят: без него ссылка схлопнется в ноль. */
@supports not (aspect-ratio: 1) {
  .uphotos a { min-height: 90px; }
}
.uphotos img { width: 100%; height: 100%; object-fit: cover; display: block; }
.uphotos .rm {
  display: block; width: 100%; margin-top: 4px; padding: 2px 0;
  font-size: 11px; line-height: 1.4; border: 1px solid var(--rule); border-radius: 3px;
  background: none; color: var(--meta); cursor: pointer;
}
.uphotos .rm:hover { border-color: var(--female); color: var(--female); }
/* Поле файла: браузер рисует его во всю ширину, и без max-width оно
   растянуло бы ящик за край телефона. */
.addph input[type="file"] { display: block; max-width: 100%; margin: 8px 0; font-size: 12px; }

/* Жители в панели администратора: фото слева, как везде, чтобы менять его
   глядя ровно на то, что меняешь. */
.plist { list-style: none; margin: 0; padding: 0; }
.pitem { border-top: 1px solid var(--rule); padding: 12px 0; display: flex; gap: 16px; }
.pbody { min-width: 0; flex: 1; }

.modlog { list-style: none; margin: 0; padding: 0; }
.lgi {
  border-top: 1px solid var(--rule); padding: 6px 0; font-size: 13px;
  display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline;
}
.lgact { font-weight: 600; }
.lgwho { color: var(--meta); }
.lgwhy { color: var(--meta); }

/* ---------------------------------------------------------------- справка */

/* Справка — единственная страница, которую пишет человек, а не запрос. Мера
   здесь другая, чем в ленте: не «как на НГС», а «читается с экрана телефона».
   Отсюда узкая колонка (44rem — примерно 70 знаков в строке) и крупный
   межстрочный интервал. */
.help { max-width: 44rem; }
.help .lead { font-size: 16px; margin: 0 0 20px; }
.help section { margin: 0 0 26px; }
.help h2 { font-size: 20px; font-weight: 600; margin: 26px 0 10px; }
.help p { margin: 10px 0; line-height: 1.55; }
.help ul, .help ol { margin: 10px 0; padding-left: 22px; line-height: 1.55; }
.help li { margin: 6px 0; }
/* Пригласительная ссылка в мессенджер — это 50–60 знаков без единого пробела, и
   переносить её браузеру негде. На узком экране (замер 18.08.2026: аппарат с
   крупным системным шрифтом даёт viewport 288px) такая строка распирает
   страницу вбок. Тот же приём, что у .text и .upub a. */
#contacts a { overflow-wrap: anywhere; }
/* Оглавление — плитками, а не списком: на телефоне список из одиннадцати
   названий занимает экран целиком. Стоит оно ВНИЗУ темы («Другие разделы»):
   наверху страницы место ответа, ради которого сюда и пришли. */
.toc { display: flex; flex-wrap: wrap; gap: 6px 10px; margin: 0 0 24px; }
.toc a {
  font-size: 13px; padding: 4px 10px; border: 1px solid var(--rule);
  border-radius: 3px; background: var(--btn);
}
.toc a:hover { text-decoration: none; border-color: var(--link); }
/* Карточки на оглавлении: название темы и строка, чем она отличается от
   соседней. Колонки набираются сами (auto-fill), поэтому на телефоне это один
   столбец, а на широком экране — три; отдельной мобильной ветки не нужно. */
.hcards {
  display: grid; gap: 10px; margin: 0 0 26px;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}
.hcard {
  display: block; padding: 10px 12px; border: 1px solid var(--rule);
  border-radius: 3px; background: var(--btn); color: var(--ink);
}
.hcard:hover { text-decoration: none; border-color: var(--link); }
.hcard b { display: block; color: var(--link); }
.hcard span { display: block; margin-top: 2px; font-size: 12px; color: var(--meta); }
/* «Справка → Как читать»: строка над заголовком темы. Мелкая и приглушённая —
   это дорога назад, а не содержание. */
.crumb { font-size: 12px; color: var(--meta); margin: 0 0 6px; }
/* Значок ВНУТРИ строки справки. Тот же svg, что в интерфейсе, поэтому размер и
   цвет задаются здесь, а не в шаблоне: значок обязан читаться в тексте, а не
   спорить с ним, — отсюда приглушённый цвет и выравнивание по строке. */
.hico { display: inline-flex; align-items: center; vertical-align: -4px; color: var(--meta); }
.hico .ico { width: 18px; height: 18px; }
/* Список, где у каждого пункта свой значок: маркер убран — его роль исполняет
   сам значок, а два маркера в строке читаются как опечатка. */
.hicons { list-style: none; padding-left: 0; }
/* Значок ВЫНЕСЕН в поле, а текст пункта течёт как обычно. Через flex нельзя:
   он делает flex-элементом каждого ребёнка, и жирное имя со ссылками внутри
   пункта разъезжается по колонкам (поймано глазом на первом же рендере). */
.hicons li { position: relative; padding-left: 26px; }
.hicons > li > .hico { position: absolute; left: 0; top: 3px; }
/* ЖИВОЙ СНИМОК куска интерфейса внутри справки: та же разметка и те же стили,
   что на настоящей странице, — поэтому он не устаревает и живёт во всех
   палитрах. Рамка и подпись отделяют его от текста: без них карточка читается
   как настоящая заметка, попавшая в справку по ошибке.

   Нажатия внутри запрещены дважды. inert снимает и фокус, и клики — но он
   моложе площадки, поэтому под ним лежит pointer-events: ссылка «Комментарии»
   ведёт в заметку, которой не существует. */
.demo {
  margin: 14px 0 4px; padding: 10px; border: 1px dashed var(--rule);
  border-radius: 3px; pointer-events: none;
}
.demo .notes { margin: 0; }
.demo .note { border: 0; padding: 0; }
/* Панель смайлов внутри снимка обязана открываться — это единственное нажатие,
   которое здесь осмысленно (справочник), и pointer-events ему возвращаются. */
.demo .smbox { pointer-events: auto; }
/* Ряд образцов: кнопки реакций и смайлы с их кодами. */
.hrow { display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: center; }
/* Схема устройства — РЯД ТЕКСТОВЫХ КОРОБОК, а не картинка.
   Причина не в лени: рисунок, сведённый к ширине телефона, отдал бы подписям
   шесть точек кегля, а здесь текст остаётся текстом — его можно увеличить,
   прочитать вслух читалкой и выделить. Ряд складывается в столбец сам
   (flex-wrap), и на узком экране стрелка поворачивается вниз.
   Цвета — токены, значит все шесть палитр даром. */
.flow { display: flex; flex-wrap: wrap; align-items: stretch; gap: 8px; margin: 14px 0; }
.fbox {
  flex: 1 1 9rem; min-width: 8rem; padding: 8px 10px;
  border: 1px solid var(--rule); border-radius: 3px; background: var(--btn);
}
.fbox b { display: block; font-size: 13px; }
.fbox span { display: block; margin-top: 2px; font-size: 11px; color: var(--meta); }
/* Стрелка — знак, а не картинка: читалке она не нужна (aria-hidden), а глазу
   говорит направление. Ширина фиксирована, чтобы коробки не прыгали. */
.farr { flex: 0 0 auto; align-self: center; color: var(--meta); font-size: 15px; }
@media (max-width: 560px) {
  .flow { flex-direction: column; }
  .farr { align-self: flex-start; margin-left: 12px; transform: rotate(90deg); }
}
/* Оглавление внизу темы отделено чертой: под ним уже не текст раздела. */
.more { margin: 30px 0 0; padding-top: 14px; border-top: 1px solid var(--rule); }
.more h2 { font-size: 15px; margin: 0 0 10px; }
.more .toc { margin: 0; }
/* Команды бота: слева команда, справа что делает. На узком экране столбцы
   складываются в строки — определение уходит под термин, а не жмётся в колонку
   шириной в слово. */
.cmds { margin: 10px 0; display: grid; grid-template-columns: max-content 1fr; gap: 6px 14px; }
.cmds dt { white-space: nowrap; }
.cmds dd { margin: 0; }
@media (max-width: 560px) {
  .cmds { display: block; }
  .cmds dt { margin-top: 10px; }
  .cmds dd { margin: 2px 0 0 14px; color: var(--meta); }
}
/* Номер правила виден: на него ссылаются в решениях модератора. */
.rules li { margin: 8px 0; }
.help code {
  font: 90%/1 "Cascadia Mono", Consolas, "DejaVu Sans Mono", monospace;
  padding: 1px 4px; border-radius: 3px; background: var(--btn);
}

/* ---------------------------------------------------------------- подвал */

.foot { flex: 0 0 auto; border-top: 1px solid var(--rule); background: var(--card); padding: 12px 0 24px; }
/* Ряд документов. Разделитель между ссылками рисуется не символом в разметке
   (точку или палку читалка озвучит), а промежутком: на узком экране ряд
   переносится строкой, и в вертикальном списке разделитель был бы лишним.
   Цвет ссылок обычный, а не приглушённый «служебный»: мелкий текст цветом
   --meta на карточке даёт 2,85:1 в палитре НГС, и политику пришлось бы
   разглядывать. */
.fnav { display: flex; flex-wrap: wrap; gap: 4px 16px; font-size: 12px; }
.fine { color: var(--meta); font-size: 11px; margin: 10px 0 0; }

.sr-only {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* На телефоне колонка автора ужимается, а отступ ветки уменьшается вчетверо:
   иначе десятый уровень уезжает за край экрана. Отдельной мобильной вёрстки
   у нас нет — в отличие от НГС, где их две и правки в них расходятся. */
@media (max-width: 700px) {
  :root { --step: 8px; }
  /* На телефоне альбом уходит под факты, а не жмётся к их правому краю, и
     закладки ужимаются: две с числом в пять знаков иначе не встают в строку. */
  .ufacts .ualbum { margin-left: 0; }
  .utabs a { padding: 8px 12px; font-size: 16px; }
  /* ПОЛЕ СЛЕВА ОДНО, А НЕ ДВА ВЛОЖЕННЫХ (жалоба владельца 26.08.2026:
     «большой промежуток между краем экрана и полосой закрепления»). Полей тут
     стояло две штуки одно в другом — 10px у .main и ещё 10px у .wrap внутри,
     — итого 40px на обе стороны, десятая часть экрана в 389 пикселей.
     На десктопе внешнее поле осмысленно: карточка ЛЕЖИТ на странице, и вокруг
     неё видна подложка. На телефоне карточка идёт от края до края (margin: 0
     строкой ниже), подложки не видно вовсе — и второе поле держало пустоту
     ради вида, которого нет. */
  .main { padding: 12px 12px 20px; margin: 0; }
  .main .wrap { padding: 0; }
  /* ЧИТАЕМОСТЬ ПРОТИВ ПУСТОТЫ (жалоба владельца 23.08.2026: «слишком мелко и
     при этом слишком много свободного пространства»). Числа НГС здесь не указ:
     14px и три отдельные строки служебного под каждой репликой — это мера
     десктопа, где строка втрое длиннее, а курсор точнее пальца. На телефоне
     тот же набор давал однострочную реплику высотой в четыре строки: текст,
     реакции, «Ответить», полоска модератора.
     Текст читают — ему 16px, столько же, сколько браузер считает нормой для
     страницы вообще; служебное вокруг остаётся мелким, потому что оно и есть
     служебное. */
  .text { font-size: 16px; line-height: 1.5; }
  .chead .nick { font-size: 15px; }
  .cact { font-size: 13px; gap: 12px; }
  /* Правило «реакции, „Ответить“ и полоска модератора идут ОДНОЙ строкой»
     стояло здесь с 23.08.2026, а 02.09.2026 уехало в базовые стили: на
     десктопе «+» занимал лишнюю строку ровно по той же причине. Второй копии
     тут нет намеренно — разъехались бы они молча. */
  .c { gap: 12px; padding: 10px 0 8px; }
  .ttl { font-size: 22px; }
  .note { min-height: 0; }
  /* КОЛОНКА АВТОРА ЗАПОЛНЯЕТСЯ ЦЕЛИКОМ (второй заход, 26.08.2026: «слева много
     места теряется, аватарку можно увеличить, и чтобы Паноптикум влезал в одну
     строку»). Три просьбы разом решаются одним: аватар становится РАВЕН
     колонке. Пустые поля по бокам маленькой картинки и были тем самым
     потерянным местом — колонка при этом не выросла, выросла картинка.
     Заодно ник получает всю её ширину и перестаёт рваться посреди слова
     («Панопт/икум» с первого снимка). */
  .author { width: 84px; flex-basis: 84px; }
  .ava { width: 84px; height: 84px; }
  .author .nick { font-size: 13px; }
  /* Зазор между колонкой автора и текстом, наоборот, РАСТЁТ (та же жалоба:
     «между аватаром и текстом кажется можно увеличить»). Первым заходом он был
     ужат до 8px ради ширины текста — платить за неё было больше нечем; теперь
     есть, её вернуло снятое второе поле, и с запасом. 14px — это видимая
     граница двух столбцов: ниже десяти они читаются как один. */
  .note { gap: 14px; }
  .note.pinned { padding-left: 8px; }
  .cava { flex-basis: 56px; }
  .cava .ava { width: 56px; height: 56px; }
  /* СЛУЖЕБНАЯ СТРОКА ПОД ЗАМЕТКОЙ — ОДНА, А НЕ ТРИ (жалоба владельца
     26.08.2026: «элементы слишком мелкие, какие-то съехали в асимметричную
     кучку»). До этого дня «Комментарии N» и «Добавить комментарий» стояли
     display:block каждый со своей строки — три этажа по 12px под каждой из
     двадцати заметок ленты. Теперь это ряд, который переносится сам.

     И это ряд ПОД ПАЛЕЦ: 14px вместо 12 и высота 34px у нажимаемого. Цифра не
     из воздуха — палец накрывает около 9 мм, а 12px-ссылка высотой 16px
     означает «целься». Строка от этого не выросла: три этажа занимали больше,
     чем один ряд с запасом. */
  .nfoot { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 12px; font-size: 13px; }
  /* Подвал здесь — флексбокс с собственным зазором, поэтому поля у меток
     снимаются: иначе к 12px зазора прибавилось бы правое поле метки. */
  .date, .orig, .edited { margin-left: 0; }
  .nfoot .orig { margin-right: 0; }
  .nfoot .rx { margin-left: 0; }
  .clink, .addc {
    margin-left: 0; display: inline-flex; align-items: center; gap: 5px;
    min-height: 34px; font-size: 14px;
  }
  /* «Комментарии» становится значком: тем же самым, что стоит в липкой шапке,
     — человек видит его там всё время, пока читает тред. Слово не удаляется, а
     прячется .sr-only: оно остаётся именем ссылки для читалки, а объяснение
     для глаза даёт title (правило Ш5з). */
  .modbar .ico, .clink .ico, .dlgacts .ico { display: inline-block; }
  .modbar .lbl, .clink .lbl, .dlgacts .lbl {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap;
  }
  /* ПОЛОСКА МОДЕРАТОРА — ЗНАЧКАМИ И В ОДНУ СТРОКУ (решение владельца
     26.08.2026). Пять подписей словами в строку не помещались и складывались
     кучей: «Скрыть» и «Закрыть обсуждение» в первый ряд, «Открепить» и два
     серых слова во второй. Значками пять кнопок это 5×40 + 4×6 = 224px, а под
     полоску на экране в 360 CSS-пикселей остаётся 254 — помещается.

     flex-wrap оставлен СТРАХОВКОЙ, а не отменён: на 288 пикселях (аппарат с
     крупным системным шрифтом) ряд не влезает и переносится. Перенос честнее
     прокрутки вбок: уехавшую за край кнопку не видно вовсе, а искать её внутри
     ленты никто не станет.

     40px — это и есть размер под палец; у кнопок он берётся минимумами, а не
     кеглем, потому что содержимое у них теперь значок, а не текст. */
  .modbar { gap: 6px; }
  /* ПОЛОСКА НАД РАЗГОВОРОМ — ЗНАЧКАМИ И В ОДНУ СТРОКУ С АВАТАРОМ (просьба
     владельца 12.09.2026). Три подписи словами («Отметить прочитанным»,
     «Убрать у себя», «Закрыть переписку») в строку с колонкой собеседника не
     помещались никогда, и .dlghead честно переносил их под неё второй кучкой —
     та же беда и то же лечение, что у полоски модератора выше.

     Своего правила «в строку» для этого не нужно, и это не лень: .dlghead и так
     флекс с переносом, а перенос случается, только когда не влезает. Считаем:
     на 360 CSS-пикселях колонка автора 84 (ветка выше), зазор 12, три кнопки
     по 40 с зазорами по 6 — это 216 из 360. Влезает и на 288, где колонка 72.
     Значит flex-wrap остаётся ровно тем, чем был, — страховкой, а не рабочим
     режимом; отними мы его (nowrap), кнопки при первом же новом соседе уехали
     бы за край, где их не видно вовсе. */
  .dlgacts { gap: 6px; }
  .minline { margin: 0; }
  .mbtn, .mlink {
    display: inline-flex; align-items: center; justify-content: center; gap: 6px;
    min-width: 40px; min-height: 40px; padding: 0 9px; font-size: 13px;
  }
  .mbtn .ico, .mlink .ico { width: 20px; height: 20px; }
  /* «Поправить» и «Автор» на телефоне тоже нажимают пальцем, поэтому получают
     рамку: серое слово без границ на сенсорном экране не читается как кнопка —
     и именно поэтому «Поправить Автор» слипались в одну фразу. */
  .mlink { border: 1px solid var(--rule); border-radius: 3px; }
  .mlink + .mlink::before { content: none; }
  /* А РАМКИ РАЗГОВОРА НА ТЕЛЕФОНЕ НЕТ ВОВСЕ (жалоба владельца 12.09.2026,
     в тот же вечер, что и сама рамка: «кнопка отправки уезжает вниз и её не
     видно; по полю с сообщениями вниз не проскроллить, нужно попасть по тонкой
     полоске страницы. Если об этом не знать, то отправить сообщение весьма
     затруднительно»).
     Считается это, а не выбирается: у аппарата с viewport 384×719 карточка
     собеседника с тремя кнопками занимает 240 точек, рамка в 60vh — ещё 430, и
     форма ответа начинается за нижним краем экрана. То есть единственное, ради
     чего рамка заведена, — «поле ответа остаётся на виду» — на этой ширине
     недостижимо ВООБЩЕ: даже уместись форма, открытая клавиатура съедает
     половину экрана. Платить же за недостижимое приходится двумя поверхностями
     прокрутки под одним пальцем.
     Взамен страница снова прокручивается ОДНА, как всякая другая страница
     площадки, а обещание «разговор открывается последним письмом» исполняет тот
     же скрипт — только двигает он страницу (app.js, talkToEnd): подводит к форме
     ответа, и хвост разговора встаёт над ней, то есть даёт ровно то, что на
     большом экране даёт рамка. Граница и поля при этом остаются: рамка отделяет
     разговор от формы, а без неё письма слились бы с полем ввода. */
  .lts.talk { max-height: none; overflow-y: visible; }
  /* Шапка и подвал выровнены по КОЛОНКЕ ТЕКСТА, а не по краю экрана: название
     площадки и значок участника упирались в самый край (жалоба владельца
     18.08.2026). Число здесь ОДНО с полем .main выше и обязано ехать вместе с
     ним — было 20px, стало 12: шапка, отбитая не по тексту, читается как
     съехавшая. Ширины у заметки это по-прежнему не отнимает: отступ выдан
     шапке и подвалу поимённо, а не всем .wrap разом. */
  .top .wrap, .foot .wrap { padding: 0 12px; }
  /* Ник рядом со значком уступает место: меню и без него понятно, а ширина в
     шапке кончается раньше всего. */
  .acctbtn .who { display: none; }
  /* Переключатель уходит из угла в свою строку, а резерв под него снимается
     совсем: 76px на телефоне — это треть строки. */
  .switch { position: static; justify-content: flex-end; margin: 0 0 6px; }
  .notebox .nbody { padding-right: 0; }
  /* Вкладки шапки ужимаются вместе с названием: «Зазеркалье», «Лента» и
     «Справка» в прежнем кегле не оставляли места значку участника, и он уезжал
     второй строкой. Переносу это не мешает — flex-wrap остаётся страховкой. */
  .brand { font-size: 16px; }
  .topin { gap: 8px 10px; }
  .tab { padding: 5px 8px; font-size: 13px; }
  /* Счётчик реплик ужимается вместе с ними, и по той же причине: строка шапки на
     телефоне считается по знакам. Со счётчиком в ней пять элементов вместо
     четырёх, и лишние десять пикселей у каждого — это перенос на вторую строку,
     то есть липкая шапка в пол-экрана. */
  .hcount { padding: 4px 8px; font-size: 12px; gap: 4px; }
  .hcount .ico { width: 16px; height: 16px; }
  /* ПОСТРАНИЧКА — НА ВСЮ ШИРИНУ И ПОД ПАЛЕЦ (просьба владельца 26.08.2026:
     «слишком мелкий, растяни на всю ширину и сделай кнопки крупнее»). До этого
     дня это была десктопная строка, ужатая до телефона: кегль 13, высота 26,
     кучка в левом углу — а справа полстроки пустоты. Мимо такой кнопки
     промахнуться легче, чем попасть, и промах тут дороже обычного: уехал не в
     ту страницу ленты, а вернуться нечем — «назад» у постранички нет.

     Растягивает её flex, и доли получаются НЕРОВНЫЕ нарочно: flex-basis auto
     оставляет каждому номеру его собственную ширину («5887» шире единицы и
     обязан остаться шире), а поровну делится только свободное место. Ровные
     доли порвали бы четырёхзначный номер либо раздули одноклеточную единицу на
     пол-экрана.

     Одной строкой она держится с 23.08.2026 («5867» и «След. »» уезжали вниз,
     отрываясь от номеров), и держат её по-прежнему разметка и показ: дальние
     номера и слова у стрелок снимаются, а вместо спрятанного хвоста встаёт своё
     многоточие. flex-wrap не заводится НАРОЧНО — перенос вернул бы ровно тот
     обломок второй строки, из-за которого правило и появилось; overflow-x
     остаётся страховкой на системный шрифт вдвое крупнее обычного. */
  .pages { display: flex; gap: 4px; white-space: nowrap; overflow-x: auto; }
  .pg {
    flex: 1 1 auto; display: flex; align-items: center; justify-content: center;
    min-width: 34px; min-height: 40px; padding: 0 8px; margin: 0; font-size: 15px;
  }
  /* Многоточие и погашенная стрелка — знаки, а не кнопки: нажимать в них
     некуда, и доля общей ширины, которую они бы забрали, отнялась бы у номеров,
     то есть у единственного, во что здесь целятся. */
  .pg.gap, .pg.off { flex: 0 0 auto; min-width: 0; }
  .pg.arr .wd { display: none; }
  .pg.far { display: none; }
  .pg.gap.mob { display: flex; }
}

/* Слово «Зазеркалье» уходит из шапки, знак остаётся. Порог считается по самой
   тесной шапке — странице заметки у вошедшего: название, «Лента / Справка»,
   счётчик реплик, колокольчик и меню участника. На 360 CSS-пикселях им нужно
   на полсотни больше, чем есть, и угол «про меня» уезжал второй строкой
   (жалоба владельца 23.08.2026): липкая шапка в две строки съедает пол-экрана
   треда. Выше 460px слово помещается — и остаётся: имя площадки читают, а не
   опознают по значку. */
@media (max-width: 460px) {
  /* Подсказка служебного ящика на узком экране ломала ярлык в две строки;
     что внутри, скажет сам ящик, открывшись. */
  .uadm > summary .fine { display: none; }
  .brand .bname { display: none; }
  /* Знак остаётся один, и нажимать теперь надо в него: без слова у ссылки
     осталось бы 20 пикселей на палец. Растёт при этом высота, а не ширина —
     ширина здесь и есть дефицит. */
  .brand .mark { width: 22px; height: 22px; }
  .brand { padding: 6px 2px; }
  /* Тем же заходом ужимается угол «про меня»: 12px по бокам у двух значков —
     это ещё четверть того, что дало снятое слово. */
  .bell, .acctbtn { padding-left: 9px; padding-right: 9px; }
}

/* Совсем узкий экран — не редкость: аппарат 1080px с крупным системным шрифтом
   даёт viewport 288 CSS-пикселей. Отступ ветки и аватар реплики ужимаются ещё,
   иначе от колонки текста в глубине треда остаётся полслова. */
@media (max-width: 400px) {
  :root { --step: 6px; }
  .c { gap: 10px; }
  .cava { flex-basis: 48px; }
  .cava .ava { width: 48px; height: 48px; }
  /* На 288 CSS-пикселях (аппарат с крупным системным шрифтом) колонка ужимается
     вместе со всем прочим, но правило то же: аватар равен колонке, пустых полей
     нет. Ник здесь на кегль мельче — иначе «Паноптикум» опять не помещается. */
  .note { gap: 10px; }
  .note.pinned { padding-left: 6px; }
  .author { width: 72px; flex-basis: 72px; }
  .ava { width: 72px; height: 72px; }
  .author .nick { font-size: 12px; }
  /* 288 CSS-пикселей — реальный viewport телефона с крупным системным шрифтом.
     Постраничка на нём считается по знакам: пять кнопок, из них одна с
     четырёхзначным номером. Ужимаются ширина и кегль, а ВЫСОТА не трогается:
     сорок пикселей — это палец, и на узком экране он не худеет. */
  .pg { min-width: 30px; padding: 0 6px; font-size: 14px; }
  /* Шапка на этой ширине считается по знакам так же, как постраничка: у вкладок
     снимается ещё по паре пикселей, у счётчика — кегль. Знак площадки при этом
     не трогается: он последнее, что от названия осталось. */
  .tab { padding: 5px 7px; font-size: 12px; }
  .hcount { padding: 4px 7px; }
}

@media (min-width: 900px) {
  :root { --step: 40px; }
}

/* Согласие отзывается необратимо, и кнопка обязана выглядеть иначе
   остальных: та же краска, что у «Отозвать» в списке согласий. */
.wact button.danger { border-color: var(--female); color: var(--female); }
.revlist { margin: 14px 0 0; padding-left: 20px; line-height: 1.5; }
.revlist li { margin-bottom: 8px; }

/* Картинка заметки в очереди модератора. Миниатюрой и с потолком по высоте:
   очередь обязана читаться за минуту, а не пролистываться. */
/* Фотография в очереди модератора — вместе со своей кнопкой: вердикт судит
   карточку целиком, а снимок скрывают поштучно, и кнопка обязана стоять у того
   снимка, про который она. */
.mphoto { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap; margin: 8px 0; }

.mshot { display: block; margin: 8px 0; }
.mshot img { max-width: 100%; max-height: 200px; width: auto; height: auto; display: block; border: 1px solid var(--rule); }

/* Ссылка на смежное обсуждение — строка под заголовком треда. Значок и текст в
   одной строке, поэтому inline-flex: иконка внутри ссылки не должна съезжать
   относительно текста. Число — той же плашкой, что счётчик в шапке. */
.synth { margin: 0 0 10px; }
.synth a { display: inline-flex; align-items: center; gap: 6px; }
.synth .cnt { font-weight: 600; }

/* ─── Пятничный вопрос (эпик J, web/quiz.go) ───────────────────────────────
   Варианты под репликой, которая их задаёт. СВОИХ ЦВЕТОВ здесь нет намеренно:
   палитр у площадки шесть, и токен, объявленный в одной, молча ломает
   остальные пять (см. TestDarkPalettesRestateWhatBaseWroteForWhite). Верный
   ответ показан --accent, который есть везде, а «мимо» — словом: красный
   пришлось бы заводить шестикратно ради одной строки.

   Ширина полосы едет КЛАССОМ: CSP без unsafe-inline запрещает и атрибут style,
   тем же правилом, по которому глубина ветки это d1…d12. Шаг пять процентов —
   двадцать один класс вместо ста одного; точное число стоит рядом словом. */
.quiz { margin: 8px 0 0; }
.qopts { display: flex; flex-direction: column; gap: 6px; margin: 0; padding: 0; list-style: none; }
.qform { margin: 0; }
.qbtn {
  display: block; width: 100%; text-align: left; cursor: pointer;
  padding: 8px 11px; border: 1px solid var(--rule); border-radius: 4px;
  background: var(--btn); color: var(--ink); font: inherit; line-height: 1.4;
}
.qbtn:hover { border-color: var(--accent); }
/* Ответ дан: варианты перестают быть кнопками и становятся строками со счётом. */
.qo {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 7px 11px; border: 1px solid var(--rule); border-radius: 4px;
  background: var(--btn); line-height: 1.4;
}
.qo.qright { border-color: var(--accent); }
/* Свой выбор — жирным, а не заливкой: заливка спорила бы с полосой счёта. */
.qo.qmine .qtext { font-weight: 600; }
.qtext { flex: 1 1 12em; }
.qbar {
  display: block; flex: 0 0 84px; height: 6px; border-radius: 3px;
  background: var(--page); overflow: hidden;
}
.qfill { display: block; height: 100%; background: var(--accent); }
.qpct { flex: 0 0 auto; color: var(--meta); font-size: 12px; font-variant-numeric: tabular-nums; }
.qverdict { margin: 8px 0 0; font-size: 14px; }
.qhit { color: var(--accent); }
.qmiss { color: var(--meta); }
.qreveal { color: var(--ink); }
.qsrc, .qguest, .qtotal { margin: 4px 0 0; font-size: 13px; color: var(--meta); }
.qfill.qw0 { width: 0%; }
.qfill.qw5 { width: 5%; }
.qfill.qw10 { width: 10%; }
.qfill.qw15 { width: 15%; }
.qfill.qw20 { width: 20%; }
.qfill.qw25 { width: 25%; }
.qfill.qw30 { width: 30%; }
.qfill.qw35 { width: 35%; }
.qfill.qw40 { width: 40%; }
.qfill.qw45 { width: 45%; }
.qfill.qw50 { width: 50%; }
.qfill.qw55 { width: 55%; }
.qfill.qw60 { width: 60%; }
.qfill.qw65 { width: 65%; }
.qfill.qw70 { width: 70%; }
.qfill.qw75 { width: 75%; }
.qfill.qw80 { width: 80%; }
.qfill.qw85 { width: 85%; }
.qfill.qw90 { width: 90%; }
.qfill.qw95 { width: 95%; }
.qfill.qw100 { width: 100%; }

/* ------------------------------------------------- «Гримуар» и «Витраж»

   Ниже — только ВИД: цвет, шрифт и свечение. Ни одно правило здесь не двигает
   и не перекладывает ничего, потому что устройство страницы одно на все шесть
   палитр, а тема отвечает за палитру. Правило, положившее сетку на
   [data-theme], означало бы, что в четырёх остальных палитрах страница
   осталась без сетки, — и заметили бы это не сразу, а когда кто-то переключит
   тему.

   Селекторы длинные намеренно: правка темы не должна однажды стать правкой
   всей площадки. Отсюда же следствие про КЕГЛЬ: `:root[data-theme] .ttl` — это
   (0,3,0), а мобильная ветка `.ttl { font-size: 22px }` — (0,1,0), и
   медиазапрос специфичности не добавляет. Значит всякий кегль, назначенный
   здесь, ОБЯЗАН иметь свою мобильную ветку, иначе заголовок в 34px останется
   таким и на 360 точках.

   Из хендоффа НЕ перенесена метка происхождения точкой (решение владельца
   12.09.2026): значков четыре, и один из них — песочница, единственное
   предупреждение читателю «здесь говорит машина». Цветом точки его не
   заменить, а спрятать значок значило бы снять предупреждение.

   И граница, чтобы её не искали заново: правило Ш5з — про МЕТКИ, а не про
   узоры. Латунная линия, дорожка под шапкой и блик по зеркалу ничего не
   сообщают, объяснять их заголовком не нужно и не надо. */

/* Заголовки антиквой — фасад. Тело не трогается ни здесь, ни где-либо ещё:
   заметку в 600 реплик читают телом, а не заголовками, и сорок тысяч знаков
   политики — тоже. Заголовки документов, входа и справки взяты поимённо: они
   не .ttl и не .sub, а остаться единственным островом Arial посреди антиквы
   хуже, чем не переходить на неё вовсе. */
:root[data-theme="grimoire"] .ttl,
:root[data-theme="stained"] .ttl,
:root[data-theme="grimoire"] .cttl,
:root[data-theme="stained"] .cttl,
:root[data-theme="grimoire"] .sub,
:root[data-theme="stained"] .sub,
:root[data-theme="grimoire"] .brand,
:root[data-theme="stained"] .brand,
:root[data-theme="grimoire"] .enterbox h1,
:root[data-theme="stained"] .enterbox h1,
:root[data-theme="grimoire"] .doctext h1,
:root[data-theme="stained"] .doctext h1,
:root[data-theme="grimoire"] .doctext h2,
:root[data-theme="stained"] .doctext h2,
:root[data-theme="grimoire"] .help h2,
:root[data-theme="stained"] .help h2 {
  font-family: "Grim Serif", Georgia, serif;
  letter-spacing: .01em;
}
:root[data-theme="grimoire"] .ttl,
:root[data-theme="stained"] .ttl { font-size: 34px; line-height: 1.1; }
:root[data-theme="grimoire"] .brand,
:root[data-theme="stained"] .brand { font-size: 20px; }

/* Служебное моноширинным: дата, номера страниц, «сколько реплик», код входа.
   Всё, что человек не читает, а СВЕРЯЕТ или переписывает руками.

   .cnt перечислен по местам, а не взят целиком: тот же класс носит кружок
   колокольчика (.bell .cnt, min-width 15px), и «99+» моноширинным его
   распирает. */
:root[data-theme="grimoire"] .date,
:root[data-theme="stained"] .date,
:root[data-theme="grimoire"] .cdate,
:root[data-theme="stained"] .cdate,
:root[data-theme="grimoire"] .pg,
:root[data-theme="stained"] .pg,
:root[data-theme="grimoire"] .cttl .cnt,
:root[data-theme="stained"] .cttl .cnt,
:root[data-theme="grimoire"] .sub .cnt,
:root[data-theme="stained"] .sub .cnt,
:root[data-theme="grimoire"] .synth .cnt,
:root[data-theme="stained"] .synth .cnt,
:root[data-theme="grimoire"] .upub .fine,
:root[data-theme="stained"] .upub .fine,
:root[data-theme="grimoire"] .uhead .fine,
:root[data-theme="stained"] .uhead .fine,
:root[data-theme="grimoire"] .code,
:root[data-theme="stained"] .code {
  font-family: "Grim Mono", ui-monospace, Consolas, monospace;
}

/* Витраж в шапке — ГРАНЯМИ, а не картинкой: полосы разной прозрачности под
   углом. Ни одного файла, ни одного запроса, и на светлой сестре работает тот
   же код с другими токенами. */
:root[data-theme="grimoire"] .top,
:root[data-theme="stained"] .top {
  background-image: linear-gradient(115deg,
    var(--pane, transparent) 0 18%, transparent 18% 34%,
    var(--pane2, transparent) 34% 47%, transparent 47% 63%,
    var(--pane, transparent) 63% 74%, transparent 74% 88%,
    var(--pane2, transparent) 88%);
  border-bottom: 1px solid var(--accent);
}

/* Дорожка платы под шапкой: импульс пробегает по тонкой линии. Отдельного
   элемента не заводим — это ::after у шапки, разметка остаётся прежней, а сама
   шапка и так позиционирована (она липкая).

   Проходов ТРИ, а не бесконечность. Шапка липкая, то есть движение стояло бы
   у читателя перед глазами всё время, пока он читает тред в девятьсот реплик:
   WCAG 2.2.2 требует остановки для движения дольше пяти секунд рядом с
   текстом, а prefers-reduced-motion покрывает только тех, кто его выставил. */
:root[data-theme="grimoire"] .top::after,
:root[data-theme="stained"] .top::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: -2px; height: 2px;
  background-image: linear-gradient(90deg, transparent 45%, var(--nav-ink) 50%, transparent 55%);
  background-size: 600px 2px;
  background-repeat: no-repeat;
  animation: trace 4s linear 3;
  pointer-events: none;
}
@keyframes trace { from { background-position: 0 0; } to { background-position: 600px 0; } }

/* Знак площадки светится, и по нему изредка проходит блик. Свечение —
   drop-shadow, а не box-shadow: у зеркала есть форма, и квадратный ореол вокруг
   неё выдал бы, что это картинка в рамке.

   Блик висит на .brand, а не на самом знаке: знак — это <svg>, а
   псевдоэлементы на SVG браузеры не рисуют ВОВСЕ (хендофф этого не знал, и
   правило оттуда не работало бы ни в одном браузере). Бегунок сделан движением
   ФОНА, а не сдвигом элемента: ширина у .brand разная — ниже 460px имя
   площадки снимается показом, — и сдвиг, подобранный в пикселях, подошёл бы
   одной из двух. Проходов три, по тому же доводу, что у дорожки. */
:root[data-theme="grimoire"] .brand .mark,
:root[data-theme="stained"] .brand .mark {
  color: var(--brass, currentColor);
  filter: drop-shadow(0 0 6px var(--glow, transparent));
}
:root[data-theme="grimoire"] .brand,
:root[data-theme="stained"] .brand { position: relative; overflow: hidden; }
:root[data-theme="grimoire"] .brand::after,
:root[data-theme="stained"] .brand::after {
  content: "";
  position: absolute; inset: 0; pointer-events: none;
  background-image: linear-gradient(105deg, transparent 45%, rgba(255, 255, 255, .45) 50%, transparent 55%);
  background-size: 220% 100%;
  background-repeat: no-repeat;
  animation: gleam 5.5s ease-in-out 3;
}
@keyframes gleam {
  0%, 60% { background-position: -60% 0; }
  100%    { background-position: 160% 0; }
}

/* Движение — украшение, и у кого оно мешает, тот его не получает. Блик и
   импульс снимаются целиком: без них тема остаётся собой. */
@media (prefers-reduced-motion: reduce) {
  :root[data-theme="grimoire"] .top::after,
  :root[data-theme="stained"] .top::after,
  :root[data-theme="grimoire"] .brand::after,
  :root[data-theme="stained"] .brand::after { animation: none; display: none; }
}

/* ФОН — собор, картинка у каждой сестры своя (15.09.2026): у «Гримуара» неф
   ночью (assets/grimoire/nave.webp), у «Витража» тот же собор снаружи утром —
   две башни по краям и фасад в дымке (facade.webp). Пара узнаётся по
   композиции, а не по одному рисунку в двух яркостях: светлый неф владелец
   отверг, фасад принял.

   Рисунок, собранный кодом из стрельчатых арок, был отвергнут первым —
   «геометрически примитивный»; картинки сгенерированы по промпту, и главное в
   промпте было про КОМПОЗИЦИЮ: середину закрывает карточка, поэтому центр у
   обеих пустой (у нефа тёмный, у фасада светлое небо), а витражи, колонны и
   статуи живут по бокам. Исходники — PNG 3072×2048 по 7–8 МБ, в статике — WebP
   2560 по длинной стороне, 205 и 270 КБ: разницы на глаз нет, а качает его
   каждый читатель темы.

   Слой НЕПОДВИЖНЫЙ, и сделан он псевдоэлементом с position: fixed, а не
   background-attachment: fixed: тот лишает страницу быстрой прокрутки (фон
   перерисовывается на ходу), а iOS не исполняет его вовсе. Лежит слой под
   содержимым, но над фоном страницы, и держится это на двух условиях: фон
   body уходит на холст (у html своего нет), а у body нет ни z-index, ни
   transform — заведи их, и z-index: -1 провалится под фон самого body.

   Только от 960 точек: уже этого карточка (910) занимает всю ширину, полей нет
   и смотреть на фон негде. Картинка из несработавшего медиазапроса не
   скачивается, то есть телефон её двести килобайт не платит. Высота 100lvh —
   по большому вьюпорту: на планшете с плавающей панелью адреса слой иначе
   менял бы размер, а cover пересчитывал бы кадр на каждом её движении.

   Затемнения поверх нет (решение владельца 15.09.2026, вариант «как есть»):
   текст стоит только на непрозрачной карточке. В режиме высокой контрастности
   слой снимается — там цвета выбирает система, и картинка под её палитрой была
   бы шумом за текстом. */
@media (min-width: 960px) {
  :root[data-theme="grimoire"] body::before,
  :root[data-theme="stained"] body::before {
    content: "";
    position: fixed; left: 0; top: 0; z-index: -1;
    width: 100%; height: 100vh; height: 100lvh;
    background: var(--page) center / cover no-repeat;
    pointer-events: none;
  }
  :root[data-theme="grimoire"] body::before { background-image: url("/assets/grimoire/nave.0e660804.webp"); }
  :root[data-theme="stained"] body::before { background-image: url("/assets/grimoire/facade.a8242e9d.webp"); }
}
@media (forced-colors: active) {
  :root[data-theme="grimoire"] body::before,
  :root[data-theme="stained"] body::before { display: none; }
}

/* Латунная линия над карточкой: переплёт книги, единственная «золотая» деталь
   на странице. Больше ей быть нельзя — латунь работает, пока её мало. */
:root[data-theme="grimoire"] .main,
:root[data-theme="stained"] .main {
  border: 1px solid var(--rule);
  border-top: 1px solid var(--brass, var(--rule));
}
:root[data-theme="grimoire"] .pin,
:root[data-theme="stained"] .pin { color: var(--brass, var(--meta)); letter-spacing: .08em; }
:root[data-theme="grimoire"] .note.pinned,
:root[data-theme="stained"] .note.pinned { border-left-color: var(--brass, var(--accent)); }

/* Свечение — ТОЛЬКО тень: ни один цвет текста от него не зависит, поэтому
   контрасты остаются посчитанными выше. Светится главное действие страницы, а
   не всякая кнопка подряд: свечение здесь — примета «жать сюда». */
:root[data-theme="grimoire"] .newnote,
:root[data-theme="stained"] .newnote,
:root[data-theme="grimoire"] .wform .wact button,
:root[data-theme="stained"] .wform .wact button { box-shadow: 0 0 16px var(--glow, transparent); }

/* Фокус поля — свечение вместо браузерной обводки, но ВИДИМОЕ: две тени, одна
   из них в пиксель по контуру. Фокус, который надо искать, это не фокус.

   Обводка не снимается, а делается ПРОЗРАЧНОЙ: в режиме высокой контрастности
   (forced-colors) тени не рисуются вовсе, и `outline: none` оставил бы человека
   без фокуса совсем, а прозрачную систему перекрасит своей.

   Поля перечислены отрицанием, а не `input[type="text"]`: у половины полей
   площадки (.mreason, .mnum в модерации) атрибута type нет вовсе, и половина
   формы получила бы свечение, а половина — браузерную обводку. */
:root[data-theme="grimoire"] textarea:focus,
:root[data-theme="stained"] textarea:focus,
:root[data-theme="grimoire"] input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="submit"]):focus,
:root[data-theme="stained"] input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="submit"]):focus {
  outline: 2px solid transparent;
  outline-offset: 2px;
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), 0 0 18px var(--glow, transparent);
}

/* Лицо в мордоленте светится под курсором. Замер 70×70 и отступ в 5px не
   трогаем, и СДВИГА здесь нет намеренно: у .faces a уже есть свой hover —
   лицо растёт до 80px, занимая собственные поля, — а полоса подрезана по
   вертикали (overflow-y: hidden), и приподнятое лицо ушло бы под обрез вместе
   с тенью. Поэтому светится сама картинка, она внутри гнезда. */
:root[data-theme="grimoire"] .faces .ava,
:root[data-theme="stained"] .faces .ava { transition: box-shadow .18s; }
:root[data-theme="grimoire"] .faces a:hover .ava,
:root[data-theme="stained"] .faces a:hover .ava {
  box-shadow: 0 0 0 1px var(--accent), 0 0 14px var(--glow, transparent);
}

/* Новая реплика: к прежнему затуханию добавляется контур свечения, и гаснет он
   ВМЕСТЕ с заливкой — анимацией, а не свойством. Класс fresh app.js ставит
   навсегда, поэтому контур, заданный просто box-shadow, оставался рамкой у
   каждой дописанной реплики до перезагрузки (жалоба владельца 14.09.2026).
   Заливка при этом обязана остаться прежней, а `animation` — одно свойство
   списком: дописать в него третью анимацию нельзя, только повторить весь
   список, и у ветки prefers-reduced-motion тоже — эта строка специфичнее общей
   и перебила бы её. Заполнение у контура backwards, а не both: по окончании
   анимация снимается, и прозрачный контур не остаётся висеть поверх любой
   другой тени строки. */
@keyframes fresh-ring {
  from { box-shadow: inset 0 0 0 1px var(--glow, transparent); }
  to   { box-shadow: inset 0 0 0 1px transparent; }
}
:root[data-theme="grimoire"] .c.fresh,
:root[data-theme="stained"] .c.fresh {
  animation: fresh-in .28s ease-out, fresh-glow 6s ease-out .28s both, fresh-ring 6s ease-out .28s backwards;
}
@media (prefers-reduced-motion: reduce) {
  :root[data-theme="grimoire"] .c.fresh,
  :root[data-theme="stained"] .c.fresh { animation: fresh-glow 6s linear both, fresh-ring 6s linear backwards; }
}

/* Выбранная тема: на фиолетовом акценте «Гримуара» белая подпись даёт 2,4:1, и
   читать её нечем. Тёмная на том же фоне — 9,5:1. */
:root[data-theme="grimoire"] .tbtn.on { color: #12161d; }

/* Страница участника. Ярлыки фактов, легенды служебного ящика и ссылки
   листания — моноширинным: это надписи на полке, а не текст. */
:root[data-theme="grimoire"] .ufacts .lbl,
:root[data-theme="stained"] .ufacts .lbl,
:root[data-theme="grimoire"] .mdecs legend,
:root[data-theme="stained"] .mdecs legend,
:root[data-theme="grimoire"] .utabs .cnt,
:root[data-theme="stained"] .utabs .cnt,
:root[data-theme="grimoire"] .upubnav,
:root[data-theme="stained"] .upubnav {
  font-family: "Grim Mono", ui-monospace, Consolas, monospace;
}
/* Ярлык служебного ящика — латунью: единственная золотая надпись профиля, и
   говорит она «здесь рычаги», а не «здесь рассказ». */
:root[data-theme="grimoire"] .uadm > summary,
:root[data-theme="stained"] .uadm > summary {
  font-family: "Grim Mono", ui-monospace, Consolas, monospace;
  color: var(--brass, var(--meta));
}
/* Закладки — антиквой, как заголовки: они и есть заголовки двух списков. */
:root[data-theme="grimoire"] .utabs a,
:root[data-theme="stained"] .utabs a {
  font-family: "Grim Serif", Georgia, "Times New Roman", serif; font-weight: 500;
  font-size: 19px; transition: color .18s, background-color .18s;
}
:root[data-theme="grimoire"] .utabs a[aria-current],
:root[data-theme="stained"] .utabs a[aria-current] { background: var(--pane, var(--btn)); }
/* Латунь и свечение достаются ФОТО, а не силуэту: силуэт означает отсутствие
   фотографии, и золотая рамка вокруг пустоты говорит «смотри сюда» про то,
   чего нет. Тёмные палитры силуэт, наоборот, гасят (--sil-fade). */
:root[data-theme="grimoire"] .uhead .ava:not(.sil),
:root[data-theme="stained"] .uhead .ava:not(.sil) {
  border-color: var(--brass, var(--rule));
  box-shadow: 0 0 18px var(--glow, transparent);
}
:root[data-theme="grimoire"] .ualbum img,
:root[data-theme="stained"] .ualbum img,
:root[data-theme="grimoire"] .uphotos a,
:root[data-theme="stained"] .uphotos a { transition: box-shadow .18s, border-color .18s; }
:root[data-theme="grimoire"] .ualbum a:hover img,
:root[data-theme="stained"] .ualbum a:hover img { box-shadow: 0 0 12px var(--glow, transparent); }
:root[data-theme="grimoire"] .uphotos a:hover,
:root[data-theme="stained"] .uphotos a:hover {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), 0 0 14px var(--glow, transparent);
}

/* Плашка «это житель площадки» — грань витража слева, а не плоская рамка:
   единственное место страницы, где сказано, что текст пишет машина. */
:root[data-theme="grimoire"] .ubio,
:root[data-theme="stained"] .ubio {
  background-image: linear-gradient(90deg, var(--pane, transparent), transparent 60%);
}

/* Мобильные ветки темы. Они здесь, а не в общих @media выше, по той же
   причине, по которой они вообще нужны: специфичность у темы (0,3,0) выше, чем
   у общего правила (0,1,0), и медиазапрос её не поднимает. Пороги те же, что у
   всей площадки (700 и 460), четвёртого порога не заводим. */
@media (max-width: 700px) {
  :root[data-theme="grimoire"] .ttl,
  :root[data-theme="stained"] .ttl { font-size: 22px; }
}
@media (max-width: 700px) {
  :root[data-theme="grimoire"] .utabs a,
  :root[data-theme="stained"] .utabs a { font-size: 17px; }
}
@media (max-width: 460px) {
  :root[data-theme="grimoire"] .brand,
  :root[data-theme="stained"] .brand { font-size: 16px; }
}

/* ---------------------------------------------------------------- фильм на входе

   Фильм «Гримуар» (assets/film/film.js) идёт фоном экрана входа — первого и
   единственного, что видит гость при закрытых воротах. Слой неподвижный и
   лежит ПОД содержимым тем же приёмом, что фоновая картинка темы выше: у body
   нет ни z-index, ни transform, поэтому z-index: -1 не проваливается под его
   фон. Нажатий слой не ловит — форма входа поверх него обязана работать.

   Появляется канвас плавно и только после первого кадра (класс film-on ставит
   скрипт): three.js весит 690 КБ, и до его загрузки на месте фильма тёмный фон
   страницы, а не белый прямоугольник. Картинка-собор под ним не нужна вовсе. */
.film-host { position: fixed; inset: 0; z-index: -1; pointer-events: none; }
.filmcanvas { display: block; opacity: 0; transition: opacity 1.4s ease; }
.film-on .filmcanvas { opacity: 1; }
:root[data-theme] body.film::before { display: none; }
:root[data-theme] body.film .main { background: transparent; border: 0; padding-top: 0; }

/* Место под золотые буквы. Их рисует WebGL по центру этого блока; сама строка
   остаётся для читалки (opacity, а не display: читалка её видит) и для
   браузера без WebGL — тогда она и есть заголовок, золотом средствами CSS. */
.filmhero { height: clamp(170px, 40vh, 440px); display: grid; place-items: center; }
.filmtitle {
  margin: 0;
  font: 500 clamp(40px, 11vw, 96px)/1 "Grim Serif", Georgia, serif;
  letter-spacing: .02em;
  background: linear-gradient(180deg, #fff1c4 0%, #e7bf62 38%, #a9772b 62%, #f0cf7c 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  filter: drop-shadow(0 2px 0 #5c3f14) drop-shadow(0 0 18px rgba(194, 160, 97, .35));
  transition: opacity .8s ease;
}
.film-on .filmtitle { opacity: 0; }

/* Карточка входа — тёмное стекло поверх собора, с латунной кромкой, как у
   карточки темы: прочесть форму важнее, чем разглядеть фильм сквозь неё. */
body.film .enterbox {
  margin: 0 auto 5vh;
  padding: 22px 26px 24px;
  background: rgba(14, 17, 23, .74);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  border: 1px solid var(--rule);
  border-top-color: var(--brass, var(--rule));
}
@media (max-width: 460px) {
  body.film .enterbox { padding: 18px 14px 20px; }
}
@media (forced-colors: active) {
  .film-host { display: none; }
  .film-on .filmtitle { opacity: 1; }
}

/* ---------------------------------------------------------------- двери входа

   По фразе на дверь (23.09.2026): кнопки одной ширины столбиком, Яндекс —
   своим видом из его правил оформления (чёрная плашка, красный знак, «Войти с
   Яндекс ID»), бот — двумя кнопками в ряд, анкета — полем с кнопкой в одну
   строку. Ширина одна на всё (20rem), иначе столбец читается рваным. */
.ways { display: flex; flex-direction: column; gap: 10px; max-width: 20rem; margin: 20px auto 0; }
.way {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  min-height: 44px; padding: 0 16px; border-radius: 10px;
  border: 1px solid var(--rule); background: var(--btn); color: var(--ink);
  font-weight: 600; text-decoration: none;
}
.way:hover { text-decoration: none; border-color: var(--accent); }
.way.ya { background: #000; border-color: #3a3a3a; color: #fff; }
.way.ya:hover { background: #1c1c1c; border-color: #555; }
.yalogo { flex: none; }
.bots { display: flex; gap: 10px; }
.bots .way { flex: 1; }
.ways .fine { margin-top: 0; }
.or { display: flex; align-items: center; gap: 10px; max-width: 20rem; margin: 22px auto 12px; color: var(--meta); font-size: 12px; }
.or::before, .or::after { content: ""; flex: 1; border-top: 1px solid var(--rule); }
.rowform { display: flex; gap: 8px; max-width: 20rem; margin: 0 auto; }
.rowform input {
  flex: 1; min-width: 0; font: inherit; padding: 8px 10px;
  border: 1px solid var(--rule); border-radius: 3px; background: var(--card); color: var(--ink);
}
.rowform + .fine { max-width: 20rem; margin-left: auto; margin-right: auto; }

/* Диктовка на телефоне (эпик N): кнопка-значок под палец, которую держат, пока
   говорят. Квадратная со скруглением, как значки полоски действий (.mbtn), а не
   круглая: круглого на НГС нет ничего, и держит это fidelity_test. Описание —
   у общих правил .vbox выше. */
@media (max-width: 700px) {
  .vbox { align-items: center; }
  .wbar .vbtn { align-self: center; }
  .vbtn {
    width: 40px; height: 40px; border-radius: 3px; justify-content: center;
    border: 1px solid var(--rule); background: var(--btn);
    touch-action: none; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
  }
  .vbtn .ico { width: 20px; height: 20px; }
  .vbtn .vlbl {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap;
  }
  .vbox.rec .vbtn { background: var(--female); border-color: var(--female); color: var(--card); }
}
