/* Смальта — стиль страницы.
   Палитра выведена из самой мозаики: цвета взяты из палитры смальты
   (см. data/mosaic.json), а не подобраны отдельно. */

@font-face {
  font-family: 'Old Standard TT';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/old-standard-cyrillic-400.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Old Standard TT';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/old-standard-latin-400.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F, U+2122, U+2191, U+2193, U+2212;
}
@font-face {
  font-family: 'Old Standard TT';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/old-standard-cyrillic-400i.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Old Standard TT';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/old-standard-latin-400i.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F, U+2122;
}
@font-face {
  font-family: 'Old Standard TT';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/old-standard-cyrillic-700.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Old Standard TT';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/old-standard-latin-700.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F, U+2122;
}

:root {
  --void:    #0b0a12;   /* фон страницы, темнее самого тёмного поля мозаики */
  --field:   #151420;   /* поле смальты */
  --rule:    #2a2436;   /* линии */
  --ink:     #d8cbb4;   /* текст */
  --muted:   #8a7f6d;   /* даты, служебное */
  --porphyry:#883b34;   /* порфир */
  --gold:    #c9a227;
  --gold-lit:#e8c86a;

  --serif: 'Old Standard TT', Georgia, 'Times New Roman', serif;
  --inset: clamp(14px, 2.6vw, 40px);
  /* На широком экране рама не растягивается на весь монитор, а садится на
     ширину самого портрета: она обозначает панель, а стена смальты идёт
     дальше и уходит за неё. Строку в три метра всё равно никто не прочтёт. */
  --inset-x: max(var(--inset), calc((100vw - 1760px) / 2));
  --hair: 1px;
  --ease: cubic-bezier(.22, .61, .36, 1);
  --slow: cubic-bezier(.16, .84, .28, 1);
}

*, *::before, *::after { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--void);
  color: var(--ink);
  font-family: var(--serif);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body {
  overflow: hidden;
  overscroll-behavior: none;
}

a { color: inherit; }

/* ── Сцена ──────────────────────────────────────────────────────────────── */

.stage {
  position: fixed;
  inset: 0;
  /* На телефоне адресная строка уезжает при прокрутке, и вьюпорт становится
     выше, чем был при раскладке. Слой, растянутый на 100%, за ним не тянется —
     снизу открывается полоса фона страницы. `lvh` — высота экрана с убранными
     панелями, то есть наибольшая: слой покрывает экран в любом состоянии.
     Браузер без `lvh` берёт предыдущую строку и остаётся при прежнем. */
  height: 100%;
  height: 100lvh;
  z-index: 0;
  background: var(--field);
}

.stage__canvas,
.stage__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

.stage__canvas { opacity: 0; transition: opacity 1.1s var(--ease); }
.stage.is-live .stage__canvas { opacity: 1; }

/* Постер — и заставка на время загрузки бинарника, и запасной вариант там,
   где WebGL нет.
   Кадрируется он так же, как холст, иначе на перекрёстном затухании портрет
   заметно прыгает в масштабе. Холст всегда показывает портрет по высоте, а
   бока досыпает смальтой; узкий экран при этом режет по бокам. Ровно это и
   делает пара cover/contain: contain на экране шире портрета даёт ту же
   высоту, а поля остаются цветом поля — их-то холст и заполнит тессерами. */
.stage__poster {
  object-fit: cover;
  object-position: 40% 39%;
  opacity: 1;
  filter: saturate(.92) brightness(.86);
  transition: opacity 1.2s var(--ease);
}
@media (min-aspect-ratio: 818 / 1100) {
  .stage__poster { object-fit: contain; object-position: 50% 50%; }
}
.stage.is-live .stage__poster { opacity: 0; }
.stage.is-fallback .stage__poster { opacity: 1; }

/* Виньетка сажает края в тень: у стенной мозаики свет всегда падает пятном. */
.stage__vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(120% 90% at 42% 36%, transparent 30%, rgba(6, 5, 11, .38) 74%, rgba(6, 5, 11, .72) 100%);
  transition: opacity .9s var(--ease);
}
body.is-reading .stage__vignette { opacity: .5; }

/* ── Рама ───────────────────────────────────────────────────────────────── */

.frame { position: fixed; inset: 0; z-index: 2; pointer-events: none; }

.frame__rule { position: absolute; background: var(--rule); opacity: .85; }

.frame__rule--top, .frame__rule--bottom {
  left: var(--inset-x); right: var(--inset-x); height: var(--hair);
  /* Блик обходит раму против часовой, в противоход свету на мозаике. */
  background-image: linear-gradient(90deg, transparent 0%, rgba(232, 200, 106, .55) 12%, transparent 26%);
  background-size: 260% 100%;
  animation: sheen-x 19s linear infinite;
}
.frame__rule--top { top: var(--inset); }
.frame__rule--bottom { bottom: var(--inset); animation-direction: reverse; }

.frame__rule--left, .frame__rule--right {
  top: var(--inset); bottom: var(--inset); width: var(--hair);
  background-image: linear-gradient(180deg, transparent 0%, rgba(232, 200, 106, .4) 12%, transparent 26%);
  background-size: 100% 260%;
  animation: sheen-y 19s linear infinite;
}
.frame__rule--left { left: var(--inset-x); animation-direction: reverse; }
.frame__rule--right { right: var(--inset-x); }

@keyframes sheen-x { from { background-position: -160% 0; } to { background-position: 160% 0; } }
@keyframes sheen-y { from { background-position: 0 -160%; } to { background-position: 0 160%; } }

/* Угловые засечки — единственный орнамент. Меандр по всему периметру был бы
   перегрузкой; хватает того, что углы обозначены. */
.frame__tick { position: absolute; width: 9px; height: 9px; border: var(--hair) solid var(--gold); opacity: .5; }
.frame__tick--tl { top: calc(var(--inset) - 4px); left: calc(var(--inset-x) - 4px); border-right: 0; border-bottom: 0; }
.frame__tick--tr { top: calc(var(--inset) - 4px); right: calc(var(--inset-x) - 4px); border-left: 0; border-bottom: 0; }
.frame__tick--bl { bottom: calc(var(--inset) - 4px); left: calc(var(--inset-x) - 4px); border-right: 0; border-top: 0; }
.frame__tick--br { bottom: calc(var(--inset) - 4px); right: calc(var(--inset-x) - 4px); border-left: 0; border-top: 0; }

/* ── Надпись ────────────────────────────────────────────────────────────── */

.band {
  position: fixed;
  z-index: 3;
  margin: 0;
  font-size: clamp(10px, 1.05vw, 12px);
  letter-spacing: .26em;
  text-transform: uppercase;
  color: var(--muted);
  opacity: 0;
  transition: opacity 1.4s var(--ease), transform 1.4s var(--ease);
}
body.is-ready .band { opacity: 1; }
/* В чтении надпись уходит: она держит мозаику, а не текст. */
body.is-reading .band { opacity: 0 !important; pointer-events: none; }
body.is-reading .frame { opacity: .35; transition: opacity .7s var(--ease); }

/* Имя канала стоит по центральной оси рамы, как посвятительная надпись над
   апсидой. Больше в полосе ничего нет: «послания» стояло справа и повторяло
   то, что и так сказано оглавлением. */
.band--top {
  top: 0; left: 0; right: 0;
  height: var(--inset);
  padding: 0 calc(var(--inset-x) + 14px);
  display: flex; align-items: center; justify-content: center;
  transform: translateY(-6px);
}
body.is-ready .band--top { transform: none; }

.band__name {
  display: inline-flex;
  align-items: center;
  gap: .8em;
  text-decoration: none;
  color: var(--ink);
  letter-spacing: .42em;
  transition: color .5s var(--ease), text-shadow .5s var(--ease);
}
.band__name:hover { color: var(--gold-lit); text-shadow: 0 0 18px rgba(232, 200, 106, .35); }
/* Значок канала — часть надписи, а не кнопка: тот же цвет, тот же кегль.
   Инлайновый SVG, потому что у страницы нет и не должно быть внешних запросов. */
.band__tg {
  width: 1.25em; height: 1.25em;
  flex: none;
  fill: currentColor;
  opacity: .85;
  transition: opacity .5s var(--ease);
}
.band__name:hover .band__tg { opacity: 1; }

/* Подчёркивание — единственное, что говорит, что надпись ведёт наружу:
   золотая волосяная линия под именем канала. Правый край подрезан на разрядку,
   иначе черта уезжает за последнюю букву. */
.band__channel { position: relative; padding: .4em 0; }
.band__channel::after {
  content: '';
  position: absolute;
  left: 0; right: .42em; bottom: 0;
  height: var(--hair);
  background: var(--gold);
  opacity: .6;
  transition: opacity .45s var(--ease), background .45s var(--ease);
}
.band__name:hover .band__channel::after,
.band__name:focus-visible .band__channel::after { opacity: 1; background: var(--gold-lit); }

/* Заголовок страницы для поиска и экранного чтения: на экране его роль играет
   сама мозаика, но документ без h1 — документ без имени. */
.a11y-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── Шторка с оглавлением ───────────────────────────────────────────────── */

/* Мозаика — тёмная и пёстрая, тонкая надпись по ней тонет. Поэтому у низа
   экрана лежит подложка: она гасит смальту ровно настолько, чтобы заголовки
   читались, и при этом не режет кадр горизонтальной линией. */
.dock {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 3;
  padding: 0 calc(var(--inset-x) + 14px) calc(var(--inset) - 4px);
  display: flex;
  flex-direction: column;
  background: linear-gradient(180deg, transparent, rgba(8, 7, 14, .5) 42%, rgba(8, 7, 14, .92));
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 1.4s var(--ease), transform 1.4s var(--ease);
}
body.is-ready .dock { opacity: 1; transform: none; }
body.is-reading .dock { opacity: 0; pointer-events: none; transition-duration: .5s; }
/* Открытая шторка гасит смальту под собой сильнее: список должен читаться,
   а не угадываться. */
body.is-toc-open .dock {
  background: linear-gradient(180deg, transparent, rgba(8, 7, 14, .84) 16%, rgba(8, 7, 14, .96));
  transition: opacity 1.4s var(--ease), transform 1.4s var(--ease), background .5s var(--ease);
}

/* Всё по центральной оси: рама симметрична, и кнопка, сдвинутая в угол,
   ломала эту симметрию. Последнее послание — строкой по центру, кнопка
   оглавления ровно под ним. */
.dock__bar {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .1em;
  padding-top: .7em;
  text-align: center;
}

/* Последнее послание видно всегда: главная задача полосы — не перечислить всё,
   а показать, что здесь вообще есть что читать. */
.dock__latest {
  display: inline-flex;
  align-items: baseline;
  gap: 1em;
  padding: .45em 0 .3em;
  position: relative;
  text-decoration: none;
  color: var(--ink);
  font-size: clamp(13px, .95vw + 4px, 16px);
  letter-spacing: .24em;
  text-transform: uppercase;
  transition: color .4s var(--ease);
}
.dock__latest::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: var(--hair);
  background: linear-gradient(90deg, var(--gold), rgba(201, 162, 39, .15));
  opacity: .45;
  transform-origin: 0 50%;
  transition: opacity .45s var(--ease), transform .55s var(--slow);
}
.dock__latest[hidden] { display: none; }
/* Открытая шторка снимает эту строку: она и так первая в списке, а рядом
   с ним превращается в дубль. */
body.is-toc-open .dock__latest {
  opacity: 0;
  pointer-events: none;
  transition: opacity .3s var(--ease);
}
.dock__latest:hover, .dock__latest:focus-visible { color: var(--gold-lit); outline: none; }
.dock__latest:hover::after, .dock__latest:focus-visible::after { opacity: 1; transform: scaleY(2); }
.dock__latest-date {
  color: var(--muted);
  font-size: .76em;
  letter-spacing: .18em;
  white-space: nowrap;
}

.dock__toggle {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: .8em;
  border: 0;
  background: none;
  cursor: pointer;
  font: inherit;
  font-size: clamp(10px, 1.05vw, 12px);
  letter-spacing: .26em;
  text-transform: uppercase;
  color: var(--muted);
  padding: .6em 0 .5em;
  transition: color .4s var(--ease);
}
.dock__toggle:hover, .dock__toggle:focus-visible { color: var(--gold-lit); outline: none; }

.dock__chev {
  width: 9px; height: 9px;
  border-right: var(--hair) solid currentColor;
  border-top: var(--hair) solid currentColor;
  transform: rotate(-45deg) translate(-1px, 1px);
  transition: transform .45s var(--slow);
}
.dock__toggle[aria-expanded="true"] .dock__chev { transform: rotate(135deg) translate(-1px, 1px); }

/* Подсказка: пришедшему впервые надо показать, что низ экрана — не подпись,
   а шторка, и она открывается. Кнопка трижды медленно поднимается и светлеет —
   это дыхание, а не мигание: резкий призыв к действию спорил бы со всем
   остальным на странице. Один раз на посетителя (флаг в localStorage). */
body.is-toc-hint .dock__toggle {
  color: var(--ink);
  animation: toc-hint 3.4s var(--slow) 3;
}
body.is-toc-hint .dock__chev { animation: toc-hint-glow 3.4s var(--slow) 3; }

@keyframes toc-hint {
  0%, 100% { transform: none; color: var(--muted); }
  32% { transform: translateY(-5px); color: var(--gold-lit); }
  64% { transform: none; color: var(--muted); }
}
@keyframes toc-hint-glow {
  0%, 100% { filter: none; }
  32% { filter: drop-shadow(0 0 7px rgba(232, 200, 106, .6)); }
  64% { filter: none; }
}

/* Панель растёт вверх. max-height, а не height: список заранее не известен,
   а анимировать auto нечем. */
.dock__panel {
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  transition: max-height .55s var(--slow), opacity .4s var(--ease);
}
body.is-toc-open .dock__panel {
  max-height: min(46vh, 30rem);
  overflow-y: auto;
  opacity: 1;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--rule) transparent;
}
body.is-toc-open .frame__rule--bottom { opacity: 0; }

.toc { list-style: none; margin: 0; padding: .4em 0 .2em; }
.toc__item + .toc__item { border-top: var(--hair) solid rgba(42, 36, 54, .8); }

/* Заголовок — по центру строки, дата прижата к правому краю. Три колонки,
   крайние равной ширины: пустая слева уравновешивает дату справа, и середина
   остаётся серединой независимо от длины даты. */
.toc__link {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: baseline;
  gap: .25em 1.2em;
  padding: .95em 0;
  position: relative;
  text-decoration: none;
  color: #c6b89f;
  transition: color .4s var(--ease);
}
/* Золотой штрих живёт в левой колонке — той самой, что уравновешивает дату.
   Поэтому он проявляется, ничего не сдвигая: заголовок остаётся по центру. */
.toc__link::before {
  content: '';
  grid-column: 1;
  justify-self: end;
  align-self: center;
  width: .45em; height: var(--hair);
  background: var(--gold);
  opacity: 0;
  transition: opacity .4s var(--ease);
}
.toc__link:hover, .toc__link:focus-visible { color: var(--gold-lit); outline: none; }
.toc__link:hover::before, .toc__link:focus-visible::before { opacity: .9; }
.toc__link[aria-current="page"] { color: var(--ink); }

.toc__title {
  grid-column: 2;
  font-size: clamp(15px, 1.1vw + 5px, 19px);
  letter-spacing: .1em;
  text-align: center;
  text-wrap: balance;
}
.toc__date {
  grid-column: 3;
  justify-self: end;
  /* Дата не переносится по слогам: скорее заголовок займёт две строки,
     а совсем в притык — сработает правило ниже и дата уйдёт под него. */
  white-space: nowrap;
  font-size: clamp(10px, 1vw, 12px);
  letter-spacing: .2em;
  text-transform: uppercase;
  color: #6d6455;
}

/* Узко — дата не жмётся к заголовку сбоку, а уходит под него: строка
   складывается в две, обе по центру. */
@media (max-width: 620px) {
  .toc__link { grid-template-columns: 1fr; }
  .toc__link::before { display: none; }
  .toc__title, .toc__date { grid-column: 1; justify-self: center; text-align: center; }
}

/* ── Чтение ─────────────────────────────────────────────────────────────── */

.reader {
  position: fixed;
  inset: 0;
  z-index: 4;
  opacity: 0;
  pointer-events: none;
  transition: opacity .7s var(--ease);
  /* Не глухая заливка: сквозь колонку должна оставаться видна смальта —
     иначе мозаика не отступает, а просто выключается. */
  background:
    radial-gradient(120% 90% at 50% 6%, rgba(11, 10, 18, .5), rgba(11, 10, 18, .84) 55%, rgba(9, 8, 15, .9));
  backdrop-filter: blur(2px) saturate(.8);
  -webkit-backdrop-filter: blur(2px) saturate(.8);
}
.reader[hidden] { display: block; }
body.is-reading .reader { opacity: 1; pointer-events: auto; }

.reader__scroll {
  height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--rule) transparent;
}
.reader__scroll::-webkit-scrollbar { width: 10px; }
.reader__scroll::-webkit-scrollbar-thumb { background: var(--rule); }

.reader__col {
  position: relative;
  /* Колонка растёт вместе с экраном и упирается в предел строки: ниже 34rem
     она рвёт длинные слова, выше 44rem глаз теряет начало следующей строки.
     На большом мониторе держать её узкой незачем — места хватает. */
  max-width: clamp(34rem, 48vw, 44rem);
  margin: 0 auto;
  padding: clamp(4rem, 12vh, 9rem) var(--inset) clamp(6rem, 18vh, 12rem);
}

/* Тень под самой колонкой: по краям экрана смальта остаётся видна, а под
   строками тон садится ровно настолько, чтобы лицо не спорило с текстом. */
.reader__col::before {
  content: '';
  position: absolute;
  inset: -6vh -14vw;
  z-index: -1;
  background: radial-gradient(58% 50% at 50% 50%, rgba(8, 7, 14, .82), rgba(8, 7, 14, .55) 62%, transparent 100%);
  pointer-events: none;
}

/* Свеча на полях: линия слева от колонки, светлое пятно на ней идёт
   за прокруткой. */
.reader__candle {
  position: absolute;
  top: clamp(4rem, 12vh, 9rem);
  bottom: clamp(6rem, 18vh, 12rem);
  left: calc(var(--inset) - 1.6rem);
  width: var(--hair);
  background: linear-gradient(180deg, transparent, var(--rule) 8%, var(--rule) 92%, transparent);
}
.reader__candle::after {
  content: '';
  position: absolute;
  left: -2px; width: 5px; height: 92px;
  top: var(--candle, 0%);
  transform: translateY(-50%);
  background: radial-gradient(closest-side, rgba(232, 200, 106, .85), rgba(232, 200, 106, 0));
  transition: top .18s linear;
}

/* Это ссылка на главную, а не кнопка: без JS она всё равно работает, а с JS
   перехватывается — гасит оверлей и раскрывает оглавление.
   Плашка, а не голая надпись: она лежит поверх текста, и под ней должен быть
   свой тон, иначе строки просвечивают сквозь буквы. */
.reader__close {
  position: fixed;
  left: calc(var(--inset-x) + 14px);
  top: calc(var(--inset) + 14px);
  z-index: 5;
  display: inline-flex;
  align-items: center;
  gap: .7em;
  border: var(--hair) solid var(--rule);
  background: rgba(11, 10, 18, .72);
  backdrop-filter: blur(6px) saturate(.8);
  -webkit-backdrop-filter: blur(6px) saturate(.8);
  text-decoration: none;
  color: var(--muted);
  font: inherit;
  font-size: clamp(10px, 1.05vw, 12px);
  letter-spacing: .26em;
  text-transform: uppercase;
  cursor: pointer;
  padding: .7em .95em .65em;
  opacity: 0;
  transform: translateY(-.7rem);
  pointer-events: none;
  transition: opacity .5s var(--ease) .25s, transform .5s var(--slow) .25s,
              color .4s var(--ease), border-color .4s var(--ease);
}
body.is-reading .reader__close { opacity: 1; transform: none; pointer-events: auto; }
/* Скольжение: вниз по тексту кнопка убирается за край, вверх — возвращается.
   Задержки на возврате нет: уходит она не спеша, приходит сразу. */
body.is-reading.is-back-away .reader__close {
  opacity: 0;
  transform: translateY(-.9rem);
  pointer-events: none;
  transition-delay: 0s;
}
.reader__close:hover, .reader__close:focus-visible {
  color: var(--gold-lit); border-color: var(--gold); outline: none;
}
/* С клавиатуры фокус может остаться на уехавшей кнопке — тогда она держится. */
body.is-reading .reader__close:focus-visible {
  opacity: 1; transform: none; pointer-events: auto;
}

/* ── Типографика послания ───────────────────────────────────────────────────── */

.epistle { font-size: clamp(17px, 1.15vw + 13px, 20px); line-height: 1.72; }

.epistle__head { margin-bottom: 3.2rem; }
.epistle__num {
  margin: 0 0 1.4rem;
  color: var(--porphyry);
  letter-spacing: .5em;
  font-size: .72em;
  text-transform: uppercase;
}
.epistle__title {
  margin: 0 0 .7rem;
  font-weight: 400;
  font-size: clamp(2rem, 5.2vw, 3.1rem);
  line-height: 1.12;
  letter-spacing: .01em;
  color: #e9dfcb;
}
.epistle__meta {
  margin: 0 0 2.6rem;
  color: var(--muted);
  font-size: .68em;
  letter-spacing: .24em;
  text-transform: uppercase;
}

.epigraph {
  margin: 0;
  padding-left: 1.4rem;
  border-left: var(--hair) solid var(--rule);
  color: var(--muted);
  font-style: italic;
  font-size: .92em;
  line-height: 1.6;
}
.epigraph p { margin: 0 0 .6em; }
.epigraph cite { font-style: normal; font-size: .82em; letter-spacing: .1em; color: #6d6455; }

.epistle p { margin: 0 0 1.5em; hanging-punctuation: first last; text-wrap: pretty; }
/* Первый абзац не крупнее прочих: вход в текст даёт тон, а не кегль —
   лишний размер спорит с заголовком и рвёт серую массу полосы. */
.epistle .lead { color: #e4d8c1; }

.epistle blockquote {
  margin: 2.4rem 0;
  padding: 0 1.6rem;
  border-left: 2px solid var(--porphyry);
  font-style: italic;
  color: #cbbda4;
}
.epistle blockquote p:last-child { margin-bottom: 0; }

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

.epistle h2, .epistle h3 {
  margin: 2.6rem 0 1rem;
  font-weight: 400;
  line-height: 1.24;
  color: #e2d6bf;
}
.epistle h2 { font-size: 1.5em; letter-spacing: .01em; }
.epistle h3 { font-size: 1.16em; letter-spacing: .06em; color: var(--ink); }

.epistle ul, .epistle ol { margin: 0 0 1.5em; padding-left: 1.4em; }
.epistle li { margin-bottom: .5em; }
.epistle li::marker { color: var(--porphyry); }
.epistle ul { list-style: none; padding-left: 1.1em; }
.epistle ul > li { position: relative; }
.epistle ul > li::before {
  content: '';
  position: absolute;
  left: -1.1em; top: .62em;
  width: .42em; height: var(--hair);
  background: var(--porphyry);
}

/* Ссылка в тексте не должна перебивать строку: подчёркивание тонкое и
   тёплое, цвет тот же, что у прозы, — светится только под курсором. */
.epistle p a:not([class]), .epistle li a:not([class]) {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: rgba(201, 162, 39, .5);
  text-decoration-thickness: 1px;
  text-underline-offset: .22em;
  transition: color .35s var(--ease), text-decoration-color .35s var(--ease);
}
.epistle p a:not([class]):hover, .epistle li a:not([class]):hover {
  color: var(--gold-lit);
  text-decoration-color: var(--gold-lit);
}

.epistle strong { font-weight: 700; color: #e6dac3; }
.epistle em { font-style: italic; }

.epistle code, .epistle pre {
  font-family: ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace;
  font-size: .82em;
}
.epistle code { color: #cbbda4; background: rgba(21, 20, 32, .8); padding: .1em .35em; }
.epistle pre {
  margin: 2rem 0;
  padding: 1rem 1.1rem;
  overflow-x: auto;
  background: rgba(11, 10, 18, .78);
  border-left: var(--hair) solid var(--rule);
  line-height: 1.55;
}
.epistle pre code { background: none; padding: 0; }

.epistle img { display: block; max-width: 100%; height: auto; margin: 2.4rem auto; }
.epistle figure { margin: 2.4rem 0; }
.epistle figcaption {
  margin-top: .7em;
  font-size: .74em;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Таблица прокручивается внутри своей обёртки: колонка чтения узкая, а сама
   страница разъезжаться вбок не должна. */
.epistle__wide { margin: 2.2rem 0; overflow-x: auto; overscroll-behavior-x: contain; }
.epistle table { width: 100%; border-collapse: collapse; font-size: .86em; }
.epistle th, .epistle td { padding: .55em .7em; border-bottom: var(--hair) solid var(--rule); text-align: left; }
.epistle th { font-weight: 400; color: var(--muted); letter-spacing: .12em; text-transform: uppercase; font-size: .82em; }

/* Соседние послания: тем же тоном, что оглавление, — это указатель,
   а не призыв. */
.epistle__nav {
  display: flex;
  flex-direction: column;
  gap: .9em;
  margin-top: 3.4rem;
  padding-top: 1.6rem;
  border-top: var(--hair) solid var(--rule);
  font-size: .78em;
  letter-spacing: .14em;
  text-transform: uppercase;
}
.epistle__nav-link {
  text-decoration: none;
  color: var(--muted);
  transition: color .4s var(--ease);
}
.epistle__nav-link:hover, .epistle__nav-link:focus-visible { color: var(--gold-lit); outline: none; }

.asterism {
  margin: 2.8rem 0 !important;
  text-align: center;
  color: var(--porphyry);
  letter-spacing: .6em;
  font-size: 1.3em;
}

.note-ref {
  font-size: .7em;
  vertical-align: super;
  line-height: 0;
  text-decoration: none;
  color: var(--porphyry);
  padding: 0 .15em;
  transition: color .3s var(--ease);
}
.note-ref:hover { color: var(--gold-lit); }

.notes {
  margin: 3.4rem 0 0;
  padding: 1.6rem 0 0 1.2rem;
  border-top: var(--hair) solid var(--rule);
  font-size: .8em;
  line-height: 1.6;
  color: var(--muted);
}
.notes li { margin-bottom: .9em; }
.notes li::marker { color: var(--porphyry); }
/* Маркдаун оборачивает сноску в абзац; отбивка абзаца здесь лишняя. */
.notes p { margin: 0 0 .4em; }
.notes p:last-child { margin-bottom: 0; }
.notes li::before { display: none; }
.note-back { text-decoration: none; color: var(--rule); }
.note-back:hover { color: var(--gold-lit); }

/* Концевой знак. На цвет линий он не годится: тот выбран, чтобы не мешать
   чтению, и знак в нём попросту не читается. Здесь нужна точка, а не
   разлиновка, — поэтому золото вполнакала и кегль вдвое против текста. */
.reader__end {
  margin: 4rem 0 0;
  text-align: center;
  font-size: clamp(3.4rem, 6vw, 5rem);
  line-height: 1;
  color: rgba(232, 200, 106, .88);
  text-shadow: 0 0 42px rgba(232, 200, 106, .45);
}

/* Абзацы поднимаются по одному — текст «выкладывается», как и мозаика. */
.reader__col .rise {
  opacity: 0;
  transform: translateY(14px);
  filter: blur(6px);
}
body.is-reading .reader__col .rise {
  opacity: 1;
  transform: none;
  filter: none;
  transition: opacity .8s var(--ease), transform .8s var(--slow), filter .8s var(--ease);
  transition-delay: var(--rise-delay, 0ms);
}

/* ── Загрузка ───────────────────────────────────────────────────────────── */

body.is-loading .band, body.is-loading .dock { opacity: 0; }

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

/* Узкий экран: рама остаётся, боковые линии сняты — на 360 px они съедают
   поля, а держать кадр им уже нечем. */
@media (max-width: 720px) {
  .frame__rule--left, .frame__rule--right { display: none; }

  .band--top { padding: 0 calc(var(--inset) + 4px); }

  .dock {
    padding: 0 calc(var(--inset) + 4px) calc(var(--inset) + 6px);
    background: linear-gradient(180deg, transparent, rgba(8, 7, 14, .72) 40%, rgba(8, 7, 14, .96));
  }
  .dock__bar { gap: .2em; }
  .dock__toggle-word { display: none; }
  /* Слово скрыто, остаётся стрелка — она и так по центру, перекос не нужен. */
  .dock__toggle { padding: .7em .6em .6em; }
  .dock__chev { width: 12px; height: 12px; }
  body.is-toc-open .dock__panel { max-height: min(58vh, 26rem); }
  .toc__link { padding: 1.05em 0; }

  .reader__col { max-width: none; padding-left: max(var(--inset), 1.15rem); padding-right: max(var(--inset), 1.15rem); }
  .reader__candle { display: none; }
  /* На телефоне кнопка садится к большому пальцу — вниз, к правому краю,
     и уезжает вниз же. */
  .reader__close {
    left: auto; right: calc(var(--inset) + 4px);
    top: auto; bottom: calc(var(--inset) + 6px);
    color: var(--ink);
    background: rgba(9, 8, 15, .92);
    padding: .85em 1.05em;
    box-shadow: 0 6px 24px rgba(0, 0, 0, .5);
    transform: translateY(.7rem);
  }
  body.is-reading.is-back-away .reader__close { transform: translateY(1.1rem); }
  .reader__close-word { display: none; }
}

/* Низкий ландшафт на телефоне: подложка тоньше и шторка ниже — высоты
   там и без того нет. */
@media (max-height: 480px) and (orientation: landscape) {
  .dock {
    padding-bottom: calc(var(--inset) - 2px);
    background: linear-gradient(180deg, transparent, rgba(8, 7, 14, .78));
  }
  .dock__bar { padding-top: .3em; }
  .dock__latest { padding: .3em 0 .25em; }
  body.is-toc-open .dock__panel { max-height: 62vh; }
  .toc__link { padding: .7em 0; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .12s !important;
  }
  .reader__col .rise { opacity: 1; transform: none; filter: none; }
  .frame__rule { background-image: none; }
}
