/* LIFE — редакторская тема.
   Порт docs/prototypes/life-editorial.html, принятого направления дизайна.
   Правила ниже перенесены оттуда как есть; всё, чего в прототипе не было —
   вход, аккаунт, установка — собрано из тех же токенов в конце файла и
   помечено отдельно. */

:root{
  --desk:#D6CDBC;
  --bg:#E9E4D9;
  --card:#F7F4ED;
  --sheet-2:#F1EDE4;
  --ink:#191713;
  --ink-2:#423C33;
  --muted:#8A8072;
  /* Пример в пустом поле. Бледнее `--muted` нарочно: подсказка «Валентина»
     тем же тоном, что и служебная надпись, читается как уже вписанное имя, и
     человек не трогает поле, потому что оно кажется заполненным. Разница
     цвета здесь и есть разница между «так может быть» и «так есть». */
  --hint:#B5AEA3;
  --line:rgba(25,23,19,.14);
  --line-2:rgba(25,23,19,.28);
  --accent:#A0402A;
  --on-accent:#FFFFFF;
  --accent-soft:rgba(160,64,42,.10);
  --flag:#8A6B24;
  --flag-soft:rgba(138,107,36,.12);
  /* Приглушённый мох, а не сигнальный зелёный: в теме цветом сказаны ровно
     две вещи — «идёт запись» и «это сейчас на экране», — и третий цвет обязан
     быть тише обоих, иначе он начнёт с ними спорить. Живёт он полторы секунды
     и больше нигде в продукте не встречается. */
  --done:#4F6B45;
  /* Ткани переплёта. Глубокие нарочно: на светлой ткани золото тиснения
     выцветало до бледной охры, и обложка читалась макетом, а не книгой. */
  --cloth-teal:#1D4750;
  --cloth-umber:#4F3E30;
  --cloth-camel:#6B4E21;
  --cloth-cream:#E6DCC2;
  --cloth-terra:#6B2318;
  --gold:#E7CD96;
  --gold-deep:#7A5E2C;
  --alarm:#A0402A;

  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:24px; --s6:32px; --s7:48px; --s8:64px;
  --r1:2px; --r2:4px;
  --t-cap:11px; --t-sm:13px; --t-meta:12px; --t-body:17px; --t-read:19px; --t-ask:25px; --t-h2:22px; --t-h1:30px;
  --tap:44px;
  --dur:.22s;
  --ease:cubic-bezier(.2,.7,.3,1);
  --serif:"Iowan Old Style","Palatino Linotype",Palatino,"PT Serif",Charter,Georgia,"Times New Roman",serif;
  --sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  --track:.16em;
  --measure:68ch;
  --menu:38.2%;

  /* Возрасты в гребёнке «собрано». Возраст — порядок, а не набор, и красится
     он последовательной шкалой одного семейства: от светлой охры к той же
     терракоте, что служит акценту, и на полтона темнее. Двенадцать разных
     красок здесь были бы третьим цветным пятном в теме, где цветом отмечены
     ровно две вещи — идёт запись и это сейчас на экране. */
  --age-1:#C9A05A; --age-2:#BE8340; --age-3:#B0632F; --age-4:#A0402A; --age-5:#7C2E20;
}

@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --desk:#080706;
    --bg:#14120E;
    --card:#1C1915;
    --sheet-2:#221E19;
    --ink:#EDE7DA;
    --ink-2:#BDB5A6;
    --muted:#8B8272;
    --hint:#5F5849;
    --line:rgba(237,231,218,.16);
    --line-2:rgba(237,231,218,.32);
    --accent:#D2765C;
    --on-accent:#14120E;
    --accent-soft:rgba(210,118,92,.14);
    --flag:#C4A153;
    --flag-soft:rgba(196,161,83,.14);
    --done:#93AE80;
    --cloth-teal:#193C44;
    --cloth-umber:#443528;
    --cloth-camel:#5A4119;
    --cloth-cream:#C7B995;
    --cloth-terra:#5C1E14;
    --gold:#E7CD96;
    --gold-deep:#4A3617;
    --alarm:#D2765C;
    --age-1:#E0BE84; --age-2:#DFA067; --age-3:#DB8459; --age-4:#D2765C; --age-5:#B45A47;
  }
}

:root[data-theme="dark"]{
  --desk:#080706;
  --bg:#14120E;
  --card:#1C1915;
  --sheet-2:#221E19;
  --ink:#EDE7DA;
  --ink-2:#BDB5A6;
  --muted:#8B8272;
  --hint:#5F5849;
  --line:rgba(237,231,218,.16);
  --line-2:rgba(237,231,218,.32);
  --accent:#D2765C;
  --on-accent:#14120E;
  --accent-soft:rgba(210,118,92,.14);
  --flag:#C4A153;
  --flag-soft:rgba(196,161,83,.14);
  --done:#93AE80;
  --alarm:#D2765C;
  --age-1:#E0BE84; --age-2:#DFA067; --age-3:#DB8459; --age-4:#D2765C; --age-5:#B45A47;
}

*{ box-sizing:border-box; margin:0; padding:0; }

/* Прототип рисовал состояния через data-атрибуты; здесь разметка приходит с
   сервера, поэтому скрытое скрывается атрибутом hidden и должен побеждать он. */
[hidden]{ display:none !important; }

/* Полоса прокрутки волосяная и без дорожки: в Firefox через
   scrollbar-width, в WebKit — через собственные псевдоэлементы. */
*{
  scrollbar-width:thin;
  scrollbar-color:color-mix(in srgb, var(--muted) 32%, transparent) transparent;
}

*::-webkit-scrollbar{ width:4px; height:4px; }

*::-webkit-scrollbar-track{ background:transparent; }

*::-webkit-scrollbar-thumb{
  background:color-mix(in srgb, var(--muted) 32%, transparent);
  border-radius:999px;
}

*::-webkit-scrollbar-corner{ background:transparent; }

html,body{ height:100%; }

/* Место под полосу занято всегда, даже когда прокручивать нечего. «Интервью» —
   экран короткий, полосы на нём не было, а на «Истории» и «Карте» была, и при
   переходе весь лист вздрагивал на её ширину. Волосяная ширина сверху делает
   постоянную полосу почти невидимой: четыре пикселя дорожки без дорожки. */
html{
  overflow-y:scroll;
  scrollbar-gutter:stable;
  /* Переход по длинному тексту — прокруткой, а не прыжком: и по ссылке
     оглавления, и по рейке листов. Мгновенная подмена одного экрана другим
     не читается как перемещение — непонятно, что текст уже сменился и в
     какую сторону. Правило стоит на корне, потому что прокручивается лист, а
     не блок; своя прокрутка у шторки и у линии времени этим не задета
     (`scroll-behavior` не наследуется), а сценарий, которому нужно мгновенно,
     говорит об этом сам — так ведёт себя рейка под пальцем. */
  scroll-behavior:smooth;
}

@media (prefers-reduced-motion:reduce){
  html{ scroll-behavior:auto; }
}

body{
  background:var(--desk);
  color:var(--ink);
  font:400 var(--t-body)/1.55 var(--sans);
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}

button,textarea{ font:inherit; color:inherit; }

/* Пример — не ответ. Родной цвет подсказки браузер берёт почти в силу
   основного текста, и пустое поле с «Валентина» внутри выглядит заполненным:
   человек проходит мимо поля, которое на самом деле ждёт его. Правило одно на
   все поля сразу, потому что и ошибка была одна на все. `opacity:1` — из-за
   Firefox: он гасит подсказку своей прозрачностью поверх цвета. */
input::placeholder,
textarea::placeholder{ color:var(--hint); opacity:1; }

:focus-visible{ outline:2px solid var(--accent); outline-offset:3px; }

/* ── Оболочка. Слева аппарат, в центре полоса набора. ── */
/* Экран разделён золотым сечением: меньшая доля слева под меню,
   большая справа под содержимое. Прокрутка обычная, страничная. */
.app{
  min-height:100dvh;
  display:grid;
  grid-template-columns:1fr;
  /* Правый край страницы. По нему стоит правый угол шапки — и рейка листов,
     которая висит `position:fixed` и о поле шапки иначе не знает: она стояла
     на 16 пикселях от края экрана против сорока восьми у шапки, и кнопка темы
     с полоской листов не совпадали. Величина одна, потому что край один. */
  --edge:var(--s5);
}

/* Вертикаль в рельсе одна на всё: с неё начинаются метка открытого раздела,
   столбик области в каждой строке решётки и кружок аккаунта. Толщина метки и
   жёлоб под неё заданы здесь, а не в каждом блоке по месту: за одну вертикаль
   отвечают меню, решётка и аккаунт, и разойтись им нечем, только пока обе
   величины берутся отсюда. Раньше их было три — метка на −12, решётка на 0,
   кружок на +2, — и ни одна не была выведена из другой. */
.rail{ display:none; --mark:3px; --mark-gap:var(--s3); }

.sheet{
  min-width:0;
  min-height:100dvh;
  display:flex;
  flex-direction:column;
  background:var(--bg);
}

/* Шапка переносится. Мачта, вкладки и правый угол в сумме шире телефона —
   на 390px «История» растягивала документ до 498px, и вместе с шапкой за край
   уезжали текст ответов и четвёртый раздел дока. Ничего не прячется: на узком
   экране правый угол просто становится вторым рядом. */
/* Слой выше диктофона (30) и дока (20). Сама шапка ни с тем, ни с другим не
   пересекается — они у нижнего края, — но `isolation` замыкает в ней всё, что
   из неё растёт, и раскрытое меню историй с его тридцаткой оставалось внутри
   двадцатки шапки. На экране записи оно уходило под диктофон и обрывалось на
   его верхней кромке: список был, а последних историй в нём было не видно. */
.top{
  position:sticky;
  top:0;
  z-index:40;
  isolation:isolate;
  display:flex;
  align-items:center;
  justify-content:space-between;
  flex-wrap:wrap;
  gap:var(--s2) var(--s3);
  padding:var(--s4) var(--edge);
  background:var(--bg);
  border-bottom:1px solid var(--line);
}

/* Левая ячейка шапки — крошка «LIFE / раздел / чья это жизнь». Звенья стоят
   на одной базовой линии, а не по центрам своих коробок: у мачты 11 пикселей
   прописных, у имени — тринадцать антиквой, и совмещение центров разводило их
   на пару пикселей, отчего имя висело над разделом, а не продолжало его.
   Ячейка не переносится: длинное имя переносится внутри себя, и косая черта
   не остаётся хвостом предыдущей строки. */
.brand{
  display:flex;
  align-items:baseline;
  gap:var(--s3);
  min-width:0;
  margin-right:auto;
}

.mast{
  flex:none;
  font-family:var(--sans);
  font-size:var(--t-cap);
  font-weight:700;
  letter-spacing:var(--track);
  /* Разрядка вешает пустой хвост справа от последней буквы. В одном слове его
     не видно, в крошке он сдвигал черту, и зазоры вокруг неё выходили разными. */
  margin-right:calc(var(--track) * -1);
  text-transform:uppercase;
}

.mastDim{ color:var(--muted); font-weight:500; }

/* Звено-ссылка выглядит как звено, а не как ссылка: подчёркивания в крошке
   нет, цвет свой, и только на наведении звено становится чернилами — так же
   отзывается пункт меню в рельсе. */
a.mast{ text-decoration:none; color:inherit; transition:color var(--dur) var(--ease); }

a.mast:hover{ color:var(--ink); }

/* Разделитель звеньев. Тем же кеглем и цветом, что тусклое звено, но
   без разрядки: у одиночного знака она только хвост справа. */
.brandSep{
  flex:none;
  color:var(--muted);
  font-family:var(--sans);
  font-size:var(--t-cap);
  font-weight:500;
}

/* Правый угол тоже переносится: на 320px «Оборот», «Рассказчик», «Аккаунт»
   и «Тон» не встают в одну строку даже отдельным рядом. */
.topRight{ display:flex; align-items:center; flex-wrap:wrap; gap:var(--s2) var(--s4); margin-left:auto; }

/* Чья жизнь на экране. Раскрывается вниз поверх листа, ничего не сдвигая:
   шапка липкая, и меню, раздвигающее её, увело бы вниз всю страницу. */
.story-switch{ position:relative; min-width:0; }

/* Имя — единственная клавиша в крошке, и это видно только по уголку, поэтому
   у неё есть своя площадка: подложка на наведении и на раскрытии, ровно в
   рост «Тона» в правом углу, чтобы шапка держала одну высоту. Отрицательные
   поля возвращают текст на место — площадка растёт наружу, а имя стоит в
   строке крошки там же, где стояло бы без неё. */
.story-switch summary{
  display:flex;
  align-items:center;
  gap:var(--s2);
  min-width:0;
  min-height:var(--tap);
  padding-inline:var(--s2);
  margin-inline:calc(var(--s2) * -1);
  border-radius:var(--r2);
  list-style:none;
  cursor:pointer;
  transition:background var(--dur) var(--ease);
}

.story-switch summary::-webkit-details-marker{ display:none; }

.story-switch:not([data-alone="1"]) summary:hover{ background:var(--sheet-2); }

/* Раскрытая — той же бумаги, что и меню под ней: клавиша и список читаются
   одним листом, а не подложкой и карточкой разных тонов. */
.story-switch[open] summary{ background:var(--card); }

/* Уголок вниз — единственный знак, что это открывается. Рисуется рамками:
   картинки здесь были бы отдельным запросом ради шести пикселей. Нижней
   рамки у него нет: коробка в рост самого треугольника, и знак встаёт по
   середине строки, а не выше неё.
   Стоит он отдельной ячейкой клавиши, а не в строке текста. В строке между
   словом и такой коробкой есть законное место для переноса, и на телефоне
   уголок уезжал на вторую строку один: имя занимало первую целиком, а ему
   не хватало восьми пикселей. Отдельной ячейкой он забирает своё место
   первым, а переносится внутри себя уже имя — по пробелам, как и положено.
   Отрицательное поле съедает хвост разрядки у последней прописной, иначе
   зазор перед знаком выходит шире положенного. */
.story-switch summary::after{
  content:"";
  flex:none;
  width:0;
  height:0;
  margin-left:calc(var(--track) * -1);
  border-left:3px solid transparent;
  border-right:3px solid transparent;
  border-top:4px solid var(--muted);
  transition:transform var(--dur) var(--ease), border-top-color var(--dur) var(--ease);
}

.story-switch summary:hover::after{ border-top-color:var(--ink); }

.story-switch[open] summary::after{ transform:rotate(180deg); border-top-color:var(--ink); }

.story-switch[data-alone="1"] summary{ cursor:default; }

.story-switch[data-alone="1"] summary::after{ display:none; }

/* Имя человека — не служебная метка, а содержимое, и набрано антиквой, как
   заголовки и названия историй в кабинете. Гротеск вокруг него — мачта,
   раздел, автор — этим и отличается: он подпись, а имя — то, о чём страница. */
.switchName{
  min-width:0;
  font:400 var(--t-sm)/1.3 var(--serif);
  color:var(--ink);
  overflow-wrap:anywhere;
}

/* В крошке — исключение: там имя не третья надпись рядом с двумя другими, а
   последнее звено той же строки, и набрано оно тем же, чем «LIFE» и раздел.
   Отличает его цвет: пройденные звенья тусклые, текущее — в полную силу.
   Антиква остаётся списку в меню, где имена читают, а не опознают.
   Разрядка вешает хвост справа от последней буквы, и уголок за ним отходил
   дальше, чем нужно, — на этот хвост уменьшен его отступ. */
.story-switch summary .switchName{
  font:600 var(--t-cap)/1.5 var(--sans);
  letter-spacing:var(--track);
  text-transform:uppercase;
  /* Переносить где угодно тут нельзя: прописные шире строчных, длинное
     название на телефоне занимает всю строку, и разрыв «где угодно» уводил
     на вторую строку один уголок — тот повисал под разделом сам по себе.
     Рвётся теперь по пробелам, а слово длиннее строки — по слову. */
  overflow-wrap:break-word;
}

/* Автор отодвинут от имени сильнее, чем уголок: между двумя надписями нужен
   зазор, между надписью и её знаком — нет. */
.story-switch summary .switchWho{ margin-left:var(--s1); }

.switchWho{
  font-family:var(--sans);
  font-size:var(--t-cap);
  letter-spacing:.08em;
  text-transform:uppercase;
  color:var(--muted);
}

/* Ровно под клавишей: левый край меню совпадает с левым краем её площадки,
   а не с первой буквой имени, — раскрытая клавиша той же бумаги, и вместе
   они должны читаться одним листом, а не карточкой со ступенькой.

   Три ряда: заголовок, список, выход в кабинет. Средний — единственный, кому
   позволено сжиматься (`minmax(0,1fr)`), поэтому при десятке историй едет он,
   а крайние стоят. Высота меню упёрта в нижний край экрана: считается она от
   того, подо что меню подвешено, а `100%` — рост этого самого места. На столе
   это клавиша в `--tap`, на телефоне — вся шапка, потому что там меню
   отсчитывается от неё.

   Ещё один `--tap` внизу — под док, который там же и стоит. Меню теперь
   лежит слоем выше него и закрыло бы его собой; диктофон оно закрывает и
   дальше — тот всплывает над листом на время ответа и уходит, а док на
   экране всегда, и наехавшая на него карточка читается поломкой. */
.switchMenu{
  position:absolute;
  top:100%;
  left:calc(var(--s2) * -1);
  z-index:30;
  display:grid;
  grid-template-rows:auto minmax(0,1fr) auto;
  gap:var(--s1);
  min-width:min(22rem, 84vw);
  max-width:calc(100vw - var(--s5) * 2);
  max-height:calc(100dvh - var(--s4) - 100% - var(--tap) - var(--s5));
  padding:var(--s4);
  border:1px solid var(--line-2);
  border-radius:var(--r2);
  background:var(--card);
  box-shadow:0 8px 28px rgba(25,23,19,.12);
}

.switchMenu .switchHead{ margin-bottom:var(--s1); }

/* Едет список, а не карточка. Полосы прокрутки нет нарочно: в меню шириной
   в ладонь она вставала бы второй рамкой вдоль края ради трёх строк, а что
   список продолжается, видно по истории, обрезанной нижним краем.
   Боковые поля с обратным знаком — чтобы подсветка истории, которая на
   двенадцать пикселей шире содержимого, не оказалась за краем прокрутки:
   обрезка идёт по этому краю, и без них подсветка стриглась бы слева. */
.switchList{
  display:grid;
  gap:var(--s1);
  min-height:0;
  padding-inline:var(--s3);
  margin-inline:calc(var(--s3) * -1);
  overflow-y:auto;
  overscroll-behavior:contain;
  scrollbar-width:none;
}

.switchList::-webkit-scrollbar{ display:none; }

/* Имя слева, число справа: два столбца, а не два ряда. Правый — по
   содержимому, левый забирает остальное и жмётся первым, потому что имя
   переносится, а число нет. Ряда по-прежнему два: под именем стоит автор, и
   число остаётся при первом — оно про историю, а не про её вторую строку. */
.switchOne{
  display:grid;
  grid-template-columns:minmax(0,1fr) auto;
  column-gap:var(--s3);
  row-gap:2px;
  justify-items:start;
  align-items:baseline;
  width:100%;
  min-height:var(--tap);
  padding:var(--s2) var(--s3);
  margin-left:calc(var(--s3) * -1);
  border:0;
  border-radius:var(--r2);
  background:none;
  text-align:left;
  cursor:pointer;
  transition:background var(--dur) var(--ease);
}

.switchOne:hover{ background:var(--sheet-2); }

.switchOne>.switchName,
.switchOne>.switchWho{ grid-column:1; }

/* Сколько вопросов этой истории уже с ответами. Цифры моноширинные: в
   столбце из десятка историй «7» и «12» иначе стоят вразнобой, а сравнивают
   их именно взглядом сверху вниз. Тускло и гротеском — это счёт, а не имя,
   и спорить с названием истории он не должен. */
.switchCount{
  grid-column:2;
  grid-row:1;
  justify-self:end;
  font-family:var(--sans);
  font-size:var(--t-cap);
  font-variant-numeric:tabular-nums;
  letter-spacing:.08em;
  color:var(--muted);
}

/* Шапка меню: слева «Другие истории», справа название столбца под ним. Правая
   надпись стоит над числами, а клавиша истории отступает от края списка
   дважды — своим полем и своим сдвигом влево. На эти же два шага отодвинута и
   она, иначе столбец и его название разъезжаются. */
.switchHead{
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:var(--s3);
  padding-right:calc(var(--s3) * 2);
}

.switchAll{
  margin-top:var(--s2);
  padding-top:var(--s3);
  border-top:1px solid var(--line);
  color:var(--muted);
  font-family:var(--sans);
  font-size:var(--t-cap);
  letter-spacing:.08em;
  text-transform:uppercase;
  text-decoration:none;
}

.switchAll:hover{ color:var(--ink); }

/* На узком экране меню не привязано к имени вовсе: где бы имя ни оказалось
   после переноса шапки, меню от его угла уезжает за край — то вправо, то
   влево. Поэтому там оно считает края от самой шапки и встаёт во всю её
   ширину. Шапка липкая, то есть уже позиционирована, и служит точкой отсчёта
   сама, стоит снять её у переключателя. */
/* ── Заголовок истории: два лица ──
   Закрытый — имя истории с уголком. Раскрытый на телефоне — «Истории» и знак
   закрытия: там список занимает весь экран ниже шапки, и заголовок служит ему
   крестиком. Без сценария закрыть `details` больше нечем, а это меню обязано
   работать и без него. */
.switchFace{ display:flex; align-items:center; gap:var(--s2); min-width:0; }

.switchShut{ display:none; }

.switchX{
  flex:none;
  width:var(--tap);
  height:var(--tap);
  margin-right:calc(var(--s2) * -1);
  display:flex;
  align-items:center;
  justify-content:center;
  color:var(--muted);
  font-size:var(--t-h2);
  line-height:1;
}

/* ── Та история, что сейчас на экране ──
   Блок во всю ширину, а не строка с отступами: полоса стоит по самому краю
   экрана и идёт от верхней кромки блока до нижней. Скруглений у неё нет —
   скруглённый угол на краю экрана читается как карточка, которая не доехала.
   Только на телефоне: на широком экране имя стоит над меню само. */
.switchNow{
  display:none;
  grid-template-columns:minmax(0,1fr) auto;
  gap:var(--s1) var(--s4);
  padding:var(--s4) var(--s4) var(--s4) calc(var(--s4) - 3px);
  border-left:3px solid var(--accent);
  border-bottom:1px solid var(--line);
}

.switchNow .switchName{ font-size:var(--t-read); line-height:1.25; }

.fill{ grid-column:1 / -1; display:flex; flex-direction:column; gap:var(--s2); margin-top:var(--s3); }

.fillHead{ display:flex; align-items:baseline; justify-content:space-between; gap:var(--s3); }

/* ── Гребёнка: сколько клеток карты закрыто ──
   Одно деление — одна клетка. Отрезок — возраст, деления в нём — области
   жизни, и закрытые стоят слева: это счётчик, а не карта. Где именно пусто,
   видно на «Карте», здесь видно, что пусто и в какой поре жизни.

   Цвет тут обязан что-то называть. Раскраска ради раскраски в теме, где
   цветом отмечены запись и текущее, читается как тревога, — поэтому цвет
   отрезка и есть его подпись. */
.comb{ display:flex; gap:5px; align-items:flex-end; }

.combGroup{ display:flex; gap:2px; flex:1 1 0; min-width:0; }

.tick{ flex:1 1 0; min-width:0; height:12px; background:var(--line); }

.tick[data-on="1"]{ background:var(--tint,var(--accent)); }

.tick[data-age="1"]{ --tint:var(--age-1); }
.tick[data-age="2"]{ --tint:var(--age-2); }
.tick[data-age="3"]{ --tint:var(--age-3); }
.tick[data-age="4"]{ --tint:var(--age-4); }
.tick[data-age="5"]{ --tint:var(--age-5); }

/* ── Три лица одной плиты ──
   Покой, поиск по архиву, планка правки. Второе и третье ложатся поверх
   первого ровно в его высоту: ни одна строка на листе не сдвигается. */
.face{
  position:absolute;
  inset:0;
  z-index:2;
  display:none;
  align-items:center;
  gap:var(--s3);
  padding:0 var(--s5);
  background:var(--bg);
}

.face[data-open="1"]{ display:flex; }

.faceClose{
  flex:none;
  width:var(--tap);
  height:var(--tap);
  border:0;
  border-radius:50%;
  background:none;
  color:var(--muted);
  font-size:var(--t-h2);
  line-height:1;
  cursor:pointer;
  transition:color var(--dur) var(--ease);
}

.faceClose:hover{ color:var(--ink); }

/* ── Знак на клавише ──
   Иконки чёрные с альфой, и как картинки они годились бы ровно для одной
   темы. Поэтому они не картинки, а маски: подложка красится `currentColor`,
   знак вырезается из неё альфа-каналом. Один файл на обе темы, и он темнеет
   вместе с подписью на наведении. */
.ic{
  display:block;
  width:20px;
  height:20px;
  background:currentColor;
  -webkit-mask:var(--ic) center/contain no-repeat;
  mask:var(--ic) center/contain no-repeat;
}

.ic-content_copy{ --ic:url("icons/content_copy.png"); }
.ic-format_align_center{ --ic:url("icons/format_align_center.png"); }
.ic-format_align_justify{ --ic:url("icons/format_align_justify.png"); }
.ic-format_align_left{ --ic:url("icons/format_align_left.png"); }
.ic-format_align_right{ --ic:url("icons/format_align_right.png"); }
.ic-format_bold{ --ic:url("icons/format_bold.png"); }
.ic-format_italic{ --ic:url("icons/format_italic.png"); }
.ic-format_list_bulleted{ --ic:url("icons/format_list_bulleted.png"); }
.ic-format_list_numbered{ --ic:url("icons/format_list_numbered.png"); }
.ic-format_underlined{ --ic:url("icons/format_underlined.png"); }
.ic-image{ --ic:url("icons/image.png"); }
.ic-redo{ --ic:url("icons/redo.png"); }
.ic-restore{ --ic:url("icons/restore.png"); }
.ic-search{ --ic:url("icons/search.png"); }
.ic-subscript{ --ic:url("icons/subscript.png"); }
.ic-superscript{ --ic:url("icons/superscript.png"); }
.ic-undo{ --ic:url("icons/undo.png"); }

/* ── Лицо второе: поиск по архиву ──
   Клавиша стоит в служебном углу, а не в планке правки: искать можно всегда,
   а править — только тот ответ, в котором стоит каретка. */
.findOpen{
  display:flex;
  align-items:center;
  gap:var(--s2);
  min-height:var(--tap);
  padding-inline:var(--s2);
  margin-inline:calc(var(--s2) * -1);
  border:0;
  border-radius:var(--r2);
  background:none;
  color:var(--muted);
  font-size:var(--t-meta);
  letter-spacing:.04em;
  cursor:pointer;
  transition:background var(--dur) var(--ease), color var(--dur) var(--ease);
}

.findOpen .ic{ width:16px; height:16px; }

.findOpen:hover{ background:var(--sheet-2); color:var(--ink); }

/* Пока идёт правка, искать нечем: шапка занята текстом, в котором каретка. */
.findOpen[disabled]{ opacity:.3; pointer-events:none; }

/* Поле без подложки и без черты снизу: у плиты уже есть своя нижняя черта, и
   вторая рядом читается как лишний штрих, а не как поле. Что сюда пишут,
   видно по мигающей каретке. */
.findField{
  flex:1 1 auto;
  min-width:0;
  height:var(--tap);
  border:0;
  background:none;
  font-family:var(--serif);
  font-size:var(--t-body);
  outline:none;
}

.findField::placeholder{ color:var(--hint); font-family:var(--sans); font-size:var(--t-sm); }

/* Родной крестик поля `search` синий и своей формы: в теме, где весь цвет
   расписан по смыслам, он читается как чужой элемент. */
.findField::-webkit-search-cancel-button,
.findField::-webkit-search-decoration{ -webkit-appearance:none; appearance:none; }

.findCount{
  flex:none;
  color:var(--muted);
  font-size:var(--t-meta);
  font-variant-numeric:tabular-nums;
  text-align:right;
  white-space:nowrap;
}

.findStep{
  flex:none;
  width:32px;
  height:var(--tap);
  border:0;
  border-radius:var(--r2);
  background:none;
  color:var(--ink-2);
  cursor:pointer;
  transition:background var(--dur) var(--ease), opacity var(--dur) var(--ease);
}

.findStep:hover{ background:var(--sheet-2); }

.findStep[disabled]{ opacity:.28; pointer-events:none; }

.findStep svg{ display:block; margin:0 auto; }

/* Метка совпадения ахроматична. По правилу темы терракота помечает только
   след к первоисточнику, охра — только неуверенность, и больше цвета нет.
   Совпадение поиска — ни то, ни другое: это не свойство текста, а линейка,
   положенная поверх него. Найденное получает промывку чернилами, а то, на
   чём стоишь, — выворотку. Размётка ответа при этом не трогается вовсе:
   врезанный в `contenteditable` тег уехал бы на сервер вместе с редакцией. */
::highlight(find){ background:color-mix(in srgb, var(--ink) 13%, transparent); }

::highlight(find-on){ background:var(--ink); color:var(--bg); }

.answerList[data-finding="1"] .turn[data-match="0"]{ opacity:.32; }

/* Запасной путь для браузера без Highlight API: слово отметить нечем, поэтому
   метка переезжает на оборот целиком. Только здесь — где подсветка есть,
   полоска у текста лишняя. */
.answerList[data-fallback="1"] .turn[data-match="1"]{ box-shadow:-12px 0 0 -11px var(--line-2); }

.answerList[data-fallback="1"] .turn[data-here="1"]{ box-shadow:-12px 0 0 -11px var(--ink); }

/* ── Лицо третье: планка правки ──
   Ряд знаков прижат влево и стоит ровно над колонкой ответа: поля у плиты и
   у листа одни и те же, поэтому клавиша отмены приходится на ту же
   вертикаль, что и первая буква текста, который ею правят. Первая колонка
   через `minmax(0,1fr)` — иначе четырнадцать клавиш не сжимаются и на узком
   экране заезжают под «Удалить» вместо того, чтобы прокручиваться. Две
   правые колонки — по содержимому: «Удалить» с «Сохранить», а за ними
   крестик. */
.format[data-open="1"]{ display:grid; grid-template-columns:minmax(0,1fr) auto auto; }

/* Крестик ушёл в дальний правый угол планки: отмена правки — выход из неё, а
   не первое из её действий, и слева его место занял ряд знаков. У поиска
   крестик остался слева: там он закрывает поле, стоящее сразу за ним. */
.format .faceClose{ justify-self:end; }

/* Ряд знаков всегда один — и на столе, и в руке. Когда он перестаёт
   помещаться, его листают пальцем, и полосы прокрутки при этом не видно:
   полоса под клавишами — лишняя горизонталь на плите, где и так есть своя
   нижняя черта. `overscroll-behavior-x` нужен затем, чтобы протянутый до
   упора палец не ушёл в жест «назад» и не унёс человека со страницы. */
.formatTools{
  display:flex;
  align-items:center;
  gap:var(--s1);
  /* Знак в клавише — двадцать пикселей в тридцати шести, то есть по восемь
     на сторону. На эти восемь ряд и сдвинут влево: к краю поля выходит не
     коробка первой клавиши, а сам знак, и он встаёт вровень с первой буквой
     абзаца под ним. */
  margin-inline-start:-8px;
  min-width:0;
  overflow-x:auto;
  overscroll-behavior-x:contain;
  -webkit-overflow-scrolling:touch;
  scrollbar-width:none;
}

.formatTools::-webkit-scrollbar{ width:0; height:0; display:none; }

.tool{
  flex:none;
  display:grid;
  place-items:center;
  width:36px;
  height:var(--tap);
  border:0;
  border-radius:var(--r2);
  background:none;
  color:var(--ink-2);
  cursor:pointer;
  transition:background var(--dur) var(--ease), color var(--dur) var(--ease), opacity var(--dur) var(--ease);
}

.tool:hover{ background:var(--sheet-2); color:var(--ink); }

/* Нажатое начертание: та же подложка, что на наведении, плюс волосок по
   периметру — клавиша выглядит утопленной. Подчёркиванием снизу, как у
   вкладок шапки, метить нельзя: черта под знаком «подчёркнутый» — это уже не
   состояние кнопки, а её же смысл во второй раз. */
.tool[data-on="1"]{ background:var(--sheet-2); color:var(--ink); box-shadow:inset 0 0 0 1px var(--line-2); }

.tool[disabled]{ opacity:.26; pointer-events:none; }

/* Кружок имён собственных. Знака у клавиши нет: всё прочее в ряду меняет
   текст, а эта одна показывает найденное в нём, и рисовать ей букву или лупу
   значило бы обещать не то. Охра здесь по правилу темы — ею помечена
   неуверенность, а метка по большой букве и есть догадка. Погашенная клавиша
   держит кружок приглушённым: цвет в ряду знаков — уже сообщение, и до
   нажатия сообщать нечего. */
.toolDot{
  width:12px;
  height:12px;
  border-radius:50%;
  background:var(--flag);
  opacity:.42;
  transition:opacity var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

.toolNames:hover .toolDot{ opacity:.8; }

.toolNames[data-on="1"] .toolDot{ opacity:1; box-shadow:0 0 0 3px var(--flag-soft); }

/* Промывка тише поисковой: поиск — линейка, положенная на текст по просьбе, а
   это пометка самого текста, и читать сквозь неё придётся долго. Разметка
   ответа не трогается вовсе — метки ставит Highlight API. */
::highlight(names){ background:var(--flag-soft); }

/* Волосок между смысловыми группами: отмена, начертание, перечни, выключка,
   индексы, снимок, имена. */
.toolSep{ flex:none; width:1px; height:20px; margin-inline:var(--s1); background:var(--line); }

.formatActs{ flex:none; display:flex; align-items:center; gap:var(--s4); margin-left:auto; }

.format .formatActs{ justify-self:end; margin-left:0; }

.quiet{
  border:0;
  background:none;
  color:var(--muted);
  font-size:var(--t-cap);
  font-weight:600;
  letter-spacing:.08em;
  text-transform:uppercase;
  cursor:pointer;
  transition:color var(--dur) var(--ease);
}

.quiet:hover{ color:var(--accent); }

.formatSave{
  display:none;
  align-items:center;
  min-height:var(--tap);
  padding:0 var(--s4);
  border:1px solid var(--accent);
  border-radius:var(--r2);
  background:none;
  color:var(--accent);
  font-size:var(--t-cap);
  font-weight:600;
  letter-spacing:.08em;
  text-transform:uppercase;
  cursor:pointer;
  transition:background var(--dur) var(--ease);
}

.formatSave:hover{ background:var(--accent-soft); }

.format[data-dirty="1"] .formatSave{ display:inline-flex; }

/* Клавиша темы. Слово «Тон» на ней было названием того, чем управляют, а не
   тем, что будет по нажатию, и на четырёх буквах в кружке это не читалось.
   Теперь на ней знак того, куда нажатие ведёт: на светлом листе луна, на
   тёмном солнце. Оба знака лежат в разметке, а выбирает их CSS — тема стоит
   на самом корне документа, и сценарию про эту подмену знать незачем.
   Состояний, как и у палитры, три: день по умолчанию, `prefers-color-scheme`
   для тех, кто не выбирал, и `data-theme` для тех, кто выбрал; последнее
   правило идёт ниже и потому перебивает системное в обе стороны. */
.theme{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  flex:none;
  width:var(--tap);
  height:var(--tap);
  border:1px solid var(--line);
  border-radius:50%;
  background:none;
  color:var(--muted);
  cursor:pointer;
  transition:color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.theme:hover{ color:var(--ink); border-color:var(--line-2); }

.theme svg{ width:20px; height:20px; }

.themeDay{ display:none; }

@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .themeNight{ display:none; }

  :root:not([data-theme="light"]) .themeDay{ display:block; }
}

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

:root[data-theme="dark"] .themeDay{ display:block; }

.body{
  flex:1 1 auto;
  padding:var(--s6) var(--s5);
}

/* Подвал больше не липнет сам: липнет диктофон внутри него, потому что теперь
   он самостоятельный слой и появляется не только здесь. Подвалу остались поля
   листа и место в потоке. */
/* Подвал больше ничего не показывает: диктофон всплывает над листом сам. За
   подвалом остаётся отведённое место, иначе последний ответ пришлось бы читать
   из-под блока. */
/* Место под диктофоном. Он всплывающий слой, из потока выключен, и если под
   ним ничего не отвести, последняя строка листа окажется под планкой навсегда:
   прокручивать будет нечего. Раньше здесь стояло число — 280px, — и оно
   разошлось с делом: на узком экране планка вырастает (кнопки переносятся) и
   закрывает 395px, а отведено было 280. Хвост длинного вопроса не доставался
   прокруткой вообще — не «неудобно», а недоступно.

   Поэтому высоту сюда сообщает сам диктофон: он один её знает и знает в тот
   момент, когда она меняется. Число осталось запасным — на случай, когда
   сценарий не отработал; тогда планки на экране всё равно нет. */
.footer{
  flex:0 0 auto;
  min-height:var(--dictaphone-space,280px);
}

/* Поля по краям — те же восемь пикселей, что у клетки внутри: без них крайние
   разделы упирались надписью в самый край экрана и читались обрезанными.
   Больше взять нельзя: на 320px строка дока занимает почти всю ширину, и
   `bin/layout_shots.php` показывает переполнение сразу, стоит прибавить. */
.dock{
  position:sticky;
  bottom:0;
  z-index:20;
  display:flex;
  background:var(--card);
  border-top:1px solid var(--line);
  padding-inline:var(--s2);
  padding-bottom:env(safe-area-inset-bottom);
}

.dock a,
.dock button{
  flex:1;
  min-height:var(--tap);
  padding:var(--s3) var(--s1);
  border:0;
  border-top:2px solid transparent;
  background:none;
  color:var(--muted);
  font-size:var(--t-cap);
  letter-spacing:.06em;
  text-transform:uppercase;
  cursor:pointer;
}

.dock [data-on="1"]{ color:var(--ink); border-top-color:var(--accent); }

/* Пятый раздел довёл строку дока до 359 px, и на экране в 320 px «Аккаунт»
   уезжал за правый край: шесть надписей вразрядку и капителью столько и
   занимают, а `flex:1` не ужимает пункт ниже его собственного слова. Померено
   `bin/layout_shots.php` — читать CSS тут бесполезно, надо считать.

   Поэтому на узком экране разрядка снимается, кегль опускается на пункт, а
   поля прижимаются: строка садится примерно в 295 px и держит запас на случай
   слова длиннее «Материалов». Резать надписи до значков нечем — у разделов
   значков нет, а прокрутка дока прячет разделы за краем, и человек их не
   найдёт. Всё, что шире 380 px, набирается как раньше. */
@media (max-width:380px){
  .dock a,
  .dock button{
    font-size:10px;
    letter-spacing:0;
    padding:var(--s3) 2px;
  }
}

/* Новая запись в разделе. Метка раздела — та самая чёрточка у названия, слева
   в рельсе и сверху в доке, — несколько раз мигает акцентом и гаснет: человек
   договорил, ответ ушёл разбираться в фон, и узнать, что он уже записан и
   лежит в «Истории», иначе неоткуда. Мигает только чужой раздел: ставит и
   снимает `data-flash` сценарий страницы, а на самой «Истории» мигать нечему.
   Правило одно на обе метки, потому что мигает у них одно и то же — цвет
   рамки; ширина есть только у той стороны, которая эту метку и рисует. */
.railNav [data-flash="1"],
.dock [data-flash="1"]{ animation:markFlash .9s var(--ease) 3; }

@keyframes markFlash{
  from,to{ border-color:transparent; }
  50%{ border-color:var(--accent); }
}

/* Кому мигание мешает, тому метка просто горит те же пару секунд: сообщение
   то же, движения нет. Гасит её тот же сценарий, что и зажёг. */
@media (prefers-reduced-motion:reduce){
  .railNav [data-flash="1"],
  .dock [data-flash="1"]{ animation:none; border-color:var(--accent); }
}

/* ── Служебные метки: единственная роль гротеска. ── */
.kicker{
  font-family:var(--sans);
  font-size:var(--t-cap);
  font-weight:600;
  letter-spacing:var(--track);
  text-transform:uppercase;
  color:var(--muted);
}

.folio{
  font-family:var(--sans);
  font-size:var(--t-cap);
  letter-spacing:.1em;
  color:var(--muted);
  font-variant-numeric:tabular-nums;
}

.read{ max-width:var(--measure); }

/* ── Экран интервью. Один вопрос на пустом листе. ── */
/* Лист записи — столбец во всю свободную высоту, и вопрос стоит в нём
   посередине. Раньше вопрос жался к вкладкам, а под ним до планки пустовало
   260 px: короткий вопрос — а короткими они бывают чаще длинных — читался как
   заголовок брошенной страницы. Середина здесь честная: `margin-block:auto`
   разводит поля поровну, пока место есть, и схлопывает их в ноль, когда
   вопрос перерос лист, — длинный вопрос по-прежнему начинается сверху и
   растёт вниз, в отведённое под планкой место. `justify-content:center` так не
   умеет: он обрезал бы верх длинного вопроса, и прокрутка его не достала бы. */
.bodyStage{
  display:flex;
  flex-direction:column;
}

.bodyStage .stage{
  flex:1 1 auto;
  display:flex;
  flex-direction:column;
}

.bodyStage .askSlot{ margin-block:auto; }

.stage{ max-width:var(--measure); }

/* Вкладки записи: ответ на вопрос или своя мысль. Ряд — тот же `.topTabs`,
   что в шапке: набор, шаг и подчёркивание активной берутся оттуда, здесь
   только место в столбце над вопросом. */
.stageTabs{ margin-bottom:var(--s7); }

/* Подпись под приглашением своей мысли: пояснение, а не второй вопрос,
   поэтому размер обычного текста, а не слота вопроса. */
.askNote{
  margin-top:var(--s4);
  font:400 var(--t-body)/1.5 var(--serif);
  color:var(--ink-2);
  max-width:36ch;
}

/* Слот вопроса больше не резервирует высоту: планка прибита к низу,
   и вопрос любой длины растёт вниз, в распорку, никого не сдвигая.

   Кегль телефона задан ниже, отдельным правилом: здесь он всё равно не
   работает — на телефоне включается `@media (max-width:600px)`, — а мерка у
   телефона своя, по самому длинному вопросу.

   На столе кегль держит не ширина, а высота. Прежние 52px мерились от ширины
   окна и на любом настольном экране выходили потолком, а места вопросу
   отпущено не ширина, а полоса между признанием и планкой записи: планка с
   шагами занимает нижние 307px, и на ноутбуке 1440×780 вопросу остаётся 148px.
   Предельный вопрос — 24 слова, 300 знаков, потолок `QuestionPolicy` —
   набирался при 52px десятью строками и уходил под планку на 381px: половины
   не было видно, и `bin/layout_shots.php --shoot --widths=1280` это печатал.

   Поэтому мерка кегля — `3.4vh`: высота окна и есть то, чего не хватает.
   Ширина оставлена вторым ограничителем (`2.8vw`) для узкого, но высокого
   окна, где полоса длинная, а строка короткая. Потолок 36px — там, где высоты
   больше 1060px; пол 24px — на ноутбуке 1366×700, ниже опускать нечего:
   предельный вопрос помещается и там.

   Померено `bin/layout_shots.php` на 1024×640, 1280×800, 1366×700, 1440×780,
   1512×850 и 1920×950: предельный вопрос виден целиком на каждом. */
.askSlot{ font-size:clamp(24px, min(2.8vw, 3.4vh), 36px); }

/* Признание стоит над вопросом и обязано быть меньше его, но читаемым: это
   слова, обращённые к человеку, а не подпись. Поэтому размер основного текста
   и цвет основного текста, а не 11px капсом, как служебные надписи. */
.heard{
  font-size:var(--t-body);
  line-height:1.45;
  color:var(--ink);
  margin:0 0 18px;
  max-width:34ch;
}

.ask{
  /* Мера в знаках, а не в пикселях: она сужается вместе с кеглем. 22ch при
     мелком кегле держали то же число строк, что и при крупном, — вопрос лишь
     становился теснее, а под планку уезжал по-прежнему. 40ch — это те же пять
     строк вместо десяти на предельном вопросе, и упирается она в `--measure`
     самого листа раньше, чем строка станет длиннее читаемого. */
  max-width:40ch;
  /* 1.14 — плотность заголовка, хороша на двух-трёх строках; на пяти подряд
     мелким кеглем плотный набор читается тяжело. */
  font:400 1em/1.18 var(--serif);
  letter-spacing:-.02em;
  text-wrap:balance;
  /* На телефоне мера снята совсем (правило ниже), и одно длинное слово —
     «Александровна», «двоюродная» — уехало бы за правый край. */
  overflow-wrap:break-word;
}


.askIn{ animation:rise var(--dur) var(--ease) both; }

/* Ожидание на месте вопроса. Заказ с «Карты» переносит сюда сразу, а вопрос про
   выбранную клетку сочиняется секунды — и всё это время место занято, но не
   чужим вопросом: набор здесь служебный, мелкий, и с вопросом его не спутать. */
.askWait{
  display:flex;
  align-items:center;
  gap:var(--s3);
  min-height:1.14em;
  font-family:var(--sans);
  font-size:var(--t-cap);
  font-weight:600;
  letter-spacing:var(--track);
  text-transform:uppercase;
  color:var(--muted);
  animation:rise var(--dur) var(--ease) both;
}

.spinner{
  width:16px;
  height:16px;
  flex:0 0 auto;
  border:1px solid var(--line-2);
  border-top-color:var(--accent);
  border-radius:50%;
  animation:spin 1s linear infinite;
}

@keyframes spin{ to{ transform:rotate(1turn); } }

/* Кому вращение мешает, тому оно и не нужно: дуга остаётся на месте и дышит
   тем же светом, что и лампа записи. */
@media (prefers-reduced-motion:reduce){
  .spinner{ animation:blink 1.4s var(--ease) infinite alternate; }
}

@keyframes rise{ from{ opacity:0; transform:translateY(6px); } to{ opacity:1; transform:none; } }

/* Диктофон — отдельный слой у нижнего края окна, а не часть листа. Он
   всплывает над содержимым, потому что вызывать его будут и оттуда, где
   содержимое длинное и прокручивается: на телефоне он стоит над доком, на
   столе — по ширине листа. Закрытый он уезжает вниз целиком. */
/* Высота блока постоянная. Он открывается один раз и дальше не меняет ни
   состава, ни размера: лестница на месте всегда, ряд действий всегда один, и
   ничто под курсором не переезжает на шаг вперёд. */
.dictaphone{
  position:fixed;
  left:0;
  right:0;
  bottom:calc(var(--tap) + env(safe-area-inset-bottom));
  z-index:30;
  display:grid;
  gap:var(--s5);
  padding:var(--s5) var(--s5) var(--s5);
  background:linear-gradient(to top,var(--bg) 74%,transparent);
  transition:transform var(--dur) var(--ease), opacity var(--dur) var(--ease);
}

/* Диктофон держит меру набора, а не собственную ширину: его края совпадают с
   краями текста на листе слева и справа. Поля у блока те же, что у `.body`,
   поэтому левый край сходится сам, а правый задаёт `--measure`. `ch` здесь
   считается по тому же шрифту, что и у `.turn` и `.stage` — по основному
   набору страницы, — и потому даёт ту же ширину. */
.ladder{ max-width:var(--measure); }

.dictaphone[data-open="0"]{
  transform:translateY(110%);
  opacity:0;
  pointer-events:none;
}

/* Планка записи: клавиша во всю ширину и второй ярус под ней.
   Лампа стоит у счётчика, поэтому подпись и кнопки нижнего яруса
   начинаются от одного края плиты. */
.deck{
  max-width:var(--measure);
  border:1px solid var(--line-2);
  border-radius:var(--r2);
  background:var(--card);
  overflow:hidden;
}

.key{
  position:relative;
  width:100%;
  min-height:76px;
  padding:0 var(--s5);
  border:0;
  background:none;
  display:flex;
  align-items:center;
  gap:var(--s4);
  cursor:pointer;
  transition:background var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

.key:hover{ background:var(--sheet-2); }

.key[data-on="1"]{ background:var(--sheet-2); box-shadow:inset 0 1px 5px var(--accent-soft); }

.key[data-busy="1"]{ opacity:.45; pointer-events:none; }

/* Микрофон слева от надписи: клавиша во всю ширину сама по себе не говорит,
   что отвечать надо голосом. */
.keyMic{
  flex:0 0 auto;
  display:flex;
  align-items:center;
  justify-content:center;
  width:20px;
  color:var(--muted);
  transition:color var(--dur) var(--ease);
}

.key:hover .keyMic{ color:var(--accent); }

.key[data-on="1"] .keyMic{ color:var(--accent); }

.keyLabel{
  font-family:var(--sans);
  font-size:var(--t-cap);
  font-weight:600;
  letter-spacing:var(--track);
  text-transform:uppercase;
  color:var(--ink-2);
  text-align:left;
}

/* Лампа записи: сердцевина неподвижна, ореол уходит вовне.
   Период долгий — ответ длится минутами, частое мигание изводит. */
.dot{ position:relative; display:block; width:14px; height:14px; margin-left:auto; flex:0 0 auto; }

.core{
  position:absolute;
  inset:0;
  display:block;
  border-radius:50%;
  background:var(--accent);
}

.halo{
  position:absolute;
  inset:0;
  display:block;
  border-radius:50%;
  border:1px solid var(--accent);
  opacity:0;
}

.key[data-on="1"] .halo{ animation:ping 2.4s var(--ease) infinite; }

@keyframes ping{
  0%{ transform:scale(1); opacity:.6; }
  70%{ opacity:0; }
  100%{ transform:scale(3.4); opacity:0; }
}

/* «Идёт запись» — терракотой, той же, что горит в точке рядом: лампа записи
   одна, и надпись при ней, а не сама по себе. Дышит в такт расходящимся от
   точки кругам — тот же период.
   Дыхание ровное и симметричное: `ease-in-out`, туда и обратно за одинаковое
   время, без рывка на краю, который даёт общий `--ease`. И не гаснет: нижняя
   точка — 0.55, потому что надпись, пропадающая совсем, читается как сбой, а
   не как «идёт». Меняется только прозрачность — размер или цвет дёргали бы
   строку и тянули взгляд обратно на экран, пока человек говорит. */
/* Полукруг туда и полукруг обратно, `alternate`: вторая половина — та же
   первая, проигранная назад, поэтому у цикла нет стыка, на котором надпись
   срывалась бы в темноту.
   Имя своё, а не общее `breathe`. Общее уже занято полосой у абзаца, и оно
   идёт от 0.42 к 1 по `--ease`; кадры с одинаковым именем не складываются, а
   заменяют друг друга последним объявленным, поэтому надпись всё это время
   дышала чужим ходом — плавно светлела и обрывалась. Столкновение в каскаде
   не видно ни в одном файле по отдельности: видно его глазами, на экране. */
.key[data-on="1"] .keyLabel{
  color:var(--accent);
  animation:recording 1.3s ease-in-out infinite alternate;
}

@keyframes recording{
  from{ opacity:1; }
  to{ opacity:.55; }
}

/* Кому движение мешает — тому и это: надпись остаётся на месте и читается. */
@media (prefers-reduced-motion:reduce){
  .key[data-on="1"] .halo,
  .key[data-on="1"] .keyLabel{ animation:none; }
  .key[data-on="1"] .halo{ opacity:.4; }
}

.window{
  font-family:var(--sans);
  font-size:var(--t-sm);
  font-variant-numeric:tabular-nums;
  letter-spacing:.08em;
  color:var(--ink-2);
}

/* Полоса времени ползёт ровно по шву между ярусами. */
.keyFill{
  position:absolute;
  left:0;
  bottom:0;
  height:2px;
  width:0;
  background:var(--accent);
  transition:width 1s linear;
}

.deckActs{
  display:flex;
  align-items:center;
  flex-wrap:wrap;
  gap:var(--s2) var(--s5);
  min-height:var(--tap);
  padding:0 var(--s5);
  border-top:1px solid var(--line);
  transition:opacity var(--dur) var(--ease);
}

.deckActs[data-off="1"]{ opacity:.26; pointer-events:none; }

.pushRight{ margin-left:auto; display:inline-flex; align-items:center; gap:var(--s5); }

.ghost{
  min-height:var(--tap);
  padding:0;
  white-space:nowrap;
  border:0;
  background:none;
  color:var(--muted);
  font-size:var(--t-cap);
  font-weight:600;
  letter-spacing:.08em;
  text-transform:uppercase;
  cursor:pointer;
  transition:color var(--dur) var(--ease);
}

.ghost:hover{ color:var(--ink); }

/* Отмена диктовки: единственное действие, которое отменяет, а не продолжает. */
.ghostAccent{ color:var(--accent); }

/* «Отправить» во время записи: то, ради чего человек говорил. Чернилами, а не
   акцентом — акцент в этом ряду занят отменой, и два цветных слова рядом
   отменяют друг друга. */
.ghostStrong{ color:var(--ink); }

.ghostAccent:hover{ color:var(--accent); }

/* Клавиша записи: точёный диск с одной красной точкой. */
.rec{
  position:relative;
  width:92px;
  height:92px;
  border:1px solid var(--line-2);
  border-radius:50%;
  background:var(--sheet-2);
  display:grid;
  place-items:center;
  cursor:pointer;
  transition:border-color var(--dur) var(--ease), transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

.rec::before{
  content:"";
  position:absolute;
  inset:7px;
  border:1px solid var(--line);
  border-radius:50%;
}

.rec:hover{ border-color:var(--accent); }

.rec:active{ transform:scale(.96); box-shadow:inset 0 0 0 2px var(--accent-soft); }

.rec[data-busy="1"]{ opacity:.4; pointer-events:none; }

.rec i{
  width:26px;
  height:26px;
  border-radius:50%;
  background:var(--accent);
  display:block;
  transition:border-radius var(--dur) var(--ease), width var(--dur) var(--ease), height var(--dur) var(--ease);
}

.rec[data-on="1"]{ border-color:var(--accent); }

.rec[data-on="1"] i{ width:22px; height:22px; border-radius:var(--r1); }

.ladder{
  position:relative;
  display:grid;
  gap:var(--s3);
  padding-left:var(--s5);
}

.ladderRail{
  position:absolute;
  left:5px;
  width:1px;
  background:var(--line);
}

.ladderFill{
  position:absolute;
  left:5px;
  width:1px;
  height:0;
  background:var(--accent);
  transition:height .6s var(--ease);
}

.rung{
  position:relative;
  display:flex;
  align-items:center;
  gap:var(--s3);
  font-family:var(--sans);
  font-size:var(--t-cap);
  letter-spacing:.08em;
  text-transform:uppercase;
  color:var(--ink);
  opacity:.14;
  transition:opacity .55s var(--ease), color .55s var(--ease);
}

.rung::before{
  content:"";
  position:absolute;
  left:-24px;
  width:9px;
  height:9px;
  border:1px solid var(--line-2);
  border-radius:50%;
  background:var(--bg);
  transition:border-color .4s var(--ease), background .4s var(--ease);
}

.rung[data-state="done"]{ opacity:.5; }

.rung[data-state="done"]::before{ border-color:var(--accent); background:var(--accent); }

.rung[data-state="now"]{ opacity:1; }

.rung[data-state="now"]::before{ border-color:var(--accent); animation:blink 1.2s var(--ease) infinite alternate; }

/* Сорванная стадия: ступень остаётся видимой и объясняет себя. */
.rung[data-state="fail"]{ opacity:1; color:var(--accent); }

.rung[data-state="fail"]::before{ border-color:var(--accent); background:var(--bg); }

@keyframes blink{ from{ opacity:.3; } to{ opacity:1; } }

/* Объяснение сорванного микрофона. Пусто и скрыто всё остальное время, поэтому
   высоту блока в обычной работе не трогает: строка появляется один раз — там,
   где красная ступень сказала «сорвалось», а человеку нужно знать, что с этим
   делать. Цвет тот же, что у ступени: это её продолжение, а не второй голос. */
.dictHint{
  max-width:var(--measure);
  margin:var(--s3) 0 0;
  font-family:var(--sans);
  font-size:var(--t-meta);
  line-height:1.5;
  color:var(--accent);
}

.dictHint a{ color:inherit; }

/* На телефоне вопрос набирается мельче — иначе длинный не помещается.
   Мерка не на глаз: продукт выпускает вопросы до 24 слов (`QuestionPolicy`),
   и такой вопрос при 30px занимал на экране 390px десять строк и уезжал под
   планку записи на 118px.

   Первая мерка взяла с запасом: при 20–26px тот же предельный вопрос на самом
   тесном телефоне (320×568, iPhone SE) не доходил до планки 128px, то есть
   четверть экрана оставалась пустой, а вопрос читался мельче, чем следует
   единственному, что на экране есть. Кегль поднят до 24–32px — это те же
   девять-десять строк в худшем случае и 55px запаса до планки; 25px запас
   съедает до 12px, поэтому 24px и есть потолок, а не круглое число.
   Померено `bin/layout_shots.mjs` на 320×568, 360×640, 375×667 и 390×844.

   Вместе с кеглем снята и мера строки. Мера в знаках сужается вместе с
   кеглем, и уменьшение шрифта не убирало ни одной строки, только делало их
   теснее. На узком листе меру задаёт сам лист, и строка идёт во всю его
   ширину.

   Межстрочный чуть шире: плотность заголовка хороша, когда строк две-три; на
   восьми строках подряд читать плотный набор тяжело. */
@media (max-width:600px){
  .askSlot{ font-size:clamp(24px,7vw,32px); }

  .ask{
    max-width:none;
    line-height:1.22;
  }

  /* Отступы над вопросом на телефоне дороже, чем на столе: каждый из них —
     строка вопроса, которая не поместилась. */
  .stageTabs{ margin-bottom:var(--s5); }

  .heard{ margin-bottom:var(--s3); }
}

/* Низкий экран — отдельная беда, и кеглем она не лечится. На телефоне высотой
   568–667px планка записи занимала 300–395px: больше половины экрана, и
   вопросу оставалась щель в полтораста пикселей. Поэтому там планка ужимается
   — теряет половину внутренних полей и сдвигает ступени лестницы, — а лист
   отдаёт вопросу свои верхние отступы.

   Чего здесь больше нет — так это сдвига планки к самому нижнему краю. Она
   стояла `bottom:env(safe-area-inset-bottom)`, то есть на нуле, а на нуле
   стоит док; слой у планки выше (30 против 20), подложка непрозрачная, и меню
   она закрывала собой целиком — не только от глаза, но и от пальца:
   `elementFromPoint` в середине дока возвращал ступень лестницы. На всяком
   экране ниже 740px — а это и iPhone SE, и телефон 360×640, и всякий телефон
   повыше, у которого адресная строка браузера съела полтораста пикселей, — на
   экране записи меню внизу просто не было. Сорок четыре пикселя под вопрос
   того не стоят: вопрос и без них виден целиком на 320×568, а самый длинный
   достаётся прокруткой, как и всегда.

   Постоянству высоты это не противоречит: она постоянна на том экране, где
   человек работает, и не меняется у него под рукой. Одинаковой на всех
   экранах она никогда и не была — ряд кнопок переносится сам.

   Порога два, и они про разное: `max-width` выше — про кегль и меру строки,
   этот — про высоту. Узкий экран не обязан быть низким, и наоборот. */
@media (max-height:740px){
  .dictaphone{
    gap:var(--s3);
    padding:var(--s4) var(--s5);
  }

  .ladder{ gap:var(--s2); }

  .body{ padding:var(--s5); }

  .stageTabs{ margin-bottom:var(--s4); }

  .heard{ margin-bottom:var(--s2); }
}

.fail{ color:var(--accent); }


/* Ответ текстом. */
.compose{
  position:relative;
  border:1px solid var(--line-2);
  border-radius:var(--r2);
  background:var(--card);
  padding:var(--s5);
  display:flex;
  flex-direction:column;
  gap:var(--s4);
  max-width:var(--measure);
}

.compose textarea{
  width:100%;
  min-height:132px;
  border:0;
  outline:0;
  resize:vertical;
  background:transparent;
  font:400 var(--t-read)/1.6 var(--serif);
}

.composeClose{
  position:absolute;
  top:var(--s2);
  right:var(--s2);
  width:var(--tap);
  height:var(--tap);
  border:0;
  border-radius:50%;
  background:none;
  color:var(--muted);
  font-size:var(--t-h2);
  line-height:1;
  cursor:pointer;
  transition:color var(--dur) var(--ease);
}

.composeClose:hover{ color:var(--ink); }

/* Отказ отправки остаётся в самом поле: планка записи в этот момент спрятана,
   и уроненный в неё отказ прятался вместе с ней. Повторяет отправку та же
   кнопка «Отправить», она тут же. */
.compose[data-trouble="1"] .note{ color:var(--alarm); }

.composeFoot{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:var(--s4);
}

/* Отправка становится доступна, когда в поле что-то есть. */
.compose .primary{ opacity:.25; pointer-events:none; }

.compose[data-filled="1"] .primary{ opacity:1; pointer-events:auto; }

.btn{
  /* И кнопка, и ссылка в облике кнопки: без inline-flex ссылка «Читать»
     стояла подчёркнутым текстом в левом верхнем углу своей плашки. */
  display:inline-flex;
  align-items:center;
  justify-content:center;
  text-decoration:none;
  min-height:var(--tap);
  padding:0 var(--s5);
  border:1px solid var(--accent);
  border-radius:var(--r2);
  background:var(--accent);
  color:var(--on-accent);
  font-size:var(--t-cap);
  font-weight:600;
  letter-spacing:.08em;
  text-transform:uppercase;
  cursor:pointer;
  transition:opacity var(--dur) var(--ease);
}

.btn:hover{ opacity:.86; }

.btn[data-soft]{ background:none; color:var(--ink-2); border-color:var(--line); }

.btn[data-soft]:hover{ border-color:var(--line-2); color:var(--ink); }

/* ── Заголовок раздела. ── */
.head{
  display:flex;
  flex-direction:column;
  gap:var(--s3);
  margin-bottom:var(--s7);
  max-width:var(--measure);
}

.h1{ font:400 var(--t-h1)/1.16 var(--serif); letter-spacing:-.02em; text-wrap:balance; }

/* Имя раздела и одно действие над всей страницей — в одном ряду, действие у
   правого края. Край здесь тот же, по которому кончается текст: страница
   читается по мерке, и клавиша, уехавшая за неё к краю экрана, оторвалась бы
   от того, к чему относится. Узкому экрану ряд переносится: короткое имя
   раздела и клавиша рядом с ним помещаются не всегда. */
.headRow{
  display:flex;
  align-items:center;
  justify-content:space-between;
  flex-wrap:wrap;
  gap:var(--s3) var(--s4);
}

/* Действия над всей страницей — знаками, у правого края её ряда.

   Подписью такое действие спорит с именем раздела: два набора слов в одной
   строке читаются как два заголовка, и глаз выбирает больший, а меньший
   мешает. Знак не окликает: его находит тот, кто ищет, — а имя действия
   остаётся в `aria-label` и подсказке, так что для диктора ничего не
   изменилось.

   Размер знака — тот же, что у знаков в планке правки, и это не случайно:
   один продукт, один размер. Само поле клавиши — в палец: знак маленький,
   промахнуться по нему легко, и на телефоне это стоило бы человеку второго и
   третьего касания. Тон — приглушённый, терракота приходит на наведении: цвет
   здесь значит «сейчас произойдёт». */
.headActs{ display:flex; align-items:center; gap:var(--s2); margin-left:auto; }

.headAct{
  display:flex;
  align-items:center;
  justify-content:center;
  width:var(--tap);
  height:var(--tap);
  padding:0;
  border:0;
  border-radius:var(--r2);
  background:none;
  color:var(--muted);
  cursor:pointer;
  transition:color var(--dur) var(--ease);
}

.headAct:hover{ color:var(--accent); }

/* Что случилось — вспышкой, а не словом: клавиша со знаком, ставшая надписью,
   на полторы секунды пропадает с экрана, а рука уже тянется к ней второй раз.
   Мох — «сделано», и это тот же мох, которым в продукте отмечен разобранный
   ответ; терракота — «не вышло», и рядом словами объясняет ярлычок. Ни рамки,
   ни заливки: знак просто становится ярче себя. */
.headAct[data-flash]{ transition:none; }

.headAct[data-flash="done"]{ color:var(--done); }

.headAct[data-flash="miss"]{ color:var(--alarm); }

.headAct:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }

/* Заголовок блока внутри страницы. Блоки «Карты» — решётка, линия, люди —
   представляются одинаково: надзаголовок, имя, объяснение, ряд чисел. Разные
   шапки у соседних блоков читались как разные страницы, съехавшиеся вместе. */
.h2{ font:400 clamp(26px,3vw,34px)/1.18 var(--serif); letter-spacing:-.01em; text-wrap:balance; }

.lede{ font:400 var(--t-body)/1.65 var(--serif); color:var(--ink-2); }

/* Переключатель раздела стоит в шапке, рядом с колонтитулом. */
.topTabs{ display:flex; align-items:center; gap:var(--s5); }

.topTabs a,
.topTabs button{
  display:inline-flex;
  align-items:center;
  text-decoration:none;
  min-height:var(--tap);
  padding:0;
  border:0;
  background:none;
  color:var(--muted);
  font-size:var(--t-cap);
  font-weight:600;
  letter-spacing:var(--track);
  text-transform:uppercase;
  cursor:pointer;
  transition:color var(--dur) var(--ease);
}

.topTabs a:hover,
.topTabs button:hover{ color:var(--ink); }

.topTabs [data-on="1"]{ color:var(--ink); box-shadow:inset 0 -2px 0 var(--accent); }

/* ── История: оборот = вопрос курсивом и ответ набором. ── */
.turn{
  position:relative;
  display:grid;
  gap:var(--s4);
  padding-bottom:var(--s6);
  margin-bottom:var(--s6);
  border-bottom:1px solid var(--line);
  max-width:var(--measure);
}

/* Строка переносится: на телефоне дата слева и три подписи справа в одну
   строку не встают, а ужимать их до нечитаемого — хуже, чем отдать им вторую
   строку. `align-items:baseline` держит подписи на одной линии с датой, пока
   они помещаются рядом. */
.turnHead{ display:flex; align-items:baseline; justify-content:space-between; flex-wrap:wrap; gap:var(--s2) var(--s3); }

/* Три действия при своём ответе, у правого края его же строки: «Удалить»,
   «Пересчитать», «Исходник». Набор тот же, что у даты слева, и тон тот же:
   это подписи, а не клавиши, и звать они не должны — на них смотрят, только
   когда уже решили. Терракота приходит на наведении: цвет здесь значит
   «сейчас произойдёт», а не «опасно вообще».

   Прижаты вправо распоркой, а не полем: между ними и «Редакцией», когда та
   есть, остаётся общий зазор строки.

   Видны они только у ответа, который сейчас правят: пометку ставит сценарий,
   открывая редактор. Пока ответы читают, подписей нет вовсе — они были бы
   третьей надписью в строке с датой, которую никто не звал. Не наведение, а
   правка: наведения на телефоне нет, а правка есть везде — по касанию
   ответа. */
.turnHeadActs{ display:none; margin-left:auto; align-items:baseline; gap:var(--s3); }

.turn[data-editing="1"] .turnHeadActs,
.turn[data-showing="raw"] .turnHeadActs{ display:inline-flex; flex-wrap:wrap; justify-content:flex-end; }

.turnHeadAct{
  padding:0;
  border:0;
  background:none;
  color:var(--muted);
  font-family:var(--sans);
  font-size:var(--t-cap);
  letter-spacing:.06em;
  text-transform:uppercase;
  cursor:pointer;
  transition:color var(--dur) var(--ease);
}

/* Разделитель рисуется, а не набирается: лишний знак в разметке читался бы
   вслух экранным диктором как слово. */
.turnHeadAct + .turnHeadAct{ position:relative; padding-left:var(--s3); }

.turnHeadAct + .turnHeadAct::before{
  content:"";
  position:absolute;
  left:0;
  top:0;
  width:1px;
  height:1em;
  background:var(--line);
}

.turnHeadAct:hover{ color:var(--accent); }

/* Работа идёт — подпись гаснет и не нажимается: второе нажатие по «Пересчитать»
   стоило бы второго обращения к модели за тот же ответ. */
.turnHeadAct:disabled{ opacity:.45; cursor:default; }

.turnHeadAct:disabled:hover{ color:var(--muted); }

/* Пока на экране расшифровка, её подпись — тоном ответа, а не подписи: по
   ней и видно, что показан не свой текст. */
.turn[data-showing="raw"] .turnHeadAct[data-source]{ color:var(--ink); }

/* Распознанное с записи встаёт на место ответа, а не под ним: это один и тот
   же ответ в двух видах, и показывать их разом — значит заставить сличать два
   столбца там, где довольно переключить один.

   Тише ответа, но не мельче до неудобства: читать эту плиту всё-таки будут, и
   ради того её и показывают. Волосок слева и приглушённый тон говорят, что
   текст этот не твой и правке не подлежит — правят чистовик. Моноширинного
   набора здесь нет нарочно: это речь человека, а не выдача машины, хотя
   показала её машина. */
.turnRaw{
  padding-left:var(--s3);
  border-left:1px solid var(--line);
  color:var(--ink-2);
  font-family:var(--serif);
  font-size:var(--t-body);
  line-height:1.7;
}

.turnRaw p{ margin:0 0 var(--s2); }

.turnRaw p:last-child{ margin-bottom:0; }

.turnRawSays{
  display:block;
  margin-bottom:var(--s2);
  font-family:var(--sans);
  font-size:var(--t-cap);
  letter-spacing:.06em;
  text-transform:uppercase;
}

/* На телефоне подписи получают палец: сама надпись остаётся той же, растёт
   только то, во что попадают. Зазор и разрядка при этом ужимаются: три слова
   в разрядку на узком экране встают ровно впритык к краю, а запас нужен и под
   крупный системный шрифт, и под экран в 320 точек. */
@media (max-width:600px){
  .turnHeadActs{ gap:var(--s2); }

  .turnHeadAct{ padding-top:var(--s2); padding-bottom:var(--s2); letter-spacing:.04em; }

  .turnHeadAct + .turnHeadAct{ padding-left:var(--s2); }

  .turnHeadAct + .turnHeadAct::before{ top:var(--s2); }
}

/* Вопрос — половина смысла записи, а не подпись к ответу:
   полный тон, крупнее ответа, отличается начертанием. */
.turnAsk{ font:italic 400 var(--t-ask)/1.4 var(--serif); color:var(--ink); }

/* Ответ — невидимое поле. Ни фона, ни рамки, ни сдвига:
   при клике появляется только каретка. */
.turnSaid{
  font:400 var(--t-read)/1.72 var(--serif);
  outline:0;
  cursor:text;
  caret-color:var(--accent);
}

/* Перечень, выключка и снимок — всё, что грамматика ответа теперь допускает.
   Больше в `AnswerHtmlSanitizer` ничего и нет, поэтому и правил здесь ровно
   столько. */
.turnSaid ul,
.turnSaid ol{ margin:var(--s3) 0; padding-left:1.4em; }

.turnSaid li{ margin-bottom:var(--s1); }

.turnSaid p{ margin:0 0 var(--s3); }

.turnSaid p:last-child{ margin-bottom:0; }

/* Снимок во всю меру набора: у ответа одна колонка, и обтекать его нечем.
   `height:auto` держит пропорции, `display:block` снимает подстрочный зазор,
   который иначе читается как лишняя пустая строка под каждой фотографией. */
.turnSaid img{
  display:block;
  width:100%;
  height:auto;
  margin:var(--s4) 0;
  border-radius:var(--r2);
  background:var(--sheet-2);
}

/* Снимок, который прямо сейчас уходит на сервер. */
.turnSaid img[data-pending="1"]{ opacity:.45; }

.primary{
  min-height:var(--tap);
  padding:0 var(--s4);
  border:1px solid var(--accent);
  border-radius:var(--r2);
  background:none;
  color:var(--accent);
  font-size:var(--t-cap);
  font-weight:600;
  letter-spacing:.08em;
  text-transform:uppercase;
  cursor:pointer;
  transition:background var(--dur) var(--ease), opacity var(--dur) var(--ease);
}

.primary:hover{ background:var(--accent-soft); }


.turnActs{ display:flex; gap:var(--s5); flex-wrap:wrap; align-items:center; margin-top:var(--s3); }

/* Клавиша слушания. Стоит она в том же ряду, что «Дописать» и «Перезаписать»,
   но у другого края: те правят ответ, эта его только проигрывает, и ставить её
   третьей подписью в ряду значило бы предлагать её как третью правку.

   Круг, волосок по краю, знак внутри — и ничего больше: ни дорожки, ни
   времени, ни громкости. Слушают здесь свой же ответ, а не разбирают запись
   по секундам; всё остальное умеет системный проигрыватель, и звать его сюда
   нечем. Размер — тот же круг, что у аккаунта в рельсе: одна и та же круглая
   клавиша на весь продукт. */
.listen{
  flex:none;
  display:grid;
  place-items:center;
  width:36px;
  height:36px;
  margin-left:auto;
  padding:0;
  border:1px solid var(--line-2);
  border-radius:50%;
  background:none;
  color:var(--ink-2);
  cursor:pointer;
  transition:color var(--dur) var(--ease), border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}

.listen:hover{ color:var(--ink); border-color:var(--ink-2); background:var(--sheet-2); }

/* Пока играет — терракотовый ободок, тот же, каким помечен открытый раздел:
   один цвет, одно значение — «вот это сейчас». */
.listen[aria-pressed="true"]{ color:var(--accent); border-color:var(--accent); }

.listen svg{ width:20px; height:20px; }

/* Знака два, виден один: играет — пауза, стоит — треугольник. Ширины у них
   разные, поэтому оба в одной ячейке сетки, а не рядом: иначе клавиша дышала
   бы на пиксель при каждом нажатии. */
.listenPause{ display:none; }

.listen[aria-pressed="true"] .listenPlay{ display:none; }

.listen[aria-pressed="true"] .listenPause{ display:block; }

/* Ответ, к которому сейчас приставлен диктофон. Метка стоит на поле слева, с
   отступом от набора: тень, прижатая к тексту, читалась как рамка блока и
   спорила с ним. Черта дышит — медленно и неглубоко, чтобы её было видно
   боковым зрением, но она не отвлекала от чтения. */
.turn[data-recording="1"]::before{
  content:"";
  position:absolute;
  top:0;
  bottom:var(--s6);
  left:calc(var(--s4) * -1);
  width:3px;
  border-radius:var(--r1);
  background:var(--accent);
  animation:breathe 2.2s var(--ease) infinite alternate;
}

@keyframes breathe{ from{ opacity:.42; } to{ opacity:1; } }

.note{
  font-size:var(--t-meta);
  color:var(--muted);
  font-style:italic;
  max-width:52ch;
}

.gone{
  display:flex;
  align-items:baseline;
  gap:var(--s3);
  padding-bottom:var(--s5);
  margin-bottom:var(--s5);
  border-bottom:1px solid var(--line);
  color:var(--muted);
  font-size:var(--t-sm);
  max-width:var(--measure);
}

.rev{
  font-family:var(--sans);
  font-size:var(--t-cap);
  letter-spacing:.06em;
  text-transform:uppercase;
  color:var(--muted);
}

/* ── Память: утверждение с обязательной ссылкой на источник. ── */
.group{ margin-bottom:var(--s7); }

.groupHead{
  display:flex;
  align-items:baseline;
  gap:var(--s3);
  margin-bottom:var(--s3);
  max-width:var(--measure);
}

.claim{
  display:grid;
  gap:var(--s2);
  padding:var(--s4) 0;
  border-bottom:1px solid var(--line);
  max-width:var(--measure);
}

.claimTop{ display:flex; align-items:baseline; gap:var(--s3); flex-wrap:wrap; }

.claimText{ font:400 var(--t-read)/1.5 var(--serif); flex:1 1 24ch; }

.mark{
  font-family:var(--sans);
  font-size:var(--t-cap);
  font-weight:600;
  letter-spacing:.08em;
  text-transform:uppercase;
  color:var(--muted);
  white-space:nowrap;
}

.mark[data-kind="hypothesis"]{ color:var(--flag); }

.mark[data-kind="conflict"]{ color:var(--flag); background:var(--flag-soft); padding:var(--s1) var(--s2); }

.claimActs{ display:flex; gap:var(--s4); align-items:center; flex-wrap:wrap; }

.quiet{
  min-height:var(--tap);
  padding:0;
  border:0;
  background:none;
  color:var(--muted);
  font-size:var(--t-cap);
  letter-spacing:.08em;
  text-transform:uppercase;
  cursor:pointer;
  transition:color var(--dur) var(--ease);
}

.quiet:hover{ color:var(--ink); }

.cite{
  min-height:var(--tap);
  padding:0 var(--s3);
  border:0;
  background:none;
  color:var(--accent);
  font-size:var(--t-cap);
  font-weight:600;
  letter-spacing:.08em;
  text-transform:uppercase;
  cursor:pointer;
  margin-left:calc(var(--s3) * -1);
}

/* Раскрытая цитата держит меру набора собственным кеглем: `ch` считается по
   шрифту самого блока, поэтому те же 68 знаков в наборе цитаты выходят уже,
   чем в наборе ответа. Строка длиннее меры читается рывками — а раскрытие
   должно быть подспорьем, а не полотном во весь лист. */
.source{
  border-left:2px solid var(--accent);
  padding:var(--s2) 0 var(--s2) var(--s4);
  display:grid;
  gap:var(--s2);
  max-width:var(--measure);
  animation:rise var(--dur) var(--ease) both;
}

.quote{ font:italic 400 var(--t-body)/1.6 var(--serif); color:var(--ink-2); }

.origin{ font-size:var(--t-meta); color:var(--muted); }

/* ── Портрет: разворот с полем для доказательств. ── */
.spread{ display:grid; gap:var(--s5); }

.para{ display:grid; gap:var(--s3); }

.paraText{ font:400 var(--t-read)/1.75 var(--serif); max-width:var(--measure); }

.ref{
  border:0;
  background:none;
  padding:0 var(--s1);
  color:var(--accent);
  font-family:var(--sans);
  font-size:var(--t-cap);
  font-weight:700;
  cursor:pointer;
  vertical-align:super;
  line-height:1;
}

/* Диагностический ряд лежит в разметке всегда, но виден только в «Рентгене».
   Режим держится атрибутом, а не классом: класс с тем же именем совпал бы
   и с самой оболочкой, и она бы пропала целиком. */
.xray{ display:none; }

.app[data-mode="xray"] .xray{
  display:grid;
  gap:var(--s4);
  padding-left:var(--s4);
  border-left:1px solid var(--line-2);
  max-width:var(--measure);
}

.xrayRow{ display:grid; gap:var(--s1); font-size:var(--t-sm); line-height:1.5; color:var(--ink-2); }

.coords{
  display:flex;
  flex-wrap:wrap;
  gap:var(--s5) var(--s6);
  max-width:var(--measure);
  padding-bottom:var(--s5);
  margin-bottom:var(--s7);
  border-bottom:1px solid var(--line);
}

.coord{ display:grid; gap:var(--s1); }

.coordVal{ font:400 var(--t-h2)/1.1 var(--serif); font-variant-numeric:tabular-nums; }

/* Тот же ряд, но значение — не число, а строка: адрес почты в кегле счётчика
   ответов читается как заголовок, которым он не является, а моноширинные
   цифры адресу и вовсе не нужны. */
.coordText{ font-size:var(--t-body); font-variant-numeric:normal; overflow-wrap:anywhere; }

.section{ padding-bottom:var(--s7); }

.sectionHead{
  margin-bottom:var(--s5);
  max-width:var(--measure);
}

.empty{
  padding:var(--s8) 0;
  color:var(--muted);
  font-size:var(--t-sm);
  max-width:var(--measure);
}

/* ── Десктоп: две зоны. Порог взят там, где правая доля вмещает
      полную меру набора; ниже раскладка остаётся одноколоночной. ── */
/* ── Телефон: шапка в две вещи ──
   Граница здесь та же, что у рельса, только с другой стороны: где рельса нет,
   там и шапка другая. Выше 1200px в ней стоят мачта, раздел, служебный угол и
   тон; ниже — имя истории слева и знак поиска справа, а всё остальное ушло на
   четвёртую клавишу дока. Дробное значение, а не 1199: между целым и целым
   есть половина пикселя, и на ней не работало бы ни то правило, ни другое.

   «Диагностика» на телефон не переезжает: это средство разработки за флагом
   `app.dev_tools_enabled`, и открывают его с большого экрана. Адрес `dev/map`
   работает по-прежнему. */
@media (max-width:1199.98px){
  .app{ --edge:var(--s4); }

  .top{ flex-wrap:nowrap; padding:var(--s2) var(--edge); }

  /* На «Аккаунте» и в «Моих историях» истории на экране нет, а мачта и раздел
     здесь не показываются: шапке было бы нечего показать, и она стояла бы
     пустой плитой в шесть десятков пикселей над собственным заголовком
     страницы. Плиты нет — заголовок и есть шапка. */
  .top:not(:has(.story-switch)):not(:has(.findOpen)){ display:none; }

  .face{ padding:0 var(--s3); }

  .mast,
  .brandSep{ display:none; }

  /* Крошки на телефоне нет, и имя истории перестало быть её последним
     звеном: оно единственное, что в шапке написано словами. Поэтому и набрано
     оно не как звено — прописными вразрядку, — а как имя: строчными, в рост
     подписи. Прописные тут отняли бы у него полстроки и прибавили строгости,
     которой в имени человека нет. */
  .story-switch summary .switchName{
    font:400 var(--t-sm)/1.3 var(--sans);
    letter-spacing:.02em;
    text-transform:none;
  }

  /* В служебном углу остаётся один поиск. Остальное — «Диагностика», «Мои
     истории», «Аккаунт», разделитель, выход и тон — либо переехало в аккаунт,
     либо не показывается вовсе. */
  .topRight > :not(.findOpen){ display:none; }

  /* Слову «Найти» нужна ширина, которой на телефоне нет: знака хватает, а
     подпись у него есть — `aria-label`. */
  .findWord{ display:none; }

  .findOpen{
    width:var(--tap);
    justify-content:center;
    padding-inline:0;
    margin-inline:0;
  }

  /* Знак остался в шапке один и вырос до своего роста: рядом с подписью он
     был шестнадцатью пикселями при ней, а сам по себе — двадцать, как знаки
     в планке правки. */
  .findOpen .ic{ width:20px; height:20px; }

  /* Пока список открыт, шапка принадлежит ему: имя истории уступает место
     слову «Истории» и крестику, а поиск уходит — искать в списке нечего.
     `:has()` тут единственный способ дотянуться от открытого меню до соседней
     ячейки шапки; где его нет, знак поиска просто останется на месте. */
  /* Раскрытый заголовок — крестик, а не клавиша: подложки у него нет ни в
     покое, ни под курсором, и скругления тоже — он идёт во всю ширину шапки. */
  .story-switch[open] summary,
  .story-switch:not([data-alone="1"])[open] summary:hover{ background:none; border-radius:0; }

  /* Пока список открыт, служебного угла нет, и крошка забирает ширину себе:
     иначе крестик стоял бы сразу за словом «Истории», а не в правом углу.
     Ширину приходится передавать по всей цепочке — крошка, переключатель,
     заголовок: у каждого звена свой флексбокс, и растянуть последнее, не
     растянув первых, нельзя. */
  .top:has(.story-switch[open]) .brand,
  .story-switch[open],
  .story-switch[open] summary{ flex:1 1 auto; min-width:0; }

  .story-switch[open] summary .switchFace,
  .story-switch[open] summary::after{ display:none; }

  .story-switch[open] .switchShut{
    display:flex;
    flex:1 1 auto;
    align-items:center;
    justify-content:space-between;
    gap:var(--s3);
  }

  .top:has(.story-switch[open]) .topRight{ display:none; }

  /* Список — отдельный экран, а не карточка поверх листа. Отсчёт от самой
     шапки: `100%` — её рост, и списку достаётся ровно всё, что ниже, до
     нижнего края. Шапка липкая и позиционирована, поэтому служит точкой
     отсчёта сама, стоит снять её у переключателя. */
  .story-switch{ position:static; }

  .switchMenu{
    left:0;
    right:0;
    height:calc(100dvh - 100%);
    min-width:0;
    max-width:none;
    max-height:none;
    grid-template-rows:auto auto minmax(0,1fr) auto;
    gap:0;
    padding:0;
    border:0;
    border-radius:0;
    background:var(--bg);
    box-shadow:none;
  }

  .switchNow{ display:grid; }

  .switchHead{ padding:var(--s5) var(--s4) var(--s2); }

  /* Строки во всю ширину экрана: подсветка идёт от края до края, как и блок
     текущей истории, и скруглять их незачем — скруглять нечего. */
  /* Строки лежат сверху, а не растягиваются по всей высоте: список — часть
     сетки во весь экран, и без этого две истории разъезжались бы на пол-экрана
     каждая. */
  .switchList{
    gap:0;
    align-content:start;
    padding-inline:0;
    margin-inline:0;
  }

  .switchList .switchName{ font-size:var(--t-read); line-height:1.25; }

  .switchOne{
    min-height:0;
    padding:var(--s3) var(--s4);
    margin-left:0;
    border-radius:0;
  }

  /* Выход в кабинет стоит на месте и со списком не едет: иначе до него
     пришлось бы прокручивать десяток чужих жизней. */
  .switchAll{
    display:flex;
    align-items:center;
    min-height:var(--tap);
    margin-top:0;
    padding:0 var(--s4);
    padding-bottom:env(safe-area-inset-bottom);
    border-top:1px solid var(--line);
  }
}

@media (min-width:1200px){
  .app{ grid-template-columns:var(--menu) 1fr; --edge:var(--s7); }

  /* На входе и в аккаунте меню нет, и делить экран нечем: лист ложится
     на стол посередине, а не прижимается к левому краю монитора. */
  .app[data-rail="0"]{ grid-template-columns:1fr; }

  .app[data-rail="0"] .sheet{ width:min(920px,100%); margin:0 auto; }

  .dictaphone{ left:var(--menu); bottom:0; padding:var(--s5) var(--s7) var(--s6); }

  /* Левая зона залита ровно, а её содержимое липнет к верху. */
  .rail{ display:block; background:var(--desk); }

  /* Меню стоит у самой границы с содержимым, а не у края монитора:
     блок прижат вправо и держится липко. */
  .railInner{
    position:sticky;
    top:0;
    height:100dvh;
    width:min(232px,100%);
    margin-left:auto;
    display:flex;
    flex-direction:column;
    gap:var(--s6);
    padding:var(--s5) var(--s6) var(--s5) 0;
  }

  .railNav{ display:flex; flex-direction:column; gap:var(--s1); }

  .railNav a,
  .railNav button{
    min-height:var(--tap);
    /* Метка — левый край строки, а не вынос за него: её край и есть общая
       вертикаль, а подпись отходит от неё на ширину жёлоба и встаёт на ту же
       вторую вертикаль, что клетки решётки и шапка «Собрано». */
    padding:0 var(--s3) 0 var(--mark-gap);
    border:0;
    border-left:var(--mark) solid transparent;
    background:none;
    color:var(--muted);
    font-size:var(--t-sm);
    text-align:left;
    cursor:pointer;
    transition:color var(--dur) var(--ease);
  }

  .railNav a:hover,
  .railNav button:hover{ color:var(--ink); }

  .railNav [data-on="1"]{ color:var(--ink); border-left-color:var(--accent); }

  /* Решётка собранного. Живёт только в рельсе: на узком экране рельса нет, и
     показывать её над диктофоном значило бы отодвигать разговор ради отчёта о
     разговоре. Туда, где она помещается целиком, ведёт ссылка «Карта жизни». */
  .census{ display:grid; gap:var(--s3); text-decoration:none; margin-top:var(--s6); }

  /* Шапка и сноска сдвинуты на жёлоб: сама решётка начинается с общей
     вертикали столбиком области, а её подписи — со второй, где стоят клетки и
     названия разделов выше. */
  .censusHead{
    display:flex;
    align-items:baseline;
    justify-content:space-between;
    gap:var(--s3);
    margin-left:calc(var(--mark) + var(--mark-gap));
  }

  .censusNote{
    margin-left:calc(var(--mark) + var(--mark-gap));
    font-size:var(--t-cap);
    color:var(--muted);
    letter-spacing:.04em;
  }

  .census:hover .cell[data-fill="0"]{ border-color:var(--line-2); }

  /* Счёт вместо карты — невысокое окно. Карте в рельсе нужно больше восьмисот
     пикселей высоты; когда их нет, `rail.js` ставит рельсу `data-fit="tally"`,
     и на месте карты встают два числа и клавиша на «Карту жизни». Без
     сценария остаётся карта: так было до сих пор, и молчащий сценарий не
     повод менять экран.

     Цифры антиквой — тем же шрифтом, что авторский текст: это числа его
     рассказа, а не наша отчётность. Служебное вокруг — гротеском и вполтона,
     как везде в рельсе. */
  .tally{ display:none; gap:var(--s5); margin-top:var(--s6); }

  .rail[data-fit="tally"] .tally{ display:grid; }

  .rail[data-fit="tally"] .census{ display:none; }

  .tallyHead,
  .score,
  .tallyNote,
  .tallyGo{ margin-left:calc(var(--mark) + var(--mark-gap)); }

  .score{ display:grid; gap:var(--s1); }

  .scoreLine{ display:flex; align-items:baseline; gap:var(--s2); }

  .scoreNow{
    font:400 58px/.9 var(--serif);
    color:var(--ink);
    font-variant-numeric:tabular-nums lining-nums;
    letter-spacing:-.01em;
  }

  .scoreAll{
    font:400 var(--t-h2)/1 var(--serif);
    color:var(--muted);
    font-variant-numeric:tabular-nums lining-nums;
  }

  .scoreOf{ font:400 var(--t-sm)/1 var(--sans); color:var(--muted); }

  .scoreWhat{ font:400 var(--t-sm)/1.3 var(--sans); color:var(--ink-2); }

  /* Второе число мельче первого: они не равны по весу. Первое — про рассказ
     целиком, второе — про его течение во времени. */
  .score[data-rank="2"] .scoreNow{ font-size:32px; }

  /* Линия жизни ростом в один рельс: та же линия, что на «Карте». Метка —
     год, и чем больше в нём событий, тем она заметнее; охрой помечено время
     без года — событие есть, год неизвестен, и метка стоит в середине
     промежутка (инвариант 4). */
  .lifeline{ display:block; margin-top:var(--s3); }

  .lifeline svg{ display:block; width:100%; height:auto; overflow:visible; }

  .lifelineBase{ stroke:var(--line-2); stroke-width:1; }

  .lifelineNow{ stroke:var(--muted); stroke-width:1; }

  .lifelineDot{ fill:var(--ink-2); }

  .lifelineDot[data-near="1"]{ fill:var(--accent); }

  /* Линия проявляется слева направо — как она и читается, — и в ту же
     секунду, что набегают числа: это один жест, а не два. */
  .lifeline[data-draw="1"] svg{ clip-path:inset(0 100% 0 0); animation:lifelineIn .62s var(--ease) forwards; }

  @keyframes lifelineIn{ to{ clip-path:inset(0 0 0 0); } }

  .lifelineYears{
    display:flex;
    justify-content:space-between;
    gap:var(--s2);
    margin-top:var(--s1);
    font-size:var(--t-cap);
    letter-spacing:.04em;
    color:var(--muted);
    font-variant-numeric:tabular-nums;
  }

  .tallyNote{ font-size:var(--t-cap); color:var(--muted); letter-spacing:.04em; }

  /* Клавиша ведёт на «Карту жизни» и ничего не раскрывает на месте: в рельсе
     карте тесно, а на своей странице она стоит целиком и с подписями. */
  .tallyGo{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:var(--s3);
    min-height:var(--tap);
    padding:0 var(--s3);
    border:1px solid var(--line-2);
    border-radius:var(--r2);
    color:var(--ink-2);
    font-size:var(--t-sm);
    text-decoration:none;
    transition:color var(--dur) var(--ease), border-color var(--dur) var(--ease), background var(--dur) var(--ease);
  }

  .tallyGo::after{ content:'\2192'; color:var(--muted); }

  .tallyGo:hover{ color:var(--ink); border-color:var(--ink-2); background:var(--sheet-2); }

  /* Совсем низкое окно: сначала уходит сноска, потом линия, и только потом
     второе число. Уходят они ради «Аккаунта» — вход в свой аккаунт нужнее
     их всех, — и в этом порядке: число без линии остаётся числом, а линия без
     числа была бы украшением. */
  .rail[data-trim="note"] .tallyNote,
  .rail[data-trim="line"] .tallyNote,
  .rail[data-trim="second"] .tallyNote,
  .rail[data-trim="scroll"] .tallyNote,
  .rail[data-trim="line"] .lifeline,
  .rail[data-trim="line"] .lifelineYears,
  .rail[data-trim="second"] .score[data-rank="2"],
  .rail[data-trim="scroll"] .score[data-rank="2"]{ display:none; }

  /* Пол у этой лестницы: окно, в котором не помещаются даже одно число и
     клавиша. Снимать дальше нечего — снимать пришлось бы то, ради чего рельс
     стоит, — и рельс начинает прокручиваться. Полоса прокрутки в тихом рельсе
     шумит, поэтому она здесь последнее средство, а не первое. */
  .rail[data-trim="scroll"] .railInner{ overflow-y:auto; overscroll-behavior:contain; }

  /* Аккаунт внизу рельса — там, где его ищут: у Клода и у GPT он стоит там же.
     Прижат к низу распоркой, а не отступом: между ним и меню сколько угодно
     пустого поля, и оно принадлежит листу, а не строке. Кружок и подпись — на
     одной линии, и вся строка держит палец: высота у неё та же, что у разделов
     выше. */
  .railMe{
    margin-top:auto;
    display:flex;
    align-items:center;
    gap:var(--s3);
    min-height:var(--tap);
    /* Ни черты, ни отрицательного поля: на общей вертикали стоит сам кружок,
       левым краем, и метить строку нечем — открытую страницу метит ободок
       кружка. Подпись за ним отходит вправо на его ширину и на вторую
       вертикаль не встаёт: двух сразу кружок не держит, а вертикаль важнее
       ровного столбца из четырёх слов. */
    padding:0 var(--s3) 0 0;
    color:var(--muted);
    font-size:var(--t-sm);
    text-decoration:none;
    transition:color var(--dur) var(--ease);
  }

  .railMe:hover{ color:var(--ink); }

  /* Открытый аккаунт помечен не чертой слева, как разделы, а ободком у кружка:
     черта метит строку списка, а здесь метить надо человека, и кружок — это
     он. Ободок рисуется тенью, а не толщиной рамки: рамка в два пикселя
     подвинула бы буквы внутри на пиксель, и кружок дёргался бы при переходе. */
  .railMe[data-on="1"]{ color:var(--ink); }

  .railMe[data-on="1"] .railDot{
    border-color:var(--accent);
    box-shadow:0 0 0 1px var(--accent);
    color:var(--ink);
  }

  /* В кружке буквы имени и фамилии, а не снимок: лицо в углу каждого экрана —
     это второй портрет рядом с тем, ради которого всё написано. Размер взят по
     буквам и по ободку: в двадцать два пикселя две прописные не помещались, а
     в тридцать шести и они стоят свободно, и красный ободок открытой страницы
     не жмёт им. Заливка бумажная, буквы — тем же гротеском прописными, что и
     остальные служебные надписи. Имени нет — кружок пустой и ровно такой же:
     метка места на месте, пока имя не спросили. */
  .railDot{
    flex:none;
    display:grid;
    place-items:center;
    width:36px;
    height:36px;
    border:1px solid var(--line-2);
    border-radius:50%;
    background:var(--sheet-2);
    color:var(--ink-2);
    font:600 var(--t-cap)/1 var(--sans);
    letter-spacing:.04em;
  }

  .railMe:hover .railDot{ border-color:var(--ink-2); }

  /* Пока рельс на экране, аккаунт в нём один. В шапке та же ссылка осталась
     для узких экранов, где рельса нет вовсе. */
  .app[data-rail="1"] .topRight [data-nav="account"]{ display:none; }

  .dock{ display:none; }

  .top{ padding:var(--s5) var(--edge); }

  /* Шапка стала выше на восемь пикселей с каждой стороны — столько же теряет
     и место под меню, иначе оно вылезло бы за нижний край экрана. Зато дока
     здесь нет, и место под него меню забирает себе. */
  .switchMenu{ max-height:calc(100dvh - var(--s5) - 100% - var(--s5)); }

  .face{ padding:0 var(--s7); }

  .body{ padding:var(--s7) var(--s7) var(--s6); }

  .footer{ padding:0 var(--s7) var(--s7); }

}

/* Двухколоночный аппарат портрета включается, только когда лист
   развернулся во всю ширину A4. Иначе колонка текста сжималась бы
   до трети шапки. */
@media (min-width:1500px){
  /* Сетку строит сам абзац, а не разворот. Раньше её строил разворот, а абзац
     раздавал в неё свои части через `display:contents` — и раскрытая цитата
     оказывалась в колонке шириной в 18 знаков, по три-четыре слова в строке.
     Теперь в поле доказательств стоит только клавиша «Источник», а сама цитата
     ложится под абзацем и в его меру: читается как продолжение текста, а не
     как столбик на полях. */
  .para{
    grid-template-columns:minmax(0,var(--measure)) minmax(0,18ch);
    column-gap:var(--s6);
    row-gap:0;
    align-items:start;
  }

  .paraText{ grid-column:1; }

  /* Поле раздаёт свои части в сетку абзаца, но сам след не разбирается на
     клавишу и панель: <details> остаётся целым и целиком стоит в колонке
     набора. Разобрать его — значит зависеть от того, срабатывает ли
     `display:contents` на самом <details>, а в движках, где не срабатывает,
     цитата остаётся в поле шириной в 18 знаков. Целый след ведёт себя
     одинаково везде: клавиша под абзацем, цитата под клавишей, обе в меру
     набора. В поле остаётся то, чему там и место, — ряд «Рентгена». */
  .aside{ display:contents; }

  .aside > .trace{ grid-column:1; }

  .aside > .xray{ grid-column:2; grid-row:1; }
}

@media (prefers-reduced-motion:reduce){
  *{ animation:none !important; transition:none !important; }
}

/* ═══════════════════════════════════════════════════════
   ЧЕГО НЕ БЫЛО В ПРОТОТИПЕ
   Экраны входа, аккаунта и установки; раскрытие источника
   через <details>, чтобы след к первоисточнику работал без
   JavaScript. Ни одного нового токена: только те же
   переменные, что и выше.
   ═══════════════════════════════════════════════════════ */

/* Ссылки. Терракота в этой теме означает след к первоисточнику, поэтому
   обычная ссылка держит цвет текста и отличается только чертой. */
a{ color:var(--ink); text-decoration-color:var(--line-2); text-underline-offset:3px; }

a:hover{ text-decoration-color:var(--accent); }

/* Навигация — ссылки, а не кнопки: страница приходит с сервера. */
.topRight a{ text-decoration:none; }

.topRight a:hover{ color:var(--ink); }

/* Аккаунт в правом углу отмечается так же, как раздел в рельсе: акцентом и
   чернилами вместо серого. Подчёркивания здесь нет — угол шапки узкий, и
   линия под словом читалась бы как вторая граница шапки. */
.topRight [data-on="1"]{ color:var(--ink); }

/* Выход отделён от имени аккаунта чертой и тише соседей: это выход из сеанса,
   а не ещё один раздел, и нажимают его редко. */
.topSep{ width:1px; height:14px; background:var(--line-2); }

.folioOut{
  min-height:var(--tap);
  padding:0;
  border:0;
  background:none;
  text-transform:uppercase;
  cursor:pointer;
  opacity:.75;
  transition:color var(--dur) var(--ease), opacity var(--dur) var(--ease);
}

.folioOut:hover{ color:var(--accent); opacity:1; }

.dock a{ display:flex; align-items:center; justify-content:center; text-decoration:none; }

.railNav a{ display:flex; align-items:center; text-decoration:none; }

/* След к источнику. Сводка — та же клавиша, что .cite в прототипе,
   раскрытие — та же плашка с терракотовым краем. */
.trace{ display:grid; }

.trace > summary{
  display:inline-flex;
  align-items:center;
  justify-self:start;
  min-height:var(--tap);
  padding:0 var(--s3);
  margin-left:calc(var(--s3) * -1);
  color:var(--accent);
  font-family:var(--sans);
  font-size:var(--t-cap);
  font-weight:600;
  letter-spacing:.08em;
  text-transform:uppercase;
  cursor:pointer;
  list-style:none;
}

.trace > summary::-webkit-details-marker{ display:none; }

.trace > summary::marker{ content:""; }

/* Утверждение: сводка источника и отказ стоят в одном ряду, раскрытая
   цитата ложится под ними во всю ширину. `display:contents` на <details>
   нужен именно для этого: клавиша остаётся ровно там, где на неё
   нацелились, а панель не растягивает её колонку. */
/* Колонка набора гибкая, колонка кнопки — по её собственной ширине. Наоборот
   было нельзя: раскрытая цитата попадает в первую колонку, и если та задана
   как `max-content`, длинная цитата растягивает её изнутри `max-width` —
   панель уезжает за край листа, а страница получает горизонтальную прокрутку.
   Проверено на движке, где `display:contents` у <details> не срабатывает:
   колонка вырастала до 1130px при листе в 643, документ — до 1707px при
   экране 1255. Теперь распирать нечего: гибкая колонка сжимается, а вторая
   ровно такая, как надпись на кнопке. */
.claim{ grid-template-columns:minmax(0,1fr) max-content; column-gap:var(--s5); }

.claimTop{ grid-column:1 / -1; grid-row:1; }

.claim > .trace{ display:contents; }

.claim > .trace > summary{ grid-column:1; grid-row:2; }

.claim > .trace > .source{ grid-column:1; grid-row:3; margin-top:var(--s2); }

/* Chrome 131+ заворачивает содержимое <details> в `::details-content`, и
   элементом сетки становится он, а не панель внутри. Правило строкой выше
   там не работает вовсе: панель вставала в первую свободную клетку, то есть
   в колонку под клавишей, и цитата читалась в треть ширины утверждения.
   Движки без этого псевдоэлемента правило пропускают целиком — и место
   панели назначает строка выше. Поэтому обе, а не одна. */
.claim > .trace::details-content{ grid-column:1; }

.claimReject{ grid-column:2; grid-row:2; justify-self:end; align-self:center; }

/* ── Своё окно с вопросом «точно?» ──
   Родное `confirm()` не знает ни темы, ни набора и говорит чужим голосом о
   том, что человек вот-вот потеряет. `dialog` взят ради поведения — он сам
   модальный, сам закрывается по Esc и сам возвращает фокус туда, откуда его
   позвали, — а вид у него весь наш: лист бумаги на затемнённом столе.

   Ширина по мере набора, а не по экрану: вопрос читается одной строкой глаз,
   как и всё остальное на этом листе. */
/* ── Окно продукта ──
   Окно в LIFE одно на все случаи: подтверждение удаления, разговор перед
   долгой работой, добавление материала. Одна мера, одна бумага, один порядок
   клавиш — иначе каждое окно читается новым местом, и человек заново
   разбирается, где здесь «да». Состав всегда такой: заголовок `.sureTitle`,
   несколько строк `.sureText`, ряд `.sureActs` — отказ слева тише, согласие
   справа.

   Родное окно центрируется полями `auto`, а сброс выше стирает поля у всего
   подряд, поэтому центр проставлен вручную: края по нулям, поля обратно в
   `auto`, высота по содержимому. */
.sure{
  position:fixed;
  inset:0;
  width:min(34rem, calc(100vw - var(--s6)));
  height:fit-content;
  /* Длинному окну — прокрутка внутри себя, а не срезанный низ: у окна
     материала список видов длиннее иного экрана телефона. */
  max-height:min(84vh, calc(100vh - var(--s6)));
  overflow:auto;
  overscroll-behavior:contain;
  margin:auto;
  padding:0;
  border:1px solid var(--line-2);
  border-radius:var(--r2);
  background:var(--card);
  color:var(--ink);
}

.sure::backdrop{ background:rgba(25,23,19,.38); }

.sureBody{ display:grid; gap:var(--s4); padding:var(--s6); }

.sureTitle{ font:400 var(--t-h2)/1.25 var(--serif); letter-spacing:-.015em; }

.sureText{ color:var(--ink-2); font-size:var(--t-sm); line-height:1.55; }

/* Согласие справа, отказ слева и тише: рука идёт слева направо, и
   необратимое стоит последним, а не первым под пальцем. */
.sureActs{ display:flex; align-items:center; justify-content:flex-end; gap:var(--s5); margin-top:var(--s2); }

.sureYes{
  min-height:var(--tap);
  padding:0 var(--s5);
  border:1px solid var(--accent);
  border-radius:var(--r2);
  background:var(--accent);
  color:var(--on-accent);
  font-size:var(--t-cap);
  font-weight:600;
  letter-spacing:.08em;
  text-transform:uppercase;
  cursor:pointer;
  transition:opacity var(--dur) var(--ease);
}

.sureYes:hover{ opacity:.88; }

/* ── Окно догрузки ──
   То же окно, что «удалить ответ», только разговор в нём длиннее: человек
   выбирает не «да или нет», а чьи слова он приносит. Отсюда и рост в ширину и
   прокрутка внутри: на телефоне четыре вида с объяснениями не помещаются в
   экран, а обрезать объяснение — значит вернуть выбор вслепую, ради которого
   окно и заведено.

   Вид — не строка списка, а карточка: название и под ним, чем этот файл
   ляжет. Выбранная помечена подложкой и рамкой, а не одной точкой слева:
   точка отвечает, что нажато, а не что произойдёт. */
.importSure{ width:min(36rem, calc(100vw - var(--s6))); max-height:calc(100dvh - var(--s7)); overflow:auto; }

/* Полосы прокрутки не видно, а прокрутка есть: окно листается колесом,
   клавишами и пальцем, но серая колонка справа резала карточки видов по краю
   и читалась рамкой. */
.importSure{ scrollbar-width:none; }

.importSure::-webkit-scrollbar{ width:0; height:0; }

/* Крестик держится верхнего правого угла и при прокрутке остаётся на месте:
   закрывают окно оттуда, где смотрят, а не долистав до низа. */
.sureShut{
  position:sticky;
  top:var(--s4);
  z-index:1;
  justify-self:end;
  display:grid;
  place-items:center;
  width:var(--tap);
  height:var(--tap);
  margin:calc(var(--s4) * -1) calc(var(--s4) * -1) calc(var(--tap) * -1) 0;
  padding:0;
  border:0;
  border-radius:var(--r1);
  background:none;
  color:var(--muted);
  cursor:pointer;
  transition:color var(--dur) var(--ease), background var(--dur) var(--ease);
}

.sureShut:hover{ background:var(--sheet-2); color:var(--ink); }

/* Рамки у группы нет: каждая карточка вида уже обведена своей, и вторая линия
   вокруг них читается сбоем вёрстки, а не группировкой. Браузерная рамка
   `fieldset` снимается здесь же вместе с его отступами. */
.importPick{ display:grid; gap:var(--s3); border:0; padding:0; margin:0; }

.importPick > legend{ padding:0; }

.importKind{
  display:grid;
  grid-template-columns:auto 1fr;
  align-items:start;
  gap:var(--s2) var(--s4);
  padding:var(--s4);
  border:1px solid var(--line);
  border-radius:var(--r2);
  cursor:pointer;
  transition:border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}

.importKind:hover{ border-color:var(--accent); }

.importKind:has(input:checked){ border-color:var(--accent); background:var(--accent-soft); }

.importKind:focus-within{ outline:2px solid var(--accent); outline-offset:3px; }

.importKind input{ grid-row:1 / span 2; margin-top:.2em; }

.importKindName{ font-weight:600; font-size:var(--t-sm); }

.importKindNote{ color:var(--ink-2); font-size:var(--t-meta); line-height:1.5; }

/* Имя спрашивается только у рассказа о герое: у справки рассказчика нет, а
   свой рассказ человек уже подписал названием истории. Где `:has()` нет, поле
   остаётся видимым — сервер читает его лишь при этом виде. */
.importBody:has(.importPick input:checked:not([value="ABOUT"])) .importWho{ display:none; }

.importHow{ display:grid; gap:var(--s2); }

/* Пример показывается тот, который выбран: в файле людей в скобках стоят темы,
   а не дата, и общий на всех пример подсказывал троим из четырёх не тот
   формат. Где `:has()` нет, остаются видны все четыре — длиннее, но честно. */
.importBody:has(.importPick input[value="SELF"]:checked) .importSample:not([data-kind="SELF"]),
.importBody:has(.importPick input[value="ABOUT"]:checked) .importSample:not([data-kind="ABOUT"]),
.importBody:has(.importPick input[value="DATES"]:checked) .importSample:not([data-kind="DATES"]),
.importBody:has(.importPick input[value="PEOPLE"]:checked) .importSample:not([data-kind="PEOPLE"]){ display:none; }

.importSample{ display:grid; gap:var(--s2); }

/* Полка загруженного — под чертой и тише формы: она отвечает на вопрос «что я
   уже принёс», а не «что принести». */
.importShelf{ display:grid; gap:var(--s3); padding:var(--s6); border-top:1px solid var(--line); }

.importOne{ display:grid; gap:var(--s1); }

.importOneName{ font-weight:600; font-size:var(--t-sm); }

.importOneNote{ color:var(--muted); font-size:var(--t-meta); }

/* ── Числа над картой: объём рассказанного ──
   Три числа и подпись под каждым. Ряд собран сеткой о двух строках, и каждое
   число берёт обе своей сеткой (`subgrid`): цифры стоят на одной линии, а
   подписи — на своей, как бы ни разошлись «12» и «18 400» по ширине и сколько
   бы строк ни заняла подпись на телефоне. Флексом это не держится: там каждый
   столбец меряется сам по себе, и подпись под коротким числом уезжала ниже
   подписи под длинным. */
.volume{
  max-width:var(--measure);
  padding-bottom:var(--s5);
  margin-bottom:var(--s6);
  border-bottom:1px solid var(--line);
}

.figures{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(0,max-content));
  grid-template-rows:auto auto;
  gap:var(--s1) var(--s6);
  justify-content:start;
  align-items:end;
}

.figure{
  display:grid;
  grid-row:span 2;
  grid-template-rows:subgrid;
  align-items:end;
  gap:var(--s1);
  padding:0;
  border:0;
  background:none;
  color:inherit;
  font:inherit;
  text-align:left;
  cursor:pointer;
}

/* Пока скрипта нет, нажимать нечего: клавиша приходит запертой и отпирается
   сценарием (`volume.js`). Мёртвая клавиша хуже её отсутствия. */
.figure[disabled]{ cursor:default; }

.figureVal{
  font:400 var(--t-h1)/1 var(--serif);
  font-variant-numeric:tabular-nums;
  transition:color var(--dur) var(--ease);
}

.figureUnit{
  padding-bottom:2px;
  border-bottom:1px solid transparent;
  color:var(--muted);
  font-size:var(--t-sm);
  line-height:1.3;
  transition:color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.figure:not([disabled]):hover .figureVal,
.figure[aria-expanded="true"] .figureVal{ color:var(--accent); }

.figure:not([disabled]):hover .figureUnit{ border-bottom-color:var(--line-2); }

.figure[aria-expanded="true"] .figureUnit{ color:var(--accent); border-bottom-color:var(--accent); }

.figure:focus-visible{ outline:2px solid var(--accent); outline-offset:4px; }

/* Пока ничего не раскрыто, места под пояснение нет вовсе: пустая полоса в
   треть экрана между числами и картой — это не «место наготове», а дыра.
   Раскрытое пояснение сдвигает карту вниз, и это честно: человек сам его
   позвал и смотрит в него, а не на таблицу под ним. Высота у всех трёх
   пояснений всё равно одна — они написаны в одну меру, — поэтому переход от
   одного к другому карту уже не двигает. */
.gloss{
  position:relative;
  margin-top:var(--s5);
  padding:var(--s4) var(--s5);
  border:1px solid var(--line);
  border-left:2px solid var(--accent);
  border-radius:var(--r2);
  background:var(--card);
}

.glossTitle{ display:block; margin-bottom:var(--s2); font:400 var(--t-h2)/1.2 var(--serif); }

.glossText{ color:var(--ink-2); font-size:var(--t-sm); line-height:1.5; }

.glossText + .glossText{ margin-top:var(--s2); }

/* Знак маленький, зона нажатия во весь угол: закрыть пояснение — движение
   мелкое и частое, и промахиваться в нём человеку не за что. */
.glossClose{
  position:absolute;
  top:var(--s1);
  right:var(--s1);
  display:grid;
  place-items:center;
  width:var(--tap);
  height:var(--tap);
  border:0;
  background:none;
  color:var(--muted);
  cursor:pointer;
  transition:color var(--dur) var(--ease);
}

.glossClose:hover{ color:var(--accent); }

.glossClose:focus-visible{ outline:2px solid var(--accent); outline-offset:-4px; }

/* На узком экране числа стоят теснее: подпись под ними переносится в две
   строки, и широкий зазор растащил бы ряд по всей ширине. */
@media (max-width:640px){
  .figures{ gap:var(--s1) var(--s5); }
}

/* ── Что собрано: решётка «область жизни × возраст». ──
   Цвет здесь набран чернилами, а не терракотой: терракота в этой теме означает
   след к первоисточнику, охра — неуверенность, и третьего значения у цвета
   быть не должно. Заполненность читается плотностью, а не оттенком смысла. */
.grid{ display:grid; gap:4px; }

/* Первая колонка — метка вместе с жёлобом, без зазора между ними: зазор перед
   клетками стоит внутри неё, поэтому клетки выходят на ту же вертикаль, что
   подписи разделов, а столбик области — на ту, что метка открытого раздела. */
.gridRow{
  display:grid;
  grid-template-columns:calc(var(--mark) + var(--mark-gap) - 4px) repeat(5,1fr);
  gap:4px;
  align-items:stretch;
}

.gridArea{ width:var(--mark); border-radius:1px; background:var(--line); }

.gridArea[data-on="1"]{ background:var(--ink-2); }

.cell{
  min-height:16px;
  border:1px solid var(--line);
  border-radius:1px;
  background:none;
}

/* Есть чем ответить — и есть о чём рассказывать дальше. Порог плотности
   считает `CoverageCensus`, а не эта таблица: он про то, много ли известно. */
.cell[data-fill="1"]{ border-color:transparent; background:color-mix(in srgb, var(--ink) 22%, transparent); }

.cell[data-fill="2"]{ border-color:transparent; background:var(--ink-2); }

/* Карта во весь экран: те же клетки, но с подписями по обеим осям.

   Раскладку решает ширина самой таблицы, а не ширина окна. Окно про таблицу не
   знает: на широком мониторе лист занимает меньшую долю экрана, потому что
   слева стоит рельса, и по вьюпорту в 1400px таблице доставалось меньше места,
   чем по вьюпорту в 1140px без рельсы. Пороги, взятые с окна, включали там
   просторную раскладку в тесной колонке, и подписи возрастов налезали друг на
   друга. Поэтому мерой служит сама колонка. */
.mapWrap{ container-type:inline-size; }

.mapTable{ display:grid; gap:var(--s2); margin-bottom:var(--s5); }

.mapHead,
.mapRow{ display:grid; grid-template-columns:minmax(0,11ch) repeat(5,minmax(0,1fr)) 5ch; gap:var(--s2); align-items:center; }

/* На телефоне подписи возрастов не встают над клетками: пять слов вроде
   «молодость» требуют вчетверо больше ширины, чем сама клетка, и налезают
   друг на друга. Там ось названа строкой под решёткой — это те же слова в том
   же порядке, только читаются они как предложение, а не как заголовки. */
.mapHead{ display:none; }

.mapLegend{ margin-bottom:var(--s6); }

.mapHint{ margin-bottom:var(--s6); }

.mapRow .cell{ min-height:26px; }

.mapArea{ font-size:var(--t-sm); color:var(--ink-2); overflow-wrap:anywhere; }

/* Колонка «без времени» — такая же клетка, только не квадрат, а число:
   нажимается и она, потому что «про учёбу рассказано, но неизвестно когда» —
   такой же повод заговорить, как и пустой квадрат. */
.mapUndated{
  font-size:var(--t-meta);
  text-decoration:none;
  color:var(--muted);
  text-align:right;
  font-variant-numeric:tabular-nums;
  cursor:pointer;
  transition:color var(--dur) var(--ease);
}

.mapUndated:hover{ color:var(--ink); }

/* Клетка карты нажимается: она и отчёт о собранном, и вход в разговор об этой
   области. Отсюда рамка на наведении — единственное, что в ней меняется:
   заливка уже занята под то, сколько известно, и вторым смыслом на том же
   месте была бы путаница. */
a.cell{ display:block; }

a.cell:hover{ border-color:var(--accent); }

/* Дальше таблица становится одной решёткой, а не стопкой независимых рядов:
   шапка и строки отдают свои ячейки общей сетке через `display:contents`, и
   колонки под названия областей и правые пометки получают ширину по самому
   длинному содержимому. Пока сетка была своя у каждого ряда, эти колонки
   приходилось задавать на глаз в `ch`, и то, что не помещалось, переносилось:
   «время вокруг: город, страна, мир» шло в две строки, «без времени» в шапке —
   тоже, и ряды выходили разной высоты. Теперь ни одна подпись не переносится,
   а высоту ряда задаёт клетка. */
@container (min-width:640px){
  .mapTable{
    grid-template-columns:minmax(0,max-content) repeat(5,minmax(0,1fr)) minmax(0,max-content);
    column-gap:var(--s3);
    align-items:center;
  }

  .mapRow{ display:contents; }

  .mapArea,
  .mapUndated{ white-space:nowrap; }
}

/* Шапка встаёт, как только колонка возраста становится шире слова
   «молодость»: одно длинное слово не переносится и в колонке уже себя лезет на
   соседнюю. Пока ширины нет, ось названа строкой под решёткой — теми же
   словами и в том же порядке, только читается она как предложение.
   «Поздние годы» здесь ещё занимают две строки: это два слова, и перенос между
   ними ничего не ломает — высота ряда с клетками от шапки не зависит. */
@container (min-width:660px){
  .mapHead{ display:contents; }

  .mapLegend{ display:none; }

  /* Трек снят: он рассчитан на просторную шапку, а здесь съедал бы ту самую
     ширину, ради которой шапка и появилась. В тесной полосе снимается ещё и
     кегль: «молодость» — одно слово, переносить его негде, и на 11 пикселях
     оно обрезалось краем колонки. Порог опущен с 720 до 660 ради стола с
     рельсом: там на решётку остаётся под семьсот пикселей, и подписи возрастов
     пропадали ровно на той ширине, где человек чаще всего и смотрит карту. */
  .mapHead .kicker{ line-height:1.2; letter-spacing:0; font-size:10px; }

  /* Правый столбец читается от правого края: число стоит под своим
     заголовком, а не разъезжается с ним по краям колонки. */
  .mapHead .kicker:last-child{ text-align:right; }
}

@container (min-width:760px){
  .mapHead .kicker{ font-size:var(--t-cap); }
}

/* Просторно: подписи возрастов встают в одну строку и возвращают себе трек. */
@container (min-width:1060px){
  .mapHead .kicker{ white-space:nowrap; letter-spacing:var(--track); }
}

/* Сообщение о сохранении: висит под шапкой, ничего не сдвигая. */
.saveStatus{
  margin-top:var(--s4);
  color:var(--muted);
  font-size:var(--t-cap);
  letter-spacing:.06em;
  text-transform:uppercase;
}

.saveStatus:empty{ display:none; }


/* ── Прочтения: какие книги выходят из рассказанного ──
   Экран «Что из этого выйдет». Книга здесь выглядит книгой — та же обложка,
   что на полке, только не отпечатанная: книги ведь ещё нет. Обложка слева,
   рассказ о книге справа, блоки один под другим: шесть обложек в ряд
   читались бы витриной, а тут надо прочесть каждую. */
/* Что книга набирает по ходу разговора: строка на полке, не полоса.
   Полоса означала бы план, который человек обязан выполнить, а он просто
   рассказывает. */
.bookPlaces{ font-size:var(--t-sm); color:var(--muted); }

/* «Можно писать» — единственное место, где полка говорит о книге терракотой:
   это новость, а не состояние. */
.bookPlaces[data-ready="1"]{ color:var(--accent); }

/* Книги стоят порознь: между ними не отступ, а разрыв с чертой, иначе цитата
   одной книги читается подписью к следующей. Черта — та же волосяная линия,
   что отделяет подпись со счётом ответов, и стоит она посередине разрыва. */
/* ── Догадки о книгах: второй этаж полки ──
   Раздел отделён от полки чертой во всю ширину листа: то, что уже начато, и
   то, что ещё выходит из рассказанного, — разные мысли, а не продолжение
   ряда обложек. */
.ideas{
  display:grid;
  gap:var(--s5);
  margin-top:var(--s8);
  border-top:1px solid var(--line);
  padding-top:var(--s7);
}

.ideasHead{ display:grid; gap:var(--s3); max-width:var(--measure); }

/* Заголовок раздела тише заголовка страницы и громче названия книги в
   догадке: три ступени набора — страница, раздел, книга. */
.ideasTitle{ font:400 clamp(23px,2.2vw,26px)/1.2 var(--serif); letter-spacing:-.01em; text-wrap:balance; }

/* Клавиша и строка о пределе стоят вместе: сколько поисков осталось — часть
   самой клавиши, а не примечание где-то ниже. */
.ideasFoot{
  display:flex;
  align-items:center;
  gap:var(--s3) var(--s5);
  flex-wrap:wrap;
}

.ideasFoot .note{ margin:0; }

.ideas .figures{ margin:0; }

.readings{ display:grid; gap:var(--s8); margin-top:var(--s6); }

.readings .reading + .reading{ border-top:1px solid var(--line); padding-top:var(--s8); }

/* На телефоне разрыв тот же по смыслу, но мельче: там книга и так одна в
   ширину экрана, и 64 px сверху и снизу выгоняли бы соседа за экран. */
@media (max-width:560px){
  .readings{ gap:var(--s6); }
  .readings .reading + .reading{ padding-top:var(--s6); }
}

/* Золотое сечение: меньшая доля — книга, большая — рассказ о ней. Обложка
   занимает свою долю целиком, поэтому название встаёт на ней целыми строками;
   маленькая обложка сбоку от сплошного текста ломала его по слогам. */
.reading{
  display:grid;
  grid-template-columns:38.2% minmax(0,1fr);
  column-gap:var(--s6);
  row-gap:var(--s5);
  align-items:start;
  max-width:760px;
}

/* На телефоне сечения нет: книга встаёт над рассказом и перестаёт расти. */
@media (max-width:560px){
  .reading{ grid-template-columns:minmax(0,1fr); column-gap:0; row-gap:var(--s4); }
  .reading .bookCover{ max-width:190px; }
}

/* Столбцом, а не сеткой: подпись с клавишей прижимается к низу обложки, и
   блок закрывается решением, а не обрывается на полуслове. */
.readingText{ display:flex; flex-direction:column; gap:var(--s4); min-width:0; min-height:100%; }

.readingSum{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:var(--s3) var(--s5);
  flex-wrap:wrap;
  margin-top:auto;
  padding-top:var(--s2);
}

/* Обложка догадки набирается той же формулой, что и полочная: доли ширины
   вместо пикселей, и своей меры ей не нужно. */

.readingTitle{ font:400 clamp(21px,2vw,26px)/1.22 var(--serif); text-wrap:balance; }

.readingWhat{ font:400 var(--t-body)/1.55 var(--serif); color:var(--ink-2); }

.readingFoot{ font-size:var(--t-sm); color:var(--muted); }

.readingFoot b{ font-weight:400; color:var(--ink-2); font-variant-numeric:tabular-nums; }

.readingActs{ margin:0; }

/* Ряд из тихих клавиш стоит строкой: форма вокруг клавиши не делает её
   отдельным абзацем. */
/* Ряд действий страницы прочтений: клавиша и тихие ссылки на одной строке.
   Форма вокруг клавиши не делает её отдельным абзацем. */
.acts{
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  gap:var(--s4) var(--s5);
  margin-top:var(--s5);
}

.acts[data-quiet="1"]{ align-items:baseline; }

/* Задуманная книга остаётся на месте и говорит, что будет дальше: дальше от
   человека ничего не требуется. */
.reading[data-taken="1"] .bookCover{ box-shadow:0 1px 2px rgba(0,0,0,.22), 0 10px 24px rgba(0,0,0,.14), 0 0 0 2px var(--accent); }

.readingTaken{ font-size:var(--t-sm); color:var(--accent); }

.readingTaken span{ color:var(--muted); }

/* ── Чтение: три строки обычными словами ──
   Человек должен понимать, что происходит прямо сейчас, без наших слов
   «свод» и «прочтение» (docs/NAMING.md). Лампа — та же, что лампа записи. */
.steps{ display:grid; gap:var(--s5); margin-top:var(--s6); max-width:var(--measure); }

.step{ display:grid; grid-template-columns:auto 1fr; gap:var(--s4); align-items:start; }

.stepLamp{ position:relative; width:9px; height:9px; margin-top:9px; }

.stepLamp i{ position:absolute; inset:0; border-radius:50%; background:var(--line-2); }

.step[data-state="done"] .stepLamp i{ background:var(--ink-2); }

.step[data-state="now"] .stepLamp i{ background:var(--accent); }

.step[data-state="now"] .stepLamp em{
  position:absolute;
  inset:-6px;
  border-radius:50%;
  border:1px solid var(--accent);
  animation:stepHalo 1.6s var(--ease) infinite;
}

@keyframes stepHalo{ from{ opacity:.7; transform:scale(.7); } to{ opacity:0; transform:scale(1.35); } }

@media (prefers-reduced-motion:reduce){
  .step[data-state="now"] .stepLamp em{ animation:none; opacity:.5; }
}

.stepWhat{ font:400 var(--t-read)/1.4 var(--serif); color:var(--muted); }

.step[data-state="done"] .stepWhat{ color:var(--ink-2); }

.step[data-state="now"] .stepWhat{ color:var(--ink); }

.stepSay{ display:block; margin-top:var(--s1); font:400 var(--t-sm)/1.4 var(--sans); color:var(--muted); }

/* ── Вход, аккаунт, состояние подарка ── */
.form{ display:grid; gap:var(--s4); max-width:34rem; }

.form + .form{
  margin-top:var(--s7);
  padding-top:var(--s6);
  border-top:1px solid var(--line);
}

.formTitle{ font:400 var(--t-h2)/1.25 var(--serif); letter-spacing:-.015em; }

.field{ display:grid; gap:var(--s2); }

.field input{
  width:100%;
  min-height:var(--tap);
  border:0;
  border-bottom:1px solid var(--line-2);
  padding:0 0 var(--s2);
  background:none;
  color:var(--ink);
  font:400 var(--t-read)/1.5 var(--serif);
}

.field input:focus{ border-bottom-color:var(--accent); outline:0; }

/* Выпадающий список в форме — та же строка с чертой, что и ввод. */
.field select{
  width:100%;
  min-height:var(--tap);
  border:0;
  border-bottom:1px solid var(--line-2);
  padding:0 0 var(--s2);
  background:none;
  color:var(--ink);
  font:400 var(--t-read)/1.5 var(--serif);
}

.field select:focus{ border-bottom-color:var(--accent); outline:0; }

/* Многострочное поле — та же строка с чертой, только их несколько: замысел
   книги просит сказать о желаемом своими словами, а не выбрать из списка. */
.field textarea{
  width:100%;
  border:0;
  border-bottom:1px solid var(--line-2);
  padding:0 0 var(--s2);
  background:none;
  color:var(--ink);
  font:400 var(--t-read)/1.5 var(--serif);
  resize:vertical;
}

.field textarea:focus{ border-bottom-color:var(--accent); outline:0; }

/* `align-content:start` — чтобы две радиокнопки рядом с тремя не растягивались
   по высоте соседа: в паре «как обращаться» и «род в вопросах» левый набор
   расползался вдвое. */
.choice{ display:grid; gap:var(--s3); border:0; align-content:start; }

/* Полка книг. Каждая книга — карточка с нарисованной обложкой, как в
   книжном: ткань переплёта, корешок, рамка тиснения. Подсеткой полки
   обложки, метаданные, ярлыки и действия всех карточек стоят на общих
   линиях ряда. */
.shelf{
  display:grid;
  grid-template-columns:repeat(auto-fill,minmax(224px,1fr));
  grid-auto-rows:auto;
  gap:var(--s7) var(--s6);
  align-items:start;
}

/* На телефоне обложки мельче, но их две в ряд: одна обложка во весь экран
   читалась бы плакатом, а не полкой. */
@media (max-width:560px){
  .shelf{ grid-template-columns:repeat(auto-fill,minmax(148px,1fr)); gap:var(--s6) var(--s4); }
}

.bookOne{
  display:grid;
  grid-template-rows:subgrid;
  grid-row:span 2;
  row-gap:var(--s4);
  min-width:0;
}

@supports not (grid-template-rows:subgrid){
  .bookOne{ grid-template-rows:auto auto; }
}

.bookCover{
  /* `min-width:0` обрывает перенесённый из высоты ряда минимум: без него
     обложка с заданной пропорцией выводила ширину из общей высоты ряда и
     вылезала из своей колонки на узкой полке. */
  min-width:0;
  width:100%;
  max-width:100%;
  align-self:start;
  aspect-ratio:2/3;
  border-radius:var(--r1) var(--r2) var(--r2) var(--r1);
  /* Ткань переплёта: цвет плюс тонкое переплетение нитей — два повторяющихся
     градиента по волокну — и мягкий свет слева сверху. Никаких картинок:
     ткань дешевле рисуется, чем грузится. */
  background:
    linear-gradient(115deg, rgba(255,255,255,.10), transparent 45%, rgba(0,0,0,.14)),
    repeating-linear-gradient(90deg, rgba(0,0,0,.045) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(0deg, rgba(255,255,255,.035) 0 1px, transparent 1px 3px),
    var(--cloth);
  color:var(--foil,var(--gold));
  position:relative;
  /* Единица набора на обложке — её собственная ширина: одна формула кегля
     служит и крупной книге на полке, и маленькой в догадке. */
  container-type:inline-size;
  display:grid;
  grid-template-rows:auto 1fr auto;
  /* Поля долями, а не пикселями: иначе на маленькой обложке текст упирается
     в рамку тиснения, а на большой болтается посреди пустого поля. */
  padding:11% 13% 8% calc(13% + 14px);
  box-shadow:0 1px 2px rgba(0,0,0,.28), 0 14px 30px rgba(0,0,0,.18);
  overflow:hidden;
}

/* Корешок: тень сгиба у левого края, блик по ребру и фальц — жёлоб, которым
   переплёт отделён от крышки. Фальц не линия: в книге это углубление, куда
   уходит ткань, поэтому он рисуется мягким переходом в несколько пикселей —
   свет по ребру корешка, тень в самом жёлобе, подъём на крышке. Резкая линия
   в один пиксель читалась печатной чертой, а не сгибом. */
.bookCover::before{
  content:"";
  position:absolute;
  inset:0 auto 0 0;
  width:20px;
  background:linear-gradient(90deg,
    rgba(0,0,0,.26) 0,
    rgba(0,0,0,.10) 36%,
    rgba(255,255,255,.10) 56%,
    rgba(0,0,0,.07) 76%,
    rgba(0,0,0,.16) 88%,
    rgba(0,0,0,.03) 100%);
}

/* Рамка тиснения — двойная, как на переплёте: тонкая черта и волосяная
   изнутри. Доли вместо пикселей по той же причине, что и поля. */
.bookCover::after{
  content:"";
  position:absolute;
  inset:5% 5% 5% calc(5% + 14px);
  border:1px solid color-mix(in srgb, var(--foil,var(--gold)) 52%, transparent);
  box-shadow:inset 0 0 0 4px color-mix(in srgb, var(--foil,var(--gold)) 22%, transparent);
  border-radius:var(--r1);
  pointer-events:none;
}

.bookCover[data-cloth="teal"]{ --cloth:var(--cloth-teal); }
.bookCover[data-cloth="umber"]{ --cloth:var(--cloth-umber); }
.bookCover[data-cloth="terra"]{ --cloth:var(--cloth-terra); }

/* Светлой ткани — тёмное тиснение: светлое золото на ней выцветает и
   название приходится угадывать. Правило одно на сливочный холст; охра
   потемнела до табака и держит обычное золото. */
.bookCover[data-cloth="camel"]{ --cloth:var(--cloth-camel); }

.bookCover[data-cloth="cream"]{
  --cloth:var(--cloth-cream);
  --foil:var(--gold-deep);
  box-shadow:0 1px 2px rgba(0,0,0,.12), 0 10px 24px rgba(0,0,0,.10);
}

/* Замысел: обложка ещё не «отпечатана» — бледная ткань, пунктирная рамка.
   Отпечатает её первая сборка. */
.bookCover[data-ghost="1"]{
  background:color-mix(in srgb, var(--cloth) 30%, var(--bg));
  color:color-mix(in srgb, var(--cloth) 70%, var(--ink));
  box-shadow:none;
  border:1px solid var(--line);
}

/* Замысел: тот же жёлоб, но тоном ткани, а не тенью — на бледной обложке
   тень была бы грязным пятном. */
.bookCover[data-ghost="1"]::before{
  background:linear-gradient(90deg,
    color-mix(in srgb, var(--cloth) 38%, transparent) 0,
    color-mix(in srgb, var(--cloth) 20%, transparent) 40%,
    transparent 58%,
    color-mix(in srgb, var(--cloth) 30%, transparent) 80%,
    color-mix(in srgb, var(--cloth) 45%, transparent) 90%,
    transparent 100%);
}

.bookCover[data-ghost="1"]::after{ border:1px dashed color-mix(in srgb, currentColor 45%, transparent); }

/* Сливочная ткань в замысле выцветала бы до невидимости: тон ей даёт золото. */
.bookCover[data-cloth="cream"][data-ghost="1"]{ color:color-mix(in srgb, var(--gold-deep) 70%, var(--muted)); }

.coverWho{
  font:600 clamp(8px,4.2cqw,11px)/1.4 var(--sans);
  letter-spacing:var(--track);
  text-transform:uppercase;
  text-align:center;
  opacity:.9;
}

/* Жанр мелким прописным у нижнего края — там, где на переплёте стоит
   издательство. */
.coverKind{
  align-self:end;
  text-align:center;
  font:600 clamp(7px,3.8cqw,10px)/1.4 var(--sans);
  letter-spacing:var(--track);
  text-transform:uppercase;
  color:color-mix(in srgb, currentColor 62%, transparent);
}

/* Название стоит в верхней трети переплёта, как на настоящей книге: по
   центру крышки его не набирают — там пусто, и от этого обложка выглядит
   макетом. Отступ и кегль заданы долями ширины обложки, поэтому набор
   одинаково стоит и на полке, и в догадке. */
.coverName{
  align-self:start;
  margin-top:17%;
  text-align:center;
  font:400 clamp(14px, 10.5cqw, 28px)/1.22 var(--serif);
  letter-spacing:.02em;
  text-wrap:balance;
  overflow-wrap:anywhere;
}

/* Волосяная черта под названием — тиснением, как на переплёте. */
.coverRule{
  width:26%;
  height:1px;
  margin:5% auto 0;
  background:color-mix(in srgb, currentColor 60%, transparent);
}

/* Пустая обложка «начать книгу». */
.coverNew{
  grid-row:span 2;
  aspect-ratio:2/3;
  align-self:start;
  border:1px dashed var(--line-2);
  border-radius:var(--r2);
  display:grid;
  place-content:center;
  gap:var(--s3);
  color:var(--muted);
  text-align:center;
  text-decoration:none;
  transition:border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}

.coverNew:hover,.coverNew:focus-visible{ border-color:var(--accent); color:var(--accent); outline:0; }

.coverPlus{ font:300 44px/1 var(--serif); }

.coverNewWord{ font:600 var(--t-cap)/1.4 var(--sans); letter-spacing:var(--track); text-transform:uppercase; }

.bookMeta{ color:var(--muted); font:400 var(--t-sm)/1.5 var(--serif); font-style:italic; }

/* Под обложкой одно действие и настройки. Ярлыков состояния здесь больше нет:
   что с книгой можно сделать, говорит сама клавиша, а чего нельзя — её
   погасший вид и подсказка при наведении. Три надписи под каждой обложкой
   («мемуары · от первого лица», «материала пока мало», «править книгу»)
   складывались в мелкий шрифтовой шум, из-за которого полка переставала быть
   полкой. */
.bookActs{
  display:flex;
  align-items:center;
  align-self:start;
  gap:var(--s3) var(--s4);
  flex-wrap:wrap;
}

/* Обложка — ссылка: книгу открывают, нажав на книгу, а не на подпись под
   ней. Поднимается она на те же три пикселя, что и всё живое в теме. */
.coverLink{
  display:block;
  min-width:0;
  text-decoration:none;
  color:inherit;
  border-radius:var(--r2);
}

.coverLink:hover .bookCover{ transform:translateY(-3px); box-shadow:0 2px 4px rgba(0,0,0,.26), 0 20px 38px rgba(0,0,0,.20); }

.coverLink:focus-visible{ outline:2px solid var(--accent); outline-offset:6px; }

.bookCover{ transition:transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }

/* Настройки книги: то, что раньше висело на полке мелкими ссылками. */
.bookSet{ display:grid; gap:var(--s4); margin-top:var(--s6); border-top:1px solid var(--line); padding-top:var(--s5); }

.bookActs form{ margin:0; display:inline-flex; }

/* Настройки книги — тихая клавиша со значком. «Править книгу» не объясняло,
   что там правится: слова книги пишет редакция, а человек задаёт название,
   голос, жанр и объём. */
.bookGear{
  display:inline-flex;
  align-items:center;
  gap:var(--s2);
  border:0;
  background:none;
  cursor:pointer;
  padding:8px 0;
  min-height:34px;
  color:var(--muted);
  font:600 var(--t-cap)/1 var(--sans);
  letter-spacing:var(--track);
  text-transform:uppercase;
  text-decoration:none;
  transition:color var(--dur) var(--ease);
}

.bookGear svg{ display:block; flex:none; }

.bookGear:hover{ color:var(--ink); }

.bookGear:focus-visible{ outline:2px solid var(--accent); outline-offset:3px; }

/* В узкой колонке полки от настроек остаётся один значок: со словом строка
   ломалась пополам, и клавиша уезжала под обложку соседа. */
.shelf .bookGear span{ position:absolute; width:1px; height:1px; overflow:hidden; clip-path:inset(50%); }

.shelf .bookGear{ padding:8px; }

.shelf .btn{ padding-inline:var(--s4); }

/* В колонке шириной с ладонь клавиша переносит свои слова сама, а значок
   настроек остаётся с ней на одной строке: иначе он висел отдельной строкой
   под каждой обложкой. Правило стоит после базового, потому что побеждает их
   порядок, а не экран. */
@media (max-width:560px){
  .shelf .bookActs{ gap:var(--s2); flex-wrap:nowrap; }
  .shelf .btn{ white-space:normal; text-wrap:balance; min-width:0; padding-inline:var(--s3); }
}


/* Чтение книги: текст как в книге, основания — под абзацем, не вместо него. */
.bookText{ display:grid; gap:var(--s7); max-width:var(--measure); }

.bookChapter{ display:grid; gap:var(--s4); }

.bookChapterTitle{ display:grid; gap:var(--s2); font:400 var(--t-h2)/1.3 var(--serif); }

.bookParagraph{ display:grid; gap:var(--s2); }

.bookParagraph .trace{ font-size:var(--t-sm); }


/* «Источник» на планке — словом, не знаком: общепринятого значка у
   источника нет, а слово короткое. Начертание и состояния — как у клавиш
   ряда. */
.toolWord{
  flex:none;
  min-height:var(--tap);
  border:0;
  border-radius:var(--r2);
  background:none;
  color:var(--ink-2);
  font:600 var(--t-cap)/1 var(--sans);
  letter-spacing:var(--track);
  text-transform:uppercase;
  padding:0 var(--s3);
  cursor:pointer;
  transition:background var(--dur) var(--ease), color var(--dur) var(--ease);
}

.toolWord:hover{ background:var(--sheet-2); color:var(--ink); }

.toolWord[data-on="1"]{ background:var(--sheet-2); color:var(--ink); box-shadow:inset 0 0 0 1px var(--line-2); }

.toolWord[disabled]{ opacity:.26; pointer-events:none; }

/* Выбранный абзац книги — редактируется на месте, тем же чтением.

   Поле невидимое, как ответ в «Истории»: ни фона, ни рамки, ни сдвига — при
   щелчке появляется только каретка. Подсветка выбранного абзаца отсюда ушла:
   она говорила то же, что и каретка, но громче, и книга под ней переставала
   читаться книгой. Что правка открыта, видно по планке знаков в шапке — там
   же, где это видно на всех остальных экранах. */
.bookParagraph{ position:relative; }

.bookParagraph [data-para-text]{ caret-color:var(--accent); cursor:text; }

.bookParagraph [data-para-text]:focus-visible{ outline:0; }

/* Правка человека — тонкая черта у поля, а не ярлык в потоке. */
.bookParagraph[data-edited="1"]::before{
  content:"";
  position:absolute;
  left:calc(-1*var(--s3));
  top:var(--s1);
  bottom:var(--s1);
  width:2px;
  background:var(--line-2);
}

/* Поиск по книге — то же приглушение непопавшего, что в «Истории». */
[data-book][data-finding="1"] .bookParagraph[data-match="0"]{ opacity:.32; }

[data-book][data-fallback="1"] .bookParagraph[data-match="1"]{ box-shadow:-12px 0 0 -11px var(--line-2); }

[data-book][data-fallback="1"] .bookParagraph[data-here="1"]{ box-shadow:-12px 0 0 -11px var(--ink); }

/* Основания выбранного абзаца — под ним, тем же начертанием, что «Источник»
   на «Фактах». Раскрываются с планки. */
.paraSources{ display:none; }

.paraSources[data-open="1"]{ display:grid; gap:var(--s3); margin-top:var(--s3); border-left:2px solid var(--line); padding-left:var(--s4); }

/* Выбор внутри формы: метка-кикер над радиокнопками. Не `.field` нарочно:
   тот растягивает всякий ввод на всю ширину строки, и кружок радиокнопки
   становился блюдцем во весь столбец. */
.pick{ display:grid; gap:var(--s2); border:0; padding:0; margin:0; min-width:0; }

/* `legend` живёт вне сетки `fieldset` — шаг до кнопок ему нужен свой. */
.pick legend{ padding:0; margin-bottom:var(--s2); }

.choice label{
  display:flex;
  align-items:baseline;
  gap:var(--s3);
  font:400 var(--t-body)/1.5 var(--serif);
  cursor:pointer;
}

.choice input{ accent-color:var(--accent); }

.formActs{ display:flex; align-items:center; gap:var(--s5); flex-wrap:wrap; }

/* Ссылка в ряду действий стоит на одной линии с клавишами. У строчной ссылки
   своя высота строки, и без этого «Другой адрес» висел выше «Войти» на
   полсантиметра — ряд читался как две разные полки. */
.formActs .quiet{ display:inline-flex; align-items:center; }

/* Тихая дорога у правого края набора: «Нет доступа к почте» на входе. Это
   выход для того, у кого не вышло, а не второе предложение всем подряд, —
   поэтому он у дальнего края и той же меры, что форма, а не во всю ширину
   листа. */
.formAside{
  display:flex;
  justify-content:flex-end;
  max-width:34rem;
  margin-top:var(--s6);
}

/* Поле кода из письма. Та же строка с чертой, что и все поля формы, только
   крупнее и с разрядкой: цифры сверяют с письмом взглядом, а не пальцем по
   экрану. Ширина в знаках, а не в пикселях, — поле ровно под шесть цифр и
   ни на знак шире, чтобы было видно, сколько их ждут.

   Цвета взяты токенами, как и везде: ночью поле красится само вместе с темой,
   и второго правила под неё не заводится. */
.codeField{
  width:100%;
  max-width:9ch;
  min-height:var(--tap);
  border:0;
  border-bottom:1px solid var(--line-2);
  padding:0 0 var(--s2);
  background:none;
  color:var(--ink);
  font:400 var(--t-h1)/1.2 var(--serif);
  font-variant-numeric:tabular-nums;
  letter-spacing:.18em;
}

.codeField:focus{ border-bottom-color:var(--accent); outline:0; }

.alert{
  max-width:var(--measure);
  margin-bottom:var(--s6);
  padding:var(--s3) 0 var(--s3) var(--s4);
  border-left:2px solid var(--accent);
  color:var(--ink);
  font:400 var(--t-body)/1.6 var(--serif);
}

/* Выбор истории на «Истории»: ряд названий, текущее — чернилами. Не список и
   не выпадающее меню: историй у человека единицы, а название длинное, и ряд,
   который переносится, читается быстрее, чем меню, которое надо открыть.
   Появляется он только от второй истории. */
.picker{
  display:flex;
  flex-wrap:wrap;
  align-items:baseline;
  gap:var(--s2) var(--s5);
  max-width:var(--measure);
  margin:calc(var(--s7) * -1 + var(--s4)) 0 var(--s6);
  padding-bottom:var(--s3);
  border-bottom:1px solid var(--line);
}

.pickerOne{
  display:inline-flex;
  align-items:center;
  min-height:var(--tap);
  padding:0;
  border:0;
  background:none;
  color:var(--muted);
  font-family:var(--sans);
  font-size:var(--t-cap);
  font-weight:600;
  letter-spacing:.08em;
  text-transform:uppercase;
  text-align:left;
  cursor:pointer;
  transition:color var(--dur) var(--ease);
}

.pickerOne[data-on="1"]{ color:var(--ink); cursor:default; }

.pickerOne:hover{ color:var(--ink); }

/* ── Кабинет: истории и два способа завести ещё одну ── */

/* Зона с заголовком и линией под ним. Внешних отступов сверху у зоны нет:
   расстояние между зонами задаёт нижний отступ предыдущей. */
.zone{ max-width:var(--measure); padding-bottom:var(--s7); }

.zoneHead{
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:var(--s4);
  padding-bottom:var(--s3);
  border-bottom:1px solid var(--line-2);
  margin-bottom:var(--s5);
}

.zoneTitle{ font:400 var(--t-h2)/1.25 var(--serif); letter-spacing:-.015em; }

/* Два входа, один под другим. В два столбца они читались как выбор из меню, а
   это последовательность: сначала обычный путь, потом путь для тех, у кого
   запись уже есть. */
.ways{ display:grid; gap:var(--s5); }

.way{
  display:flex;
  flex-direction:column;
  gap:var(--s4);
  padding:var(--s5);
  border:1px solid var(--line-2);
  border-radius:var(--r2);
  background:var(--card);
}

.wayHead{ display:grid; gap:var(--s2); }
.wayTitle{ font:400 var(--t-h2)/1.25 var(--serif); letter-spacing:-.015em; }
.wayText{ font:400 var(--t-body)/1.6 var(--serif); color:var(--ink-2); }

/* Ширина, которую даёт колонка во всю меру, уходит внутрь карточки: образец и
   выбор файла встают рядом, а не башней. */
.wayPair{ display:grid; gap:var(--s4); }
@media (min-width:760px){ .wayPair{ grid-template-columns:1fr 1fr; } }

/* Образец формата: его читают как текст и повторяют у себя, поэтому гротеск и
   сохранённые переводы строк. Переносится по словам — полоса прокрутки под
   четырьмя строками выглядит поломкой, а прокручивать в примере нечего. */
.sampleWrap{ display:grid; gap:var(--s2); align-content:start; }

.sample{
  padding:var(--s4);
  border:1px solid var(--line);
  border-radius:var(--r2);
  background:var(--sheet-2);
  color:var(--ink-2);
  font:400 var(--t-sm)/1.7 var(--sans);
  white-space:pre-wrap;
  overflow-wrap:anywhere;
}

.sample b{ color:var(--accent); font-weight:700; }

/* Выбор файла: настоящий input скрыт внутри метки, потому что стандартную
   кнопку браузера нельзя ни увеличить, ни назвать своими словами. */
.drop{
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  gap:var(--s2);
  min-height:132px;
  height:100%;
  padding:var(--s5);
  border:1px dashed var(--line-2);
  border-radius:var(--r2);
  background:var(--sheet-2);
  text-align:center;
  cursor:pointer;
  transition:border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}

.drop:hover{ border-color:var(--accent); background:var(--accent-soft); }
.drop:focus-within{ border-color:var(--accent); outline:2px solid var(--accent); outline-offset:3px; }
.drop input{ position:absolute; width:1px; height:1px; opacity:0; pointer-events:none; }

.dropTitle{
  font-family:var(--sans);
  font-size:var(--t-cap);
  font-weight:600;
  letter-spacing:.08em;
  text-transform:uppercase;
  color:var(--accent);
}

.dropNote{ font-size:var(--t-meta); color:var(--muted); }

/* Всё, что нужно для загрузки, за одной кнопкой — и пример, и сам файл.
   Родные элементы браузера: раскрытие работает с клавиатуры и без сценария. */
.more{ border-top:1px solid var(--line); padding-top:var(--s4); }

.more summary{
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:var(--s4);
  min-height:var(--tap);
  cursor:pointer;
  list-style:none;
}

.more summary::-webkit-details-marker{ display:none; }

.moreAct{
  font-family:var(--sans);
  font-size:var(--t-cap);
  font-weight:600;
  letter-spacing:.08em;
  text-transform:uppercase;
  color:var(--accent);
}

.moreAct::after{ content:attr(data-shut); }
.more[open] .moreAct::after{ content:attr(data-open); }

.moreBody{ display:grid; gap:var(--s4); padding-top:var(--s4); }

/* Карточка истории. Обычный поток, а не сетка: «Продолжить» обтекает, чтобы
   «Настроить» встало с ним в один ряд, а панель настроек — во всю ширину
   карточки. Сетка этого не даёт: панель лежит внутри `details`, и в ячейке
   рядом с кнопкой ей досталась бы ширина этой ячейки. */
.story{
  display:flow-root;
  padding:var(--s5) 0;
  border-bottom:1px solid var(--line);
  max-width:var(--measure);
}

.story > * + *{ margin-top:var(--s3); }

/* Первая история начинается сразу под заголовком экрана: собственный отступ
   ей не нужен. А вот после ссылки для рассказчика — нужен, иначе имя матери
   прилипает к плашке со ссылкой. */
.story:first-of-type{ padding-top:0; }

.form + .zone{ margin-top:var(--s6); }

.storyTitle{ font:400 var(--t-ask)/1.3 var(--serif); }

.storyMeta{ display:flex; flex-wrap:wrap; align-items:baseline; gap:var(--s2) var(--s4); }

.storyGo{ float:left; margin-right:var(--s3); }

/* «Настроить» — такая же клавиша, что и «Продолжить», но она же и крышка:
   раскрывается ею тот же `details`, в котором лежат настройки. Раскрытая
   заливается своим же цветом навылет — так видно, что панель ниже принадлежит
   ей, а не карточке. */
.storySet summary{
  display:inline-flex;
  align-items:center;
  width:max-content;
  list-style:none;
}

.storySet summary::-webkit-details-marker{ display:none; }

.storySet[open] summary{ background:var(--accent-soft); }

/* Панель обязана начинаться под обеими клавишами, а не рядом с обтекающей. */
.storyPanel{
  clear:both;
  padding-top:var(--s4);
}

/* Настройки истории. Шаг между вопросами вдвое больше шага между ответами:
   пока он был один на всё, следующий вопрос стоял под чертой поля так же
   близко, как свой вопрос над ним, и форма читалась одной кучей. */
.narrator{ gap:var(--s5); }

/* Вопрос и ответ. Черты между группами нет нарочно: держит группу отступ, а
   не линейка, и форма из четырёх вопросов не выглядит бланком из четырёх
   разделов. */
.setAsk{ display:grid; gap:var(--s2); border:0; }

/* Вопрос звучит голосом интервью — той же антиквой, что и всё, что здесь
   спрашивают. Гротеск прописными остаётся служебным меткам: «В КАКОМ РОДЕ?»
   читается графой анкеты, а не вопросом к человеку. */
.setQ{ font:400 var(--t-body)/1.5 var(--serif); color:var(--ink); }

/* `legend` живёт вне сетки `fieldset` — своим шагом, а не общим `gap`, иначе
   вопрос-легенда прилипает к первой кнопке, а вопрос-подпись не прилипает. */
legend.setQ{ padding:0; margin-bottom:var(--s2); }

/* Тот самый отступ: ответ живёт под своим вопросом и виден им. */
.setA{ display:grid; gap:var(--s3); padding-left:var(--s4); }

/* О ком история — сказано, а не спрошено: у начатой истории это не меняется, и
   набор вопросов под этой строкой выбран на сервере. Прежде здесь стоял
   переключатель, а лишний набор прятал `:has()` по отмеченной кнопке; кнопки
   больше нет, и прятать нечего.

   Строка звучит той же антиквой, что и вопросы под ней, — это ответ на первый
   из них, только отвечать на него уже нечего. Пояснение под ним мельче и
   служебное: почему выбора нет. */
.setSaid{ display:grid; gap:var(--s2); font:400 var(--t-body)/1.5 var(--serif); color:var(--ink); }

/* Начало истории спрашивает то же, что настройки, и тем же шагом: вопрос,
   ответ под ним, между вопросами шаг вдвое больше. */
.start{ gap:var(--s5); }

/* Вопрос меняется по выбранному рассказчику: «как зовут рассказчика», пока
   отвечать будет другой, — и «как вас зовут», когда рассказывает сам
   заполняющий. Общая формулировка стоит первой и остаётся, пока не выбрано
   ничего и там, где `:has()` не работает: она верна во всех четырёх случаях,
   тогда как «вас» — только в двух.

   Второй человек появляется единственным выбором — «я рассказываю о близком».
   До него полей о нём нет: спрашивать имя и род того, кого в истории не будет,
   значит спрашивать зря. */
/* Вопросы о близком показываются, только когда история о нём. */
.start .askHero{ display:none; }
.start:has(input[name="story_mode"][value="ABOUT"]:checked) .askHero{ display:grid; }

/* Поля новой истории не нужны, когда книга дозагружается в уже начатую. */
.way:has(select[name="import_into"] option:checked:not([value=""])) .importNew{ display:none; }

.importNew{ display:grid; gap:var(--s4); }

/* Пояснение под полем — часть поля, а не следующая надпись: сетка `.field`
   ставит его тем же шагом, что и подпись сверху, и получается подпись, поле,
   подпись. Здесь оно прижимается к своему полю — в ответе настроек тем же
   способом, но от его собственного шага. */
.field .note{ margin-top:calc(var(--s2) * -1); }
.setA .note{ margin-top:calc(var(--s2) - var(--s3)); }

/* ── Установка: страница без темы, но с той же типографикой ── */
.installer{
  max-width:var(--measure);
  margin:0 auto;
  padding:var(--s7) var(--s5);
  background:var(--bg);
  min-height:100dvh;
}

.installer h1{ font:400 var(--t-h1)/1.16 var(--serif); margin-bottom:var(--s4); }

.installer h2{ font:400 var(--t-h2)/1.25 var(--serif); margin:var(--s7) 0 var(--s3); }

.installer p, .installer li{ margin-bottom:var(--s3); font:400 var(--t-body)/1.6 var(--serif); }

.installer .diagnostics{ list-style:none; }

.installer li[data-status="error"]{ color:var(--accent); }

.installer li[data-status="warning"]{ color:var(--flag); }

.installer label{ display:grid; gap:var(--s2); margin-bottom:var(--s4); max-width:34rem; }

.installer input{
  min-height:var(--tap);
  border:0;
  border-bottom:1px solid var(--line-2);
  padding:0 0 var(--s2);
  background:none;
  color:var(--ink);
  font:400 var(--t-body)/1.5 var(--sans);
}

.installer button{
  min-height:var(--tap);
  padding:0 var(--s5);
  border:1px solid var(--accent);
  border-radius:var(--r2);
  background:var(--accent);
  color:var(--on-accent);
  font-size:var(--t-cap);
  font-weight:600;
  letter-spacing:.08em;
  text-transform:uppercase;
  cursor:pointer;
}

.installer code{ font-size:var(--t-sm); }

.installer h3{ font:400 var(--t-ask)/1.3 var(--serif); margin:var(--s5) 0 var(--s3); }

.installer select{
  min-height:var(--tap);
  border:0;
  border-bottom:1px solid var(--line-2);
  padding:0 0 var(--s2);
  background:none;
  color:var(--ink);
  font:400 var(--t-body)/1.5 var(--sans);
}

/* Четыре шага установки: где стоим сейчас и что уже позади. Список, а не
   мастер со стрелками, — шаг определяется состоянием сервера, и обновление
   страницы показывает его заново. */
.installer .installSteps{
  display:flex;
  flex-wrap:wrap;
  gap:var(--s2) var(--s5);
  margin:var(--s5) 0 var(--s7);
  padding:0;
  list-style:none;
  counter-reset:installStep;
}

.installer .installSteps li{
  margin:0;
  font:400 var(--t-cap)/1.4 var(--sans);
  letter-spacing:.06em;
  text-transform:uppercase;
  color:var(--ink-2);
  counter-increment:installStep;
}

.installer .installSteps li::before{ content:counter(installStep) ". "; }

.installer .installSteps li[data-status="done"]{ color:var(--ink); }

.installer .installSteps li[data-status="now"]{ color:var(--accent); font-weight:600; }


/* ── Аккаунт на телефоне ──
   Тон и выход стояли в шапке, а шапки на телефоне больше нет. Здесь они
   строками, а не клавишами в углу: на этой странице всё остальное — тоже
   строки и поля. Выше 1200px их не видно: там те же две вещи стоят в шапке,
   и две одинаковые на одном экране спорят, какая настоящая. */
.slab{ display:flex; flex-direction:column; border-top:1px solid var(--line); }

.slab a,
.slab button{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:var(--s3);
  width:100%;
  min-height:56px;
  padding:0;
  border:0;
  border-bottom:1px solid var(--line);
  background:none;
  color:var(--ink);
  font-family:var(--sans);
  font-size:var(--t-body);
  text-align:left;
  text-decoration:none;
  cursor:pointer;
  transition:color var(--dur) var(--ease);
}

.slab a:hover,
.slab button:hover{ color:var(--accent); }

.slab form{ display:contents; }

@media (min-width:1200px){ .phoneOnly{ display:none; } }

/* ── Комментарии к тексту истории: заметки на полях ответа. ──
   Подсветка рисуется поверх ответа отдельным слоем, а не тегами внутри него:
   ответ — contenteditable, его innerHTML сохраняется редакцией, и любая метка
   внутри уехала бы в первоисточник. Карточки живут в правой полосе, встают на
   высоту своей подсветки и едут вместе с текстом; где полоса не помещается,
   комментарий открывается нижней плашкой. */
.answerList{ position:relative; }

.turnSaid{ position:relative; z-index:1; }

.commentGlow{ position:absolute; inset:0; pointer-events:none; z-index:0; }

.commentGlow i{
  position:absolute;
  background:var(--flag-soft);
  border-radius:var(--r1);
}

.commentGlow i[data-on="1"]{ background:color-mix(in srgb, var(--flag) 30%, transparent); }

.commentGlow i[data-draft]{ background:var(--accent-soft); }

/* Кнопка над выделением. */
.commentAdd{
  position:absolute;
  z-index:25;
  display:none;
  align-items:center;
  gap:var(--s2);
  min-height:var(--tap);
  padding:0 var(--s4);
  border:1px solid var(--line-2);
  border-radius:var(--r2);
  background:var(--card);
  color:var(--ink);
  font:500 var(--t-sm)/1 var(--sans);
  cursor:pointer;
  white-space:nowrap;
  transform:translateX(-50%);
  box-shadow:0 2px 10px color-mix(in srgb, var(--ink) 18%, transparent);
}

.commentAdd[data-show="1"]{ display:inline-flex; }

.commentAdd svg{ flex:0 0 auto; }

.commentRail{
  position:absolute;
  top:0;
  left:calc(var(--measure) + var(--s6));
  width:260px;
  display:none;
}

.answerList[data-comments="rail"] .commentRail{ display:block; }

.comment{
  position:absolute;
  left:0;
  width:100%;
  background:var(--card);
  border:1px solid var(--line);
  border-radius:var(--r2);
  padding:var(--s3) var(--s4);
  transition:top var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

.comment[data-on="1"]{ box-shadow:0 2px 12px color-mix(in srgb, var(--ink) 12%, transparent); }

.commentHead{
  display:flex;
  gap:var(--s2);
  align-items:center;
  font:400 var(--t-meta)/1.3 var(--sans);
  color:var(--muted);
  margin-bottom:var(--s2);
}

.commentAuthor{
  font-weight:600;
  color:var(--ink-2);
  max-width:12ch;
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}

/* Удаление — единственная кнопка карточки; зона нажатия 44px,
   отрицательные поля не дают ей раздуть шапку. */
.commentDel{
  flex:0 0 auto;
  width:var(--tap);
  height:var(--tap);
  margin:calc(-1 * var(--s3)) calc(-1 * var(--s3)) calc(-1 * var(--s3)) auto;
  border:0;
  background:none;
  color:var(--muted);
  cursor:pointer;
  display:inline-flex;
  align-items:center;
  justify-content:center;
}

.commentDel:hover{ color:var(--alarm); }

/* Подтверждение вместо корзины: тот же угол шапки, красным. */
.commentAsk{
  flex:0 0 auto;
  min-height:var(--tap);
  padding:0 var(--s2);
  margin:calc(-1 * var(--s3)) calc(-1 * var(--s3)) calc(-1 * var(--s3)) auto;
  border:0;
  background:none;
  color:var(--alarm);
  font:600 var(--t-meta)/1 var(--sans);
  cursor:pointer;
  white-space:nowrap;
}

/* Текст комментария правится на месте, без формы. */
.commentText{
  font:400 var(--t-sm)/1.5 var(--sans);
  color:var(--ink);
  white-space:pre-wrap;
  overflow-wrap:anywhere;
  border-radius:var(--r1);
  outline:0;
  cursor:text;
}

.commentText[contenteditable="false"]{ cursor:auto; }

.commentText:focus{
  background:var(--sheet-2);
  box-shadow:0 0 0 var(--s1) var(--sheet-2);
}

/* Пустой черновик показывает подсказку вместо текста — и тем же бледным
   тоном, что и примеры в полях: это поле тоже пустое, хотя поле здесь своё. */
.commentText:empty::before{
  content:attr(data-ph);
  color:var(--hint);
}

/* Цитата — след к месту в тексте; без места остаётся только след. */
.commentQuote{
  display:block;
  font:italic 400 var(--t-sm)/1.4 var(--serif);
  color:var(--muted);
  border-left:2px solid var(--flag);
  padding-left:var(--s2);
  margin-bottom:var(--s2);
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}

.commentQuote[data-lost="1"]{ text-decoration:line-through; border-left-color:var(--line-2); }

/* Узко для полосы — комментарий открывается плашкой снизу. */
.commentVeil{
  position:fixed;
  inset:0;
  z-index:70;
  background:color-mix(in srgb, var(--ink) 40%, transparent);
  border:0;
  cursor:pointer;
}

.commentSheet{
  position:fixed;
  left:0;
  right:0;
  bottom:0;
  z-index:71;
  margin:0 auto;
  max-width:640px;
  max-height:70dvh;
  overflow-y:auto;
  background:var(--card);
  border:1px solid var(--line);
  border-bottom:0;
  border-radius:var(--r2) var(--r2) 0 0;
  padding:var(--s4) var(--s4) calc(var(--s4) + env(safe-area-inset-bottom));
}

.commentSheet .comment{ position:static; }

.commentGrip{
  width:var(--s6);
  height:var(--s1);
  border-radius:var(--r1);
  background:var(--line-2);
  margin:0 auto var(--s3);
}

/* Длинный комментарий свёрнут до четырёх строк — «весь» раскрывает. */
.commentText[data-clamp="1"]{
  display:-webkit-box;
  -webkit-box-orient:vertical;
  -webkit-line-clamp:4;
  overflow:hidden;
}

.commentMore{
  min-height:var(--tap);
  margin:calc(-1 * var(--s2)) 0 calc(-1 * var(--s3));
  padding:0;
  border:0;
  background:none;
  color:var(--accent);
  font:600 var(--t-meta)/1 var(--sans);
  cursor:pointer;
}

.commentHead span{ white-space:nowrap; }

/* ── Навигация по длинному листу: листы «Истории», главы книги. ──
   Лента истории идёт в порядке записи и темы в ней вперемешку, глав нет —
   единица навигации там лист: десять ответов, подписанные диапазоном номеров
   и месяцем записи. В собранной книге единица — глава, подписанная названием.
   Вид, разметка и сценарий у них общие: вопрос у человека один и тот же — где
   я сейчас и как попасть в другое место.
   Прототип: docs/prototypes/life-history-pages.html.
   Терракота у текущего листа — то же употребление, что у активного пункта
   дока и меню: «где ты», а не след к первоисточнику. */

/* Переход к листу останавливается под липкой шапкой, а не за ней. */
.turn{ scroll-margin-top:calc(var(--tap) + var(--s6)); }

/* Рейка у правого края — широкий экран. Ползунок, а не только кнопки:
   вся зона слева от штрихов ловит и клик, и ведение.
   Правый край общий с шапкой: полоска стоит на той же вертикали, что правый
   бок кнопки темы над ней, а штрихи растут от полоски влево. Раньше полоска
   висела посередине штрихов, и правого края у рейки не было вовсе. */
.pagesRail{
  position:fixed;
  right:var(--edge);
  top:0;
  bottom:0;
  z-index:25;
  padding:var(--s3) 0 var(--s3) var(--s5);
  touch-action:none;
  user-select:none;
  cursor:pointer;
  display:none;
  flex-direction:column;
  justify-content:center;
  align-items:flex-end;
}

/* Окно рейки: то, сквозь что видно полосу штрихов. Пока единиц немного, полоса
   короче окна, ничего не едет, и рейка выглядит как прежде. Когда полоса
   длиннее окна (`data-band="1"`), окно её режет, а полоса едет — иначе на
   рассказе в сотню листов рейка уходила за оба края экрана: верхних листов не
   видно вовсе, нижние обрезаны, и вести по ней некуда, конец рейки за
   пределами окна.

   Высоту окна ставит `pages.js` по свободному месту между шапкой и
   диктофоном: рейка стоит поверх листа, а не рядом с ним, и место у неё
   ровно то, которое не заняли они.

   Края растворяются маской: обрубленный штрих читается как конец списка, а
   список не кончился. Волосяная полоска лежит внутри окна и потому гаснет
   вместе со штрихами. */
.pagesRailView{
  position:relative;
  width:100%;
  min-height:0;
}

.pagesRail[data-band="1"] .pagesRailView{
  overflow:hidden;
  -webkit-mask-image:linear-gradient(to bottom,transparent 0,#000 28px,#000 calc(100% - 28px),transparent 100%);
  mask-image:linear-gradient(to bottom,transparent 0,#000 28px,#000 calc(100% - 28px),transparent 100%);
}

.pagesRailLine{
  position:absolute;
  right:0;
  top:0;
  bottom:0;
  width:1px;
  background:var(--line);
}

.pagesRailList{
  list-style:none;
  margin:0;
  padding:0;
  display:flex;
  flex-direction:column;
  gap:18px;
  position:relative;
  will-change:transform;
  transition:transform var(--dur) var(--ease);
}

/* Пока полосу ведут рукой, она обязана быть под пальцем, а не догонять его
   анимацией, которую каждое движение обрывает заново. */
.pagesRail[data-drag="1"] .pagesRailList{ transition:none; }

/* Возврат к месту чтения. Стоит под окном и показывается, только когда полосу
   увели и текущая единица вышла из окна: иначе человек листает список и не
   знает, где он сам. Стрелка смотрит в ту сторону, где осталось чтение.
   Место под клавишу занято всегда — появляясь, она не должна сдвигать
   штрихи на свою высоту. */
.pagesHome{
  flex:0 0 auto;
  margin-top:var(--s2);
  display:flex;
  align-items:center;
  gap:var(--s1);
  padding:var(--s1) var(--s2);
  border:0;
  border-radius:var(--r1);
  background:var(--bg);
  color:var(--muted);
  font:500 var(--t-cap)/1 var(--sans);
  letter-spacing:var(--track);
  text-transform:uppercase;
  cursor:pointer;
  visibility:hidden;
}

.pagesHome::before{ content:'\2191'; }

.pagesRail[data-away="1"][data-dir="down"] .pagesHome::before{ content:'\2193'; }

.pagesRail[data-away="1"] .pagesHome{ visibility:visible; }

.pagesHome:hover{ color:var(--ink); }

.pagesStop{ position:relative; }

.pagesRail .pagesStop{ display:flex; justify-content:flex-end; }

.pagesTick{
  display:flex;
  align-items:center;
  gap:var(--s3);
  min-height:12px;
  padding:2px 0;
  border:0;
  background:none;
  cursor:pointer;
  pointer-events:none;
}

.pagesMark{
  width:12px;
  height:1px;
  background:var(--line-2);
  transition:width var(--dur) var(--ease), background var(--dur) var(--ease);
}

/* Подпись штриха: у листа истории она короткая, у главы книги — название,
   которое пишет модель. Ширина ограничена, хвост срезается многоточием, а под
   подписью лежит плита цвета листа: жёлоба между мерой набора и рейкой на
   1200 пикселях нет вовсе, и без плиты подпись читалась поверх строки
   ответа — это и было видно на снимке «Истории» до того, как книга
   потребовала той же рейки себе. */
.pagesLabel{
  font:500 var(--t-cap)/1 var(--sans);
  letter-spacing:var(--track);
  color:var(--muted);
  white-space:nowrap;
  max-width:min(34ch,22vw);
  overflow:hidden;
  text-overflow:ellipsis;
  padding:var(--s1) var(--s2);
  border-radius:var(--r1);
  background:var(--bg);
  font-variant-numeric:tabular-nums;
  opacity:0;
  transform:translateX(4px);
  transition:opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.pagesLabel b{ font-weight:500; color:var(--ink-2); }

.pagesStop:hover .pagesLabel{ opacity:1; transform:none; }

.pagesStop[data-on="1"] .pagesMark{ width:20px; background:var(--ink); }

.pagesStop[data-on="1"] .pagesLabel{ opacity:1; transform:none; }

/* Пока по рейке ведут, подписи видны у всех штрихов: это листание,
   и человек должен видеть, мимо чего идёт. */
.pagesRail[data-scrub="1"] .pagesLabel{ opacity:1; transform:none; }

/* Строка-адрес над доком и шторка — узкий экран. Диктофон (z-index 30)
   ложится поверх обоих: пока идёт запись, шапка листов не нужна. */
.pagesBar{
  display:none;
  position:fixed;
  left:0;
  right:0;
  bottom:calc(var(--tap) + env(safe-area-inset-bottom));
  z-index:22;
  width:100%;
  min-height:var(--tap);
  padding:0 var(--s6);
  align-items:center;
  justify-content:space-between;
  gap:var(--s4);
  border:0;
  border-top:1px solid var(--line);
  background:color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter:blur(6px);
  font:500 var(--t-cap)/1 var(--sans);
  letter-spacing:var(--track);
  text-transform:uppercase;
  color:var(--muted);
  font-variant-numeric:tabular-nums;
  text-align:left;
  cursor:pointer;
}

.pagesBar b{ font-weight:500; color:var(--ink-2); }

.pagesVeil{
  display:none;
  position:fixed;
  inset:0;
  z-index:24;
  background:color-mix(in srgb, var(--desk) 62%, transparent);
  opacity:0;
  transition:opacity var(--dur) var(--ease);
}

.pagesSheet{
  position:fixed;
  left:0;
  right:0;
  bottom:calc(var(--tap) + env(safe-area-inset-bottom));
  z-index:26;
  height:min(60vh,560px);
  transform:translateY(110%);
  transition:transform var(--dur) var(--ease);
  background:var(--card);
  border-top:1px solid var(--line-2);
  box-shadow:0 -18px 48px color-mix(in srgb, var(--ink) 16%, transparent);
  display:none;
  flex-direction:column;
}

body[data-pages-open="1"] .pagesVeil{ display:block; opacity:1; }

body[data-pages-open="1"] .pagesSheet{ transform:none; }

/* Список в языке меню: гротеск, приглушённый тон, текущий — чернилами и
   терракотовой полосой, прижатой к левому краю шторки. Текст строк стоит
   на той же вертикали, что текст ленты. Ни линеек, ни рамок; отступ под
   полосу общий, чтобы при смене текущего ничего не прыгало. */
.pagesSheetList{
  flex:1;
  overflow-y:auto;
  list-style:none;
  margin:0;
  padding:var(--s4) 0 var(--s5);
  overscroll-behavior:contain;
}

.pagesItem{ position:relative; }

.pagesGo{
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:var(--s4);
  width:100%;
  min-height:var(--tap);
  padding:var(--s3) var(--s6);
  border:0;
  background:none;
  text-align:left;
  font:400 var(--t-sm)/1.2 var(--sans);
  color:var(--muted);
  transition:color var(--dur) var(--ease);
  font-variant-numeric:tabular-nums;
  cursor:pointer;
}

.pagesGo:hover{ color:var(--ink); }

.pagesDate{
  flex:none;
  font:500 var(--t-meta)/1 var(--sans);
  letter-spacing:var(--track);
  color:var(--muted);
  white-space:nowrap;
}

.pagesItem[data-on="1"] .pagesGo{ color:var(--ink); }

.pagesItem[data-on="1"]::before{
  content:'';
  position:absolute;
  left:0;
  top:0;
  bottom:0;
  width:3px;
  background:var(--accent);
}

@media (min-width:1200px){
  .pagesRail{ display:flex; }
}

@media (max-width:1199.98px){
  .pagesBar{ display:flex; }
  .pagesSheet{ display:flex; }
}

@media (prefers-reduced-motion:reduce){
  .pagesSheet,
  .pagesVeil{ transition:none; }
}

/* Линия времени: жизнь одной непрерывной линией под решёткой карты.

   Год стоит на своём месте, а не через равный промежуток от соседа: пустой
   участок — это годы, о которых не сказано ничего, и он обязан читаться как
   пустой. Отсюда вся геометрия ниже — доли от длины линии, а не колонки
   потока. Горизонтальный скролл принадлежит секции и никогда не расширяет
   документ на узком экране. */
.timelineWrap{
  margin:var(--s8) 0 0; padding-top:var(--s7); border-top:1px solid var(--line);

  /* Геометрия линии — один набор чисел, на который ссылаются и метки, и
     колонка точек, и поводок к карточке. Менять здесь, а не по месту. */
  --axis-top:92px;      /* где проходит сама линия внутри дорожки */
  --track-h:146px;      /* высота дорожки вместе с подписями десятилетий */
  /* Ширина одного года жизни. Была тринадцать пикселей — столько, чтобы жизнь
     целиком уместилась в экран, — и годы плотного десятилетия слипались в
     кляксу: круги стояли друг на друге, а нажать удавалось не тот. Теперь год
     занимает ровно ширину своей клавиши (34px): соседние круги стоят рядом, не
     наезжая, а линия выходит за края экрана и листается. */
  --year-px:34px;       /* ширина одного года жизни — она же ширина клавиши */
  /* Насколько линия вылезает за поля листа. Ровно на поле `.body`, чтобы
     прокрутка шла от края экрана до края: линия — не абзац, её незачем держать
     в мерке текста. Значение повторяет поле листа и меняется вместе с ним. */
  --bleed:var(--s5);
}

@media (min-width:1200px){
  .timelineWrap{ --bleed:var(--s7); }
}


/* Прокрутка принадлежит этой полосе: она шире листа ровно на его поля, и
   потому линия уходит под край экрана, а не обрывается в поле. Поля вернулись
   внутрь — первый и последний год стоят там же, где начинается текст. */
.timeline{ overflow-x:auto; margin-inline:calc(var(--bleed) * -1); padding-inline:var(--bleed); overscroll-behavior-inline:contain; scrollbar-width:none; -ms-overflow-style:none; -webkit-overflow-scrolling:touch; cursor:grab; touch-action:pan-x pan-y; -webkit-user-select:none; user-select:none; }
/* Выделения на линии нет: её тянут мышью, а протяжка с зажатой кнопкой — это
   ровно тот жест, которым выделяют текст, и подписи годов синели у каждого,
   кто листал. Читать здесь нечего — числа и подписи стоят в карточке под
   линией, и она выделяется как обычный текст. */
/* Полосу можно взять с клавиатуры: стрелки листают линию, как колесо и палец. */
.timeline:focus-visible{ outline:1px solid var(--accent); outline-offset:2px; border-radius:var(--r1); }

/* Дорожка держит две клавиши листания по краям линии: полоса под ними едет,
   они стоят. Линия уходит за них под край экрана — потому клавиши и стоят по
   краю листа, а не по краю экрана: у самого обреза их не нажать пальцем.

   Клавиша лежит поверх всего, что едет под ней. Слой у неё выше самого
   высокого на линии — вспышки приземления (3), охряной метки (2), штанги (1),
   — иначе круг, доехавший до края, вылезает сквозь клавишу и читается как
   часть её. Заводя на линии новый слой, поднимите и этот. */
.timelineLane{ position:relative; }
.timelineSlide{
  position:absolute; top:var(--axis-top); z-index:4;
  width:32px; height:32px; display:inline-flex; align-items:center; justify-content:center;
  padding:0; border:1px solid var(--line); border-radius:50%;
  background:var(--bg); color:var(--ink-2); cursor:pointer;
  transform:translateY(-50%);
  transition:color var(--dur) var(--ease),border-color var(--dur) var(--ease),opacity var(--dur) var(--ease);
}
.timelineSlide[data-slide="-1"]{ left:0; }
.timelineSlide[data-slide="1"]{ right:0; }
.timelineSlide:hover{ border-color:var(--accent); color:var(--accent); }
.timelineSlide:focus-visible{ outline:1px solid var(--accent); outline-offset:2px; }
/* У конца линии клавиша не пропадает, а гаснет: исчезнувшая кнопка сдвигает
   соседнюю и заставляет искать её заново. */
.timelineSlide:disabled{ opacity:.3; cursor:default; }
.timelineSlide:disabled:hover{ border-color:var(--line); color:var(--ink-2); }
.timelineSlide[hidden]{ display:none; }
.timeline::-webkit-scrollbar{ display:none; width:0; height:0; }
.timeline:active{ cursor:grabbing; }
.timelineTrack{ position:relative; height:var(--track-h); min-width:100%; width:calc(var(--years) * var(--year-px)); padding:0 34px; }
/* Внутренняя дорожка нужна затем, что доли отсчитываются от неё: 0 % — начало
   линии, 100 % — её конец, и оба стоят внутри полей, а не под обрезом. */
.timelineInner{ position:relative; height:100%; }
.timelineAxis{ position:absolute; left:0; right:0; top:var(--axis-top); height:1px; background:var(--line-2); }

.timelineStage{ position:absolute; top:0; transform:translateX(-50%); white-space:nowrap; font-size:var(--t-cap); letter-spacing:var(--track); text-transform:uppercase; color:var(--muted); }
.timelineStage[data-edge="left"],.timelineTickLabel[data-edge="left"]{ transform:translateX(0); }
.timelineStage[data-edge="right"],.timelineTickLabel[data-edge="right"]{ transform:translateX(-100%); }

.timelineTick{ position:absolute; top:var(--axis-top); width:1px; background:var(--line-2); transform:translateX(-50%); }
.timelineTick[data-kind="decade"]{ height:14px; }
.timelineTick[data-kind="half"]{ height:7px; margin-top:-3px; opacity:.7; }
.timelineTickLabel{ position:absolute; top:calc(var(--axis-top) + 28px); transform:translateX(-50%); font-size:var(--t-sm); font-variant-numeric:tabular-nums; color:var(--muted); }

.timelineYear{ position:absolute; top:0; bottom:0; width:34px; transform:translateX(-50%); padding:0; border:0; background:none; color:inherit; font:inherit; cursor:pointer; }
.timelineYear:focus-visible{ outline:1px solid var(--accent); outline-offset:2px; border-radius:var(--r1); }
.timelineMark{ position:absolute; left:50%; top:var(--axis-top); width:8px; height:8px; transform:translate(-50%,-50%); border-radius:50%; background:var(--line-2); box-shadow:0 0 0 3px var(--bg); transition:background var(--dur) var(--ease),width var(--dur) var(--ease),height var(--dur) var(--ease); }
.timelineMark[data-weight="2"]{ width:10px; height:10px; }
.timelineMark[data-weight="3"]{ width:12px; height:12px; }
.timelineMark[data-weight="4"]{ width:14px; height:14px; }
.timelineMark[data-weight="5"]{ width:17px; height:17px; }
.timelineColumn{ position:absolute; left:50%; bottom:calc(var(--track-h) - var(--axis-top) + 12px); transform:translateX(-50%); display:flex; flex-direction:column-reverse; align-items:center; gap:4px; }
.timelineColumn i{ display:block; width:5px; height:5px; border-radius:50%; background:var(--line-2); }
.timelineColumn b{ font-style:normal; font-weight:400; font-size:var(--t-cap); line-height:1; color:var(--muted); }
.timelineYear:hover .timelineMark,.timelineYear:hover .timelineColumn i{ background:var(--ink-2); }
.timelineYear[aria-pressed="true"] .timelineMark{ background:var(--accent); box-shadow:0 0 0 3px var(--bg),0 0 0 4px var(--accent); }
.timelineYear[aria-pressed="true"] .timelineColumn i{ background:var(--accent); }

/* Поводок к карточке. Он живёт внутри прокручиваемой области и доходит до её
   нижнего края; дальше его продолжает верхняя граница карточки — одной линией
   это не сделать, карточка стоит вне прокрутки. */
.timelineYear[aria-pressed="true"]::after{ content:""; position:absolute; left:50%; top:calc(var(--axis-top) + 14px); width:1px; bottom:0; background:repeating-linear-gradient(var(--line-2) 0 3px,transparent 3px 7px); }

/* ── Событие, у которого года нет, а время есть ──

   `TemporalBounds` вывел из порядка рассказа «не раньше — не позже», и
   интервал шире трёх лет годом не становится никогда (инвариант 4). Такое
   событие стоит охрой — тем же цветом, каким в теме помечена гипотеза, — и
   точка года собой не называет: об этом говорит её цвет, а не место. Место у
   неё — середина промежутка, и другого честного места нет.

   Терракота сюда не годится: ею в теме сказаны ровно две вещи — «это назвал
   человек» и «это сейчас на экране», и третьего значения у неё быть не может.

   Клавиша ростом в палец и стоит поверх кругов: там, где середина промежутка
   легла впритык к названному году, сверху обязана оказаться она. Круг под ней
   от этого не пропадает — он нажимается из своей колонки выше. */
.timelineNear{
  position:absolute;
  top:var(--axis-top);
  width:34px;
  height:34px;
  transform:translate(-50%,-50%);
  padding:0;
  border:0;
  background:none;
  color:inherit;
  font:inherit;
  cursor:pointer;
  z-index:2;
}

.timelineNear:focus-visible{ outline:1px solid var(--flag); outline-offset:2px; border-radius:var(--r1); }

.timelineNearDot{
  position:absolute;
  left:50%;
  top:50%;
  width:10px;
  height:10px;
  transform:translate(-50%,-50%);
  border-radius:50%;
  background:var(--flag);
  box-shadow:0 0 0 3px var(--bg);
  transition:box-shadow var(--dur) var(--ease);
}

.timelineNear:hover .timelineNearDot{ box-shadow:0 0 0 3px var(--bg),0 0 0 4px var(--flag-soft); }

.timelineNear[aria-pressed="true"] .timelineNearDot{ box-shadow:0 0 0 3px var(--bg),0 0 0 4px var(--flag); }

/* Штанга размаха приходит на выбор и уходит с ним. Держать их у всех сразу
   нельзя: пять полос через полжизни перечеркнули бы линию, ради которой всё и
   рисуется. Показывает её соседство с нажатой клавишей — одним правилом, без
   единой строки сценария; поэтому она и стоит в разметке сразу за клавишей. */
.timelineSpan{
  position:absolute;
  top:var(--axis-top);
  height:3px;
  transform:translateY(-50%);
  border-radius:2px;
  background:color-mix(in srgb, var(--flag) 55%, transparent);
  opacity:0;
  pointer-events:none;
  z-index:1;
  transition:opacity var(--dur) var(--ease);
}

/* Засечки по краям: полоса говорит «где-то здесь», засечки — «не раньше вот
   этого и не позже вот того». */
.timelineSpan::before,
.timelineSpan::after{ content:""; position:absolute; top:50%; width:1px; height:11px; transform:translateY(-50%); background:var(--flag); }

.timelineSpan::before{ left:0; }

.timelineSpan::after{ right:0; }

.timelineNear[aria-pressed="true"] + .timelineSpan{ opacity:1; }

/* Поводок к карточке — тот же, что у года, только охрой. Ростом он от края
   клавиши до низа дорожки: дальше его продолжает верхняя граница карточки. */
.timelineNear[aria-pressed="true"]::after{
  content:"";
  position:absolute;
  left:50%;
  top:calc(50% + 12px);
  width:1px;
  height:calc(var(--track-h) - var(--axis-top) - 12px);
  background:repeating-linear-gradient(color-mix(in srgb, var(--flag) 40%, transparent) 0 3px,transparent 3px 7px);
}

.timelineToday{ position:absolute; top:var(--axis-top); width:9px; height:9px; transform:translate(-50%,-50%); border-radius:50%; background:var(--accent); box-shadow:0 0 0 3px var(--bg); }
.timelineTodayLabel{ position:absolute; top:calc(var(--axis-top) + 28px); transform:translateX(-100%); font-size:var(--t-cap); letter-spacing:var(--track); text-transform:uppercase; color:var(--accent); }

.timelineDetails{ margin-top:var(--s3); }
.timelineCard{ display:grid; grid-template-columns:minmax(150px,24%) 1fr; gap:var(--s5); padding:var(--s5) var(--s6); border:1px solid var(--line); border-radius:var(--r1); background:var(--card); }
.timelineCard[hidden]{ display:none; }
.timelineCardYear{ display:flex; flex-direction:column; align-items:center; text-align:center; padding-right:var(--s5); border-right:1px solid var(--line); }
.timelineCardYear strong{ font-family:var(--serif); font-size:clamp(48px,7vw,76px); font-weight:400; line-height:1; color:var(--accent); font-variant-numeric:tabular-nums; }
.timelineCardYear span{ margin-top:var(--s2); color:var(--muted); font-size:var(--t-sm); }

/* Числа два, потому что время одно, но названо границами. Стоят они столбиком:
   «1984—1993» в строку при кегле года не помещается ни в одну колонку, а
   уменьшить его значило бы сказать, что промежуток менее важен, чем год.
   Отбор идёт по имени столбца — правило года стоит выше и одной длины с этим,
   и без имени столбца оно бы его перебило. */
.timelineCardYear .timelineCardSpan{ display:flex; flex-direction:column; align-items:inherit; line-height:1.04; }

.timelineCardYear .timelineCardSpan strong{ font-family:var(--serif); font-size:clamp(34px,4.6vw,50px); font-weight:400; line-height:1.04; color:var(--flag); font-variant-numeric:tabular-nums; }

.timelineCardYear .timelineCardSpan span{ margin:0; color:var(--flag); opacity:.5; font-size:var(--t-body); line-height:1; }

.timelineCardYear .timelineNearTag{
  margin-top:var(--s3);
  padding:2px var(--s2);
  border:1px solid color-mix(in srgb, var(--flag) 40%, transparent);
  border-radius:var(--r1);
  color:var(--flag);
  font-size:var(--t-cap);
  letter-spacing:var(--track);
  text-transform:uppercase;
}
/* Стрелки по бокам числа листают годы по одному, включая пустые. Сервер отдаёт
   их спрятанными, скрипт показывает: без него листать некому. Голый знак без
   рамки, крупный, но цвета линеек: число года остаётся главным в карточке, а
   стрелка проявляется под курсором. Поле нажатия шире знака. */
.timelineYearRow{ display:flex; align-items:center; gap:var(--s2); }
.timelineStep{ flex:none; width:30px; height:34px; display:inline-flex; align-items:center; justify-content:center; padding:0; border:0; background:none; color:var(--line-2); font-size:24px; line-height:1; cursor:pointer; }
.timelineStep:hover{ color:var(--accent); }
.timelineStep:disabled{ opacity:.35; cursor:default; }
.timelineStep:disabled:hover{ color:var(--line-2); }
.timelineStep[hidden]{ display:none; }
.timelineAdd{ margin-top:var(--s4); padding:var(--s2) var(--s3); border:1px solid var(--line-2); border-radius:var(--r1); color:var(--ink-2); font-size:var(--t-sm); text-decoration:none; }
.timelineAdd:hover{ border-color:var(--accent); color:var(--accent); }

/* Разделителей между фактами нет: год — это не таблица, а несколько строк об
   одном времени, и линейки резали бы их на записи реестра. */
.timelineFacts{ min-width:0; margin:0; padding:0; list-style:none; display:grid; gap:var(--s4); align-content:start; }
.timelineFacts li{ display:grid; grid-template-columns:32px minmax(0,1fr); align-items:center; gap:var(--s4); }
.timelineFactText{ font-family:var(--serif); font-size:var(--t-body); overflow-wrap:anywhere; }
.timelineFactText em{ font-style:normal; color:var(--muted); }
/* Слева от события — клавиша с микрофоном: единственное действие, которое в
   этой строке имеет смысл. Прежде тут стоял значок источника, `▶` или `Т`, и
   он отвечал на вопрос, которого человек не задавал. Размер и рамка у клавиши
   те же, значковые: ряд от замены не шелохнулся. */
/* Пометка «в справке» на строке человека: он же нашёлся в загруженном списке.
   Тише имени и тише пометки тёзки — это сведение, а не действие: нажимать
   нечего, темы справки висят подсказкой. */
.peopleFound{
  align-self:center;
  padding:0 var(--s2);
  border:1px solid var(--line);
  border-radius:var(--r1);
  color:var(--muted);
  font-size:var(--t-meta);
  white-space:nowrap;
  cursor:help;
}

/* ── Найденные люди: полка под кругом общения ── */
.foundPeople{ display:grid; gap:var(--s5); }

.foundShelf{ display:grid; gap:var(--s3); }

.foundShelfName{ font:400 var(--t-h3)/1.3 var(--serif); display:flex; align-items:baseline; gap:var(--s3); }

.foundList{ display:grid; gap:var(--s4); list-style:none; padding:0; }

.foundOne{ display:grid; gap:var(--s2); padding-left:var(--s4); border-left:1px solid var(--line); }

.foundName{ font-size:var(--t-sm); font-weight:600; }

/* Клавиша «расскажите о нём» и её след. Тише имени: это приглашение, а не
   действие страницы, — но и не сноска, потому что ради него список и грузят. */
.foundAsk{ justify-self:start; }

.foundAsked{ color:var(--muted); font-size:var(--t-meta); }

.foundTags{ color:var(--muted); font-size:var(--t-meta); }

/* ── Найденное на линии ──
   Запись справки стоит той же строкой, что рассказанное, и отличается двумя
   вещами: пометкой, чей это голос, и тем, что микрофона у неё нет — на его
   месте засечка, чтобы строки не разъезжались по левому краю. Помечать цветом
   всю строку не стали: цвет сказал бы «это другого сорта», а это та же жизнь,
   просто узнанная не от неё самой. */
.timelineSaidBy{
  color:var(--muted);
  font-size:var(--t-meta);
  white-space:nowrap;
}

.timelineFound{
  display:block;
  width:30px;
  height:30px;
  border-left:1px solid var(--line-2);
  margin-left:14px;
}

.timelineNotes{ display:grid; gap:var(--s2); margin-top:var(--s2); }

.timelineNote{
  display:flex;
  align-items:baseline;
  justify-content:space-between;
  gap:var(--s3);
  padding:var(--s2) var(--s3);
  border-left:2px solid var(--line-2);
  background:var(--sheet-2);
  color:var(--ink-2);
  font-size:var(--t-meta);
  line-height:1.5;
}

.timelineSayNote{ display:flex; align-items:flex-end; gap:var(--s3); }

.timelineSayNote textarea{
  flex:1;
  min-height:var(--tap);
  padding:var(--s2) var(--s3);
  border:1px solid var(--line);
  border-radius:var(--r1);
  background:var(--card);
  color:var(--ink);
  font:inherit;
  font-size:var(--t-meta);
  resize:vertical;
}

.timelineSay{
  display:grid;
  place-items:center;
  width:30px;
  height:30px;
  padding:0;
  border:1px solid var(--line-2);
  border-radius:var(--r1);
  background:none;
  color:var(--muted);
  cursor:pointer;
  transition:color var(--dur) var(--ease), border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}

.timelineSay:hover{ border-color:var(--accent); color:var(--accent); }

/* Открытая клавиша горит всё время, пока под нею говорят: это единственная
   метка того, к какой строке относится планка внизу столбца. */
.timelineSay[aria-expanded="true"]{ border-color:var(--accent); color:var(--accent); background:var(--accent-soft); }

/* «Примерно» набрано как метка, а не как часть события: год этой строки
   вычислен из порядка рассказа, и глаз должен отличать его от названного. */
.timelineAbout{ color:var(--muted); font-family:var(--sans); font-size:var(--t-meta); white-space:nowrap; display:inline; }
.timelineMore{ color:var(--muted); font-size:var(--t-sm); }

/* Второй столбец карточки — сам столбец: события сверху, планка записи под
   ними. Без него планка становится строкой всей карточки и уезжает под нижний
   край первого столбца: высоту строки задаёт то из двух, что выше, а выше
   обычно число года, и между последним событием и чертой оставалась ладонь
   пустого места. */
.timelineCardBody{ display:grid; align-content:start; gap:var(--s5); min-width:0; }

/* Планка записи — нижний этаж второго столбца, во всю его ширину, отделённый
   от событий чертой от края столбца до края. В первый столбец она не заходит:
   там время, и время у карточки одно на все события, а планка — про одно из
   них. Своей рамки у планки нет: рамка здесь одна, карточкина, а планка внутри
   неё — этаж, а не коробка в коробке. Ровно так планка стоит в окне человека
   (`.peopleDeck`), и второго облика для одного и того же диктофона заводить
   незачем.

   Между строк перечня она раздвигала бы его надвое и на каждое нажатие толкала
   половину событий вниз. Внизу список остаётся списком, а планка у карточки
   одна и всегда на одном месте. */
.timelineSayRow{
  min-width:0;
  border-top:1px solid var(--line);
  animation:rise var(--dur) var(--ease);
}

.timelineSayRow[hidden]{ display:none; }

.timelineSayRow .dictaphone{
  position:static;
  z-index:auto;
  gap:var(--s2);
  padding:var(--s2) 0 0;
  background:none;
}

.timelineSayRow .dictaphone[data-open="0"]{ transform:none; opacity:1; pointer-events:auto; }

.timelineSayRow .deck{ max-width:none; border:0; border-radius:0; background:none; }

/* Полей по бокам у планки нет: она и есть столбец. Микрофон клавиши стоит в
   той же колонке в 32px, что и микрофоны строк, а надпись начинается там же,
   где текст события, — иначе планка съезжает влево от всего, под чем стоит. */
.timelineSayRow .key{ min-height:56px; padding:0; }

.timelineSayRow .keyMic{ width:32px; }

.timelineSayRow .key:hover{ background:none; }

.timelineSayRow .key[data-on="1"]{ background:none; box-shadow:none; }

.timelineSayRow .deckActs{ border-top:0; padding:0; }

/* Лестница внутри карточки лежит строкой: четыре ряда съели бы год целиком.
   Рейка с заливкой на это время уходит — они рисуют вертикаль, которой здесь
   нет, — а кружок ступени встаёт перед словом, в общий ряд. */
.timelineSayRow .ladder{
  display:flex;
  align-items:center;
  flex-wrap:wrap;
  gap:var(--s2) var(--s5);
  max-width:none;
  padding:0 0 var(--s3);
}

.timelineSayRow .ladderRail,
.timelineSayRow .ladderFill{ display:none; }

.timelineSayRow .rung{ flex:0 0 auto; white-space:nowrap; }

.timelineSayRow .rung::before{ position:static; left:auto; flex:0 0 auto; }

.timelineSayRow .compose{ max-width:none; margin:0 0 var(--s4); }

.timelineSayRow .dictHint{ padding:0 0 var(--s3); }

/* Дом планки: она лежит на странице ничьей, пока её не позвали. */
.dictHome{ display:none; }

/* Подпись только для чтения с экрана: круг сам по себе ничего не говорит. */
.srOnly{ position:absolute; width:1px; height:1px; margin:-1px; padding:0; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }

@media (max-width:600px){
  .timelineCard{ grid-template-columns:1fr; gap:var(--s4); padding:var(--s4); }
  .timelineCardYear{ align-items:flex-start; text-align:left; padding:0 0 var(--s4); border-right:0; border-bottom:1px solid var(--line); }
  .timelineCardYear strong{ font-size:52px; }

  .timelineCardYear .timelineCardSpan strong{ font-size:36px; }
  .timelineFacts li{ grid-template-columns:30px minmax(0,1fr); gap:var(--s3); }
  .timelineFactText{ font-size:var(--t-sm); }

  /* Колонка значков на телефоне 30px, и микрофон клавиши встаёт в неё же. */
  .timelineSayRow .keyMic{ width:30px; }

  .timelineSayRow .key{ gap:var(--s3); }
}

/* ── Что вышло из рассказа, сказано на линии, а не словами ──

   Человек назвал время, и единственное, что он хочет увидеть, — куда оно
   встало. Метка вспыхивает там сама, крупно, и слов при ней не нужно; а когда
   времени в рассказе не было, не вспыхивает ничего, и это тоже сказано без
   слов.

   Круги расходятся от метки тем же ходом, каким расходится ореол у лампы
   записи (`ping` у `.halo`): в теме уже есть способ сказать «здесь сейчас
   происходит», и второго заводить незачем. Цвет — мох (`--done`), «сделано»;
   терракотой сказаны ровно две вещи, «идёт запись» и «это сейчас на экране».

   Живёт вспышка около трёх секунд: полутора хватало галочке в строке, а
   единственному отчёту на весь экран — нет, его надо успеть найти глазами. */
.timelineLanded{
  position:absolute;
  top:var(--axis-top);
  width:0;
  height:0;
  transform:translate(-50%,-50%);
  pointer-events:none;
  z-index:3;
}

/* Тёплое пятно под кругами: без него три волоска на светлой бумаге читаются
   как рябь, а не как «сюда смотреть». */
.timelineLanded::before{
  content:"";
  position:absolute;
  left:0;
  top:0;
  width:78px;
  height:78px;
  margin:-39px 0 0 -39px;
  border-radius:50%;
  background:radial-gradient(circle, color-mix(in srgb, var(--done) 30%, transparent) 0%, transparent 68%);
  opacity:0;
  animation:landGlow 2.9s var(--ease) 1;
}

.timelineLanded i{
  position:absolute;
  left:0;
  top:0;
  width:18px;
  height:18px;
  margin:-9px 0 0 -9px;
  border-radius:50%;
  border:2px solid var(--done);
  opacity:0;
  animation:landRing 2.1s var(--ease) 1;
}

.timelineLanded i:nth-child(2){ animation-delay:.6s; }

.timelineLanded i:nth-child(3){ animation-delay:1.2s; }

@keyframes landRing{
  0%{ transform:scale(.5); opacity:.9; }
  60%{ opacity:.24; }
  100%{ transform:scale(5); opacity:0; }
}

@keyframes landGlow{
  0%{ opacity:0; }
  12%{ opacity:1; }
  62%{ opacity:1; }
  100%{ opacity:0; }
}

@media (prefers-reduced-motion:reduce){
  .timelineMark{ transition:none; }

  /* Кому движение мешает, тому вспышка остаётся кольцом: место названо, а
     ничто не прыгает. */
  .timelineLanded::before{ animation:none; opacity:1; }

  .timelineLanded i{ animation:none; opacity:.6; transform:scale(2.4); }

  .timelineLanded i:nth-child(2),
  .timelineLanded i:nth-child(3){ display:none; }
}

/* Люди рассказа: блок пополам по золотому сечению — сферы слева, люди справа.

   Секция стоит под линией времени и отвечает на третий вопрос страницы: линия
   говорит, что было, решётка — о чём говорили, эта таблица — кто в этом был.
   Полос прокрутки нет нигде: списки листаются, скроллбар скрыт.

   Ширина половин — `--menu`, то самое золотое сечение, каким держится рельс
   меню: второго числа для той же пропорции заводить незачем. Сферам оно даёт
   место под «время вокруг: город, страна, мир», людям оставляет большую
   половину.

   Четыре ряда по краям блока — «Все люди», поиск, «+ Сфера», «Добавить
   человека» — одной высоты в 52px: разная высота у соседних краёв читается
   как перекос вёрстки, а не как разница смысла. Строка человека и строка
   сферы — 44px, зона нажатия. */
.people{ margin-top:var(--s8); padding-top:var(--s7); border-top:1px solid var(--line); }

.peopleTable{
  display:grid;
  grid-template-columns:var(--menu) minmax(0,1fr);
  height:min(600px, 78vh);
  border:1px solid var(--line-2);
  border-radius:var(--r2);
  background:var(--card);
  overflow:hidden;
}

/* ── Левая половина: сферы. Один столбец в двух ролях — пока никто не выбран,
     это фильтр; выбран человек — это его галочки. ── */
.peopleLeft{
  display:grid;
  grid-template-rows:auto minmax(0,1fr) auto auto;
  min-height:0;
  min-width:0;
  border-right:1px solid var(--line);
  transition:background var(--dur) var(--ease);
}

.peopleLeft[data-mine="1"]{ background:color-mix(in srgb, var(--accent-soft) 45%, var(--card)); }

.peopleSpheres,
.peopleMarks,
.peopleMore{
  overflow-y:auto;
  overscroll-behavior:contain;
  scrollbar-width:none;
  -ms-overflow-style:none;
}

.peopleSpheres::-webkit-scrollbar,
.peopleMarks::-webkit-scrollbar,
.peopleMore::-webkit-scrollbar,
.peopleCrowd::-webkit-scrollbar{ display:none; width:0; height:0; }

.peopleSpheres[hidden],
.peopleMarks[hidden],
.peopleMore[hidden]{ display:none; }

/* Полосы прокрутки нет — значит, о том, какой список слушает стрелки, говорит
   только фокус. Без этой рамки клавиатура работала бы вслепую. */
.peopleSpheres:focus-visible,
.peopleCrowd:focus-visible{ outline:2px solid var(--accent); outline-offset:-2px; }

/* Заголовок столбца в двух лицах и в одном ряду: «Все люди», пока выбирают
   сферу, и имя человека, когда ставят галочки. */
.peopleLead{
  display:flex;
  align-items:center;
  gap:var(--s3);
  width:100%;
  min-height:52px;
  padding:0 var(--s4);
  border:0;
  border-bottom:1px solid var(--line-2);
  background:var(--sheet);
  color:var(--ink);
  font:inherit;
  text-align:left;
  transition:background var(--dur) var(--ease), color var(--dur) var(--ease);
}

.peopleLead[hidden]{ display:none; }

.peopleLead[data-on="1"]{ background:var(--accent); color:var(--on-accent); }

.peopleLead[data-on="1"] .peopleNum{ color:color-mix(in srgb, var(--on-accent) 72%, transparent); }

.peopleLeadSay{ flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:var(--s1); }

.peopleLeadName{
  font-family:var(--serif);
  font-size:var(--t-body);
  line-height:1.2;
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}

button.peopleLead{ cursor:pointer; }

button.peopleLead:hover{ background:var(--accent-soft); }

button.peopleLead:focus-visible{ outline:2px solid var(--accent); outline-offset:-2px; }

button.peopleLead[data-on="1"]:hover{ background:var(--accent); }

/* Стрелка назад слева от «Сфер»: пока открыт человек, левый столбец — его
   галочки, и уйти из окна надо там же, где на него смотрят. Делает она ровно
   то же, что крестик в правом верхнем углу, — это два края одного окна, а не
   два разных выхода.

   Стоит она в колонке галочек, а имя человека — в колонке названий сфер: под
   заголовком идут два десятка строк той же разбивки, и заголовок, набранный
   по своей мерке, ломал бы обе вертикали разом. Клавиша при этом остаётся
   пальцевой — 44px, — а лишнее по бокам снимается отрицательным полем: место
   в ряду она занимает галочкино, восемнадцать, и имя встаёт туда же, куда
   встают названия сфер. */
.peopleShutBack{
  flex:0 0 auto;
  display:flex;
  align-items:center;
  justify-content:center;
  width:var(--tap);
  height:var(--tap);
  /* Половина того, чем клавиша шире галочки: ряд её не замечает. */
  margin:0 calc((var(--tap) - 18px) / -2);
  border:0;
  background:none;
  color:var(--muted);
  font-size:20px;
  line-height:1;
  cursor:pointer;
  transition:color var(--dur) var(--ease);
}

.peopleShutBack:hover{ color:var(--accent); }

.peopleShutBack:focus-visible{ outline:2px solid var(--accent); outline-offset:-2px; }

/* ── Сфера: имя слева, число людей справа ── */
.peopleSphere{
  display:flex;
  align-items:center;
  gap:var(--s3);
  width:100%;
  min-height:var(--tap);
  padding:0 var(--s4);
  border:0;
  border-bottom:1px solid var(--line);
  background:none;
  color:var(--ink);
  font:inherit;
  text-align:left;
  cursor:pointer;
  transition:background var(--dur) var(--ease), color var(--dur) var(--ease);
}

.peopleSphere:hover{ background:var(--accent-soft); }

.peopleSphere:focus-visible{ outline:2px solid var(--accent); outline-offset:-2px; }

/* Выбранная сфера залита — «это сейчас на экране». Отмеченная у человека
   только подсвечена: заливка обоих состояний сделала бы фильтр и галочку
   одним и тем же знаком. */
.peopleSphere[data-on="1"]{ background:var(--accent); color:var(--on-accent); }

.peopleSphere[data-mine="1"]{ background:var(--accent-soft); }

.peopleSphere[data-debt="1"]{ color:var(--flag); border-top:1px solid var(--line-2); }

.peopleSphere[data-debt="1"][data-on="1"]{ background:var(--flag); color:var(--on-accent); }

.peopleSphere[data-debt="1"] .peopleNum{ color:var(--flag); }

.peopleSphere[data-on="1"] .peopleNum{ color:color-mix(in srgb, var(--on-accent) 72%, transparent); }

.peopleSphereName{
  flex:1 1 auto;
  min-width:0;
  font-family:var(--serif);
  font-size:15px;
  line-height:1.2;
}

.peopleNum{
  flex:0 0 auto;
  font-family:var(--sans);
  font-size:var(--t-meta);
  color:var(--muted);
  font-variant-numeric:tabular-nums;
}

.peopleTick{
  flex:0 0 auto;
  display:flex;
  align-items:center;
  justify-content:center;
  width:18px;
  height:18px;
  border:1px solid var(--line-2);
  border-radius:var(--r1);
  color:var(--on-accent);
  transition:background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.peopleTick[data-on="1"]{ background:var(--accent); border-color:var(--accent); }

/* ── «+ Сфера»: две вещи под одной клавишей, и обе про полку ── */
.peopleAdd{
  display:flex;
  align-items:center;
  gap:var(--s3);
  width:100%;
  min-height:52px;
  padding:0 var(--s4);
  border:0;
  border-top:1px solid var(--line);
  background:none;
  color:var(--muted);
  font:inherit;
  font-size:var(--t-sm);
  text-align:left;
  cursor:pointer;
  transition:color var(--dur) var(--ease), background var(--dur) var(--ease);
}

.peopleAdd:hover{ color:var(--accent); }

.peopleAdd:focus-visible{ outline:2px solid var(--accent); outline-offset:-2px; }

/* Раскрытая панель говорит то же слово другим лицом: «+ Сфера» в покое,
   «Сфера ×» раскрытой — тот же приём, что у переключателя историй в шапке. */
.peopleAdd[data-open="1"]{ color:var(--ink); background:var(--sheet); }

.peopleAdd[data-open="0"] .peopleAddX{ display:none; }

.peopleAdd[data-open="1"] .peoplePlus{ display:none; }

.peopleAddName{ flex:1 1 auto; }

.peopleAddX{ flex:0 0 auto; color:var(--muted); font-size:20px; line-height:1; }

.peopleAdd:hover .peopleAddX{ color:var(--accent); }

.peoplePlus{ flex:0 0 auto; color:var(--accent); font-size:18px; line-height:1; }

.peopleMore{
  padding:var(--s4);
  display:flex;
  flex-direction:column;
  gap:var(--s4);
}

.peopleSay{ font-size:var(--t-sm); color:var(--muted); line-height:1.5; }

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

.peopleMoreItem{ display:flex; align-items:center; }

.peopleMoreOne{
  flex:1 1 auto;
  min-width:0;
  display:flex;
  align-items:center;
  min-height:var(--tap);
  padding:0 var(--s3);
  border:0;
  border-radius:var(--r1);
  background:none;
  color:var(--ink);
  font-family:var(--serif);
  font-size:15px;
  text-align:left;
  cursor:pointer;
  transition:background var(--dur) var(--ease);
}

.peopleMoreOne:hover{ background:var(--accent-soft); }

.peopleMoreOne:focus-visible{ outline:2px solid var(--accent); outline-offset:-2px; }

/* Убирается только пустая своя полка: на занятой стоят чужие пометки, и
   крестиком у названия человек их снимать не просил. */
.peopleMoreDrop{
  flex:0 0 auto;
  width:var(--tap);
  height:var(--tap);
  border:0;
  background:none;
  color:var(--muted);
  font-size:18px;
  line-height:1;
  cursor:pointer;
}

.peopleMoreDrop:hover{ color:var(--accent); }

.peopleMoreNew{
  display:flex;
  align-items:center;
  gap:var(--s3);
  min-height:52px;
  padding:0 var(--s3);
  border-radius:var(--r1);
  background:color-mix(in srgb, var(--accent-soft) 55%, transparent);
  transition:background var(--dur) var(--ease);
}

.peopleMoreNew:focus-within{ background:var(--accent-soft); }

.peopleMoreField{
  flex:1 1 auto;
  min-width:0;
  min-height:var(--tap);
  border:0;
  background:none;
  color:var(--ink);
  font-family:var(--serif);
  font-size:var(--t-sm);
}

.peopleMoreField::placeholder{ color:var(--hint); }

.peopleMoreField:focus{ outline:none; }

/* ── Правая половина: поиск сверху всегда, под ним список или человек, снизу
     то, чем половину продолжают: строка «добавить» или диктофон. ── */
.peopleRight{
  display:grid;
  grid-template-rows:auto minmax(0,1fr) auto;
  min-height:0;
  min-width:0;
}

/* Открытый человек добавляет ряд: поиск, имя, рассказанное, диктофон.
   Тянется по-прежнему одно — то, что читают. */
.peopleTable[data-who="1"] .peopleRight{ grid-template-rows:auto auto minmax(0,1fr) auto; }

.peopleSeekRow{
  display:flex;
  align-items:center;
  gap:var(--s3);
  min-width:0;
  min-height:52px;
  padding:0 var(--s3) 0 var(--s4);
  border-bottom:1px solid var(--line-2);
  background:var(--sheet);
  transition:background var(--dur) var(--ease);
}

.peopleSeekRow:focus-within{ background:var(--accent-soft); }

.peopleSeek{
  flex:1 1 auto;
  min-width:0;
  min-height:var(--tap);
  border:0;
  background:none;
  color:var(--ink);
  font-family:var(--serif);
  font-size:var(--t-sm);
}

.peopleSeek::placeholder{ color:var(--hint); }

.peopleSeek:focus{ outline:none; }

.peopleSeek::-webkit-search-cancel-button{ -webkit-appearance:none; appearance:none; }

/* Число найденных стоит у самого поиска и только пока ищут: в покое его место
   занято лупой, а счёт людей и так стоит у каждой сферы слева. */
.peopleFound{
  flex:0 0 auto;
  font-size:var(--t-meta);
  color:var(--muted);
  font-variant-numeric:tabular-nums;
}

.peopleFound[hidden]{ display:none; }

.peopleGlass{ flex:0 0 auto; display:flex; color:var(--muted); }

.peopleGlass[hidden]{ display:none; }

.peopleSeekRow:focus-within .peopleGlass{ color:var(--accent); }

/* Ярлычок «сохранено»: появляется, когда правка легла, и мягко уходит сам.
   Место — слева от крестика: это верхний край окна, туда смотрят, закрывая
   его, и туда же смотрят, дописав строку.

   Места он не занимает, когда его нет: `visibility` вместо `display`, и
   строка поиска от его появления не дёргается. Приходит быстро, уходит
   медленно — так и читается «уже сделано», а не «смотри сюда». */
.peopleSaved{
  flex:0 0 auto;
  display:flex;
  align-items:center;
  gap:var(--s2);
  height:24px;
  padding:0 var(--s2);
  color:var(--done);
  font-family:var(--sans);
  font-size:var(--t-cap);
  letter-spacing:var(--track);
  text-transform:uppercase;
  white-space:nowrap;
  opacity:0;
  visibility:hidden;
  transition:opacity .6s var(--ease), visibility .6s var(--ease);
}

.peopleSaved[data-on="1"]{ opacity:1; visibility:visible; transition-duration:.18s; }

.peopleSavedTick{ display:flex; color:var(--done); }

/* Не легло — это не «сохранено» другим цветом: галочка уходит вместе со
   словом, потому что галочка и значит «легло». Ярлычок при этом не тает —
   таять ему нечем: правка всё ещё не сохранена. */
.peopleSaved[data-tone="fail"]{ color:var(--alarm); }

.peopleSaved[data-tone="fail"] .peopleSavedTick{ display:none; }

/* Крестик один и стоит в правом верхнем углу: пока открыт человек, правая
   половина и есть его окно, а верхний ряд — его край. Лупа на это время
   уступает ему место — поле и без неё говорит, что это поиск. */
.peopleShut{
  flex:0 0 auto;
  width:var(--tap);
  height:var(--tap);
  border:0;
  background:none;
  color:var(--muted);
  font-size:22px;
  line-height:1;
  cursor:pointer;
}

.peopleShut[hidden]{ display:none; }

.peopleShut:hover{ color:var(--accent); }

/* `position:relative` — не украшение: от него считаются `offsetTop` строк, а
   по ним курсор стрелок подводится в вид. */
.peopleCrowd{
  position:relative;
  overflow-y:auto;
  overscroll-behavior:contain;
  scrollbar-width:none;
  -ms-overflow-style:none;
}

.peopleCrowd[hidden]{ display:none; }

/* ── Человек в списке: имя и больше ничего. Где он числится — видно слева, в
     столбце сфер; адрес рядом с каждым именем в записной книжке не пишут. ── */
/* Строка — ряд из двух клавиш: имя и, у тёзок, пометка. Черта под строкой
   принадлежит ряду, а не имени: иначе у помеченных строк она обрывалась бы
   там, где кончается первая клавиша. Подложка наведения общая на обе
   половины — рука наводится на строку, а не на её часть. */
.peopleRow{
  display:flex;
  align-items:center;
  border-bottom:1px solid var(--line);
  transition:background var(--dur) var(--ease);
}

.peopleRow:hover{ background:var(--accent-soft); }

.peopleRow[hidden]{ display:none; }

.peopleOne{
  display:flex;
  align-items:center;
  gap:var(--s3);
  flex:1 1 auto;
  min-width:0;
  min-height:var(--tap);
  padding:0 var(--s4);
  border:0;
  background:none;
  color:var(--ink);
  text-align:left;
  cursor:pointer;
  transition:color var(--dur) var(--ease);
}

.peopleOne:focus-visible{ outline:2px solid var(--accent); outline-offset:-2px; }

.peopleOne[hidden]{ display:none; }

/* Точка у имени: об этом человеке сейчас разбирают ответ. Нужна затем, чтобы
   уход из окна не выглядел потерей работы — ушли к другому, видно, что там
   ещё идёт. Ставит её сценарий атрибутом: разметка списка приходит с сервера,
   а это состояние минуты, а не свойство человека. */
.peopleOne[data-live="1"]::after{
  content:"";
  flex:0 0 auto;
  width:6px;
  height:6px;
  border-radius:999px;
  background:var(--accent);
  animation:peopleLive 1.2s var(--ease) infinite alternate;
}

@keyframes peopleLive{
  from{ opacity:.25; }
  to{ opacity:1; }
}

/* Курсор стрелок — не выбор: открытый человек занимает всю правую половину, а
   это только «сюда ведут стрелки». Черта слева, потому что подложка уже
   занята наведением. */
.peopleOne[data-cursor="1"]{
  background:var(--accent-soft);
  box-shadow:inset 2px 0 0 var(--accent);
}

/* Пометка тёзки живёт в том же ряду, и подложка курсора обязана доставать до
   неё: половина подсвеченной строки читалась бы как другая строка. */
.peopleRow:has(.peopleOne[data-cursor="1"]){ background:var(--accent-soft); }

.peopleName{
  flex:1 1 auto;
  min-width:0;
  font-family:var(--serif);
  font-size:var(--t-body);
  line-height:1.2;
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}

/* Пометка тёзки: в списке два человека с одним именем. Стоит справа, охрой и
   мелко — это вопрос, а не приговор: разбор не вправе решать, один это человек
   или двое, и нажатие ведёт к тому же вопросу о склейке, что и в окне
   человека. Строка при этом остаётся строкой человека: нажатое мимо пометки
   открывает его, как и всякое имя. */
.peopleTwin{
  flex:0 0 auto;
  min-height:var(--tap);
  padding:0 var(--s4) 0 0;
  border:0;
  background:none;
  color:var(--accent);
  font-family:var(--sans);
  font-size:var(--t-meta);
  white-space:nowrap;
  cursor:pointer;
  text-decoration:underline;
  text-decoration-color:var(--line-2);
  text-underline-offset:3px;
}

.peopleRow:hover .peopleTwin{ text-decoration-color:var(--accent); }

.peopleTwin:focus-visible{ outline:2px solid var(--accent); outline-offset:-2px; }

.peopleList[hidden]{ display:none; }

.peopleBare{ padding:var(--s5) var(--s4); color:var(--muted); font-size:var(--t-sm); line-height:1.6; }

.peopleBare[hidden]{ display:none; }

/* Пустой поиск не тупик: набранное имя тут же можно завести. */
.peopleCoin{
  display:flex;
  align-items:center;
  gap:var(--s3);
  width:100%;
  min-height:var(--tap);
  padding:0 var(--s4);
  border:0;
  background:none;
  color:var(--accent);
  font-family:var(--serif);
  font-size:var(--t-sm);
  text-align:left;
  cursor:pointer;
  transition:background var(--dur) var(--ease);
}

.peopleCoin[hidden]{ display:none; }

.peopleCoin:hover{ background:var(--accent-soft); }

/* ── Добавить человека: строка заметна, но с кнопками не спорит. ── */
.peopleGrowRow{
  display:flex;
  align-items:center;
  gap:var(--s3);
  min-width:0;
  min-height:52px;
  padding:0 var(--s4);
  border-top:1px solid var(--line);
  background:color-mix(in srgb, var(--accent-soft) 55%, var(--card));
  transition:background var(--dur) var(--ease);
}

.peopleGrowRow[hidden]{ display:none; }

.peopleGrowRow:focus-within{ background:var(--accent-soft); }

.peopleGrow{
  flex:1 1 auto;
  min-width:0;
  min-height:var(--tap);
  border:0;
  background:none;
  color:var(--ink);
  font-family:var(--serif);
  font-size:var(--t-sm);
}

.peopleGrow::placeholder{ color:var(--hint); }

.peopleGrow:focus{ outline:none; }

/* ── Дорога назад: на широком экране не нужна — обе половины видны сразу. ── */
.peopleBack{
  display:none;
  align-items:center;
  gap:var(--s3);
  width:100%;
  min-width:0;
  min-height:52px;
  padding:0 var(--s4);
  border:0;
  border-bottom:1px solid var(--line-2);
  background:color-mix(in srgb, var(--accent-soft) 55%, var(--card));
  color:var(--ink-2);
  font-family:var(--serif);
  font-size:var(--t-body);
  text-align:left;
  cursor:pointer;
  transition:background var(--dur) var(--ease);
}

.peopleBack:hover{ background:var(--accent-soft); }

.peopleBack::before{ content:"\2190"; color:var(--accent); font-size:18px; }

/* ── Окно человека: имя, рассказанное о нём и диктофон ── */
.peopleWho{
  display:flex;
  align-items:center;
  gap:var(--s3);
  min-width:0;
  padding:var(--s4);
  border-bottom:1px solid var(--line);
  background:var(--sheet);
}

.peopleWho[hidden]{ display:none; }

.peopleWhoSay{ flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:var(--s1); }

.peopleWhoName{
  width:100%;
  border:0;
  background:none;
  color:var(--ink);
  font-family:var(--serif);
  font-size:22px;
  line-height:1.2;
  box-shadow:inset 0 -1px 0 var(--line-2);
  transition:box-shadow var(--dur) var(--ease);
}

.peopleWhoName:focus{ outline:none; box-shadow:inset 0 -1px 0 var(--accent); }

.peopleWhoMeta{
  display:flex;
  align-items:baseline;
  flex-wrap:wrap;
  gap:var(--s2);
  font-size:var(--t-meta);
  color:var(--muted);
}

/* Тихая строка под именем: одно и то же лицо приходит из рассказа дважды —
   «Нина» и «Нина Петровна», — и склеить их надо там, где на них смотрят. */
.peopleTwinKey{
  border:0;
  background:none;
  color:var(--muted);
  font:inherit;
  font-size:var(--t-meta);
  text-decoration:underline;
  text-decoration-color:var(--line-2);
  text-underline-offset:3px;
  cursor:pointer;
  transition:color var(--dur) var(--ease);
}

.peopleTwinKey:hover{ color:var(--accent); }

.peopleTwinKey:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }

/* Строка склейки стоит на месте дороги назад и в том же обличье: и то и
   другое — «где вы сейчас и как отсюда выйти». */
.peopleAsk{
  display:flex;
  align-items:center;
  gap:var(--s3);
  min-width:0;
  min-height:52px;
  padding:0 var(--s3) 0 var(--s4);
  border-bottom:1px solid var(--line-2);
  background:var(--accent-soft);
}

.peopleAsk[hidden]{ display:none; }

.peopleAskSay{
  flex:1 1 auto;
  min-width:0;
  font-family:var(--serif);
  font-size:var(--t-sm);
  color:var(--ink);
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}

.peopleDeed{
  flex:0 0 auto;
  min-height:var(--tap);
  padding:0 var(--s4);
  border:0;
  border-radius:var(--r1);
  background:var(--accent);
  color:var(--on-accent);
  font:inherit;
  font-size:var(--t-sm);
  cursor:pointer;
  transition:opacity var(--dur) var(--ease);
}

.peopleDeed[hidden]{ display:none; }

.peopleDeed:hover{ opacity:.86; }

.peopleQuiet{
  flex:0 0 auto;
  min-height:var(--tap);
  padding:0 var(--s3);
  border:0;
  background:none;
  color:var(--muted);
  font:inherit;
  font-size:var(--t-sm);
  cursor:pointer;
}

.peopleQuiet:hover{ color:var(--accent); }

/* Ряд к сферам человека — только на телефоне: там столбца с галочками рядом
   нет, и попасть в него надо из окна. */
.peopleToMarks{
  display:none;
  align-items:center;
  gap:var(--s3);
  width:100%;
  min-width:0;
  min-height:52px;
  padding:0 var(--s4);
  border:0;
  border-bottom:1px solid var(--line);
  background:none;
  color:var(--ink-2);
  font:inherit;
  font-size:var(--t-sm);
  text-align:left;
  cursor:pointer;
}

.peopleToMarks:hover{ background:var(--accent-soft); }

.peopleToMarksWhere{
  flex:1 1 auto;
  min-width:0;
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
  color:var(--muted);
}

.peopleToMarksGo{ flex:0 0 auto; color:var(--accent); }

/* Рассказанное открывается сразу правимым: ни кнопки «править», ни второго
   режима. Это тот же ответ, что на «Истории», и правится он тем же приёмом —
   прямо там, где читается. */
.peopleStory{
  min-width:0;
  overflow-y:auto;
  overscroll-behavior:contain;
  padding:var(--s5) var(--s5) var(--s6);
  display:flex;
  flex-direction:column;
  gap:var(--s5);
  scrollbar-width:none;
  -ms-overflow-style:none;
}

.peopleStory::-webkit-scrollbar{ display:none; width:0; height:0; }

.peopleSaid{ display:flex; flex-direction:column; gap:var(--s2); }

.peopleWhen{
  display:flex;
  align-items:center;
  gap:var(--s3);
  font-size:var(--t-cap);
  letter-spacing:var(--track);
  text-transform:uppercase;
  color:var(--muted);
  font-variant-numeric:tabular-nums;
}

.peopleMark{ color:var(--accent); }

.peopleText{
  padding:0 0 0 var(--s4);
  border-left:1px solid var(--line);
  font-family:var(--serif);
  font-size:var(--t-body);
  line-height:1.65;
  color:var(--ink);
  transition:border-color var(--dur) var(--ease);
}

.peopleText:hover{ border-left-color:var(--line-2); }

.peopleText:focus{ outline:none; border-left-color:var(--accent); }

/* ── Строка, которую ещё разбирают ──

   Ответ появляется в окне вместе с расшифровкой, а год приходит к нему потом,
   с разбором. Всё это время строка не правится — правка ушла бы в ответ,
   который в эту секунду разбирают, — и говорит об этом черта слева, взявшая
   цвет работы. */
.peopleSaid[data-live="1"] .peopleText{ border-left-color:var(--accent); }

/* ── Пелена: окно занято работой ──

   Пока идёт расшифровка только что записанного, притушено всё рассказанное, а
   посреди него крутится кольцо. Знак привычный, и ждут его именно таким:
   поверх всего, на что смотрят, а не значком в строке, которой ещё нет. Слов
   у пелены нет — какая стадия идёт, говорит лестница под клавишей.

   Лежит она поверх видимой части рассказанного, а не поверх всей его длины:
   рассказ прокручивается, и кольцо, привязанное к концу списка, оказалось бы
   за нижним краем. Поэтому коробка, а не сам список: список внутри неё
   прокручивается, пелена стоит. */
.peopleTold{
  position:relative;
  display:grid;
  grid-template-rows:minmax(0,1fr);
  min-height:0;
  min-width:0;
}

.peopleTold[hidden]{ display:none; }

.peopleVeil{
  position:absolute;
  inset:0;
  display:flex;
  align-items:center;
  justify-content:center;
  background:color-mix(in srgb, var(--card) 72%, transparent);
}

.peopleVeil[hidden]{ display:none; }

.peopleSpin{
  width:28px;
  height:28px;
  border:2px solid var(--line-2);
  border-top-color:var(--accent);
  border-radius:50%;
  animation:peopleSpin .9s linear infinite;
}

@keyframes peopleSpin{
  from{ transform:rotate(0deg); }
  to{ transform:rotate(360deg); }
}

/* ── Диктофон в окне человека ──

   Тот же единственный диктофон, что на «Записи» (`views/_dictaphone.php`,
   `dictaphone.js`), только на другом месте: там он планкой у нижнего края
   листа, здесь — рядом окна. Всё, что для этого нужно, — перестать быть
   приклеенным к экрану; ни одной второй кнопки записи в продукте не
   заводится. */
.peopleDeck{
  min-width:0;
  border-top:1px solid var(--line-2);
  background:var(--card);
}

.peopleDeck[hidden]{ display:none; }

/* Вопрос стоит над клавишей: ответ обязан кому-то принадлежать, и рассказчик
   обязан видеть, на что он отвечает. */
.peopleAsked{
  padding:var(--s4) var(--s5) 0;
  font-family:var(--serif);
  font-size:var(--t-sm);
  line-height:1.5;
  color:var(--ink-2);
}

.peopleAsked[hidden]{ display:none; }

.people .dictaphone{
  position:static;
  z-index:auto;
  gap:var(--s2);
  padding:var(--s3) 0 0;
  background:none;
}

/* Внутри окна человека планка стоит в потоке и видна всегда. Видна — значит
   нажимается: `pointer-events` из общего правила закрытой планки здесь надо
   снять руками, иначе клавиша выглядит живой и не отвечает на нажатие. */
.people .dictaphone[data-open="0"]{ transform:none; opacity:1; pointer-events:auto; }

.people .deck{ max-width:none; border:0; border-radius:0; background:none; }

.people .key{ min-height:56px; }

/* Ряд действий внутри окна идёт без черты сверху: в покое он пуст — здесь
   некуда отвечать текстом и нечего откладывать, — и черта над пустотой
   читается как ещё одна пустая строка. Высоту он держит прежнюю, чтобы
   планка не подпрыгивала, когда в нём появляются «Отправить» и «Отменить». */
.people .deckActs{ border-top:0; }

.people .key:hover{ background:none; }

.people .key[data-on="1"]{ background:none; box-shadow:none; }

/* Лестница внутри окна лежит строкой: четыре ряда съели бы рассказ. Рейка с
   заливкой на это время уходит — они рисуют вертикаль, которой здесь нет, — а
   кружок ступени встаёт перед словом, в общий ряд. */
.people .ladder{
  display:flex;
  align-items:center;
  flex-wrap:wrap;
  gap:var(--s2) var(--s5);
  max-width:none;
  padding:0 var(--s5) var(--s4);
}

.people .ladderRail,
.people .ladderFill{ display:none; }

.people .rung{ flex:0 0 auto; white-space:nowrap; }

.people .rung::before{ position:static; left:auto; flex:0 0 auto; }

/* ── Узкий экран: половины становятся шагами. Обе рядом на телефоне не
     помещаются, а выбирать сферу вслепую — то же, что не выбирать. Шагов
     четыре, а не три: у человека здесь и рассказанное, и галочки, и на одном
     экране они не живут. ── */
@media (max-width:700px){
  .peopleTable{ grid-template-columns:minmax(0,1fr); height:min(620px, 82vh); }

  .peopleLeft{ border-right:0; }

  .peopleTable[data-step="people"] .peopleLeft{ display:none; }

  .peopleTable[data-step="story"] .peopleLeft{ display:none; }

  .peopleTable[data-step="spheres"] .peopleRight{ display:none; }

  .peopleTable[data-step="marks"] .peopleRight{ display:none; }

  .peopleBack{ display:flex; }

  .peopleBack[hidden]{ display:none; }

  /* Стрелки назад в заголовке столбца здесь нет: половины идут шагами, и
     отсюда назад ведёт своя дорога — «К рассказанному». Две стрелки подряд,
     ведущие в разные места, читались бы как одна. */
  .peopleShutBack{ display:none; }

  .peopleToMarks{ display:flex; }

  .peopleToMarks[hidden]{ display:none; }

  .peopleRight{ grid-template-rows:auto auto minmax(0,1fr) auto; }

  .peopleTable[data-who="1"] .peopleRight{ grid-template-rows:auto auto auto auto minmax(0,1fr) auto; }

  .peopleLeft[data-mine="1"]{ grid-template-rows:auto auto minmax(0,1fr) auto; }
}

/* Кому движение мешает, тому ярлычок не тает, а просто появляется и уходит:
   сказанное им от этого не меняется. Кольцо посреди рассказанного и точка у
   имени стоят неподвижно: что работа идёт, сказано и без вращения — самой
   пеленой и лестницей под клавишей. */
@media (prefers-reduced-motion:reduce){
  .peopleSaved{ transition:none; }

  .peopleSpin,
  .peopleOne[data-live="1"]::after{ animation:none; }

  /* Просившему покоя линия просто стоит нарисованной, а числа сразу свои:
     `rail.js` не запускает счёт, но правило нужно и здесь — на случай, если
     метка проявления осталась от прошлой отрисовки. */
  .lifeline[data-draw="1"] svg{ clip-path:none; animation:none; }

  .tallyGo{ transition:none; }
}


/* ==== Издательство (порт life-publishing.html): прочтения, сцена, книга.
   Экранов не было в life-editorial.html — блок собран из тех же токенов,
   как экраны входа и установки. ==== */

.pressChoices{ display:grid; grid-template-columns:repeat(auto-fill,minmax(260px,1fr)); gap:var(--s4,16px); margin-top:24px; }

.pressChoice{
  display:flex; flex-direction:column; align-items:flex-start; gap:8px;
  padding:18px; text-align:left;
  background:var(--card); border:1px solid var(--line-2); border-radius:10px;
  color:var(--ink); cursor:pointer; font:inherit;
}

.pressChoice:hover{ border-color:var(--accent); }

.pressChoice[data-own="1"]{ cursor:default; }

.pressChoiceFrame{ font-family:var(--sans); font-size:11px; letter-spacing:.08em; text-transform:uppercase; color:var(--muted); }

.pressChoiceTitle{ font-family:var(--serif); font-size:18px; line-height:1.35; }

.pressOwn{
  width:100%; min-height:72px; resize:vertical;
  font-family:var(--serif); font-size:16px; line-height:1.5; color:var(--ink);
  background:var(--sheet-2); border:1px solid var(--line); border-radius:8px; padding:10px;
}

.pressStages{ display:flex; flex-direction:column; gap:14px; margin:24px 0; }

.pressStage{ display:grid; grid-template-columns:14px 1fr; gap:12px; align-items:start; }

.pressLamp{ width:10px; height:10px; margin-top:5px; border-radius:50%; background:var(--line-2); }

.pressStage[data-state="done"] .pressLamp{ background:var(--muted); }

.pressStage[data-state="now"] .pressLamp{ background:var(--accent); animation:pressPulse 1.4s infinite; }

@keyframes pressPulse{ 50%{ opacity:.35; } }

.pressStage[data-state="next"] .pressActor,
.pressStage[data-state="next"] .pressDoing{ color:var(--muted); }

.pressActor{ font-family:var(--sans); font-weight:600; }

.pressDoing{ color:var(--ink-2); font-size:15px; }

.pressFeed{ display:flex; flex-direction:column; gap:8px; margin-top:12px; }

.pressFeedCap{ margin-top:14px; }

.pressFeedLine{ font-family:var(--serif); font-size:15px; line-height:1.5; color:var(--ink-2); }

.pressFeedWho{ font-family:var(--sans); font-size:12px; color:var(--muted); margin-right:8px; }

/* ── Собранная издательством книга на экране. ──
   Читается она тем же, чем читается рассказ на «Истории»: колонка в меру
   набора, антиква, тот же кегль чтения и та же навигация справа. Своего
   набора у книги ровно столько, сколько в ней есть своего: титул, оглавление
   и врезки верстальщика. Прежде здесь стояли пиксели и кегли мимо токенов —
   62ch, 17px, 14px полей, — и книга выпадала из темы, в которой всё остальное
   меряется одним набором величин. */
.pressBook{ display:grid; gap:var(--s7); max-width:var(--measure); }

/* Титул: слово редакции о себе самой. Курсив, мелкий кегль и приглушённый
   тон — набор колофона из эталона (docs/prototypes/life-publishing.html,
   `.bookNote`): это ещё не книга, а то, что сказано перед ней. */
.pressFront{
  display:grid;
  gap:var(--s3);
  font:italic 400 var(--t-sm)/1.6 var(--serif);
  color:var(--muted);
}

/* Оглавление — ход в книгу, а не перечень названий: строка ведёт к главе.
   Номер служебным набором слева, название антиквой; линейки сверху и снизу
   отделяют его и от титула, и от первой главы. */
.bookContents{
  display:grid;
  gap:var(--s4);
  padding:var(--s5) 0;
  border-top:1px solid var(--line);
  border-bottom:1px solid var(--line);
}

.bookContentsList{ list-style:none; margin:0; padding:0; display:grid; }

.bookContentsList a{
  display:flex;
  align-items:baseline;
  gap:var(--s4);
  padding:var(--s2) 0;
  text-decoration:none;
  color:var(--ink);
  font:400 var(--t-body)/1.4 var(--serif);
  transition:color var(--dur) var(--ease);
}

.bookContentsList a:hover{ color:var(--accent); }

.bookContentsList .folio{ flex:none; }

.bookContentsName{ text-wrap:pretty; }

/* Глава — раздел со своим якорем: к нему ведёт и оглавление, и рейка, и
   останавливается переход под липкой шапкой, а не за ней. */
.bookPart{
  display:grid;
  gap:var(--s5);
  scroll-margin-top:calc(var(--tap) + var(--s6));
}

/* Куски главы стоят рядами сетки, а не полями абзацев: каждый из них —
   отдельное поле правки, и расстояние между ними принадлежит главе, а не
   тому, что внутри поля. */
.bookProse{ display:grid; gap:var(--s4); font:400 var(--t-read)/1.72 var(--serif); color:var(--ink); }

/* Абзацы внутри поля — правка человека: `contenteditable` возвращает её
   абзацами, и они уже свои. Каретка и курсор поля общие для обеих книг и
   стоят при `.bookParagraph`. */
.bookProse .read p{ margin:0 0 var(--s3); }

.bookProse .read p:last-child{ margin-bottom:0; }

.bookProse .read ul,
.bookProse .read ol{ margin:0 0 var(--s3); padding-left:1.4em; }

.bookProse .read li{ margin-bottom:var(--s1); }

/* Подзаголовок внутри главы — курсивом, а не вторым заголовком того же
   кегля: имя главе даёт страница, и спорить с ним разделу нечем. Курсивная
   антиква — тот же приём, которым набран вопрос в «Истории». */
.bookSub{ font-style:italic; font-size:var(--t-read); line-height:1.4; margin-top:var(--s4); }

/* Врезка верстальщика: вынутая суть сказанного. Набор — из эталона
   (`.bookIns`): курсив, воздух, хайрлайны сверху и снизу, посередине полосы.
   Полоса акцента слева, стоявшая здесь прежде, звала как ссылка, хотя врезка
   никуда не ведёт. Подпись над цитатой — служебная строка, а не полужирный
   кусок самой цитаты. */
.bookQuote{
  margin:var(--s3) 0;
  padding:var(--s5) var(--s4);
  border-top:1px solid var(--line);
  border-bottom:1px solid var(--line);
  font:italic 400 var(--t-h2)/1.45 var(--serif);
  text-align:center;
  color:var(--ink-2);
}

.bookQuote p:first-child > strong:only-child{
  display:block;
  font:600 var(--t-cap)/1 var(--sans);
  letter-spacing:var(--track);
  text-transform:uppercase;
  color:var(--muted);
}

/* Название главы правится там же, где стоит: поле — только само имя, номер
   рядом с ним служебной строкой и правке не подлежит. Поле по ширине имени, а
   не строки: растянутое на всю меру набора, оно ловило бы щелчок далеко
   правее последней буквы. */
.bookPartName{ justify-self:start; max-width:100%; }

/* Черта внутри главы — хайрлайн во всю меру набора, как в эталоне. */
.bookRule{
  border:0;
  border-top:1px solid var(--line);
  margin:var(--s3) 0;
}

/* Ссылки под книгой — не её последняя строка: между концом текста и ими
   стоит тот же зазор, что между главами. На узком экране к нему прибавляется
   место под строку-адрес, которая висит над доком. */
.pressBook ~ .formActs{ margin-top:var(--s7); }

@media (max-width:1199.98px){
  .pressBook ~ .formActs{ margin-bottom:calc(var(--tap) + var(--s6)); }
}

/* Знак сомнения — охра, а не терракота. Терракотой в теме сказано «ты
   здесь» и «сейчас произойдёт»; место, ждущее визы рассказчика, — это
   пометка на полях, и в эталоне (`.visaMark`) она того же цвета, что ярлык
   «материала пока мало». Курсив врезки на знаке снимается: это не слово. */
.doubt{ color:var(--flag); font-weight:600; font-style:normal; cursor:help; }

/* ── Материалы: исследование, положенное рядом с рассказом. ──

   Экран собирается на сервере целиком, скрипта у него нет. Отсюда и облик:
   ни одного состояния, которое нужно было бы показывать движением, — строка
   времени слева, найденное справа, заметки под ним лесенкой.

   Факт набран тем же шрифтом, что и рассказ на других экранах: это чужой
   текст о той же жизни, и делать его мельче значило бы сказать, что он
   второго сорта. Заметка на полях — плашка, как в редакторе документов. */
.matAdd .way{ display:flex; flex-direction:column; gap:var(--s4); align-items:flex-start; }

.matSample{
  font:400 var(--t-sm)/1.6 var(--sans);
  white-space:pre-wrap;
  background:var(--sheet-2);
  border:1px solid var(--line);
  border-radius:var(--r2);
  padding:var(--s4);
  margin:var(--s3) 0;
  overflow-x:auto;
}

.matOne + .matOne{ margin-top:var(--s8); }

.matHead{ position:relative; }

.matDrop{ position:absolute; top:0; right:0; }

/* Полоса раздела внутри материала: линия времени, без времени, люди. */
.matBand{
  border-top:1px solid var(--line);
  padding-top:var(--s3);
  margin:var(--s6) 0 var(--s4);
}

.matLine{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:var(--s5); }

/* Запись: время слева, найденное справа. На узком экране время встаёт над
   строкой — колонка в четверть ширины телефона режет каждую вторую дату. */
.matRow{ display:grid; grid-template-columns:minmax(9ch,14ch) 1fr; gap:var(--s4); align-items:baseline; }

@media (max-width:719.98px){
  .matRow{ grid-template-columns:1fr; gap:var(--s2); }
}

.matWhen{
  font:600 var(--t-meta)/1.5 var(--sans);
  letter-spacing:var(--track);
  text-transform:uppercase;
  color:var(--ink-2);
}

/* Времени нет — и строка об этом молчит серым, а не кричит пустотой. */
.matWhenNone{ color:var(--muted); font-weight:400; text-transform:none; letter-spacing:0; }

.matWhat{ display:flex; flex-direction:column; gap:var(--s2); max-width:var(--measure); }

.matText{ font:400 var(--t-body)/1.5 var(--serif); color:var(--ink); margin:0; }

.matNote{
  background:var(--sheet-2);
  border:1px solid var(--line);
  border-radius:var(--r2);
  padding:var(--s3);
  display:flex;
  flex-direction:column;
  gap:var(--s2);
}

.matNoteHead{
  display:flex;
  gap:var(--s3);
  font:400 var(--t-meta)/1.4 var(--sans);
  color:var(--muted);
}

.matNoteWho{ color:var(--ink-2); font-weight:600; }

.matNoteText{ font:400 var(--t-sm)/1.6 var(--sans); color:var(--ink); margin:0; white-space:pre-wrap; }

.matNote textarea,
.matSay textarea{
  width:100%;
  font:400 var(--t-sm)/1.6 var(--sans);
  color:var(--ink);
  background:var(--card);
  border:1px solid var(--line);
  border-radius:var(--r2);
  padding:var(--s2);
  resize:vertical;
}

.matNoteActs{ display:flex; gap:var(--s3); }

/* Правка и удаление — одна строка на двоих: две формы рядом, потому что
   форма внутри формы разметкой невозможна, а кнопки стоят вместе. */
.matNoteEdit,
.matNoteDrop{ display:flex; gap:var(--s3); align-items:flex-end; }

.matSay summary{
  font:400 var(--t-meta)/1.4 var(--sans);
  color:var(--muted);
  cursor:pointer;
  letter-spacing:var(--track);
  text-transform:uppercase;
}

.matSay[open] summary{ margin-bottom:var(--s2); }

.matSay form{ display:flex; flex-direction:column; gap:var(--s2); align-items:flex-start; }

/* Метки записи. Решётка на экране не рисуется: в файле она — знак для
   разбора, а здесь метка уже стоит отдельно от текста, и повторять знак
   значит показывать человеку наш способ чтения вместо его слова. */
.matTags,
.matMarks{ display:flex; flex-wrap:wrap; gap:var(--s2); margin:0; }

.matMarks{ margin-bottom:var(--s5); }

.matTag{
  display:inline-flex;
  align-items:baseline;
  gap:var(--s2);
  font:400 var(--t-meta)/1.4 var(--sans);
  color:var(--ink-2);
  background:var(--sheet-2);
  border:1px solid var(--line);
  border-radius:var(--r2);
  padding:2px var(--s2);
}

.matTag b{ font-weight:600; color:var(--muted); }
