/* Монохром по §12.4: холод создают контраст и плотность, а не палитра.
   Ориентир — бумажная книга и iA Writer, не приложение самопомощи.
   Тёмная тема обязательна: с терапевтом разговаривают ночью. */

:root {
  --ink: #171614;
  /* Текст абзацев чуть бледнее заголовков (правка владельца): сплошные чернила на длинном
     чтении гудят, а разница тона сама делит страницу на «где я» и «что тут написано». */
  --body-ink: #3a3833;
  --paper: #faf9f6;
  --muted: #6b6862;
  /* Ссылка внутри текста — единственная роль, различаемая цветом (§12.4г): подчёркиваний
     в продукте нет, а ссылке, вплетённой в абзац, показать себя больше нечем. Тон тёплой
     палитры, взятый гуще; значения — те же, что в `packages/design-tokens`. */
  --link: #6d4f1d;
  /* Акцент знака (правка владельца): тон своей палитры, взятый светлее и насыщеннее
     `--link`. Ею красится только знак: в бледном виде и чернила, и густая ссылка
     читаются серым пятном, а марка обязана оставаться цветной. Тон бумаги акцент
     уважает: охра на тёплой, терракота на розовой — иначе на розовом листе стоял бы
     чужой жёлтый знак (правка владельца). */
  --accent: #b5822a;
  --rule: #ddd9d1;
  /* Карточка — одна форма на всю страницу: кадр ленты и рамка цены отличаются содержимым,
     а не углом и не тенью. Тень едва различима намеренно (правка владельца): она отделяет
     карточку от листа, но не превращает лист в панель приложения. */
  --card-radius: 14px;
  --card-shadow: 0 1px 2px rgba(23, 22, 20, 0.04), 0 10px 28px rgba(23, 22, 20, 0.05);
  --measure: 36.4rem;
  /* Боковое поле нажатия у кнопки-знака. Знаки не центрируются в боксе фиксированной
     ширины — бокс растёт по рисунку, а нажимать можно на это поле вокруг; ряд сдвигается
     на него влево, и левый край рисунка садится ровно на край колонки, каким бы ни был
     сам рисунок. Буква и кружок центрированием никогда не совпали бы: у текста своя
     ширина и свои боковые свесы. */
  --icon-inset: 0.5rem;
  /* Поле над локапом — единственное, что отделяет шапку от верхнего края листа. */
  --head-lift: 2rem;
  /* Боковой свес буквы: «A» начинается наклонной, и её левый край стоит по краю колонки
     раньше, чем это видит глаз. Кружкам компенсация не нужна — у них края нет. */
  --face-overhang: 0.14rem;
}

/* Тёмная тема — те же три состояния и те же значения, что в кабинете
   (`apps/web/src/tokens.css`, `theme.ts`): умолчание — система, а выбор человека
   атрибутом `data-theme` перебивает её в обе стороны. Значения продублированы, а не
   импортированы: лендинг живёт отдельным Worker'ом и токенов клиента не грузит. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ink: #e8e6e1;
    --body-ink: #c9c6c0;
    --paper: #141312;
    --muted: #8e8a83;
    --link: #d0ac6c;
    --accent: #d8ae63;
    --rule: #2c2a27;
    /* Ночью тень не читается и подделывать её свечением нельзя: кадр перестал бы быть
       бумагой. Остаётся рамка. */
    --card-shadow: none;
  }
}

:root[data-theme="dark"] {
  --ink: #e8e6e1;
  --body-ink: #c9c6c0;
  --paper: #141312;
  --muted: #8e8a83;
  --link: #d0ac6c;
  --accent: #d8ae63;
  --rule: #2c2a27;
  --card-shadow: none;
}

/* Golos Text §12.10 — тот же шрифт, что в клиентах и на карусели сторов; собирается
   `python3 scripts/build-fonts.py`. Копия своя, а не ссылка на app.epimelea.com: лендинг
   и клиент — разные проекты Cloudflare, и общий файл связал бы их деплои. */
@font-face {
  font-family: "Golos Text";
  src: url("/fonts/golos-text-regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Golos Text";
  src: url("/fonts/golos-text-medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

* { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  /* Лента кадров выходит из колонки в поля окна и на ширину полосы прокрутки промахивается
     мимо края: `clip` съедает этот промах, не заводя странице горизонтальной прокрутки. */
  overflow-x: clip;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--body-ink);
  font-family: "Golos Text", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  /* Кегль крупнее системного: в тревоге падает способность читать мелкое (§12.4).
     Один и тот же на всех экранах (правка владельца) — телефон здесь не исключение. */
  font-size: 17px;
  line-height: 1.41;
  font-kerning: normal;
  text-rendering: optimizeLegibility;
}

.page {
  position: relative;
  max-width: var(--measure);
  margin: 0 auto;
  padding: var(--head-lift) 1.5rem 5rem;
}

header {
  position: relative;
  /* Первый экран стоит просторнее остального листа (правка владельца): здесь одна фраза
     и одна кнопка, и воздух вокруг них — единственное, чем страница показывает, что
     торопиться некуда. Воздух этот стоит **под** локапом, а не над ним: сам локап сидит
     у верхнего края листа (правка владельца), и поле над ним держит только `--head-lift`. */
  padding: 0.5rem 0 1rem;
  /* Воздуха под первым экраном стало меньше: под обещанием теперь стоит строка о том,
     что это за терапия, и следом раздел, а не сразу лента. Прежние 4.5rem оставляли между
     нотой и первой карточкой поле в треть телефонного экрана — на нём страница читалась
     недогруженной, а не спокойной. */
  margin-bottom: 2.6rem;
}

/* Знак (§12.10, эталон — assets/brand/epimelea-mark.svg). Инлайном и на currentColor:
   в тёмной теме он обязан перевернуться вместе с текстом, чего внешний <img> не умеет.
   Геометрия канонизирована и правится только в эталоне. */
.mark {
  display: block;
  /* Круглый знак весит на глаз меньше буквы того же роста, и рядом со словом локапа он
     обязан быть заметно крупнее кегля (§12.76). */
  width: 2.1rem;
  height: 2.1rem;
  color: var(--ink);
  flex: none;
}

/* В шапке юридической страницы знак стоит в строке с названием — она там ссылка домой,
   а не заголовок, и блочный знак разорвал бы её на две строки. */
.note .mark {
  display: inline-block;
  width: 1.35em;
  height: 1.35em;
  margin: 0 0.45em -0.15em 0;
}

/* Заголовок набран крупнее прозы, и интерлиньяж прозы на нём читается провалом между
   строками: чем крупнее кегль, тем плотнее строки. Своё значение у обещания (`.lede`)
   ниже — оно крупнее прочих заголовков. */
h1,
h2,
h3,
.lede {
  color: var(--ink);
  line-height: 1.2;
}

h1 {
  font-size: 2.1rem;
  /* Medium §12.10: Regular крупным кеглем на светлой бумаге бледнеет. */
  font-weight: 500;
  letter-spacing: 0.02em;
  margin: 0 0 0.6rem;
}

/* Локап: знак, за ним слово — одной строкой, как в шапке приложения (`Head`, §12.10).
   Слово набрано текстовым кеглем и Regular: это подпись знака, а не заголовок страницы.
   Заголовком работает обещание под ним — название человек уже прочитал в адресной строке. */
.brand {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  /* Кегль слова равен кеглю прозы — как в приложении, где локап набран `--text-body`.
     Мельче он читался бы подписью-мелочью, крупнее — заголовком, которым не является. */
  font-size: 1.1875rem;
  font-weight: 400;
  letter-spacing: 0.06em;
  line-height: 1;
  margin: 0 0 3.4rem;
}

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

   Вторая строка на узком экране всё же переносится сама, и `pretty` не даёт ей оставить
   одно слово внизу. */
.lede {
  margin: 0;
  font-size: 1.6rem;
  line-height: 1.18;
  letter-spacing: -0.01em;
  text-wrap: pretty;
}

/* Дескриптор: что это, с кем и в какой форме — одной строкой под обещанием (§11.5а).
   Обещание говорит о состоянии человека и категорию не называет: без этой строки первый
   экран одинаково подходит дневнику, анонимному форуму и линии доверия, а слово «терапия»
   читатель находит только на кнопке. Набран прозой и тоном текста, а не заголовка: второй
   крупной строки на экране быть не должно, иначе она спорит с обещанием. */
.claim {
  margin: 0.9rem 0 0;
  font-size: 1.0625rem;
  line-height: 1.45;
  /* Колонка уже меры листа: строка держится в две-три строки набора и не растягивается
     на всю ширину десктопа, где она читалась бы подзаголовком во весь экран. */
  max-width: 30rem;
  text-wrap: pretty;
}

/* Остальной набор страницы: заголовки и короткие строки делятся поровну (`balance`),
   абзацы просто не оставляют висячего слова в конце (`pretty`). Оба свойства только
   улучшают перенос там, где браузер их знает, и ничего не ломают там, где нет. */
h1,
h2,
.price,
.act .note,
.plan li {
  text-wrap: balance;
}

.page p,
.page li {
  text-wrap: pretty;
}

/* Заголовок разделов — тем же тоном, что текст, и крупнее его (правка владельца).
   Капитель бледным серым читалась как служебная подпись: заголовок весил меньше абзаца,
   который он открывает, и глаз не находил, где начинается раздел. */
h2 {
  font-size: 1.35rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--ink);
  margin: 3rem 0 0.8rem;
}

p { margin: 0 0 1.1rem; }

ul {
  margin: 0 0 1.1rem;
  padding-left: 1.1rem;
}

li { margin-bottom: 0.5rem; }

a {
  color: var(--link);
  text-decoration: none;
}

em { font-style: italic; }

/* Греческого в Golos Text нет ни одного знака (проверено по cmap), и с переходом на него
   строка этимологии поехала в последний-по-скрипту шрифт системы — то есть в антикву,
   посреди гротескового абзаца. Своего греческого подмножества ради двух слов на одной
   странице заводить незачем; здесь названы системные гротески, у которых есть политоника
   (ἐ, ἑ, ῦ): у «Helvetica» и «Arial» её нет, поэтому одного `sans-serif` в стеке не хватило. */
.greek {
  font-family: "Lucida Grande", "Segoe UI", "Noto Sans", sans-serif;
  font-style: normal;
  white-space: nowrap;
}

.note {
  color: var(--muted);
  font-size: 0.94rem;
  line-height: 1.36;
}

/* Локап юридической страницы — тот же, что на главной, и ведёт на главную. Ссылка
   не подчёркивается: это знак издания, а не место в тексте. */
.brand a {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  color: inherit;
  text-decoration: none;
}

/* В шапке юридической страницы за локапом сразу идёт текст, и второй отступ здесь лишний:
   расстояние до заголовка держит сам header. */
.legal header .brand { margin-bottom: 0; }

.legal h1 {
  margin-bottom: 1.6rem;
}

.legal h2 { margin-top: 2.6rem; }

footer {
  margin-top: 4rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--rule);
  color: var(--muted);
  font-size: 0.94rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.4rem;
}

/* Языки повторяются в подвале: знак в углу видит открывший страницу, а дочитавший до конца
   уже увёл его за край экрана. Знак тот же — планета, — но рядом с ним стоит название языка,
   на котором человек читает: в углу листа спрашивать «какой сейчас?» незачем, а в подвале
   это первое, что нужно знать перед выбором. Список раскрывается вверх: под подвалом
   страницы нет. */
/* Переключатели вида — над языком, в той же подвальной группе: и то и другое настройка
   чтения, а не часть предложения. */
.views {
  display: flex;
  align-items: center;
  /* Зазор между знаками держат их собственные поля нажатия. */
  gap: 0;
  /* Ряд равняется по рисунку, а не по краю нажатия: он уходит влево ровно на боковое
     поле кнопки, и первый знак садится на край колонки — туда же, где стоит планета
     языка ниже. */
  margin: 4rem 0 0 calc(-1 * var(--icon-inset));
}

.views + .langs.foot { margin-top: 0.6rem; }

.langs.foot {
  position: relative;
  top: auto;
  right: auto;
  margin-top: 4rem;
  font-size: 0.94rem;
}

/* Планета подвала стоит рисунком на краю колонки, а поле нажатия выступает за край
   влево — тем же полем, каким сдвинут ряд переключателей вида над ней. */
.langs.foot > summary {
  width: auto;
  height: auto;
  margin: 0 0 0 calc(-1 * var(--icon-inset));
  padding: 0.4rem var(--icon-inset);
  justify-content: flex-start;
  gap: 0.5rem;
}

.langs.foot > summary svg {
  width: 1.3rem;
  height: 1.3rem;
}

.langs.foot > ul {
  top: auto;
  bottom: 100%;
  right: auto;
  left: 0;
  margin-bottom: 0.4rem;
}

/* Подвал идёт сразу за переключателем, и второй отступ сверху здесь лишний. */
.langs.foot + footer { margin-top: 1.4rem; }


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

   Знак — планета, тот же, что на входе в приложение (`Globe.tsx`): флаг называет страну,
   а не язык (чей флаг у испанского?), а код «RU» читается только тем, кто и так справится.
   На <details> — без единой строки JS: в разметке остаются обычные ссылки, и hreflang
   с обходом поисковиком не страдают. */
/* Планета стоит в строке локапа (правка владельца): она в шапке одна, и висеть ей отдельной
   строкой над названием не за чем. Высота равна знаку — по нему кнопка и центрируется;
   вправо она выступает за колонку на своё поле нажатия, чтобы у края листа стоял рисунок,
   а не край кнопки. */
.corner {
  position: absolute;
  /* Угол стоит ровно в строке локапа: знак и планета одного роста, и планета садится
     на ту же линию, что знак с названием, — на верхнее поле шапки. */
  top: 0.5rem;
  right: -0.65rem;
  z-index: 3;
  display: flex;
  align-items: center;
  height: 2.1rem;
}

.langs {
  position: relative;
}

/* Вылет за край держит теперь угол целиком, а не каждый знак по отдельности. */
.corner .langs > summary { margin: 0; }

/* Переключатель гарнитуры (правка владельца). Знак — буква той гарнитуры, в которую
   он переключит: подписи ему не нужно, а прочитать чужую букву можно и не зная слова.
   Кнопка рисуется скриптом (`site.js`): без него переключать нечем, и пустого знака
   на странице оставаться не должно. */
.typeface {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  margin: 0;
  padding: 0.7rem var(--icon-inset);
  border: none;
  background: none;
  color: var(--muted);
  font-size: 1.35rem;
  line-height: 1;
  cursor: pointer;
}

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

/* Знак гарнитуры — буква, а не рисунок, и по краю колонки она равняется на свой свес. */
.typeface.face { padding-left: calc(var(--icon-inset) + var(--face-overhang)); }

/* Знак тона бумаги — кружок этого самого тона, обведённый его же краем: цвет здесь
   и есть название, а обводка держит форму — бумага почти белая, и без края кружок
   растворился бы в листе.
   Размер меньше, чем у соседей: сплошная залитая форма при равной высоте читается
   крупнее рисунка из линий. */
.typeface.tone::before {
  content: "";
  width: 1.15rem;
  height: 1.15rem;
  border-radius: 50%;
  background: var(--tone);
  border: 1px solid var(--tone-edge);
}

/* У знака темы рисунок, а не буква: размер задаётся ему, а не кеглю кнопки. Он же самый
   разреженный из трёх — тонкая линия при равной высоте кажется мельче заливки. */
.typeface.theme svg {
  width: 1.4rem;
  height: 1.4rem;
}

/* Антиква — прежний канон страницы (`VISION §12.10`), оставленный читателю на выбор:
   решение «гротеск» принято за продукт, но лендинг человек читает, а не работает в нём.
   Шрифт системный, ни одного килобайта загрузки. */
:root[data-face="serif"] body {
  font-family: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
}

.langs > summary {
  display: flex;
  align-items: center;
  justify-content: center;
  /* Поле нажатия по минимуму сенсорной цели, при том что рисунок вдвое меньше. */
  width: 2.75rem;
  height: 2.75rem;
  /* Рисунок стоит вплотную к правому краю колонки, а поле нажатия выступает за неё:
     иначе знак кажется отступившим от края сильнее, чем текст. */
  margin: -0.7rem -0.85rem -0.7rem 0;
  cursor: pointer;
  list-style: none;
  color: var(--muted);
}

.langs > summary:hover { color: var(--ink); }

.langs > summary svg {
  width: 1.35rem;
  height: 1.35rem;
}

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

.langs > ul {
  position: absolute;
  top: 100%;
  right: 0;
  margin: 0;
  padding: 0.4rem 0;
  min-width: 10rem;
  list-style: none;
  background: var(--paper);
  border: 1px solid var(--rule);
}

.langs > ul li { margin: 0; }

.langs > ul a {
  display: block;
  padding: 0.3rem 0.9rem;
  text-decoration: none;
  white-space: nowrap;
}

.langs > ul a { color: var(--ink); }

.langs > ul a[aria-current] { color: var(--muted); }

.langs > ul a:hover,
.langs > ul a:focus {
  color: var(--link);
}

/* Действие: кнопка и строка под ней. Кнопка на лендинге одна и та же в двух местах —
   вверху для решившего и в конце для дочитавшего; второго вида кнопки на странице нет. */
.act {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1rem;
  margin: 2.4rem 0 0;
}

.cta {
  display: inline-block;
  padding: 0.7rem 1.5rem;
  background: var(--ink);
  color: var(--paper);
  text-decoration: none;
  /* Тот же радиус, что у кнопки в приложении (`radius.pill`, `packages/design-tokens`):
     страница и кабинет — одна вещь, и форма кнопки не имеет права различаться.
     Пилюля — та же порода формы, что у круглого знака. */
  border-radius: 999px;
  font-weight: 500;
  letter-spacing: 0.01em;
}

.cta:hover { opacity: 0.86; }

/* Кадры витрины сторов: горизонтальная лента с прилипанием, прокручивается пальцем
   и колесом. Без JS и без стрелок — карусель со скриптом весила бы больше всей страницы,
   а стрелки на телефоне всё равно не нужны.
   Лента выходит за колонку текста в поля листа: кадр — картинка, и мера строки ему
   не указ, а обрезанный по краю следующий кадр и есть приглашение листать. */
.frames {
  display: flex;
  /* Зазор мал намеренно: нить выходит из кадра на том же уровне, на каком входит
     в следующий, и широкий промежуток рвал бы единственную линию, ради которой
     заголовки стоят на разной высоте. */
  gap: 0.5rem;
  /* Отрицательные поля равны полям листа: лента идёт от края до края колонки, а на
     телефоне — от края до края экрана. `vw` здесь не годится: он считает и полосу
     прокрутки, то есть иногда выносит ленту за окно вместе со всей страницей. */
  /* Вертикальные поля ленты — место для тени кадров. `overflow-x: auto` заставляет
     браузер считать и `overflow-y` прокручиваемым: снизу тень обрезалась краем ленты,
     а `overflow-y: visible` рядом с прокруткой по горизонтали невозможен. Снизу поле
     больше верхнего: тень смещена вниз (`--card-shadow`, 10px сдвига и 28px размытия),
     и равные поля срезали её нижний край. Внешние отступы уменьшены на ту же величину,
     чтобы лист не разъехался. */
  margin: 1.2rem -1.5rem -1.15rem;
  padding: 1rem 1.5rem 2.25rem;
  overflow-x: auto;
  /* `proximity`, а не `mandatory`: обязательное прилипание перехватывает ленту посреди
     выката и превращает движение в ступеньки (правка владельца). Здесь оно только
     доводит кадр, оказавшийся почти на месте.
     Ничего не выделяется: лента тащится мышью, а выделение текста и картинки —
     то самое, чем браузер отвечает на протяжку по умолчанию. */
  scroll-snap-type: x proximity;
  user-select: none;
  -webkit-user-select: none;
  /* Полосы прокрутки под лентой нет намеренно: она здесь единственный элемент управления,
     и рисовать её значит объяснять человеку устройство страницы. Гасить правый край
     градиентом пробовали — в прокручиваемом боксе маска едет вместе с содержимым
     и не видна вовсе (живой прогон). */
  scrollbar-width: none;
}

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

/* Распорки по краям ленты: без них крайние кадры упираются в границу окна, и первый
   с последним читаются в углу экрана, а не там, куда смотрит глаз (правка владельца).
   Ширина — ровно столько, чтобы крайний кадр доводился до середины окна; на узком
   экране она обнуляется, и лента идёт от края до края, как прежде.
   Начальное положение ленты выставляет `site.js`: страница открывается на первом кадре
   у левого края, а левая распорка — запас хода назад, а не пустое поле при загрузке. */
.frames::before,
.frames::after {
  content: '';
  flex: none;
  width: max(0px, 50vw - 13rem);
}

/* Курсор-ладонь — единственное приглашение тащить: подписи «листайте» на странице нет
   и быть не должно. Без скрипта класс не появляется, и ладонь не обещает того, чего нет. */
.frames { cursor: grab; }

/* Пока лента в руке и пока она катится по инерции, прилипания нет вовсе: иначе оно тянет
   кадр к себе одновременно с рукой, и обе силы читаются рывками. */
.frames.holding {
  cursor: grabbing;
  scroll-snap-type: none;
}

.frames.holding .frame { pointer-events: none; }

/* Кадр — живая вёрстка, а не картинка (`scripts/build-frames.mjs`): текст читается поиском
   и голосом, набирается тем же шрифтом и кеглем, что страница, и не весит полутора мегабайт
   на семь языков. `container-type: size` оставлен ради `cqw`/`cqh`: полями и посадкой
   утверждения кадр по-прежнему повторяет кадры сторов, набором — страницу.

   Обе величины подобраны замером самого длинного кадра на всех семи языках: текст обязан
   помещаться в кадр целиком, а не обрезаться его нижним краем. Доля окна велика намеренно —
   на телефоне кадр ужат по экрану, строка там короче, строк выходит больше, и при 82 vw
   французский и русский вылезали за край. */
.frame {
  position: relative;
  flex: none;
  /* Размер кадра больше не считается от окна (правка владельца). Пока набор внутри мерился
     долями кадра, зум был осмыслен: кадр рос, и текст рос с ним. С типографикой листа кадр
     обязан вмещать её, а не задавать, — и одна константа делает то, ради чего прежде стояли
     `svh`, `clamp` и отдельное правило для телефона. Ширина в `rem` держит внутри строку
     телефонной длины; на узком экране кадр ужимается по окну. */
  width: min(23rem, 90vw);
  /* Пропорция 2:3, а не кадр витрины 9:19.5 (правка владельца): под набор страницы
     телефонная вытяжка оставляла полкадра пустой бумаги под текстом. */
  aspect-ratio: 2 / 3;
  container-type: size;
  overflow: hidden;
  scroll-snap-align: center;
  border: 1px solid var(--rule);
  border-radius: var(--card-radius);
  box-shadow: var(--card-shadow);
  /* Кадр держит полярность страницы (правка владельца): ночью восемь светлых листов
     в тёмном тексте — это восемь фонарей.

     Кадр одного тона с листом (правка владельца): ни наращивания цвета по ленте,
     ни деления кадра на две цветовые части. От листа его отделяет рамка, а не заливка. */
  background: var(--paper);
  color: var(--body-ink);
}

/* Локап первого кадра: знак и слово в углу, теми же пропорциями, что в шапке приложения. */
.frame-brand {
  position: absolute;
  top: 6.9cqw;
  left: 7.5cqw;
  display: flex;
  align-items: center;
  gap: 0.4em;
  margin: 0;
  font-size: 4.07cqw;
  letter-spacing: 0.06em;
  line-height: 1;
}

.frame-brand .mark {
  width: 1.45em;
  height: 1.45em;
}

/* Сквозная нить (`tools/thread_overlay.py`): входит слева на своём уровне и уходит на уровень
   следующего кадра по всей ширине, без прямых участков. На стыке карточек уровни и касательные
   совпадают, поэтому в ленте она читается одной линией, идущей через все восемь. */
.frame-thread {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.frame-thread .line {
  fill: none;
  /* Нить не красится — она бледнеет (правка владельца): цвет один, чернила страницы,
     а прозрачность растёт вдоль ленты. Значения прозрачности стоят в самой разметке
     (`build-frames.mjs`), и потому переход непрерывен через все восемь кадров. */
  stroke-width: var(--thread, 3.5px);
  vector-effect: non-scaling-stroke;
}

.frame-thread stop { stop-color: var(--ink); }

:root[data-face="serif"] .frame-thread .line {
  --thread: 2px;
}

/* Утверждение садится под нить: высоту каждому кадру задаёт она, отсюда и разная
   посадка текста. Значение приезжает из конфига кадров переменной. */
.frame-say {
  position: absolute;
  top: var(--top);
  left: 7.5cqw;
  right: 7.5cqw;
}

/* Набор кадра — тот же, что на листе (правка владельца): те же кегли, тот же интерлиньяж,
   те же межбуквенные. Доли высоты кадра (`cqh`) здесь были ради совпадения с кадрами сторов,
   но на странице они давали второй, чужой типографике масштаб: одна и та же мысль читалась
   в ленте крупнее и плотнее, чем в абзаце под ней. Утверждение равно заголовку раздела,
   пояснение — обычному абзацу. */
.frame-say h3 {
  margin: 0;
  font-size: calc(1.35rem * var(--scale, 1));
  font-weight: 500;
  letter-spacing: 0.01em;
  text-wrap: balance;
}

.frame-say p {
  margin: 1.1rem 0 0;
  font-size: calc(1em * var(--scale, 1));
  color: var(--muted);
}

/* Последний кадр зовёт войти, и знак на нём — подпись под приглашением, а не колофон. */
.frame-door {
  position: absolute;
  left: 7.5cqw;
  bottom: 7.5cqw;
  margin: 0;
  /* Знак крупный и бледный, и чернилами он читался серым пятном. Красится акцентом
     своей палитры, но остаётся водяным знаком (правка владельца): он подпись под
     приглашением, а не второй логотип на кадре. */
  color: var(--accent);
  opacity: 0.1;
}

.frame-door .mark {
  /* Общее правило знака красит его чернилами, и `currentColor` внутри svg брался оттуда,
     а не от родителя: акцент до рисунка не доходил вовсе. */
  color: inherit;
  width: 20.9cqw;
  height: 20.9cqw;
}

/* Цена. Обведена рамкой, потому что это единственное место страницы, где human ищет
   не мысль, а число, — и находить его должен глазом, не чтением. */
.plan {
  border: 1px solid var(--rule);
  border-radius: var(--card-radius);
  box-shadow: var(--card-shadow);
  padding: 1.6rem 1.5rem 1.4rem;
  margin-bottom: 1.1rem;
}

.price {
  font-size: 2.2rem;
  font-weight: 500;
  line-height: 1.1;
  margin: 0 0 1rem;
}

.price span {
  font-size: 1rem;
  font-weight: 400;
  color: var(--muted);
  letter-spacing: 0.01em;
}

.plan ul { margin-bottom: 0; }

.plan .act { margin-top: 1.4rem; }

.plan .note { margin: 1.2rem 0 0; }

/* Бейджи магазинов. Правила Apple и Google запрещают перерисовывать знаки, поэтому
   единственный рычаг — размер бокса: в ряд они уравнены по высоте, в столбик — по ширине
   (правка владельца). Оба файла заполнены рисунком до края, так что равный бокс и есть
   равный знак. */
.stores {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.8rem 1rem;
}

.badge {
  display: block;
  width: auto;
  height: 2.75rem;
}

/* Третий знак — наш, и потому нарисован здесь, а не скопирован файлом: он повторяет
   пропорции соседей (высота бейджа, тот же радиус), но говорит своё — за ним не магазин,
   а дверь, открывающаяся на месте. Цвета взяты у листа: в тёмной теме он светлеет вместе
   с белым лок-апом Apple и не остаётся чёрным пятном. */
.badge.web {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0 0.95rem;
  width: auto;
  background: var(--ink);
  color: var(--paper);
  border-radius: 8px;
  text-decoration: none;
}

/* Иконка двери: наше окно (`svg`) или фирменный знак браузера, который скрипт ставит
   на его место (`img`). Размер один на оба — знак браузера не должен спорить величиной
   с яблоком и треугольником соседей. */
.badge.web svg,
.badge.web img {
  width: 1.35rem;
  height: 1.35rem;
  flex: none;
}

.badge.web span {
  display: flex;
  flex-direction: column;
  line-height: 1.12;
}

.badge.web small {
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.82;
}

.badge.web b {
  font-size: 1.05rem;
  font-weight: 500;
  letter-spacing: 0.01em;
}

.badge.web:hover { opacity: 0.86; }

/* Знаки магазинов погашены и не нажимаются: приложений в сторах пока нет, и знак,
   ведущий в пустоту, обманывает раньше, чем человек дочитает оговорку под ним. Живой знак
   на странице один — наша дверь, и она стоит первой. */
.soon {
  display: block;
  opacity: 0.42;
  pointer-events: none;
}

/* Строка под знаками: два приложения ещё не вышли, и знать об этом человеку надо до того,
   как он нажмёт. Тон тише самих знаков — это оговорка, а не предложение. */
.stores + .note { margin-top: 0.9rem; }

/* На десктопе лента идёт во всю ширину окна, а не в мере текста (правка владельца):
   кадр — снимок телефона, и в колонке шириной в абзац их видно полтора. Ширину даёт
   выход из колонки в поля листа: кадры идут от края окна до края окна, а обрезанный
   по краю следующий по-прежнему приглашает листать.
   `50% - 50vw` считает от ширины колонки: это ровно расстояние от её края до края окна. */
@media (min-width: 481px) {
  /* Строка на десктопе длиннее, чем на телефоне, и глазу труднее возвращаться к началу
     следующей (правка владельца): чем длиннее строка, тем больше нужен интерлиньяж.
     Правится только сплошной набор — строки со своим кеглем (обещание, дескриптор,
     цена, сноски) держат собственный интерлиньяж: он выбран под их кегль, и общий
     разгон делает их рыхлыми. */
  .page p:not([class]),
  .page li {
    line-height: 1.6;
  }

  .frames {
    margin-inline: calc(50% - 50vw);
    padding-inline: 1.5rem;
    /* Когда окно шире всей ленты, кадры без этого прижаты к левому краю, а справа
       остаётся пустое поле в полэкрана. `safe` обязателен: при обычном `center`
       переполнившая лента срезается слева и первые кадры не докручиваются. */
    justify-content: safe center;
    /* На широком экране лента длиннее окна и кадров не два-три, а гораздо больше:
       прилипание к центру каждого кадра на такой длине читается как рывки, а не
       как помощь (правка владельца). На телефоне, где лента листается пальцем
       и кадров в окне почти всегда один, прилипание остаётся. */
    scroll-snap-type: none;
  }
}

@media (max-width: 480px) {
  .page { padding: var(--head-lift) 1.25rem 4rem; }
  h1 { font-size: 1.8rem; }
  .lede { font-size: 1.4rem; }
  .corner { right: 0.4rem; }
  .frames {
    margin-inline: -1.25rem;
    padding-inline: 1.25rem;
  }
  .plan { padding: 1.3rem 1.2rem 1.2rem; }
  /* Столбиком знаки равняются по ширине: разная ширина в колонке читается как разный вес
     двух магазинов, а он одинаков. */
  .stores {
    flex-direction: column;
    align-items: flex-start;
  }
  .badge {
    width: 12.5rem;
    height: auto;
  }
  /* Погашенный знак — блок вокруг картинки, и ширину столбика берёт он. */
  .soon { width: 12.5rem; }
  /* Наш знак растягивается тем же способом, но высоту держит сам: у него не картинка,
     а строки текста, и `height: auto` дал бы бокс ниже соседей.

     В столбике знаки равняются по ширине, и высота у соседей вырастает вместе с ней —
     4 rem против 2.75 в ряду. Оставленная прежней, она делала нашу дверь ниже соседних,
     а знак браузера в ней — вдвое мельче их яблока и треугольника (правка владельца).
     Растёт всё вместе: бокс, знак и обе строки. */
  .badge.web {
    width: 12.5rem;
    height: 4rem;
    padding: 0 1.1rem;
    gap: 0.7rem;
  }

  .badge.web svg,
  .badge.web img {
    width: 2rem;
    height: 2rem;
  }

  .badge.web small { font-size: 0.72rem; }
  .badge.web b { font-size: 1.2rem; }
}

/* Тон бумаги — вторая ось вида, ровно та же, что в кабинете (`apps/web/src/tokens.css`,
   `theme.ts`): четыре тона, у каждого светлая и тёмная версия. Значения скопированы,
   а не импортированы: лендинг живёт отдельным Worker'ом и токенов клиента не грузит,
   но расходиться им нельзя — страница и кабинет обязаны выглядеть одним продуктом. */
:root[data-palette="warm"] {
  color-scheme: light dark;
  --paper: #faf9f6;
  --ink: #111111;
  --muted: #55524c;
  --link: #6d4f1d;
  --accent: #b5822a;
  --rule: #cbc6bc;
  --edge: #948f86;
  --fill: #efece3;
  --field: #ffffff;
}

@media (prefers-color-scheme: dark) {
  :root[data-palette="warm"]:not([data-theme="light"]) {
    --paper: #141312;
    --ink: #ece0ca;
    --muted: #a59578;
    --link: #d0ac6c;
    --accent: #d8ae63;
    --rule: #514a3b;
    --edge: #756a58;
    --fill: #211f1b;
    --field: #242220;
  }
}

:root[data-palette="warm"][data-theme="dark"] {
  color-scheme: dark;
  --paper: #141312;
  --ink: #ece0ca;
  --muted: #a59578;
  --link: #d0ac6c;
  --accent: #d8ae63;
  --rule: #514a3b;
  --edge: #756a58;
  --fill: #211f1b;
  --field: #242220;
}

:root[data-palette="warm"][data-theme="light"] {
  color-scheme: light;
  --paper: #faf9f6;
  --ink: #111111;
  --muted: #55524c;
  --link: #6d4f1d;
  --accent: #b5822a;
  --rule: #cbc6bc;
  --edge: #948f86;
  --fill: #efece3;
  --field: #ffffff;
}

:root[data-palette="rose"] {
  color-scheme: light dark;
  --paper: #fcf1ef;
  --ink: #111111;
  --muted: #5a4c49;
  --link: #7d392c;
  --accent: #b0574a;
  --rule: #dec4bf;
  --edge: #a68882;
  --fill: #f7e4e0;
  --field: #ffffff;
}

@media (prefers-color-scheme: dark) {
  :root[data-palette="rose"]:not([data-theme="light"]) {
    --paper: #171212;
    --ink: #f3d4ce;
    --muted: #b78d85;
    --link: #d9998c;
    --accent: #e0a99c;
    --rule: #513934;
    --edge: #826059;
    --fill: #241b1a;
    --field: #251d1c;
  }
}

:root[data-palette="rose"][data-theme="dark"] {
  color-scheme: dark;
  --paper: #171212;
  --ink: #f3d4ce;
  --muted: #b78d85;
  --link: #d9998c;
  --accent: #e0a99c;
  --rule: #513934;
  --edge: #826059;
  --fill: #241b1a;
  --field: #251d1c;
}

:root[data-palette="rose"][data-theme="light"] {
  color-scheme: light;
  --paper: #fcf1ef;
  --ink: #111111;
  --muted: #5a4c49;
  --link: #7d392c;
  --accent: #b0574a;
  --rule: #dec4bf;
  --edge: #a68882;
  --fill: #f7e4e0;
  --field: #ffffff;
}

:root[data-palette="mono"] {
  color-scheme: light dark;
  --paper: #f7f7f7;
  --ink: #111111;
  --muted: #525252;
  --link: #3b5272;
  --accent: #5c6f8f;
  --rule: #c9c9c9;
  --edge: #8e8e8e;
  --fill: #eaeaea;
  --field: #ffffff;
}

@media (prefers-color-scheme: dark) {
  :root[data-palette="mono"]:not([data-theme="light"]) {
    --paper: #121212;
    --ink: #e8e8e8;
    --muted: #9c9c9c;
    --link: #97aece;
    --accent: #a9bcd8;
    --rule: #3a3a3a;
    --edge: #6b6b6b;
    --fill: #1e1e1e;
    --field: #242424;
  }
}

:root[data-palette="mono"][data-theme="dark"] {
  color-scheme: dark;
  --paper: #121212;
  --ink: #e8e8e8;
  --muted: #9c9c9c;
  --link: #97aece;
  --accent: #a9bcd8;
  --rule: #3a3a3a;
  --edge: #6b6b6b;
  --fill: #1e1e1e;
  --field: #242424;
}

:root[data-palette="mono"][data-theme="light"] {
  color-scheme: light;
  --paper: #f7f7f7;
  --ink: #111111;
  --muted: #525252;
  --link: #3b5272;
  --accent: #5c6f8f;
  --rule: #c9c9c9;
  --edge: #8e8e8e;
  --fill: #eaeaea;
  --field: #ffffff;
}

:root[data-palette="pistachio"] {
  color-scheme: light dark;
  --paper: #f6f8f0;
  --ink: #111111;
  --muted: #4e5546;
  --link: #3b5b25;
  --accent: #55803a;
  --rule: #c9d0bc;
  --edge: #8e9682;
  --fill: #e9eede;
  --field: #ffffff;
}

@media (prefers-color-scheme: dark) {
  :root[data-palette="pistachio"]:not([data-theme="light"]) {
    --paper: #12140f;
    --ink: #dde5c9;
    --muted: #98a37f;
    --link: #a3c987;
    --accent: #b4d69b;
    --rule: #3e4634;
    --edge: #69715a;
    --fill: #1b1e16;
    --field: #1f2219;
  }
}

:root[data-palette="pistachio"][data-theme="dark"] {
  color-scheme: dark;
  --paper: #12140f;
  --ink: #dde5c9;
  --muted: #98a37f;
  --link: #a3c987;
  --accent: #b4d69b;
  --rule: #3e4634;
  --edge: #69715a;
  --fill: #1b1e16;
  --field: #1f2219;
}

:root[data-palette="pistachio"][data-theme="light"] {
  color-scheme: light;
  --paper: #f6f8f0;
  --ink: #111111;
  --muted: #4e5546;
  --link: #3b5b25;
  --accent: #55803a;
  --rule: #c9d0bc;
  --edge: #8e9682;
  --fill: #e9eede;
  --field: #ffffff;
}

/* Образцы тона — вне обеих осей: кружок стоит своим цветом при любой текущей палитре.
   Заливка — бумага своей палитры той же светлоты, но с насыщенностью в полную силу:
   различие четырёх почти белых бумаг в кружке размером с букву иначе не видно вовсе
   (правка владельца). Источник значений — `swatches` в `packages/design-tokens`. */
:root {
  --swatch-warm: #fffcf1;
  --swatch-warm-edge: #d1a149;
  --swatch-rose: #ffefec;
  --swatch-rose-edge: #d46954;
  --swatch-mono: #f7f7f7;
  --swatch-mono-edge: #8e8e8e;
  --swatch-pistachio: #faffe9;
  --swatch-pistachio-edge: #9ad147;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --swatch-warm: #261300;
    --swatch-warm-edge: #a47529;
    --swatch-rose: #290000;
    --swatch-rose-edge: #af422c;
    --swatch-mono: #121212;
    --swatch-mono-edge: #6b6b6b;
    --swatch-pistachio: #152300;
    --swatch-pistachio-edge: #78a229;
  }
}

:root[data-theme="dark"] {
  --swatch-warm: #261300;
  --swatch-warm-edge: #a47529;
  --swatch-rose: #290000;
  --swatch-rose-edge: #af422c;
  --swatch-mono: #121212;
  --swatch-mono-edge: #6b6b6b;
  --swatch-pistachio: #152300;
  --swatch-pistachio-edge: #78a229;
}

:root[data-theme="light"] {
  --swatch-warm: #fffcf1;
  --swatch-warm-edge: #d1a149;
  --swatch-rose: #ffefec;
  --swatch-rose-edge: #d46954;
  --swatch-mono: #f7f7f7;
  --swatch-mono-edge: #8e8e8e;
  --swatch-pistachio: #faffe9;
  --swatch-pistachio-edge: #9ad147;
}
