/* Дизайн-система EPOCH — токены и основа.
   ========================================================================
   Ни одного значения цвета, отступа, радиуса или длительности прямо в
   компонентах. Хардкод в компоненте — повод переделать компонент.

   Порядок слоёв объявлен один раз здесь: что бы ни писали дальше и в каком
   бы порядке ни грузились файлы, utilities всегда перебьёт components, а
   components — base. Это избавляет от гонки специфичности и от !important. */

@layer tokens, base, components, utilities;

@layer tokens {
  :root{
    /* Схема. `light dark` означает «идём за системной настройкой» —
       это состояние «авто». Явный выбор ставится на <html> атрибутом
       data-theme, и он лишь сужает эту пару до одного значения; сами
       цвета при этом не переобъявляются.

       Отсюда правило файла: каждый цвет объявлен ОДИН раз, парой
       `light-dark(светлое, тёмное)`. Отдельного файла светлой темы нет
       и быть не должно — второй файл разъедется с первым, это уже
       проверено на конфигах серверов. */
    color-scheme: light dark;

    /* ---- поверхности --------------------------------------------- */
    --bg-void:      light-dark(#F2F5F8, #06080B);  /* фон страницы */
    --bg-panel:     light-dark(#FFFFFF, #0E1319);  /* карточки, панели */
    --bg-elevated:  light-dark(#FFFFFF, #141A22);  /* модалки, поповеры */
    --bg-sunken:    light-dark(#E9EDF2, #090C11);  /* поля ввода */

    /* ---- линии ---------------------------------------------------- */
    --stroke:        light-dark(#DCE3EB, #1C2531);
    --stroke-strong: light-dark(#B9C4D1, #2A3646);

    /* ---- текст -----------------------------------------------------
       Значения подобраны замером, а не на глаз: `tools/contrast.py`
       считает отношение по WCAG против всех трёх поверхностей схемы.
       Порог обычного текста 4.5.

       На тёмной схеме --text-faint давал 3.07–3.52 и порога не
       проходил — это тянулось давно. Поднят до 4.52. */
    --text:          light-dark(#0F141A, #E7ECF3);
    --text-muted:    light-dark(#4B596A, #8595A8);
    --text-faint:    light-dark(#5E6C7E, #738397);
    --text-on-accent:light-dark(#1A1204, #1A1204);  /* по янтарю — тёмный */

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

       Поэтому --accent это ЗАЛИВКА, а --accent-ink — то же по смыслу,
       но пригодное для текста, обводки и кольца фокуса. На тёмной
       схеме они совпадают, поэтому тёмная тема не меняется ни на
       пиксель. */
    --accent:        light-dark(#FFB020, #FFB020);
    --accent-ink:    light-dark(#946000, #FFB020);
    --accent-hover:  light-dark(#FFC24D, #FFC24D);
    --accent-soft:   light-dark(rgba(148,96,0,.10),  rgba(255,176,32,.12));
    --accent-line:   light-dark(rgba(148,96,0,.34),  rgba(255,176,32,.34));

    --online:        light-dark(#13795E, #3DE0B4);
    --online-soft:   light-dark(rgba(19,121,94,.10), rgba(61,224,180,.12));
    --online-line:   light-dark(rgba(19,121,94,.28), rgba(61,224,180,.28));

    --danger:        light-dark(#D90000, #FF5C5C);
    --danger-soft:   light-dark(rgba(217,0,0,.08),   rgba(255,92,92,.12));
    --danger-line:   light-dark(rgba(217,0,0,.28),   rgba(255,92,92,.28));

    --unknown:       light-dark(#5E6C85, #74839D);
    --unknown-soft:  light-dark(rgba(94,108,133,.12), rgba(86,99,122,.14));

    /* ---- плёнки и подложки ----------------------------------------
       Всё, что раньше стояло в компонентах прямым rgba. Аудит нашёл
       57 таких мест; каждое из них на светлой схеме дало бы либо
       чёрную плёнку поверх белого, либо белый блик на белом. */

    /* Подложка под модальным окном. */
    --scrim:         light-dark(rgba(15,23,42,.38), rgba(4,6,9,.74));
    /* Полупрозрачные шапка и выдвижная панель — под ними едет контент. */
    --scrim-header:  light-dark(rgba(255,255,255,.88), rgba(6,8,11,.86));
    --scrim-drawer:  light-dark(rgba(255,255,255,.97), rgba(10,13,19,.96));
    /* Затемнение поверх выдвижной панели. */
    --scrim-soft:    light-dark(rgba(15,23,42,.28), rgba(4,6,9,.6));

    /* Лёгкая заливка строки под курсором и в таблицах. На тёмной это
       белый с малой прозрачностью, на светлой — наоборот тёмный. */
    --wash-1:        light-dark(rgba(15,23,42,.030), rgba(255,255,255,.02));
    --wash-2:        light-dark(rgba(15,23,42,.055), rgba(255,255,255,.04));

    /* Блик по кромке и полосы «стекла». */
    --sheen-1:       light-dark(rgba(15,23,42,.05),  rgba(255,255,255,.045));
    --sheen-2:       light-dark(rgba(15,23,42,.07),  rgba(255,255,255,.055));
    --sheen-3:       light-dark(rgba(15,23,42,.10),  rgba(255,255,255,.09));

    /* Цвет тени. Вынесен отдельно, потому что на светлой схеме чёрная
       тень выглядит грязью — нужна холодная и слабее. */
    --shadow-color:  light-dark(rgba(15,23,42,.16), rgba(0,0,0,.6));
    --shadow-deep:   light-dark(rgba(15,23,42,.22), rgba(0,0,0,.9));

    /* Тёмная подложка под художественной картинкой (обложки эпох,
       картинка входа). Там лежит фотография, и на светлой схеме она
       остаётся на тёмном — так задумано, менять не надо. */
    --art-bg:        #05070A;
    /* Затухание художественной картинки в фон панели. */
    --art-fade-1:    light-dark(rgba(5,7,10,.10), rgba(5,7,10,.15));
    --art-fade-2:    light-dark(rgba(5,7,10,.70), rgba(5,7,10,.82));

    /* Расхождение каналов на карточке карты без картинки. Красный и
       синий по краям буквы. На светлой карточке прежние яркие оттенки
       вымываются, поэтому взяты глубже — подобраны под каждую схему, а
       не пересчитаны из тёмной. */
    --glitch-a:        light-dark(rgba(200,0,0,.35),  rgba(255,80,80,.35));
    --glitch-b:        light-dark(rgba(0,90,200,.35), rgba(80,200,255,.35));
    --glitch-a-strong: light-dark(rgba(200,0,0,.60),  rgba(255,80,80,.60));
    --glitch-b-strong: light-dark(rgba(0,90,200,.60), rgba(80,200,255,.60));

    /* Запасной цвет режима, когда у режима не задан свой. Раньше
       #8595A8 стоял прямо в трёх шаблонах и в modes.js. */
    --mode-accent:   light-dark(#5E6C85, #8595A8);

    /* Режим наложения для световых эффектов — см. ниже, за пределами
       этого блока. Через `light-dark()` его задать НЕЛЬЗЯ: функция
       типизирована под цвет, и `light-dark(multiply, screen)` — не
       значение, а мусор. Проверено: `mix-blend-mode` при таком
       объявлении вычисляется в `normal`, то есть эффект пропадает
       совсем, и на тёмной схеме тоже. */

    /* ---- шрифты ---------------------------------------------------
       Правило проекта: моноширинным набирается только то, что измерено
       игровым сервером. Взгляд отделяет факт от текста. */
    --font-display: Unbounded, "IBM Plex Sans", system-ui, sans-serif;
    --font-body:    "IBM Plex Sans", -apple-system, "Segoe UI", Roboto,
                    system-ui, sans-serif;
    --font-data:    "JetBrains Mono", ui-monospace, "Cascadia Mono",
                    Consolas, monospace;

    /* шкала: 12 14 16 20 28 40 64 */
    /* Микро-лейбл: прописные подписи над числами и в углах карточек.
       Заведён потому, что таких подписей набралось пять штук и все они
       стояли жёстко в пикселях — 9, 10 и снова 10. */
    --fs-2xs:11px;
    --fs-xs:12px; --fs-sm:14px; --fs-md:16px; --fs-lg:20px;
    /* Текст внутри полей ввода. Отдельный токен, а не --fs-sm: поле
       мельче 16px заставляет мобильную Safari приближать страницу при
       установке курсора, и человек остаётся на увеличенной странице
       после ввода. Подписи и подсказки при этом должны остаться
       мелкими, поэтому общую ступень шкалы трогать нельзя — у неё
       28 применений на подписи, а не на поля. */
    --fs-field:16px;
    --fs-xl:28px; --fs-2xl:40px; --fs-3xl:64px;
    --lh-text:1.5; --lh-head:1.1;
    --tracking-head:-.02em;
    --tracking-caps:.14em;   /* для рубрик капслоком */

    /* ---- отступы: шаг 4 ------------------------------------------- */
    --sp-1:4px;  --sp-2:8px;  --sp-3:12px; --sp-4:16px; --sp-5:20px;
    --sp-6:24px; --sp-7:32px; --sp-8:40px; --sp-9:56px; --sp-10:80px;

    /* ---- радиусы: 4 / 8 / 16 -------------------------------------- */
    --r-sm:4px; --r-md:8px; --r-lg:16px; --r-pill:999px;

    /* ---- тени ------------------------------------------------------
       Мягкие и низкие: интерфейс не парит над страницей. */
    --sh-1:0 1px 2px var(--shadow-color);
    --sh-2:0 8px 24px var(--shadow-color);
    --sh-3:0 24px 64px var(--shadow-deep);

    /* ---- слои ------------------------------------------------------ */
    --z-canvas:0; --z-content:1; --z-sticky:20; --z-header:30;
    --z-drawer:40; --z-overlay:50; --z-modal:60; --z-toast:70;

    /* ---- движение --------------------------------------------------
       Три длительности, две кривые. Больше не заводим. */
    --dur-1:120ms; --dur-2:220ms; --dur-3:420ms;
    --ease-out:cubic-bezier(.2,.8,.3,1);
    --ease-in-out:cubic-bezier(.65,0,.35,1);

    /* ---- сетка ------------------------------------------------------ */
    --grid-max:1280px;
    --grid-gutter:24px;
    --grid-pad:24px;

    /* ---- каркас ------------------------------------------------------ */
    --header-h:64px;
    --tabbar-h:60px;
    --sidebar-w:248px;
  }

  /* Явный выбор человека. Атрибут ставится на <html>: в самом начале
     head — по значению из профиля, чтобы первый кадр был уже нужной
     схемы. Ни одного цвета здесь нет и быть не должно: выбор лишь
     сужает пару `light dark` до одного значения, а значения живут
     выше, по одному объявлению на токен. */
  :root[data-theme="light"]{ color-scheme: light }
  :root[data-theme="dark"] { color-scheme: dark }

  /* Режим наложения для световых эффектов (подсветка по курсору, блик
     по кромке). Это не цвет, поэтому парой его не объявить — нужны три
     правила, и они стоят здесь же, в одном месте с остальными
     значениями схемы.

     `screen` осветляет: на тёмном фоне светящееся пятно проявляется, на
     белом даёт белое по белому и исчезает. Обратное действие даёт
     `multiply` — он затемняет, и то же пятно на светлом фоне читается
     как тень цвета акцента. Значения подобраны под каждую схему
     отдельно, а не пересчитаны из прежних. */
  :root{ --blend-fx: screen }
  @media (prefers-color-scheme: light){
    :root:not([data-theme="dark"]){ --blend-fx: multiply }
  }
  :root[data-theme="light"]{ --blend-fx: multiply }

  @media (max-width:720px){
    :root{ --grid-pad:16px; --grid-gutter:16px; --fs-3xl:40px; --fs-2xl:28px; }
  }
}

@layer base {
  *, *::before, *::after{ box-sizing:border-box }

  html{
    -webkit-text-size-adjust:100%;
    scroll-padding-top:calc(var(--header-h) + var(--sp-6));
  }

  body{
    margin:0;
    background:var(--bg-void);
    color:var(--text-muted);
    font:400 var(--fs-md)/var(--lh-text) var(--font-body);
    -webkit-font-smoothing:antialiased;
    text-rendering:optimizeLegibility;
  }

  h1,h2,h3,h4{
    margin:0;
    color:var(--text);
    line-height:var(--lh-head);
    letter-spacing:var(--tracking-head);
    font-weight:600;
  }
  h1{ font-family:var(--font-display); font-weight:700; font-size:var(--fs-2xl) }
  h2{ font-family:var(--font-display); font-weight:600; font-size:var(--fs-xl) }
  h3{ font-size:var(--fs-lg) }
  h4{ font-size:var(--fs-md) }
  p{ margin:0 }

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

  button{ font:inherit; color:inherit; background:none; border:0; padding:0 }

  img,svg,video{ max-width:100%; display:block }

  /* Атрибут hidden обязан прятать. Браузер задаёт ему display:none в своих
     правилах, а любой наш класс с display перебивает их по специфичности —
     и элемент остаётся на экране. Так кнопка загрузки лаунчера показывалась
     до того, как проверка пройдена. */
  [hidden]{ display:none !important }

  /* Видимый фокус обязателен: без него по сайту нельзя ходить с
     клавиатуры. Это ЕДИНСТВЕННОЕ место в проекте, где он описан.

     :focus-visible, а не :focus — чтобы кольцо не мигало от мыши.

     Радиус здесь больше не задаётся. Он задавался — и это был не радиус
     кольца, а радиус самого элемента: кнопка с r-md при получении
     фокуса ужимала себе углы до r-sm и слегка меняла форму. Обводка
     повторяет скругление элемента сама.

     Компонентам разрешено добавлять к фокусу свой признак (например,
     подсветить рамку), но не отменять кольцо: `outline:none` в
     компоненте — это то, из-за чего в проекте завелось три разных
     способа показать фокус. */
  :focus-visible{
    outline:2px solid var(--accent-ink);
    outline-offset:2px;
  }

  ::selection{ background:var(--accent-soft); color:var(--text) }

  /* Всё измеренное сервером — моноширинным. */
  code, kbd, samp, .data{
    font-family:var(--font-data);
    font-size:.94em;
    font-variant-numeric:tabular-nums;
  }

  hr{ border:0; border-top:1px solid var(--stroke); margin:var(--sp-6) 0 }

  /* Уважаем настройку системы: анимации выключаются целиком, а не «почти». */
  @media (prefers-reduced-motion:reduce){
    *,*::before,*::after{
      animation-duration:.01ms !important;
      animation-iteration-count:1 !important;
      transition-duration:.01ms !important;
      scroll-behavior:auto !important;
    }
  }
}

@layer utilities {
  .wrap{
    width:100%;
    max-width:var(--grid-max);
    margin-inline:auto;
    padding-inline:var(--grid-pad);
  }

  /* 12 колонок. Дочерние элементы занимают колонки утилитами ниже. */
  .grid{ display:grid; gap:var(--grid-gutter);
         grid-template-columns:repeat(12,minmax(0,1fr)) }
  .col-3{ grid-column:span 3 } .col-4{ grid-column:span 4 }
  .col-6{ grid-column:span 6 } .col-8{ grid-column:span 8 }
  .col-12{ grid-column:span 12 }
  @media (max-width:1024px){
    .col-3{ grid-column:span 6 } .col-4{ grid-column:span 6 }
    .col-8{ grid-column:span 12 }
  }
  @media (max-width:720px){
    .col-3,.col-4,.col-6,.col-8{ grid-column:span 12 }
  }

  .stack{ display:grid; gap:var(--sp-4) }
  .stack-sm{ display:grid; gap:var(--sp-2) }
  .stack-lg{ display:grid; gap:var(--sp-7) }
  /* min-width:0 обязателен. По умолчанию элемент в grid не сжимается уже
     своего содержимого, и одна широкая строка внутри — таблица, длинный
     адрес, полоса переключателей без переноса — растягивает всю колонку, а
     за ней и страницу. На телефоне это выглядит как «вёрстку унесло вбок»,
     и найти виновника глазами почти невозможно. */
  .stack > *, .stack-sm > *, .stack-lg > *{ min-width:0 }
  .row{ display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap }
  .row-tight{ display:flex; align-items:center; gap:var(--sp-2) }
  .push{ margin-left:auto }

  .muted{ color:var(--text-muted) }
  .faint{ color:var(--text-faint) }
  .strong{ color:var(--text) }
  /* Числа-метрики: табличные цифры везде, где число может смениться на
     месте. Пропорциональные цифры разной ширины заставляют строку
     дёргаться при каждом обновлении счётчика — а обновляется он у нас
     раз в несколько секунд. Правило собрано в одном месте, чтобы не
     вспоминать про него в каждом новом блоке. */
  .num, .stat-num, .modes-sum .v, .epoch__facts .num,
  .mcard__facts .num, .row__v, .srow__load{
    font-variant-numeric:tabular-nums;
  }
  .num{ font-family:var(--font-data) }

  /* Рубрика: мелкие капсом над блоком. */
  .eyebrow{
    font-size:var(--fs-xs); font-weight:600; letter-spacing:var(--tracking-caps);
    text-transform:uppercase; color:var(--text-faint);
  }

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

  /* Ссылка «перейти к содержимому» для клавиатуры. */
  .skip{
    position:absolute; left:var(--sp-4); top:-100px; z-index:var(--z-toast);
    padding:var(--sp-3) var(--sp-5); background:var(--bg-elevated);
    border:1px solid var(--stroke-strong); border-radius:var(--r-md);
    color:var(--text); transition:top var(--dur-1) var(--ease-out);
  }
  .skip:focus{ top:var(--sp-4) }

  /* Одна поставленная сцена загрузки: элементы проявляются каскадом сверху
     вниз. Сделано целиком на CSS и с fill-mode both — если скрипты не
     выполнятся, содержимое всё равно окажется видимым, потому что анимация
     заканчивается в нормальном состоянии. Задержка задаётся переменной,
     шаг — 60 мс.

     При prefers-reduced-motion общее правило в base сводит длительность к
     нулю, и всё просто появляется сразу. */
  .reveal{ animation:reveal .55s var(--ease-out) both;
           animation-delay:var(--rd,0ms) }
  .reveal-1{ --rd:60ms }  .reveal-2{ --rd:120ms }
  .reveal-3{ --rd:180ms } .reveal-4{ --rd:240ms }
  .reveal-5{ --rd:320ms }
  @keyframes reveal{ from{ opacity:0; transform:translateY(14px) } }

  /* Проявление при прокрутке. Правило действует только когда класс на
     <html> поставил скрипт: без него содержимое видно сразу, а не спрятано
     в ожидании скрипта, который мог и не выполниться. */
  /* Появление считается на видеокарте: меняются только прозрачность и
     сдвиг, а will-change просит браузер подготовить слой заранее — без
     этого первый кадр каждой карточки собирается на процессоре, и именно
     он выглядит рывком. */
  .has-rise [data-rise] > *{
    opacity:0; transform:translateY(20px);
    transition:opacity .6s var(--ease-out), transform .6s var(--ease-out);
    transition-delay:var(--rise-d, 0ms);
  }
  /* Подсказку браузеру даём только тем, кто ещё не появился: держать
     will-change на всей странице после анимации — это занятая память
     впустую, а именно ради памяти браузер и не готовит слои сам. */
  .has-rise [data-rise] > *:not(.is-in){ will-change:opacity, transform }
  .has-rise [data-rise] > .is-in{ opacity:1; transform:none }
}

/* Холст фона всего сайта. Лежит под всем и ничего не ловит; страница
   поверх него прозрачна там, где раньше была чёрная заливка. */
.site-gl{
  position:fixed; inset:0; z-index:-1;
  width:100vw; height:100vh;
  display:block; pointer-events:none;
  opacity:0; animation:site-gl-in 1.4s ease-out .2s forwards;
}
@keyframes site-gl-in{ to{ opacity:1 } }

/* Когда холст есть, убираем непрозрачную заливку страницы — иначе он
   просто не виден. Панели и карточки остаются плотными: читать текст
   поверх дыма нельзя. */
.has-site-gl body{ background:transparent }
/* Под холстом герой прозрачен — но только на тёмной схеме, где холст
   тоже тёмный. Само правило ниже, вместе с обратными. */

/* ...но только пока холст тёмный.
   Герой — тёмный художественный блок в обеих схемах: у него внутри
   `color-scheme:dark`, и весь текст в нём светлый. Прозрачным он сделан,
   чтобы сквозь него был виден шейдер. На светлой схеме шейдер светлый —
   и получилось светлое по светлому: заголовок первого экрана почти
   пропал. Поймано на боевом сайте после выкладки, на стенде пряталось
   за прежней ошибкой в шейдере.

   Поэтому на светлой схеме герой остаётся со своим фоном. */
/* Без пробела перед `.has-site-gl`: этот класс висит на том же <html>,
   что и `:root`. С пробелом селектор требовал бы, чтобы холст был
   потомком корня, — и не совпадал никогда. Первая правка так и не
   сработала, поймано замером на стенде. */
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]).has-site-gl .hero{ background:transparent }
}
:root[data-theme="dark"].has-site-gl .hero{ background:transparent }

@media (prefers-reduced-motion:reduce){
  .site-gl{ display:none }
}
