/* Стили страницы симулятора.
   ВЫНЕСЕНЫ ИЗ index.html В Ф5, и это не косметика: CSP поддомена объявлена как
   `style-src 'self'` (4.6). Инлайновый <style> под такой директивой БЛОКИРУЕТСЯ
   браузером - страница уехала бы на прод без единого стиля. Альтернатива
   ('unsafe-inline') ослабила бы ровно ту директиву, ради проверяемости которой всё и
   затевалось. Файл участвует в хеше фронтенда и получает хеш в имени при сборке. */
/* Шрифт сцены: тот же, которым рисует планшет. block, а не swap - сцена измеряет текст
   через measureText, и подмена начертания после первой отрисовки сдвинула бы подписи.
   Веб-слой всё равно ждёт готовности шрифта до первого кадра (см. app.js). */
@font-face {
	font-family: 'Roboto';
	font-style: normal;
	font-weight: 400;
	font-display: block;
	src: url(fonts/roboto-cyrillic-400.c9ca589c3a.woff2) format('woff2');
	unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
	font-family: 'Roboto';
	font-style: normal;
	font-weight: 400;
	font-display: block;
	src: url(fonts/roboto-latin-400.10f4e710cb.woff2) format('woff2');
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
		U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
		U+FEFF, U+FFFD;
}

/* Шрифт страницы (дизайн под vlg2.ru). swap: интерфейс переживёт подмену начертания,
   в отличие от сцены. */
@font-face {
	font-family: 'Inter Tight';
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url(fonts/intertight-cyrillic-400.dacf424a6f.woff2) format('woff2');
	unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
	font-family: 'Inter Tight';
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url(fonts/intertight-latin-400.6f32da94d4.woff2) format('woff2');
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
		U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
		U+FEFF, U+FFFD;
}
@font-face {
	font-family: 'Inter Tight';
	font-style: normal;
	font-weight: 600;
	font-display: swap;
	src: url(fonts/intertight-cyrillic-600.4704d4f58a.woff2) format('woff2');
	unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
	font-family: 'Inter Tight';
	font-style: normal;
	font-weight: 600;
	font-display: swap;
	src: url(fonts/intertight-latin-600.db1a039d03.woff2) format('woff2');
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
		U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
		U+FEFF, U+FFFD;
}

/* Палитра vlg2.ru */
:root {
	--accent: #3363b9;
	--accent-dk: #2a538f;
	--deep: #0e2a52;
	--ink: #1c1c1c;
	--tint: #eaf1fd;
	--muted: #7e93b4;
	--line: #d3e0f5;
	--scene-bg: #1a1d22;	/* SimSceneView.COLOR_BG - фон холста до первого кадра */
}

* { box-sizing: border-box; }

html, body { margin: 0; padding: 0; }

body {
	font: 400 16px/1.5 'Inter Tight', system-ui, -apple-system, 'Segoe UI', sans-serif;
	color: var(--ink);
	background: #fff;
	/* Страница живёт в iframe Тильды: горизонтальный скролл body там выглядит поломкой,
	   а высоту вьюпорта считать нельзя - её задаёт рамка, а не устройство. */
	overflow-x: hidden;
	-webkit-text-size-adjust: 100%;
}

.wrap { max-width: 1199px; margin: 0 auto; padding: 14px 16px 24px; }

/* ---- шапка ---- */

/* Шапка ЛИПКАЯ (П18): постоянная кнопка CTA обязана быть видна и тому, кто отвалился
   на середине (сценарий 8). В рамке Тильды высота вьюпорта задаётся рамкой, поэтому
   липкая шапка - единственный способ держать CTA «выше сгиба» независимо от прокрутки. */
.hdr {
	position: sticky; top: 0; z-index: 10;
	display: flex; align-items: center; justify-content: space-between;
	gap: 12px; flex-wrap: wrap;
	margin: 0 0 10px; padding: 8px 0;
	background: #fff; border-bottom: 1px solid var(--line);
}
.brand { font-weight: 600; font-size: 19px; color: var(--deep); letter-spacing: -0.01em; }
.brand span { color: var(--muted); font-weight: 400; }
.hdr-cta { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* Кнопка-ссылка CTA. Ссылка, а не кнопка с обработчиком: правый клик, «открыть в новой
   вкладке» и предпросмотр адреса в статусной строке - это доверие, а на холодном
   трафике оно и есть конверсия. */
.cta {
	display: inline-block; padding: 9px 16px; border-radius: 8px;
	background: var(--accent); color: #fff; text-decoration: none;
	font: 600 15px/1.2 'Inter Tight', system-ui, sans-serif;
	white-space: nowrap;
}
.cta:hover { background: var(--accent-dk); }
.cta.ghost {
	background: #fff; color: var(--accent); border: 1px solid var(--line); font-weight: 400;
}
.cta.ghost:hover { background: var(--tint); }
.cta.big { padding: 13px 22px; font-size: 16px; }

.task {
	margin: 0 0 12px;
	padding: 10px 14px;
	background: var(--tint);
	border-left: 3px solid var(--accent);
	border-radius: 0 8px 8px 0;
	color: var(--deep);
	font-size: 16px;
	transition: opacity 0.4s ease, background 0.4s ease;
}
.task b { font-weight: 600; }
/* Затухающий scaffold: подсказки гаснут по мере того, как человек осваивается.
   Совсем убирать строку нельзя - в ней формулировка задачи. */
.task.faded { opacity: 0.62; }
.task.hint { background: #fff; border-left-color: var(--muted); }

/* Честная плашка (сценарий 12, риск 3). Постоянная, не всплывающая: ожидание «это и
   есть прибор» снимается тем, что оговорка видна всё время, а не один раз. */
.honest {
	margin: 10px 0 0; padding: 8px 12px;
	border: 1px dashed var(--line); border-radius: 8px;
	font-size: 13px; color: var(--muted);
}
.honest b { color: var(--deep); font-weight: 600; }

/* ---- подсказка «показать где» (Т2) ---- */
#reveal-box { margin: 12px 0 0; display: none; }
#reveal-box.on { display: block; }
#reveal-box .why { font-size: 13px; color: var(--muted); margin: 6px 0 0; }

/* ---- экран результата ---- */
/* Не модальное окно поверх всего: карточка встаёт НАД сценой, оставляя прибор видимым.
   Человек должен видеть, ЧТО именно он нашёл, а не читать вердикт на пустом фоне. */
#result {
	display: none;
	margin: 0 0 12px; padding: 16px 18px;
	border: 1px solid var(--accent); border-radius: 10px;
	background: var(--tint);
}
#result.on { display: block; }
#result h2 {
	margin: 0 0 6px; font-size: 20px; font-weight: 600; color: var(--deep);
	letter-spacing: -0.01em; text-transform: none;
}
#result .what { margin: 0 0 14px; font-size: 15px; color: var(--ink); }
#result .acts { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
#result .again {
	background: none; border: 0; padding: 9px 4px; cursor: pointer;
	color: var(--muted); font: 400 14px/1.2 'Inter Tight', system-ui, sans-serif;
	text-decoration: underline;
}
#result .again:hover { color: var(--deep); }

/* ---- переход на этап 2 (Ф4b) ---- */
/* Отдельной строкой ПОД действиями, а не четвёртой кнопкой в ряду: главная кнопка
   результата - CTA, и предложение продолжить не должно с ней соревноваться за взгляд.
   Разделитель сверху отделяет «что дальше» от «что вы нашли». */
#result .next {
	margin: 14px 0 0; padding-top: 12px; border-top: 1px solid var(--line);
	font-size: 14px; color: var(--muted);
}
#result .next button {
	background: none; border: 0; padding: 0 2px; cursor: pointer;
	color: var(--accent); font: 600 14px/1.2 'Inter Tight', system-ui, sans-serif;
	text-decoration: underline;
}
#result .next button:hover { color: var(--accent-dk); }

/* ---- этап 2: кнопка «Настроить» (Ф4b) ---- */
/* Кнопка нажимается всегда - на приборе «80 %» тоже не блокируется, - но заливку
   получает только в максимуме. Это подсказка «сейчас самое время», а не запрет:
   нажатие мимо максимума честно настраивает по текущему сигналу и объясняет, что
   вышло глуховато. */
#tune-box { margin: 12px 0 0; display: none; }
#tune-box.on { display: block; }
#tune-box .btn { max-width: 260px; }
#tune-box .why { font-size: 13px; color: var(--muted); margin: 6px 0 0; }
#tune-box #tune {
	background: #fff; color: var(--accent); border: 1px solid var(--line);
	transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
#tune-box.ready #tune { background: var(--accent); color: #fff; border-color: var(--accent); }
#tune-box.ready #tune:hover { background: var(--accent-dk); }

/* ---- строка согласия (только standalone, Т1) ---- */
#consent {
	display: none;
	position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
	padding: 12px 16px; background: var(--deep); color: #fff;
	font-size: 13px; line-height: 1.45;
}
#consent.on { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
#consent a { color: #cfe0ff; }
#consent .acts { margin-left: auto; display: flex; gap: 8px; }
#consent button {
	border: 0; border-radius: 6px; padding: 8px 14px; cursor: pointer;
	font: 600 13px/1.2 'Inter Tight', system-ui, sans-serif;
}
#consent .yes { background: var(--accent); color: #fff; }
#consent .no { background: rgba(255, 255, 255, 0.16); color: #fff; }

/* ---- раскладка ---- */

.grid {
	display: grid;
	grid-template-columns: minmax(0, 1fr) 264px;
	gap: 16px;
	align-items: start;
}
@media (max-width: 900px) {
	.grid { grid-template-columns: minmax(0, 1fr); }
}

/* Холсты держат пропорцию сами - без единого расчёта от высоты окна:
   в iframe высота вьюпорта не про устройство, и на неё опираться нельзя. */
.canvas-box { position: relative; width: 100%; border-radius: 8px; overflow: hidden; }
.canvas-box canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }

/* Пропорции холста сцены подобраны замером, а не на глаз: сцена вписывает образец
   как min(масштаб по ширине, по высоте) и центрирует остаток. При 900/240 шов
   заполняет 96,6 % ширины и 76,8 % высоты - дальше сплющивать нельзя, образец
   начинает уменьшаться (при 900/210 ширина падает до 84 %). У СО-2 пропорция своя:
   он выше и короче, оптимум 900/360 (96,5 % и 88,2 %). Доли эти от абсолютного
   размера не зависят, поэтому отдельных значений для планшета и телефона не нужно. */
#scene-box {
	aspect-ratio: 900 / 240;
	background: var(--scene-bg);
	/* Обязательно (П15): без этого перетаскивание на мобиле превращается в скролл,
	   и особенно больно это внутри рамки Тильды. */
	touch-action: none;
	cursor: crosshair;		/* тап по образцу переносит ПЭП */
}
#scene-box.on-probe { cursor: grab; }
#scene-box.dragging { cursor: grabbing; }
#scene-box.so2 { aspect-ratio: 900 / 360; }

#ascan-box {
	aspect-ratio: 900 / 300;
	margin-top: 10px;
	border: 1px solid var(--line);
	background: #fff;
}
@media (max-width: 560px) {
	#ascan-box { aspect-ratio: 900 / 380; }	/* на телефоне полоса А-скана иначе слишком низкая */
}

/* ---- панель управления ---- */

.panel { border: 1px solid var(--line); border-radius: 10px; padding: 14px; }
.panel h2 {
	margin: 0 0 12px; font-size: 13px; font-weight: 600; letter-spacing: 0.06em;
	text-transform: uppercase; color: var(--muted);
}
.ctl + .ctl { margin-top: 16px; }
.ctl-head {
	display: flex; justify-content: space-between; align-items: baseline; gap: 8px;
	font-size: 15px; color: var(--deep); margin-bottom: 2px;
}
.ctl-val { font-weight: 600; font-variant-numeric: tabular-nums; }
.ctl-note { font-size: 13px; color: var(--muted); margin-top: 2px; }

input[type=range] {
	width: 100%; margin: 0; height: 28px; accent-color: var(--accent);
	touch-action: pan-y;	/* ползунок тянется пальцем, страница листается вдоль */
}

.btn {
	display: block; width: 100%; min-height: 44px;
	border: 0; border-radius: 8px; cursor: pointer;
	background: var(--accent); color: #fff;
	font: 600 15px/1.2 'Inter Tight', system-ui, sans-serif;
}
.btn:hover { background: var(--accent-dk); }
.btn:active { transform: translateY(1px); }
.btn.secondary { background: #fff; color: var(--accent); border: 1px solid var(--line); }
.btn.secondary:hover { background: var(--tint); }

.sep { height: 1px; background: var(--line); margin: 16px 0; border: 0; }

/* ---- показания и служебное ---- */

.readout {
	margin: 12px 0 0; font-size: 13px; color: var(--muted);
	font-variant-numeric: tabular-nums;
}
/* Хеши ядра - длинные строки без пробелов. На телефоне (замер: 390 px) такая строка не
   переносится, а вылезает за контейнер, и увидеть её край нельзя: горизонтальный скролл
   body съеден overflow-x: hidden выше, поэтому выглядит это как обрезанная вёрстка. */
.foot { margin-top: 14px; font-size: 12px; color: var(--muted); overflow-wrap: anywhere; }
#metrics {
	margin: 10px 0 0; padding: 8px 10px; background: var(--tint); border-radius: 6px;
	font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; white-space: pre;
	overflow-x: auto;
}

/* ---- индикатор загрузки ---- */

#loader {
	position: fixed; inset: 0; z-index: 20;
	display: flex; flex-direction: column; align-items: center; justify-content: center;
	gap: 14px; background: #fff; color: var(--deep);
	transition: opacity 0.25s ease;
}
#loader.gone { opacity: 0; visibility: hidden; pointer-events: none; }
#loader .cap { font-size: 16px; font-weight: 600; }
#loader .sub { font-size: 14px; color: var(--muted); }
#loader .bar {
	width: min(320px, 70vw); height: 4px; background: var(--tint);
	border-radius: 2px; overflow: hidden;
}
#loader .bar i {
	display: block; width: 38%; height: 100%; background: var(--accent); border-radius: 2px;
	animation: slide 1.15s ease-in-out infinite;
}
@keyframes slide {
	0%   { transform: translateX(-105%); }
	100% { transform: translateX(268%); }
}
@media (prefers-reduced-motion: reduce) {
	#loader .bar i { animation: none; width: 100%; opacity: 0.5; }
}

/* ============================================================================
   КАДР ПРИБОРА (Ф8.1). Десктопная раскладка = экран планшета (В1).

   Единицы - CSS px, равные dp прибора 1:1 (Т13). Кадр НЕ масштабируется: при
   transform: scale критерий Ф8.4 «±2 px» стал бы неизмеримым, а ради его
   измеримости и снималась спека Ф8.0. Все позиции проставляет device-frame.js
   из device-layout.js, сгенерированного со спеки живого прибора, - в этом файле
   геометрических констант нет намеренно.
   ============================================================================ */

#device-host {
	display: flex;
	justify-content: center;
}

.dev-frame {
	position: relative;
	background: #f2f2f2;
	color: #111;
	font-family: Roboto, system-ui, sans-serif;
	overflow: hidden;
	flex: 0 0 auto;
}

.dev-top {
	position: absolute;
	left: 0;
	right: 0;
	top: 0;
	background: #ffffff;
	border-bottom: 1px solid #c9c9c9;
}

.dev-top-item {
	position: absolute;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 20px;
	white-space: nowrap;
	background: #f7f7f7;
	border: 1px solid #cfcfcf;
	border-radius: 2px;
	box-sizing: border-box;
}

/* Вторая строка показаний - только в узких раскладках прибора (в широкой все показания
   помещаются в верхнюю полосу, и этого узла в кадре нет вовсе). Своего вида у контейнера
   нет: у прибора это прозрачная полоса, которая лежит ПОВЕРХ холста А-скана и только
   держит показания. Указатель она не ловит намеренно - полоса тянется до правого края
   кадра над органами панели, и невидимый прямоугольник поверх них съедал бы клики. */
.dev-top-row { position: absolute; pointer-events: none; }

.dev-rocker { position: absolute; display: flex; flex-direction: column; }
.dev-rock-btn {
	flex: 1 1 50%;
	font-size: 26px;
	background: #e8e8e8;
	border: 1px solid #cfcfcf;
	border-radius: 4px;
	/* БЕЗ вертикального отступа. Было `margin: 1px 0`, и это единственное, что не
	   проходило геометрический чек-лист Шва 3a: верхняя половина начиналась на 1 px ниже
	   и была на 2 px короче, итог 2,05 px при допуске 2,0. На приборе половины касаются
	   точно - `seekbar_direction_plus` [55,1..342,7] и `_minus` [342,7..598,2] делят
	   контейнер 511,1 dp ровно пополам, без зазора. Отступ был оформительским и стоил
	   ровно того, чтобы шов краснел. */
	margin: 0;
}

.dev-params-slot { position: absolute; overflow: hidden; }
.dev-params { position: relative; height: 100%; }
/* Высота ячейки задаётся ИЗ ДАМПА поштучно в скрипте кадра, а не растягивается на
   строку: у прибора соседние виджеты одной визуальной строки бывают разной высоты
   (спиннер «Шкала» 48,4 dp и его подпись 19,1 dp). Прежние top:0/bottom:0 растягивали
   всё на общую сетку 48 dp и давали до 32,4 px расхождения с прибором. */
.dev-cell {
	position: absolute;
	display: flex;
	align-items: center;
	font-size: 19px;
	box-sizing: border-box;
	padding: 0 6px;
}
.dev-kind-value {
	justify-content: center;
	background: #fff;
	border: 1px solid #c9c9c9;
	border-radius: 2px;
}
.dev-kind-switch, .dev-kind-radio, .dev-kind-spinner {
	background: #ededed;
	border: 1px solid #d5d5d5;
	border-radius: 3px;
}

/* Состояние тумблеров и радиокнопок (Ф8.2). Само СОСТОЯНИЕ приходит из дампа прибора
   через device-layout.js - здесь только то, чем оно рисуется. Маркеры позиционируются
   абсолютно, поэтому текст строки с места не двигается: сместить его значило бы
   поменять уже сверенную геометрию ради состояния. */
.dev-kind-radio { padding-left: 28px; }
.dev-kind-radio::before {
	content: '';
	position: absolute;
	left: 6px;
	top: 50%;
	width: 16px;
	height: 16px;
	margin-top: -8px;
	box-sizing: border-box;
	border: 2px solid #8a8a8a;
	border-radius: 50%;
	padding: 3px;
	background-clip: content-box;
}
.dev-kind-radio.dev-on::before { border-color: #f0a500; background-color: #f0a500; }

.dev-kind-switch::after {			/* дорожка */
	content: '';
	position: absolute;
	right: 6px;
	top: 50%;
	width: 34px;
	height: 14px;
	margin-top: -7px;
	border-radius: 7px;
	background: #bdbdbd;
}
.dev-kind-switch::before {			/* бегунок */
	content: '';
	position: absolute;
	right: 22px;
	top: 50%;
	width: 18px;
	height: 18px;
	margin-top: -9px;
	box-sizing: border-box;
	border: 1px solid #9a9a9a;
	border-radius: 50%;
	background: #fafafa;
	z-index: 1;
}
.dev-kind-switch.dev-on::after { background: #f6cd8b; }
.dev-kind-switch.dev-on::before {
	right: 6px;
	background: #f0a500;
	border-color: #cf8f00;
}

/* Верхняя строка: строб выбран радиокнопкой, состояние из дампа. Оранжевый - СЛУЖЕБНЫЙ
   цвет витрины «активно», он остаётся у всего остального. */
.dev-top-item.dev-on { border-color: #f0a500; box-shadow: inset 0 0 0 2px #f0a500; }

/* ИНДИКАТОР СТРОБА - ЦВЕТ СТРОБА, А НЕ СЛУЖЕБНЫЙ ОРАНЖЕВЫЙ. У прибора рамка вокруг
   номера строба окрашена в цвет самого строба, и для первого строба это чистый красный:
   Velograph_main/app/src/main/java/com/velog/velograph_ii/EhogramProperties.java:164 -
   StrobColor[0].setARGB(255, 255, 0, 0). Замер по эталону планшета сошёлся с исходником
   независимо: рамка вокруг бокса 48x48 dp - ровно (255,0,0), 1260 px, без иных оттенков.
   На приборе цвет НАСТРАИВАЕМЫЙ (PropertiesActivity), веб показывает заводской дефолт -
   витрина рисует прибор в состоянии «из коробки».
   Правило адресное (класс `dev-<id>` кадр вешает на каждый узел шапки), а не общее по
   .dev-on: цвет строба - свойство строба, и красить им всё «активное» значило бы выдать
   служебный признак витрины за приборный.
   Кольцо с зелёной точкой внутри индикатора - ПРИНЯТОЕ расхождение (решение владельца),
   здесь его нет намеренно. */
.dev-gate0.dev-on { border-color: #ff0000; box-shadow: inset 0 0 0 2px #ff0000; }

/* КНОПКА ОГИБАЮЩЕЙ - ИКОНКА ПРИБОРА, А НЕ ПУСТОЙ ПРЯМОУГОЛЬНИК. У прибора это
   ImageButton без собственного текста - картинку ему задаёт атрибут android:src со
   значением @drawable/ogib_off
   (Velograph_main/app/src/main/res/layout-w1100dp/main_activity.xml:20-24), а кадр кладёт
   в узел шапки его текст - у ImageButton он пустой, и на витрине оставалась пустая
   плашка.
   ИСТОЧНИК КАРТИНКИ - РЕАЛЬНЫЙ РЕСУРС ПРИБОРА drawable-xhdpi/ogib_off.png (80x80,
   1049 байт), а не похожая картинка, нарисованная нами: витрина обязана показывать то,
   что видит оператор.
   ПОЧЕМУ ВСТРОЕНА, А НЕ ОТДЕЛЬНЫМ ФАЙЛОМ. Сборка статики забирает из web/ только
   fonts/, shots/ и сами таблицы стилей со скриптами; картинка, положенная рядом,
   в отданную статику не попала бы вовсе, и самопроверка сборки уронила бы её как ссылку
   на несуществующий актив. Встроенная строка данных из проверки исключена явно, лишнего
   запроса на критическом пути не появляется, вес - 1 КБ.
   РАЗМЕР 40x40. Ресурс xhdpi, а xhdpi - это 2 px на dp, то есть 80 px = 40 dp; кадр
   рисует 1 dp = 1 CSS px (Т13). Замер по эталону планшета: белый квадрат иконки внутри
   бокса кнопки ~41 dp с учётом сглаживания краёв - то есть прибор рисует ресурс в его
   собственном размере, не растягивая.
   Своей плашки у кнопки нет: в разметке прибора у ImageButton не задан background, и на
   эталоне вокруг иконки виден фон полосы, а не кнопка. Приглушённость (В5 - «в витрине
   не работает») остаётся: она приходит классом dev-grey, как у остальных органов шапки. */
.dev-imageButtonOgib {
	background-color: transparent;
	border-color: transparent;
	background-repeat: no-repeat;
	background-position: center;
	background-size: 40px 40px;
	background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4AcVCw4N4xB0TQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAADfUlEQVR4Xu2cvW7TUBTH/05ap5TGaUsrtQyUiRcoAw+AYGFm4AUQSDwDEk+BeAXEyACsMAYGGDuB1G60SUrJR4sZ7j3VreOP5h7btXPPT4qum56o8k//43vtuAYEFh4AhGF4M6tQmMbzvP0F4+fdxEohji4AmAIBkXhZurQRFQiIxCy6MIgTCIjEJC7IA5IFAsB2yu9c5CDuzUbcm8LlEYFMRCCTWgl89RJ48hj48T2rsjxqJfDTR+DdW2BvL6uyPGolcDxW46CfXlcmtRTYF4F2TLTAXi+9rkxqJXA0UuPxIL2uTGolUFqYyVhamIfMwkykhRmEoQhkMZkACNV2X46Bs0PpAySBVohAJqbAs1Pg5CS5tkxqKRCoTgprI5BO44iqLKZrI3ASSWBVFtO1ESgtzMRpgc+fAvfuAl8+Z1UmExXoVAt/7QLfusCvn1mVyTg9idCp1+Fhel0aTrcw7WyeAp1qYRJ4xBAYXcY4k8DxGBgN1fbRUXptGpTARlONzgg0d/Twd3JdFjSJrK+r0ZkWNmfLPI6Bm5tqdGYWNpOSh8ANLdDJFuZMIucCN9QoLTwjJPCGFuhMAs2k/DnW321YMIkksN9XXzRdNYULjCbFdilDCby+ArSWqnNVunCB0dnSto1JoO8DnY7arkIbFy4weiNQj5lA3weCQG07ITCvBNJC2veBthZYhZm4cIEDnUBP/yXbpQwlsNVyLIF0KWtrS422CYw7BlbhbKR4gTolt3bUaCuQljGLrrUwpWTnthq5LezcJEIp2WEm0JxESKCTLZxHAqmFq3CvdOkCbRPo5Cw8HALjEeC3gG39z7NcgU4dA2kHgwBYXVPbti184RhIp3Lzfgw0Ba5pgbYJNJcx7iRQJ6QdAO020FxQZyanp+mfi8PpFu50AM/TZxCh3fLDnEScWUibLQzw2riqp3JpD51gY7YwwJtIzElkeVlt01Vpz0v+XNGUkkBKTF4JXFwElq4B/86u/qr0VAJfPMOdN6/zfeRJu63GzqoaHz1Mrs3C99UYBMDwL7C+kl6fF+8/wL//YPr9QhNIUAvTXQW2NJpAU9/aQcfVq8Z8ettu5JVrCueAA6gnF52/PM/bLyWB84wIZCICmYhAJiKQiQhkIgKZiEAmIpCJCGSSdjkr9pmhwkWSBE49rVaIJ06gyJuBqECRNyOmQJEnlM9/ceR0t/QMJIwAAAAASUVORK5CYII=);
}

/* БЕЙДЖ РЕЖИМА ОГИБАЮЩЕЙ («З+П») - МЕЛКАЯ ПОДПИСЬ, А НЕ ЗАГОЛОВОК. Кегль верхней полосы
   (20 px) не помещался в её бокс высотой 11,5 dp и вылезал за него. Вид взят из разметки
   прибора, а не подобран на глаз:
   Velograph_main/app/src/main/res/layout-w1100dp/main_activity.xml:26-36 -
   textSize="9sp", textStyle="bold", textColor="#FF000000", background="#A0FFFFFF",
   paddingLeft/Right="2dp". Что подпись в бокс поместится, показывает сам прибор: бокс
   22,7x11,5 dp из дампа - это wrap_content, то есть измеренный прибором размер ЭТОГО
   текста ЭТИМ кеглем с этими полями.
   Рамки у бейджа нет: у прибора фон - сплошной полупрозрачный белый, без обводки. */
.dev-textOgibModeBadge {
	font-size: 9px;
	font-weight: 700;
	line-height: 1;
	color: #000;
	background: rgba(255, 255, 255, .627);
	border: 0;
	border-radius: 0;
	padding: 0 2px;
}

/* Живое поле параметра (Ф8.2): кликабельно ТОЛЬКО оно, подпись строки - нет. */
.dev-cell.dev-live { cursor: pointer; }

/* Подсветка активного поля. Это НЕ рамка самого поля: на приборе её рисует системный
   9-patch, который занимает не весь бокс строки. Поэтому собственные рамка и фон поля
   на время подсветки убираются, а рисует .dev-hl - его геометрия и цвета приходят
   из device-params.js, здесь их нет намеренно (то же правило, что и для раскладки).
   Обнулённая рамка обязательна: иначе .dev-hl считал бы отступы от padding-box, то
   есть на 1 px внутрь, а замер прибора даёт по бокам ровно 0.
   Фокус-кольцо браузера снимается потому, что подсветка И ЕСТЬ индикатор фокуса -
   она появляется ровно на focus, как на приборе. */
.dev-cell.dev-live:focus { outline: none; }
.dev-cell.dev-active {
	background: transparent;
	border-width: 0;
	isolation: isolate;
}
/* z-index -1 внутри изолированного поля: маркер рисуется НАД фоном поля и ПОД его
   текстом. Без isolation он ушёл бы под фон всего кадра. */
.dev-hl {
	position: absolute;
	z-index: -1;
	box-sizing: border-box;
}

/* Единый стиль «нерабочего» (В5). Усечённость обязана читаться ДО нажатия,
   поэтому это не disabled-атрибут, а видимая приглушённость плюс подсказка. */
/* Нижние кнопки левой колонки прибора - «80 %» и стоп-кадр. Геометрия приходит из
   генерата раскладки (снята дампом с прибора), здесь только вид приборной кнопки: тот же
   светлый градиент и рамка, что у остальных кнопок кадра. */
.dev-corner-btn {
	position: absolute;
	box-sizing: border-box;
	padding: 0;
	font: inherit;
	font-size: 17px;
	line-height: 1;
	color: #333;
	background: linear-gradient(#fdfdfd, #e6e6e6);
	border: 1px solid #b6b6b6;
	border-radius: 3px;
	z-index: 2;
}

.dev-grey { opacity: .55; cursor: help; }
.dev-mode-opt.dev-grey { cursor: pointer; }

/* ПРОЗРАЧНАЯ, и это не вкус. Нижняя панель прибора (`bottom_tab`, y 566,2..614,2 в кадре)
   перекрывает низ зоны А-скана (50,2..614,2) на 48,0 dp - так у прибора, замер по дампу.
   Но у прибора это контейнер БЕЗ фона, и сквозь него видно то, что нарисовал плоттер:
   нижнюю границу поля развёртки и всю шкалу мкс с подписями «0 12 24 … 84» и «мкс».
   Здесь стоял `background:#ffffff` - непрозрачная заливка закрашивала эти 48 dp, и в вебе
   шкалы мкс не было вовсе. Нашла это сверка признаков Шва 3a: `plot_bottom` 581,78 dp у
   планшета против 539,50 у веба (расхождение 42,28 при допуске 2,0) и «чернил ниже поля»
   6,09 % против 0,00 %. Глазом дефект не читался - выглядело как просто короткий А-скан. */
.dev-modebar {
	position: absolute;
	background: transparent;
	display: flex;
	align-items: center;
}
.dev-modebar-top { background: transparent; }

.dev-mode { position: relative; }
.dev-mode-btn {
	width: 100%;
	height: 100%;
	text-align: left;
	padding: 0 12px;
	font-size: 19px;
	background: #e9e9e9;
	border: 1px solid #c2c2c2;
	border-radius: 3px;
}
.dev-mode-list {
	position: absolute;
	bottom: 100%;
	left: 0;
	min-width: 100%;
	background: #fff;
	border: 1px solid #b5b5b5;
	box-shadow: 0 6px 20px rgba(0,0,0,.22);
	z-index: 30;
	max-height: 520px;
	overflow: auto;
}
.dev-modebar-top .dev-mode-list { bottom: auto; top: 100%; }
.dev-mode-opt {
	display: block;
	width: 100%;
	text-align: left;
	padding: 12px 16px;
	font-size: 19px;
	background: #fff;
	border: 0;
	border-bottom: 1px solid #eee;
}

.dev-ascan-slot, .dev-panel-slot { position: absolute; }

/* Живые боксы вида Ф3 переезжают в приборные слоты (модуль монтирования кадра) и ОБЯЗАНЫ
   слот заполнять: размер слота - это геометрия прибора, снятая машинно с планшета (Ф8.0), а
   не оформительское решение. Без этих строк сайтовые `aspect-ratio` и отступы побеждают,
   и замер до правки был такой: А-скан 404,4x134,8 в слоте 404,4x564,0 (24 % высоты),
   сцена 429,4x114,5 в слоте 429,4x270,2 (42 %). «Один в один» при этом неисполнимо по
   построению, а сверка Ф8.4 красная не из-за отрисовки, а из-за вёрстки.
   Рамку и скругление тоже снимаем: на приборе их нет - рамку поля развёртки рисует сам
   плоттер (frame_width), а сверка идёт по пикселям. */
.dev-ascan-slot > #ascan-box,
.dev-scene-slot > #scene-box {
	aspect-ratio: auto;
	height: 100%;
	margin: 0;
	border: 0;
	border-radius: 0;
}

/* Бейдж «СИМУЛЯТОР» - тот же, что на приборе, а не «похожий». Каждое значение снято
   замером с эталона планшета artifacts/f8-w1100-05-gates.png и совпало с тем, чем бейдж
   рисует прибор:
     - заливка: у прибора #e65100 при непрозрачности 0,8; над белым верхом холста это даёт
       ровно замеренные (235,116,51) - именно этот цвет и лежит на эталоне;
     - текст белый (255,255,255), высота прописных 20 физических px при 2,25 px на dp =
       8,89 dp, что отвечает кеглю 12 и жирному начертанию;
     - скругление 6 dp, боковые поля 10 dp (замер: 10,22 слева и 10,67 справа), разрядка
       0,08 em - при ней ширина набора выходит 84,0 dp внутри бокса 104,9 dp.
   Геометрия бокса приходит из генерата раскладки, снятого с прибора, - здесь её нет
   намеренно, как и у всего остального в кадре.

   Разрядка добавляется и ПОСЛЕ последней буквы, поэтому при центрировании строка уезжает
   влево на половину разряда; padding-left ровно в один разряд это компенсирует.

   Жирное начертание браузер синтезирует из обычного Roboto: отдельного жирного файла в
   кадре нет намеренно - это лишний актив на критическом пути ради девяти букв.

   pointer-events: none - не аккуратность, а требование: под бейджем лежит холст А-скана,
   по которому посетитель таскает строб, и перехваченный указатель означал бы мёртвую зону
   105x22 dp в правом верхнем углу графика.

   z-index - тоже не украшение: живой бокс А-скана переезжает в слот ПОЗЖЕ бейджа, а
   позиционированные элементы с автоматическим слоем рисуются в порядке разметки, и без
   явного слоя бейдж оказался бы под холстом и не был бы виден вовсе. */
.dev-sim-badge {
	position: absolute;
	z-index: 2;
	box-sizing: border-box;
	display: flex;
	align-items: center;
	justify-content: center;
	padding-left: 0.08em;
	white-space: nowrap;
	font-size: 12px;
	font-weight: 700;
	line-height: 1;
	letter-spacing: 0.08em;
	color: #ffffff;
	background: rgba(230, 81, 0, .8);
	border-radius: 6px;
	pointer-events: none;
}

/* Сайтовая панель управления переезжает в приборный слот НЕ для показа, а чтобы её живые
   контролы не пересоздавались второй разметкой (модуль монтирования кадра). Показывать её
   там нельзя: её место занял приборный `.dev-panel`, а сама она торчала 433,7x219 НИЖЕ
   кадра - второй, сайтовый слайдер усиления под приборным экраном. Элементы остаются в
   DOM, и кадровый цикл по-прежнему держит их по id.
   ВНИМАНИЕ: имя js-файла в комментарии CSS писать нельзя - задача `site` проверяет ссылки
   на активы наивным грепом по именам и роняет сборку. Проверено на своей шкуре здесь. */
.dev-panel-slot > #panel { display: none; }

/* Снимок неживого раздела (Ч3). Обязан читаться как продолжение интерфейса, а не
   как вставленная картинка: без рамок и теней, в тот же кадр панели. Инородный
   вид - дефект приёмки Ф8.4, а не мелочь. */
.dev-shot { position: relative; height: 100%; overflow: hidden; }
/* СНИМОК ПРИВОДИТСЯ К dp ПРИБОРА, а не кладётся в натуральных пикселях. Найдено
   2026-07-31 замером живой страницы: было `max-width: none` без ширины, и кроп
   510x1150 px (это 226,7x511,1 dp при плотности планшета 2,25) рисовался в слот
   шириной 226,7 dp как есть - то есть в 2,25 раза крупнее и с обрезкой по
   `overflow: hidden`. Посетитель видел огромные подписи «Усиление, дБ» и «C звука,
   м/с» БЕЗ САМИХ ЗНАЧЕНИЙ: колонка значений уезжала за правый край. У модального
   снимка («Прочее…») масштабирование было с самого начала (`.dev-modal img`), а у
   четырёх снимков колонки - нет.
   ПОЧЕМУ 100%, А НЕ КОЭФФИЦИЕНТ 2,25: слот и кроп - одна и та же область прибора,
   поэтому равенство проверяемо по данным, а не подбирается. Замер: кроп 510x1150 px
   / 2,25 = 226,7x511,1 dp, слот `params` раскладки w1100 = [226.7, 511.1] dp.
   Своего числа плотности здесь нет намеренно - оно бы разъехалось с генератом. */
.dev-shot img {
	position: absolute;
	left: 0;
	top: 0;
	width: 100%;
	height: 100%;
}
.dev-shot-cap {
	position: absolute;
	right: 4px;
	bottom: 4px;
	font-size: 14px;
	padding: 2px 8px;
	border-radius: 3px;
	background: rgba(0,0,0,.55);
	color: #fff;
}

/* Правая панель образца (В4) - геометрия прибора, содержимое из спеки. */
.dev-panel { position: relative; height: 100%; }
.dev-pitem {
	position: absolute;
	display: flex;
	align-items: center;
	font-size: 18px;
	box-sizing: border-box;
	padding: 0 8px;
	color: #eaeaea;
	white-space: nowrap;
	overflow: hidden;
}
.dev-panel-slot { background: #1c1c1c; }

/* Подпись панели прибора («Образец:», «ПЭП:», «Частота:», «Ракурс:»). У прибора это
   TextView без ресурсного id, поэтому в вебе их не было вовсе, и спиннеры висели с
   пустотой слева. Геометрия - из дампа поштучно (её ставит скрипт кадра), здесь только
   вид. Это НЕ орган: серым (язык витрины В5 - «не работает») подпись быть не должна,
   поэтому цвет тот же, что у остального текста панели.
   Размер шрифта - единственное, что берётся не из дампа, и он не выдуман: у прибора бокс
   подписи 17,4 dp против 21,8 dp у текста спиннера, который веб рисует 18 px, то есть
   17,4/21,8 x 18 = 14. Гарантируется этим только БОКС (он из дампа поштучно); ширина
   самих букв - дело шрифта, и мерить её надо в браузере, а не здесь.
   Указатель не ловит: подпись стоит вплотную к органам (4 dp зазора до спиннера), и
   невидимый прямоугольник поверх них съедал бы клики - та же причина, что у .dev-top-row. */
.dev-plabel {
	position: absolute;
	box-sizing: border-box;
	display: flex;
	align-items: center;
	font-size: 14px;
	line-height: 1;
	color: #eaeaea;
	white-space: nowrap;
	pointer-events: none;
}

.dev-p-spinner, .dev-p-button {
	/* Толщина рамки объявлена переменной, потому что на неё опирается .dev-ptext: у
	   абсолютно позиционированного потомка начало координат - padding-box родителя, то
	   есть его border-box, сдвинутый на эту самую рамку. Второе «1px», вписанное в calc
	   руками, разъехалось бы с этим при первой же правке вида. */
	--dev-p-border: 1px;
	background: #d8d8d8;
	color: #111;
	border: var(--dev-p-border) solid #b6b6b6;
	border-radius: 3px;
}
/* Кнопка подпись ЦЕНТРИРУЕТ - так она стоит и на приборе. У спиннера центрирования нет:
   прибор прижимает текст влево, и раньше общее правило `justify-content: center` било
   именно по спиннерам - «Шов», «Наклонный 65°» и «2,5 МГц» стояли посередине бокса
   вместо левого края. */
.dev-p-button { justify-content: center; }

/* Текст ВНУТРИ органа панели - отдельный элемент, потому что у прибора он отдельный узел
   (`android:id/text1`) со своим боксом, а не «текст спиннера». Позиция и размер приходят
   из дампа переменными --dev-ptext-*, здесь их значений нет: любое число, вписанное сюда,
   стало бы вторым источником геометрии и разошлось бы с прибором молча.
   Вычитание рамки - не подгонка, а перевод систем координат (см. --dev-p-border выше):
   дамп даёт смещение от border-box органа, CSS отсчитывает от его padding-box.

   ВЫРАВНИВАНИЕ ВЛЕВО - ОТДЕЛЬНОЕ ТРЕБОВАНИЕ, А НЕ СЛЕДСТВИЕ БОКСА. Бокс text1 внутри
   спиннера почти симметричен (139,1 в 161,3 - 10,7 dp слева и 11,5 справа), поэтому
   поставить спан в этот бокс мало: оставь текст внутри спана центрованным - и бокс
   сойдётся, а видимые буквы останутся не там. У прибора чернила всех трёх подписей
   начинаются на 12,0..12,3 dp от левого края спиннера, то есть текст прижат к левому
   краю своего бокса. Отсюда явные flex-start и text-align: полагаться на значения по
   умолчанию нельзя - text-align наследуется, и любая правка выше по дереву развернула бы
   подпись обратно.
   Обрезки здесь нет намеренно: бокс подписи по высоте почти равен строке (21,8 dp при
   кегле 18 px), и overflow:hidden срезал бы хвосты букв. Всё, что не влезло, всё равно
   обрежет .dev-pitem - у него запас больше. */
.dev-ptext {
	position: absolute;
	left: calc(var(--dev-ptext-x) - var(--dev-p-border, 0px));
	top: calc(var(--dev-ptext-y) - var(--dev-p-border, 0px));
	box-sizing: border-box;
	display: flex;
	align-items: center;
	justify-content: flex-start;
	text-align: left;
	white-space: nowrap;
}
.dev-p-seekbar { background: #f0a500; border-radius: 14px; }
.dev-p-radiobutton, .dev-p-switch { color: #eaeaea; }
/* Состояние органов правой панели - из того же дампа. В эталоне отмечены «Сбоку»,
   «Луч и связка с пиком» и «Ближняя зона»; без отрисовки состояния вид расходился бы
   с прибором при совпавшей до пикселя геометрии. */
.dev-p-radiobutton { padding-left: 24px; }
.dev-p-radiobutton::before {
	content: '';
	position: absolute;
	left: 4px;
	top: 50%;
	width: 14px;
	height: 14px;
	margin-top: -7px;
	box-sizing: border-box;
	border: 2px solid #9a9a9a;
	border-radius: 50%;
	padding: 2px;
	background-clip: content-box;
}
.dev-p-radiobutton.dev-on::before { border-color: #f0a500; background-color: #f0a500; }
.dev-p-switch::after {
	content: '';
	position: absolute;
	right: 4px;
	top: 50%;
	width: 26px;
	height: 10px;
	margin-top: -5px;
	border-radius: 5px;
	background: #6a6a6a;
}
.dev-p-switch::before {
	content: '';
	position: absolute;
	right: 16px;
	top: 50%;
	width: 12px;
	height: 12px;
	margin-top: -6px;
	border-radius: 50%;
	background: #cfcfcf;
	z-index: 1;
}
.dev-p-switch.dev-on::after { background: #8a6300; }
.dev-p-switch.dev-on::before { right: 4px; background: #f0a500; }
.dev-scene-slot { position: absolute; overflow: hidden; }

/* Усыновлённый живой контрол: приборная рамка, но внутри работающая кнопка Ф3. */
.dev-adopted { padding: 0; }
.dev-adopted > .btn { width: 100%; height: 100%; margin: 0; border-radius: 3px; }

/* Кнопка показа/скрытия подсказки сценария - геометрия из эталона прибора
   (button_toggle_scen_info). Вид приборной кнопки: тот же светлый градиентный фон, что у
   остальных кнопок кадра. */
.dev-scen-toggle {
	position: absolute;
	box-sizing: border-box;
	padding: 0;
	font: inherit;
	font-size: 20px;
	line-height: 1;
	color: #333;
	background: linear-gradient(#fdfdfd, #e6e6e6);
	border: 1px solid #b6b6b6;
	border-radius: 3px;
	cursor: pointer;
	z-index: 6;
}
.dev-scen-toggle:hover { background: linear-gradient(#ffffff, #ededed); }
.dev-scen-toggle:active { background: linear-gradient(#e2e2e2, #f2f2f2); }

/* Приборный слой сценария (В6) - место задачи, как на приборе. */
.dev-scen {
	position: absolute;
	box-sizing: border-box;
	padding: 10px 12px;
	font-size: 17px;
	line-height: 1.35;
	background: #fffdf3;
	border: 1px solid #d9d2b0;
	border-radius: 3px;
	overflow: auto;
	z-index: 5;
}

/* Модалка поверх кадра - для пунктов, которые и на приборе модалки (В11). */
.dev-modal-back {
	position: absolute;
	inset: 0;
	background: rgba(0,0,0,.45);
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 40;
}
.dev-modal {
	position: relative;
	background: #fff;
	border-radius: 4px;
	padding: 12px;
	max-width: 92%;
	max-height: 92%;
	overflow: auto;
	text-align: center;
}
.dev-modal img { max-width: 100%; height: auto; display: block; }
.dev-modal-title { font-size: 20px; font-weight: 600; margin-bottom: 8px; text-align: left; }
.dev-modal-note { padding: 24px; color: #555; }
.dev-modal-close { margin-top: 10px; padding: 8px 20px; font-size: 16px; }

/* Липкая полоса вне кадра прибора (П18): CTA и честный текст усечённости. */
.dev-ctabar {
	position: sticky;
	bottom: 0;
	display: flex;
	align-items: center;
	gap: 16px;
	justify-content: space-between;
	padding: 10px 16px;
	background: #12213c;
	color: #e8eefb;
	font-size: 15px;
	z-index: 20;
}
.dev-ctabar-note { flex: 1 1 auto; min-width: 0; }

/* УЗКИЙ ЭКРАН: полоса становится столбиком. Замер живого прода 2026-08-17 при 390 px:
   полоса 358x290 - треть экрана телефона, - а текст внутри сжат до 92 px ширины при
   270 px высоты, то есть сыплется по одному слову в строку. Причина не в тексте: кнопка
   «Подобрать комплектацию» шириной 218 px не ужимается (её содержимое неразрывно), и в
   ряду ей достаётся всё, а тексту остаток.
   ПОЧЕМУ 560, А НЕ «на глаз»: кнопка занимает 218, читаемой строке нужно около 260
   (примерно 40 знаков при 15 px), плюс зазор 16 и поля 32 - ряд перестаёт быть читаемым
   уже около 526 px. 560 берём с запасом.
   Порог СВОЙ, а не 930 из breakpoints.json: тот про то, показывать ли кадр прибора
   (решение В7), и к вёрстке полосы отношения не имеет - связать их значило бы объяснять
   потом, почему полоса ломается там же, где прячется прибор. */
@media (max-width: 560px) {
	.dev-ctabar {
		flex-direction: column;
		align-items: stretch;
		gap: 10px;
	}
	/* Кнопка во всю ширину - на телефоне это ещё и нормальная цель для пальца. */
	.dev-ctabar .cta { text-align: center; }
}
#device-host { flex-direction: column; align-items: center; }
