/* qa-tools — общие компоненты
   Разделяется всеми страницами: сброс, фон-небо, лоудер/бутскрин,
   шапка, переключатель темы. Специфика каталога и специфика страницы
   инструмента — в catalog.css и tool.css соответственно. */

/* ==========================================================================
   4. БАЗА
   ========================================================================== */
*{box-sizing:border-box}

/* Атрибут hidden должен скрывать — но браузерное правило [hidden]{display:none}
   слабее любого нашего display, и .pane{display:flex} его перебивал. Из-за
   этого «скрытые» панели («Сводка», «Поиск по пути», «Стандартные поля»)
   и кнопка «Открыть в JSON» на самом деле показывались — пустыми. Ставим
   правило явно и с !important, иначе следующий компонент со своим display
   сломает то же самое заново. */
[hidden]{display:none!important}

/* ---- смена темы одним движением -------------------------------------
   Класс висит на <html> только те 340ms, пока идёт перекраска (ставит
   и снимает initTheme). Иначе часть компонентов — плашки, поля, блоки
   кода, заголовки — перекрашивалась мгновенно, а фон и карточки уплывали
   плавно, и переключение выглядело рваным.
   !important осознан: на время перекраски общий тайминг важнее
   локальных hover-переходов, у которых свои короткие длительности.
   Заодно после снятия класса на элементах не остаётся ни одного
   лишнего transition — переключение больше не «моргает» вдогонку.

   color/fill/stroke намеренно НЕ анимируются, и это не упущение.
   color — наследуемое свойство: когда его анимирует предок, у потомка
   переход перезапускается каждый кадр и не сдвигается с нуля. У нас
   вложенность .workbench > .stack > #verdict > .verdict, поэтому цвет
   текста в плашках полз ~15% за 700ms, а в конце прыгал к финальному
   значению — это и было мигание. Проверено: тот же элемент анимирует
   background-color нормально, а color стоит, пока предки не перестанут
   его анимировать. Поэтому цвет текста меняется сразу и весь разом
   (один кадр, без рассинхрона), а поверхности плавно уезжают. */
html.theme-anim,html.theme-anim *,
html.theme-anim *::before,html.theme-anim *::after{
  transition:background-color var(--dur-theme) var(--ease-theme),
             border-color var(--dur-theme) var(--ease-theme),
             box-shadow var(--dur-theme) var(--ease-theme),
             opacity var(--dur-theme) var(--ease-theme) !important}
/* Частицы из-под общего правила выведены: у шести элементов на них
   висит около 1200 теней, и цвет каждой завязан на тему. Анимировать
   такой box-shadow незачем — слой целиком уезжает по opacity, — а
   перерисовывать тысячу теней каждый кадр дорого. */
body{margin:0;background:var(--surface-app);color:var(--text-body);
  /* --text-body-sm, а не --text-body: второй токен в блоке темы
     переопределён как ЦВЕТ, и font: с ним невалиден целиком — body
     молча жил на шрифте браузера (Times), спасали только компоненты со
     своим font:. Поймано в design-bold, 2026-09-06. */
  font:var(--fw-regular) var(--text-body-sm)/1.45 var(--font-ui);
  -webkit-font-smoothing:antialiased;
  /* колонка на всю высоту окна: подвал прижат вниз и не прыгает
     при смене категории, когда карточек мало */
  display:flex;flex-direction:column;min-height:100vh;min-height:100dvh;
  transition:background-color var(--dur-theme) var(--ease-theme),
             color var(--dur-theme) var(--ease-theme)}

/* ---- фон: небо / пылинки --------------------------------------------
   Слой прибит к вьюпорту и замаскирован сверху вниз: частицы живут
   в верхней трети, где пусто, и полностью исчезают к плотной сетке. */
.sky{position:fixed;inset:0;z-index:-1;overflow:hidden;pointer-events:none;
  background:var(--sky-dark)}
/* design-bold: один источник света сверху. Не «космос» с частицами и не
   синева по всему экрану — узкое пятно у верхней кромки, которое даёт
   холсту глубину и объясняет, откуда на панелях светлый верх. Один цвет
   (акцент), одна форма, неподвижно. */
.sky::before{content:"";position:absolute;inset:0;z-index:1;
  background:radial-gradient(900px 340px at 50% -120px,rgba(53,184,255,.13),transparent 70%)}
[data-theme="light"] .sky::before{
  background:radial-gradient(900px 340px at 50% -120px,rgba(15,127,209,.08),transparent 70%)}
/* Светлый фон лежит отдельным слоем поверх тёмного и проявляется по
   opacity. Так надо потому, что градиенты друг в друга не
   интерполируются: одним свойством background фон менялся бы скачком,
   пока всё остальное плавно едет, — это и был главный источник
   ощущения, что тема переключается частями. */
.sky::after{content:"";position:absolute;inset:0;z-index:0;
  background:var(--sky-light);opacity:0;
  transition:opacity var(--dur-theme) var(--ease-theme)}
[data-theme="light"] .sky::after{opacity:1}
button{font-family:inherit}
::selection{background:var(--accent-quiet);color:var(--text-heading)}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-thumb{background:var(--border-subtle);border-radius:var(--radius-pill);
  border:2px solid transparent;background-clip:padding-box}
::-webkit-scrollbar-track{background:transparent}
/* Курсор над полосой прокрутки — стрелка, а не I-образный курсор набора.
   У textarea стоит cursor:text, и раз полоса стилизована через
   ::-webkit-scrollbar, она наследует курсор поля: наводишь на полосу —
   видишь курсор текста и не понимаешь, можно ли тянуть. Замечание
   владельца (2026-09-06, «везде в полях»). До этого чинилось точечно
   в css/tools/users.css для .editor--rows — теперь для всех полос. */
::-webkit-scrollbar,::-webkit-scrollbar-thumb,
::-webkit-scrollbar-track,::-webkit-scrollbar-corner{cursor:default}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:var(--radius-sm)}

/* ---- движение между страницами ------------------------------------
   Переход между страницами сайта — короткое перекрёстное затухание
   средствами браузера (View Transitions, cross-document). Ноль JS и
   ноль разметки: браузеры без поддержки просто открывают страницу как
   раньше. Шапка закреплена отдельно, чтобы при переходе она стояла на
   месте, а менялось только содержимое под ней — так переход читается
   как «сменился инструмент», а не «перезагрузился сайт». */
@view-transition{navigation:auto}
::view-transition-old(root),::view-transition-new(root){animation-duration:var(--dur-base)}
.topbar{view-transition-name:topbar}
::view-transition-old(topbar),::view-transition-new(topbar){animation:none}

/* Появление страницы: шапка инструмента и рабочие панели поднимаются на
   несколько пикселей и проявляются — один кадр, одна длительность, все
   вместе (планка: не больше одного движения одновременно — это одно). */
@keyframes pageIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
/* Без fill-mode:both нарочно. С ним элемент держит opacity:0 до старта
   анимации, а в фоновой вкладке анимации не идут — страница, открытая
   через Ctrl+клик или в скрытой панели, стояла бы пустой, пока вкладку
   не покажут. Поймано скриншотом /status в скрытой панели браузера:
   DOM на месте, workbench с opacity 0. */
/* И второй пояс: анимация включается классом is-shown, который common.js
   ставит, только когда вкладка видима (visibilityState). Даже без both
   в скрытой вкладке анимация замирает на первом кадре (opacity 0) и
   догоняет время лишь при показе; в скрытой панели браузера страница
   оставалась пустой. Нет класса — нет анимации, содержимое видно. */
html.is-shown .toolhead,html.is-shown .workbench,
html.is-shown .searchzone,html.is-shown .layout{
  animation:pageIn var(--dur-slow) var(--ease-emphasis)}

/* ==========================================================================
   5. ЛОУДЕР — пять квадратов, перекрашены в акцент
   ========================================================================== */
.loader{
  --square:22px;--offset:26px;--duration:2.4s;--delay:.2s;
  --timing:ease-in-out;--in-dur:.4s;--in-delay:.1s;--in-timing:ease-out;
  width:calc(3 * var(--offset) + var(--square));
  height:calc(2 * var(--offset) + var(--square));
  position:relative;margin:0 auto}
.loader div{position:absolute;width:var(--square);height:var(--square);
  border-radius:var(--radius-sm);background:var(--accent);
  box-shadow:var(--glow-accent-soft)}
.loader div:nth-child(1){left:0;top:0;
  animation:sq1 var(--duration) var(--delay) var(--timing) infinite,
            sqin var(--in-dur) calc(1 * var(--in-delay)) var(--in-timing) both}
.loader div:nth-child(2){left:0;top:var(--offset);opacity:.85;
  animation:sq2 var(--duration) var(--delay) var(--timing) infinite,
            sqin var(--in-dur) calc(1 * var(--in-delay)) var(--in-timing) both}
.loader div:nth-child(3){left:var(--offset);top:var(--offset);opacity:.7;
  animation:sq3 var(--duration) var(--delay) var(--timing) infinite,
            sqin var(--in-dur) calc(2 * var(--in-delay)) var(--in-timing) both}
.loader div:nth-child(4){left:calc(2 * var(--offset));top:var(--offset);opacity:.55;
  animation:sq4 var(--duration) var(--delay) var(--timing) infinite,
            sqin var(--in-dur) calc(3 * var(--in-delay)) var(--in-timing) both}
.loader div:nth-child(5){left:calc(3 * var(--offset));top:var(--offset);opacity:.4;
  animation:sq5 var(--duration) var(--delay) var(--timing) infinite,
            sqin var(--in-dur) calc(4 * var(--in-delay)) var(--in-timing) both}

@keyframes sq1{
  0%{left:0;top:0} 8.333%{left:0;top:var(--offset)} 100%{left:0;top:var(--offset)}}
@keyframes sq2{
  0%{left:0;top:var(--offset)} 8.333%{left:0;top:calc(2 * var(--offset))}
  16.67%{left:var(--offset);top:calc(2 * var(--offset))}
  25%{left:var(--offset);top:var(--offset)} 83.33%{left:var(--offset);top:var(--offset)}
  91.67%{left:var(--offset);top:0} 100%{left:0;top:0}}
@keyframes sq3{
  0%,100%{left:var(--offset);top:var(--offset)}
  16.67%{left:var(--offset);top:var(--offset)} 25%{left:var(--offset);top:0}
  33.33%{left:calc(2 * var(--offset));top:0} 41.67%{left:calc(2 * var(--offset));top:var(--offset)}
  66.67%{left:calc(2 * var(--offset));top:var(--offset)}
  75%{left:calc(2 * var(--offset));top:calc(2 * var(--offset))}
  83.33%{left:var(--offset);top:calc(2 * var(--offset))}
  91.67%{left:var(--offset);top:var(--offset)}}
@keyframes sq4{
  0%{left:calc(2 * var(--offset));top:var(--offset)}
  33.33%{left:calc(2 * var(--offset));top:var(--offset)}
  41.67%{left:calc(2 * var(--offset));top:calc(2 * var(--offset))}
  50%{left:calc(3 * var(--offset));top:calc(2 * var(--offset))}
  58.33%{left:calc(3 * var(--offset));top:var(--offset)}
  100%{left:calc(3 * var(--offset));top:var(--offset)}}
@keyframes sq5{
  0%{left:calc(3 * var(--offset));top:var(--offset)}
  50%{left:calc(3 * var(--offset));top:var(--offset)}
  58.33%{left:calc(3 * var(--offset));top:0}
  66.67%{left:calc(2 * var(--offset));top:0}
  75%{left:calc(2 * var(--offset));top:var(--offset)}
  100%{left:calc(2 * var(--offset));top:var(--offset)}}
@keyframes sqin{0%{transform:scale(.75);opacity:0}100%{transform:scale(1)}}

.bootscreen{position:fixed;inset:0;z-index:60;display:grid;place-content:center;
  gap:var(--space-8);justify-items:center;background:var(--surface-app);
  transition:opacity var(--dur-slow) var(--ease-standard)}
.bootscreen[hidden]{display:none}
.bootscreen.is-out{opacity:0;pointer-events:none}
.bootscreen__t{margin:0;font:var(--fw-medium) var(--text-body-sm)/1 var(--font-mono);
  color:var(--text-ghost);letter-spacing:var(--ls-label)}

/* ==========================================================================
   6. ШАПКА
   ========================================================================== */
/* design-bold: шапка 64px, органы 34px — владелец на большом мониторе:
   «слишком мелкие кнопки на строке». Было 52/28. */
.topbar{position:sticky;top:0;z-index:20;flex:0 0 auto;
  display:flex;align-items:center;gap:var(--space-6);
  height:64px;padding:0 var(--gutter-page);background:var(--surface-chrome);
  backdrop-filter:blur(14px);border-bottom:1px solid var(--border-hairline);
  transition:background-color var(--dur-theme) var(--ease-theme),
             border-color var(--dur-theme) var(--ease-theme)}
.wordmark{font:var(--fw-bold) var(--text-brand)/1 var(--font-brand);letter-spacing:var(--ls-caps);
  text-transform:uppercase;color:var(--text-heading);text-decoration:none}
.wordmark span{color:var(--accent)}
/* Знак перед названием: инлайн-SVG на каждой странице, цвета берёт из
   currentColor и --accent, поэтому живёт в обеих темах без своих правил.
   На узком экране прячется: шапка на 375px и так впритык (см. ниже),
   а имя сайта важнее знака. Исходники знака — design/logo/. */
.wordmark{display:inline-flex;align-items:center;gap:var(--space-4)}
.wordmark__mark{height:.95em;width:auto;flex:0 0 auto}
/* На узком экране логотип ужимается: «qatoolqa.ru» на 60px длиннее
   прежнего «qa-tools», и на 375px шапка (логотип + регулятор текста +
   переключатель темы) переставала помещаться — страница получала
   горизонтальный скролл. Поймано общей проверкой 375px на всех 25
   страницах сразу после переименования. Уменьшаем кегль и разрядку,
   а не прячем «.ru»: имя сайта — решение владельца, оно целиком. */
@media (max-width:480px){
  .wordmark{font-size:calc(var(--text-brand) * .72);letter-spacing:.02em}
  .wordmark__mark{display:none}
  .topbar{gap:var(--space-4);height:56px}
  /* Уменьшение органов до 28px — в самом конце файла: медиазапрос не
     добавляет специфичности, и правила .fontsize/.themeswitch ниже по
     файлу перебивали бы его. */
}
.spacer{flex:1}
/* Плашка «Проверьте Network — запросов нет» убрана из шапки: запросы там
   как раз есть — за шрифтами, стилями и скриптами, — так что надпись была
   неточной, а предложение самому лезть в devtools звучало как вызов, а не
   как обещание. Смысл переехал в подвал обычными словами (.foot__note). */
/* Ссылки «GitHub» в шапке больше нет — репозиторий закрыт, и она вела бы
   в 404 (причина в HANDOFF.md). Сам класс остался: им набрана ссылка на
   «Кодировки» в тексте /base64. */
.ghostlink{color:var(--text-muted);text-decoration:none;
  font:var(--fw-medium) var(--text-body-sm)/1 var(--font-ui);
  transition:color var(--dur-fast) var(--ease-standard)}
.ghostlink:hover{color:var(--text-heading)}

/* ==========================================================================
   6b. РЕГУЛЯТОР ТЕКСТА — три фиксированных шага
   Механика как у переключателя темы: спрятанные radio + скользящая
   подсветка на общем родителе, поэтому работает без JS и доступен с
   клавиатуры стрелками (radiogroup — штатное поведение браузера).
   Размеры самого регулятора намеренно в px и НЕ через --text-*: иначе
   он ужимался бы вместе с текстом и по нему было бы не попасть после
   переключения на мелкий.
   ========================================================================== */
.fontsize{--fs-item:34px;--fs-pad:3px;
  position:relative;flex:0 0 auto;display:inline-flex;align-items:center;
  height:34px;padding:var(--fs-pad);border-radius:var(--radius-pill);
  background:var(--surface-input);border:1px solid var(--border-subtle)}
.fontsize__in{position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.fontsize__lb{position:relative;z-index:1;display:grid;place-items:center;
  width:var(--fs-item);height:26px;cursor:pointer;border-radius:var(--radius-pill);
  color:var(--text-faint);font-family:var(--font-ui);font-weight:var(--fw-semibold);
  line-height:1;-webkit-tap-highlight-color:transparent;
  transition:color var(--dur-fast) var(--ease-standard)}
/* Кегль буквы показывает, что делает кнопка: мелкая, обычная, крупная */
.fontsize__lb:nth-of-type(1){font-size:11px}
.fontsize__lb:nth-of-type(2){font-size:14px}
.fontsize__lb:nth-of-type(3){font-size:18px}
.fontsize__lb:hover{color:var(--text-heading)}
.fontsize__lb:active{transform:scale(.94)}
.fontsize__marker{position:absolute;top:var(--fs-pad);left:var(--fs-pad);z-index:0;
  width:var(--fs-item);height:26px;border-radius:var(--radius-pill);
  background:var(--accent-quiet);border:1px solid var(--border-accent);
  transition:transform var(--dur-base) var(--ease-spring)}
#fs-s:checked ~ .fontsize__marker{transform:translateX(0)}
#fs-m:checked ~ .fontsize__marker{transform:translateX(100%)}
#fs-l:checked ~ .fontsize__marker{transform:translateX(200%)}
#fs-s:checked ~ .fontsize__lb[for="fs-s"],
#fs-m:checked ~ .fontsize__lb[for="fs-m"],
#fs-l:checked ~ .fontsize__lb[for="fs-l"]{color:var(--accent-text)}
.fontsize__in:focus-visible ~ .fontsize__marker{
  border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-quiet)}

/* ==========================================================================
   7. ПЕРЕКЛЮЧАТЕЛЬ ТЕМЫ
   Был крупный тумблер с нарисованным ночным небом — самая игрушечная деталь
   на строгом сайте и единственный элемент шапки, который тянул взгляд на
   себя. Стал иконкой в той же оправе, что и регулятор кегля: у эталонов
   переключатель темы — либо мелкая иконка, либо его нет вовсе.
   Ширина в px и не зависит от --fs-scale: регулятор текста не должен
   утаскивать за собой органы управления.
   ========================================================================== */
.themeswitch{position:relative;flex:0 0 auto;display:inline-grid;place-items:center;
  width:34px;height:34px;border-radius:var(--radius-pill);
  background:var(--surface-input);border:1px solid var(--border-subtle);
  color:var(--text-muted);cursor:pointer;-webkit-tap-highlight-color:transparent;
  transition:color var(--dur-fast) var(--ease-standard),
             border-color var(--dur-fast) var(--ease-standard)}
.themeswitch:hover{color:var(--text-heading);border-color:var(--border-accent)}
/* Чекбокс скрыт визуально, но остаётся настоящим: клавиатура, screen reader
   и :checked работают как раньше — на нём же завязан initTheme(). */
.themeswitch__input{position:absolute;opacity:0;width:0;height:0;margin:0}
.themeswitch__icons{display:grid;place-items:center}
/* Обе иконки лежат в одной ячейке грида и подменяют друг друга: так кнопка
   не дёргается по ширине при смене темы. */
.themeswitch__sun,.themeswitch__moon{grid-area:1/1;display:block}
.themeswitch__moon{opacity:0}
.themeswitch__input:checked ~ .themeswitch__icons .themeswitch__sun{opacity:0}
.themeswitch__input:checked ~ .themeswitch__icons .themeswitch__moon{opacity:1}
.themeswitch__input:focus-visible ~ .themeswitch__icons{
  outline:2px solid var(--accent);outline-offset:6px;border-radius:var(--radius-pill)}

.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* ==========================================================================
   7b. ПЕРЕХОД К ИНСТРУМЕНТУ — кнопка в шапке и палитра (js/palette.js)
   Кнопка стоит на страницах инструментов: на главной вход в каталог —
   само поле поиска. Стиль тот же, что у регулятора кегля и переключателя
   темы: оправа, без заливки; акцент здесь не нужен — это навигация, а
   не действие. Ширина в px, независимо от --fs-scale, как у соседей.
   ========================================================================== */
.jump{flex:0 0 auto;display:inline-flex;align-items:center;gap:var(--space-4);
  height:34px;padding:0 var(--space-6);border-radius:var(--radius-pill);
  background:var(--surface-input);border:1px solid var(--border-subtle);
  color:var(--text-muted);cursor:pointer;-webkit-tap-highlight-color:transparent;
  font:var(--fw-medium) 14px/1 var(--font-ui);white-space:nowrap;
  transition:color var(--dur-fast) var(--ease-standard),
             border-color var(--dur-fast) var(--ease-standard)}
.jump:hover{color:var(--text-heading);border-color:var(--border-accent)}
.jump svg{flex:0 0 auto}
.jump__kbd{display:inline-flex;align-items:center;height:20px;padding:0 var(--space-3);
  border-radius:var(--radius-sm);background:var(--surface-hover);
  border:1px solid var(--border-hairline);color:var(--text-faint);
  font:var(--fw-semibold) 11px/1 var(--font-mono)}
@media (max-width:640px){
  .jump{width:34px;padding:0;justify-content:center}
  .jump__t,.jump__kbd{display:none}
}

/* Палитра: слой поверх страницы, окно у верхнего края — как у командных
   палитр в редакторах, чтобы список рос вниз и не прыгал по центру при
   каждом символе. Граница вместо тени (планка: поверхности разделяет
   граница), плюс одна мягкая тень, потому что окно лежит ПОВЕРХ
   страницы, а не рядом с ней, и одной границы на скриме мало. */
/* Список выпадает ИЗ-ПОД ШАПКИ, у правого края, где стоит кнопка, — как
   поиск в шапке у документации, а не окно посреди затемнённой страницы.
   Первый вариант был именно окном по центру со скримом; владелец: «как
   будто всплывающее окно, непривычно». Страница под списком не
   затемняется и не блокируется — прозрачный слой только ловит клик
   мимо, чтобы закрыть. На телефоне по-прежнему на весь экран. */
html.has-palette{overflow:hidden}
.palette{position:fixed;inset:0;z-index:70;display:flex;justify-content:flex-end;
  align-items:flex-start;padding:68px var(--gutter-page) var(--space-8)}
.palette__scrim{position:absolute;inset:0;background:transparent}
.palette__box{position:relative;width:min(560px,100%);max-height:min(72vh,640px);
  display:flex;flex-direction:column;border-radius:var(--radius-card);
  background:var(--surface-card);border:1px solid var(--border-subtle);
  box-shadow:0 16px 48px rgba(0,0,0,.28);overflow:hidden;
  transform-origin:top right;
  animation:paletteIn var(--dur-base) var(--ease-emphasis) both}
[data-theme="light"] .palette__box{box-shadow:0 16px 48px rgba(7,9,11,.16)}
@keyframes paletteIn{from{opacity:0;transform:translateY(-8px) scale(.98)}to{opacity:1;transform:none}}
.palette__field{display:flex;align-items:center;gap:var(--space-5);
  padding:0 var(--space-6);height:56px;border-bottom:1px solid var(--border-hairline);
  color:var(--text-faint)}
.palette__input{flex:1;min-width:0;height:100%;background:none;border:0;outline:0;
  color:var(--text-heading);font:var(--fw-medium) var(--text-body-lg)/1 var(--font-ui)}
.palette__input::placeholder{color:var(--text-ghost)}
.palette__kbd,.palette__foot kbd{display:inline-flex;align-items:center;justify-content:center;
  min-width:22px;height:20px;padding:0 var(--space-3);border-radius:var(--radius-sm);
  background:var(--surface-hover);border:1px solid var(--border-hairline);
  color:var(--text-faint);font:var(--fw-semibold) 11px/1 var(--font-mono)}
.palette__list{margin:0;padding:var(--space-3);list-style:none;overflow:auto;
  overscroll-behavior:contain}
.palette__group{padding:var(--space-5) var(--space-5) var(--space-3);
  font:var(--fw-semibold) var(--text-micro)/1 var(--font-ui);
  letter-spacing:var(--ls-caps);text-transform:uppercase;color:var(--text-ghost)}
.palette__item{display:grid;grid-template-columns:auto auto 1fr auto;align-items:center;
  column-gap:var(--space-4);padding:var(--space-4) var(--space-5);
  border-radius:var(--radius-md);cursor:pointer;color:var(--text-body)}
.palette__item[aria-selected="true"]{background:var(--surface-selected);color:var(--text-heading)}
.palette__dot{width:6px;height:6px;border-radius:var(--radius-pill);background:var(--dot)}
.palette__name{font:var(--fw-semibold) var(--text-body-sm)/1.3 var(--font-ui)}
.palette__slug{font:var(--fw-medium) var(--text-micro)/1 var(--font-mono);color:var(--text-ghost)}
.palette__desc{grid-column:2/5;font:var(--fw-regular) var(--text-caption)/1.4 var(--font-ui);
  color:var(--text-faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.palette__item[aria-selected="true"] .palette__desc{color:var(--text-muted)}
.palette__here{grid-column:4;grid-row:1;font:var(--fw-medium) var(--text-micro)/1 var(--font-ui);
  color:var(--accent-text)}
.palette__item.is-here{opacity:.7}
.palette__empty{padding:var(--space-8) var(--space-6);
  font:var(--fw-regular) var(--text-body-sm)/1.55 var(--font-ui);color:var(--text-muted)}
.palette__empty code{font-family:var(--font-mono);color:var(--accent-text)}
.palette__foot{display:flex;gap:var(--space-7);padding:var(--space-4) var(--space-6);
  border-top:1px solid var(--border-hairline);
  font:var(--fw-regular) var(--text-micro)/1 var(--font-ui);color:var(--text-ghost)}
.palette__foot span{display:inline-flex;align-items:center;gap:var(--space-3)}
/* На телефоне окно занимает экран целиком: пальцем удобнее список во
   всю ширину, а клавиатура и так съедает половину высоты. */
@media (max-width:640px){
  .palette{padding:0}
  .palette__box{width:100%;height:100%;max-height:none;border-radius:0;border:0}
  .palette__foot{display:none}
}

/* ==========================================================================
   7c. КОЛОНКА ИНСТРУМЕНТОВ на странице инструмента (design-bold)
   Строится js/palette.js из того же TOOLS. Закреплена слева под шапкой,
   содержимое страницы сдвигается на её ширину. Ниже 1200px её нет —
   там рабочей области и так впритык, переключение берёт на себя палитра.
   Состояние «свёрнута» помнится в qa:rail; кнопка — в шапке.
   ========================================================================== */
.toolrail{position:fixed;left:0;top:64px;bottom:0;z-index:15;width:var(--rail-w);
  display:none;flex-direction:column;overflow:auto;overscroll-behavior:contain;
  padding:var(--space-6) var(--space-4) var(--space-8);
  background:var(--surface-chrome);backdrop-filter:blur(14px);
  border-right:1px solid var(--border-hairline)}
.toolrail__group{margin:var(--space-6) var(--space-4) var(--space-3);
  font:var(--fw-semibold) var(--text-micro)/1 var(--font-ui);
  letter-spacing:var(--ls-caps);text-transform:uppercase;color:var(--text-ghost)}
.toolrail__group:first-child{margin-top:0}
.toolrail__link{display:flex;align-items:center;gap:var(--space-4);
  padding:var(--space-3) var(--space-4);border-radius:var(--radius-md);
  color:var(--text-muted);text-decoration:none;
  font:var(--fw-medium) var(--text-body-sm)/1.3 var(--font-ui);
  transition:background var(--dur-fast) var(--ease-standard),
             color var(--dur-fast) var(--ease-standard)}
.toolrail__link:hover{background:var(--surface-hover);color:var(--text-heading)}
.toolrail__link[aria-current="page"]{background:var(--surface-selected);color:var(--text-heading);
  font-weight:var(--fw-semibold)}
.toolrail__dot{flex:0 0 auto;width:5px;height:5px;border-radius:var(--radius-pill);
  background:var(--dot);opacity:.85}
/* Слаг в колонке не показываем: при 236px он теснил длинные названия
   («Генератор пользователей») в две строки. Слаг остаётся в палитре,
   где строка шире. */
.toolrail__slug{display:none}
.railbtn{flex:0 0 auto;display:none;place-items:center;width:34px;height:34px;
  border-radius:var(--radius-pill);background:var(--surface-input);
  border:1px solid var(--border-subtle);color:var(--text-muted);cursor:pointer;
  transition:color var(--dur-fast) var(--ease-standard),
             border-color var(--dur-fast) var(--ease-standard)}
.railbtn:hover{color:var(--text-heading);border-color:var(--border-accent)}
.railbtn[aria-pressed="true"]{color:var(--accent-text);border-color:var(--border-accent)}
@media (min-width:1200px){
  .railbtn{display:grid}
  html.has-rail .toolrail{display:flex}
  html.has-rail .toolhead,html.has-rail .workbench,html.has-rail .foot{
    padding-left:calc(var(--rail-w) + var(--gutter-page))}
}

/* Ссылка «к инструменту» для клавиатуры и читалок: появляется только в
   фокусе, первой по Tab, и перескакивает шапку. */
.skip{position:absolute;left:var(--gutter-page);top:-100px;z-index:80;
  padding:var(--space-4) var(--space-6);border-radius:var(--radius-pill);
  background:var(--accent-fill);color:var(--on-accent);text-decoration:none;
  font:var(--fw-semibold) var(--text-body-sm)/1 var(--font-ui)}
.skip:focus{top:var(--space-5)}

/* Телефон: органы шапки 28px вместо 34. Крупные нужны на большом
   мониторе (просьба владельца), а на 375px они с логотипом не
   помещаются в строку — общая проверка «шапка помещается» это ловит.
   Блок стоит ПОСЛЕ всех правил шапки нарочно: медиазапрос не добавляет
   специфичности, побеждает то, что ниже по файлу. */
@media (max-width:480px){
  .fontsize{--fs-item:28px;height:28px}
  .fontsize__lb,.fontsize__marker{height:20px}
  .fontsize__lb:nth-of-type(1){font-size:10px}
  .fontsize__lb:nth-of-type(2){font-size:13px}
  .fontsize__lb:nth-of-type(3){font-size:16px}
  .themeswitch,.jump,.railbtn{width:28px;height:28px}
}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:1ms!important;
    animation-iteration-count:1!important;transition-duration:1ms!important}
  /* Повторяем адресно: правило theme-anim выше по специфичности, и без
     этой строки оно перебивало бы отказ от анимаций. */
  html.theme-anim,html.theme-anim *,
  html.theme-anim *::before,html.theme-anim *::after{transition-duration:1ms!important}
}
