/* Компоновка: окно делится на две колонки, каждая со своей прокруткой.
   Общая прокрутка страницы здесь мешает — список настроек длинный, и вместе
   с ним уезжает превью, ради которого настройки и крутят. */
@media (min-width: 992px) {
  html, body { height: 100%; }
  body { display: flex; flex-direction: column; overflow: hidden; }
  .workspace { flex: 1 1 auto; min-height: 0; display: flex; }
  /* Без flex-wrap: nowrap строка тянется по содержимому. Перенос оставляет
     высоту строки на усмотрение самого высокого столбца, и min-height: 0 на
     нём уже ничего не решает: картинка в масштабе 400% растягивала страницу
     на три экрана вместо того, чтобы прокручиваться внутри своего окна. */
  .workspace-row { flex: 1 1 auto; min-height: 0; height: 100%; width: 100%;
                   flex-wrap: nowrap; }

  .side { display: flex; flex-direction: column; min-height: 0; }
  .side-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding-right: .4rem; }
  .main { min-height: 0; height: 100%; }
  .stage-card { height: 100%; display: flex; flex-direction: column; overflow: hidden; }
  .stage-wrap { flex: 1 1 auto; min-height: 0; }
  .stage, .viewer { height: 100%; }
  /* сведения не растут вниз без предела: превью важнее подробностей */
  .info-panel { flex: 0 0 auto; max-height: 40vh; overflow-y: auto; }
}

/* На узком экране всё идёт обычным потоком, но кнопки всё равно под рукой. */
.side-actions {
  flex: 0 0 auto;
  position: sticky;
  bottom: 0;
  padding-top: .5rem;
  background: var(--bs-body-bg);
  border-top: 1px solid var(--bs-border-color);
}

.dropzone {
  border: 2px dashed var(--bs-border-color);
  border-radius: .5rem;
}
/* подсветка на всей области превью, а не только на рамке */
.stage.dragover {
  outline: 2px dashed var(--bs-primary);
  outline-offset: -8px;
  background-color: var(--bs-primary-bg-subtle);
}
.stage.dragover .dropzone { border-color: var(--bs-primary); }

.stage {
  display: flex;
  padding: 1rem;
  overflow: auto;
}
/* Минимальная высота — только для узкого экрана, где всё идёт обычным потоком.
   В широкой раскладке высоту задаёт flex, и 60vh оказывались БОЛЬШЕ того, что
   сцене осталось после шапки и панели сведений: сцена наезжала на панель,
   текст рисовался поверх картинки, а кнопка «свернуть» уходила под сцену
   и переставала нажиматься — панель было не закрыть.

   Медиазапрос сам по себе не спасал: он не добавляет специфичности, и правило
   ниже по файлу перебивало его при той же специфичности. Поэтому ограничение
   задаётся именно там, где оно нужно, а не отменяется там, где мешает. */
@media (max-width: 991.98px) {
  .stage, .viewer { min-height: 60vh; }
}
/* Центрирование через auto-поля, а не justify-content: увеличенная картинка
   больше окна, и при центрировании её левый верхний угол уходит за границу
   прокрутки — доскроллить туда уже нельзя. */
.stage > * { margin: auto; }
/* шахматка, иначе прозрачный фон неотличим от белого */
.stage.checker {
  background-image:
    linear-gradient(45deg, var(--bs-tertiary-bg) 25%, transparent 25%),
    linear-gradient(-45deg, var(--bs-tertiary-bg) 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, var(--bs-tertiary-bg) 75%),
    linear-gradient(-45deg, transparent 75%, var(--bs-tertiary-bg) 75%);
  background-size: 20px 20px;
  background-position: 0 0, 0 10px, 10px -10px, -10px 0;
}
.stage.panning { cursor: grabbing; user-select: none; }

/* Вписанная картинка ужимается по окну; увеличенной задаётся ширина в
   пикселях, и тогда её носит прокрутка самой сцены, а не преобразование —
   так работают и полосы прокрутки, и попадание курсором.

   Селектор начинается с img не для красоты: `:not(.fit)` добавляет
   специфичность, и правило перебивало высоту у соседа по классу `preview` —
   трёхмерного просмотрщика. Тот получал `height: auto`, схлопывался до
   собственной высоты холста (150 px по умолчанию) и оставался в ней навсегда:
   дальше three.js читал эти 150 и снова выставлял их холсту. */
img.preview.fit { max-width: 100%; max-height: 100%; width: auto; height: auto; }
img.preview:not(.fit) {
  max-width: none;
  max-height: none;
  height: auto;
  flex: 0 0 auto;
  image-rendering: pixelated;   /* при большом увеличении нужен пиксель, а не мыло */
  cursor: grab;
}

.param-row { margin-bottom: .85rem; }
.param-row .form-label { margin-bottom: .15rem; }
.param-value {
  font-variant-numeric: tabular-nums;
  min-width: 3.4rem;
  text-align: right;
}
.param-note {
  font-size: .72rem;
  line-height: 1.3;
  margin-top: .15rem;
  color: var(--bs-warning-text-emphasis);
}

/* знак вопроса: заметен, когда его ищут, и не спорит с подписью поля */
.help-dot {
  flex: 0 0 auto;
  width: 1.05rem;
  height: 1.05rem;
  padding: 0;
  border: 1px solid var(--bs-border-color);
  border-radius: 50%;
  background: transparent;
  color: var(--bs-secondary-color);
  font-size: .68rem;
  line-height: 1;
  cursor: help;
}
.help-dot:hover, .help-dot:focus {
  border-color: var(--bs-primary);
  color: var(--bs-primary);
}

/* поле, которое сейчас ни на что не влияет */
.param-inactive { opacity: .45; }
.param-inactive .form-range,
.param-inactive .form-select,
.param-inactive .form-control { pointer-events: none; }
.param-inactive .param-note { opacity: 1; }
/* по умолчанию такие поля просто скрыты — их больше десятка */
.hide-inactive .param-inactive { display: none; }

/* регулятор, к которому только что отправили из таблицы деталей */
@keyframes param-flash {
  from { background: var(--bs-warning-bg-subtle); }
  to { background: transparent; }
}
.param-flash {
  animation: param-flash 1.6s ease-out;
  border-radius: .25rem;
}

.param-popover { --bs-popover-max-width: 22rem; }
.param-popover .popover-header { font-size: .8rem; font-family: var(--bs-font-monospace); }
.param-popover .popover-body { font-size: .8rem; line-height: 1.4; }

.nav-sm .nav-link { padding: .25rem .7rem; font-size: .875rem; }

.zoom-value {
  font-variant-numeric: tabular-nums;
  min-width: 4.2rem;
}

.info-toggle { font-size: 1rem; line-height: 1; }
.info-panel.collapsed #info-body { display: none; }
/* краткая строка нужна только вместо свёрнутых подробностей */
.info-panel:not(.collapsed) #info-brief { display: none !important; }

/* --- уголок «свернуть/развернуть» --- */

/* Тот же уголок, что у заголовков групп параметров: путь взят из аккордеона
   Bootstrap, чтобы на странице не было двух разных значков об одном и том же.
   Красится маской по currentColor — тогда цвет совпадает с текстом рядом
   и в светлой теме, и в тёмной, без второй иконки на каждую тему. */
:root {
  --chevron-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e");
}
.chevron {
  display: inline-block;
  width: 1.25rem;
  height: 1.25rem;
  vertical-align: -.3rem;
  background-color: currentColor;
  -webkit-mask: var(--chevron-icon) center / 1.25rem no-repeat;
  mask: var(--chevron-icon) center / 1.25rem no-repeat;
  transition: transform .2s ease-in-out;
}
/* Направление — как в аккордеоне: свёрнуто смотрит вниз («открой меня»),
   развёрнуто повёрнут вверх. Поворотом управляет тот же класс состояния,
   что прячет содержимое, поэтому значок не может разойтись с ним. */
.info-panel:not(.collapsed) #btn-info .chevron,
#palette-card:not(.collapsed) #btn-palette .chevron,
.palette:not(.hide-dupes) #btn-palette-dupes .chevron { transform: rotate(-180deg); }

@media (prefers-reduced-motion: reduce) {
  .chevron { transition: none; }
}

/* И цвет как у заголовка группы: обычный текст, пока свёрнуто, и цвет
   активного заголовка, когда развёрнуто. Синяя стрелка на свёрнутой
   карточке выбивалась из ряда — Bootstrap красит её так только у открытых. */
.palette-toggle, .info-toggle { color: var(--bs-body-color); }
#palette-card:not(.collapsed) #btn-palette,
.info-panel:not(.collapsed) #btn-info { color: var(--bs-primary-text-emphasis); }

/* в строке повторов уголок мельче: он там при мелком тексте */
.palette-dupes .chevron { width: 1rem; height: 1rem; vertical-align: -.2rem; }

/* --- палитра --- */

/* Свёрнутая палитра: остаётся заголовок с числом цветов и замок, список
   уходит. При десятке цветов он занимал пол-экрана, и до настроек ниже
   приходилось долго прокручивать. */
#palette-card.collapsed #palette-list,
#palette-card.collapsed #palette-tools,
#palette-card.collapsed #palette-hint { display: none; }
/* число цветов нужно только вместо свёрнутого списка */
#palette-card:not(.collapsed) #palette-count { display: none; }
.palette-toggle { line-height: 1; text-decoration: none; }

.palette { display: flex; flex-direction: column; gap: .2rem; }

.palette-row {
  display: flex;
  align-items: center;
  gap: .35rem;
  font-size: .78rem;
  line-height: 1.1;
}
/* номер цвета: по нему опознают слой в таблице деталей */
.palette-num {
  flex: 0 0 auto;
  width: 1.3rem;
  text-align: right;
  color: var(--bs-secondary-color);
  font-variant-numeric: tabular-nums;
}
.palette-hex { font-family: var(--bs-font-monospace); }
.palette-share {
  margin-left: auto;
  color: var(--bs-secondary-color);
  font-variant-numeric: tabular-nums;
}
.palette-row .btn { --bs-btn-padding-y: 0; --bs-btn-padding-x: .25rem; font-size: .7rem; }
.palette-row.picking { outline: 2px solid var(--bs-primary); border-radius: .25rem; }

/* --- выбор цвета --- */

/* Квадрат насыщенности и яркости: два наложенных градиента поверх чистого
   тона — ни холста, ни чтения пикселей не нужно. */
.color-plane {
  position: relative;
  height: 9rem;
  border-radius: .25rem;
  cursor: crosshair;
  touch-action: none;
  background-image:
    linear-gradient(to top, #000, rgba(0, 0, 0, 0)),
    linear-gradient(to right, #fff, var(--hue, #f00));
}
.color-thumb {
  position: absolute;
  width: .85rem;
  height: .85rem;
  margin: -.42rem 0 0 -.42rem;
  border: 2px solid #fff;
  border-radius: 50%;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .45);
  pointer-events: none;
}
.color-hue::-webkit-slider-runnable-track {
  height: .55rem;
  border-radius: .3rem;
  background: linear-gradient(to right,
    #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
}
.color-hue::-webkit-slider-thumb { margin-top: -.15rem; }
#color-preview { width: 1.6rem; height: 1.6rem; }

/* образец в строке палитры: он же кнопка, открывающая выбор цвета */
.palette-swatch {
  flex: 0 0 auto;
  width: 1.5rem;
  height: 1.1rem;
  padding: 0;
  border: 1px solid var(--bs-border-color);
  border-radius: 3px;
  cursor: pointer;
}
.palette-swatch:disabled { cursor: default; opacity: .8; }

/* --- замена цвета --- */

.merge-from {
  width: 4.5rem;
  height: 4.5rem;
  border: 1px solid var(--bs-border-color);
  border-radius: .35rem;
}
.merge-arrow { font-size: 1.4rem; color: var(--bs-secondary-color); }
.merge-choices {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr));
  gap: .35rem;
}
.merge-choice {
  display: flex;
  align-items: center;
  gap: .4rem;
  padding: .25rem .4rem;
  border: 1px solid var(--bs-border-color);
  border-radius: .3rem;
  background: transparent;
  font-size: .78rem;
  text-align: left;
}
.merge-choice:hover { border-color: var(--bs-primary); }
.merge-choice.chosen {
  border-color: var(--bs-primary);
  box-shadow: inset 0 0 0 1px var(--bs-primary);
}
.merge-choice .swatch { width: 1.1rem; height: 1.1rem; }
.merge-choice .font-monospace { font-size: .72rem; }

/* повторы цвета: свёрнуты по умолчанию, но из списка не выброшены —
   он же и есть сам параметр */
.palette.hide-dupes .palette-dupe { display: none; }
.palette-dupes {
  align-self: flex-start;
  font-size: .72rem;
  color: var(--bs-secondary-color);
  text-decoration: none;
  margin-top: .15rem;
}
.palette-dupes:hover { color: var(--bs-primary); }
.palette-dupe { opacity: .75; }

/* строка палитры, которая печатается тем же цветом, что и другая */
.palette-same {
  font-size: .68rem;
  color: var(--bs-secondary-color);
  white-space: nowrap;
}

/* режим пипетки: курсор-крест на всём превью */
.stage.picking, .stage.picking img { cursor: crosshair; }

.swatch {
  display: inline-block;
  width: .9rem; height: .9rem;
  border-radius: 2px;
  border: 1px solid var(--bs-border-color);
  vertical-align: -2px;
}

/* условия и приватность: строка мелким шрифтом под подписью сборки */
.legal-links { font-size: .72rem; line-height: 1.5; }
.legal-links a { color: inherit; }

/* сами документы: колонка для чтения, а не во всю ширину монитора */
.legal { max-width: 46rem; }
.legal h2 { margin-top: 1.5rem; }
.legal p, .legal li { line-height: 1.6; }

/* просьба о поддержке: заметна, но не спорит с работой */
.support { border-style: dashed; }
.support .btn { --bs-btn-font-size: .78rem; }

/* подпись с версиями: должна быть заметна, только когда её ищут */
.build {
  font-size: .7rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: .01em;
  opacity: .65;
}
.build b { font-weight: 600; }
.build.stale { color: var(--bs-warning-text-emphasis); opacity: 1; }

.viewer {
  width: 100%;
  height: 100%;
  border-radius: .25rem;
  overflow: hidden;
}
/* Размер холста в разметке задаёт CSS, а не атрибуты width/height: three.js
   пишет в них разрешение отрисовки и стилей не трогает, поэтому холст
   с устаревшими атрибутами вылезал за контейнер. */
.viewer canvas { display: block; width: 100%; height: 100%; }

.report {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: .35rem 1rem;
  font-size: .8rem;
}
.report .verdict-ok { color: var(--bs-success-text-emphasis); font-weight: 600; }
.report .verdict-bad { color: var(--bs-danger-text-emphasis); font-weight: 600; }

/* --- таблица деталей ---
   Класс у таблицы отдельный, не `report`: у той display: grid, и таблица
   с ним перестаёт быть таблицей — строки разъезжаются по ячейкам сетки. */

.parts { font-size: .78rem; margin-bottom: 0; }
.parts td, .parts th { padding: .15rem .5rem .15rem 0; }
.parts th {
  font-weight: 600;
  color: var(--bs-secondary-color);
  border-bottom: 1px solid var(--bs-border-color);
}
/* номер цвета в палитре: по нему деталь опознают быстрее, чем по коду */
.parts .part-number {
  width: 1.6rem;
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--bs-secondary-color);
}
.parts .part-pick { width: 1.4rem; }
.parts .part-height { white-space: nowrap; }
.parts .part-fixed {
  font-size: .72rem;
  vertical-align: baseline;
  font-family: var(--bs-font-monospace);
}
.parts .part-height input {
  width: 5rem;
  font-size: .78rem;
  padding: .05rem .25rem;
  font-variant-numeric: tabular-nums;
}
