/* Узкая страница базы знаний для панели чата открытых линий.
 *
 * Догрузка поверх kb.css, а не своя тема: плашки, разделы и окно блока обязаны
 * выглядеть так же, как во вкладке пульта, — оператор смотрит на одно и то же
 * содержимое в двух местах. Здесь только то, что меняется от ширины: панель
 * Битрикс24 около 400px, а вкладка пульта — во весь экран.
 *
 * kb.css живёт без переменных из style.css (ни одной var()), поэтому подключается
 * сюда в одиночку и ничего не тянет за собой. */

/* Ширина считается по краю рамки, вместе с полями.
 *
 * kb.css написан и проверен во вкладке пульта, а там поверх него лежит style.css
 * со своим `* { box-sizing: border-box }`. Сюда style.css не подключён, и те же
 * правила считались иначе: ширина 100% у поля поиска, у пункта списка и у самого
 * окна означала «сто процентов ПЛЮС свои поля и рамка». Поиск вылезал за правый
 * край шапки на 30px, окно за край подложки — на 28px, и страницу можно было
 * утянуть вбок. В широкой панели это пряталось за краем фрейма; на экране в
 * 360px это треть экрана вправо.
 *
 * Переносим только это правило: `margin: 0; padding: 0` из style.css сюда не
 * идут — kb.css рассчитан на обычные отступы браузера. */
*, *::before, *::after {
  box-sizing: border-box;
}

/* Шрифт остаётся того размера, какой задан. iOS считает себя вправе увеличить
 * его сам, когда телефон кладут набок, — и надписи на вкладках, подобранные под
 * ширину до точки («Преподаватели» помещаются впритык), обрезаются на ровном
 * месте. */
html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

/* Страница целиком — один экран без прокрутки документа: прокручивается только
 * список блоков. Иначе строка поиска уезжала бы вверх ровно тогда, когда
 * оператор ищет второе слово. */
html, body {
  height: 100%;
  margin: 0;
}

/* Высота окна на телефоне — величина подвижная: сверху адресная строка, которая
 * прячется при прокрутке, снизу клавиатура. 100% выше меряют окно, каким его
 * считает вёрстка, и низ списка блоков остаётся за экраном.
 *
 * dvh — высота с поправкой на адресную строку, её знает сам браузер. Про
 * клавиатуру браузер не скажет ничего: её меряет js/lines-fit.js и кладёт ответ
 * в --ln-vh. Нет переменной (клавиатура закрыта) — работает dvh; нет и dvh
 * (старый браузер) — остаётся 100% из правила выше, поэтому вся связка и стоит
 * внутри @supports. */
@supports (height: 100dvh) {
  html, body {
    height: var(--ln-vh, 100dvh);
  }
}
body {
  display: flex;
  flex-direction: column;
  font: 14px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
  color: var(--b24-text);
  background: var(--b24-bg);
}

/* ─── Выход из тесного окна ────────────────────────────────────────────────── */

/* Мессенджер обрезает панель справа и снизу, поэтому кнопка занимает самый
 * верх и всю ширину: при любой обрезке от неё остаётся видимым левый край со
 * значком ⤢. Цвет — синий Битрикс24, чтобы её было видно как кнопку, а не как
 * заголовок, даже когда от неё видна треть. */
.ln-open {
  flex: none;
  padding: 6px 6px 0;
  background: var(--b24-white);
}
.ln-open-btn {
  display: block;
  width: 100%;
  padding: 7px 8px;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.2;
  color: var(--b24-white);
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  background: var(--b24-primary);
  border: 0;
  border-radius: 6px;
  cursor: pointer;
}
.ln-open-btn:hover { background: var(--b24-blue-dark); }

/* ─── Панель в чате: на странице одна кнопка ───────────────────────────────── */

/* В чате страница не показывает содержимого вовсе. Причина не в вёрстке:
 * мессенджер отводит виджету окно со стороной примерно в сотню точек, шире не
 * делает и обрезает всё лишнее справа и снизу. Панель, от которой видно три
 * обрезанных слова, человек читает как поломку; кнопка, видная целиком, — как
 * вход. Содержимое открывается слайдером поверх портала (js/lines-open.js) —
 * ровно так документация Битрикс24 и предписывает поступать со всем, что в
 * виджет не помещается.
 *
 * Пометку ln-panel ставит скрипт в <head> страницы — до отрисовки, иначе панель
 * успевает мелькнуть целиком. Снимает её js/lines-size.js, если портал назовёт
 * место встраивания, где страница помещается вся. */
/* Фон страницы в чате — тот же синий, что у кнопки. Это подстраховка от белой
 * полосы: сколько бы точек ни осталось между краем кнопки и краем фрейма —
 * от округления высоты, от полосы прокрутки, от чужой темы, — они будут
 * синими, а не белыми. Прозрачный фон, который стоял здесь раньше, показывал
 * в этих точках белую подложку мессенджера. */
.ln-panel body { background: var(--b24-primary); }
.ln-panel .ln-head,
.ln-panel .ln-body,
.ln-panel .kb-overlay { display: none; }

/* Кнопка занимает фрейм целиком — и по ширине, и по высоте.
 *
 * Раньше она была заперта в 88 точек, и всё, что мессенджер отводил сверх,
 * выходило белым полем справа и снизу. Поле это не пустяк: внутри виджета
 * нажатие по нему не делает ничего, а человек читает такой угол как
 * недогруженный кусок, а не как кнопку.
 *
 * Своего размера у кнопки нет вовсе — его задаёт фрейм, а фрейм js/lines-size.js
 * просит заведомо больше отведённого окна. Лишнее мессенджер обрежет по краю, и
 * в чате останется ровно синий прямоугольник: белому полю взяться неоткуда ни
 * при каком размере окна.
 *
 * Скруглять углы в этом облике нечего: по радиусу видны те же белые уголки,
 * из-за которых всё и затевалось. */
.ln-panel .ln-open {
  flex: 1;
  display: flex;
  padding: 0;
  background: transparent;
}
.ln-panel .ln-open-btn {
  flex: 1;
  max-width: none;
  display: flex;
  align-items: flex-start;
  justify-content: flex-start;
  padding: 0;
  font-size: 12px;
  text-align: center;
  white-space: normal;
  overflow: visible;
  border-radius: 0;
}

/* Надпись — посередине видимой части кнопки.
 *
 * Кнопка растянута на весь фрейм, а фрейм просится больше отведённого окна:
 * поставь надпись посреди кнопки — обрезка прошла бы ровно по ней, и в чате
 * остался бы синий квадрат без единого слова. Ровно это и случилось при первой
 * попытке закрасить белые края.
 *
 * Поэтому середина здесь своя: в левом верхнем углу кнопки отведён клочок
 * 88×76 — примерно то самое окно, которое мессенджер показывает, — и надпись
 * стоит по центру этого клочка. Мерить окно изнутри нечем: страница знает
 * размер своего фрейма, а не той дырки, через которую его показывают, — потому
 * клочок и взят чуть меньше окна, каким мы его видели в чате заказчика. Ошибись
 * мы в меньшую сторону — надпись просто окажется выше и левее середины, но
 * останется на виду; в большую — её срезало бы краем.
 *
 * Надпись переносится внутри этой ширины на строки и стоит целиком: обрезка
 * многоточием была бы худшим из исходов — «Во весь…» в углу чата не читается
 * ни как кнопка, ни как приглашение. */
.ln-panel .ln-open-in {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 88px;
  height: 76px;
  padding: 0;
}
.ln-panel .ln-open-ic { display: block; font-size: 15px; }

/* ─── Шапка ────────────────────────────────────────────────────────────────── */

/* Вкладки, поиск и строка выбора всегда на виду: в разговоре к ним возвращаются
 * чаще, чем к любому блоку. Три строки — ровно та же высота, что была у прежней
 * шапки (поиск, список семинаров, ряд разделов). */
.ln-head {
  flex: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 12px;
  background: var(--b24-white);
  border-bottom: 1px solid var(--b24-border);
}

.ln-head .kb-search { width: 100%; }

/* Три вкладки одним переключателем, как сегментный выключатель: они делят
 * ширину панели поровну и никуда не уезжают. Прокрутки здесь нет намеренно —
 * именно она в прежнем ряду разделов прятала половину кнопок за краем. */
.ln-tabs {
  display: flex;
  gap: 4px;
  padding: 3px;
  background: var(--b24-bg);
  border-radius: 8px;
}
.ln-tab {
  flex: 1 1 auto;
  min-width: 0;
  padding: 6px 8px;
  font: inherit;
  font-size: 13px;
  line-height: 1.2;
  color: var(--b24-text);
  background: transparent;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ln-tab:hover { background: var(--b24-border); }
.ln-tab--active {
  background: var(--b24-white);
  color: var(--b24-text);
  font-weight: 600;
  box-shadow: 0 1px 2px rgba(31, 43, 56, 0.14);
}

/* Раздел и семинар — две кнопки в одной строке. Каждая открывает список окном,
 * поэтому длинное название («Регистрационный сбор», «Продажи на маркетплейсах»)
 * обрезается только здесь, на кнопке, а в самом списке стоит целиком. */
.ln-context { display: flex; gap: 6px; }
.ln-pick {
  flex: 1 1 0;
  min-width: 0;
  position: relative;
  padding: 6px 22px 6px 8px;
  font: inherit;
  font-size: 13px;
  text-align: left;
  color: var(--b24-text);
  background: var(--b24-white);
  border: 1px solid var(--b24-border);
  border-radius: 6px;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ln-pick:hover { border-color: var(--b24-primary); }
/* Стрелка нарисована стилем, а не вписана в текст кнопки: текст кнопки — это
 * ответ на вопрос «какой раздел выбран», и посторонний знак в нём мешал бы и
 * человеку, и проверке. */
.ln-pick::after {
  content: "▾";
  position: absolute;
  right: 7px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--b24-text-light);
}
.ln-pick--empty { color: var(--b24-text-light); }

/* ─── Список ───────────────────────────────────────────────────────────────── */

.ln-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 12px;
}

.ln-body .kb-notice,
.ln-body .kb-error { margin-bottom: 10px; }

.ln-body .kb-empty { padding: 18px 4px; font-size: 13px; }

/* Одна колонка: на 400px вторая всё равно не помещается, но правило задано
 * прямо, а не оставлено на арифметику auto-fill, — панель Битрикс24 бывает и
 * шире, а две колонки по 250px здесь читаются хуже одной. */
.ln-body .kb-tiles {
  grid-template-columns: 1fr;
  gap: 10px;
  margin-bottom: 16px;
}

/* Плашка ниже, чем в пульте: 168px рассчитаны на сетку из четырёх колонок, где
 * высота выравнивает ряд. В одной колонке ряда нет, и лишняя высота — это
 * лишняя прокрутка. */
.ln-body .kb-tile {
  min-height: 0;
  padding: 10px 10px 8px 14px;
}
.ln-body .kb-tile-preview { font-size: 13px; }

.ln-body .kb-section-title { margin-top: 0; font-size: 13px; }

/* ─── Окно блока ───────────────────────────────────────────────────────────── */

/* В панели окно занимает её почти целиком: полей по 20px, как в пульте, здесь
 * съели бы десятую часть ширины строки. Селекторы без своей приставки: этот файл
 * подключён только на этой странице, и приставка добавляла бы вес правилам,
 * которым не с чем спорить. */
.kb-overlay { padding: 8px; }
.kb-dialog {
  padding: 14px;
  max-height: 96vh;
}

/* Кнопки окна переносятся: «← Предыдущий», «Следующий →» и «Копировать» в одну
 * строку на 400px не встают. Распорка между стрелками и копированием на узкой
 * панели не нужна — она бы просто рвала ряд пополам. */
.kb-view-actions { flex-wrap: wrap; gap: 8px; }
.kb-view-gap { display: none; }
.kb-view-actions .kb-btn { flex: 1; min-width: 120px; }

/* ─── Окно со списком разделов и семинаров ─────────────────────────────────── */

/* Список прижат к верху, а не к середине: он открывается кнопкой из шапки, и
 * прыжок содержимого от неё к середине экрана человек читает как «открылось
 * что-то другое». */
.ln-menu { align-items: flex-start; }
.ln-menu-dialog {
  width: 100%;
  max-width: 520px;
  max-height: 86vh;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.ln-menu-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
}
.ln-menu-title { margin: 0; font-size: 16px; }
.ln-menu-filter { width: 100%; }
/* Прокручивается только сам список: заголовок и строка отбора остаются на месте,
 * иначе на девяноста трёх семинарах отбор уезжает вверх ровно тогда, когда до
 * него дошла очередь. */
.ln-menu-list {
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* Пункт занимает всю ширину окна и ничего не обрезает по краям — в этом вся
 * разница с прежним рядом кнопок. Цифра справа — та же плашка со счётчиком,
 * что стояла на кнопке раздела (.kb-section-n из kb.css). */
.ln-menu-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  padding: 9px 10px;
  font: inherit;
  font-size: 14px;
  text-align: left;
  color: var(--b24-text);
  background: var(--b24-white);
  border: 1px solid var(--b24-border);
  border-left-width: 3px;
  border-radius: 6px;
  cursor: pointer;
}
.ln-menu-item:hover { background: var(--b24-bg); }
.ln-menu-item-title {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ln-menu-item--active {
  border-color: var(--b24-primary);
  background: var(--b24-primary-soft);
  font-weight: 600;
}

/* Цвет раздела — тот же, что у полосы слева на плашке и под кнопкой в пульте.
 * Стоят после правила выбранного пункта: у выбранного раздела левая полоса
 * обязана остаться своего цвета, а не стать общей синей. */
.ln-menu-item--step      { border-left-color: var(--b24-primary); }
.ln-menu-item--objection { border-left-color: var(--b24-orange); }
.ln-menu-item--regfee    { border-left-color: var(--b24-green); }
.ln-menu-item--template  { border-left-color: var(--b24-purple); }
.ln-menu-item--fact      { border-left-color: var(--b24-text-light); }

/* ─── Семинары и преподаватели ─────────────────────────────────────────────── */

/* Плашка справочника ниже, чем в пульте, по той же причине, что и плашка блока:
 * в одной колонке ряда нет, и лишняя высота — это лишняя прокрутка. */
.ln-body .kb-sem-tile { padding: 10px 12px 9px; gap: 2px; }
.ln-body .kb-sem-card { padding: 12px; gap: 10px; }
.ln-body .kb-sem-head { margin-bottom: 10px; }

/* Подпись поля и кнопка «Копировать» в одной строке: кнопка под подписью
 * добавляла бы карточке по строке на каждое поле, а их в ней три. */
.ln-field-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

/* Кнопки карточки переносятся, как в окне блока: «Скрипт с этим семинаром» и
 * «Методичка · 14» в одну строку на 400px не встают. */
.ln-card-actions { flex-wrap: wrap; }
.ln-card-actions .kb-btn { flex: 1; min-width: 140px; }

/* ─── Пальцем ────────────────────────────────────────────────────────────────
 *
 * Всё ниже включается там, где по экрану водят пальцем: телефон, планшет.
 * Спрашиваем именно указатель, а не ширину. Узким окно бывает и на столе —
 * панель чата 420px, рамка предпросмотра во вкладке пульта, — а мышью попадают
 * и в кнопку высотой 22px. По ширине эти два случая не различить, по указателю
 * различаются точно. */
@media (pointer: coarse) {

  /* Кнопка «Во весь экран» ведёт в пульт: карта, графики, вёрстка в три колонки
   * и лишние полмегабайта — на телефоне это заведомо не то, за чем человек
   * открывал базу знаний. Сама страница на телефоне и так во весь экран, звать
   * её туда нечем. В чате (.ln-panel) кнопка остаётся: там она — единственный
   * выход из окна величиной со значок. */
  html:not(.ln-panel) .ln-open { display: none; }

  /* 16px здесь не про читаемость, а про поведение iOS: поле с меньшим шрифтом
   * Safari при касании увеличивает вместе со всей страницей и обратно уже не
   * уменьшает. Человек остаётся в перекошенной странице, из которой выходят
   * двумя пальцами. Правило одно на оба поля — поиск по базе и отбор в списке
   * разделов и семинаров. */
  .kb-search {
    min-height: 44px;
    padding: 11px 14px;
    font-size: 16px;
  }

  /* Мимо всего, что ниже 44px, палец промахивается (столько советует Apple, у
   * Google — 48). Высоту добираем min-height, а не полями: поля отбирают место
   * у надписи, а у вкладок оно на пределе — «Преподаватели» на экране в 360px
   * помещаются только 13-м шрифтом, и прибавка обрежет их до «Преподават…». */
  .ln-tab { min-height: 40px; }
  .ln-pick { min-height: 40px; }
  .kb-btn { min-height: 44px; font-size: 15px; }
  .kb-tile-btn { min-height: 36px; padding: 7px 12px; font-size: 13px; }
  .ln-menu-item { min-height: 48px; font-size: 15px; }
  /* Кнопка отдельной реплики в окне блока. Промах по ней дороже обычного: рядом
   * стоят соседние реплики, и вместо нужного варианта в чат клиенту уйдёт тот,
   * что оператор не выбирал. Размер — как у кнопок плашки, чтобы одно и то же
   * действие не отличалось от места к месту. */
  .kb-part-copy { min-height: 36px; padding: 7px 12px; font-size: 13px; }

  /* Крестик закрытия — знак в 20px в поле шириной 8px: попасть в него пальцем
   * нельзя, а промах по нему в окне блока значит «читаю дальше не тот блок». */
  .kb-view-close {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    padding: 0;
  }

  /* Касание не наводит курсор. Ответ на нажатие поэтому даём сами (:active),
   * а наведение гасим ниже, в @media (hover: none): без ответа кнопка кажется
   * мёртвой и её жмут второй раз, а с залипшим наведением — наоборот, нажатой
   * там, где давно отпустили. */
  .kb-tile, .kb-sem-tile, .ln-menu-item, .ln-tab, .ln-pick,
  .kb-btn, .kb-tile-btn, .kb-part-copy, .ln-open-btn, .kb-view-close {
    touch-action: manipulation;          /* без ожидания второго касания в 300 мс */
    -webkit-tap-highlight-color: transparent;
  }
  .kb-tile, .kb-sem-tile, .ln-menu-item, .ln-tab, .ln-pick, .kb-btn, .kb-tile-btn {
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;         /* долгое нажатие по плашке не зовёт меню браузера */
  }
  .kb-tile:active, .kb-sem-tile:active, .ln-menu-item:active { background: var(--b24-primary-soft); }
  .kb-btn:active, .kb-tile-btn:active, .kb-part-copy:active, .ln-pick:active { background: var(--b24-border); }
  .kb-btn--primary:active { background: var(--b24-blue-dark); }
  .ln-tab:active { background: var(--b24-border); }
  .ln-open-btn:active { background: var(--b24-blue-dark); }

  /* Вырез и полоса жестов. Без viewport-fit=cover (см. lines.html) значения
   * env() — нули, и правила ничего не меняют; с ним страница занимает экран
   * целиком, а отступы уводят содержимое из-под выреза и из-под полосы. Шапку и
   * список правим только вне панели чата: в чате выреза нет, а места мало. */
  html:not(.ln-panel) .ln-head {
    padding-top: calc(10px + env(safe-area-inset-top, 0px));
    padding-right: calc(12px + env(safe-area-inset-right, 0px));
    padding-left: calc(12px + env(safe-area-inset-left, 0px));
  }
  html:not(.ln-panel) .ln-body {
    padding-right: calc(12px + env(safe-area-inset-right, 0px));
    padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
    padding-left: calc(12px + env(safe-area-inset-left, 0px));
  }
  .kb-overlay {
    padding:
      calc(8px + env(safe-area-inset-top, 0px))
      calc(8px + env(safe-area-inset-right, 0px))
      calc(8px + env(safe-area-inset-bottom, 0px))
      calc(8px + env(safe-area-inset-left, 0px));
  }

  /* Прокрутка не перетекает наружу: докрутив список до конца, палец не тянет за
   * собой страницу под ним, а на Android не срывает перезагрузку жестом «потянуть
   * вниз». Посреди звонка перезагрузка — это потеря и набранного поиска, и места
   * в списке. */
  html, body { overscroll-behavior: none; }
  .ln-body, .ln-menu-list, .kb-view-body, .kb-dialog { overscroll-behavior: contain; }

  /* Окно блока — один прокручиваемый кусок вместо двух вложенных: заголовок и
   * кнопки стоят, ездит только текст. Раньше текст был заперт в 55vh, а остаток
   * окна прокручивался отдельно, и палец попадал то в одну прокрутку, то в
   * другую — текст казался застрявшим. */
  .kb-dialog--view {
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }
  .kb-dialog--view .kb-view-head,
  .kb-dialog--view .kb-view-actions { flex: none; }
  .kb-dialog--view .kb-view-body {
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;
  }

  /* Это читают вслух клиенту, держа телефон в руке, а не рассматривают за
   * столом. Плюс шрифта здесь — минус к числу прищуриваний за смену. */
  .kb-view-body { font-size: 17px; }
  .ln-body .kb-tile-title { font-size: 16px; }
  .ln-body .kb-tile-preview { font-size: 14px; }
  .ln-body .kb-empty { font-size: 14px; }

  /* Окно во всю видимую высоту — ту, что осталась от клавиатуры (--ln-vh,
   * js/lines-fit.js). vh здесь врал бы дважды: он не знает ни про адресную
   * строку, ни про клавиатуру, и список из 93 семинаров с открытым отбором
   * показывал бы три строки — остальные под клавиатурой.
   *
   * Высота у подложки задана вместе с inset: 0 из kb.css. Это нарочно: браузер
   * не может выполнить оба указания разом и отбрасывает нижний край, оставляя
   * верхний и высоту, — то есть подложка прижата к верху видимой части. */
  @supports (height: 100dvh) {
    .kb-overlay { height: var(--ln-vh, 100dvh); }
    .kb-dialog { max-height: 100%; }
    .ln-menu-dialog { max-height: 100%; }
  }
}

/* ─── Наведение, которого нет ────────────────────────────────────────────────
 *
 * У касания нет «до»: браузер телефона показывает :hover уже после нажатия и
 * снимает его только при нажатии в другом месте. Из-за этого закрытая плашка
 * остаётся подсвеченной, будто она всё ещё открыта, а в списке семинаров
 * подсвечены сразу две строки — та, куда ткнули, и та, что выбрана.
 *
 * Возвращаем каждому правилу наведения его обычный вид. Ответ на само касание
 * даёт :active выше, так что кнопка не станет молчаливой. */
@media (hover: none) {
  .kb-tile:hover { border-color: var(--b24-border); box-shadow: none; }
  .kb-sem-tile:hover { border-color: var(--b24-border-strong); background: var(--b24-white); box-shadow: none; }
  .ln-tab:hover { background: transparent; }
  .ln-tab--active:hover { background: var(--b24-white); }
  .ln-pick:hover { border-color: var(--b24-border); }
  .kb-btn:hover { background: var(--b24-white); }
  .kb-btn--primary:hover { background: var(--b24-primary); }
  .kb-tile-btn:hover { background: var(--b24-white); }
  .kb-part-copy:hover { background: var(--b24-white); }
  .ln-menu-item:hover { background: var(--b24-white); }
  .ln-menu-item--active:hover { background: var(--b24-primary-soft); }
  .ln-open-btn:hover { background: var(--b24-primary); }
}
