/**
 * Сага: хаб и история.
 *
 * ВИД ИЗ ПРИЛОЖЕНИЯ, краски из нашего свода. Раскладка, размеры и поведение
 * шторок перенесены из lib/games/saga/, а цвета, шрифты и скругления берутся из
 * общих переменных, как везде в каркасе. Ничего своего рядом с общим не заводим:
 * похожее, собранное рядом, всегда чуть другое.
 *
 * ЧТО ПРИХОДИТ С СЕРВЕРА ГОТОВОЙ РАЗМЕТКОЙ. Тексты паков содержат боевые классы,
 * и мы их НЕ переписываем: те же паки читает боевая страница, и правка тридцати
 * одной записи ради двух классов это риск без выгоды.
 *
 *   .pm  .pf        имя игрока, синий и розовый. Ставит сервер (fmt в saga.js)
 *   .speaker        кто-то в мире говорит: страж, суккуб, капитанша
 *   .narrator       рассказчик, который смотрит на всё с ведром попкорна
 *   .sic            значок предмета прямо в строке
 *
 * Значит наша задача одна: ПЕРЕКРАСИТЬ их по-своему. Один контент, два вида.
 */

/* ==========================================================================
   ОБЩЕЕ ДЛЯ ХАБА И ИСТОРИИ
   ========================================================================== */

/* Разметка, приезжающая в тексте сцены. Единственное место, где мы красим
   чужие классы, и потому они собраны здесь вместе, а не рассыпаны по файлу. */
.sg-tekst .pm, .sg-replika .pm, .sg-zadanie .pm { color: var(--c-player-1); font-weight: 600; }
.sg-tekst .pf, .sg-replika .pf, .sg-zadanie .pf { color: var(--c-player-2); font-weight: 600; }

.sg-tekst .speaker,
.sg-zadanie .speaker {
  color: var(--c-gold);
  font-weight: 600;
}

/* Рассказчик говорит сбоку от истории, а не внутри неё: засечный курсив и
   приглушённый тон, как реплика в скобках. */
.sg-tekst .narrator,
.sg-zadanie .narrator {
  color: var(--c-muted);
  font-family: var(--f-serif);
  font-style: italic;
}

/* Значок предмета в строке: по высоте буквы, а не картинкой поверх текста. */
.sg-tekst .sic,
.sg-zadanie .sic,
.sg-replika .sic {
  height: 1.05em;
  width: auto;
  vertical-align: -0.15em;
  margin: 0 2px;
}

.sg-tekst {
  font: var(--t-body-m);
  line-height: 1.6;
  color: var(--c-text);
  white-space: pre-wrap;
}
.sg-tekst b { font-weight: 600; }

/* ==========================================================================
   ХАБ
   ========================================================================== */

.sg-shapka {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin-bottom: var(--s-4);
}
.sg-shapka__imya {
  margin: 0;
  font: var(--t-headline-m);
  font-family: var(--f-serif);
}

/* Место под пилюлю внизу: без него последняя карточка прячется под ней. */
.sg-list { padding-bottom: 104px; }

/* ---------- карточка приключения ---------- */

/**
 * Приключение это КАРТИНКА С ПОДПИСЬЮ, а не строка списка. Так в приложении, и
 * причина простая: их всего три, и каждое обещает вечер. Список из трёх строк
 * не обещает ничего.
 */
.sg-karta {
  display: block;
  margin-bottom: var(--s-4);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  overflow: hidden;
}

.sg-karta__art {
  position: relative;
  aspect-ratio: 16 / 10;
  background: var(--c-surface-alt);
}
.sg-karta__art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* Низ картинки растворяется в карточке, иначе шов между ними режет глаз. */
.sg-karta__art::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 45%;
  background: linear-gradient(to bottom, transparent, var(--c-surface));
  pointer-events: none;
}

.sg-znak {
  position: absolute;
  top: var(--s-3);
  inset-inline-start: var(--s-3);
  padding: 4px var(--s-3);
  border-radius: var(--r-tag, 10px);
  font: 600 var(--t-label-s) var(--f-mono);
  letter-spacing: 1px;
  text-transform: uppercase;
  background: rgba(10, 6, 6, .72);
  border: 1px solid transparent;
  color: var(--c-muted);
  backdrop-filter: blur(6px);
}
.sg-znak--novoe { color: var(--c-gold); border-color: rgba(212, 175, 55, .5); }
.sg-znak--idyot { color: var(--c-player-2); border-color: rgba(244, 63, 94, .5); }
.sg-znak--sdelano { color: var(--c-text); border-color: var(--c-border-strong); }
.sg-znak--zamok { color: var(--c-dim); }

.sg-karta__niz {
  position: relative;
  margin-top: -28px;
  padding: 0 var(--s-4) var(--s-4);
}
.sg-karta__nomer {
  display: block;
  font: 600 var(--t-label-s) var(--f-mono);
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--c-gold);
}
.sg-karta__imya {
  margin: var(--s-1) 0 var(--s-2);
  font: var(--t-headline-s);
  font-family: var(--f-serif);
}
.sg-karta__opis {
  margin: 0 0 var(--s-4);
  color: var(--c-muted);
  font: var(--t-body-s);
}
.sg-karta__shag {
  display: block;
  margin: var(--s-2) 0 var(--s-4);
  color: var(--c-muted);
  font: var(--t-body-s);
}
.sg-karta .btn { width: 100%; justify-content: center; }
/* Столбиком, а не в ряд: сквозное правило, docs/refactor/RULES.md. */
.sg-karta__ryad { display: flex; flex-direction: column; gap: var(--s-2); }

.sg-polosa {
  height: 4px;
  border-radius: var(--r-pill);
  background: var(--c-surface-alt);
  overflow: hidden;
}
.sg-polosa i {
  display: block;
  height: 100%;
  background: var(--c-gold);
  border-radius: inherit;
}

/* ---------- герои ---------- */

.sg-geroi { display: flex; flex-direction: column; gap: var(--s-3); }

.sg-geroy__verkh {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin-bottom: var(--s-4);
}
.sg-geroy__litso {
  width: 56px; height: 56px;
  flex: 0 0 56px;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid var(--c-border-strong);
  background: var(--c-surface-alt);
}
.sg-geroy[data-storona="0"] .sg-geroy__litso { border-color: var(--c-player-1); }
.sg-geroy[data-storona="1"] .sg-geroy__litso { border-color: var(--c-player-2); }
.sg-geroy__kto { flex: 1; min-width: 0; }
.sg-geroy__imya {
  display: block;
  font: var(--t-title-m);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sg-geroy__rolj {
  display: block;
  margin-top: 2px;
  color: var(--c-muted);
  font: var(--t-body-s);
}

.sg-mera {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-2) 0;
}
.sg-mera__imya { color: var(--c-muted); font: var(--t-label-s); }

.sg-serdtsa { display: inline-flex; gap: 3px; }
.sg-serdtse { width: 20px; height: 20px; }

/* Пять слоёв одежды. Погашенные не прячем: иначе не видно, сколько уже снято. */
.sg-tryapki { display: inline-flex; gap: 4px; }
.sg-tryapka {
  width: 16px; height: 6px;
  border-radius: var(--r-pill);
  background: var(--c-gold);
}
.sg-tryapka--net { background: var(--c-surface-alt); }

/* ---------- сокровищница и награды ---------- */

.sg-predmetyi {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(88px, 1fr));
  gap: var(--s-3);
}
.sg-predmet { margin: 0; text-align: center; }
.sg-predmet img { width: 56px; height: 56px; object-fit: contain; }
.sg-predmet figcaption {
  margin-top: var(--s-1);
  color: var(--c-muted);
  font: var(--t-body-s);
}

.sg-nagradyi {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: var(--s-3);
}
/**
 * Клетка награды: значок в КОРОБКЕ ПОСТОЯННОЙ ВЫСОТЫ, подпись под ним.
 *
 * Без коробки сетка разъезжается: подписи бывают в одну строку и в две, и
 * соседние значки встают на разной высоте. На снимке это читалось как «у
 * половины наград картинки нет».
 */
.sg-nagrada {
  margin: 0;
  padding: 0;
  background: none;
  border: 0;
  color: inherit;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-1);
}
.sg-nagrada img {
  width: 72px;
  height: 72px;
  object-fit: contain;
  flex: 0 0 72px;
}
.sg-nagrada__imya {
  text-align: center;
  font: var(--t-body-s);
  color: var(--c-text);
}
/* Неполученная гаснет, но остаётся на месте: сетка показывает, сколько всего
   в блоке, и по ней видно, что ещё не найдено. */
.sg-nagrada--net img { filter: grayscale(1); opacity: .32; }
.sg-nagrada--net figcaption { color: var(--c-dim); }

.sg-pusto {
  padding: var(--s-10) var(--s-4);
  text-align: center;
}
.sg-pusto h3 {
  margin: 0 0 var(--s-2);
  font: var(--t-headline-s);
  font-family: var(--f-serif);
}

/* ---------- пилюля вкладок ---------- */

/**
 * ПИЛЮЛЯ КАК В ПРИЛОЖЕНИИ, _HubSwitcher, вместе с числами оттуда: высота 62,
 * поля по бокам space4, пять равных долей, значок 22 и подпись 9.5 с трекингом
 * 0.3.
 *
 * ТРЕКИНГ ИМЕННО 0.3, а не общий для мелких подписей: в приложении об этом
 * написан отдельный абзац. Обычный 1.6 хорош для одинокой подписи и плох для
 * пяти в ряд: он добавлял восемнадцать пунктов к одиннадцатибуквенному слову и
 * ставил многоточие на «Приключения».
 *
 * ВО ВСЮ ШИРИНУ, а не по содержимому: в приложении это Row из пяти Expanded, и
 * доли равны. Пилюля по содержимому съезжала бы вбок при смене языка.
 */
.sg-pilyulya {
  position: fixed;
  inset-inline: var(--s-4);
  bottom: calc(var(--s-2) + env(safe-area-inset-bottom, 0px));
  z-index: 30;
  display: flex;
  height: 62px;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--c-surface-high) 88%, transparent);
  border: 1px solid var(--c-border);
  backdrop-filter: blur(14px);
  /* Тень из приложения: чёрная при 40%, размытие 24, растяжение -6, сдвиг вниз
     на 10. Пилюля висит над страницей, а не лежит на ней. */
  box-shadow: 0 10px 24px -6px rgba(0, 0, 0, .4);
}
.sg-pilyulya__kn {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  padding: 0 2px;
  background: none;
  border: 0;
  border-radius: var(--r-pill);
  color: var(--c-muted);
  font: 600 9.5px/1.1 var(--f-sans);
  letter-spacing: .3px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.sg-pilyulya__kn span {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Значки из НАШЕГО набора, того же, что носит навигация каркаса: в приложении
   это тот же лист (lib/theme/glyphs.dart). Погашенный значок это тот же значок
   тише, а не серая копия. */
.sg-pilyulya__kn .glyph { width: 22px; height: 22px; opacity: .45; }
.sg-pilyulya__kn[aria-current="page"] { color: var(--c-gold); }
.sg-pilyulya__kn[aria-current="page"] .glyph { opacity: 1; }

/* ==========================================================================
   ИСТОРИЯ
   ========================================================================== */

/**
 * Экран истории НЕ ПРОКРУЧИВАЕТСЯ: бит всегда влезает целиком. Это главное, что
 * отличает сагу от простыни, и ради этого JS ужимает кегль, а в крайнем случае
 * и картинку (класс «тесно» ниже).
 */
.sg-igra {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--c-bg);
}

/* ---------- картинка сцены ---------- */

/**
 * Картинка ЗАПОЛНЯЕТ блок, а не лежит в чёрных полях.
 *
 * Сцена это широкая полоса 16:9, лицо это вертикальный кадр 4:5, и оба должны
 * занять один и тот же блок. Приложение решает это тремя слоями, и мы теми же:
 * размытая копия позади, сам кадр поверх, виньетка и растворение внизу.
 *
 * ЛИЦО ЕЩЁ И ПРИБЛИЖЕНО. Портреты нарисованы с воздухом вокруг фигуры; в
 * приложении этот воздух уводят за край масштабом 1.52 со сдвигом вверх, чтобы
 * в кадре был человек, а не его поля.
 */
.sg-art {
  position: relative;
  flex: 0 0 auto;
  height: min(56vw, 42svh);
  overflow: hidden;
  background: var(--c-surface-alt);
}
.sg-igra--tesno .sg-art { height: min(44vw, 30svh); }

.sg-art__fon,
.sg-art__kadr {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.sg-art__fon {
  filter: blur(26px);
  opacity: .55;
  transform: scale(1.15);   /* размытие съедает края: растягиваем, чтобы не было каймы */
}
.sg-art--litso .sg-art__kadr {
  transform: scale(1.52) translateY(-6%);
  transform-origin: center 35%;
}

/* Мягкая виньетка: глаз садится в середину кадра. */
.sg-art__vinjetka {
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 50% 45%, transparent 55%, rgba(0, 0, 0, .4) 100%);
  pointer-events: none;
}
/* Картинка кончается растворением, а не обрезом. */
.sg-art__kray {
  position: absolute;
  inset: auto 0 0 0;
  height: 56px;
  background: linear-gradient(to bottom, transparent, var(--c-bg));
  pointer-events: none;
}

.sg-verkh {
  position: absolute;
  top: calc(var(--s-3) + env(safe-area-inset-top, 0px));
  inset-inline-start: var(--s-3);
  z-index: 3;
}

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

/**
 * Двое висят НАД картинкой, у нижнего края: имя с уровнем, под ним сердца и
 * слои. Строками, а не карточками: строка не отнимает у сцены высоту.
 *
 * ПРАВАЯ ЗЕРКАЛЬНА. В приложении у неё alignRight, и порядок внутри строк
 * переворачивается. Делаем это направлением флекса, а не разметкой: в разметке
 * порядок должен остаться тем, в каком это читает голосовой доступ.
 */
.sg-para {
  position: absolute;
  inset-inline: var(--s-4);
  bottom: var(--s-3);
  z-index: 3;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--s-3);
  pointer-events: none;
}
/**
 * Строка игрока: лицо по краю, текст внутрь.
 *
 * «Он у левого поля, она у правого, и страницу обрамляют двое, а не два блока
 * текста»: подписано в приложении дословно (_PlayerLine). Круг 34, рамка в цвет
 * игрока при 75 процентах, свечение того же цвета при 22 с размытием 10.
 */
.sg-stroka {
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--s-2);
}
.sg-stroka[data-storona="1"] { text-align: end; flex-direction: row-reverse; }
.sg-stroka__tekst { min-width: 0; }

.sg-litso {
  width: 34px; height: 34px;
  flex: 0 0 34px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--c-surface-alt);
}
.sg-stroka[data-storona="0"] .sg-litso {
  border: 1.5px solid color-mix(in srgb, var(--c-player-1) 75%, transparent);
  box-shadow: 0 0 10px color-mix(in srgb, var(--c-player-1) 22%, transparent);
}
.sg-stroka[data-storona="1"] .sg-litso {
  border: 1.5px solid color-mix(in srgb, var(--c-player-2) 75%, transparent);
  box-shadow: 0 0 10px color-mix(in srgb, var(--c-player-2) 22%, transparent);
}

.sg-stroka__verkh,
.sg-stroka__niz {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  min-width: 0;
}
.sg-stroka__niz { margin-top: 3px; gap: var(--s-2); }
.sg-stroka[data-storona="1"] .sg-stroka__verkh,
.sg-stroka[data-storona="1"] .sg-stroka__niz { flex-direction: row-reverse; }

.sg-imya {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font: 700 var(--t-label-l) var(--f-sans);
  /* Тень под именем: оно лежит на картинке, и на светлой сцене без неё пропадает. */
  text-shadow: 0 1px 4px rgba(0, 0, 0, .8);
}
.sg-stroka[data-storona="0"] .sg-imya { color: var(--c-player-1); }
.sg-stroka[data-storona="1"] .sg-imya { color: var(--c-player-2); }
.sg-ur {
  flex: 0 0 auto;
  color: var(--c-gold);
  font: var(--t-label-s);
  text-shadow: 0 1px 4px rgba(0, 0, 0, .8);
}

/* Сердце двенадцать пикселей, как в приложении. Рисуем фигурой, а не картинкой:
   двенадцать пикселей это размер, на котором PNG становится кашей. */
.sg-serdtsa { display: inline-flex; gap: 2px; }
.sg-serdtse {
  width: 12px; height: 12px;
  background: var(--c-player-2);
  -webkit-mask: var(--serdtse) center / contain no-repeat;
  mask: var(--serdtse) center / contain no-repeat;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .8));
}
.sg-serdtse--net { background: var(--c-dim); opacity: .55; }
:root {
  --serdtse: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>');
}

.sg-sloi {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  color: var(--c-muted);
  font: var(--t-label-s);
  text-shadow: 0 1px 4px rgba(0, 0, 0, .8);
}
.sg-sloi--net { color: var(--c-gold-light, var(--c-gold)); }
.sg-melkiy { width: 12px; height: 12px; }

/* ---------- страница под картинкой ---------- */

/* Свет от картинки падает на верх страницы и гаснет к сгибу. */
.sg-stranitsa {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding: 0 var(--s-4) calc(var(--s-4) + env(safe-area-inset-bottom, 0px));
  background: linear-gradient(to bottom, #17130e 0%, var(--c-bg) 180px);
}

/**
 * НАЗВАНИЕ СЦЕНЫ ЗОЛОТИСТОЕ, а не белое.
 *
 * Так и в приложении (headlineSmall с Ref.goldLight), и на бою (.stitle с
 * var(--accent)). Оба сходятся, и это не совпадение: белое название сливается
 * с текстом сцены под ним, и «Три дороги по кораблю» читается как первая
 * строка абзаца, а не как имя места. Владелец увидел ровно это.
 */
.sg-nazvanie {
  margin: var(--s-2) 0 var(--s-3);
  font: var(--t-headline-s);
  font-family: var(--f-serif);
  color: var(--c-gold-light, #FDE68A);
}
.sg-igra--tesno .sg-nazvanie { margin: var(--s-1) 0 var(--s-2); font: var(--t-title-m); }

/* Текст бита занимает всё, что осталось, и НЕ прокручивается: если не влез,
   его ужимает JS. */
.sg-tekst {
  flex: 1 1 auto;
  min-height: 0;
  font: var(--t-body-m);
  line-height: 1.55;
  color: var(--c-text);
  white-space: pre-wrap;
}
.sg-tekst b { font-weight: 600; }

/* Меняется только текст, и меняется мягко: картинка на бите повествования та же,
   и мигать ей незачем. */
.sg-feyd { animation: sg-proyavitj .28s ease both; }
@keyframes sg-proyavitj {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .sg-feyd { animation: none; }
}

/* ---------- низ: точки битов и кнопка ---------- */

.sg-niz { flex: 0 0 auto; padding-top: var(--s-3); }

/* Точки говорят, сколько осталось. Пройденные тусклее непройденных, текущая
   золотая и шире: так видно место в сцене, не считая. */
.sg-tochki {
  display: flex;
  justify-content: center;
  gap: 5px;
  margin-bottom: var(--s-3);
}
.sg-tochki i {
  width: 5px; height: 5px;
  border-radius: var(--r-pill);
  background: var(--c-surface-alt);
  transition: width .2s ease, background .2s ease;
}
.sg-tochki i.seen { background: var(--c-dim); }
.sg-tochki i.on { width: 16px; background: var(--c-gold); }

.sg-niz > .btn { width: 100%; justify-content: center; }

.sg-vyiboryi { display: flex; flex-direction: column; gap: var(--s-2); }
.sg-vyiboryi .btn {
  width: 100%;
  justify-content: center;
  text-align: center;
  /* Выбор бывает предложением, а не словом: пусть переносится, а не уезжает. */
  white-space: normal;
  line-height: 1.35;
  padding-block: var(--s-3);
  flex-direction: column;
  gap: 2px;
}
/**
 * Метка выбора: две строки и хватит.
 *
 * Потолок из приложения (_ChoiceButton, maxLines: 2). Без него встречались
 * метки на три и на четыре строки, и кнопка отбирала у сцены ту самую высоту,
 * ради которой сцену вообще режут на биты.
 *
 * Отсекает CSS, а не JS: где оборвётся строка, зависит от ширины экрана и от
 * шрифта, и считать это в коде значит считать неверно.
 */
.sg-metka {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.sg-podskazka {
  display: block;
  color: var(--c-muted);
  font: var(--t-body-s);
  /* Подпись под меткой это уточнение, ей одной строки довольно. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}

/* ---------- обряд и финал ---------- */

/* На алтаре говорит шторка, и говорит она в середину: там нет сцены сбоку. */
.sg-tekst--tsentr { text-align: center; }

.sg-final__klich {
  margin: 0 0 var(--s-3);
  font: var(--t-title-s);
  text-align: center;
}
.sg-final__schyot {
  margin: 0 0 var(--s-3);
  text-align: center;
  color: var(--c-gold);
}
.sg-final__schyot p { margin: 0; }

/* ---------- всплывашки хода ---------- */

/**
 * НАВЕРХУ СПРАВА, а не внизу по центру.
 *
 * Из приложения: Positioned(top: safeArea + 112, right: space4), и причина
 * подписана там же, «там, где ничего не нажимают». Я поставил их внизу, и они
 * легли ровно на кнопку «дальше»: владелец увидел прибавку опыта, накрывшую то,
 * на что он собирался нажать.
 *
 * ПРИЕЗЖАЮТ СПРАВА. Всплывашка, возникающая на месте, читается как часть
 * страницы; приехавшая сбоку читается как сообщение о том, что случилось.
 */
.sg-tostyi {
  position: fixed;
  top: calc(112px + env(safe-area-inset-top, 0px));
  inset-inline-end: var(--s-4);
  inset-inline-start: auto;
  z-index: 70;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--s-2);
  pointer-events: none;
}
.sg-tost {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  max-width: 240px;
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-pill);
  background: rgba(0, 0, 0, .72);
  border: 1px solid var(--c-border-strong);
  color: var(--c-text);
  font: var(--t-label-m);
  text-align: start;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .5);
  animation: sg-tost-vjjezd .26s cubic-bezier(.2, .7, .3, 1) both;
  transition: opacity .4s ease, transform .4s ease;
}
.sg-tost__znak { width: 16px; height: 16px; flex: 0 0 16px; }
.sg-tost__tekst {
  min-width: 0;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* Цвет рамки по тому, ЧТО СЛУЧИЛОСЬ: золото за полученное, роза за то, что
   это стоило. Соответствие из приложения (_SagaToast). */
.sg-tost--zoloto  { border-color: rgba(212, 175, 55, .5); }
.sg-tost--roza    { border-color: rgba(255, 120, 150, .5); }
.sg-tost--svetloe { border-color: rgba(253, 230, 138, .5); }
.sg-tost--von { opacity: 0; transform: translateX(12px); }

/* Награда это не прибавка опыта: у неё значок, имя и очки, и держат её дольше,
   потому что эту читают, а не замечают краем глаза (_AwardToast). */
.sg-nagrada-tost {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  max-width: 268px;
  padding: var(--s-2) var(--s-4) var(--s-2) var(--s-2);
  border-radius: var(--r-lg);
  background: rgba(0, 0, 0, .82);
  border: 1px solid rgba(212, 175, 55, .55);
  box-shadow: 0 0 26px -6px rgba(212, 175, 55, .45);
  animation: sg-tost-vjjezd .32s cubic-bezier(.2, .7, .3, 1) both;
  transition: opacity .5s ease, transform .5s ease;
}
.sg-nagrada-tost img { width: 42px; height: 42px; object-fit: contain; }
.sg-nagrada-tost > div { min-width: 0; display: flex; flex-direction: column; }
.sg-nagrada-tost__ochki {
  color: var(--c-gold);
  font: var(--t-label-s);
  letter-spacing: 1.6px;
  text-transform: uppercase;
}
.sg-nagrada-tost__imya {
  color: var(--c-text);
  font: var(--t-title-s);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.sg-nagrada-tost.sg-tost--von { opacity: 0; transform: translateX(12px); }

@keyframes sg-tost-vjjezd {
  from { opacity: 0; transform: translateX(28px); }
  to   { opacity: 1; transform: none; }
}

/* ---------- содержимое шторок ---------- */

.sg-podpisj {
  margin: 0 0 var(--s-4);
  color: var(--c-muted);
  font: var(--t-body-s);
}

/**
 * КУБИК ЛЕНТОЙ, как на бою.
 *
 * Спрайт 3200 на 160: двадцать граней по 160 в ряд, своя лента на каждое
 * приключение (подземелье, море, академия). Крутится сдвигом фона шагами
 * steps(20), то есть кадр сменяется целиком, а не размазывается.
 *
 * ТРИ ДВИЖЕНИЯ, и все три с боя:
 *   вылет   куб прилетает из-за угла экрана и успокаивается за 2.4 секунды
 *   прокрут лента бежит и куб покачивается, пока летит
 *   посадка садится на выпавшую грань с коротким отскоком
 *
 * Раньше здесь было число, которое перебиралось скриптом. Оно не врало, но и
 * не было кубиком: бросок это единственный миг саги, где решает случай, и он
 * заслуживает своих двух секунд.
 */
.sg-kubik {
  width: 160px;
  height: 160px;
  margin: 0 auto var(--s-4);
  filter: drop-shadow(0 4px 12px rgba(0, 0, 0, .55));
}
.sg-kubik__granj {
  width: 100%;
  height: 100%;
  background-repeat: no-repeat;
  /* Лента ровно 3200 на 160: двадцать кадров по 160. */
  background-size: 3200px 160px;
  background-position: 0 0;
}

/* Пока летит: лента бежит и куб покачивается. */
.sg-kubik--letit { animation: sg-brosok 2.4s cubic-bezier(.2, .72, .28, 1); }
.sg-kubik--letit .sg-kubik__granj {
  animation: sg-lenta .8s steps(20) infinite, sg-kachka .42s ease-in-out infinite;
}
@keyframes sg-lenta {
  from { background-position-x: 0; }
  to   { background-position-x: -3200px; }
}
@keyframes sg-kachka {
  0%, 100% { transform: translateY(0) rotate(-6deg); }
  50%      { transform: translateY(-11px) rotate(7deg); }
}
@keyframes sg-brosok {
  0%   { transform: translate(180%, -255%) scale(.3); opacity: 0; }
  8%   { opacity: 1; }
  24%  { transform: translate(-6%, 8%) scale(1.13); }
  40%  { transform: translate(4%, -9%) scale(.96); }
  56%  { transform: translate(-2%, 5%) scale(1.05); }
  72%  { transform: translate(1%, -3%) scale(.99); }
  88%  { transform: translate(0, 1%) scale(1.01); }
  100% { transform: translate(0, 0) scale(1); }
}

/* Посадка: короткий отскок на выпавшей грани. */
.sg-kubik--upal .sg-kubik__granj { animation: sg-posadka .5s cubic-bezier(.2, 1.5, .4, 1); }
@keyframes sg-posadka {
  from { transform: scale(1.15); }
  to   { transform: scale(1); }
}

/* Двадцатка и кол светятся: это единственные два броска, о которых потом
   рассказывают. */
.sg-kubik--krit {
  filter: drop-shadow(0 4px 12px rgba(0, 0, 0, .55)) drop-shadow(0 0 18px rgba(240, 208, 96, .65));
}
.sg-kubik--kol {
  filter: drop-shadow(0 4px 12px rgba(0, 0, 0, .55)) drop-shadow(0 0 16px rgba(244, 114, 182, .55));
}

@media (prefers-reduced-motion: reduce) {
  .sg-kubik--letit,
  .sg-kubik--letit .sg-kubik__granj,
  .sg-kubik--upal .sg-kubik__granj { animation: none; }
}

/* ---------- заставка главы ---------- */

/**
 * Между главами экран на секунду замирает: ромб, номер главы, её название и
 * кнопка «войти в сцену». Взято с боя (chapterBanner) вместе с движением:
 * содержимое въезжает снизу и подрастает.
 *
 * ПОКАЗЫВАЕТСЯ ОДИН РАЗ НА ГЛАВУ. Иначе она встаёт на каждом ходу внутри
 * главы, и вместо порога получается турникет.
 */
.sg-zastavka {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: var(--s-6);
  background: color-mix(in srgb, var(--c-bg) 94%, transparent);
  backdrop-filter: blur(10px);
  opacity: 0;
  pointer-events: none;
  transition: opacity .5s ease;
}
.sg-zastavka--vidna { opacity: 1; pointer-events: auto; }

.sg-zastavka__nutro {
  max-width: 640px;
  transform: translateY(16px) scale(.97);
  transition: transform .6s cubic-bezier(.22, .8, .3, 1);
}
.sg-zastavka--vidna .sg-zastavka__nutro { transform: none; }

/* Ромб над главой: единственное украшение на весь экран, и потому светится. */
.sg-zastavka__romb {
  width: 9px; height: 9px;
  margin: 0 auto var(--s-5);
  transform: rotate(45deg);
  background: var(--c-gold);
  box-shadow: 0 0 16px var(--c-gold);
}
.sg-zastavka__glava {
  margin: 0 0 var(--s-3);
  font: 800 var(--t-label-s) var(--f-sans);
  letter-spacing: 4px;
  text-transform: uppercase;
  color: var(--c-gold);
}
.sg-zastavka__imya {
  margin: 0 0 var(--s-7);
  font: var(--t-display-s);
  font-family: var(--f-serif);
  line-height: 1.14;
}
.sg-zastavka .btn { min-width: 240px; }

@media (prefers-reduced-motion: reduce) {
  .sg-zastavka, .sg-zastavka__nutro { transition: none; }
}

/* ---------- тумблер реплик ---------- */

/**
 * Реплики пары ПО УМОЛЧАНИЮ ВЫКЛЮЧЕНЫ. Решение владельца, записанное словами:
 * диалог пары рвёт целостность рассказа, чистая сцена с заданием читается
 * бодрее. Кто хочет играть в лицах, включает тумблером, и тогда реплики встают
 * под текстом сцены.
 */
.sg-tumbler {
  position: absolute;
  top: calc(var(--s-3) + env(safe-area-inset-top, 0px));
  inset-inline-end: var(--s-3);
  z-index: 3;
  width: 40px; height: 40px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  border: 1px solid var(--c-border-strong);
  background: color-mix(in srgb, var(--c-surface-high) 70%, transparent);
  backdrop-filter: blur(6px);
  cursor: pointer;
}
.sg-tumbler .glyph { width: 20px; height: 20px; opacity: .45; }
.sg-tumbler[aria-pressed="true"] .glyph { opacity: 1; }

.sg-repliki {
  margin-top: var(--s-3);
  padding-inline-start: var(--s-3);
  border-inline-start: 2px solid var(--c-border-strong);
}
.sg-replika {
  margin: 0 0 var(--s-2);
  font: var(--t-body-s);
  color: var(--c-muted);
}
.sg-replika:last-child { margin-bottom: 0; }
.sg-replika .pm { color: var(--c-player-1); font-weight: 600; }
.sg-replika .pf { color: var(--c-player-2); font-weight: 600; }

.sg-itog {
  margin: 0 0 var(--s-4);
  text-align: center;
  font: var(--t-title-s);
}
.sg-itog--da { color: var(--c-gold); }
.sg-itog--net { color: var(--c-muted); }

/* Задание: та же вложенная поверхность и та же золотая полоса, что у клетки
   сексополии. Одно правило на все игры: задание это отдельный блок. */
.sg-zadanie {
  margin: 0 0 var(--s-4);
  padding: var(--s-3) var(--s-4) var(--s-4);
  border-radius: var(--r-lg);
  border-inline-start: 3px solid var(--c-gold);
  background: var(--c-surface-alt);
  font: var(--t-title-s);
  line-height: 1.5;
  white-space: pre-wrap;
}

.sg-chasyi {
  margin: 0 auto var(--s-4);
  width: max-content;
  padding: var(--s-2) var(--s-4);
  border-radius: var(--r-pill);
  background: var(--c-surface-alt);
  color: var(--c-gold);
  font: var(--t-title-m);
  font-variant-numeric: tabular-nums;
}
.sg-chasyi--vyishlo { color: var(--c-muted); }

/* ==========================================================================
   ШИРОКИЙ ЭКРАН

   Сага это чтение, и на широком экране ей нужна КОЛОНКА, а не полотно: строка
   в тысячу пикселей не читается, глаз теряет начало следующей.
   ========================================================================== */

@media (min-width: 900px) {
  .sg-list, .sg-shapka { max-width: 720px; margin-inline: auto; }
  .sg-pilyulya { inset-inline: auto; left: 50%; transform: translateX(-50%); width: min(560px, calc(100% - var(--s-8))); }

  .sg-art { height: min(38vw, 46svh); }
  .sg-stranitsa > * { width: 100%; max-width: 680px; margin-inline: auto; }
  .sg-para { inset-inline: max(var(--s-4), calc(50% - 340px)); }

  /* Приключения в два столбца: три карточки в колонку это полоса прокрутки
     длиной в экран ради трёх штук. */
  .sg-list .sg-karta { margin-bottom: 0; }
  .sg-list:has(.sg-karta) {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--s-4);
    align-items: start;
  }
}

/* ==========================================================================
   ДОБРАНО ПРИ СВЕРКЕ С ПРИЛОЖЕНИЕМ

   Первый перенос показывал в «Героях» только имя и меры, а роль оставлял одной
   строкой. В приложении там четыре разворота: кто это, как держится, как
   играть и его фразы, и всё это уже приходит с сервера в personas. Плюс
   награда и желание, которые в приложении ОТКРЫВАЮТСЯ, а у меня были картинкой
   и строкой без нажатия.
   ========================================================================== */

/* Переключатель героя: двое чипами, выбранный в своём цвете. Один за раз,
   потому что про каждого тут не две строки, а четыре разворота. */
.sg-vyibor-geroya {
  display: flex;
  gap: var(--s-2);
  margin-bottom: var(--s-4);
}
.sg-vyibor-geroya__kn {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  padding: var(--s-3);
  border-radius: var(--r-pill);
  border: 1px solid var(--c-border);
  background: none;
  color: var(--c-muted);
  font: var(--t-label-l);
  cursor: pointer;
}
.sg-vyibor-geroya__kn img {
  width: 24px; height: 24px;
  border-radius: 50%;
  object-fit: cover;
}
.sg-vyibor-geroya__kn span {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sg-vyibor-geroya__kn[aria-current="true"] { background: var(--c-surface-high); color: var(--c-text); }
.sg-vyibor-geroya__kn[aria-current="true"][data-storona="0"] { border-color: var(--c-player-1); }
.sg-vyibor-geroya__kn[aria-current="true"][data-storona="1"] { border-color: var(--c-player-2); }

/* Разворот про роль. Родной details: открывается и без скриптов. */
.sg-razvorot {
  margin-top: var(--s-2);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-md);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
}
.sg-razvorot summary {
  cursor: pointer;
  font: var(--t-label-l);
  color: var(--c-gold);
  list-style: none;
}
.sg-razvorot summary::-webkit-details-marker { display: none; }
/* Стрелка своя, чтобы она была одинаковой в каждом браузере. */
.sg-razvorot summary::after {
  content: '';
  float: inline-end;
  width: 7px; height: 7px;
  margin-top: 5px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  transition: transform .2s ease;
}
.sg-razvorot[open] summary::after { transform: rotate(-135deg); }
.sg-razvorot p {
  margin: var(--s-3) 0 0;
  color: var(--c-muted);
  font: var(--t-body-s);
  line-height: 1.55;
  white-space: pre-line;   /* фразы приходят по одной в строке */
}

.sg-podskazka-roli { margin-top: var(--s-4); text-align: center; }

/* В карточке героя сердца крупнее, чем над картинкой: тут их разглядывают, а
   не считают на бегу. */
.sg-serdtsa-krupno { display: inline-flex; gap: 3px; }
.sg-serdtsa-krupno .sg-serdtse { width: 20px; height: 20px; }

/* Награда, открытая шторкой: значок крупно и очки под описанием. */
.sg-nagrada-krupno {
  display: flex;
  justify-content: center;
  margin-bottom: var(--s-4);
}
.sg-nagrada-krupno img { width: 128px; height: 128px; object-fit: contain; }
.sg-nagrada-krupno.sg-nagrada--net img { filter: grayscale(1); opacity: .32; }
.sg-ochki {
  margin: var(--s-3) 0 0;
  color: var(--c-gold);
  font: var(--t-label-l);
}

/* ---------- вступление рассказчика ---------- */

/**
 * Портрет крупно, речь под ним, точки и одна кнопка. Из приложения
 * (saga_intro.dart): «семь шагов, по портрету на каждый, точки внизу и одна
 * кнопка, у которой меняется подпись».
 *
 * У нас шагов четыре, а не семь: пятый там просит назваться, а имена лежат в
 * профиле. Портрет один на все четыре, потому что говорит всё время один и тот
 * же: рассказчик.
 */
.sg-vstup { text-align: center; }
.sg-vstup__litso {
  width: 132px;
  height: 132px;
  margin: 0 auto var(--s-4);
  border-radius: 50%;
  object-fit: cover;
  object-position: center 30%;
  border: 2px solid var(--c-border-strong);
  background: var(--c-surface-alt);
}
.sg-vstup__tekst {
  margin: 0 0 var(--s-5);
  min-height: 7em;   /* речи разной длины: без этого кнопка прыгает по шагам */
  font: var(--t-body-m);
  line-height: 1.6;
  color: var(--c-text);
  text-align: start;
}
.sg-vstup .sg-tochki { margin-bottom: var(--s-4); }

/* ==========================================================================
   ШИРОКИЙ ЭКРАН: ИСТОРИЯ ИДЁТ КИНОМ

   ВЗЯТО С БОЯ (ru/games/saga/play/, блок min-width:980px «CINEMATIC RESKIN»), а
   не придумано. Сперва я сделал здесь колонку для чтения: строка в тысячу
   пикселей и правда не читается, и рассуждение было верным. Только боевой
   плеер решает ту же задачу иначе и лучше: картинка занимает ВЕСЬ экран, а
   текст ложится на неё панелью слева внизу, шириной как раз в читаемую
   строку. Колонка получается, и картинка не превращается в марку в углу.

   Владелец сказал коротко: «десктоп сейчас выглядит плохо, подтяни с боевой».
   Он про это.

   БЛОК СТОИТ В КОНЦЕ ФАЙЛА. Медиазапрос не добавляет веса правилу, и всё, что
   написано ниже, перебивает его при равном весе. Правило сквозное.
   ========================================================================== */

@media (min-width: 900px) {
  /* Картинка уходит в фон: она больше не полка сверху, а сама сцена. */
  .sg-igra .sg-art {
    position: absolute;
    inset: 0;
    height: auto;
    z-index: 0;
  }
  /* Крепление к верху: подрезает снизу, а не по головам. */
  .sg-art__kadr { object-fit: cover; object-position: center 22%; }
  .sg-art--litso .sg-art__kadr { object-position: center top; }

  /* Тени по краям кадра больше не нужны: их работу делает подложка текста. */
  .sg-art__kray { display: none; }
  .sg-art__vinjetka { background: linear-gradient(180deg, rgba(4,4,6,.55) 0%, transparent 32%); }

  /* Пара расходится по верху экрана, поверх картинки. */
  .sg-para {
    inset-inline: 0;
    top: 58px;
    bottom: auto;
    z-index: 6;
    padding: 0 26px;
    filter: drop-shadow(0 1px 4px rgba(0, 0, 0, .95));
  }

  /**
   * Текст панелью слева внизу.
   *
   * Подложка НЕ прямоугольник, а растворяющаяся вправо полоса: справа под ней
   * остаётся видна картинка, и панель читается как часть кадра, а не как окно
   * поверх него. Золотая черта слева держит её на месте.
   */
  .sg-igra .sg-stranitsa {
    position: absolute;
    left: 44px;
    right: auto;
    bottom: 40px;
    z-index: 6;
    flex: none;
    width: min(660px, calc(100% - 88px));
    max-height: calc(100svh - 210px);
    overflow: auto;
    padding: 16px 90px 18px 22px;
    border-left: 3px solid var(--c-gold);
    border-radius: 0 4px 4px 0;
    background: linear-gradient(100deg,
      rgba(4, 4, 6, .72) 0%, rgba(4, 4, 6, .34) 52%, transparent 90%);
  }
  .sg-igra .sg-stranitsa > * { max-width: none; margin-inline: 0; }

  /* Крупнее, чем на телефоне: экран смотрят с полутора метров, а не с сорока
     сантиметров. Тень нужна, потому что позади живая картинка. */
  .sg-igra .sg-tekst {
    font-size: 26px;
    line-height: 1.42;
    text-shadow: 0 2px 22px rgba(0, 0, 0, .65);
  }
  .sg-igra .sg-nazvanie {
    font-size: 30px;
    text-shadow: 0 2px 22px rgba(0, 0, 0, .6);
  }

  /* Точки и «дальше» встают в строку: вертикаль тут только отнимает место. */
  .sg-igra .sg-niz { margin-top: 26px; }
  .sg-igra .sg-niz:has(.sg-tochki) {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 22px;
  }
  .sg-igra .sg-niz .sg-tochki { margin-bottom: 0; }
  .sg-igra .sg-niz > .btn { width: auto; padding: 15px 32px; }

  /* Выборы остаются столбиком (сквозное правило), но не во всю панель. */
  .sg-igra .sg-vyiboryi { max-width: 480px; }

  /**
   * «Тесно» на широком экране не значит ничего.
   *
   * На телефоне бит, который не влез, ужимает картинку: она полка сверху и
   * своей высотой распоряжается. Здесь картинка это фон во весь экран, ужимать
   * нечего, а панель и без того с прокруткой.
   */
  .sg-igra--tesno .sg-art { height: auto; }
}

/* ==========================================================================
   ДОБРАНО ПО ЗАМЕЧАНИЯМ ВЛАДЕЛЬЦА 28.08

   Всё, что здесь, он перечислил одним списком: «прямо всё нужно вытащить, все
   цвета и т.д.». Ниже по пунктам, с указанием, откуда взято.
   ========================================================================== */

/* ---------- 1. двери в комнаты разного цвета ----------

   Тему даёт имя узла, куда ведёт кнопка (сервер шлёт его в поле `to`), а не
   слова в метке: метка переводится на десять языков. Красим кромкой и лёгким
   свечением, а не заливкой: заливка спорила бы с золотой первой кнопкой. */

.sg-vyiboryi .btn[data-tema] {
  border-color: color-mix(in srgb, var(--tema) 55%, transparent);
  box-shadow: inset 3px 0 0 var(--tema);
}
.sg-vyiboryi .btn[data-tema] .sg-podskazka { color: color-mix(in srgb, var(--tema) 70%, var(--c-muted)); }

.sg-vyiboryi .btn[data-tema="ogonj"]   { --tema: #F2996B; }
.sg-vyiboryi .btn[data-tema="lyod"]     { --tema: #6FD8FF; }
.sg-vyiboryi .btn[data-tema="bagrets"]  { --tema: #F43F5E; }
.sg-vyiboryi .btn[data-tema="zerkalo"] { --tema: #C7CBD1; }
.sg-vyiboryi .btn[data-tema="kniga"]   { --tema: #B24BE0; }
.sg-vyiboryi .btn[data-tema="voda"]    { --tema: #14B8A6; }
.sg-vyiboryi .btn[data-tema="sad"]     { --tema: #3ECF8E; }
.sg-vyiboryi .btn[data-tema="altarj"]  { --tema: #D4AF37; }
/* Камбуз и ром: тёплое вино. Тему я завёл в коде и забыл здесь, и проверка
   дверей это поймала: восемь красились, девятая оставалась белой. */
.sg-vyiboryi .btn[data-tema="vino"]    { --tema: #C0392B; }

/* ---------- 2. передача бита, а не подмена ----------

   Из приложения, _HandOver: старое уходит целиком (140 мс), и только потом
   приходит новое (240 мс), чуть поднимаясь. Перекрёстное затухание читается
   как два спорящих слоя, а истории это нужно меньше всего. */

.sg-tekst { transition: opacity .14s ease, transform .14s ease; }
.sg-ushyol  { opacity: 0; transform: translateY(4px); }
.sg-prishyol { animation: sg-peredacha .24s cubic-bezier(.2, .7, .3, 1) both; }

@keyframes sg-peredacha {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* ---------- 3. голоса в тексте ----------

   Из story_text.dart: рассказчик светлым золотом при 85 процентах и курсивом,
   говорящий сплошным золотом и жирным, артефакт золотом полужирным. У меня
   рассказчик был просто приглушённым, и от обычного текста не отличался. */

.sg-tekst .narrator,
.sg-zadanie .narrator,
.sg-replika .narrator {
  color: rgba(253, 230, 138, .85);
  font-family: var(--f-serif);
  font-style: italic;
  font-weight: 400;
}
.sg-tekst .speaker,
.sg-zadanie .speaker,
.sg-replika .speaker {
  color: var(--c-gold);
  font-weight: 700;
  font-style: normal;
}
.sg-tekst .art,
.sg-zadanie .art { color: var(--c-gold); font-weight: 600; }

/* ---------- 4. строка главы ----------

   Из приложения: labelSmall, приглушённый тон, разрядка 2. Была золотой и
   спорила за внимание с названием сцены, которое под ней. */

.sg-stranitsa > .eyebrow {
  color: var(--c-muted);
  letter-spacing: 2px;
  font: var(--t-label-s);
  text-transform: uppercase;
}

/* ---------- 5. часы задания в шторке ----------

   Разметка общая с сексополией (.sx-часы), поэтому здесь только место: часы
   стоят отдельной строкой над кнопками, а не жмутся к тексту. */

.sheet .sx-chasyi { margin: var(--s-4) auto var(--s-2); }

/* ---------- 6. финал яруса ----------

   Было: два абзаца подряд без воздуха. Стало: клич крупно, счёт в рамке, и
   приписка про трофеи тише всего. Порядок из _endSheet. */

.sg-final__schyot {
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-lg);
  background: var(--c-surface-high);
  border: 1px solid rgba(212, 175, 55, .35);
}
.sg-final__schyot p {
  font: var(--t-title-s);
  color: var(--c-text);
}
.sg-final__schyot p + p { margin-top: var(--s-1); }

@media (prefers-reduced-motion: reduce) {
  .sg-ushyol, .sg-prishyol, .sg-tost, .sg-nagrada-tost { animation: none; transition: none; }
}


/* ==========================================================================
   ХАБ НА ШИРОКОМ ЭКРАНЕ: ВКЛАДКИ СБОКУ, А НЕ ПОЛОСОЙ

   ВЗЯТО С БОЯ (ru/games/saga/, блок min-width:860px), а не придумано. Владелец
   сказал прямо: «посмотри, как в бою сделано, там куда лучше». Смотрю, и там
   действительно другое решение той самой пустоты, на которую он жалуется:

     .tabbar { display: none }        полосы вкладок на десктопе НЕТ
     .sidebar { width: 224px; ... }   вместо неё колонка слева, липкая
     .ptitle { font-size: 34px }      заголовок вдвое крупнее телефонного
     .grid   { 1fr 1fr }              карточки по две, по три от 1440

   Почему это лучше моей строки сверху. Пять вкладок, растянутые на всю ширину,
   съедают полосу высотой в кнопку и не заполняют НИЧЕГО: пустота остаётся, она
   просто уезжает ниже. Колонка слева забирает пустоту себе и превращает её в
   навигацию, а содержимому оставляет ровно ту ширину, в которой карточки
   читаются.

   ДВЕ КОЛОНКИ ПОДРЯД ЭТО НЕ ОШИБКА. Слева уже стоит рельс разделов, и рядом
   встаёт колонка вкладок саги. Это два разных уровня: «где я в приложении» и
   «где я внутри саги». Так устроены почтовые клиенты, и так устроен бой.
   ========================================================================== */

@media (min-width: 900px) {
  /* Родителю нужен грид, значит нужен и он сам. Хватаем его через :has, чтобы
     не заводить класс ради одной раскладки. */
  .screen:has(> .sg-pilyulya),
  div:has(> .sg-pilyulya) {
    display: grid;
    grid-template-columns: 224px minmax(0, 1fr);
    grid-template-areas:
      'shapka shapka'
      'vkladki list';
    column-gap: 30px;
    align-items: start;
  }
  .sg-shapka  { grid-area: shapka; max-width: none; margin-inline: 0; }
  .sg-pilyulya { grid-area: vkladki; }
  .sg-list   { grid-area: list; max-width: none; margin-inline: 0; }

  /* Заголовок вдвое крупнее телефонного: 34 с боя. */
  .sg-shapka__imya { font-size: 34px; }

  /**
   * Колонка вкладок. Липкая: список приключений длиннее её, и уезжать вместе с
   * ним ей незачем.
   */
  .sg-pilyulya {
    position: sticky;
    top: 24px;
    inset: auto;
    transform: none;
    width: 224px;
    max-width: none;
    flex-direction: column;
    gap: 3px;
    padding: 0;
    margin: 0;
    background: none;
    border: none;
    box-shadow: none;
    backdrop-filter: none;
  }
  .sg-pilyulya__kn {
    flex-direction: row;
    justify-content: flex-start;
    gap: 12px;
    width: 100%;
    height: 44px;
    padding: 0 12px;
    border-radius: 10px;
    text-align: start;
  }
  .sg-pilyulya__kn span {
    font: 600 14.5px/1 var(--f-sans);
    letter-spacing: normal;
  }
  .sg-pilyulya__kn[aria-current="page"] { background: var(--c-surface-high); }

  /* Полосы внизу больше нет, значит и места под неё не нужно. */
  .sg-list { padding-bottom: var(--s-6); }

  /* Карточки по две, по три от 1440: числа с боя. */
  .sg-list:has(.sg-karta) {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 18px;
    align-items: stretch;
  }
  .sg-karta { display: flex; flex-direction: column; }
  .sg-karta__niz { flex: 1; display: flex; flex-direction: column; }
  /* Описание тянется, кнопка прижата к низу: ряд получается ровным. */
  .sg-karta__opis { flex: 1; }
  .sg-karta__imya { font-size: 19px; }
}

@media (min-width: 1440px) {
  .sg-list:has(.sg-karta) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ==========================================================================
   ИСТОРИЯ НА ШИРОКОМ: ПАРА НИЖЕ КНОПКИ «НАЗАД»

   Владелец: «стрелочка назад внутри саги накладывается на аватарку». Так и
   есть: кнопка сидит в левом верхнем углу картинки, а строка пары стояла на
   58 от верха, и лицо игрока уезжало ей под низ.

   Опускаем пару под кнопку и отводим обе строки от края на её ширину, чтобы
   имя не начиналось там, где кончается кружок.
   ========================================================================== */

@media (min-width: 900px) {
  .sg-igra .sg-para {
    top: 76px;
    padding-inline: 26px;
  }
}

/* ==========================================================================
   БОКОВАЯ КОЛОНКА ПОКРУПНЕЕ

   Владелец: «на десктопе боковое поле получилось мелким». Так и есть. Я взял с
   боя ширину 224 и кегль 14.5, но не взял того, что даёт колонке вес: там
   значки 20 на 20 при полной непрозрачности, строки высотой 48 и заголовок
   раздела сверху. У меня значки остались телефонными, погашенными на 45
   процентов, и колонка читалась как список подписей, а не как навигация.

   Числа: строка 48 (вместо 44), значок 20 при непрозрачности 0.7 у спящих и 1
   у текущей, кегль 15.5, между строками 4. Плюс метка раздела над ними: она
   объясняет, что это вкладки саги, а не второй набор разделов сайта.
   ========================================================================== */

@media (min-width: 900px) {
  .sg-pilyulya {
    width: 236px;
    gap: 4px;
    padding-top: 4px;
  }
  .sg-pilyulya__kn {
    height: 48px;
    padding: 0 14px;
    gap: 14px;
    border-radius: 12px;
    color: var(--c-muted);
    transition: background var(--m-standard) var(--m-ease),
                color var(--m-standard) var(--m-ease);
  }
  .sg-pilyulya__kn span { font: 600 15.5px/1 var(--f-sans); }
  .sg-pilyulya__kn .glyph { width: 20px; height: 20px; opacity: .7; }
  .sg-pilyulya__kn:hover { background: var(--c-surface); color: var(--c-text); }
  .sg-pilyulya__kn[aria-current="page"] {
    background: var(--c-surface-high);
    color: var(--c-text);
  }
  .sg-pilyulya__kn[aria-current="page"] .glyph { opacity: 1; }

  /**
   * Метка над колонкой: это вкладки САГИ, а не второй набор разделов сайта.
   * Рядом с рельсом каркаса без неё колонка читается двусмысленно.
   */
  .sg-pilyulya::before {
    content: attr(aria-label);
    display: block;
    padding: 0 14px var(--s-2);
    color: var(--c-dim);
    font: 600 var(--t-label-s) var(--f-mono);
    letter-spacing: 2px;
    text-transform: uppercase;
  }
}

/* ==========================================================================
   ЗАПЕРТОЕ ПРИКЛЮЧЕНИЕ ВЫГЛЯДИТ ЗАПЕРТЫМ

   Из приложения (saga_hub.dart, _AdventureCard), и там это подписано одной
   строкой: «закрытое приключение сохраняет свою картинку, но теряет цвет».
   Плюс замок поверх и призрачная кнопка, которая не нажимается.

   У меня закрытое выглядело открытым: цветная картинка и золотая кнопка
   «Начать». Владелец прислал скрин и спросил, почему она активная. Кнопка
   обещала то, чего за ней нет, а картинка не давала повода усомниться.

   Обесцвечивание с притемнением, а не просто серость: в приложении это матрица
   яркости со сдвигом на минус сорок, то есть карточка ещё и уходит в тень.
   ========================================================================== */

.sg-karta--zaperta .sg-karta__art img {
  filter: grayscale(1) brightness(.55);
}
.sg-karta--zaperta .sg-karta__imya,
.sg-karta--zaperta .sg-karta__nomer { opacity: .75; }

/* Замок ровно посередине картинки: он и есть ответ на вопрос «почему». */
.sg-zamok {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  pointer-events: none;
}
.sg-zamok .icon,
.sg-zamok svg,
.sg-zamok img {
  width: 30px; height: 30px;
  opacity: .54;
}

/**
 * Кнопка запертого. Призрачная и приглушённая; запертая по-настоящему
 * (disabled) там, где предложить нечего, и живая там, где за ней премиум.
 */
.btn--zaperta {
  background: none;
  border-color: var(--c-border);
  color: var(--c-muted);
}
.btn--zaperta[disabled] {
  opacity: .55;
  cursor: default;
}
