/* Адреса рисованных фигур — в отдельном файле: их имена содержат хэш
   содержимого, и собирает его sketch.py. */
@import url("shapes.css");

/* Слёт туристят в Сербии — палитра снята с логотипа проекта */

:root {
  --paper:      #F3F6F0;
  --surface:    #FFFFFF;
  --sunk:       #E8EFE6;
  --ink:        #10241B;
  --muted:      #5C6F63;
  --rule:       #DBE5D8;
  /* Оранжевый логотипа, затемнён: сам #F47434 на бумаге даёт 2,2:1 и как
     текст не читается. Здесь логика важнее буквального совпадения. */
  --accent:     #C1460A;
  --on-accent:  #FFFFFF;
  /* Цвета логотипа сняты с самого файла, пипеткой по самым частым точкам:
     оранжевый #F47434, тёмно-зелёный #1D6446, лиственный #459B52,
     салатовый #88C048, жёлтый #FECD28. */
  --accent-flat:#F47434;   /* оранжевый логотипа: заливки и плашки */
  --accent-soft:#FCE7D8;
  --forest:     #1D6446;
  --leaf:       #459B52;
  --lime:       #88C048;
  --sun:        #FECD28;
  /* Тёмно-зелёный фон под белым текстом. Темнее лесного из логотипа
     намеренно: на #1D6446 белый даёт 6,4:1, здесь 9,3:1. */
  --deep:       #17513A;
  --on-deep:    #EAF4EC;
  --deep-soft:  #96C4AA;
  --radius:     18px;
  /* Тени густо-зелёные, а не чёрные: чёрная тень на зелёной бумаге серая
     и грязная. Тот же цвет, что затемнение на обложке. */
  --shadow-sm:  0 1px 2px rgba(18, 41, 31, .06);
  --shadow:     0 2px 4px rgba(18, 41, 31, .05), 0 18px 40px -26px rgba(18, 41, 31, .45);

  --s--2: .715rem;
  --s--1: .8rem;
  --ls-label: .14em;
  --s-0:  1.0625rem;
  --s-1:  1.1875rem;
  --s-2:  1.45rem;
  --s-3:  1.75rem;
  --s-4:  2.4rem;

  /* Высоту шапки уточняет скрипт; значение здесь — на случай, если он ещё
     не отработал: совпадает с минимумом --band-h. */
  --head-h: 110px;
  /* Высота сжатой шапки. Нужна и самой шапке, и липкому меню — поэтому
     живёт в корне: на .top её не видно из других правил. */
  /* Высота сжатой шапки: должна вместить надпись целиком плюс поля. */
  --head-c: clamp(72px, 7.6vw, 132px);
  --bleed: clamp(.9rem, 2vw, 1.25rem);
  /* Зазор между колонками каркаса. Вынесен, потому что от него считается
     правый край содержимого — он должен упираться в полосу узора. */
  --shell-gap: clamp(1.5rem, 3.5vw, 3rem);
  /* Мера набора: 90 знаков в строке при кегле 17px. Ширина знака измерена
     напрямую на тексте сайта — 8,94px. */
  --col:   50.3rem;

  --sans: "Golos Text", ui-sans-serif, system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --disp: "Rubik", var(--sans);
  /* Рукописный: обложка, все заголовки и меню целиком. Дальше не идёт —
     основной текст, цифры, даты и кнопки остаются нейтральными: их читают
     и по ним считают, а не рассматривают. */
  --hand: "Shantell Sans", var(--disp);
  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper:      #0D1712;
    --surface:    #15221B;
    --sunk:       #101B15;
    --ink:        #E7EFE8;
    --muted:      #98AE9F;
    --rule:       #26392E;
    --accent:     #FF8B4A;
    --on-accent:  #10241B;
    --accent-flat:#F47435;
    --accent-soft:#33231A;
    --forest:     #6FC095;
    --leaf:       #7FC067;
    --lime:       #A8D96A;
    --sun:        #FFD84D;
    --deep:       #123D2C;
    --on-deep:    #E7F1E9;
    --deep-soft:  #8FC0A5;
    --shadow-sm:  0 1px 2px rgba(6, 20, 13, .45);
    --shadow:     0 2px 4px rgba(6, 20, 13, .45), 0 20px 44px -26px rgba(6, 20, 13, .9);
  }
}

:root[data-theme="dark"] {
  --paper:      #0D1712;
  --surface:    #15221B;
  --sunk:       #101B15;
  --ink:        #E7EFE8;
  --muted:      #98AE9F;
  --rule:       #26392E;
  --accent:     #FF8B4A;
  --on-accent:  #10241B;
  --accent-flat:#F47435;
  --accent-soft:#33231A;
  --forest:     #6FC095;
  --leaf:       #7FC067;
  --lime:       #A8D96A;
  --sun:        #FFD84D;
  --deep:       #123D2C;
  --on-deep:    #E7F1E9;
  --deep-soft:  #8FC0A5;
  --shadow-sm:  0 1px 2px rgba(6, 20, 13, .45);
  --shadow:     0 2px 4px rgba(6, 20, 13, .45), 0 20px 44px -26px rgba(6, 20, 13, .9);
}

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; scroll-padding-top: 5rem; }

body {
  margin: 0;
  /* Липкая шапка на сжатии становится ниже, весь текст под ней уезжает вверх,
     и браузер «помогает» — сдвигает прокрутку обратно, чтобы контент не прыгал.
     Поправка больше зоны гистерезиса, поэтому шапка тут же опрокидывалась
     обратно, и её трясло. Размеры всех картинок заданы в разметке, так что
     без этой помощи ничего не прыгает. */
  overflow-anchor: none;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--s-0);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; height: auto; display: block; }
[hidden] { display: none !important; }

a { color: var(--accent); text-underline-offset: .18em; text-decoration-thickness: .07em; }
a:hover { color: var(--ink); }

:is(a, button, summary, input):focus-visible {
  outline: 2.5px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}

/* ---------------- шапка ---------------- */

.top {
  /* Доля ширины полосы, отданная надписи. От неё же считаются боковые поля. */
  /* Доля полосы под центральную ячейку. Надпись занимает 88% этой ячейки,
     боковые поля делят остаток поровну. */
  --word-pct: 40%;
  --band-h: clamp(110px, 13vw, 200px);
  --band-c: var(--head-c);
  /* Высота развёрнутой полосы отдельной переменной: сжатое состояние
     переопределяет --band-h, а размер надписи надо считать от развёрнутого,
     иначе в сжатой шапке он поехал бы вслед за ней. */
  --band-full: clamp(110px, 13vw, 200px);
  /* Размер надписи: доля полосы, но не крупнее собственного размера файла.
     В процентах, а не в пикселях — на широком экране растёт вместе с полосой. */
  --word-h: min(117px, calc(var(--band-full) * .78));
  position: sticky;
  top: 0;
  z-index: 30;
  background: color-mix(in srgb, var(--paper) 94%, transparent);
  backdrop-filter: blur(10px);
  /* Прозрачный бордюр держит прежнюю высоту шапки, а сама полоса рисуется
     поверх него мазком — см. .top::after ниже. */
  border-bottom: 3px solid transparent;
}
.top::after, .side.open::after {
  content: "";
  position: absolute;
  left: 0; right: 0;
  bottom: -3px;
  height: 8px;
  background: var(--accent-flat);
  pointer-events: none;
  -webkit-mask-image: var(--sh-stripe);
          mask-image: var(--sh-stripe);
  -webkit-mask-size: 400px 8px;
          mask-size: 400px 8px;
  -webkit-mask-repeat: repeat-x;
          mask-repeat: repeat-x;
}

/* Сама шапка тоже обрезана по нижнему краю мазка. Иначе её край остаётся
   прямым, полоса по нему гуляет, и в прогибах из-под оранжевого выглядывает
   зелёная кромка. Два слоя: ровная заливка сверху и волна в последних 8px —
   волна взята из того же рисунка, что и полоса, поэтому края совпадают. */
.top, .side.open {
  --mask-top: var(--sh-solid), var(--sh-head-edge);
  -webkit-mask-image: var(--mask-top);
          mask-image: var(--mask-top);
  -webkit-mask-position: 0 0, 0 100%;
          mask-position: 0 0, 0 100%;
  -webkit-mask-size: 100% calc(100% - 8px), 400px 8px;
          mask-size: 100% calc(100% - 8px), 400px 8px;
  -webkit-mask-repeat: no-repeat, repeat-x;
          mask-repeat: no-repeat, repeat-x;
}

.band {
  position: relative;
  height: var(--band-h);
  max-width: 108rem;
  margin-inline: auto;
  transition: height .3s cubic-bezier(.4, 0, .2, 1);
}

/* Прокрутили — шапка садится, но лес остаётся: тот же ряд, только мельче. */
.top.compact { --band-h: var(--band-c); }
/* Подпись схлопнута, но зазор колонки под ней остаётся и приподнимает
   надпись над серединой полосы — поэтому здесь же убираем и зазор. */
.top.compact .brand { gap: 0; }
.top.compact .brand b { opacity: 0; height: 0; }

.brand {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: var(--word-pct);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .45rem;
  text-decoration: none;
  z-index: 2;
}

/* Надпись занимает всю отведённую ей долю по ширине. Потолок по высоте нужен
   только для сжатого состояния, чтобы она не упёрлась в края полосы;
   object-fit не даёт картинке сплющиться, когда потолок срабатывает. */
.brand-word {
  width: 88%;
  height: auto;
  max-height: var(--word-h);
  object-fit: contain;
  transition: max-height .3s cubic-bezier(.4, 0, .2, 1);
}
/* В сжатом виде надпись на 7% меньше развёрнутой. Уменьшаем и ширину, и
   потолок высоты: на среднем экране размер упирается в ширину колонки, и одного
   потолка высоты не хватило бы — доля осталась бы прежней. */
.top.compact .brand-word {
  width: 81.8%;
  max-height: calc(var(--word-h) * .93);
}

.brand b {
  font-family: var(--disp);
  font-weight: 600;
  font-size: clamp(.6rem, 1.05vw, .8rem);
  letter-spacing: .18em;
  text-transform: uppercase;
  line-height: 1;
  color: var(--forest);
  white-space: nowrap;
  transition: opacity .2s ease;
}

/* Бутерброд. На широком экране меню и так стоит слева столбцом, кнопка не нужна. */
.burger {
  position: absolute;
  left: var(--bleed);
  /* По центру полосы, а не в отступе от верха: полоса меняет высоту при
     сжатии, и от фиксированного отступа кнопка оказывалась прижата к верху.
     С серединой она едет вместе с полосой и попадает в один ряд с надписью. */
  top: 50%;
  transform: translateY(-50%);
  z-index: 4;
  display: none;
  align-items: center;
  gap: .5rem;
  padding: .45rem .7rem;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  border: 1px solid var(--rule);
  border-radius: 10px;
  font-family: var(--disp);
  font-weight: 600;
  font-size: var(--s--1);
  color: var(--ink);
  cursor: pointer;
}

.burger-box { display: grid; gap: 3px; width: 18px; }
.burger-box i {
  height: 2px;
  background: var(--ink);
  border-radius: 2px;
  transition: transform .2s ease, opacity .2s ease;
}

/* На узкой полосе подпись не помещается рядом с логотипом — остаётся значок.
   Название кнопки при этом не теряется: оно уходит в aria-label. */
@media (max-width: 27rem) {
  .burger-label { display: none; }
  .burger { padding: .45rem .5rem; }
}

/* Открыто — три полоски складываются в крестик. */
.burger[aria-expanded="true"] .burger-box i:nth-child(1) { transform: translateY(5px) rotate(45deg); }
.burger[aria-expanded="true"] .burger-box i:nth-child(2) { opacity: 0; }
.burger[aria-expanded="true"] .burger-box i:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }

/* Свободное место по краям делится на равные ячейки, в каждой — один элемент
   шириной 90% ячейки. Потолок по высоте держит его внутри полосы: без него
   костёр (он почти квадратный) вылезал бы за её пределы на широких экранах. */
.dec {
  position: absolute;
  top: 0; bottom: 0;
  width: calc((100% - var(--word-pct)) / 2);
  display: grid;
  grid-template-columns: 1fr;
  align-items: center;
  z-index: 1;
}
/* Фон: мягкая земля под лесом. Ёлки и костры и так стоят на прорисованных
   линиях земли — эта подложка собирает шесть отдельных предметов в одну сцену,
   не споря с ними цветом. Лежит ниже элементов и надписи. */
.band::after {
  content: "";
  position: absolute;
  /* Спускается на 3px ниже полосы — в зону прозрачного бордюра, где лежит
     оранжевый мазок. Иначе зелёное кончается прямой линией на три пикселя
     выше мазка, и в его прогибах видна светлая кромка. Лишнее срезает маска
     самой шапки. */
  inset: auto 0 -3px 0;
  height: calc(42% + 3px);
  background: linear-gradient(to top,
              color-mix(in srgb, var(--lime) 20%, transparent),
              color-mix(in srgb, var(--lime) 6%, transparent) 55%,
              transparent);
  pointer-events: none;
  z-index: 0;
}

.dec-l { left: 0; }
.dec-r { right: 0; }

.dec img {
  height: auto;
  /* Процентный max-height у элемента грида с авторазмером строки браузер
     считает за none — потолок не срабатывал, и элементы вылезали за полосу
     в сжатом состоянии. Привязываем к её высоте напрямую. */
  max-height: calc(var(--band-h) * .92);
  object-fit: contain;
  justify-self: center;
  transition: max-height .3s cubic-bezier(.4, 0, .2, 1);
}

/* Палатки крупнее костра. Одной ширины на всех для этого мало: костёр почти
   квадратный, поэтому при равной ширине он выходит выше плоской палатки и
   перетягивает внимание. Поэтому от общих ~85% отступаем в обе стороны. */
.dec .fly  { width: 100%; }
.dec .tree { width:  85%; }
.dec .fire { width:  56%; }

/* Ячеек столько же, сколько влезает элементов. Летящая палатка есть всегда,
   даже на телефоне: это главный знак Слёта, и слева с справа они разные.
   Дальше добавляется костёр, на широком экране — ёлки. */
.dec .fire, .dec .tree { display: none; }

@media (min-width: 40rem) {
  .dec { grid-template-columns: repeat(2, 1fr); }
  .dec .fire { display: block; }
}

@media (min-width: 64rem) {
  .dec { grid-template-columns: repeat(3, 1fr); }
  .dec .tree { display: block; }
}

/* ---------------- боковая навигация ---------------- */

.skip {
  position: absolute;
  left: -999px;
  top: .5rem;
  z-index: 60;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: .5rem .9rem;
  font-family: var(--disp);
  text-decoration: none;
}
.skip:focus { left: var(--bleed); }

/* Каркас больше не центрируется: меню стоит у левого края окна, а не там,
   где кончается центрированная колонка. Ширину чтения держит не он, а main. */
.shell {
  padding-inline: var(--bleed);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0 var(--shell-gap);
  align-items: start;
}

.side { padding-block: 1.1rem; }

.side-list, .side-sub { list-style: none; margin: 0; padding: 0; }

/* Линия разделяет РАЗДЕЛЫ. Она же единственная линейка в меню — под
   названиями их больше нет. */
.side-item + .side-item {
  border-top: 1px solid var(--rule);
  padding-top: .3rem;
}

.side-item + .side-item { margin-top: .72rem; }

/* Линии под названием раздела НЕТ: она отделяла название от его же
   подпунктов, то есть резала раздел пополам вместо того, чтобы отделять
   разделы друг от друга. Линия теперь между разделами, см. .side-item. */
.side-page {
  display: block;
  font-family: var(--hand);
  font-weight: 700;
  font-size: var(--s-0);
  color: var(--ink);
  text-decoration: none;
  margin-bottom: .18rem;
}
/* Названия разделов в меню — тем же зелёным, что заголовки на страницах:
   пункт меню и заголовок, к которому он ведёт, называются одинаково, пусть
   и выглядят одинаково. Подпункты остаются приглушёнными. */
.side-page { color: var(--forest); }
.side-page:hover { color: var(--accent); }
.side-page[aria-current="page"] { color: var(--accent); }

.side-sub li { margin: 0; }

.side-sub a {
  display: block;
  font-family: var(--hand);
  /* На пиксель мельче основного текста: дерево из 21 строки иначе не влезало
     в окно, и владелец выбрала кегль в обмен на то, что видно всё сразу.
     Рукописный при равном кегле читается мельче — рост строчных у Shantell
     на 3% меньше, чем у Golos, — поэтому мельче этого уже нельзя.
     Ступени меню держат цвет и жирность, а не размер. */
  font-size: 1rem;
  line-height: 1.35;
  color: var(--muted);
  text-decoration: none;
  padding: .16rem .5rem .16rem .7rem;
  border-radius: 6px;
  border-left: 2px solid transparent;
}
.side-sub a:hover { color: var(--ink); background: var(--sunk); }
.side-sub a[aria-current="true"] {
  color: var(--forest);
  font-weight: 500;
  border-left-color: var(--leaf);
  background: color-mix(in srgb, var(--lime) 14%, transparent);
}

/* На узком экране дерево целиком лежало над текстом — до содержания надо было
   пролистать всё меню. Теперь оно спрятано под кнопку и выпадает из-под шапки. */
@media (max-width: 63.99rem) {
  .burger { display: inline-flex; }
  /* По две ячейки с каждой стороны от надписи. Слева первая отдана кнопке
     меню, во второй палатка; справа палатка и костёр. Раньше кнопка лежала
     поверх палатки, и в сжатой шапке они наезжали друг на друга. */
  .dec { grid-template-columns: repeat(2, 1fr); }
  .dec-l .fly { grid-column: 2; }
  .dec-r .fire { display: block; }
  /* Потолок высоты: крупнее рисунки начинают задевать надпись в центре. */
  .dec img { max-height: calc(var(--band-h) - 44px); }

  /* Костёр и палатки — одной высоты.

     Равнять их по ШИРИНЕ нельзя: при разных пропорциях равная ширина даёт
     разную высоту. Палатка плоская (478×270), костёр почти квадратный
     (242×243) — на одной ширине костёр выходит вдвое выше и перетягивает
     на себя всё внимание. Прежние 100/85/56% как раз и были попыткой
     выровнять высоту вручную, через подобранные доли.

     Теперь коробка у всех одна: ширина 92% ячейки и пропорция самой широкой
     картинки — летящей палатки. Пропорция не уже ни одного рисунка, поэтому
     каждый упирается в ВЫСОТУ коробки, а не в её ширину: высота у всех
     одинаковая, а ширину каждый берёт свою, по своим пропорциям. Летящая
     палатка при этом занимает ровно свои 92%. `object-fit: contain` уже
     стоит выше — он и вписывает рисунок в коробку. */
  .top .dec img {
    width: 92%;
    aspect-ratio: 478 / 270;
    height: auto;
  }

  /* Потолок высоты в сжатой полосе — долей полосы, а не вычитанием: доля
     сжимается вместе с ней. Действует на все рисунки одинаково, поэтому
     равенство высот он не ломает. */
  .top.compact .dec img { max-height: calc(var(--band-h) * .8); }
  .top.compact .brand-word {
    width: 92%;
    /* На широком телефоне 92% ячейки — это надпись почти во всю высоту полосы:
       оставалось по 2px сверху и снизу. Тот же потолок в 0,8 полосы, что и
       у рисунков, возвращает ей поля; на узком экране он не достаёт. */
    max-height: min(calc(var(--word-h) * .93), calc(var(--band-h) * .8));
  }

  .side {
    position: fixed;
    /* Отсчёт от СЖАТОЙ шапки: пока меню открыто, шапка сжата — так дереву
       достаётся вся высота экрана, какая есть. Значение из CSS, а не замер
       скриптом: высота шапки едет анимацией, и замер в момент открытия давал
       старую, из-за чего панель уезжала под шапку. */
    top: calc(var(--head-c) + 3px);
    left: 0; right: 0;
    z-index: 40;
    max-height: calc(100dvh - var(--head-c) - 3px);
    overflow-y: auto;
    overscroll-behavior: contain;
    background: var(--paper);
    /* position: relative здесь стояло бы после fixed и отменяло бы его —
       панель выпадала бы в поток. Для мазка внизу опора не нужна:
       фиксированный элемент сам служит опорой абсолютным потомкам. */
    border-bottom: 3px solid transparent;
    box-shadow: var(--shadow);
    /* Снизу поле с запасом: последние 8px панели занимает волна края, и без
       запаса она подходит к тексту вплотную. */
    padding: 1.1rem var(--bleed) 1.4rem;
    display: none;
  }
  .side.open { display: block; }

  /* Разделы идут лестницей во всю ширину панели, а на две колонки делятся
     только подпункты внутри раздела.

     Прежде в две колонки шли сами разделы, и колонки не сходились: они
     разной длины, а содержимое в них течёт по порядку — разрыв возможен
     только между разделами, и при любом его месте выходило 8 строк против
     13 либо 15 против 6. Перестановка разделов ничего не решала: лучшее
     достижимое — разница 40–60px, а порядок меню разошёлся бы с порядком
     сайта, и глаз читал бы по колонкам, тогда как клавиатура и читалка
     идут по разметке. Лестница дороже на 100px высоты (панель 719 против
     617, меню на телефоне приходится прокручивать) — владелец выбрала её,
     увидев оба варианта. */
  .side-item { margin: 0 0 .55rem; }
  /* У последнего поле снизу лишнее: под ним уже поле самой панели. */
  .side-item:last-child { margin-bottom: 0; }
  .side-sub {
    columns: 2;
    column-gap: 1.6rem;
  }
  .side-sub li { break-inside: avoid; }

  /* Кегль тот же, что на мониторе, а поля под палец больше. */
  .side-sub a { padding: .42rem .5rem .42rem .7rem; }

  /* Подложка начинается под шапкой: иначе она гасит и саму кнопку, и закрыть
     меню тем же нажатием уже нельзя — палец попадает в подложку. */
  .nav-veil {
    position: fixed;
    inset: calc(var(--head-c) + 3px) 0 0 0;
    z-index: 39;
    background: color-mix(in srgb, var(--ink) 42%, transparent);
  }
  body.nav-open { overflow: hidden; }
  /* Шапку при открытии меню сжимает скрипт. Анимацию высоты на это время
     выключаем: иначе первые 300ms панель лежит под ещё высокой шапкой. */
  body.nav-open .band { transition: none; }
}

@media (min-width: 64rem) {
  .shell { grid-template-columns: 15.5rem minmax(0, 1fr); }
  /* Меню прикреплено к окну и из потока вышло — сетка его больше не считает
     своим. Поэтому колонки остальным задаём прямо: без этого содержимое
     съезжало в первую, узкую, и меню ложилось поверх текста. */
  .shell > main { grid-column: 2; }
  /* Меню стоит слева всё время. Отступ считаем от сжатой шапки: 4.4rem было
     меньше её высоты, и верхние пункты уезжали под неё на 9px. */
  .side {
    /* Прикреплено к окну, а не липкое: липкое живёт только внутри своей
       колонки и в подвале уезжало вверх вместе с ней. Отсчёт от `--head-h`
       (текущая высота шапки, её держит скрипт) — поэтому меню стоит под
       шапкой и в развёрнутом её виде, и в сжатом. Колонка каркаса задана
       числом, так что пустой она держит ту же ширину. */
    position: fixed;
    left: var(--bleed);
    width: 15.5rem;
    top: calc(var(--head-h, 203px) + .7rem);
    /* Выше подвала, но ниже шапки: подвал идёт в разметке позже и без этого
       закрашивал прикреплённое меню собой. */
    z-index: 20;
    /* Высота шапки едет анимацией, и переменная меняется шагами — сглаживаем
       тем же движением, что у самой шапки. */
    transition: top .3s cubic-bezier(.4, 0, .2, 1),
                max-height .3s cubic-bezier(.4, 0, .2, 1);
    /* Предел высоты — от ТЕКУЩЕЙ шапки, а не от сжатой. Липкое меню встаёт
       на отступ от сжатой только когда страница прокручена; в самом верху
       страницы оно начинается под РАЗВЁРНУТОЙ шапкой, и предел, посчитанный
       от сжатой, уводил его низ за край окна — «Архив» пропадал совсем.
       `--head-h` скрипт держит равной высоте шапки в её текущем виде. */
    max-height: calc(100vh - var(--head-h, 203px) - 1.4rem);
    overflow-y: auto;
    overscroll-behavior: contain;
    border-right: 1px solid var(--rule);
    padding-right: 1.2rem;
    /* Дерево из 23 строк не влезает в невысокое окно. Прокрутка внутри
       остаётся, но снизу гаснет — видно, что там есть продолжение. */
    mask-image: linear-gradient(to bottom, #000 calc(100% - 2.2rem), transparent);
  }
}

/* ---------------- каркас ---------------- */

.wrap { width: 100%; }

main { display: block; padding-bottom: 2.5rem; min-width: 0; max-width: 68rem; }

.col { max-width: var(--col); }

section { padding-block: clamp(1.3rem, 2.9vw, 1.9rem); border-top: 1px solid var(--rule); }
section:first-of-type { border-top: 0; }

h1, h2, h3, h4 { font-family: var(--disp); line-height: 1.13; text-wrap: balance; margin: 0; }
h1 { font-family: var(--hand); font-size: clamp(1.75rem, 5.4vw, 2.6rem); font-weight: 800; }
/* Межбуквенное у заголовков было поджато под Rubik; Shantell шире и круглее,
   поджимать его незачем. */
/* Кегли заголовков лежат между ступенями общей шкалы: рукописный шрифт легче
   и мельче Rubik, и на ступени шкалы читался не как афиша, а как подпись. */
h2 { font-family: var(--hand); font-size: 1.58rem; font-weight: 700; }
h3 { font-family: var(--hand); font-size: 1.29rem; font-weight: 600; }
h4 { font-size: var(--s-0); font-weight: 600; }

/* Отступ давался только паре «заголовок + абзац», поэтому h3 сразу после h2
   и список сразу после h3 слипались. Покрываем все сочетания. */
:is(h1, h2, h3, h4) + :is(p, ul, ol, dl, div, figure, table) { margin-top: .5rem; }
:is(p, ul, ol, dl, div, figure, table, .table-scroll) + :is(h3, h4) { margin-top: 2rem; }
h2 + h3 { margin-top: 1.2rem; }
p, ul, ol { margin: 0 0 .8rem; }
p:last-child, ul:last-child, ol:last-child { margin-bottom: 0; }
ul, ol { padding-left: 1.25rem; }
li { margin-bottom: .28rem; }
li::marker { color: var(--leaf); }

.lead { font-size: var(--s-1); line-height: 1.42; color: var(--muted); text-wrap: pretty; }
.muted { color: var(--muted); }
.small { font-size: var(--s--1); }


.sec-head { margin-bottom: 1.05rem; max-width: var(--col); }

/* ---------------- обложка ---------------- */

/* Воздух под шапкой даёт только этот отступ. Обложке свой верхний отступ
   здесь не нужен — она открывает страницу, и два отступа складывались
   в 77px пустоты. */
.hero { padding-block: clamp(.85rem, 2.4vw, 1.15rem) 0; }
/* Раздел сразу за обложкой прижимаем: на широком экране от него зависит,
   поместится ли вступление в экран целиком. */
.hero + section { padding-top: 1.25rem; }
.hero > .cover:first-of-type { margin-top: 0; }

/* На широком экране кнопки лежат на самом снимке, в свободном правом нижнем
   углу: отдельной строкой под ним они съедали 59px высоты, и вступительный
   текст переставал помещаться в экран целиком.

   Кнопки и обложка делят одну ячейку сетки самого раздела, а не фигуры: маска
   с неровным краем лежит на фигуре и обрезала бы их. Заголовок раздела скрыт
   через .vh, он вне потока и на сетку не влияет.

   На узком экране правило не работает: там на снимке места нет — его нижнюю
   часть занимают дата и тема. Кнопки остаются строкой под фотографией, в той
   же, главной колонке, так что в конец страницы они не уезжают. */
/* Кнопки стоят во второй колонке обложки: на широком экране — в её правом
   нижнем углу поверх снимка, на узком — строкой под фотографией, где им и
   место. Ни в том, ни в другом случае они не наезжают на подпись: колонка
   подписи сжимается ровно на их ширину. */
.cover-actions {
  grid-area: 3 / 1 / 4 / 3;
  padding: .3rem clamp(.9rem, 4vw, 1.4rem) clamp(.85rem, 3.2vw, 1.1rem);
}
@container (min-width: 30rem) {
  .cover-actions { grid-area: 2 / 1 / 3 / 3; }
}


.hero h1 span { color: var(--accent); display: block; }

.hero .lead { margin-top: 1rem; max-width: 44ch; }

/* ---------------- обложка ближайшего слёта ---------------- */

/* Фото и подпись лежат в одной ячейке сетки: высоту задаёт то, что выше.
   Поэтому крупная тема может переноситься на две строки, а обложка вырастет
   под неё, вместо того чтобы обрезать текст. */
.cover {
  position: relative;
  margin-top: 1.8rem;
  display: grid;
  /* Две колонки: подписи достаётся всё, что осталось от кнопок. Ширину никто
     не задаёт числом — поэтому на любом экране и любом масштабе название темы
     переносится ровно тогда, когда кнопки перестают в строку помещаться. */
  grid-template-columns: minmax(0, 1fr) auto;
  /* Одинаковый нижний отступ у подписи и у кнопок — иначе они стоят на разной
     высоте, и это заметно. */
  --inset-b: clamp(.6rem, 2vw, 1rem);
  /* Боковое поле подписи. Вынесено, потому что на широкой обложке подпись
     раскрывается в сетку и поле приходится задавать каждой ячейке отдельно. */
  --cap-x: clamp(1.1rem, 3.5vw, 2.1rem);

  /* Узкая обложка: снимок сверху, ниже — зелёное поле, на котором лежат
     подпись и кнопки. Низ снимка уходит в это поле шторкой, так что переход
     не виден. Силуэт с рваным краем здесь общий на весь блок, поэтому маска
     стоит на обложке, а не на снимке. */
  background: var(--deep);
  -webkit-mask-image: var(--sh-photo-edge);
          mask-image: var(--sh-photo-edge);
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
}

/* Маска с неровным краем лежит здесь, а не на всей обложке: подпись и кнопки
   ею резаться не должны. Шторка-затемнение — внутри, чтобы обрезаться тем же
   контуром, что и снимок. */
.cover-frame {
  grid-area: 1 / 1 / 2 / 3;
  position: relative;
  display: grid;
  background: var(--deep);
}

/* Сколько снимков за обложкой. Лежит в той же ячейке, что и кнопка со
   снимком, но клики не перехватывает — нажатие всё равно открывает галерею.
   Слой второй: под ним шторка (z-index 1), над ним ничего. */
.cover-count {
  grid-area: 1 / 1;
  justify-self: end;
  align-self: start;
  z-index: 2;
  pointer-events: none;
  margin: var(--inset-b) var(--cap-x) 0 0;
  padding: .3rem .7rem;
  font-family: var(--disp);
  font-size: var(--s--2);
  font-weight: 600;
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: #fff;
  position: relative;
  isolation: isolate;
}
/* Подложка тем же неровным краем, что у кнопок: иначе прямоугольник спорит
   с рисованным стилем. Не сплошная, но и не прозрачная — 85% держат белый
   текст читаемым даже там, где под пометкой светлая трава. */
.cover-count::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-color: color-mix(in srgb, var(--deep) 85%, transparent);
  -webkit-mask-image: var(--sh-btn-edge);
          mask-image: var(--sh-btn-edge);
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
}

/* Кнопка-обёртка не имеет своего вида: всю внешность задаёт .cover, а маска
   с неровным краем лежит на ней же — второй маски здесь быть не должно. */
.cover-open {
  grid-area: 1 / 1;
  display: block;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
}
.cover-open:focus-visible { outline: 3px solid var(--sun); outline-offset: -6px; }

.cover img {
  grid-area: 1 / 1;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Кадр 3:2, берём из него горизонтальную полосу, прижатую к низу: сверху
     отсекается крона, снизу — размытые головы на переднем плане. Пропорции 2:1
     подобраны так, чтобы верхняя половина снимка оставалась незатемнённой
     и костюмы были видны. Верх полосы — по головы детей: небо и крона отрезаны. */
  /* Узкая обложка: полоса короче и взята выше по кадру. Так снизу не попадают
     двое детей на корточках — они выпадают из ряда, — а сверху не лезет небо
     с шарами. Считано от пропорций исходника: полоса занимает 68% его высоты
     и стоит между 19% и 87%. */
  object-position: center 52%;
  aspect-ratio: 2.55;
}

/* Широкой обложке — плоская полоса: так вступительный текст помещается
   в экран целиком, а высоты подписи всё равно хватает. */
@container (min-width: 40rem) {
  .cover img {
    aspect-ratio: 2.5;
    /* Здесь подпись лежит на снимке и низ кадра всё равно под шторкой,
       поэтому берём его ниже — головы детей уходят выше от края. */
    object-position: center 80%;
  }
}


/* Подпись поверх снимка не должна перехватывать клик — под ней кнопка. */
.cover-in { pointer-events: none; }
.cover-in a { pointer-events: auto; }

/* Шторка снизу вверх. На широкой обложке под ней лежит подпись, на узкой она
   сводит низ снимка с зелёным полем — переход не виден. */
.cover-frame::after {
  pointer-events: none;
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  /* Снимок светлый, поэтому шторка держится плотной под текстом и резко
     сходит на нет выше него — так белый текст читается, а верх кадра чистый. */
  /* Цвет шторки — тот же тёмно-зелёный, что у кнопки регистрации под курсором
     (--deep). Он светлее прежнего почти-чёрного, поэтому контраст ниже: белый
     текст держит 9:1, оранжевая тема — 3,3:1, а это крупный кегль, ему хватает
     порога 3:1. Меньше плотность делать нельзя. */
  /* На узкой обложке шторка только сводит низ снимка с зелёным полем: детей
     затемнять не нужно, лишние ушли за край кадра. */
  background: linear-gradient(to top,
    rgba(23, 81, 58, 1) 0%,
    rgba(23, 81, 58, .92) 9%,
    rgba(23, 81, 58, .4) 19%,
    rgba(23, 81, 58, 0) 30%);
}

.cover { container-type: inline-size; }

/* На узкой обложке подпись стоит ПОД снимком, на бумаге. Белым по фотографии
   там не прочесть: полоса всего в две сотни пикселей, подпись занимает её почти
   целиком, и дети уходят под затемнение. На широкой она возвращается на снимок —
   см. @container ниже. */
.cover-in {
  grid-area: 2 / 1 / 3 / 3;
  /* Подпись заходит на затемнённый низ снимка, а не начинается под ним:
     белый текст там читается, а зелёного поля нужно меньше. Сдвиг задан долей
     ширины обложки, поэтому держится на любом размере: высота снимка от неё
     и считается. */
  margin-top: -7cqi;
  /* Раз подпись зашла на снимок — поднимаем её над шторкой, иначе та ложится
     поверх и срезает верх даты. */
  position: relative;
  z-index: 2;
  padding: .35rem clamp(.9rem, 4vw, 1.4rem) 0;
  color: #fff;
}
.cover-in .d { text-shadow: none; }
.cover-in .t { color: var(--accent-flat); }
.cover-in .k { color: var(--deep-soft); }

/* Дата — надзаголовок над темой. Прежде она была 51px против 59 у темы:
   почти ровня, и ни одна не вела. Теперь ведёт тема, а дата открывает блок
   и уводит к ней. */
.cover .d {
  margin: 0;
  font-family: var(--disp);
  font-weight: 700;
  font-size: clamp(1.1rem, 3.2cqi, 1.9rem);
  line-height: 1.15;
  letter-spacing: 0;
}
/* Отсчёт в той же строке, но тише даты: сама дата — то, что нужно запомнить. */
.cover .d .k { font-size: .84em; font-weight: 600; color: var(--deep-soft); }

.cover .t {
  /* Отбивка снизу больше, чем сверху: у прописных темы снизу нет выносных,
     и при равных числах она зрительно прилипала к кнопкам. */
  margin: .3rem 0 .2rem;
  /* Маркерный шрифт — как и надпись логотипа: прямой, круглые окончания,
     чуть неровный. Caveat был тонким наклонным почерком, другая рука. */
  font-family: var(--hand);
  font-weight: 700;
  font-size: clamp(2.4rem, 7vw, 5rem);
  /* Доля подобрана замером под нынешнюю раскладку: кнопки стоят столбиком
     справа и забирают часть ширины, и при 7cqi тема ломалась на две строки —
     подпись становилась такой высокой, что дата вылезала со шторки на светлую
     часть снимка. 6,2% ширины обложки: тема крупнее, на широкой обложке уходит в две строки и на её экране тоже; более длинная тема
     перенесётся — это не ломает раскладку, только делает подпись выше. */
  font-size: clamp(1.5rem, 7.4cqi, 5rem);
  line-height: 1;
  letter-spacing: -.01em;
  /* Оранжевый логотипа: тема читается продолжением названия, а не третьим
     голосом на афише. Контраст на затемнённом снимке 6,6 при пороге 4,5. */
  text-wrap: balance;
}

@container (min-width: 30rem) {
  .cover {
    background: none;
    -webkit-mask-image: none;
            mask-image: none;
  }
  .cover-frame {
    -webkit-mask-image: var(--sh-photo-edge);
            mask-image: var(--sh-photo-edge);
    -webkit-mask-size: 100% 100%;
            mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
  }
  /* Зелёного поля здесь нет: кнопки стоят под снимком на бумаге, боковые
     и нижний отступы им не нужны, а воздух сверху нужен. */
  .cover-actions { padding: .9rem 0 0; }
  /* Здесь подпись лежит на снимке, поэтому шторка длинная: плотная часть
     доведена до 44% — примерно столько занимает подпись. */
  .cover-frame::after {
    /* Плотная часть доведена до 38%, а не до 44: выше начиналась зелёная
       плашка, через которую просвечивала трава, и низ снимка перестал быть
       снимком. Ниже 38% опускать нельзя — там стоит строка даты, её верх
       на 46% высоты обложки от низа. */
    background: linear-gradient(to top,
      rgba(23, 81, 58, .97) 0%,
      rgba(23, 81, 58, .93) 38%,
      rgba(23, 81, 58, .58) 48%,
      rgba(23, 81, 58, .11) 58%,
      rgba(23, 81, 58, 0) 65%);
  }
  .cover-in {
    grid-area: 1 / 1;
    align-self: end;
    position: relative;
    z-index: 2;
    padding: clamp(1.1rem, 3.5vw, 2.1rem);
    /* Снизу отступ меньше боковых: там оставалось заметное пустое пятно
       затемнения. Ещё ниже опускать нельзя — рваный край снимка на большой
       обложке уходит внутрь до 13px, текст начал бы его задевать. */
    padding-bottom: var(--inset-b);
    color: #fff;
  }
  .cover-in .d { text-shadow: 0 2px 18px rgba(7, 22, 14, .55); }
  .cover-in .t { color: var(--accent-flat); text-shadow: 0 2px 18px rgba(7, 22, 14, .65); }
  .cover-in .k { color: #fff; }
}

/* Кнопки под снимком: отбивка небольшая — выше неё лежит тема, и вместе
   с её собственным просветом снизу зазор выходил в 40px зелёного воздуха. */
.cover-actions { margin-top: .5rem; }

/* Решает не ширина окна, а ширина самой обложки: в три колонки она бывает
   вдвое уже, чем в две, при одном и том же экране. Порог 56rem — та ширина,
   с которой на снимке хватает места и кнопкам, и подписи; ниже кнопки уходят
   под снимок.

   Блок стоит ПОСЛЕ блока 30rem нарочно: специфичность у правил одинаковая,
   выше по файлу он не пересилил бы ни grid-area подписи, ни margin кнопок. */
@container (min-width: 56rem) {
  /* Подпись раскрывается в сетку самой обложки: у figcaption не остаётся своей
     коробки (`display: contents`), и дата, тема, мелкая строка и кнопки
     становятся ячейками наравне. Иначе низ снимка не разложить: кнопки лежат
     вне подписи, а занять с ней один угол не могут — кто-то один оказывается
     сверху. Разметка при этом не трогается, и ниже 56rem всё как было.

     Строки: первая пустая и тянется, поэтому весь низ прижат к краю снимка.
     Дальше сверху вниз дата, тема и кнопки — одним блоком у левого края,
     тема вплотную к кнопкам. Мелкая строка стоит в правом нижнем углу:
     владелец ещё решает, куда её. */
  .cover { grid-template-rows: 1fr auto auto auto; }
  .cover-frame { grid-area: 1 / 1 / 5 / 3; }
  .cover-in { display: contents; }

  /* Своя опора и свой слой у каждой ячейки: у подписи их больше нет, а лежать
     они должны поверх шторки. */
  .cover-in > *, .cover-actions { position: relative; z-index: 2; }

  /* Боковое поле тоже у каждой ячейки своё: общий отступ на обложке сдвинул бы
     и сам снимок — он лежит в той же сетке. */
  .cover-in .d { grid-area: 2 / 1 / 3 / 3; padding-inline: var(--cap-x); }
  .cover-in .t { grid-area: 3 / 1 / 4 / 3; padding-inline: var(--cap-x); margin-bottom: .55rem; }
  /* Отбивки сверху у кнопок нет: тема должна стоять прямо над ними. */
  .cover-actions {
    grid-area: 4 / 1 / 5 / 2;
    justify-self: start;
    align-self: end;
    margin: 0;
    padding: 0 0 var(--inset-b) var(--cap-x);
  }

}

/* ---------------- факты ---------------- */

.facts {
  /* Не подзаголовок с подписью, а отдельный блок под названием страницы:
     малый шаг «заголовок и его строка» здесь слишком тесен. Значение — шаг
     шкалы, свои числа блокам заводить нельзя. */
  margin-top: 1.2rem;
  display: grid;
  gap: 1.1rem 2.2rem;
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 46rem) { .facts { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.facts > div { display: grid; gap: .18rem; }

.facts dt {
  font-family: var(--disp);
  font-size: var(--s--2);
  font-weight: 600;
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--accent);
}
.facts dd { margin: 0; color: var(--ink); font-size: var(--s-0); line-height: 1.55; }

/* ---------------- кнопки ---------------- */

.actions { display: flex; flex-wrap: wrap; gap: .6rem; }

/* У кнопки неровный край — как у снимков, без обводки. Заливка лежит слоем
   под текстом, чтобы цвет остался в CSS. */
.btn {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  font-family: var(--disp);
  font-weight: 600;
  font-size: var(--s--1);
  text-decoration: none;
  padding: .66rem 1.15rem;
  border: 0;
  color: var(--ink);
  background: none;
  position: relative;
  isolation: isolate;
  /* Без обводки белая кнопка на почти белой бумаге пропадала бы, поэтому
     заливка у неё теперь не белая, а светло-зелёная. */
  --fill: var(--sunk);
  transition: transform .12s ease;
}
.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-color: var(--fill);
  -webkit-mask-image: var(--sh-btn-edge);
          mask-image: var(--sh-btn-edge);
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  transition: background-color .12s ease;
}
.btn:hover { transform: translateY(-1px); color: var(--ink); --fill: var(--rule); }

.btn-main { color: var(--on-accent); --fill: var(--accent); }
.btn-main:hover { color: #fff; --fill: var(--deep); }

/* На обложке кнопка крупнее прочих: это главное действие страницы. Правило
   стоит ПОСЛЕ общего вида кнопки — раньше оно лежало выше, общее правило его
   перебивало, и на афише кнопки оставались набраны 12,8px. Только на широкой
   обложке: на телефоне пара таких кнопок в строку не помещается, переносится,
   и обложка от этого растёт. */
@container (min-width: 30rem) {
  .cover .btn { font-size: var(--s-1); padding: .95rem 1.8rem; }
}

/* ---------------- шаги ---------------- */

/* Шаги устроены как хроника: слева отметка, на нитке бусина, справа текст.
   Ширина колонки с номером и положение нитки считаются из одних переменных,
   чтобы бусины не разъезжались с текстом. */
.steps {
  /* Колонка с бусиной — той же ширины, что в хронике (--dot-w), чтобы зазор
     между бусиной и текстом совпадал до пикселя. */
  --bead-w: 1.9rem;
  --bead: .68rem;
  /* Бусина садится на середину первой строки текста, а не на середину всей
     ячейки: иначе на телефоне, где шаг переносится на две строки, она
     сползает вниз, а в хронике остаётся наверху. */
  --bead-top: .48rem;

  list-style: none;
  padding: 0; margin: 0;
  /* Зазор между шагами нужен в calc() у стрелки, поэтому переменной. */
  --step-gap: 1.15rem;
  display: grid; gap: var(--step-gap);
  position: relative;
}

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

   Элемент растянут от НИЗА своей бусины до ВЕРХА следующей, поэтому высота
   считается от высоты шага: 100% + зазор − бусина. У последнего шага стрелки
   нет — дальше идти некуда.

   Цвет серый, а не зелёный: зелёные здесь бусины-остановки, а путь между
   ними серый. Взят из `--muted`, разбавленного бумагой: сам `--muted` рядом
   с зелёной бусиной читался ещё одним зелёным. */
.steps li:not(:last-child)::after {
  content: "";
  position: absolute;
  left: calc(var(--bead-w) / 2 - 8px);
  top: calc(var(--bead-top) + var(--bead));
  width: 16px;
  height: calc(100% + var(--step-gap) - var(--bead));
  background: color-mix(in srgb, var(--muted) 62%, var(--surface));
  /* Линия тянется на весь перегон, кроме последних 11px — там наконечник
     своего размера. Слои перекрываются на 1px, поэтому стык не виден. */
  -webkit-mask-image: var(--sh-arrow-line), var(--sh-arrow);
          mask-image: var(--sh-arrow-line), var(--sh-arrow);
  -webkit-mask-size: 14px calc(100% - 11px), 16px 12px;
          mask-size: 14px calc(100% - 11px), 16px 12px;
  -webkit-mask-position: 1px 0, 0 100%;
          mask-position: 1px 0, 0 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
}

.steps li {
  position: relative;
  display: grid;
  grid-template-columns: var(--bead-w) 1fr;
  align-items: start;
  margin: 0;
}

/* бусина */
.steps li::before {
  content: "";
  /* Явная строка обязательна: бусина объявлена после ссылки, а курсор
     авторазмещения не ходит назад — без этого она падает во вторую строку. */
  grid-column: 1; grid-row: 1;
  justify-self: center;
  width: var(--bead); height: var(--bead);
  margin-top: var(--bead-top);
  background: var(--leaf);
}

.steps a { grid-column: 2; grid-row: 1; }

/* Шаг — одна строка. Вся она ссылка, но не кричит: цвет как у текста,
   подчёркивание появляется под курсором. Куда ведёт — написано на самой
   странице, поэтому пояснения не нужны. */
.steps a {
  font-family: var(--disp);
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
}
.steps a:hover,
.steps a:focus-visible {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: .2em;
}

/* ---------------- роли ---------------- */

.roles { display: grid; gap: 1rem; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 44rem) { .roles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.role {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-top: 5px solid var(--leaf);
  border-radius: var(--radius);
  padding: 1.15rem 1.25rem;
  display: flex;
  flex-direction: column;
  gap: .45rem;
}
.role:nth-child(1) { border-top-color: var(--forest); }
.role:nth-child(2) { border-top-color: var(--leaf); }
.role:nth-child(3) { border-top-color: var(--lime); }
.role:nth-child(4) { border-top-color: var(--sun); }
.role h3 { font-size: var(--s-1); }
.role p { font-size: var(--s--1); color: var(--muted); margin: 0; }
.role .load {
  font-family: var(--mono);
  font-size: var(--s--2);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--ink);
  background: var(--sunk);
  padding: .2rem .5rem;
  align-self: flex-start;
}

/* ---------------- программа ---------------- */

.program { display: grid; gap: 1.6rem 2.5rem; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 44rem) { .program { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.prog { min-width: 0; }

.program h3 {
  display: flex;
  align-items: center;
  gap: .55rem;
  margin-bottom: .6rem;
}
.program h3::before {
  content: "";
  width: .85rem; height: .85rem;
  border-radius: 4px;
  flex: none;
  background: var(--leaf);
}
.program ul { list-style: none; padding: 0; margin: 0; }
.program li {
  padding: .38rem 0;
  font-size: var(--s--1);
  margin: 0;
}

/* ---------------- таблица цен ---------------- */

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

   Ширина задана прямо, а не через `fit-content`: при подгонке по содержимому
   ширина обёртки зависит от таблицы, а ширина таблицы — от обёртки. Chrome
   этот круг разрывает, Safari на телефоне — нет: таблица оставалась в свои
   34rem и уезжала за экран. */
.table-card { width: min(34rem, 100%); }
.table-scroll { overflow-x: auto; }

/* Колонки раздувались от длинных подписей вроде «каждый следующий». Фиксируем
   раскладку: имя роли шире, колонки с ценами одинаковые, подписи переносятся.
   Нижняя граница подобрана так, чтобы таблица целиком помещалась в телефон:
   при большей она уезжала вбок и правые колонки читатель не находил. */
table {
  border-collapse: collapse;
  /* Во всю обёртку, а ширину держит она: 34rem — столько нужно, чтобы имя роли
     и четыре колонки с ценами стояли свободно, а подпись «каждый следующий»
     переносилась не больше чем на две строки. На телефоне упирается в экран. */
  width: 100%;
  font-size: var(--s--1);
  table-layout: fixed;
}
table col.c-name  { width: 28%; }
table col.c-price { width: 18%; }
thead th span.small { display: block; white-space: normal; font-weight: 400; line-height: 1.2; }
caption { text-align: left; padding: .9rem 1rem .2rem; color: var(--muted); font-size: var(--s--1); }
th, td { padding: .6rem .75rem; text-align: center; }
thead th { font-family: var(--disp); font-weight: 600; background: var(--sunk); }

/* Линейку рисуем на строке, а не на каждой ячейке: у ячеек разная ширина,
   и волна в каждой растянулась бы по-своему — вместо линии вышла бы пила. */
tr { position: relative; }
tbody tr:not(:last-child)::after, thead tr:first-child::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -4px;
  height: 8px;
  z-index: 2;
  background: var(--rule);
  pointer-events: none;
  -webkit-mask-image: var(--sh-rule);
          mask-image: var(--sh-rule);
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
}
tbody th { text-align: left; font-family: var(--disp); font-weight: 600; white-space: nowrap; }
td { font-family: var(--mono); font-variant-numeric: tabular-nums; }

.grp-org { background: color-mix(in srgb, var(--accent-soft) 55%, transparent); }
.grp-camp { background: color-mix(in srgb, var(--forest) 9%, transparent); }

/* Зелёная заливка — льгота за роль, как в исходной таблице организаторов. */
td.benefit {
  background: color-mix(in srgb, var(--lime) 32%, var(--surface));
  color: var(--forest);
  font-weight: 600;
}
tbody th.has-benefit { background: color-mix(in srgb, var(--lime) 18%, transparent); }

/* Новая смысловая группа внутри раздела: большой шаг шкалы. Раньше здесь
   стояли две утилиты по числам (2rem и 1,5rem) — от них и разъезжался ритм. */
.group { margin-top: 2rem; }

.legend i.is-next { background: var(--accent-flat); }
.legend i.is-slet { background: var(--leaf); }
.legend i.is-city { background: var(--sun); }

.vh {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---------------- чек-лист ---------------- */

.pack { display: grid; gap: 1.6rem; grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); }
.pack ul { list-style: none; padding: 0; margin: 0; }
.pack li { margin: 0; position: relative; }

/* Линейка между ячейками — не бордюр, а отдельный штрих: у бордюра не бывает
   дрожи. Лежит в отступе метки, поэтому клики по ней достаются метке. */
.pack li + li::before {
  content: "";
  position: absolute;
  left: 0; right: 0; top: -4px;
  height: 8px;
  background: var(--rule);
  pointer-events: none;
}

.pack label {
  display: grid;
  grid-template-columns: 1.15rem 1fr;
  gap: .7rem;
  align-items: start;
  padding: .55rem .2rem;
  cursor: pointer;
  font-size: var(--s--1);
  line-height: 1.5;
}
/* Свой квадратик вместо системного: системный не умеет быть нарисованным. */
.pack input {
  appearance: none;
  -webkit-appearance: none;
  margin: .28rem 0 0;
  width: 1.1rem; height: 1.1rem;
  background-color: var(--muted);
  cursor: pointer;
}
.pack input:checked { background-color: var(--leaf); }
/* Обводку фокуса рисуем на метке: маска обрезала бы её на самом поле. */
.pack input:focus-visible { outline: none; }
.pack label:has(input:focus-visible) { outline: 3px solid var(--accent); outline-offset: 2px; border-radius: 6px; }
.pack input:checked + span { color: var(--muted); text-decoration: line-through; text-decoration-color: var(--rule); }
.pack span b { font-weight: 600; }

.pack-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .8rem 1.2rem;
  /* Полоса подписывает свой блок, поэтому стоит к нему вплотную — малый шаг. */
  margin-bottom: .5rem;
  font-family: var(--mono);
  font-size: var(--s--1);
  color: var(--muted);
}
.pack-bar progress { width: 12rem; height: .6rem; accent-color: var(--leaf); }
.pack-bar button {
  font: inherit;
  font-family: var(--disp);
  border: 1.5px solid var(--rule);
  background: var(--surface);
  color: var(--ink);
  border-radius: 8px;
  padding: .3rem .75rem;
  cursor: pointer;
}
.pack-bar button:hover { background: var(--sunk); }

/* ---------------- правила ---------------- */

/* 11 правил в одну колонку не помещались в экран — раскладываем газетой:
   порядок чтения сохраняется (вниз по левой, потом по правой). */
.rules { max-width: var(--col); }
.warn.wide { max-width: none; }
/* Линейка та же, что между ячейками чек-листа: линеек на сайте два вида быть
   не должно — одна нарисованная, другая по линейке. */
.rule-item {
  padding: .34rem 0;
  break-inside: avoid;
  position: relative;
}
.rule-item + .rule-item::before {
  content: "";
  position: absolute;
  left: 0; right: 0; top: -4px;
  height: 8px;
  background: var(--rule);
  pointer-events: none;
}
.rule-item p { margin: 0; }
.rule-item .why { color: var(--muted); font-size: var(--s--1); display: block; margin-top: .1rem; }

/* Значок пункта живёт в ::after: ::before у пункта занят линейкой-разделителем.
   Поправок по центру тяжести, как у значков удобств, тут нет: рисунки сделаны
   в одном поле 48×48 и уже выровнены между собой. */
/* Высота пункта задана снизу: у значков ноги и рукояти доходят до самого края
   рисунка, и в коротком пункте они попадали на линейку следующего. */
.rules .rule-item { padding-left: 3.2rem; min-height: 2.9rem; }
.rule-item::after {
  content: "";
  position: absolute;
  left: 0;
  top: .3rem;
  width: 2.5rem;
  height: 2rem;
  background-repeat: no-repeat;
  background-position: center;
  background-size: auto 2rem;
}
.r-deti::after    { background-image: var(--sh-pr-deti); }
.r-ravny::after   { background-image: var(--sh-pr-ravny); }
.r-mashiny::after { background-image: var(--sh-pr-mashiny); }
.r-koster::after  { background-image: var(--sh-pr-koster); }
.r-topor::after   { background-image: var(--sh-pr-topor); }
.r-stoly::after   { background-image: var(--sh-pr-stoly); }
.r-kolonka::after { background-image: var(--sh-pr-kolonka); }
.r-fonar::after   { background-image: var(--sh-pr-fonar); }
.r-sobaka::after  { background-image: var(--sh-pr-sobaka); }
.r-musor::after   { background-image: var(--sh-pr-musor); }
.r-butylka::after { background-image: var(--sh-pr-butylka); }
.r-listok::after  { background-image: var(--sh-pr-listok); }

/* Правила идут одной колонкой: так их проще читать подряд. */


/* ---------------- дорога ---------------- */

.route { border: 1px solid var(--rule); border-radius: var(--radius); background: var(--surface); overflow: hidden; }
.route + .route { margin-top: 1rem; }
.route > summary {
  padding: .95rem 1.1rem;
  cursor: pointer;
  font-family: var(--disp);
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: .7rem;
  list-style: none;
}
.route > summary::-webkit-details-marker { display: none; }
.route > summary::after {
  content: "";
  margin-left: auto;
  width: .5rem; height: .5rem;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  transform: rotate(45deg);
  transition: transform .15s ease;
}
.route[open] > summary::after { transform: rotate(-135deg); }
.route > summary .tag {
  font-family: var(--mono);
  font-weight: 400;
  font-size: var(--s--2);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--muted);
}
.route-body { padding: 0 1.1rem 1.1rem; }
.route-body > :first-child { margin-top: 0; }

.taxi { list-style: none; padding: 0; margin: .8rem 0 0; display: grid; gap: .55rem; }
.taxi li { display: flex; flex-wrap: wrap; align-items: baseline; gap: .3rem .7rem; margin: 0; }
.taxi .name { font-weight: 500; }
.taxi .tel { font-family: var(--mono); color: var(--accent); text-decoration: none; white-space: nowrap; }
.taxi .tel:hover { text-decoration: underline; }
.taxi .note { color: var(--muted); font-size: var(--s--1); flex-basis: 100%; }

/* Предупреждение выделено маркером: три слоя-маски — волна сверху, волна снизу
   и ровная середина, которая одна и тянется. Высота у плашек разная, от трёх
   строк до десяти, и одной растянутой картинкой волну бы размазало.
   Маска режет и текст, поэтому поля по вертикали держим больше размаха волны. */
.warn {
  border-left: 5px solid var(--accent-flat);
  background-color: var(--accent-soft);
  padding: 1.05rem .95rem;
  margin: 1.2rem 0;

  -webkit-mask-image: var(--sh-marker-top), var(--sh-marker-mid), var(--sh-marker-bottom);
          mask-image: var(--sh-marker-top), var(--sh-marker-mid), var(--sh-marker-bottom);
  -webkit-mask-position: 0 0, 0 22px, 0 100%;
          mask-position: 0 0, 0 22px, 0 100%;
  -webkit-mask-size: 100% 22px, 100% calc(100% - 44px), 100% 22px;
          mask-size: 100% 22px, 100% calc(100% - 44px), 100% 22px;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
}
.warn p { margin: 0; }
.warn b { color: var(--accent); }

/* Про ответственность родителей — сильнее обычного предупреждения. */
.warn-strong {
  border-left-width: 6px;
  padding: .82rem 1.05rem;
}
.warn-strong b {
  font-family: var(--disp);
  font-weight: 700;
  font-size: var(--s-0);
  line-height: 1.35;
  display: block;
}

/* ---------------- фотографии ---------------- */

figure { margin: 1.15rem 0; }
figure img { border-radius: var(--radius); }

/* Фотографии открываются крупно — поэтому это кнопка, а не картинка. */
.shot {
  display: block;
  padding: 0;
  border: 0;
  background: none;
  width: 100%;
  cursor: zoom-in;
  border-radius: var(--radius);
  transition: transform .15s ease;
}
.shot:hover { transform: translateY(-2px); }
.shot:focus-visible { outline: 3px solid var(--accent); outline-offset: 5px; }

/* ПРОБА: у снимка неровный край — не рамка поверх, а его собственная форма.
   Дрожь гаснет к концам сторон, поэтому углы остаются почти прямыми: без
   этого стороны на углу расходятся и он выпирает пузырём. */
.shot { position: relative; border-radius: 0; }
.shot img {
  border-radius: 0;
  -webkit-mask-image: var(--sh-photo-edge);
          mask-image: var(--sh-photo-edge);
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
}

/* ---------------- просмотр фотографии ---------------- */

.lb {
  position: fixed;
  inset: 0;
  z-index: 90;
  background: rgba(7, 22, 14, .94);
  display: grid;
  grid-template-rows: 1fr auto;
  place-items: center;
  padding: clamp(.75rem, 3vw, 2.5rem);
  gap: .9rem;
}

.lb img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  border-radius: 10px;
  box-shadow: 0 24px 70px -20px rgba(7, 22, 14, .85);
}

.lb-stage { display: grid; place-items: center; width: 100%; height: 100%; min-height: 0; }

.lb-foot { color: #E7F1E9; text-align: center; max-width: 60ch; }
.lb-cap { margin: 0; font-size: var(--s--1); line-height: 1.5; }
.lb-count { margin: .35rem 0 0; font-size: var(--s--2); letter-spacing: var(--ls-label); color: #9FBCA9; }

.lb button {
  position: absolute;
  border: 0;
  background: rgba(255, 255, 255, .12);
  color: #fff;
  border-radius: 50%;
  width: 3rem; height: 3rem;
  font-size: var(--s-2);
  line-height: 1;
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: background .15s ease;
}
.lb button:hover { background: rgba(255, 255, 255, .26); }
.lb button:focus-visible { outline: 3px solid var(--sun); outline-offset: 2px; }

.lb-close { top: clamp(.6rem, 2vw, 1.4rem); right: clamp(.6rem, 2vw, 1.4rem); }
.lb-prev  { left: clamp(.4rem, 2vw, 1.6rem); top: 50%; transform: translateY(-50%); }
.lb-next  { right: clamp(.4rem, 2vw, 1.6rem); top: 50%; transform: translateY(-50%); }
.lb[data-single="1"] .lb-prev, .lb[data-single="1"] .lb-next { display: none; }

body.lb-open { overflow: hidden; }
figcaption { margin-top: .55rem; color: var(--muted); font-size: var(--s--1); }

.strip {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  /* Зазор больше прежнего: рамки вынесены за край снимка на 5px с каждой
     стороны, и при старых .7rem соседние рамки почти слипались. */
  gap: 1.1rem;
}
/* Фотографий всегда 4 или 8, поэтому колонок 2 или 4 — при трёх последний ряд рвётся. */
@media (min-width: 44rem) { .strip { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
/* ПРОБА: углы прямые — см. рамку у .shot выше. */
.strip img { border-radius: 0; aspect-ratio: 3 / 2; object-fit: cover; width: 100%; }

.wide { max-width: none; }   /* карта и широкие фигуры — во всю колонку */
.wide figcaption { max-width: var(--col); }

/* ---------------- архив ---------------- */

/* Ширина колонки с датой и положение линии считаются из одних переменных,
   чтобы линия и точки не разъезжались с текстом. Самая длинная дата —
   «Сентябрь 2026», 13 знаков моноширинным. */
.timeline {
  --when-w: 8.2rem;
  --dot-w: 1.9rem;
  list-style: none;
  padding: 0;
  position: relative;
}
.timeline::before {
  content: "";
  position: absolute;
  left: calc(var(--when-w) + var(--dot-w) / 2 - 7px);
  top: .6rem; bottom: .6rem;
  width: 14px;
  background: var(--rule);
}
/* На телефоне точки переезжают в колонку шириной 1.4rem и стоят в её середине —
   нитка должна попасть туда же, иначе она уходит к самому краю. */
@media (max-width: 40rem) { .timeline::before { left: calc(.7rem - 7px); } }

.timeline li {
  display: grid;
  grid-template-columns: var(--when-w) var(--dot-w) 1fr;
  gap: 0 0;
  margin: 0;
  padding: 1rem 0;
}
@media (max-width: 40rem) { .timeline li { grid-template-columns: 1.4rem 1fr; } }

.timeline .when {
  font-family: var(--mono);
  font-size: var(--s--1);
  color: var(--muted);
  text-align: right;
  padding-top: .3rem;
  padding-right: .8rem;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
@media (max-width: 40rem) {
  .timeline .when { grid-column: 2; text-align: left; margin-bottom: .15rem; }
}

.timeline .dot { display: grid; place-items: start center; padding-top: .65rem; }
.timeline .dot i {
  display: block;
  width: .68rem; height: .68rem;
  background: var(--leaf);
  font-style: normal;
}
.timeline li.city .dot i { background: var(--sun); }
.timeline li.next .dot i { background: var(--accent-flat); width: .92rem; height: .92rem; margin-top: -.12rem; }
@media (max-width: 40rem) { .timeline .dot { grid-row: 1 / 4; grid-column: 1; } }

.timeline .what b {
  font-family: var(--disp);
  font-weight: 700;
  font-size: var(--s-1);
  line-height: 1.2;
  letter-spacing: -.012em;
  display: block;
  text-wrap: balance;
}
.timeline .what b a { color: var(--ink); text-decoration: none; }
.timeline .what b a:hover { text-decoration: underline; }
.timeline li.themed .what b a { color: var(--accent); }
.timeline .meta { display: block; color: var(--muted); font-size: var(--s--1); margin-top: .18rem; }

.legend { display: flex; flex-wrap: wrap; gap: .4rem 1.2rem; margin-top: 1.2rem; font-size: var(--s--1); color: var(--muted); }
.legend b { display: inline-flex; align-items: center; gap: .4rem; font-weight: 400; }
/* Легенда объясняет точки на нитке — значит и рисоваться должна так же,
   иначе идеальный круг в легенде обещает не тот значок, что стоит в хронике. */
.legend i { width: .68rem; height: .68rem; display: block; }

/* ---------------- подвал ---------------- */

.foot {
  border-top: 1px solid var(--rule);
  background: var(--sunk);
  /* Содержимое подвала — одна строка ссылок в 19px, поля под неё и считаны.
     Меньше уже нельзя: строка упрётся в края серой полосы. */
  padding-block: 1rem 1.15rem;
}
.foot-in {
  max-width: 84rem;
  margin-inline: auto;
  padding-inline: var(--bleed);
}
/* Ссылки одной строкой через точку, без заголовков групп. Прежде подвал
   держал шесть ссылок в двух столбцах с заголовками и занимал 187px при
   содержимом в 98. Осталась только «Связь» — три ссылки, остальные и так
   стоят на страницах, где о них речь: регистрация на обложке, прокат
   в «Быте», геопозиция в «Дороге». Точка-разделитель та же, что в датах
   хроники: другого разделителя на сайте нет. */
.foot-links {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem 1rem;
  list-style: none;
  padding: 0;
  margin: 0;
}
.foot-links li { margin: 0; display: flex; align-items: baseline; gap: 1rem; }
.foot-links li + li::before { content: "·"; color: var(--muted); }

/* На узком экране ссылки не встают в ряд — тогда столбиком и без точек:
   точка перед второй и третьей читалась маркером списка, которого у первой
   почему-то нет. */
@media (max-width: 47.99rem) {
  .foot-links { flex-direction: column; gap: .4rem; }
  .foot-links li + li::before { display: none; }
}
/* Серая полоса — во всю ширину окна, а СОДЕРЖИМОЕ подвала начинается под
   колонкой текста: тогда ссылки не попадают под прикреплённое меню.
   Сам подвал теперь в 55px, и на окне от ~900px меню до него не доходит
   вовсе. Если окно ниже и они всё-таки встречаются, подвал накрывает меню
   собой (z-index выше) — меню выглядит оборванным о край страницы, а не
   лежащим поверх серого. */
@media (min-width: 64rem) {
  .foot { z-index: 21; }
  .foot-in {
    margin-inline: 0;
    max-width: calc(15.5rem + var(--shell-gap) + 68rem);
    padding-left: calc(15.5rem + var(--shell-gap) + var(--bleed));
  }
}

.foot li { font-size: var(--s--1); }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

/* Части стоимости со значками. Значки цветные, как логотип и палатки в шапке,
   поэтому идут фоном, а не маской: цвет — часть рисунка, и в тёмной теме они
   остаются такими же. Номера при значках не нужны — части равноправны. */
.parts { list-style: none; padding-left: 0; }
/* Значок вынут из потока, а строке отдан отступ под него. Сеткой не выйдет:
   в ней каждый элемент строки становится своей ячейкой, и «Оргвзнос», тире
   и остальные слова разъезжаются по отдельным клеткам. */
.parts li {
  position: relative;
  padding-left: 4.75rem;
  min-height: 2.5rem;
  margin-bottom: .5rem;
}
.parts li::before {
  content: "";
  position: absolute;
  left: 0;
  /* По середине ПЕРВОЙ СТРОКИ, а не по середине пункта: пункт выше строки на
     высоту значка, и от `top: 50%` все значки уезжали на 7px ниже своих
     надписей. .78em — половина строки при line-height 1,55. */
  top: .78em;
  transform: translateY(-50%);
  /* Высота у всех значков одна, ширина у каждого своя: её задаёт
     `background-size: auto <высота>`, а не вписывание в квадрат — при
     вписывании широкая летящая палатка упиралась в ширину и выходила вдвое
     ниже высоких ёлок. Коробка выше рисунка: разница — запас, в котором
     значок сдвигается по вертикали (см. ниже). */
  width: 4.1rem;
  /* Коробка выше рисунка на 16px: в этом запасе значок и сдвигается по
     вертикали. Молотку нужно +5px, а при высоте 3rem запаса было 4px —
     фон обрезался бы по нижнему краю элемента. */
  height: 3.5rem;
  background-size: auto 2.5rem;
  background-repeat: no-repeat;
  background-position: center;
}

/* Ровняем по центру ТЯЖЕСТИ рисунка, а не по его краям: у миски пар сверху,
   а масса внизу, и по краям она вставала на 4px ниже своей надписи. Сдвиги
   посчитаны замером: доля высоты, на которой лежит центр тяжести чернил,
   у молотка 0,369, у палатки 0,532, у ёлок 0,575, у миски 0,594 — отсюда
   и поправки при высоте рисунка 40px. У молотка вся масса в голове, поэтому
   поправка вдвое больше прочих. */
.p-fee::before  { background-image: var(--sh-pic-fee);  background-position: center calc(50% + 5px); }
.p-food::before { background-image: var(--sh-pic-food); background-position: center calc(50% - 4px); }
/* Ёлки и летящая палатка взяты прямо из шапки, а не нарисованы заново: это
   предметы логотипа, и узнаются они именно в своём виде. У ёлок отрезана
   маленькая палатка справа — палатка стоит отдельным значком, и две подряд
   путали бы. */
.p-camp::before { background-image: url("img/w/el/trees-firs.png"); background-position: center calc(50% - 3px); }
.p-gear::before { background-image: url("img/w/el/fly-a.png"); background-position: center calc(50% - 1px); }

/* ---------------- значки «Удобств» ---------------- */

/* Восемь предметов, из которых человек ищет один: «а душ есть?». Значок здесь
   работает не украшением, а способом найти строку не читая — поэтому он есть
   только у «Удобств» и больше ни в одном справочнике сайта.

   Мельче, чем у частей стоимости: 30px против 40. Там четыре пункта во весь
   раздел, здесь восемь записей подряд, и на 40px рисунки начинали спорить
   с текстом. */
.recs .rec.has-pic {
  position: relative;
  padding-left: 3.7rem;
  min-height: 1.9rem;
}
.recs .rec.has-pic::before {
  content: "";
  position: absolute;
  left: 0;
  /* По середине первой строки, как у частей стоимости: запись выше строки на
     высоту значка, и от `top: 50%` он уезжал бы ниже своего названия. */
  top: .78em;
  transform: translateY(-50%);
  /* Коробка выше рисунка: разница — запас, в котором значок сдвигается по
     вертикали. Ширины хватает самому широкому — машине (30px в высоту дают
     ей 48 в ширину). */
  width: 3.1rem;
  height: 2.8rem;
  background-size: auto 1.875rem;
  background-repeat: no-repeat;
  background-position: center;
}

/* Сдвиги — по центру тяжести чернил, как у частей стоимости: (0,5 − доля) × 30px.
   Доли замерены centroid2.mjs: душ 0,368 (масса в лейке, капли лёгкие),
   Wi-Fi 0,453, вилка 0,470, туалет 0,481, холодильник 0,504, мангал 0,508,
   машина 0,522, кастрюля 0,530. */
.i-park::before   { background-image: var(--sh-pic-park);   background-position: center calc(50% - 1px); }
.i-wc::before     { background-image: var(--sh-pic-wc);     background-position: center calc(50% + 1px); }
.i-plug::before   { background-image: var(--sh-pic-plug);   background-position: center calc(50% + 1px); }
.i-shower::before { background-image: var(--sh-pic-shower); background-position: center calc(50% + 4px); }
.i-fridge::before { background-image: var(--sh-pic-fridge); }
.i-pot::before    { background-image: var(--sh-pic-pot);    background-position: center calc(50% - 1px); }
.i-grill::before  { background-image: var(--sh-pic-grill); }
.i-wifi::before   { background-image: var(--sh-pic-wifi);   background-position: center calc(50% + 1px); }

/* Приписка в сторону: не предупреждение и не запись списка, а отдельная просьба.
   Отдельный класс, потому что .note уже занят в подвале и в блоке такси. */
.apart {
  margin-top: 1.2rem;
  padding: .1rem 0 .1rem .85rem;
  border-left: 3px solid var(--leaf);
  color: var(--muted);
  max-width: var(--col);
}
.apart b { color: var(--ink); }

/* ═══════════ ПОЛЕВОЙ СПРАВОЧНИК: одна форма на всё содержание ═══════════ */

/* Запись: тезис строкой, под ним серое пояснение, между записями тонкая
   линейка. Это ровно та форма, что уже работает в Правилах. */
.recs { max-width: var(--col); }

.rec { break-inside: avoid; }
.rec p { margin: 0; }
.rec .why {
  color: var(--muted);
  font-size: var(--s--1);
  display: block;
  margin-top: .1rem;
}


/* Заголовки тёмно-зелёные, а не чернильные: на этой странице всё рисованное —
   ёлки, палатки, рамки — сделано этим же зелёным, и заголовок в него ложится
   естественнее, чем в почти-чёрный. Контраст на бумаге 6,5 при пороге 4,5. */
h1, h2, h3 { color: var(--forest); }
.sec-head { margin-bottom: .8rem; }

/* ═══════════ ПОДЗАГОЛОВОК ГРУППЫ ═══════════
   Он лежит внутри .col, поэтому общее правило «после блока — заголовок» до него
   не доставало, и группа сливалась с записями выше. Даём воздух и линию —
   пусть линия работает на структуру, а не разделяет каждую запись. */
/* Ровный ритм между блоками раздела. Раньше отступ был только у абзацев снизу,
   поэтому вставки вроде «А также:» прилипали и сверху, и снизу — воздуха между
   блоками не было вообще. */
section > * + * { margin-top: 1.2rem; }

/* Не перечисляем, после чего именно идёт группа: любой подзаголовок, который
   открывает не первый блок раздела, начинает новую группу. Заплатки на каждый
   случай уже один раз подвели — «Берегите воду» слиплось с припиской.
   Добавка к общему ритму, а не вместо него — иначе отступы складываются. */
section > .col:not(:first-child) > h3:first-child {
  margin-top: .8rem;
  padding-top: .5rem;
  border-top: 2px solid var(--accent-soft);
}

/* Записи держатся на пустом месте: линеек нет, тезис и пояснение стоят вплотную
   друг к другу, а между записями просвет. Выбран из четырёх вариантов. */
.rec { padding: 0; border: 0; margin-bottom: 1rem; }
.rec:last-child { margin-bottom: 0; }
.rec .why { margin-top: .05rem; }

/* ---------------- узор ---------------- */

/* Россыпь из элементов логотипа. Лежит на z-index -1, то есть под всем
   содержимым: таблицы, карточки и картинки проходят поверх неё.
   Боковые полосы приклеены к окну, а не к колонкам, — иначе полоса за меню
   ездила бы вместе с его внутренней прокруткой. */
.pat {
  position: fixed;
  top: 0; bottom: 0;
  z-index: -1;
  pointer-events: none;
  -webkit-mask-image: url("img/w/el/pattern-5d74b5fc.png");
          mask-image: url("img/w/el/pattern-5d74b5fc.png");
  -webkit-mask-size: 340px;
          mask-size: 340px;
  -webkit-mask-repeat: repeat;
          mask-repeat: repeat;
  background-color: var(--forest);
  opacity: .13;
}

.pat-l { left: 0;  width: calc(15.5rem + var(--bleed)); }
/* Правая полоса — той же ширины, что третья колонка на главной. */
.pat-r { right: 0; width: 26%; }

.pat-f {
  position: absolute;
  inset: 0;
  width: auto;
}

.foot { position: relative; overflow: hidden; }
.foot-in { position: relative; }

/* На узком экране колонок нет — боковые полосы прятать. */
@media (max-width: 63.99rem) {
  .pat-l, .pat-r { display: none; }
}

/* ---------------- третья колонка ---------------- */

/* Появляется только там, где для неё есть содержимое, — сейчас на главной.
   В неё уходят постоянные факты о слёте; обложка и кнопки остаются в основной
   колонке, иначе на телефоне регистрация уехала бы в самый низ. */
.rail { padding-block: 1.4rem; }

.rail h4 {
  font-family: var(--disp);
  font-size: var(--s--1);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: .5rem;
}

.rail .facts { display: block; }
.rail .facts > div + div { margin-top: .9rem; }
.rail .facts dt {
  font-family: var(--disp);
  font-weight: 600;
  font-size: var(--s--1);
  color: var(--forest);
  margin-bottom: .12rem;
}
.rail .facts dd { margin: 0; font-size: var(--s--1); line-height: 1.45; color: var(--muted); }

@media (max-width: 63.99rem) {
  .rail { border-top: 1px solid var(--rule); margin-top: 1.4rem; }
}

/* «Как попасть на Слёт» переехало в правую колонку. Она узкая, поэтому шаги
   ужимаем: бусина занимает меньше места, а текст переносится. */
.rail-steps { margin-top: 1.6rem; }
.rail-steps .steps {
  --bead-w: 1.3rem;
  --step-gap: .85rem;
  font-size: var(--s--1);
}
.rail-steps .steps a { font-size: var(--s--1); }

@media (min-width: 64rem) {
  .shell.three {
    grid-template-columns: 15.5rem minmax(0, 1fr) 26%;
    gap: 0 clamp(1.2rem, 2.2vw, 2rem);
  }
  .shell.three > .rail { grid-column: 3; }
  .rail {
    position: sticky;
    top: calc(var(--head-c) + 3px + .7rem);
    border-left: 1px solid var(--rule);
    padding-left: 1.3rem;
  }
}

/* ---------------- один правый край ---------------- */

/* Текст шёл на 688px, а карточки, таблица и фотополосы — на 835 и торчали
   вправо. Теперь у всего содержимого одна правая граница. */
/* Текст держит свою меру, а карточки, таблица и фото занимают всю колонку.
   Колонка при этом кончается ровно там, где начинается узор: раньше между
   ними оставалась пустая полоса. */
@media (min-width: 64rem) {
  .shell:not(.three) main {
    /* 93% места до узора: карточки и фото не упираются в него вплотную. */
    max-width: calc((74vw - var(--bleed) - 15.5rem - var(--shell-gap)) * .93);
  }
}

/* ---------------- роли и активности одинаково ---------------- */

/* По сути это одно и то же: названная группа с содержимым. Значит и вид один —
   карточка с цветной меткой в заголовке. */
.roles, .program {
  display: grid;
  gap: 1.6rem 2.5rem;
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 44rem) {
  .roles, .program { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.role, .prog { min-width: 0; }


/* Карточка обведена от руки: два слоя-маски — неровная заливка и неровная
   линия поверх. Маска, а не готовая картинка, чтобы цвет линии оставался
   управляемым: у каждой категории он свой. Тот же почерк, что в логотипе. */
.role, .prog {
  background: none;
  border: 0;
  border-radius: 0;
  position: relative;
  isolation: isolate;
  --edge: var(--rule);
  padding: 1.05rem 1.15rem;
  /* Порядок в разметке — нагрузка, название, текст. На экране название должно
     идти первым, поэтому колонка с явным порядком. */
  display: flex;
  flex-direction: column;
}
.role h3 { order: 1; }
.role .load { order: 2; }
.role p { order: 3; }
.prog { display: block; }

.roles h3, .program h3 {
  display: flex;
  align-items: center;
  gap: .55rem;
  margin-bottom: .45rem;
  font-size: var(--s-1);
}
/* Метка роли — такой же камешек, что и бусины на нитке: ровный квадрат
   со скруглением спорил с рисованной рамкой вокруг него. */
.roles h3::before, .program h3::before {
  content: "";
  width: .85rem; height: .85rem;
  flex: none;
  background: var(--leaf);
}
/* А у категорий «Программы» вместо камешка рисунок: бегущая фигурка, кисть,
   кубик, нота. Цвет остался тот же, что был у камешка, — он у категории свой.
   28px, а не 24: на 24 фигурка и кисть сливались в кляксу. */
.program h3::before {
  width: 1.75rem; height: 1.75rem;
  border-radius: 0;
}
/* Цвет категории — и на метке в заголовке, и на верхнем крае карточки.
   У ролей так было, у активностей край оставался серым. */
.prog--move  { border-top-color: var(--accent-flat); }
.prog--make  { border-top-color: var(--sun); }
.prog--think { border-top-color: var(--leaf); }
.prog--sing  { border-top-color: var(--forest); }

.roles .role:nth-child(1) h3::before { background: var(--forest); }
.roles .role:nth-child(2) h3::before { background: var(--leaf); }
.roles .role:nth-child(3) h3::before { background: var(--lime); }
.roles .role:nth-child(4) h3::before { background: var(--sun); }

/* Цвета категорий должны идти после общего правила: специфичность одинаковая,
   и раньше общее .program h3::before перекрашивало все метки в один зелёный. */
.program .prog--move h3::before  { background: var(--accent-flat); }
.program .prog--make h3::before  { background: var(--sun); }
.program .prog--think h3::before { background: var(--leaf); }
.program .prog--sing h3::before  { background: var(--forest); }

/* Нагрузка — отдельная метка в углу карточки, а не строка описания. */
.role { container-type: inline-size; }
.role .load {
  position: absolute;
  top: 1.05rem;
  right: 1.15rem;
  font-family: var(--mono);
  font-size: var(--s--2);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--ink);
  background-color: var(--sunk);
  /* Полей больше прежнего: мазок не может выйти за края плашки, поэтому
     запас, на котором он «перехлёстывает» текст, приходится держать внутри. */
  padding: .3rem .66rem;
  white-space: nowrap;

  /* Маска красит фон, но заодно режет и текст — поля выше держат буквы
     подальше от волнистого края, иначе им срезает верхушки. */
  -webkit-mask-image: var(--sh-marker);
          mask-image: var(--sh-marker);
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
}
/* Заголовку нужно место под метку, иначе длинное название наедет. */
.role h3 { padding-right: 8.5rem; }

/* На узкой карточке места в углу нет — «Помощник» подходил к метке вплотную.
   Меряем саму карточку, а не окно: её ширина зависит от колонки, а не от экрана. */
@container (max-width: 23rem) {
  .role .load { position: static; align-self: flex-start; margin-top: .1rem; }
  .role h3 { padding-right: 0; }
}
.role p { margin: 0; }

/* ---------------- таблица карточкой ---------------- */

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

   Прежний блок про прокрутку вбок (примороженная первая колонка, тень
   у края, подсказка под таблицей) убран: он был нужен, пока таблица была
   одна на девять колонок. Сейчас их две по пять, и они влезают в телефон. */
.table-card {
  position: relative;
  isolation: isolate;
  --edge: var(--rule);
  padding: .55rem;
}

.table-card::before, .table-card::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
}
.table-card::before {
  -webkit-mask-image: var(--sh-frame-fill);
          mask-image: var(--sh-frame-fill);
  background-color: var(--surface);
}
.table-card::after {
  -webkit-mask-image: var(--sh-frame-line);
          mask-image: var(--sh-frame-line);
  background-color: var(--edge);
}
/* Угловая ячейка: слева внизу «Роль родителя» — про строки, вдоль правого
   края вертикально «Возраст» — про колонки.

   Поля у ячейки нулевые, а обе подписи отодвинуты от краёв одной и той же
   долей: только так отступ «Возраста» от правой стороны и отступ «Роли»
   от нижней получаются РАВНЫМИ. Считай у ячейки свои поля — они легли бы
   сверху и сломали это равенство. Раз подписи вне потока, высоту ячейки
   держим сами. */
.corner {
  position: relative;
  --corner-gap: .65rem;
  padding: 0;
  min-height: 3.6em;
  font-size: var(--s--2);
  line-height: 1.25;
  text-align: left;
}
.corner .c-age {
  position: absolute;
  top: 50%;
  right: var(--corner-gap);
  /* Повёрнуто на 180° относительно обычного вертикального набора: строка
     читается снизу вверх. */
  transform: translateY(-50%) rotate(180deg);
  writing-mode: vertical-rl;
}
.corner .c-role {
  position: absolute;
  left: var(--corner-gap);
  bottom: var(--corner-gap);
  padding-right: 1.5em;
}

/* Линия рамки — в цвет своей половины: у оргвзноса оранжевый, у кемпинга
   зелёный, те же, что у заливки колонок внутри. */
.t-org  { --edge: var(--accent-flat); }
.t-camp { --edge: var(--forest); }

/* ---------------- обводка от руки ---------------- */

.role::before, .prog::before,
.role::after,  .prog::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
}

/* заливка */
.role::before, .prog::before {
  -webkit-mask-image: var(--sh-frame-fill);
          mask-image: var(--sh-frame-fill);
  background-color: var(--surface);
}

/* линия — цветом категории */
.role::after, .prog::after {
  -webkit-mask-image: var(--sh-frame-line);
          mask-image: var(--sh-frame-line);
  background-color: var(--edge);
}

.roles .role:nth-child(1) { --edge: var(--forest); }
.roles .role:nth-child(2) { --edge: var(--leaf); }
.roles .role:nth-child(3) { --edge: var(--lime); }
.roles .role:nth-child(4) { --edge: var(--sun); }

.prog--move  { --edge: var(--accent-flat); }
.prog--make  { --edge: var(--sun); }
.prog--think { --edge: var(--leaf); }
.prog--sing  { --edge: var(--forest); }

/* Нитка, бусины, квадратики и линейки — тоже рисованные. Форму задаёт маска,
   цвет остаётся в CSS: одна фигура служит и светлой теме, и тёмной, и красится
   в цвет раздела. Файлы собирает sketch.py. */
.timeline::before,
.timeline .dot i,
.steps li::before,
.legend i,
.roles h3::before, .program h3::before,
.pack li + li::before,
.rule-item + .rule-item::before,
.pack input {
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
}

/* Нитка повторяется, а не растягивается: иначе в коротком списке шагов волна
   сжималась бы в змейку, а в длинной хронике распрямлялась в струну. */
.timeline::before {
  -webkit-mask-image: var(--sh-thread);
          mask-image: var(--sh-thread);
  -webkit-mask-size: 14px 400px;
          mask-size: 14px 400px;
  -webkit-mask-repeat: repeat-y;
          mask-repeat: repeat-y;
}

/* Три камешка по кругу — чтобы соседние бусины и метки не были близнецами. */
.timeline .dot i, .steps li::before, .legend i,
.roles h3::before {
  -webkit-mask-image: var(--sh-dot-a);
          mask-image: var(--sh-dot-a);
}

/* Маска метки — своя у каждой категории. */
.program .prog--move h3::before  { -webkit-mask-image: var(--sh-ic-move);  mask-image: var(--sh-ic-move); }
.program .prog--make h3::before  { -webkit-mask-image: var(--sh-ic-make);  mask-image: var(--sh-ic-make); }
.program .prog--think h3::before { -webkit-mask-image: var(--sh-ic-think); mask-image: var(--sh-ic-think); }
.program .prog--sing h3::before  { -webkit-mask-image: var(--sh-ic-sing);  mask-image: var(--sh-ic-sing); }
.timeline li:nth-child(3n+2) .dot i,
.steps li:nth-child(3n+2)::before,
.legend b:nth-child(2) i,
.roles .role:nth-child(2) h3::before {
  -webkit-mask-image: var(--sh-dot-b);
          mask-image: var(--sh-dot-b);
}
.timeline li:nth-child(3n) .dot i,
.steps li:nth-child(3n)::before,
.legend b:nth-child(3) i,
.roles .role:nth-child(3) h3::before {
  -webkit-mask-image: var(--sh-dot-c);
          mask-image: var(--sh-dot-c);
}

.pack li + li::before, .rule-item + .rule-item::before {
  -webkit-mask-image: var(--sh-rule);
          mask-image: var(--sh-rule);
}

.pack input {
  -webkit-mask-image: var(--sh-box);
          mask-image: var(--sh-box);
}
.pack input:checked {
  -webkit-mask-image: var(--sh-box-check);
          mask-image: var(--sh-box-check);
}
