/* ┌──────────────────────────────────────────────────────────────────────┐
   │  ФАЙЛ СГЕНЕРИРОВАН. РУЧНЫЕ ПРАВКИ БУДУТ ЗАТЁРТЫ.                     │
   │  Источник:  src/design/tokens.json
   │  Генератор: node scripts/design/build-tokens.mjs                     │
   │  Хвост (.brand-scroll) — scripts/design/tokens-tail.css              │
   └──────────────────────────────────────────────────────────────────────┘ */

/* Для страниц public/expo/**. В dist/ каталога src/styles/ не существует:
   /expo/ лежит в public/, vite копирует его как есть и ссылки не правит.
   Поэтому здесь и @font-face, и :root — одним файлом с путями «../fonts/». */

@font-face {
  font-family: 'Nolde';
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url('../fonts/nolde/nolde.otf') format('opentype');
}
@font-face {
  font-family: '20 Kopeek';
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url('../fonts/kopeek/20-kopeek-book.otf') format('opentype');
}
@font-face {
  font-family: '20 Kopeek';
  font-weight: 400;
  font-style: italic;
  font-display: swap;
  src: url('../fonts/kopeek/20-kopeek-book-italic.otf') format('opentype');
}
@font-face {
  font-family: '20 Kopeek';
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  src: url('../fonts/kopeek/20-kopeek-demibold.otf') format('opentype');
}
@font-face {
  font-family: '20 Kopeek';
  font-weight: 600;
  font-style: italic;
  font-display: swap;
  src: url('../fonts/kopeek/20-kopeek-demibold-italic.otf') format('opentype');
}
@font-face {
  font-family: '20 Kopeek';
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  src: url('../fonts/kopeek/20-kopeek-bold.otf') format('opentype');
}
@font-face {
  font-family: '20 Kopeek';
  font-weight: 700;
  font-style: italic;
  font-display: swap;
  src: url('../fonts/kopeek/20-kopeek-bold-italic.otf') format('opentype');
}
@font-face {
  font-family: '21 Cent';
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url('../fonts/cent/21Cent.woff') format('woff'),
       url('../fonts/cent/21Cent.ttf') format('truetype');
}
@font-face {
  font-family: '21 Cent';
  font-weight: 400;
  font-style: italic;
  font-display: swap;
  src: url('../fonts/cent/21Cent-Italic.woff') format('woff'),
       url('../fonts/cent/21Cent-Italic.ttf') format('truetype');
}
@font-face {
  font-family: '21 Cent';
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  src: url('../fonts/cent/21Cent-Bold.woff') format('woff'),
       url('../fonts/cent/21Cent-Bold.ttf') format('truetype');
}
@font-face {
  font-family: '21 Cent';
  font-weight: 700;
  font-style: italic;
  font-display: swap;
  src: url('../fonts/cent/21Cent-BoldItalic.woff') format('woff'),
       url('../fonts/cent/21Cent-BoldItalic.ttf') format('truetype');
}
@font-face {
  font-family: '21 Cent';
  font-weight: 900;
  font-style: normal;
  font-display: swap;
  src: url('../fonts/cent/21Cent-Black.woff') format('woff'),
       url('../fonts/cent/21Cent-Black.ttf') format('truetype');
}

:root {
  /* ── Бренд-палитра RAL ───────────────────────────────────────── */
  /* Стр. 5–6 гайдбука. Значения не меняются никогда — это RAL. */
  --ink-black:        #000000;                             /* RAL 9005 «Чёрный янтарь» */
  --paper-white:      #F7F9EF;                             /* RAL 9010 «Чистый белый» (тёплый) */
  --paper-pure:       #FFFFFF;                             /* чистый белый для мультимедиа */
  --brass:            #D2B773;                             /* RAL 1002 «Латунь» — акцент, заголовки */
  --signal-red:       #A02128;                             /* RAL 3001 «Сигнальный красный» */
  --slate-blue:       #5D6970;                             /* RAL 7031 «Сине-серый» — 2 этаж */
  --slate-window:     #9DA3A6;                             /* RAL 7040 «Серое окно» — вторичный фон */
  --iron-grey:        #555D61;                             /* RAL 7011 «Железо-серый» — 3 этаж (наш) */
  --graphite:         #435059;                             /* «Графитовый» для тёмных мультимедиа-полей */
  --telegrey-4:       #CFD0CF;                             /* RAL 7047 «Телегрей 4» */

  /* ── Семантические токены ────────────────────────────────────── */
  /* Что каким бренд-цветом красим. Меняется здесь, а не в компонентах. */
  --page-bg:          var(--iron-grey);                    /* фон наших страниц-разделов */
  --page-bg-deep:     var(--graphite);                     /* тёмные панели, venn-board */
  --paper:            var(--paper-white);                  /* карточки на светлом */
  --accent:           var(--brass);                        /* ведущий акцент */
  --accent-alt:       var(--signal-red);                   /* вторичный акцент */
  --ink:              var(--ink-black);                    /* текст на светлом */
  --ink-on-dark:      var(--paper-white);                  /* текст на тёмном */
  --ink-soft:         #2a2f33;                             /* полутон на светлом */
  --ink-faint:        #6c737a;                             /* тонкая мета на светлом */
  --rule:             rgba(255,255,255,0.12);              /* тонкая линия разделителя */

  /* ── Полутона — производные от бренд-цвета ───────────────────── */
  /* «Своих» цветов не бывает (§8): каждый полутон — формула от RAL, */
  /* процент виден в комментарии сгенерированного CSS. */
  /* Лестница бумаги заменяет тёплый слой design-pass-1: --paper-light */
  /* и --paper-warm были ХЕКСОВЫМИ FALLBACK'АМИ на несуществующие токены */
  /* в семи файлах зон maps и ui. Значения подобраны так, чтобы вид */
  /* не поехал: ΔE 2.2 и 4.3 к прежним #efe4cd и #d9c398 — это порог */
  /* различимости, глазом не поймать. */
  --paper-2:         #F4F4E5;                              /* чуть тёплая подложка · = mix({paper-white}, {brass}, 8%) */
  --paper-3:         #F1EEDB;                              /* карточка на бумаге · = mix({paper-white}, {brass}, 16%) */
  --paper-4:         #EDE7CC;                              /* загорелая бумага · = mix({paper-white}, {brass}, 28%) */
  --paper-5:         #E6DBB7;                              /* тень на бумаге · = mix({paper-white}, {brass}, 45%) */
  --paper-6:         #DFCD9D;                              /* край листа, самый тёплый · = mix({paper-white}, {brass}, 66%) */
  --ink-2:           #212527;                              /* длинный текст на светлом · = mix({ink-black}, {slate-blue}, 35%) */
  --ink-3:           #383F43;                              /* мета, подписи · = mix({ink-black}, {slate-blue}, 60%) */
  --scene-bg:        #060503;                              /* фон сцены /expo/: было #0a0502 (ΔE 1.19) и #050301 (ΔE 0.56) — глазом один цвет · = mix({ink-black}, {brass}, 3%) */
  --scene-ink:       #E2D3A7;                              /* текст сцены: было #f0dcae, ΔE 4.16; контраст на фоне 13.69 против 15.02 · = mix({paper-white}, {brass}, 58%) */
  --paper-light:     var(--paper-4);                       /* роль для карт: было var(--paper-light, #efe4cd), ΔE 2.2 */
  --paper-warm:      var(--paper-6);                       /* роль для карт: было var(--paper-warm, #d9c398), ΔE 4.3 */

  /* ── Палитра лагерей ─────────────────────────────────────────── */
  /* Выровнено с брендом там, где это возможно. */
  --camp-red:              var(--signal-red);              /* Красные ← сигнальный красный */
  --camp-red-deep:         #6a0f14;
  --camp-white:            var(--telegrey-4);              /* Белые ← Телегрей 4 */
  --camp-rev-dem:          #8C4A99;                        /* пурпур (вне бренд-палитры, идентифицирует лагерь) */
  --camp-green:            #5A8E55;                        /* оливковый */
  --camp-national:         var(--brass);                   /* Национальные ← латунь */
  --camp-intervention:     #2F4A6B;                        /* грозовой синий */
  --camp-uprising:         #2A8079;                        /* Восстания (вне бренд-палитры, идентифицирует лагерь) — 22 записи в данных, токена не было */

  /* ── Палитра лагерей — начертание ────────────────────────────── */
  /* Цвет лагеря опознаёт территорию заливкой, но как ТЕКСТ на светлом */
  /* читается не всякий: --camp-white даёт 1.45, --camp-national 1.84 */
  /* при пороге WCAG 4.5. На тач-столе с 70 см это нечитаемо. */
  /* Правило вызова простое: заливка — --camp-x, текст — --camp-x-ink. */
  /* Там, где заливка и так проходит порог, -ink ссылается на неё же, */
  /* поэтому на месте вызова выбирать не нужно. */
  --camp-red-ink:              var(--camp-red);            /* 7.20 — затемнять нечего */
  --camp-white-ink:            var(--iron-grey);           /* RAL 7011: та же серая семья, 6.32 вместо 1.45 */
  --camp-rev-dem-ink:          var(--camp-rev-dem);        /* 5.56 — затемнять нечего */
  --camp-green-ink:            #4D7948;                    /* 3.63 → 4.77 · = mix({camp-green}, {ink-black}, 15%) */
  --camp-national-ink:         #7E6E45;                    /* 1.84 → 4.70 · = mix({camp-national}, {ink-black}, 40%) */
  --camp-intervention-ink:     var(--camp-intervention);   /* 8.54 — затемнять нечего */
  --camp-uprising-ink:         #287A73;                    /* 4.43 → 4.79 · = mix({camp-uprising}, {ink-black}, 5%) */

  /* ── Шрифты ──────────────────────────────────────────────────── */
  /* docs/brand/Fonts-requirements.docx. Курсив на Nolde запрещён — файла начертания нет. */
  --font-display:     "Nolde", "Playfair Display", Georgia, serif; /* заголовки, крупные цифры, буквицы. КУРСИВ ЗАПРЕЩЁН */
  --font-body:        "21 Cent", "PT Serif", Georgia, serif; /* основной длинный текст, параграфы */
  --font-accent:      "20 Kopeek", "21 Cent", Georgia, serif; /* короткие надписи, кнопки, метки с «косой чертой» */
  --font-mono:        "20 Kopeek", "JetBrains Mono", "Courier New", monospace; /* акцентные капс-надписи */
  --font-stamp:       "20 Kopeek", "Special Elite", monospace; /* штампы, телеграммы. Special Elite в проект не загружается */

  /* ── Метрики ─────────────────────────────────────────────────── */
  --ui-scale:       1;                                     /* ×2 на киоске — единственный media-query проекта, см. хвост tokens.css */
  --touch-hit:      calc(120px * var(--ui-scale, 1));      /* минимальная тач-цель */
  --fs-body:        calc(32px * var(--ui-scale, 1));       /* 32 — логический размер для 1920, его наследуют 99 элементов из 131 */
  --brand-skew:     -15deg;                                /* угол наклонных полос — 75° от вертикали. На --ui-scale не множится: это угол */

  /* ── Шкалы — отступы, радиусы, толщины ───────────────────────── */
  /* Все длины умножены на --ui-scale: вёрстка ведётся в 1920×1080 */
  /* логических, на киоске 3840×2160 всё удваивается одним переключателем. */
  /* База отступов 8, а не 4: с 70 см разница в 4 px неразличима, */
  /* и половина ступеней была бы мусором. */
  --sp-1:        calc(8px * var(--ui-scale, 1));           /* прижим внутри плашки */
  --sp-2:        calc(16px * var(--ui-scale, 1));          /* между строк списка */
  --sp-3:        calc(24px * var(--ui-scale, 1));          /* внутренние поля карточки */
  --sp-4:        calc(32px * var(--ui-scale, 1));          /* между карточками */
  --sp-5:        calc(48px * var(--ui-scale, 1));          /* между блоками */
  --sp-6:        calc(64px * var(--ui-scale, 1));          /* поля страницы */
  --sp-7:        calc(96px * var(--ui-scale, 1));          /* между разделами */
  --sp-8:        calc(128px * var(--ui-scale, 1));         /* воздух вокруг главного */
  --r-sm:        calc(4px * var(--ui-scale, 1));           /* плашка, чип */
  --r-md:        calc(8px * var(--ui-scale, 1));           /* карточка */
  --r-lg:        calc(16px * var(--ui-scale, 1));          /* модальное, панель */
  --r-pill:      999px;                                    /* капсула. Не множится: и так больше любой высоты */
  --bd-hair:     calc(1px * var(--ui-scale, 1));           /* волосяная линия */
  --bd-1:        calc(1.5px * var(--ui-scale, 1));         /* рамка карточки */
  --bd-2:        calc(3px * var(--ui-scale, 1));           /* акцентная рамка, активное состояние */

  /* ── Шкалы кегля ─────────────────────────────────────────────── */
  /* --ds-* — Nolde, заголовки и крупные цифры. Семь ступеней покрывают */
  /* все кегли, которые уже встречаются в коде. */
  /* --fs-* — 21 Cent, читаемый текст. Нижняя ступень 16 — предел */
  /* для 70 см, ниже не опускаться. */
  --ds-1:     calc(280px * var(--ui-scale, 1));            /* год на главной */
  --ds-2:     calc(210px * var(--ui-scale, 1));            /* год в ленте */
  --ds-3:     calc(150px * var(--ui-scale, 1));            /* буквица */
  --ds-4:     calc(92px * var(--ui-scale, 1));             /* заголовок раздела */
  --ds-5:     calc(64px * var(--ui-scale, 1));             /* заголовок экрана */
  --ds-6:     calc(44px * var(--ui-scale, 1));             /* заголовок карточки */
  --ds-7:     calc(32px * var(--ui-scale, 1));             /* подзаголовок */
  --fs-1:     calc(32px * var(--ui-scale, 1));             /* основной текст, он же --fs-body */
  --fs-2:     calc(24px * var(--ui-scale, 1));             /* вторичный текст */
  --fs-3:     calc(20px * var(--ui-scale, 1));             /* подпись, мета */
  --fs-4:     calc(16px * var(--ui-scale, 1));             /* предел для 70 см, ниже не опускаться */

  /* ── Тени ────────────────────────────────────────────────────── */
  /* Три ступени по назначению, а не по размеру. Самодельные тени */
  /* запрещены (§8): чужая тень читается как другой материал. */
  /* Чернила теней — ink-black с прозрачностью, как у --rule. */
  --sh-1:     0 calc(2px * var(--ui-scale, 1)) calc(8px * var(--ui-scale, 1)) rgba(0,0,0,0.18); /* карточка на плоскости */
  --sh-2:     0 calc(8px * var(--ui-scale, 1)) calc(28px * var(--ui-scale, 1)) rgba(0,0,0,0.28); /* плашка над сценой */
  --sh-3:     0 calc(24px * var(--ui-scale, 1)) calc(64px * var(--ui-scale, 1)) rgba(0,0,0,0.42); /* модальное */

  /* ── Длительности ────────────────────────────────────────────── */
  /* На --ui-scale НЕ множатся — время не растягивается вместе с экраном. */
  /* Реакция на касание обязана уложиться в --dur-base: выше 280 мс */
  /* палец успевает решить, что стенд не ответил, и ткнуть second раз. */
  --dur-fast:     160ms;                                   /* подсветка нажатия */
  --dur-base:     280ms;                                   /* потолок реакции на касание */
  --dur-slow:     600ms;                                   /* смена сцены */

  /* ── Слои по глубине ─────────────────────────────────────────── */
  /* Шаг 100 — между соседями всегда есть место для незапланированного. */
  --z-scene:       0;
  --z-panel:       100;
  --z-overlay:     200;
  --z-modal:       300;

  /* ── Скроллбары ──────────────────────────────────────────────── */
  /* На тач-столе скроллбар должен быть виден — иначе не понять, что блок скроллится. */
  --scroll-thumb:            var(--brass);
  --scroll-thumb-active:     #b88e3a;
  --scroll-track:            rgba(0,0,0,0.45);
  --scroll-width:            calc(14px * var(--ui-scale, 1));

  /* ── Словарь цветов слоя карты ───────────────────────────────── */
  /* ОГРАНИЧИТЕЛЬ для map.json. Цвет слоя живёт в данных карты, а не */
  /* в токенах: 20 слоёв «Поволжья» — не 20 дизайн-решений, а содержимое */
  /* одной карты, и на 15 запланированных картах словарь распух бы */
  /* до сотен записей без единого переиспользования. Признак простой: */
  /* токен переиспользуется между разделами, значение карты — нет. */
  /*  */
  /* Но «цвет в данных» без ограничителя за месяц превращается в 19 */
  /* произвольных хексов на карту — та же вторая палитра с другого конца. */
  /* Поэтому map.json пишет ИМЯ отсюда, а не значение: */
  /*     { "id": "arrows_red", "color": "map-red" } */
  /* Имени нет в словаре — падает brand-lint R10. */
  /*  */
  /* Стороны намеренно ссылаются на --camp-*: красная стрела на карте */
  /* обязана совпадать с «Красными» в гособразованиях, иначе посетитель */
  /* не свяжет карту с разделом. Новый цвет стороны заводится один раз */
  /* в camp и приезжает сюда сам. */
  --map-red:              var(--camp-red);                 /* РККА — стрелы, фронт, территория */
  --map-white:            var(--camp-white);               /* белые армии */
  --map-green:            var(--camp-green);               /* третья сила */
  --map-national:         var(--camp-national);            /* национальные государства */
  --map-uprising:         var(--camp-uprising);            /* восстания */
  --map-rev-dem:          var(--camp-rev-dem);             /* революционная демократия */
  --map-intervention:     var(--camp-intervention);        /* интервенция */
  --map-front:            var(--ink-2);                    /* линия фронта. Дата кодируется штрихом, а не цветом: цвет занят стороной */
  --map-paper:            var(--paper-light);              /* подложка листа */
  --map-territory:        var(--paper-5);                  /* нейтральная территория, без стороны */
  --map-border:           var(--ink-3);                    /* административные границы, ж/д */
  --map-river:            var(--layer-rivers-cyan);        /* реки */
  --map-city:             var(--ink-black);                /* точки городов */
  --map-label:            var(--ink-2);                    /* подписи городов, рек, армий */
  --map-frame:            var(--brass);                    /* рамка, заголовок, легенда */
  --map-decor:            var(--paper-6);                  /* виньетка */

  /* ── Слои карт ───────────────────────────────────────────────── */
  /* Совпадают с HSV-парами из map_v6.ipynb. */
  --layer-background:       transparent;
  --layer-borders-dark:     #1a1a1a;
  --layer-arrows-red:       var(--signal-red);
  --layer-arrows-pink:      #cc0066;
  --layer-front-blue:       #0033cc;
  --layer-front-green:      #006600;
  --layer-rivers-cyan:      #5ba8a0;
}

/* Ручной хвост tokens.css. Генератор дописывает этот файл без изменений
   после сгенерированного блока :root.

   Здесь живёт только то, что не является токеном, но обязано ехать вместе
   с ними: правила, которые нужны страницам /expo/, а те подключают
   tokens.css и НЕ подключают base.css. Переносить в base.css нельзя —
   на экспо-страницах пропадут скроллбары.

   Значения брать только из var(--…). Сырой hex здесь ловит brand-lint (R1). */

/* ЕДИНСТВЕННЫЙ MEDIA-QUERY ПРОЕКТА — И В КАНОНЕ ОН НЕ СРАБАТЫВАЕТ.

   Канон запуска (CLAUDE.md §5, решение 2026-08-04): окно 1920 + флаг
   --force-device-scale-factor=2. Вьюпорт там 1920 CSS-пикселей, порог
   2400 не достигается, --ui-scale остаётся 1 — и это ОЖИДАЕМО, а не
   поломка. Физические 3840×2160 набираются удвоением на стороне
   браузера, а не удвоением чисел в CSS.

   Механизм остаётся страховкой для второго режима — 3840 CSS при DPR 1.
   Там вёрстка в 1920 заняла бы четверть экрана, и множитель её спасает.
   Порог 2400, а не 3840: между ними наших экранов не бывает, а запас
   ловит режимы вроде 2560.

   ЗАПРЕТ ФЛАГА, СТОЯВШИЙ ЗДЕСЬ ДО 2026-08-04, БЫЛ НЕВЕРЕН. Он гласил:
   «при DPR 2 clientWidth станет 1920, --ui-scale останется 1, и растр
   отрисуется как 1× на физически двойном экране». Это ошибка в самом
   рассуждении, а не в числах.

   Сцена считает свой масштаб как s = min(clientWidth/1920, …)
   (boot-expo.jsx:195). При DPR 2 выходит s = 1 — но CSS-пиксель там
   сам вдвое крупнее физического, поэтому 1920×1080 CSS занимают все
   3840×2160 физических. Резкость полная. «1× на двойном экране»
   не происходит: единица масштаба и единица плотности — разные вещи,
   и я их смешала.

   Растровых поводов бояться DPR тоже нет: srcset в проекте ни одного
   (тир выбирает код, не браузер), а <canvas> есть только в brand.html,
   calendar.html и prototypes/ — то есть в каталоге и на заморозке,
   ни одного в разделах киоска. И тот, что в brand.html, сам зовёт
   setPixelRatio(min(devicePixelRatio, 2)).

   Почему канон именно B: оба режима дают одинаковый результат, ЕСЛИ
   каждое значение домножено. Но A прощает пропуск множителя, а B
   не прощает ни одного — то есть B честнее показывает ошибку.  */
@media (min-width: 2400px) {
  :root { --ui-scale: 2; }
}

/* Универсальный класс для скроллящихся областей — везде один brass-thumb.
   На тач-столе пользователь должен видеть, что блок скроллится. */
.brand-scroll {
  scrollbar-width: auto;
  scrollbar-color: var(--scroll-thumb) var(--scroll-track);
}
.brand-scroll::-webkit-scrollbar {
  width: var(--scroll-width, 14px);
  height: var(--scroll-width, 14px);
}
.brand-scroll::-webkit-scrollbar-track {
  background: var(--scroll-track);
}
.brand-scroll::-webkit-scrollbar-thumb {
  background: var(--scroll-thumb);
  border: 3px solid var(--scroll-track);
  border-radius: 8px;
  min-height: 60px;
}
.brand-scroll::-webkit-scrollbar-thumb:active {
  background: var(--scroll-thumb-active);
}


/* ── Пин --ui-scale здесь БОЛЬШЕ НЕ СТОИТ, и это намеренно ─────────────

   До 2026-08-04 файл заканчивался строкой `:root { --ui-scale: 1 }`.
   Она защищала от четырёхкратного размера: главная масштабирует свой
   canvas 1920 → 3840 сама, и ×2 поверх ×2 дали бы ×4.

   Основание было верным ровно для одной страницы из трёх. Canvas есть
   только у index.html; people.html и chronicle.html прокручиваются
   и своего масштабирования не имеют — пин оставлял их на 4K вдвое
   мельче. Одна и та же кнопка «к экспозиции» давала 240 физических
   пикселей на главной и 120 внутри раздела.

   Пин переехал в index.html, к тому canvas, от которого защищает.
   Заводить его обратно сюда нельзя: этот файл грузят все страницы
   сцены, а canvas — не у всех. */