/* Срез v0 — на токенах каркаса brief/style.html и утверждённом макете
   gates/004-v0-verticalnyj-srez/design-gate/mockup.css (канон раскладки).
   Все значения — только из закрытых наборов (правило 4); две высоты 44/52. */
:root{
  --bg-0:#1a0620; --bg-1:#2a0a33; --bg-2:#3a1044;
  --bg-glass:rgba(58,16,68,.55);
  --stroke:rgba(255,255,255,.10); --stroke-bright:rgba(255,255,255,.22);
  --crystal-raspberry:#E91E63; --crystal-orange:#EC610A; --crystal-violet:#8E24FF;
  --crystal-yellow:#FFC300; --crystal-teal:#12D6C6; --crystal-green:#3FD152;
  --ink:#FFF3F8; --ink-dim:#D3A9C4; --ink-faint:#9A7690;
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-6:24px; --sp-8:32px;
  --r-2:8px; --r-3:12px; --r-4:18px; --r-pill:999px;
  --glow-raspberry:0 0 18px rgba(233,30,99,.65);
  --shadow-card:0 10px 30px rgba(0,0,0,.45);
  --font:'Segoe UI',system-ui,-apple-system,'Helvetica Neue',Arial,sans-serif;
  --stone:#6E6F7A;    /* камень окаменения — холодный сланец (этап 016) */
  --stone-hi:#9AA0AE;
  --h-row:44px;   /* строчные контролы */
  --h-cta:52px;   /* транспорт/CTA */
}
*{box-sizing:border-box}
html,body{height:100%}
body{
  margin:0; font-family:var(--font); color:var(--ink);
  background:radial-gradient(1200px 800px at 50% -10%,var(--bg-1) 0%,transparent 60%),var(--bg-0);
  height:100vh; display:flex; flex-direction:column; overflow:hidden;
}
.version{position:fixed; top:var(--sp-2); left:var(--sp-3); z-index:20;
  font-size:12px; letter-spacing:.4px; color:var(--ink-faint)}

/* ——— экраны: ровно один виден ——— */
.screen{display:none; flex:1; min-height:0; flex-direction:column}
.screen.on{display:flex}
.screen.center{align-items:center; justify-content:center; gap:var(--sp-6);
  padding:var(--sp-6); text-align:center}
.center h1{margin:0; font-size:34px; letter-spacing:.5px}
.center .sub{margin:0; color:var(--ink-dim); max-width:36ch; line-height:1.5}

/* ——— шапка цели ——— */
.goalbar{display:flex; align-items:center; gap:var(--sp-3); height:var(--h-row);
  margin:var(--sp-6) var(--sp-3) 0; padding:0 var(--sp-4); flex-shrink:0;
  background:var(--bg-1); border:1px solid var(--stroke); border-radius:var(--r-4)}
.goalbar .goal{display:flex; align-items:center; gap:var(--sp-2); flex-shrink:0; min-width:max-content}
.goalbar .goal .dot{width:16px; height:16px; border-radius:4px; flex-shrink:0}
.goalbar .goal b{color:var(--ink)}
.goalbar .track{flex:1; height:8px; border-radius:var(--r-pill);
  background:var(--bg-2); overflow:hidden; min-width:40px}
.goalbar .track > i{display:block; height:100%; width:0; border-radius:var(--r-pill)}
/* ——— полка снаряжения на карте (этап 026, канон р. 9) ———
   Склянка — контрол-СОСТОЯНИЯ: есть в запасе, взята в поход, нет в запасе.
   Состояние выражено кантом и свечением, НЕ смещением (правило 7). */
.snar{display:flex; flex-wrap:wrap; gap:var(--sp-2); align-items:center;
  justify-content:center; padding:0 var(--sp-3); flex-shrink:0}
.snar .flask{display:flex; align-items:center; gap:var(--sp-2);
  height:var(--h-row); padding:0 var(--sp-3); border-radius:var(--r-4);
  background:var(--bg-2); border:1px solid var(--stroke); color:var(--ink);
  font-family:var(--font); font-size:14px; cursor:pointer; flex-shrink:0}
.snar .flask .n{font-weight:700; color:var(--ink-dim)}
.snar .flask.taken{border-color:currentColor; box-shadow:0 0 12px currentColor}
.snar .flask.taken .n{color:currentColor}
.snar .flask.empty{opacity:.5}
/* ряд подвала карты: пересдача и спуск (этап 029) */
.rerow{display:flex; gap:var(--sp-2); align-items:center; justify-content:center}
.re{display:flex; align-items:center; gap:var(--sp-2); height:var(--h-row);
  padding:0 var(--sp-3); border-radius:var(--r-4); background:var(--bg-2);
  border:1px solid var(--stroke); color:var(--ink); font-family:var(--font);
  font-size:14px; cursor:pointer; flex-shrink:0}
.re[hidden]{display:none}
.re .price{color:var(--crystal-yellow); font-weight:700}
.re svg{width:18px; height:18px}
/* строка под кнопкой несёт и приветствие, и нрав владения — перенос строки в
   ней значим (этап 029) */
.foot .why{white-space:pre-line}
/* находка СТОИТ в слоте, пока её не вынут — тот же язык состояния, что у
   «взята в поход» у склянки: кант и свечение своим цветом (правило 7) */
.snar .flask.find.slotted{border-color:currentColor; box-shadow:0 0 12px currentColor}
.snar .flask.find.slotted .n{color:currentColor}
.snar .flask .price{font-size:12px; color:var(--crystal-yellow); font-weight:700}
.snar .flask svg{width:18px; height:18px; flex-shrink:0}

/* ——— разбор дохода на итогах (этап 025, канон р. 11) ———
   На экране итогов свободно 518 px из 844 — в отличие от экрана игры, где было
   40. Поэтому разбор стоит полкой и ни с чем не спорит. */
.kazna{display:flex; flex-direction:column; gap:var(--sp-2); width:100%;
  max-width:330px; margin:var(--sp-3) 0}
.kazna .row{display:flex; align-items:center; gap:var(--sp-2);
  height:var(--h-row); padding:0 var(--sp-3); border-radius:var(--r-4);
  background:var(--bg-2); border:1px solid var(--stroke)}
.kazna .row .what{flex:1; font-size:14px; color:var(--ink); text-align:left}
.kazna .row .n{font-size:15px; font-weight:700; color:var(--crystal-yellow);
  white-space:nowrap}
/* строка, которой НЕ ДАЛИ: она и учит — «в следующий раз почистите поле» */
.kazna .row.miss{opacity:.45}
.kazna .row.miss .n{color:var(--ink-dim)}
/* Итог лежал прямо на декоративной полке фона и терялся на ней: аудит этого не
   видит — фон не элемент, его ловит только глаз на 1:1 (ревью этапа 028).
   Подложка того же языка, что у строк, но легче: итог не строка дохода. */
.kazna .total{display:flex; align-items:center; gap:var(--sp-2);
  height:var(--h-row); padding:0 var(--sp-3); border-radius:var(--r-4);
  background:var(--bg-0); border:1px solid var(--stroke)}
.kazna .total .what{flex:1; font-size:14px; color:var(--ink-dim); text-align:left}
.kazna .total .n{font-size:18px; font-weight:700; color:var(--crystal-yellow)}

/* ——— два рукава прокачки цвета (этап 023, канон р. 7) ———
   Каждая метка стоит на том, на что влияет, и не стоит ряду НИ ПИКСЕЛЯ ширины:
   на 390 шесть шкал уже переносятся 3+3, на 1400 запас 49 px из 758.

   ЖИЛА ускоряет налив — её засечки живут на цветной базе шкалы. База выбрана
   потому, что её ширина ПОСТОЯННА: на самой заливке засечки уезжали вместе с
   наливом и ступень нельзя было сосчитать. Псевдоэлементами тоже нельзя — их у
   элемента два, и ступень 3 выглядела как 2.

   ОГРАНКА усиливает удар — её метка кантом вокруг значка ульты. Кант, а не
   смещение и не размер (правило 7). */
.bar.z1::after, .bar.z2::after, .bar.z3::after{opacity:.5; height:4px}
.bar.z1::after{background-image:linear-gradient(90deg,
  transparent 30%, var(--ink) 30%, var(--ink) 33%, transparent 33%)}
.bar.z2::after{background-image:linear-gradient(90deg,
  transparent 30%, var(--ink) 30%, var(--ink) 33%, transparent 33%,
  transparent 47%, var(--ink) 47%, var(--ink) 50%, transparent 50%)}
.bar.z3::after{background-image:linear-gradient(90deg,
  transparent 30%, var(--ink) 30%, var(--ink) 33%, transparent 33%,
  transparent 47%, var(--ink) 47%, var(--ink) 50%, transparent 50%,
  transparent 64%, var(--ink) 64%, var(--ink) 67%, transparent 67%)}
.bar.o1 > svg.ico{outline:1px solid currentColor; outline-offset:2px; border-radius:4px}
.bar.o2 > svg.ico{outline:2px solid currentColor; outline-offset:2px; border-radius:4px}
.bar.o3 > svg.ico{outline:3px solid currentColor; outline-offset:2px; border-radius:4px}

/* ——— шкалы прокачки пары (этап 021, канон р. 8) ———
   Созвучие личное, Лад — с этим человеком. Три сегмента = три ступени ЭХА:
   игрок принимает решение по ступени, а очки служебны. Обе стоят ВНУТРИ уже
   существующих рядов и не стоят экрану ни пикселя по вертикали: на 390×844
   свободных всего 40, и полка отняла бы их у поля (P2). */
/* ——— ЭТАП 038: ЛАД — КОЛЬЦО, А НЕ ВТОРАЯ ТРОЙКА СЕГМЕНТОВ ———
   Без подписей «Созвучие» и «Лад» становились двумя одинаковыми тройками, и
   различало их только место. Кольцо стоит ВОКРУГ огонька напарника: форма разная
   и смысл «связь именно с ним» стоит на нём. */
/* Кольцо садится ВОКРУГ огонька: 40 px вокруг аватара 26 px, по центру.
   Первый заход дал `inset:0` от строки напарника — кольцо растянулось на
   364×42 и стало отдельным кружком сбоку (замер, не глаз). */
.partner .who{position:relative; padding-left:var(--sp-2)}
.grow.lad{position:absolute; left:0; top:50%; width:42px; height:42px;
  transform:translateY(-50%); pointer-events:none}
.grow.lad .ring{width:100%; height:100%; display:block}

.grow{display:flex; align-items:center; gap:var(--sp-1); flex-shrink:0}
.grow .gl{font-size:11px; color:var(--ink-dim); white-space:nowrap}
.grow .segs{display:flex; gap:2px}
.grow .segs u{display:block; width:12px; height:6px; border-radius:2px;
  background:var(--bg-0); border:1px solid var(--stroke); position:relative;
  overflow:hidden}
.grow .segs u.on{background:var(--crystal-teal); border-color:var(--crystal-teal)}
.grow .segs u i{position:absolute; inset:0 auto 0 0; display:block;
  background:var(--crystal-teal); opacity:.55}
.grow.lad .segs u.on{background:var(--crystal-green); border-color:var(--crystal-green)}
.grow.lad .segs u i{background:var(--crystal-green)}

.goalbar .limit{display:flex; align-items:center; gap:var(--sp-1); flex-shrink:0;
  min-width:max-content; color:var(--ink-dim); font-size:14px}
/* Затишье держит время (этап 018): число ЗАМЕРЛО, и чтобы это не читалось
   подвисанием, таймер берёт цвет ульты и кант. AC-5 спеки живёт здесь. */
.goalbar .limit.stopped{color:var(--crystal-teal); font-weight:700;
  text-shadow:0 0 12px color-mix(in srgb, var(--crystal-teal) 65%, transparent)}

/* ——— присутствие + суперудары напарника ——— */
.partner{display:flex; align-items:center; gap:var(--sp-2); height:var(--h-row);
  margin:var(--sp-2) var(--sp-3) 0; padding:0 var(--sp-3); flex-shrink:0;
  background:var(--bg-glass); border:1px solid var(--stroke); border-radius:var(--r-4)}
.partner .who{display:flex; align-items:center; gap:var(--sp-2); flex-shrink:0; min-width:max-content}
.partner .ava{width:26px; height:26px; border-radius:var(--r-pill);
  background:var(--crystal-violet); border:1px solid var(--stroke-bright); flex-shrink:0}
.partner .name{font-size:14px; color:var(--ink)}
.partner .mini{display:flex; gap:var(--sp-1); flex-shrink:0}
/* Каждая мини-шкала — стабильный класс .pip (структурная группа для аудита);
   «горячее» состояние ДОБАВЛЯЕТ .hot, а не подменяет базовый класс (правило 7 по
   духу: активность — свечением, набор классов не «дробит» ряд). */
.partner .mini .pip{width:16px; height:16px; border-radius:4px; opacity:.35}
.partner .mini .pip.hot{opacity:1; box-shadow:0 0 8px currentColor}

/* ——— поле (canvas; размер считает JS под доступную высоту) ——— */
.boardwrap{flex:1; display:flex; align-items:center; justify-content:center;
  min-height:0; padding:var(--sp-3);
  /* показ жеста лежит в этом же боксе и садится по фактическому месту канваса */
  position:relative}
#board{background:var(--bg-1); border:1px solid var(--stroke);
  border-radius:var(--r-3); touch-action:none; display:block}

/* ——— мои шкалы ——— */
/* Шесть шкал — ОДНИМ рядом (этап 038). Пока у шкалы была подпись, ряд честно
   переносился на две строки по три; без подписей ширина стала меньше, и перенос
   дал 5 + 1 — ряд однотипных соседей развалился. Аудит этого не видит: перенос
   строки не его правило (класс P17), поймано глазом на кадре 1:1.
   Теперь шкалы делят ширину поровну и не переносятся по построению. */
.energy{display:flex; gap:var(--sp-2); justify-content:center;
  padding:var(--sp-2) var(--sp-3); flex-shrink:0}
.bar{position:relative; height:var(--h-row); flex:1 1 0; min-width:0; max-width:96px;
  justify-content:center;
  padding:0 var(--sp-2); border-radius:var(--r-4); overflow:hidden;
  background:var(--bg-2); border:1px solid var(--stroke); color:var(--ink);
  font-family:var(--font); display:flex; align-items:center; font-size:14px; cursor:default}
.bar > i{position:absolute; left:0; top:0; bottom:0; width:0; opacity:.55}
.bar > span{position:relative}
/* Значок ульты (этап 019): шкала говорит, ЧТО сделает, ещё до удара — AC-8
   этапа 018 был закрыт наполовину, потому что различие несёт эффект на поле, а
   решение копить игрок принимает раньше. Значок наследует currentColor, то есть
   цвет своего кристалла: игровой цвет не выбеливается (P3). */
.bar > svg.ico{position:relative; width:18px; height:18px; flex-shrink:0;
  margin-right:var(--sp-2)}
.bar.ready{border-color:var(--ink); box-shadow:0 0 16px currentColor; font-weight:700; cursor:pointer}
/* Пожар горит (этап 018): у шкалы своего цвета появляется тлеющий кант, а
   ПОЛОСКА НАСЕЧЕК сверху показывает, сколько ходов ещё гореть. Насечки — это
   градиент по существующему элементу, а не новый контрол: `--burn` задаёт JS. */
.bar.burning{border-color:var(--crystal-orange);
  box-shadow:0 0 14px color-mix(in srgb, var(--crystal-orange) 60%, transparent)}
.bar.burning::before{content:''; position:absolute; left:var(--sp-2);
  right:var(--sp-2); top:3px; height:3px; border-radius:var(--r-pill);
  background:repeating-linear-gradient(90deg,
    var(--crystal-orange) 0, var(--crystal-orange) calc(100% / var(--burn) - 3px),
    transparent calc(100% / var(--burn) - 3px), transparent calc(100% / var(--burn)))}
/* Всегда видимая цветная база — чтобы контрол читался как ШКАЛА-метр даже на нуле
   (О4/Р7), а не как цветной чип. currentColor задаёт JS по цвету кристалла. */
.bar::after{content:''; position:absolute; left:0; right:0; bottom:0; height:3px;
  background:currentColor; opacity:.4}

/* Время: часы и число одной группой. Слово «осталось» снято директивой 037. */
.limitbox{display:flex; align-items:center; gap:var(--sp-1); flex-shrink:0}
.limitbox .tic{width:18px; height:18px; color:var(--ink-dim); flex-shrink:0}

/* ——— ОТКАЗ ШКАЛЫ: «ещё копится» ЭФФЕКТОМ (этап 038) ———
   Рука опережает шкалу постоянно, и слово в ответ на это игрок перечитывает
   десятки раз за поход. В казуальных играх отказ отвечают короткой затухающей
   тряской и вспышкой (0.1–0.3 с) — так и сделано: 0.28 с, три затухающих
   толчка и вспышка канта своим цветом. Смещение здесь ЗАКОННО: это показ
   события, а не состояние «выбран» (правило 7 запрещает второе). */
.bar.malo{animation:bar-malo .28s ease-out}
@keyframes bar-malo{
  0%{transform:translateX(0)}
  20%{transform:translateX(-5px); box-shadow:0 0 0 2px currentColor}
  45%{transform:translateX(4px)}
  70%{transform:translateX(-2px); box-shadow:0 0 0 1px currentColor}
  100%{transform:translateX(0)}
}

/* Эхо «подготовил»: следующий удар сильнее. Показ — вспышка ВСЕХ шести шкал,
   потому что усилен будет любой из них. Без этого смысл эха исчез бы вместе со
   снятой строкой (правило 11). */
.bar.gotov{animation:bar-gotov 1.4s ease-out}
@keyframes bar-gotov{
  0%{box-shadow:0 0 0 0 rgba(255,255,255,0)}
  25%{box-shadow:0 0 14px 2px rgba(255,255,255,.55)}
  100%{box-shadow:0 0 0 0 rgba(255,255,255,0)}
}

/* ——— ПОКАЗ ЖЕСТА (этап 038) ———
   Заменяет подсказку словами: две соседние клетки в канте и знак обмена между
   ними. Геометрия считается по ФАКТИЧЕСКОМУ боксу канваса (P3: измеряем, не
   гадаем) — размеры ставит код, здесь только вид. Кант не двигает поле и не
   ловит касания. */
.pokaz{position:absolute; pointer-events:none; z-index:3;
  border-radius:var(--r-2); display:flex; align-items:center; justify-content:center;
  color:#fff; box-shadow:0 0 0 3px rgba(255,255,255,.92), 0 0 26px rgba(255,255,255,.5);
  animation:pokaz-breath 1.8s ease-in-out infinite}
.pokaz svg{width:26px; height:26px; filter:drop-shadow(0 2px 3px rgba(0,0,0,.9))}
@keyframes pokaz-breath{0%,100%{opacity:.55} 50%{opacity:1}}

.hint{flex-shrink:0; text-align:center; padding:0 var(--sp-4) var(--sp-3);
  font-size:13px; color:var(--ink-faint); min-height:calc(13px + var(--sp-3))}

/* ——— экран входа ——— */
.presence{display:flex; align-items:center; gap:var(--sp-3); height:var(--h-row);
  padding:0 var(--sp-6); background:var(--bg-1); border:1px solid var(--stroke);
  border-radius:var(--r-pill)}
.presence .ava{width:30px;height:30px;border-radius:var(--r-pill);
  background:var(--crystal-violet); border:1px solid var(--stroke-bright)}
.presence .st{width:10px;height:10px;border-radius:var(--r-pill);background:var(--crystal-green)}
.presence .st.off{background:var(--ink-faint)}
.cta{height:var(--h-cta); padding:0 var(--sp-8); border:0; border-radius:var(--r-pill);
  background:var(--crystal-raspberry); color:var(--ink); font:inherit; font-size:17px;
  font-weight:700; box-shadow:var(--glow-raspberry); cursor:pointer}
.cta.ghost{background:transparent; border:1px solid var(--stroke-bright);
  color:var(--ink); box-shadow:none; font-weight:600}
.cta:disabled{background:var(--bg-2); color:var(--ink-faint); box-shadow:none;
  cursor:default; border:1px solid var(--stroke)}

/* ——— экран приза ——— */
.prizegem{width:150px; height:150px; border-radius:40%;
  background:var(--crystal-raspberry); box-shadow:var(--glow-raspberry);
  border:2px solid var(--stroke-bright)}
/* цветная метка у имени приза — привязывает незнакомое слово к цвету (Р6) */
.pdot{display:inline-block; width:14px; height:14px; border-radius:4px;
  vertical-align:middle; margin-right:var(--sp-1)}
.pill{display:inline-flex; align-items:center; gap:var(--sp-2); height:var(--h-row);
  padding:0 var(--sp-4); border-radius:var(--r-pill); background:var(--bg-1);
  border:1px solid var(--stroke); color:var(--ink-dim); font-size:14px}
.pill .ava{width:22px; height:22px; border-radius:var(--r-pill);
  background:var(--crystal-violet); border:1px solid var(--stroke-bright)}

/* Узкий вьюпорт: цель+прогресс+лимит в один ряд не влезают (P3 — текст задаёт
   размер). Перепроектируем РЯД: цель и лимит сверху, трек — своей строкой на всю
   ширину. Не ужимаем контролы. Фикс живёт в media этого вьюпорта; десктоп
   перепроверяется аудитом отдельно. */
@media (max-width:480px){
  .goalbar{flex-wrap:wrap; height:auto; min-height:var(--h-row);
    padding-top:var(--sp-2); padding-bottom:var(--sp-2)}
  .goalbar .goal{order:1}
  .goalbar .limit{order:2; margin-left:auto}
  .goalbar .track{order:3; flex-basis:100%}
}

/* Десктоп: не растягиваем шапки на всю ширину (иначе поле болтается по центру, а
   имя напарника оторвано от его шкал — В1). Собираем игру в центрированную колонку
   по ширине поля: имя+шкалы напарника рядом, срочное (цель/время) у взгляда. */
@media (min-width:900px){
  #screen-game.on{align-self:center; width:100%; max-width:760px}
}

/* ——————————————————— лаборатория материала (маршрут #lab) ———————————————————
   Инструмент разработчика, не экран игрока: в игровой навигации не участвует.
   Форму держим по общим правилам — токены каркаса (правило 4), строчная высота
   44 и CTA 52 (правило 5), ничего не наложено и не обрезано (правило 2),
   иконок-эмодзи нет (правило 6). Ритм стека — gap контейнера, не парные margin. */
/* Экран не растёт за вьюпорт: длинный список переменных прокручивается ВНУТРИ
   панели, а не выталкивает её за край (иначе нижние контролы недостижимы). */
.screen.lab{flex-direction:row; gap:var(--sp-4); padding:var(--sp-6) var(--sp-3) var(--sp-3);
  min-height:0; overflow:hidden}
.labstage{flex:1; min-width:0; min-height:0; display:flex;
  align-items:center; justify-content:center}
.labstage canvas{display:block; border-radius:var(--r-4);
  border:1px solid var(--stroke); background:var(--bg-0)}
.labside{width:400px; flex-shrink:0; display:flex; flex-direction:column;
  gap:var(--sp-2); min-height:0}
.labreadout{flex-shrink:0; min-height:var(--h-row); display:flex; align-items:center;
  padding:var(--sp-2) var(--sp-3); border-radius:var(--r-3);
  background:var(--bg-1); border:1px solid var(--stroke);
  color:var(--ink-dim); font-size:13px; line-height:1.35}
/* overflow ОБА в auto, не hidden: `hidden` — это клиппер, и содержимое ниже
   сгиба становится «обрезанным без способа его увидеть» (CLIPPED в ui-audit,
   правило 2). auto означает «доступно прокруткой» — что здесь и есть правда. */
.labpanel{flex:1; min-height:0; overflow:auto;
  display:flex; flex-direction:column; gap:var(--sp-4);
  padding:var(--sp-3); border-radius:var(--r-4);
  background:var(--bg-glass); border:1px solid var(--stroke)}
.labgroup{display:flex; flex-direction:column; gap:var(--sp-1)}
.labgroup h2{margin:0; padding:0 var(--sp-1); font-size:13px; font-weight:700;
  letter-spacing:.6px; text-transform:uppercase; color:var(--ink-faint)}
.labrow{display:flex; align-items:center; gap:var(--sp-2); height:var(--h-row);
  padding:0 var(--sp-1)}
/* Подпись ПЕРЕНОСИТСЯ, а не режется многоточием: «показатель прело…» — это
   обрезанный текст, то есть ровно то, что запрещает правило 2. Две строки по
   13px укладываются в строчную высоту 44. */
.labname{flex:1; min-width:0; font-size:13px; line-height:1.15; color:var(--ink-dim)}
/* Высота контрола — строчная 44 (правило 5): мерилом служит сам интерактивный
   элемент, а не строка вокруг него, иначе палец промахивается по 28-пиксельному
   ползунку в 44-пиксельной строке. */
.labctl{flex-shrink:0; width:120px; height:var(--h-row);
  accent-color:var(--crystal-raspberry)}
.labctl-bool{width:var(--h-row); height:var(--h-row)}
.labctl-color{width:var(--h-row); height:var(--h-row); padding:0;
  border:1px solid var(--stroke); border-radius:var(--r-2); background:var(--bg-1)}
.labctl-enum{height:var(--h-row); border-radius:var(--r-2); background:var(--bg-1);
  color:var(--ink); border:1px solid var(--stroke); font:inherit; font-size:12px}
.labval{flex-shrink:0; width:60px; text-align:right; font-size:12px;
  font-variant-numeric:tabular-nums; color:var(--ink)}
.labbar{display:flex; flex-wrap:wrap; gap:var(--sp-2); padding:0 var(--sp-1)}
.labbtn{height:var(--h-cta); padding:0 var(--sp-4); border-radius:var(--r-pill);
  background:transparent; border:1px solid var(--stroke-bright); color:var(--ink);
  font:inherit; font-size:14px; font-weight:600; cursor:pointer}

/* Узкий вьюпорт: панель уходит под сцену, ширина отдаётся камню целиком. */
@media (max-width:900px){
  .screen.lab{flex-direction:column}
  .labside{width:auto; flex:1 1 0; min-height:0}
  .labstage{flex:0 0 38vh; min-height:0}
}

/* ═══════════════════════════════════════════════════════════════════════════
   ЭТАП 011 — слой «B · Полированный» + сцена «кристаллическая пещера».
   UD-007 (материал), UD-008 (сцена), UD-009 (подсветка выбора).
   Приём один на весь экран: у каждой поверхности есть ВЕРХ (светлая фаска),
   НИЗ (тёмное ребро) и ТЕНЬ НА ОСНОВАНИИ. Свет тот же, что у фишек на поле.
   ═══════════════════════════════════════════════════════════════════════════ */
:root{
  /* сцена нейтральная серо-сизая: фиолет принадлежит игре, декор его не занимает */
  --bg-0:#151922; --bg-1:#1E232C; --bg-2:#2A303B;
  --bg-glass:rgba(30,35,44,.55);
  --stroke:rgba(190,205,230,.14); --stroke-bright:rgba(190,205,230,.28);
  --ink:#F2F5FA; --ink-dim:#C3CCDA; --ink-faint:#8894A6;
  --plate-hi:#2E3340; --plate-lo:#1C2029;
  --rim-top:inset 0 1px 0 rgba(255,255,255,.16);
  --rim-bottom:inset 0 -2px 0 rgba(0,0,0,.30);
  --lift-1:0 1px 0 rgba(0,0,0,.35), 0 4px 10px rgba(0,0,0,.32);
  --lift-2:0 2px 0 rgba(0,0,0,.42), 0 10px 22px rgba(0,0,0,.42);
  --groove:inset 0 2px 4px rgba(0,0,0,.45), inset 0 -1px 0 rgba(255,255,255,.06);
}

/* декор сцены: краска слоя, а не элемент — элемент, торчащий за вьюпорт,
   ui-audit справедливо ловит как OFFSCREEN, и правило гасить ради картинки нельзя */
.bg{position:fixed; inset:0; z-index:-1; background-color:var(--bg-0);
  background-repeat:no-repeat}

/* панели приподняты над сценой и светятся верхним ребром */
.goalbar,.partner,.presence,.pill{
  background:linear-gradient(180deg,var(--plate-hi) 0%,var(--plate-lo) 100%);
  box-shadow:var(--rim-top),var(--rim-bottom),var(--lift-1);
  border-color:rgba(190,205,230,.20);
  backdrop-filter:blur(2px);
}
.goalbar .track{box-shadow:var(--groove); background:#141821; border:0}
.goalbar .goal .dot,.partner .mini .pip,.pdot{
  box-shadow:var(--rim-top),var(--rim-bottom),0 1px 0 rgba(0,0,0,.35);
}

/* поле — утопленная коробка в нише декора */
#board{
  background:transparent; border:0;
  box-shadow:var(--groove), var(--lift-2);
  border-radius:var(--r-4);
}

/* шкалы: пластиковая пилюля; метр — ДОННОЙ полосой, иначе заливка режет слово
   пополам («оранж», «жёлтый») — дефект пойман на макете */
.bar{
  background:linear-gradient(180deg,var(--plate-hi) 0%,var(--plate-lo) 100%);
  box-shadow:var(--rim-top),var(--rim-bottom),var(--lift-1);
  border-color:rgba(190,205,230,.20);
  backdrop-filter:blur(2px);
}
.bar > i{top:auto; bottom:0; height:8px; opacity:1; border-radius:var(--r-pill)}
.bar > span{color:var(--ink); font-weight:600}
.bar::after{opacity:.22}
.bar.ready{box-shadow:var(--rim-top),var(--rim-bottom),var(--lift-1),0 0 16px currentColor}

.cta{box-shadow:var(--rim-top),var(--rim-bottom),var(--lift-2),var(--glow-raspberry)}
.cta.ghost{box-shadow:var(--rim-top),var(--lift-1)}
.cta:disabled{box-shadow:var(--groove)}

/* Текст живёт ПОВЕРХ декора, а его яркость гуляет от кадра к кадру: на светлых
   кристаллах версия и подсказка пропадали. Контраст должен быть свой. */
.version,.hint{text-shadow:0 1px 3px rgba(0,0,0,.8), 0 0 10px rgba(0,0,0,.5)}
.version{color:var(--ink-dim)}

/* приз — тот же воксель, что на доске (пин P16-1): рисуется рендером, не CSS */
.prizegem{width:150px; height:150px; border:0; border-radius:0; background:none;
  box-shadow:none; display:block}

/* ═══ Очки пары — «жила пещеры» + честное число (UD-011) ═══════════════════
   Полка не подпись, а жёлоб в породе: намытая крошка заполняет его и светится.
   Число остаётся, потому что жила показывает «много/мало», а не «сколько». */
.shelf{position:relative; display:flex; align-items:center; gap:var(--sp-3);
  height:var(--h-row); margin:var(--sp-2) var(--sp-3) 0; padding:0 var(--sp-4);
  flex-shrink:0; overflow:hidden;
  background:linear-gradient(180deg,var(--plate-hi) 0%,var(--plate-lo) 100%);
  border:1px solid var(--stroke); border-radius:var(--r-4);
  box-shadow:var(--rim-top),var(--rim-bottom),var(--lift-1)}
.shelf .vein{position:absolute; left:0; top:0; bottom:0; width:0;
  background:linear-gradient(90deg, rgba(18,214,198,.50), rgba(18,214,198,.12));
  box-shadow:0 0 22px rgba(18,214,198,.40) inset;
  transition:width .5s cubic-bezier(.2,.8,.3,1)}
.shelf .lbl{position:relative; font-size:13px; color:var(--ink-faint); min-width:max-content}
.shelf .val{position:relative; font-size:20px; font-weight:800; color:var(--ink);
  font-variant-numeric:tabular-nums; min-width:max-content;
  transition:transform .12s cubic-bezier(.2,1.6,.4,1)}
.shelf .val.punch{transform:scale(1.24)}
.shelf .mult{position:relative; margin-left:auto; min-width:max-content;
  font-size:13px; font-weight:800; color:var(--crystal-yellow);
  opacity:0; transition:opacity .18s}
.shelf .mult.on{opacity:1}

/* «+N» летит от места взрыва к счётчику — иначе игрок не связывает
   свой ход с результатом (UD-011) */
.gain{position:fixed; z-index:30; pointer-events:none; font-weight:800; font-size:16px;
  text-shadow:0 2px 6px rgba(0,0,0,.75)}

/* ═══ Карта королевства — UD-013 ═══════════════════════════════════════════
   Территория с неровными владениями, а не сетка карточек (PITFALLS P12).
   Приёмы и числа — `app/client/src/realm.rs`. */
.screen#screen-map{align-items:center; gap:var(--sp-3);
  padding:var(--sp-6) var(--sp-3) var(--sp-3)}
.realm-name{flex-shrink:0; margin:0; font-size:22px; font-weight:800;
  letter-spacing:.4px; text-align:center}
.trace{flex-shrink:0; display:flex; align-items:center; gap:var(--sp-3);
  height:var(--h-row); padding:0 var(--sp-4); max-width:100%;
  background:var(--bg-1); border:1px solid var(--stroke); border-radius:var(--r-4)}
.trace .track{position:relative; width:88px; height:var(--sp-2); flex-shrink:0;
  border-radius:var(--r-pill); background:var(--bg-0);
  border:1px solid var(--stroke); overflow:hidden}
.trace .track i{position:absolute; inset:0 auto 0 0; display:block;
  border-radius:var(--r-pill); background:var(--crystal-green)}
.trace .num{font-size:14px; color:var(--ink-dim); white-space:nowrap}
.trace .num b{color:var(--ink)}

.realm{flex:1; min-height:0; width:100%; position:relative;
  border:1px solid var(--stroke); border-radius:var(--r-4); overflow:hidden;
  background:var(--bg-0)}
.realm svg{display:block; width:100%; height:100%}

/* подпись места — картографическая литера поверх местности */
.rlbl{font-family:var(--font); font-size:17px; font-weight:700;
  letter-spacing:.6px; text-anchor:middle; paint-order:stroke;
  stroke:rgba(0,0,0,.8); stroke-width:4px; stroke-linejoin:round}
.rlbl.alive{fill:var(--ink)}
.rlbl.stone{fill:var(--ink-faint)}
.rlbl.next{fill:var(--ink)}

#screen-map .foot{flex-shrink:0; display:flex; flex-direction:column;
  align-items:center; gap:var(--sp-2); max-width:100%}
#screen-map .why{margin:0; font-size:13px; color:var(--ink-dim);
  text-align:center; max-width:34ch}
/* Текст карты живёт ПОВЕРХ декора, а яркость пещеры гуляет от места к месту:
   на светлых кристаллах подпись тонула (тот же класс, что у .version/.hint). */
#screen-map .why,.realm-name,.trace .num{
  text-shadow:0 1px 3px rgba(0,0,0,.8), 0 0 10px rgba(0,0,0,.6)}

@media (min-width:900px){
  .screen#screen-map{padding:var(--sp-8) var(--sp-6) var(--sp-6);
    width:100%; max-width:980px; margin:0 auto}
  .realm-name{font-size:28px}
  .trace .track{width:132px}
}


/* ═══ Угроза окаменения — этап 016 ═══════════════════════════════════════════
   Живёт В шапке цели: отдельная полка стоила бы полю ~56 px высоты (P2).
   Камень серый (не белый и не чёрный): белое тонет в жёлтом и бирюзе. */
.threat{display:flex; align-items:center; gap:var(--sp-2); flex-shrink:0;
  min-width:max-content}
.threat[hidden]{display:none}
.threat .tic{width:16px; height:16px; display:block; color:var(--stone-hi);
  flex-shrink:0}
.threat .ttrack{position:relative; width:52px; height:var(--sp-2); flex-shrink:0;
  border-radius:var(--r-pill); background:var(--bg-0);
  border:1px solid var(--stroke); overflow:hidden}
.threat .ttrack i{position:absolute; inset:0 auto 0 0; display:block;
  border-radius:var(--r-pill); background:var(--stone); transition:width .3s ease-out}
.threat b{font-size:14px; color:var(--ink); white-space:nowrap}
/* близко к порогу — угроза перестаёт быть фоном */
.threat.near .ttrack i{background:var(--crystal-orange)}
.threat.near b{color:var(--crystal-orange)}
/* Опора заперла камень (этап 018): угроза СТОИТ, и это состояние, а не тревога.
   Выражено кантом и цветом ульты — не смещением (правило 7). */
.threat.locked .ttrack{border-color:var(--crystal-yellow);
  box-shadow:0 0 10px color-mix(in srgb, var(--crystal-yellow) 55%, transparent)}
.threat.locked .ttrack i{background:var(--crystal-yellow)}
.threat.locked b{color:var(--crystal-yellow)}
/* Замок держит, но запирать нечего: цвет остаётся правдой, свечение уходит —
   иначе «0/20» читается тревогой на ровном месте (ревью этапа 028). */
.threat.locked.quiet .ttrack{box-shadow:none}
