/* DSOT «Фундамент» — сквозной слой стилей.
   Подключение ПОСЛЕ inline <style> страницы:
   <link rel="stylesheet" href="/assets/base.css">
   Даёт: мобильный nav-дровер (чинит скрытую навигацию на телефоне),
   утилиты count/ring/reveal, глобальный reduced-motion.
   🔴 С 17.07 ЗАДАЁТ ТОКЕНЫ (см. блок ниже): раньше их не задавал никто, и каждый
   из 27 файлов держал свою копию. Теперь источник истины один — здесь. */

/* ═══════════ СЛОЙ ТОКЕНОВ — единственный источник истины ═══════════
   Юрий 17.07: «сначала слой токенов — без него любой разнос канона только множит копии».

   ПОЧЕМУ ЭТО ВООБЩЕ ПОНАДОБИЛОСЬ: слоя токенов не существовало. `grep ':root' base.css`
   давал 0, и в шапке файла прямо стояло «Токены НЕ переопределяет». То есть 27 файлов
   были 27 источниками истины — каждый со своей копией. Мы не «немного разъехались»,
   мы никогда и не были вместе.

   ЗНАЧЕНИЯ ВЗЯТЫ ПО БОЛЬШИНСТВУ, а не по вкусу — посчитано по 27 файлам:
     --muted   #8e8aa0                      24/27   (эталон /lab/ с #9d9ab0 — в меньшинстве)
     --line    rgba(216,181,120,.14)        20/27
     --line-2  rgba(216,181,120,.28)        19/21
     --serif   EB Garamond,Palatino,…       20/27
     --ui      Golos Text,system-ui,…       20/25
     --mono    JetBrains Mono,ui-monospace… 19/27
   Чистые (расхождений нет): --bg-0 #04060d · --ink #e8e2d4 · --gold #d8b578
     · --gold-hot #f3d9a4 · --cyan #8fd8e8 · --violet #b79ce0.

   КАК РАБОТАЕТ: base.css подключается ПОСЛЕ инлайн-<style> страницы, поэтому этот :root
   перекрывает местные копии — 20 страниц выравниваются без единой правки в них.
   Страницы вне общего слоя (choose, enter, map, /lab/, /lab/lab.html) пока живут сами;
   их подключение — отдельный шаг, не молча.

   ⚠️ Псевдонимы --bg-0/--bg0 объявлены оба намеренно: часть страниц пишет с дефисом,
   часть без. Убирать по одному, а не одним махом. */
:root{
  color-scheme: dark;
  --bg-0:#04060d; --bg0:#04060d;
  --ink:#e8e2d4;
  --muted:#8e8aa0;
  --gold:#d8b578; --gold-hot:#f3d9a4;
  --line:rgba(216,181,120,.14);
  --line-2:rgba(216,181,120,.28);
  --cyan:#8fd8e8; --violet:#b79ce0;
  --serif:'EB Garamond',Palatino,Georgia,serif;
  --ui:'Golos Text',system-ui,sans-serif;
  --mono:'JetBrains Mono',ui-monospace,Menlo,monospace;
  --exp:cubic-bezier(.16,1,.3,1);
}


/* ═══════════ МИКРОТИПОГРАФИКА — то, чего нет больше нигде ═══════════
   Разбор эталона /lab/ дал неожиданный вывод: «дорого» читается НЕ из-за canvas
   и шейдеров, а из-за типографики. Посчитано по 23 страницам платформы:
     hanging-punctuation → 0 страниц (у эталона есть)
     text-rendering:optimizeLegibility → 0 страниц (у эталона есть)
     'dlig' (дискреционные лигатуры) → 0 страниц (у эталона есть)
     h1,h2,h3 font-weight:500 → 16 страниц; у эталона 400
   Значения перенесены ДОСЛОВНО из platform/dsot-demo/index.html:44-54, не выдуманы.

   Работает без правки страниц: base.css грузится после инлайн-<style>, поэтому
   вес 400 перекрывает местные 500. Заголовок становится легче и просторнее —
   это и есть разница между «сайт» и «издание». */
body{
  font-feature-settings:'liga' 1,'kern' 1;
  hanging-punctuation:first;              /* кавычка/тире свисают в поле — Safari */
  text-rendering:optimizeLegibility;
}
h1,h2{
  font-weight:400;                        /* было 500 на 16 страницах: тяжелее, чем нужно */
  letter-spacing:-.012em;
  text-wrap:balance;                      /* строки уравниваются, не оставляя сироту */
  font-feature-settings:'liga' 1,'dlig' 1,'onum' 1;
}
/* числа в тексте — старостильные (вписаны в строчные), в данных — табличные */
.count,.num,td,.stat{ font-variant-numeric:tabular-nums; }


/* ═══════════ КУРСОР-ПРИБОР + МАГНИТЫ ═══════════
   Перенос из эталона (dsot-demo/index.html:70-84 CSS, 624-644 JS) — единственный
   пункт плана, который честно является УКРАШЕНИЕМ, а не починкой поломки.
   Кольцо следует за мышью, у ссылок раздувается и показывает подпись (data-cursor),
   кнопки .mag слегка тянутся к курсору.

   Две защиты, обе обязательны:
   · @media(pointer:coarse) — на телефоне курсора нет вовсе (там палец, а не мышь);
   · инициализация в dsot.js под `if (RM) return` — при reduced-motion не заводится.
   Разметку трогать не надо: элементы создаёт скрипт, подписи data-cursor опциональны. */
#dsot-cur, #dsot-curd{ opacity:0; }
body.curon #dsot-cur, body.curon #dsot-curd{ opacity:1; }
#dsot-cur{
  position:fixed; z-index:99; left:0; top:0; width:26px; height:26px; margin:-13px 0 0 -13px;
  border:1px solid rgba(216,181,120,.55); border-radius:50%; pointer-events:none;
  display:flex; align-items:center; justify-content:center;
  transition:transform .16s ease-out, width .35s var(--exp), height .35s var(--exp),
             margin .35s var(--exp), border-color .35s, opacity .3s;
}
#dsot-cur .clab{
  font-family:var(--mono); font-size:8.5px; letter-spacing:.14em; color:var(--gold-hot);
  text-transform:uppercase; opacity:0; transition:opacity .3s; white-space:nowrap;
}
#dsot-curd{
  position:fixed; z-index:99; left:0; top:0; width:4px; height:4px; margin:-2px 0 0 -2px;
  border-radius:50%; background:var(--gold-hot); pointer-events:none; transition:opacity .3s;
}
body.cur-big #dsot-cur{ width:66px; height:66px; margin:-33px 0 0 -33px; border-color:rgba(243,217,164,.8); }
body.cur-big #dsot-cur .clab{ opacity:1; }
body.cur-big #dsot-curd{ opacity:0; }
@media (pointer:coarse){ #dsot-cur, #dsot-curd{ display:none !important; } }
@media (prefers-reduced-motion:reduce){ #dsot-cur, #dsot-curd{ display:none !important; } }

/* ---------- бургер (виден только на мобиле) ---------- */
.navtoggle{
  display:none; background:none; border:0; padding:6px; cursor:pointer;
  color:var(--ink,#e8e2d4); line-height:0; -webkit-tap-highlight-color:transparent;
}
.navtoggle:hover{ color:var(--gold-hot,#f3d9a4); }

/* ---------- nav-дровер (мобайл + планшет) ----------
   Порог 1024, а не 720. Почему: дровер включался на 720, но шапка перестаёт помещаться
   гораздо раньше — index требует 1152px, science 1204px, geocarcass 923px. В полосе между
   ними ссылки молча уезжали за край окна: на 1024px обрезался «Кабинет» — главный CTA.
   Документ при этом НЕ скроллился по горизонтали, поэтому баг не ловился проверкой
   переполнения. Измерено бинарным поиском порога по всем 17 страницам. */
@media (max-width:1024px){
  .navtoggle{ display:inline-flex; align-items:center; z-index:57; position:relative; }
  header .nav{
    position:fixed; top:0; right:0; height:100dvh; width:min(80vw,300px);
    flex-direction:column; align-items:flex-start; justify-content:flex-start; gap:2px;
    background:rgba(6,8,15,.975); backdrop-filter:blur(12px);
    padding:78px 28px 32px; border-left:1px solid var(--line-2,rgba(216,181,120,.28));
    transform:translateX(105%); transition:transform .42s cubic-bezier(.16,1,.3,1);
    z-index:56; overflow-y:auto;
  }
  header .nav.open{ transform:none; box-shadow:-24px 0 60px rgba(0,0,0,.5); }
  /* показать скрытые на мобиле пункты внутри открытого дровера */
  header .nav.open a.hidem, header .nav.open a.hide-sm{ display:block!important; }
  header .nav a.hidem, header .nav a.hide-sm{
    font-size:17px; padding:11px 0; width:100%;
    border-bottom:1px solid var(--line,rgba(216,181,120,.14));
  }
  header .nav a.btn{ margin-top:14px; width:100%; text-align:center; }
  /* бургер поверх открытого дровера, справа */
  .navtoggle.open{ position:fixed; top:16px; right:18px; }
}

/* ---------- узкий десктоп: шапка поджимается, а не режется ----------
   Полоса 1025–1320: дровера уже нет, а самые длинные шапки (index 10 пунктов,
   science 10 + бейдж «Open Science») в 26px-гэпы не влезают. Жмём интервал и кегль —
   переносить пункты в дровер на десктопе было бы хуже. */
@media (min-width:1025px) and (max-width:1320px){
  header .nav{ gap:15px; font-size:13px; }
  header .nav a.btn{ padding:8px 13px!important; }
}

/* ---------- утилиты для хелперов dsot.js ---------- */
.count{ font-variant-numeric:tabular-nums; }
.js .reveal-up{ opacity:0; transform:translateY(24px); transition:opacity .9s var(--exp,cubic-bezier(.16,1,.3,1)),transform .9s var(--exp,cubic-bezier(.16,1,.3,1)); }
.js .reveal-up.in{ opacity:1; transform:none; }

/* ═══════════ ЧЕСТНЫЙ REDUCED-MOTION ═══════════
   🔴 Было живой поломкой, а не косметикой. Замер с включённым reduced-motion показал
   67 НЕВИДИМЫХ блоков контента: portal 18, product 20, about 20, science 9.
   Человек, попросивший систему убрать анимацию (а это часто те, кому от движения
   физически плохо), открывал страницу и видел пустоту.

   ПОЧЕМУ молоток не спасал: `*{animation-duration:.001ms}` ускоряет АНИМАЦИИ, но блоки
   `.reveal{opacity:0}` ждут не анимацию — они ждут класс `.in` от скрипта. Нет скрипта
   (или он не дошёл) — контента нет. Ускорять тут нечего.

   МОДЕЛЬ ЭТАЛОНА (platform/dsot-demo/index.html:286-294): не «быстро проиграть»,
   а СРАЗУ ПОСТАВИТЬ в финальное состояние, средствами CSS, без участия JS. */
@media (prefers-reduced-motion:reduce){
  /* 1. контент виден ВСЕГДА — что бы ни случилось со скриптом */
  .rv, .reveal, .reveal-up, .hero-load > *, .hero-in > *,
  .js .rv, .js .reveal, .js .reveal-up{
    opacity:1 !important; transform:none !important; filter:none !important;
    transition:none !important; animation:none !important;
  }
  /* 2. декоративные бесконечные циклы гасим (фон, вращение, мерцание) */
  .spin, .pulse, .flow, .scrollcue::after, #fol, .hero-orb{ animation:none !important; }
  /* 3. остальное — мгновенно, но не «мигает»: цикл ровно один */
  *{ animation-duration:.001ms !important; animation-iteration-count:1 !important;
     transition-duration:.001ms !important; scroll-behavior:auto !important; }
  header .nav{ transition:none !important; }
}

/* ═══════════ AWARD-TIER сквозной слой (доп. поверх страничных стилей, ничего не переопределяет деструктивно) ═══════════ */
/* кнопка-гравировка: световой мазок по hover (свет прибора).
   ::before (НЕ ::after — чтобы не перебить .btn-ghost::after со стрелкой); ghost/link исключены. */
.btn:not(.btn-ghost):not(.btn-link){ position:relative; overflow:hidden; }
.btn:not(.btn-ghost):not(.btn-link)::before{
  content:""; position:absolute; top:0; bottom:0; left:-60%; width:45%;
  background:linear-gradient(105deg,transparent,rgba(243,217,164,.2),transparent);
  transform:skewX(-14deg); transition:left .6s var(--exp,cubic-bezier(.16,1,.3,1)); pointer-events:none;
}
.btn:not(.btn-ghost):not(.btn-link):hover::before{ left:135%; }
/* активный пункт навигации (scroll-spy из dsot.js) */
header .nav a.on{ color:var(--gold,#d8b578); }
/* магнит-секция «64 грани» на страницах-контейнерах */
.gk-band{ text-align:center; padding:clamp(28px,5vw,54px) 0; }
.gk-band h2{ margin:10px 0 12px; }
.gk-band .sub{ max-width:52ch; margin:0 auto 24px; color:var(--muted,#8e8aa0); }
.gk-band .actions{ display:flex; gap:14px; justify-content:center; flex-wrap:wrap; }

/* ═══ «ДАЛЬШЕ»: конец страницы ведёт по маршруту, а не в тупик ═══
   Вставляется модулем скелета из dsot.js перед <footer> на всех страницах.
   Токены НЕ переопределяет — берёт уже объявленные страницей. */
#dsot-next{ border-top:1px solid var(--line,rgba(216,181,120,.14)); padding:clamp(34px,5vh,64px) 0; }
.dsot-next-in{ max-width:1180px; margin:0 auto; padding:0 clamp(20px,5vw,64px);
  display:flex; align-items:flex-end; justify-content:space-between; gap:24px; flex-wrap:wrap; }
.dsot-next-card{ display:flex; flex-direction:column; gap:7px; text-decoration:none; }
.dsot-next-k{ font-family:var(--mono,ui-monospace); font-size:10px; letter-spacing:.16em;
  text-transform:uppercase; color:var(--dim,#5f5c72); }
.dsot-next-t{ font-family:var(--serif,Georgia,serif); font-size:clamp(24px,3vw,38px); line-height:1.1;
  color:var(--ink,#e8e2d4); transition:color .45s var(--exp,cubic-bezier(.16,1,.3,1)); }
.dsot-next-card:hover .dsot-next-t{ color:var(--gold-hot,#f3d9a4); }
.dsot-next-map{ font-family:var(--mono,ui-monospace); font-size:10px; letter-spacing:.12em;
  text-transform:uppercase; color:var(--muted,#8e8aa0); text-decoration:none; padding-bottom:8px;
  border-bottom:1px solid transparent; transition:color .35s,border-color .35s; }
.dsot-next-map:hover{ color:var(--gold,#d8b578); border-bottom-color:var(--gold,#d8b578); }
