/* Базовый шрифт сайта. Задаём на body, чтобы элементы вне основного
   Rubik-контейнера (тосты, модалки, куки-баннер — их JS кладёт прямо в body)
   не откатывались на serif браузера. Грузится раньше инлайн-стилей страниц. */
body {
  font-family: Rubik, Arial, sans-serif;
  color: #171512;
}

/* Дизайн-токены. Инлайн-стили в шаблонах унаследованы дословно из макетов;
   эти переменные — единая точка правды для повторно используемых классов
   ниже и нового кода. */
:root {
  --ink: #171512;
  --paper: #F6F1E7;
  --red: #E23D3D;
  --yellow: #F2B233;
  --green: #2F9E44;
  --blue: #3478F6;
  --muted: #5b554b;
  --radius: 12px;
}

/* Фон страниц: кремовая бумага + едва заметная сетка точек-пипсов (как поле
   настолки) + тонкое зерно (feTurbulence), чтобы фон выглядел как картон
   коробки. Всё очень слабой прозрачности — не мешает контенту. Класс висит на
   корневом div.mv-page каждой страницы (он покрывает вьюпорт). */
.mv-page {
  background-color: var(--paper);
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.05'/%3E%3C/svg%3E"),
    radial-gradient(rgba(23, 21, 18, 0.05) 1.5px, transparent 1.6px);
  background-size: 180px 180px, 24px 24px;
  background-position: 0 0, 0 0;
}

/* Дорожка-трек лендинга: фиксированный вертикальный рельс справа с «клетками»
   по секциям; фишка (как игровая) едет к активной секции при скролле. */
.mv-track {
  position: fixed;
  right: 22px;
  top: 50%;
  transform: translateY(-50%);
  z-index: 40;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
  padding: 16px 0;
}
.mv-track-rail {
  position: absolute;
  top: 16px;
  bottom: 16px;
  left: 50%;
  width: 3px;
  transform: translateX(-50%);
  /* ровный частый пунктир (граница dashed рендерится «рвано») */
  background-image: repeating-linear-gradient(to bottom, var(--ink) 0 4px, transparent 4px 8px);
  opacity: 0.5;
}
.mv-track-dot {
  position: relative;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 2.5px solid var(--ink);
  background: var(--paper);
  display: block;
  transition: background 0.2s ease, transform 0.2s ease;
}
.mv-track-dot:hover { transform: scale(1.18); }
.mv-track-dot.active { background: var(--yellow); }
.mv-track-tip {
  position: absolute;
  right: calc(100% + 12px);
  top: 50%;
  transform: translateY(-50%);
  white-space: nowrap;
  font-family: Oswald, sans-serif;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: var(--ink);
  color: var(--paper);
  padding: 4px 10px;
  border-radius: 8px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
}
.mv-track-dot:hover .mv-track-tip,
.mv-track-dot:focus-visible .mv-track-tip { opacity: 1; }
.mv-track-piece {
  position: absolute;
  left: 50%;
  top: 24px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--red);
  border: 2.5px solid var(--ink);
  box-shadow: 2px 2px 0 var(--ink);
  transform: translate(-50%, -50%);
  transition: top 0.4s cubic-bezier(0.4, 1.25, 0.5, 1);
  pointer-events: none;
  z-index: 1;
}
@media (max-width: 1100px) { .mv-track { display: none; } }
@media (prefers-reduced-motion: reduce) { .mv-track-piece { transition: none; } }

/* Плашки платформ в герое — кликабельны, ведут к секции загрузки; на ховере
   подсвечиваются золотым, чтобы читались как действие. */
.pill-dl { cursor: pointer; transition: background 0.15s ease, transform 0.15s ease; text-decoration: none; }
.pill-dl:hover { background: var(--yellow) !important; transform: translateY(-1px); }

/* Общий хедер сайта (partials/siteheader): на узких экранах десктопная
   навигация прячется, показывается бургер с выпадающей панелью. */
.mv-header #burger-btn { display: none; }
.mv-header #mobile-menu { display: none; }
@media (max-width: 900px) {
  .mv-header .r-navlinks,
  .mv-header .hdr-desktop { display: none !important; }
  .mv-header #burger-btn { display: flex; }
  .mv-header #mobile-menu.open { display: flex; }
  .mv-header .r-nav { flex-wrap: nowrap !important; }
}

/* Повторяющиеся примитивы форм (CSS побайтово равен прежним инлайн-стилям —
   визуально ничего не меняется, но правится теперь в одном месте). */
.mv-input {
  border: 2.5px solid #171512;
  border-radius: 10px;
  padding: 12px 14px;
  font-size: 15px;
  background: #fff;
}
.mv-flabel {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 13px;
  font-weight: 700;
}
.mv-otp {
  width: 48px;
  height: 56px;
  text-align: center;
  font-size: 24px;
  font-weight: 700;
  border: 2.5px solid #171512;
  border-radius: 10px;
  background: #fff;
}

/* Общие приёмы дизайн-системы: hover-сдвиги и рост offset-тени.
   Базовая тень — inline, увеличенная задаётся переменной --hs. */
.hv { transition: transform 0.06s ease, box-shadow 0.06s ease; }
.hv:hover { transform: translate(-1px, -1px); }
.hv[style*="--hs"]:hover { box-shadow: var(--hs) !important; }

a.lnk-ink:hover { color: #E23D3D !important; }
a.lnk-gold:hover { color: #F2B233 !important; }
a.lnk-white:hover { color: #fff !important; }
button.flag-btn:hover { color: #E23D3D !important; }

/* Reveal-анимации при скролле: элементы с [data-reveal] всплывают при появлении
   во вьюпорте (site.js, IntersectionObserver). --rd — каскадная задержка. */
@media (prefers-reduced-motion: no-preference) {
  [data-reveal] {
    opacity: 0;
    transform: translateY(28px);
    transition:
      opacity 0.65s cubic-bezier(0.22, 0.61, 0.36, 1),
      transform 0.65s cubic-bezier(0.22, 0.61, 0.36, 1);
    transition-delay: var(--rd, 0s);
    will-change: opacity, transform;
  }
  [data-reveal="left"] { transform: translateX(-36px); }
  [data-reveal="right"] { transform: translateX(36px); }
  [data-reveal="pop"] { transform: translateY(28px) scale(0.95); }
  [data-reveal="tilt"] { transform: translateY(28px) rotate(-1.5deg); }
  [data-reveal].in { opacity: 1; transform: none; }

  /* Дыхание главной клавиши партии. Правило лежит внутри no-preference тем же
     приёмом, что и reveal: при reduced-motion оно просто не применяется, и
     отдельный глушитель не нужен. Дышать имеет право ровно один элемент экрана
     — иначе периферийное зрение перестаёт его выделять и приём выключается. */
  .mv-breathe { animation: mv-breathe 1.1s ease-in-out infinite alternate; }

  /* На узких экранах боковой заезд запрещён: блок шириной во всю колонку,
     сдвинутый на 36px вправо, растягивает страницу и включает горизонтальный
     скролл (телефон при этом отзумливает вёрстку). Едем только вертикально. */
  @media (max-width: 900px) {
    [data-reveal="left"],
    [data-reveal="right"] { transform: translateY(28px); }
  }
}

/* Куки-баннер на телефоне занимал почти половину экрана: иконка + текст 13.5px +
   две кнопки в столбик. Ужимаем — иконку прячем, текст мельче, кнопки в один
   ряд на всю ширину, чтобы под баннером оставалась видна сама страница. */
@media (max-width: 560px) {
  #cookie-banner { left: 10px; right: 10px; bottom: 10px; }
  #cookie-banner .ck-box { padding: 14px; gap: 12px; border-radius: 14px; }
  #cookie-banner .ck-ico { display: none; }
  #cookie-banner .ck-text { min-width: 0; font-size: 12.5px; line-height: 1.45; }
  #cookie-banner .ck-acts { width: 100%; flex-wrap: nowrap; }
  #cookie-banner .ck-acts button { flex: 1; padding: 11px 8px; font-size: 12.5px; }
}

/* Тач-зона ссылок подвала: строчная ссылка высотой 17px — промах на телефоне.
   Растим до 34px по высоте, не трогая расстановку (nav и так flex). */
@media (max-width: 900px) {
  .mv-footer .foot-lnk { display: inline-flex; align-items: center; min-height: 34px; }
  .mv-footer nav { gap: 4px 24px !important; }
}

/* То же для одиночных текстовых ссылок вне сплошного текста («Забыли пароль?»,
   «← В кабинет», ник в шапке, ники в таблице лидеров): по высоте строки в них
   пальцем не попасть. Ширину не трогаем — обрезка ников многоточием сохраняется. */
@media (max-width: 900px) {
  .tap-lnk { display: inline-flex; align-items: center; min-height: 36px; }
}

/* Плашки платформ в герое — 31px по высоте, на волосок ниже комфортного минимума. */
@media (max-width: 900px) {
  .pill-dl { padding-top: 9px !important; padding-bottom: 9px !important; }
}

.form-err {
  display: none;
  background: #FDECEC;
  border: 2px solid #E23D3D;
  color: #B23030;
  border-radius: 10px;
  padding: 10px 14px;
  font-size: 13.5px;
  line-height: 1.45;
}
.form-err.show { display: block; }

/* Модальные окна в стиле сайта (site.js: mvModal). */
#mv-modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 400;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(23, 21, 18, 0.55);
}
#mv-modal-overlay.show { display: flex; }
#mv-modal-overlay.show .mv-modal { animation: mv-modal-in 0.22s cubic-bezier(0.22, 0.61, 0.36, 1); }
@keyframes mv-modal-in {
  from { opacity: 0; transform: translateY(16px) scale(0.97); }
  to { opacity: 1; transform: none; }
}
/* Амплитуда крошечная намеренно (порт Modifier.breathing, Android): заметная
   пульсация на экране, куда заходят по десять раз в день, из подсказки
   превращается в тик. 1.1 с в одну сторону + alternate = период 2.2 с. */
@keyframes mv-breathe {
  from { transform: scale(1); }
  to { transform: scale(1.025); }
}
.mv-modal {
  /* модалка живёт в document.body вне Rubik-контейнера — задаём шрифт явно */
  font-family: Rubik, Arial, sans-serif;
  color: #171512;
  width: 100%;
  max-width: 420px;
  background: #fff;
  border: 2.5px solid #171512;
  border-radius: 16px;
  box-shadow: 6px 6px 0 rgba(23, 21, 18, 0.3);
  padding: 26px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.mv-modal h3 {
  font-family: Oswald, sans-serif;
  font-weight: 600;
  font-size: 20px;
  margin: 0;
  text-transform: uppercase;
}
.mv-modal p { margin: 0; font-size: 14px; line-height: 1.5; color: #5b554b; }
.mv-modal input, .mv-modal select {
  font-family: Rubik, Arial, sans-serif;
  border: 2.5px solid #171512;
  border-radius: 10px;
  padding: 12px 14px;
  font-size: 15px;
  background: #fff;
  width: 100%;
  box-sizing: border-box;
}
.mv-modal .mv-modal-actions { display: flex; gap: 10px; justify-content: flex-end; }
.mv-modal button {
  font-family: Oswald, sans-serif;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-size: 14px;
  padding: 11px 22px;
  border: 2.5px solid #171512;
  border-radius: 10px;
  cursor: pointer;
}
.mv-modal .mv-btn-primary { background: #E23D3D; color: #fff; box-shadow: 3px 3px 0 #171512; }
.mv-modal .mv-btn-danger { background: #E23D3D; color: #fff; box-shadow: 3px 3px 0 #171512; }
.mv-modal .mv-btn-ghost { background: #fff; color: #171512; box-shadow: 3px 3px 0 rgba(23,21,18,0.2); }

#mv-toast {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%) translateY(16px);
  opacity: 0;
  pointer-events: none;
  background: #171512;
  color: #F6F1E7;
  font-family: Oswald, sans-serif;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 14px;
  padding: 13px 24px;
  border: 2.5px solid #F2B233;
  border-radius: 12px;
  box-shadow: 4px 4px 0 rgba(23, 21, 18, 0.35);
  transition: opacity 0.25s ease, transform 0.25s ease;
  z-index: 300;
}
#mv-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ─────────────────────────── Знак игры: качание костей ───────────────────────────
   Кости качаются ПО ОТДЕЛЬНОСТИ и вразнобой (порт Android `AppLogo`): периоды
   1050 и 1370 мс нарочно некратные, поэтому фазы сходятся обратно лишь раз в
   несколько минут. Если крутить знак целиком, движется не пара костей, а картинка,
   будто её держат в руке. Базовый наклон каждой кости задан атрибутом transform
   у ВНЕШНЕЙ группы (partials/dice.ejs), анимация висит на внутренней: CSS-свойство
   transform перебило бы атрибут и наклон бы потерялся.
   transform-origin — центр СВОЕЙ кости в координатах viewBox, иначе кость
   поехала бы вокруг центра картинки. */
.mv-dice-front,
.mv-dice-back { transform-box: view-box; }
.mv-dice-front {
  transform-origin: 40px 59px;
  animation: dice-wobble-front 1.05s ease-in-out infinite alternate;
}
.mv-dice-back {
  transform-origin: 68px 32px;
  animation: dice-wobble-back 1.37s ease-in-out infinite alternate;
}
@keyframes dice-wobble-front { from { transform: rotate(-6deg); } to { transform: rotate(6deg); } }
@keyframes dice-wobble-back { from { transform: rotate(4.5deg); } to { transform: rotate(-4.5deg); } }

/* ─────────────────────────── Достижения ───────────────────────────
   Список строк, а не сетка медалей: сетка отвечает только на «что у меня
   есть», а главный вопрос экрана другой — «что взять следующим», и ради него
   нужны число и полоса, которые в клетку 80×80 не помещаются.

   Значки — эмодзи, тем же языком, каким набрана лента статистики в кабинете:
   рисованных знаков правил (RuleIcons с Android) у сайта нет вовсе, а рисовать
   четырнадцать штук ради первого захода — отдельная работа. Ступень при этом
   видна не значком, а КРАСКОЙ подложки и рамки (--tier). */
.ach-list { display: flex; flex-direction: column; gap: 10px; }

.ach-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 14px;
  align-items: center;
  padding: 12px 16px;
  background: #fff;
  border: 2.5px solid var(--ink);
  border-radius: 14px;
  box-shadow: 3px 3px 0 rgba(23, 21, 18, 0.14);
}
/* Строка, до которой рукой подать, обязана отличаться от соседних не только
   местом в списке — отсюда краска ступени в рамке. */
.ach-row.almost { border-color: var(--tier, var(--ink)); box-shadow: 4px 4px 0 rgba(23, 21, 18, 0.2); }
/* Полностью закрытое гасим: оно уже не задача, а трофей. */
.ach-row.done { background: #FBF7EE; }

.ach-medal {
  width: 44px; height: 44px; flex: none;
  display: flex; align-items: center; justify-content: center;
  font-size: 21px; line-height: 1;
  border-radius: 50%;
  border: 2.5px solid var(--tier, #c9c2b4);
  background: color-mix(in srgb, var(--tier, #c9c2b4) 16%, #fff);
}
/* Невзятая медаль не прячется и не запирается замком — тот же значок,
   приглушённый: спрятанное достижение нельзя захотеть. */
.ach-medal.empty { opacity: 0.45; filter: grayscale(1); }

.ach-name { font-family: Oswald, sans-serif; font-weight: 600; font-size: 15px; line-height: 1.15; }
.ach-hint { font-size: 12px; color: #8a8272; line-height: 1.25; }
.ach-count { font-family: Oswald, sans-serif; font-weight: 700; font-size: 15px; white-space: nowrap; text-align: right; }
.ach-note { font-size: 11px; color: #8a8272; text-align: right; white-space: nowrap; }

.ach-chip {
  display: inline-block; padding: 1px 7px; border-radius: 999px;
  font-family: Oswald, sans-serif; font-weight: 700; font-size: 11px; letter-spacing: 0.03em;
  color: var(--ink);
  background: color-mix(in srgb, var(--tier, #c9c2b4) 26%, #fff);
  border: 1.5px solid var(--tier, #c9c2b4);
}
.ach-chip.left { background: #DCF0E0; border-color: #2F9E44; color: #1d6b30; }

.ach-bar { height: 8px; border: 2px solid var(--ink); border-radius: 999px; background: var(--paper); overflow: hidden; }
.ach-bar > i { display: block; height: 100%; background: var(--tier, var(--yellow)); }

/* Плашка сводки — обведена латунью: это главное на экране, и выделяет её
   рамка, а не размер. */
.ach-summary {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 16px 20px; background: #fff;
  border: 2.5px solid var(--brass, #B08A16); border-radius: 16px;
  box-shadow: 4px 4px 0 rgba(23, 21, 18, 0.14);
}
.ach-summary .cap { font-size: 12px; color: #8a8272; text-transform: uppercase; letter-spacing: 0.06em; }
.ach-summary .rank { font-family: Oswald, sans-serif; font-weight: 700; font-size: 20px; color: var(--brass, #B08A16); text-transform: uppercase; }

@media (max-width: 560px) {
  .ach-row { grid-template-columns: auto minmax(0, 1fr); }
  .ach-row .ach-tail { grid-column: 2; text-align: left; }
  .ach-count, .ach-note { text-align: left; }
}
