@charset "UTF-8";
/* ============================================================================
   Zvukogram · /category/ · v5 · HANDOFF CSS
   ----------------------------------------------------------------------------
   Назначение этого файла:
   --------------------------------------------------------------------------
   Полный CSS-комплект для страницы /category/<tag>/. Сейчас применяется
   как override (вставляется поверх через <style>) — то же самое поведение
   планируется получить после интеграции в SCSS/прод.

   Все правила с — потому что на странице есть inline <style>
   от сайта c теми же селекторами; без перебить не получится.
   После переноса в основной SCSS большинство можно снять —
   останутся только те что бьют сторонние плагины (ya-share, wavesurfer).

   Что закрывает этот файл:
   --------------------------------------------------------------------------
   1. Wrapper и type-scale страницы (.zg-v3, h1, .zg-v3__crumbs)
      + новые элементы: meta-strip, описание с раскрытием, дата+теги.
   2. Filter panel (белая карточка с сорт-табами + чипы длительности
      + dropdowns громкость/битрейт + chip-strip групп + density toggle).
   3. Track list (slim-карточки в 7-колоночном grid, hover-tooltip на title,
      split-button MP3+▾, fav-кнопка, mini-share, иконки мета-строки).
   4. Responsive ladder (1280 / 1100 / 860 / 480 — учитывает левый
      сайдбар сайта ~340px).

   Зависимости от JS (см. category-handoff.html — комментарии):
   --------------------------------------------------------------------------
     · body[data-zg-sort]        — ставится по URL-параметру ?sort. Скрывает
                                   групп-стрип и H2 кроме режима groups.
     · body[data-zg-density]     — comfort | compact (от density-toggle).
     · .waveTitle[data-truncated="1"]
                                  — JS проставляет после render если
                                    scrollWidth > clientWidth (показывает
                                    hover-tooltip).
     · .zgcat-section-count      — счётчик треков справа от h2 секции
                                    ("9 звуков"). Прямоугольный chip с лёгким
                                    site-радиусом (--zgcat-radius-sm).
     · .zg-v3__groups[style="--zg-cols: N"]
                                  — JS считает оптимальное число колонок:
                                    9→3×3, 8→4×2, 6→3×2 итд.
     · .zgcat-fdrop-toggle[aria-expanded]
                                  — toggle dropdown'ов loudness/bitrate.
     · .zgcat-formats-toggle     — toggle popover'а wav/m4r/ogg.
     · .zgcat-fav[data-active="1"]
                                  — состояние избранного (localStorage по
                                    track-id).

   Дизайн-токены (источник: /speech/, /clone/, /rating/):
   --------------------------------------------------------------------------
     --zgcat-primary       == --pr-blue       == #2e58ef
     --zgcat-radius        == --br            == 8px
     --zgcat-border-2      == --border-card   == #dfe7f3
     --zgcat-l-blue        == --l-blue        == #eef4f9
   После интеграции желательно использовать сайтовые токены напрямую,
   а локальные --zgcat-* можно удалить (они нужны только потому что мы
   не хотим зависеть от того что эти переменные определены на странице).

   ============================================================================
   Структура секций:
     1. Wrapper / typography / desc / meta-strip
     2. Filter panel: sorts, groups chip-strip, duration row, secondary row,
        density toggle, dropdowns (loudness, bitrate)
     3. Track cards: layout grid, play, titlewrap (waveTitle + trackmeta +
        share-mini + tooltip), wave, time, fav, MP3 split-button, popover
     4. Responsive (1280 / 1100 / 860 / 480)
   ============================================================================ */
/* Дизайн-токены (локальный набор; после интеграции в SCSS можно
   заменить на сайтовые --pr-blue / --br / --border-card / --l-blue). */
:root {
  --zgcat-primary: #2e58ef; /* primary blue (== site --pr-blue) */
  --zgcat-primary-hov: #1a40c9; /* primary hover */
  --zgcat-primary-soft: rgba(46, 88, 239, 0.07); /* active tab/chip background */
  --zgcat-primary-tint: rgba(46, 88, 239, 0.10); /* hover tint */
  --zgcat-ink: #0f172a; /* heading dark */
  --zgcat-text: #2C2C2C; /* body text */
  --zgcat-muted: #6A6E8F; /* secondary muted */
  --zgcat-muted-2: #94a3b8; /* third-level muted */
  --zgcat-muted-3: #cbd5e1; /* dividers etc. */
  --zgcat-page: #f3f3f3; /* page bg */
  --zgcat-surface: #ffffff; /* card bg */
  --zgcat-surface-soft: #f8fafc; /* compact card hover */
  --zgcat-border: #e2e8f0; /* default border */
  --zgcat-border-2: #dfe7f3; /* (== site --border-card) */
  --zgcat-line-soft: #f1f5f9; /* split-toggle stripe etc. */
  --zgcat-l-blue: #eef4f9; /* (== site --l-blue) */
  --zgcat-radius: 8px; /* (== site --br) */
  --zgcat-radius-sm: 6px;
  --zgcat-shadow-card: 0 1px 2px rgba(15, 23, 42, 0.04);
  --zgcat-shadow-hover: 0 4px 16px -4px rgba(15, 23, 42, 0.08);
}

/* admin block (не показываем посетителю) */
.zg-v3__admin.forAdmin,
.zg-v3 .forAdmin {
  display: none;
}

/* .zg-v3-admin* moved to separate file: files/zg-v3-admin.css */
/* Kill the "leftover" gray outline on dashBoard wrapper that creates side-lines */
.zg-v3 .dashBoard.vv3,
.zg-v3 .dashBoard {
  outline: none;
  border: 0;
}

/* =====================================================================
   1. Wrapper
   ===================================================================== */
.zg-v3 {
  max-width: 1200px;
  margin: 0 auto;
  /* top 24px = воздух от шапки/хлебных крошек, чтобы не прилипал к верху. */
  padding: 24px 24px 32px 28px;
  color: var(--zgcat-text);
}

/* v2: no outer panel — tracks/controls sit directly on the gray page bg, edge-to-edge */
.zg-v3__tracks {
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: 0;
  box-shadow: none;
  margin-top: 14px;
}

.zg-v3__section {
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0 0 28px;
  scroll-margin-top: 24px;
}

.zg-v3__section:last-child {
  margin-bottom: 0;
}

/* Disable any sticky positioning of filter/sort blocks during scroll */
.zg-v3__filters,
.zg-v3__sorts,
.zg-v3__panel,
.zg-v3__top {
  position: static;
  top: auto;
}

/* === Group section H2 — title above each tracklist === */
.zg-v3__tracks {
  counter-reset: zg-section-num;
}

.zg-v3__section {
  counter-increment: zg-section-num;
}

.zg-v3__section > h2 {
  font-size: 18px;
  font-weight: 700;
  color: var(--zgcat-ink);
  letter-spacing: -0.02em;
  line-height: 1.3;
  margin: 0 0 10px;
  padding: 0;
  border-left: 0;
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 12px;
  font-family: inherit;
}

.zg-v3__section > h2::before {
  content: counter(zg-section-num, decimal-leading-zero);
  font-family: "JetBrains Mono", "SF Mono", "Menlo", "Consolas", monospace;
  font-size: 16px;
  font-weight: 600;
  color: var(--zgcat-primary);
  letter-spacing: 0;
  line-height: 1;
  flex-shrink: 0;
  display: inline-block;
  background: transparent;
  width: auto;
  height: auto;
  border-radius: 0;
}

.zg-v3__section > h2 .zgcat-section-count {
  font-size: 12px;
  font-weight: 500;
  color: var(--zgcat-primary);
  letter-spacing: 0;
  text-transform: lowercase;
  margin-left: auto;
  align-self: center;
  padding: 3px 8px;
  background: rgba(46, 88, 239, 0.08);
  border-radius: var(--zgcat-radius-sm, 6px);
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  flex-shrink: 0;
}

/* === Groups chip nav — only visible when sort=groups (other sorts show flat list) === */
body:not([data-zg-sort=groups]) .zg-v3__groups {
  display: none;
}

body:not([data-zg-sort=groups]) .zg-v3__section > h2 {
  display: none;
}

body:not([data-zg-sort=groups]) .zg-v3__section {
  margin-bottom: 0;
}

/* Groups mode: chip-nav as a horizontal strip with bg bleed (-16px) +
   dynamic CSS Grid (cols set by JS via --zg-cols based on group count). */
body[data-zg-sort=groups] .zg-v3__groups {
  display: grid;
  grid-template-columns: repeat(var(--zg-cols, 4), 1fr);
  gap: 6px;
  align-items: stretch;
  background: rgba(46, 88, 239, 0.045);
  border: 0;
  border-top: 1px solid rgba(46, 88, 239, 0.18);
  border-radius: 0;
  margin: 0 -16px 6px -16px;
  padding: 8px 16px;
  box-sizing: content-box;
}

.zg-v3__groups {
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  margin: 0 0 12px;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 0;
}

.zg-v3__groups::before {
  content: none;
}

.zg-v3__groups {
  counter-reset: zg-chip-num;
}

.zg-v3__groups > .zg-v3__btn {
  font-size: 11.5px;
  font-weight: 500;
  padding: 4px 10px;
  background: var(--zgcat-surface);
  border: 1px solid var(--zgcat-border-2);
  border-radius: var(--zgcat-radius-sm);
  color: var(--zgcat-text);
  text-decoration: none;
  cursor: pointer;
  line-height: 1.25;
  /* block-display + nowrap+ellipsis — работает корректно. flex ломал ellipsis
     потому что для flex-children нужен явный inner-span с flex:1; min-width:0,
     которого нет в DOM (chip — это просто <a>текст</a> + ::before counter). */
  display: block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  letter-spacing: 0;
  text-transform: none;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
  counter-increment: zg-chip-num;
  width: 100%;
  text-align: left;
}

.zg-v3__groups > .zg-v3__btn::before {
  content: counter(zg-chip-num, decimal-leading-zero);
  font-family: "JetBrains Mono", "SF Mono", "Menlo", "Consolas", monospace;
  font-size: 10px;
  font-weight: 500;
  color: var(--zgcat-muted-2);
  letter-spacing: 0;
  display: inline-block;
  margin-right: 6px;
  vertical-align: baseline;
}

.zg-v3__groups > .zg-v3__btn.is-current::before,
.zg-v3__groups > .zg-v3__btn:hover::before {
  color: var(--zgcat-primary);
}

.zg-v3__groups > .zg-v3__btn:hover {
  border-color: var(--zgcat-primary);
  color: var(--zgcat-primary);
}

.zg-v3__groups > .zg-v3__btn.is-current {
  background: #e8edfd;
  border-color: rgba(46, 88, 239, 0.25);
  color: var(--zgcat-primary);
  font-weight: 600;
}

.zg-v3__top {
  margin-bottom: 18px;
}

/* Breadcrumbs */
.zg-v3__crumbs {
  margin-bottom: 12px;
}

.zg-v3__crumbs .breadcrumb {
  font-size: 13px;
  color: var(--zgcat-muted);
  background: transparent;
  padding: 0;
  margin: 0;
}

.zg-v3__crumbs .breadcrumb a {
  color: var(--zgcat-muted);
  text-decoration: none;
  transition: color 0.15s;
}

.zg-v3__crumbs .breadcrumb a:hover {
  color: var(--zgcat-primary);
}

.zg-v3__crumbs .breadcrumb .divider {
  color: var(--zgcat-muted-2);
  margin: 0 5px;
}

/* === Mobile menu (.leftSupermenu.globm) — programmer integration 2026-05-08
   Прогер вставил `.leftSupermenu.globm` под `.contnr.morda.nofixbox`.
   1. ДЕСКТОП ≥769: глушим (видимый блок 300×372 в потоке не нужен).
   2. .pbox (login-блок) лезет под header — даём margin-top чтобы было
      видно ниже header.
   Header z-index фиксится в основном rule body.zg-extras-no-fixed-header
   header.site-header (поднят 99 → 9999, см. далее в файле — там же
   stacking-context фикс для topnav dropdown overlap). */
body.zg-extras-no-fixed-header .leftSupermenu .pbox {
  margin-top: 50px;
  padding-top: 20px;
}

@media (min-width: 769px) {
  body.zg-extras-no-fixed-header .leftSupermenu.globm {
    display: none !important;
  }
}
/* === Burger button balance (.menuButton.soloMenu) =========================
   _globmenu.scss задаёт padding: 8px 16px + margin-right: -16px (затягивает
   правый край за viewport, hover-bg клипается, выглядит криво). Здесь —
   квадратный 40×40 с центрированной 24×24 SVG-иконкой, без negative-margin. */
body.zg-extras-no-fixed-header .menuButton.soloMenu {
  width: 40px;
  height: 40px;
  padding: 0;
  margin-right: 0;
  justify-content: center;
}

/* === Header не "дёргается" пока мобильное меню открыто ====================
   _menu_top.php inline-script добавляет .scrolled на .site-header при
   scrollTop > 50 (визуальный shrink через _tts_header.scss). При открытом
   мобильном overlay юзер не должен видеть этот shrink — header должен
   оставаться в нормальном виде.

   JS-фикс уже есть (v3_showTag.php inline-script: body.position: fixed
   при open + двойной MutationObserver). Этот CSS-блок — defensive layer:
   если .scrolled всё-таки приклеился (race condition / другой код),
   визуально перекрываем все его эффекты возвратом к default-значениям
   из _tts_header.scss (header / inner / logo / nav / buttons). */
@media (max-width: 768px) {
  /* Запрещаем scroll body/html когда меню открыто — CSS-only слой защиты
     (дублирует JS body-lock в v3_showTag.php на случай если JS не успел). */
  html:has(.leftSupermenu.globm.expandMenu),
  body:has(.leftSupermenu.globm.expandMenu) {
    overflow: hidden !important;
    overscroll-behavior: contain !important;
    touch-action: none !important;
  }
  /* Header pinned. */
  body:has(.leftSupermenu.globm.expandMenu) header.site-header {
    position: fixed !important;
    top: 0 !important;
    left: 0 !important;
    right: 0 !important;
    z-index: 9999 !important;
  }
  /* Бургер при .closeMenu (меню открыто) — body locked, scrollbar исчез,
     viewport расширился на ~16px → бургер уехал правее. Компенсируем
     margin-right'ом ровно на ширину scrollbar. */
  .menuButton.soloMenu.closeMenu {
    margin-right: 16px !important;
  }
  /* Прячем visible scrollbar внутри overlay (.p20 имеет overflow-y: auto
     в legacy _nav.scss, всегда показывает scrollbar даже если контент
     помещается). Прячем визуально — scroll inside overlay сохраняется
     если контент длинный. */
  body:has(.leftSupermenu.globm.expandMenu) .leftSupermenu.expandMenu,
  body:has(.leftSupermenu.globm.expandMenu) .leftSupermenu.expandMenu * {
    scrollbar-width: none;
    -ms-overflow-style: none;
  }
  body:has(.leftSupermenu.globm.expandMenu) .leftSupermenu.expandMenu::-webkit-scrollbar,
  body:has(.leftSupermenu.globm.expandMenu) .leftSupermenu.expandMenu *::-webkit-scrollbar {
    display: none;
    width: 0;
    height: 0;
  }
  body:has(.leftSupermenu.globm.expandMenu) header.site-header.scrolled {
    background: #fff !important;
    box-shadow: none !important;
  }
  body:has(.leftSupermenu.globm.expandMenu) header.site-header.scrolled .header-inner {
    height: 3rem !important;
  }
  body:has(.leftSupermenu.globm.expandMenu) header.site-header.scrolled .logo img {
    height: 1.5rem !important;
  }
  body:has(.leftSupermenu.globm.expandMenu) header.site-header.scrolled .site-nav {
    gap: 2rem !important;
    font-size: 0.9375rem !important;
  }
  body:has(.leftSupermenu.globm.expandMenu) header.site-header.scrolled .button-login {
    padding: 0.5rem !important;
    font-size: 0.875rem !important;
  }
  body:has(.leftSupermenu.globm.expandMenu) header.site-header.scrolled .button-signup {
    padding: 0.1rem 1.25rem !important;
    font-size: 0.875rem !important;
  }
}
@media (max-width: 480px) {
  /* На совсем мелких — logo default 1.25rem (см. _tts_header.scss:163). */
  body:has(.leftSupermenu.globm.expandMenu) header.site-header.scrolled .logo img {
    height: 1.25rem !important;
  }
}
/* H1 — exact match with /rating/ + /speech/ */
.zg-v3 h1 {
  font-size: 28px;
  font-weight: 800;
  color: var(--zgcat-ink);
  letter-spacing: -0.03em;
  line-height: 1.2;
  margin: 4px 0 10px;
}

/* Description */
.zg-v3__desc {
  font-size: 14px;
  line-height: 1.55;
  color: var(--zgcat-muted);
  max-width: none;
  margin: 0 0 18px;
}

.zg-v3__desc p {
  margin: 0;
  display: block;
  max-width: 80%;
}

.zg-v3__desc p + p {
  margin-top: 6px;
}

/* Always show full first <p>; hide subsequent paragraphs until is-open */
.zg-v3__desc p:first-of-type {
  display: block;
  overflow: visible;
  -webkit-line-clamp: unset;
}

.zg-v3__desc:not(.is-open) p:nth-of-type(n+2) {
  display: none;
}

/* Description block as a flex row: text + small chevron icon on the right.
   The icon is a tiny muted square (no fill) with a chevron — no text label, no accent. */
.zg-v3__desc-wrap,
.zg-v3 > h1 + .zgcat-meta-strip + .zg-v3__desc {
  /* legacy targets in case wrap missing */
}

.zg-v3__desc {
  position: relative;
  padding-right: 0;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--zgcat-border-2);
  margin-bottom: 18px;
  cursor: default;
  max-width: none;
}

/* Pseudo-chevron is suppressed — real <button> .zgcat-desc-toggle handles all
   visuals + interaction. Keeping a no-op rule so CSS-only fallback (no JS) can
   re-enable it via .no-js scope if needed later. */
.zg-v3__desc::after {
  content: none;
}

.zg-v3__desc.is-open::after {
  content: none;
}

/* Real button (after JS restart): sits on top of pseudo, identical look + click */
.zg-v3 .zgcat-desc-toggle {
  position: absolute;
  right: 0;
  bottom: 8px;
  top: auto;
  margin: 0;
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font-size: 0;
  background: transparent;
  border: 1px solid var(--zgcat-border-2);
  border-radius: 6px;
  cursor: pointer;
  z-index: 2;
}

.zg-v3 .zgcat-desc-toggle::before {
  content: "";
  width: 12px;
  height: 12px;
  background: var(--zgcat-muted-2);
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'><polyline points='4 6 8 10 12 6'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'><polyline points='4 6 8 10 12 6'/></svg>") center/contain no-repeat;
  transition: transform 0.18s ease;
}

.zg-v3 .zgcat-desc-toggle::after {
  content: none;
}

.zg-v3 .zgcat-desc-toggle[aria-expanded=true]::before {
  transform: rotate(180deg);
}

/* Meta strip (tiny inline metadata) */
.zg-v3 .zgcat-meta-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin: 0 0 14px;
  font-size: 12px;
  color: var(--zgcat-muted-2);
}

.zg-v3 .zgcat-meta-strip .zgcat-meta-num {
  color: var(--zgcat-text);
  font-weight: 600;
}

.zg-v3 .zgcat-meta-strip .zgcat-meta-dot {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--zgcat-muted-3);
  display: inline-block;
}

/* Description top-right meta: update date + mood tags (compact two-line stack) */
.zg-v3 .zgcat-desc-meta {
  position: absolute;
  top: 0;
  right: 0;
  text-align: right;
  display: flex;
  flex-direction: column;
  gap: 2px;
  pointer-events: none;
  font-family: inherit;
  z-index: 1;
}

.zg-v3 .zgcat-desc-meta-date {
  font-size: 12px;
  font-weight: 500;
  color: var(--zgcat-text);
  letter-spacing: 0;
  line-height: 1.3;
  font-variant-numeric: tabular-nums;
}

.zg-v3 .zgcat-desc-meta-tags,
.zg-v3 .zgcat-desc-meta-downloads {
  font-size: 11px;
  font-weight: 400;
  color: var(--zgcat-muted);
  letter-spacing: 0.01em;
  line-height: 1.3;
}

/* =====================================================================
   2. Filter panel — white card (как на /rating/)
   ===================================================================== */
.zg-v3__panel {
  background: var(--zgcat-surface);
  border: 1px solid var(--zgcat-border);
  border-radius: var(--zgcat-radius);
  padding: 14px 16px;
  margin: 0 0 14px;
  box-shadow: var(--zgcat-shadow-card);
  display: flex;
  flex-direction: column;
  gap: 12px;
  position: relative;
  z-index: 30;
  overflow: visible;
}

.zg-v3__filters.zgcat-rows,
.zg-v3__filters.zgcat-rows .zgcat-row[data-row=secondary],
.zg-v3__filters.zgcat-rows .zgcat-fdrop {
  overflow: visible;
}

.zg-v3__tracks {
  position: relative;
  z-index: 1;
}

/* Sort row — only chips, NO label */
.zg-v3__sorts {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 0;
  margin: 0;
  background: transparent;
  border: 0;
  flex-wrap: wrap;
}

.zg-v3__sorts::before {
  content: none;
}

.zg-v3__sorts .zg-v3__btn {
  font-size: 13px;
  font-weight: 500;
  color: var(--zgcat-muted);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--zgcat-radius-sm);
  padding: 5px 12px;
  text-decoration: none;
  transition: all 0.15s;
  line-height: 1.3;
  margin: 0;
}

.zg-v3__sorts .zg-v3__btn:hover {
  color: var(--zgcat-text);
  background: var(--zgcat-surface-soft);
}

.zg-v3__sorts .zg-v3__btn.is-active {
  color: var(--zgcat-primary);
  background: var(--zgcat-primary-soft);
  font-weight: 600;
}

/* Filters container — flex-column */
.zg-v3__filters,
.zg-v3__filters.zgcat-rows {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 0;
  background: transparent;
  align-items: stretch;
  border: 0;
}

/* Separator above filter rows — only in non-groups modes (in groups mode the chip-nav serves
   as the visual divider between sort tabs and filter rows). */
body:not([data-zg-sort=groups]) .zg-v3__filters.zgcat-rows {
  border-top: 1px solid var(--zgcat-border);
  padding-top: 12px;
  margin-top: 2px;
}

/* ---------- Duration row — primary ---------- */
.zg-v3__filters.zgcat-rows .zgcat-row {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  padding: 0;
  border: 0;
  margin: 0;
  width: 100%;
}

/* Hide ALL row-labels — we no longer use ДЛИТЕЛЬНОСТЬ/ГРОМКОСТЬ/etc. text labels */
.zg-v3__filters.zgcat-rows .zgcat-row-label {
  display: none;
}

/* Duration chips — site standard */
.zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration] .zg-v3__btn,
.zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration] .zgcat-chip-all {
  font-size: 13px;
  font-weight: 500;
  padding: 5px 14px;
  background: var(--zgcat-surface);
  border: 1px solid var(--zgcat-border);
  border-radius: var(--zgcat-radius-sm);
  color: var(--zgcat-text);
  line-height: 1.3;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  transition: all 0.15s;
  width: auto;
  min-width: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  flex-basis: auto;
  letter-spacing: 0;
  text-transform: none;
  box-shadow: none;
}

.zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration] .zgcat-chip-all {
  border-color: transparent;
  background: transparent;
  color: var(--zgcat-muted);
}

.zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration] .zg-v3__btn:hover,
.zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration] .zgcat-chip-all:hover {
  border-color: rgba(46, 88, 239, 0.3);
  color: var(--zgcat-primary);
  background: var(--zgcat-surface);
}

/* Active — filled blue (как primary CTA на сайте) */
.zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration] .zg-v3__btn.is-active {
  background: var(--zgcat-primary);
  border-color: var(--zgcat-primary);
  color: #fff;
  font-weight: 600;
  box-shadow: 0 2px 6px rgba(46, 88, 239, 0.22);
}

.zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration] .zgcat-chip-all.is-active {
  background: var(--zgcat-primary-soft);
  border-color: rgba(46, 88, 239, 0.2);
  color: var(--zgcat-primary);
  font-weight: 600;
  box-shadow: none;
}

/* ---------- Sort row right-side controls: live counter + density toggle ---------- */
.zg-v3__sorts {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
}

.zg-v3__sorts .zgcat-sort-controls {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 14px;
  flex-shrink: 0;
}

/* DEAD-CODE removed: .zg-v3__sorts .zgcat-density (~2.2KB) — density-row
   рендерится в .zg-v3__filters, не в .zg-v3__sorts. Coverage подтвердил
   0 использований в music+sound × desktop+mobile. */
/* ---------- Favorite (★) button — sits between time and dwdWrap (secondary action) ---------- */
.zg-v3 .onetrack.accordion .zgcat-fav {
  grid-column: 5;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  margin: 0;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--zgcat-radius-sm);
  color: var(--zgcat-muted-2);
  cursor: pointer;
  transition: color 0.15s, background 0.15s, border-color 0.15s;
  font-family: inherit;
}

.zg-v3 .onetrack.accordion .zgcat-fav:hover {
  color: var(--zgcat-primary);
  background: var(--zgcat-primary-soft);
}

/* Активное «в избранном» состояние — зеркалим class .s1 со скрытого .addList
   (сайтовый JS ставит .s1 после успешного запроса list/update). */
.zg-v3 .onetrack.accordion:has(.addList.s1) .zgcat-fav {
  color: var(--zgcat-primary);
}

.zg-v3 .onetrack.accordion .zgcat-fav-icon {
  display: inline-block;
  width: 15px;
  height: 15px;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polygon points='12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polygon points='12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2'/></svg>") center/contain no-repeat;
}

.zg-v3 .onetrack.accordion:has(.addList.s1) .zgcat-fav .zgcat-fav-icon {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'><polygon points='12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'><polygon points='12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2'/></svg>") center/contain no-repeat;
}

/* ---------- Compact density mode ---------- */
body[data-zg-density=compact] .onetrack.accordion {
  height: 64px;
  min-height: 64px;
  max-height: 64px;
}

body[data-zg-density=compact] .onetrack.accordion .zgcat-trackmeta {
  display: none;
}

body[data-zg-density=compact] .onetrack.accordion .zgcat-titlewrap {
  gap: 0;
}

body[data-zg-density=compact] .zg-v3__list.trackList {
  gap: 4px;
}

/* Wave height stays at native 48px in compact mode — it's a rendered wavesurfer canvas
   and cannot be shrunk via CSS without clipping. Compact reduces row height by hiding
   trackmeta + tightening title gap, but the wave keeps its full size. */
/* ---------- Secondary row: Type + Loudness + Bitrate as popover dropdowns ----------
   Layout via CSS Grid: sort spans full width on top, duration + secondary share row 2. */
.zg-v3__filters.zgcat-rows {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  column-gap: 16px;
  row-gap: 10px;
}

.zg-v3__filters.zgcat-rows .zgcat-row[data-row=sort] {
  grid-column: 1/-1;
}

.zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration] {
  grid-column: 1;
  width: auto;
  padding-right: 0;
}

/* Duration + secondary в одной grid-строке. align-items: start — duration
   может wrap'нуться на несколько строк, и dropdowns должны быть выровнены
   по ВЕРХНЕЙ строке duration (не по центру всех строк). */
.zg-v3__filters.zgcat-rows {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: start;
  column-gap: 12px;
  row-gap: 10px;
}

.zg-v3__filters.zgcat-rows .zgcat-duration-heading {
  grid-column: 1/-1;
}

.zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration] {
  grid-column: 1;
  width: auto;
  padding-right: 0;
  flex-wrap: wrap;
}

/* Прогер удалил duration row, теперь secondary — единственная row с ВСЕМИ
   фильтрами включая Длительность (dropdown).
   Layout: span 1/-1, flex с content-fit dropdown'ами (без stretch).
   Left-cluster: Длительность, Теги, BPM.
   Right-cluster: Громкость, Битрейт, density (через margin-left: auto на
   первом правом элементе — толкает всё последующее вправо). */
.zg-v3__filters.zgcat-rows .zgcat-row[data-row=secondary] {
  grid-column: 1/-1;
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  padding: 0;
  border-top: 0;
  margin: 0;
  gap: 8px;
  align-items: center;
  position: static;
  transform: none;
  justify-self: stretch;
}

.zg-v3__filters.zgcat-rows .zgcat-row[data-row=secondary] .zgcat-fdrop-loudness {
  margin-left: auto;
}

/* На узких ширинах secondary стек'ом ПОД duration — иначе кнопки соприкасаются. */
@media (max-width: 768px) {
  .zg-v3__filters.zgcat-rows {
    grid-template-columns: 1fr;
  }
  .zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration],
  .zg-v3__filters.zgcat-rows .zgcat-row[data-row=secondary] {
    grid-column: 1;
    justify-self: start;
  }
  /* Admin-метка (track-id + delete) — desktop-only. На мобиле она мешает popup'у
     wav/m4r/ogg (один уровень bottom-right карточки) и не нужна — admin работает
     с десктопа. */
  .zg-v3 .onetrack.accordion .zgcat-track-admin {
    display: none !important;
  }
}
/* Громкость остаётся видимой на всех ширинах — юзер хочет доступ к фильтру
   даже на самых узких экранах. Margin-left:auto на density сохраняем, чтобы
   density-toggle оставался справа от Громкости. */
@media (max-width: 680px) {
  .zg-v3__filters.zgcat-rows .zgcat-row[data-row=secondary] .zgcat-density {
    margin-left: auto;
  }
}
/* Each filter dropdown group */
.zg-v3__filters.zgcat-rows .zgcat-fdrop {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  position: relative;
}

/* Toggle-кнопки дропдаунов (Громкость / Битрейт) — match с duration-чипами
   по геометрии. Отличаются ФОНОМ (light blue-gray) — визуальный signal
   «триггер группы фильтров» vs duration-чипы (прямые value-селекторы). */
.zg-v3__filters.zgcat-rows .zgcat-fdrop-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px;
  min-width: 100px;
  font-size: 13px;
  font-weight: 500;
  color: var(--zgcat-muted);
  background: #f8fafc; /* едва уловимый off-white */
  border: 1px solid var(--zgcat-border); /* тот же border что у chips */
  border-radius: var(--zgcat-radius-sm);
  cursor: pointer;
  line-height: 1.3;
  white-space: nowrap;
  transition: all 0.15s;
  font-family: inherit;
  letter-spacing: 0;
  text-transform: none;
}

.zg-v3__filters.zgcat-rows .zgcat-fdrop-toggle:hover {
  color: var(--zgcat-text);
  border-color: var(--zgcat-muted-3);
  background: #f1f5f9;
}

.zg-v3__filters.zgcat-rows .zgcat-fdrop.is-open .zgcat-fdrop-toggle,
.zg-v3__filters.zgcat-rows .zgcat-fdrop.is-active .zgcat-fdrop-toggle {
  color: var(--zgcat-primary);
  background: var(--zgcat-primary-soft);
  border-color: rgba(46, 88, 239, 0.25);
  font-weight: 600;
}

/* Group icons */
.zg-v3__filters.zgcat-rows .zgcat-fdrop-icon {
  display: inline-block;
  width: 12px;
  height: 12px;
  background-color: currentColor;
  flex-shrink: 0;
}

.zg-v3__filters.zgcat-rows .zgcat-fdrop-icon-grid {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='3' width='8' height='8' rx='1.5'/><rect x='13' y='3' width='8' height='8' rx='1.5'/><rect x='3' y='13' width='8' height='8' rx='1.5'/><rect x='13' y='13' width='8' height='8' rx='1.5'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='3' width='8' height='8' rx='1.5'/><rect x='13' y='3' width='8' height='8' rx='1.5'/><rect x='3' y='13' width='8' height='8' rx='1.5'/><rect x='13' y='13' width='8' height='8' rx='1.5'/></svg>") center/contain no-repeat;
}

/* DEAD-CODE removed: fdrop-icon-bars + fdrop-icon-speaker (~3KB) — иконки
   для loudness/bitrate, но эти fdrop-toggle не рендерят span.zgcat-fdrop-icon
   в PHP-шаблоне. Coverage подтвердил 0 использований. */
/* Tag icon — классический ценник/лейбл (наклонный прямоугольник + dot-hole). */
.zg-v3__filters.zgcat-rows .zgcat-fdrop-icon-tag {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z'/><circle cx='7' cy='7' r='1.5' fill='black'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z'/><circle cx='7' cy='7' r='1.5' fill='black'/></svg>") center/contain no-repeat;
}

.zg-v3__filters.zgcat-rows .zgcat-fdrop[data-fdrop=loudness] .zgcat-fdrop-icon,
.zg-v3__filters.zgcat-rows .zgcat-fdrop[data-fdrop=bitrate] .zgcat-fdrop-icon {
  display: none;
}

.zg-v3__filters.zgcat-rows .zgcat-fdrop-caret {
  display: inline-block;
  width: 8px;
  height: 8px;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>") center/contain no-repeat;
  transition: transform 0.18s ease;
  opacity: 0.55;
  flex-shrink: 0;
}

.zg-v3__filters.zgcat-rows .zgcat-fdrop.is-open .zgcat-fdrop-caret {
  transform: rotate(180deg);
  opacity: 1;
}

/* Chips area — popover dropdown anchored below the toggle, no layout shift */
.zg-v3__filters.zgcat-rows .zgcat-fdrop-chips {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  left: auto;
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  max-width: none;
  min-width: 100%;
  padding: 6px;
  background: var(--zgcat-surface);
  border: 1px solid var(--zgcat-border);
  border-radius: var(--zgcat-radius);
  box-shadow: 0 6px 18px -4px rgba(15, 23, 42, 0.12), 0 1px 3px rgba(15, 23, 42, 0.06);
  overflow: visible;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity 0.18s ease, transform 0.18s ease, visibility 0s linear 0.18s;
  pointer-events: none;
  margin: 0;
  white-space: nowrap;
  z-index: 100;
}

.zg-v3__filters.zgcat-rows .zgcat-fdrop.is-open .zgcat-fdrop-chips {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  pointer-events: auto;
  transition: opacity 0.18s ease, transform 0.22s cubic-bezier(0.2, 0.8, 0.3, 1), visibility 0s;
}

.zg-v3__filters.zgcat-rows .zgcat-fdrop-chips .zg-v3__btn,
.zg-v3__filters.zgcat-rows .zgcat-fdrop-chips .zgcat-chip-all {
  font-size: 11.5px;
  font-weight: 500;
  padding: 4px 9px;
  background: var(--zgcat-surface);
  border: 1px solid var(--zgcat-border);
  border-radius: var(--zgcat-radius-sm);
  color: var(--zgcat-muted);
  text-decoration: none;
  cursor: pointer;
  line-height: 1.3;
  white-space: nowrap;
  margin: 0;
  letter-spacing: 0;
  transition: all 0.12s;
  text-transform: none;
  width: auto;
  min-width: 0;
  display: inline-flex;
}

.zg-v3__filters.zgcat-rows .zgcat-fdrop-chips .zgcat-chip-all {
  border-color: transparent;
  background: transparent;
}

.zg-v3__filters.zgcat-rows .zgcat-fdrop-chips .zg-v3__btn:hover,
.zg-v3__filters.zgcat-rows .zgcat-fdrop-chips .zgcat-chip-all:hover {
  color: var(--zgcat-primary);
  border-color: rgba(46, 88, 239, 0.3);
  background: #fff;
}

.zg-v3__filters.zgcat-rows .zgcat-fdrop-chips .zg-v3__btn.is-active,
.zg-v3__filters.zgcat-rows .zgcat-fdrop-chips .zgcat-chip-all.is-active {
  background: var(--zgcat-primary-soft);
  color: var(--zgcat-primary);
  font-weight: 600;
  border-color: rgba(46, 88, 239, 0.25);
}

body[data-zg-sort=groups] .zg-v3__filters.zgcat-rows .zgcat-fdrop[data-fdrop=type] {
  display: none;
}

.zg-v3__filters.zgcat-rows .zgcat-fdrop[data-fdrop=type] .zgcat-fdrop-chips {
  flex-direction: column;
  align-items: stretch;
  width: max-content;
  min-width: 0;
  max-width: 240px;
}

.zg-v3__filters.zgcat-rows .zgcat-fdrop[data-fdrop=type] .zgcat-fdrop-chips .zg-v3__btn {
  padding: 2px 9px;
  line-height: 1.25;
  text-align: left;
  justify-content: flex-start;
}

/* Duration + BPM dropdowns: each option on its own row.
   Reason: duration ranges read better as a vertical scan (e.g. "0-5 / 5-10 /
   10-30 / 30-60 / 60+") and BPM is now multi-select so a stacked column
   makes the toggle state easier to scan visually. Mirrors the "type"
   dropdown vertical pattern above. */
.zg-v3__filters.zgcat-rows .zgcat-fdrop[data-fdrop=duration] .zgcat-fdrop-chips,
.zg-v3__filters.zgcat-rows .zgcat-fdrop[data-fdrop=bpm] .zgcat-fdrop-chips,
.zg-v3__filters.zgcat-rows .zgcat-fdrop[data-fdrop=loudness] .zgcat-fdrop-chips,
.zg-v3__filters.zgcat-rows .zgcat-fdrop[data-fdrop=bitrate] .zgcat-fdrop-chips {
  flex-direction: column;
  align-items: stretch;
  /* Width = ширине toggle-кнопки (родитель `.zgcat-fdrop` имеет position:relative).
     min-width:auto чтобы не растягивало шире trigger'а. Раньше base-rule выше
     был `flex-wrap: wrap` → chips собирались в горизонтальные ряды разной
     ширины (Все/Тихая на одной строке, Норм на другой и т.д.) — не единообразно. */
  width: 100%;
  min-width: auto;
  max-width: none;
}

.zg-v3__filters.zgcat-rows .zgcat-fdrop[data-fdrop=duration] .zgcat-fdrop-chips .zg-v3__btn,
.zg-v3__filters.zgcat-rows .zgcat-fdrop[data-fdrop=bpm] .zgcat-fdrop-chips .zg-v3__btn,
.zg-v3__filters.zgcat-rows .zgcat-fdrop[data-fdrop=loudness] .zgcat-fdrop-chips .zg-v3__btn,
.zg-v3__filters.zgcat-rows .zgcat-fdrop[data-fdrop=bitrate] .zgcat-fdrop-chips .zg-v3__btn {
  padding: 4px 9px;
  line-height: 1.25;
  text-align: left;
  justify-content: flex-start;
  width: 100%;
}

/* Hide redundant legacy type/group rows + pill labels. Type filter is rendered as a dropdown above. */
.zg-v3__filters.zgcat-rows .zgcat-row[data-row=type],
.zg-v3__filters.zgcat-rows .zgcat-row[data-row=group],
.zg-v3__filters .zg-v3__pill {
  display: none;
}

/* hide duration heading from earlier editorial pass */
.zg-v3 .zgcat-duration-heading {
  display: none;
}

/* Standalone «Похожие категории» секцию скрываем — переехала в meta-strip
   как inline-list (см. `.zgcat-meta-cats` ниже). PHP-side markup в
   v3_search.php остался ради SEO/fallback на случай отключения CSS. */
.zg-v3-search-categories {
  display: none;
}

/* Inline-список категорий в meta-strip. Намеренно приглушённый — читается
   как «фоновый» контекст («99 результатов · категории: ...»), а не как
   яркий CTA. Ссылки в slate-500 как и остальной текст strip'а, синий
   проявляется только на hover. */
.zgcat-meta-cats {
  display: inline;
  color: var(--zgcat-muted, #6c7390);
}

.zgcat-meta-cats-label {
  color: #475569;
  font-weight: 500;
  margin-right: 4px;
}

.zgcat-meta-cat {
  color: #64748b;
  text-transform: lowercase;
  text-decoration: none;
  border-bottom: 1px dotted #cbd5e1;
  transition: color 0.12s, border-color 0.12s;
}
.zgcat-meta-cat:hover {
  color: var(--pr-blue, #2e58ef);
  border-bottom-color: var(--pr-blue, #2e58ef);
  border-bottom-style: solid;
}

/* Suppress legacy ::before icons */
.zg-v3__filters.zgcat-rows .zgcat-chip-all::before,
.zg-v3__filters.zgcat-rows .zgcat-chip-all::after,
.zg-v3__filters.zgcat-rows .zg-v3__btn[data-value=""]::before,
.zg-v3__filters.zgcat-rows .zg-v3__btn[data-value=""]::after {
  content: none;
  display: none;
}

/* =====================================================================
   3. Track list — slim cards, DNA from /speech/ result player
   ===================================================================== */
.zg-v3__list.trackList {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 0 0 50px;
  margin: 10px 0 0;
  border: 0;
  counter-reset: zgcat-index;
}

/* Hard reset positioning */
.zg-v3 .onetrack.accordion,
.zg-v3 .onetrack.accordion *:not(canvas):not(wave):not(audio) {
  position: static;
  top: auto;
  left: auto;
  right: auto;
  bottom: auto;
  float: none;
}

.zg-v3 .onetrack.accordion {
  position: relative;
}

/* Each track row — TALLER, wave is the hero.
   ya-share2 (col 7 в DOM) скрыт display:none → исключаем из template.
   Без этого пустая 7-я колонка + gap создавали двойной отступ справа. */
.zg-v3 .onetrack.accordion {
  display: grid;
  grid-template-columns: 44px minmax(160px, 220px) minmax(0, 1fr) 44px auto auto; /* col 6: dwdWrap (mp3 + ▾) */
  align-items: center;
  gap: 14px;
  width: 100%;
  margin: 0;
  padding: 0 14px;
  background: var(--zgcat-surface);
  border: 1px solid var(--zgcat-border-2);
  border-radius: var(--zgcat-radius);
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.03);
  height: 102px;
  min-height: 102px;
  max-height: 102px;
  transition: background 0.15s;
  min-width: 0;
  counter-increment: zgcat-index;
  overflow: visible;
}

.zg-v3 .onetrack.accordion:hover {
  border-color: rgba(46, 88, 239, 0.3);
  box-shadow: 0 4px 14px -4px rgba(15, 23, 42, 0.08), 0 1px 2px rgba(15, 23, 42, 0.04);
  z-index: 2;
}

/* Flatten nested wrappers */
.zg-v3 .onetrack.accordion > .mainPlayer {
  display: contents;
}

.zg-v3 .onetrack.accordion .mainPlayer > .wrapElements {
  display: contents;
}

.zg-v3 .onetrack.accordion .mainPlayer .rightWavebox {
  display: contents;
}

/* Yandex ya-share2 widget — полностью скрыт. Шеринг идёт через свой popup
   .zgcat-share-pop (см. ниже), Yandex остаётся в DOM только для совместимости. */
.zg-v3 .onetrack.accordion {
  position: relative;
}

.zg-v3 .onetrack.accordion .ya-share2 {
  display: none;
}

/* === Custom share popup =================================================== */
/* Singleton popup, вставляется в body при первом клике на .zgcat-share-mini.
   Позиционируется через JS (position:fixed, координаты от getBoundingClientRect
   мини-кнопки). Открыт ↔ имеет класс .is-open. */
.zgcat-share-pop {
  position: fixed;
  z-index: 9999;
  min-width: 180px;
  padding: 6px;
  background: #fff;
  border: 1px solid var(--zgcat-border-2);
  border-radius: var(--zgcat-radius);
  box-shadow: 0 8px 24px -6px rgba(15, 23, 42, 0.18), 0 2px 6px rgba(15, 23, 42, 0.06);
  font-family: inherit;
  display: none;
}

.zgcat-share-pop.is-open {
  display: block;
}

.zgcat-share-pop__list {
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.zgcat-share-pop__item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  font-size: 13px;
  font-weight: 400;
  color: var(--zgcat-ink);
  background: transparent;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  width: 100%;
  text-align: left;
  font-family: inherit;
  transition: background-color 0.12s;
}

.zgcat-share-pop__item:hover {
  background: var(--zgcat-primary-soft);
  color: var(--zgcat-primary);
}

.zgcat-share-pop__icon {
  display: inline-flex;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--zgcat-line-soft, #f1f5f9);
}

.zgcat-share-pop__icon svg {
  width: 11px;
  height: 11px;
}

.zgcat-share-pop__item--vk .zgcat-share-pop__icon {
  background: #4a76a8;
  color: #fff;
}

.zgcat-share-pop__item--ok .zgcat-share-pop__icon {
  background: #ee8208;
  color: #fff;
}

.zgcat-share-pop__item--tg .zgcat-share-pop__icon {
  background: #2aabee;
  color: #fff;
}

.zgcat-share-pop__item--tw .zgcat-share-pop__icon {
  background: #0f1419;
  color: #fff;
}

.zgcat-share-pop__item--wa .zgcat-share-pop__icon {
  background: #25d366;
  color: #fff;
}

.zgcat-share-pop__item--copy .zgcat-share-pop__icon {
  background: var(--zgcat-line-soft, #f1f5f9);
  color: var(--zgcat-muted);
}

.zgcat-share-pop__item--copied {
  background: rgba(34, 197, 94, 0.1);
  color: #16a34a;
}

.zgcat-share-pop__item--copied .zgcat-share-pop__icon {
  background: #16a34a;
  color: #fff;
}

/* Mini share button rendered inside meta line */
.zg-v3 .onetrack.accordion .zgcat-share-mini {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin: 0 0 0 6px;
  padding: 2px;
  width: 18px;
  height: 18px;
  background: transparent;
  border: 0;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.15s;
  flex-shrink: 0;
  color: var(--zgcat-muted-2);
  font-family: inherit;
}

.zg-v3 .onetrack.accordion .zgcat-share-mini:hover {
  color: var(--zgcat-primary);
  background: var(--zgcat-primary-soft);
}

.zg-v3 .onetrack.accordion .zgcat-share-mini-icon {
  display: inline-block;
  width: 12px;
  height: 12px;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='18' cy='5' r='3'/><circle cx='6' cy='12' r='3'/><circle cx='18' cy='19' r='3'/><line x1='8.59' y1='13.51' x2='15.42' y2='17.49'/><line x1='15.41' y1='6.51' x2='8.59' y2='10.49'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='18' cy='5' r='3'/><circle cx='6' cy='12' r='3'/><circle cx='18' cy='19' r='3'/><line x1='8.59' y1='13.51' x2='15.42' y2='17.49'/><line x1='15.41' y1='6.51' x2='8.59' y2='10.49'/></svg>") center/contain no-repeat;
}

/* DEAD: блок про .zgcat-trackmeta .ya-share2 удалён — ya-share2 никогда не
   вкладывался внутрь нашей trackmeta плашки, эти селекторы ни на что не
   матчили. Виджет рендерится прямым ребёнком .onetrack (см. _v3_one_track*). */
/* Admin-only мини-блок: «id 12345» + микроиконка корзины.
   Показывается только в admin-партиалах (_v3_one_track_admin / _music_admin),
   мьютед-стиль чтобы не отвлекал от обычной meta-строки.
   В sound-партиале (.trackList без .longTracks) — справа от share, отделяется
   left-border. В music-партиале (.longTracks) — слева от size, поэтому
   стоит первым ребёнком .zgcat-trackmeta — правый border вместо левого. */
/* Admin-блок (id трека + кнопка удаления) — absolute снизу-справа карточки,
   видим только при hover. Не занимает место в grid'е, не мешает layout'у. */
.zg-v3 .onetrack.accordion .zgcat-track-admin {
  position: absolute;
  right: 8px;
  bottom: 6px;
  z-index: 5;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 6px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.95);
  font-size: 10px;
  line-height: 1;
  color: #94a3b8;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
}

.zg-v3 .onetrack.accordion:hover .zgcat-track-admin {
  opacity: 1;
  pointer-events: auto;
}

.zg-v3 .onetrack.accordion .zgcat-track-id {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}

.zg-v3 .onetrack.accordion .zgcat-track-del {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  color: #cbd5e1;
  opacity: 0.7;
  text-decoration: none;
  border-radius: 3px;
  transition: color 0.15s ease, background 0.15s ease, opacity 0.15s ease;
}
.zg-v3 .onetrack.accordion .zgcat-track-del:hover {
  color: #dc2626;
  opacity: 1;
  background: #fee2e2;
}
.zg-v3 .onetrack.accordion .zgcat-track-del svg {
  width: 10px;
  height: 10px;
  display: block;
}

/* Lock all to row 1 на десктопе. Без этого reset ★/title auto-place в row 2.
   Внутри @media (max-width: 1250) и (max-width: 768) элементы переопределяют
   row 1 → row 2 для wave-first 2-row layout. Specificity та же (0,3,1),
   media-rules идут позже в файле — побеждают каскадом. */
.zg-v3 .onetrack.accordion > .addList,
.zg-v3 .onetrack.accordion .playWrap,
.zg-v3 .onetrack.accordion .zgcat-titlewrap,
.zg-v3 .onetrack.accordion .waveTitle,
.zg-v3 .onetrack.accordion .waveWrap,
.zg-v3 .onetrack.accordion .waveTime,
.zg-v3 .onetrack.accordion .zgcat-fav,
.zg-v3 .onetrack.accordion .dwdWrap,
.zg-v3 .onetrack.accordion .boxLR {
  grid-row: 1;
}

/* === Play button — strict 40x40 circle === */
.zg-v3 .onetrack.accordion .playWrap {
  width: 40px;
  height: 40px;
  min-width: 40px;
  min-height: 40px;
  max-width: 40px;
  max-height: 40px;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
  grid-column: 1;
  flex-shrink: 0;
  flex-grow: 0;
  aspect-ratio: 1/1;
  align-self: center;
  justify-self: center;
}

.zg-v3 .onetrack.accordion .playBu {
  width: 40px;
  height: 40px;
  min-width: 40px;
  max-width: 40px;
  flex-shrink: 0;
  aspect-ratio: 1/1;
  border-radius: 50%;
  background-color: #f1f3f6;
  border: 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.15s ease;
  color: #1c2b39;
  position: relative;
}

.zg-v3 .onetrack.accordion .playBu::before {
  content: "";
  display: block;
  width: 0;
  height: 0;
  border-left: 9px solid currentColor;
  border-top: 6px solid transparent;
  border-bottom: 6px solid transparent;
  margin-left: 3px;
  transition: all 0.15s;
}

.zg-v3 .onetrack.accordion:hover .playBu,
.zg-v3 .onetrack.accordion .playBu:hover {
  background-color: var(--zgcat-primary);
  color: #fff;
}

/* Pause state — playing track. Site adds .Pause class. */
.zg-v3 .onetrack.accordion .playBu.Pause {
  background-color: var(--zgcat-primary);
  color: #fff;
}

.zg-v3 .onetrack.accordion .playBu.Pause::before {
  content: "";
  width: 10px;
  height: 12px;
  border: 0;
  margin-left: 0;
  background-color: currentColor;
  -webkit-mask: linear-gradient(currentColor, currentColor) left/3px 100% no-repeat, linear-gradient(currentColor, currentColor) right/3px 100% no-repeat;
  mask: linear-gradient(currentColor, currentColor) left/3px 100% no-repeat, linear-gradient(currentColor, currentColor) right/3px 100% no-repeat;
}

/* Loading state — кнопка остаётся серой, спиннер крутится primary-цветом */
.zg-v3 .onetrack.accordion .playBu.loading {
  cursor: wait;
}

.zg-v3 .onetrack.accordion .playBu.loading::before {
  content: "";
  width: 16px;
  height: 16px;
  border: 2px solid rgba(15, 23, 42, 0.12);
  border-top-color: var(--zgcat-primary);
  border-radius: 50%;
  margin: 0;
  background: transparent;
  -webkit-mask: none;
  mask: none;
  animation: zgcat-playBu-spin 0.7s linear infinite;
}

.zg-v3 .onetrack.accordion:hover .playBu.loading {
  background-color: #f1f3f6;
  color: #1c2b39;
}

@keyframes zgcat-playBu-spin {
  to {
    transform: rotate(360deg);
  }
}
/* === Title cell wrapper — col 2 — title (top) + meta (bottom) === */
.zg-v3 .onetrack.accordion .zgcat-titlewrap {
  grid-column: 2;
  grid-row: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  min-width: 0;
  width: 100%;
  max-width: 100%;
  overflow: visible;
  position: relative;
}

.zg-v3 .onetrack.accordion .waveTitle {
  font-size: 13px;
  font-weight: 400;
  color: var(--zgcat-text);
  white-space: normal;
  word-break: break-word;
  overflow: hidden;
  text-overflow: clip;
  margin: 0;
  padding: 0;
  line-height: 1.3;
  height: 1.3em;
  max-height: 1.3em;
  width: 100%;
  max-width: 100%;
  min-width: 0;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 1;
  line-clamp: 1;
  cursor: default;
  letter-spacing: 0;
  font-family: inherit;
  position: relative;
}

/* CSS-only hover expansion fallback — solid pop-in, no transform animation */
.zg-v3 .onetrack.accordion .zgcat-titlewrap:not(:has(.zgcat-title-tip)):hover .waveTitle {
  position: absolute;
  top: -5px;
  left: -9px;
  right: -9px;
  width: auto;
  max-width: none;
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  background: #fff;
  border: 1px solid var(--zgcat-border);
  border-radius: var(--zgcat-radius);
  padding: 4px 8px;
  box-shadow: 0 4px 12px -4px rgba(15, 23, 42, 0.1), 0 1px 2px rgba(15, 23, 42, 0.04);
  z-index: 200;
  word-break: break-word;
  text-align: left;
  transition-delay: 0.08s;
}

/* JS-injected tooltip — solid pop-in, no scale/grow animation */
.zg-v3 .onetrack.accordion .zgcat-title-tip {
  position: absolute;
  top: -5px;
  left: -9px;
  right: -9px;
  width: auto;
  z-index: 200;
  background: #fff;
  border: 1px solid var(--zgcat-border);
  border-radius: var(--zgcat-radius);
  padding: 4px 8px;
  font-size: 13px;
  font-weight: 400;
  line-height: 1.3;
  color: var(--zgcat-ink);
  text-align: left;
  white-space: normal;
  word-break: break-word;
  box-shadow: 0 4px 12px -4px rgba(15, 23, 42, 0.1), 0 1px 2px rgba(15, 23, 42, 0.04);
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.12s ease, visibility 0s linear 0.12s;
  letter-spacing: 0;
}

/* Триггер только на сам .waveTitle (не на всю обёртку), чтобы наведение на
   .zgcat-trackmeta / .zgcat-share-mini не вызывало tooltip полного заголовка.
   Tip — это sibling waveTitle (через .zgcat-trackmeta), `~` справляется. */
.zg-v3 .onetrack.accordion .waveTitle:hover ~ .zgcat-title-tip[data-show="1"] {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.12s ease, visibility 0s;
  transition-delay: 0.08s;
}

/* Metadata line UNDER title — size · downloads · bitrate */
.zg-v3 .onetrack.accordion .zgcat-trackmeta {
  font-size: 10.5px;
  font-weight: 400;
  color: var(--zgcat-muted-2);
  line-height: 1.1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: flex;
  align-items: center;
  gap: 5px;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
  font-family: inherit;
  margin: 0;
}

.zg-v3 .onetrack.accordion .zgcat-trackmeta .zgcat-trackmeta-icon {
  width: 10px;
  height: 10px;
}

.zg-v3 .onetrack.accordion .zgcat-trackmeta-sep {
  width: 2px;
  height: 2px;
  border-radius: 50%;
  background: var(--zgcat-muted-3);
  display: inline-block;
  flex-shrink: 0;
}

.zg-v3 .onetrack.accordion .zgcat-trackmeta-icon {
  width: 10px;
  height: 10px;
  background-color: currentColor;
  display: inline-block;
  flex-shrink: 0;
  margin-right: 2px;
}

.zg-v3 .onetrack.accordion .zgcat-trackmeta-icon[data-icon=size] {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'><rect x='4' y='3' width='16' height='18' rx='2'/><line x1='8' y1='8' x2='16' y2='8'/><line x1='8' y1='12' x2='16' y2='12'/><line x1='8' y1='16' x2='12' y2='16'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'><rect x='4' y='3' width='16' height='18' rx='2'/><line x1='8' y1='8' x2='16' y2='8'/><line x1='8' y1='12' x2='16' y2='12'/><line x1='8' y1='16' x2='12' y2='16'/></svg>") center/contain no-repeat;
}

/* `dl` icon: глаз/views (не путаем с download — для скачивания есть mp3-кнопка).
   !important на mask — sound prod-CSS с download-стрелкой бьёт по cascade. */
.zg-v3 .onetrack.accordion .zgcat-trackmeta-icon[data-icon=dl] {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z'/><circle cx='12' cy='12' r='3.5' fill='black' stroke='none'/></svg>") center/contain no-repeat !important;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z'/><circle cx='12' cy='12' r='3.5' fill='black' stroke='none'/></svg>") center/contain no-repeat !important;
}

.zg-v3 .onetrack.accordion .zgcat-trackmeta-icon[data-icon=bps] {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='10' width='3' height='11' rx='1'/><rect x='8.5' y='6' width='3' height='15' rx='1'/><rect x='14' y='2' width='3' height='19' rx='1'/><rect x='19.5' y='8' width='3' height='13' rx='1'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='10' width='3' height='11' rx='1'/><rect x='8.5' y='6' width='3' height='15' rx='1'/><rect x='14' y='2' width='3' height='19' rx='1'/><rect x='19.5' y='8' width='3' height='13' rx='1'/></svg>") center/contain no-repeat;
}

/* === Waveform — col 3 === */
.zg-v3 .onetrack.accordion .waveWrap {
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  grid-column: 3;
  min-width: 0;
}

.zg-v3 .onetrack.accordion .wave {
  width: 100%;
  /* height MUST match wavesurfer JS init — см. v2_faq.php:314 (height: 50).
     Иначе canvas (50px) рендерится в frame 48px и даёт 1-2px вертикальный
     offset gray vs pink overlay. */
  height: 50px;
  position: relative;
}

.zg-v3 .zg-v3__list.trackList .wave {
  margin-bottom: 0;
}

/* Start marker — left vertical line, mirrors the end-of-wave cursor visual */
.zg-v3 .onetrack.accordion .wave::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 1px;
  height: 100%;
  background: rgba(15, 23, 42, 0.2);
  z-index: 4;
  pointer-events: none;
}

/* End marker — right vertical line for symmetry */
.zg-v3 .onetrack.accordion .wave::after {
  content: "";
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 1px;
  height: 100%;
  background: rgba(15, 23, 42, 0.2);
  z-index: 4;
  pointer-events: none;
}

.zg-v3 .onetrack.accordion .wave > wave {
  /* height match JS init (50). width/display/position — от wavesurfer'а. */
  height: 50px;
  width: 100%;
}

/* DEAD: outer canvas — не трогаем, всё ставит wavesurfer. */
.zg-v3 .onetrack.accordion .wave {
  font-size: 0;
  line-height: 0;
}

/* Wavesurfer canvas filter — main waveform = last canvas inside outer wave.
   Solid charcoal (no transparency) by default; progress canvas stays pink. */
.zg-v3 .onetrack.accordion .wave > wave > canvas {
  filter: saturate(0) opacity(1);
  transition: filter 0.18s;
}

.zg-v3 .onetrack.accordion:hover .wave > wave > canvas {
  filter: saturate(0) brightness(0.92) opacity(1);
}

/* Inner progress canvas — только цвет/фильтр. Размеры/позицию ставит
   wavesurfer JS inline-styles. НЕ переопределяем height/display, иначе
   pixel-perfect overlay ломается (1px offset gray vs pink). */
/* Inner progress canvas — не трогаем layout, только filter/opacity. */
.zg-v3 .onetrack.accordion .wave > wave > wave > canvas {
  filter: none;
  opacity: 1;
}

/* Inner progress wave — только цвет playhead-cursor (border-right wavesurfer'а).
   НЕ переопределяем border-width/style/box-sizing — wavesurfer ставит сам. */
.zg-v3 .onetrack.accordion .wave > wave > wave {
  border-right-color: #0f172a !important;
}

/* DEAD: ::after pink cursor — мешал native border-right wavesurfer'а
   (который даёт тонкий серый playhead). Убран. */
/* === Time — col 4, mono-ish numerals === */
.zg-v3 .onetrack.accordion .waveTime {
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  color: var(--zgcat-muted);
  text-align: right;
  margin: 0;
  padding: 0;
  white-space: nowrap;
  letter-spacing: 0;
  font-weight: 500;
  grid-column: 4;
  font-family: inherit;
}

/* === Download group — MP3 + ▾ split-button popover for WAV/M4R/OGG === */
.zg-v3 .onetrack.accordion .dwdWrap {
  display: inline-flex;
  align-items: center;
  gap: 0;
  margin: 0;
  padding: 0;
  width: auto;
  height: auto;
  grid-column: 6;
  position: relative;
}

/* MP3 + toggle as a single split-button:
   MP3 has rounded left corners only; toggle has rounded right corners only.
   They share a single 1px divider line (toggle's left border). Toggle has subtle gray fill. */
.zg-v3 .onetrack.accordion .dwdWrap .dwdButtn[data-type=mp3] {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
  border-right-width: 0;
}

.zg-v3 .onetrack.accordion .zgcat-formats-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  align-self: stretch;
  width: 22px;
  height: auto;
  padding: 0;
  background: var(--zgcat-line-soft);
  border: 1px solid var(--zgcat-border);
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  border-top-right-radius: var(--zgcat-radius-sm);
  border-bottom-right-radius: var(--zgcat-radius-sm);
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
  font-family: inherit;
  color: var(--zgcat-muted);
  margin: 0;
  box-sizing: border-box;
}

.zg-v3 .onetrack.accordion .zgcat-formats-toggle:hover {
  background: #eef2f7;
  color: var(--zgcat-text);
}

.zg-v3 .onetrack.accordion .dwdWrap.is-open .zgcat-formats-toggle {
  background: var(--zgcat-primary-soft);
  border-color: rgba(46, 88, 239, 0.3);
  color: var(--zgcat-primary);
}

/* When MP3 hovered (gets primary fill), toggle adopts matching primary border */
.zg-v3 .onetrack.accordion .dwdButtn[data-type=mp3]:hover + .zgcat-formats-toggle {
  border-color: var(--zgcat-primary);
  border-left-color: rgba(255, 255, 255, 0.4);
}

.zg-v3 .onetrack.accordion .zgcat-formats-caret {
  display: inline-block;
  width: 9px;
  height: 9px;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>") center/contain no-repeat;
  transition: transform 0.18s;
}

.zg-v3 .onetrack.accordion .dwdWrap.is-open .zgcat-formats-caret {
  transform: rotate(180deg);
}

/* Popup with WAV/M4R/OGG */
.zg-v3 .onetrack.accordion .dwdWrap .zgcat-formats-popup {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  display: none;
  flex-direction: column;
  gap: 1px;
  background: #fff;
  border: 1px solid var(--zgcat-border);
  border-radius: var(--zgcat-radius);
  box-shadow: 0 8px 24px -8px rgba(15, 23, 42, 0.18), 0 1px 4px rgba(15, 23, 42, 0.04);
  padding: 4px;
  min-width: 124px;
  z-index: 50;
}

.zg-v3 .onetrack.accordion .dwdWrap.is-open .zgcat-formats-popup {
  display: flex;
}

.zg-v3 .onetrack.accordion .zgcat-formats-popup .dwdButtn {
  display: flex;
  align-items: center;
  width: 100%;
  padding: 8px 12px;
  font-size: 12px;
  font-weight: 500;
  text-align: left;
  background: transparent;
  border: 0;
  border-radius: var(--zgcat-radius-sm);
  color: var(--zgcat-text);
  cursor: pointer;
  transition: all 0.12s;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  line-height: 1;
  text-decoration: none;
  margin: 0;
  font-family: inherit;
}

.zg-v3 .onetrack.accordion .zgcat-formats-popup .dwdButtn:hover {
  background: var(--zgcat-primary-soft);
  color: var(--zgcat-primary);
}

/* All 4 formats visible. m4r/ogg styled compact (text-only mini-chips). */
/* MP3 download — calm outlined by default, primary blue only on hover */
.zg-v3 .onetrack.accordion .dwdButtn[data-type=mp3] {
  font-size: 12px;
  font-weight: 500;
  padding: 7px 12px;
  background: transparent;
  border: 1px solid var(--zgcat-border);
  border-radius: var(--zgcat-radius-sm);
  color: var(--zgcat-text);
  cursor: pointer;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  line-height: 1;
  transition: all 0.15s;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  width: auto;
  min-width: 0;
  height: auto;
  box-shadow: none;
  margin: 0;
  font-family: inherit;
}

.zg-v3 .onetrack.accordion .dwdButtn[data-type=mp3]:hover {
  background: var(--zgcat-primary);
  border-color: var(--zgcat-primary);
  color: #fff;
}

.zg-v3 .onetrack.accordion .dwdButtn[data-type=mp3]::before {
  content: "";
  width: 13px;
  height: 13px;
  background-color: currentColor;
  display: inline-block;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><path d='M12 3v13M5 11l7 7 7-7M3 21h18'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><path d='M12 3v13M5 11l7 7 7-7M3 21h18'/></svg>") center/contain no-repeat;
  flex-shrink: 0;
}

.zg-v3 .onetrack.accordion .dwdButtn[data-type=mp3]::after {
  content: none;
}

/* WAV/M4R/OGG — minimal text mini-chips inside .zgcat-formats-popup */
.zg-v3 .onetrack.accordion .dwdButtn[data-type=wav],
.zg-v3 .onetrack.accordion .dwdButtn[data-type=m4r],
.zg-v3 .onetrack.accordion .dwdButtn[data-type=ogg] {
  font-size: 11px;
  font-weight: 500;
  padding: 4px 7px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--zgcat-radius-sm);
  color: var(--zgcat-muted-2);
  cursor: pointer;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  line-height: 1;
  transition: all 0.12s;
  text-decoration: none;
  width: auto;
  min-width: 0;
  height: auto;
  box-shadow: none;
  margin: 0;
  font-family: inherit;
}

.zg-v3 .onetrack.accordion .dwdButtn[data-type=wav]:hover,
.zg-v3 .onetrack.accordion .dwdButtn[data-type=m4r]:hover,
.zg-v3 .onetrack.accordion .dwdButtn[data-type=ogg]:hover {
  color: var(--zgcat-primary);
  background: var(--zgcat-primary-soft);
}

.zg-v3 .onetrack.accordion .dwdButtn::before,
.zg-v3 .onetrack.accordion .dwdButtn::after {
  content: none;
}

/* Restore mp3 ::before */
.zg-v3 .onetrack.accordion .dwdButtn[data-type=mp3]::before {
  content: "";
}

/* Hide heart + expand chevron from the main row — keep DOM but visually drop */
.zg-v3 .onetrack.accordion > .addList,
.zg-v3 .onetrack.accordion > .boxLR {
  display: none;
}

/* DEAD-CODE removed: .boxL / .boxR — в HTML рендерится только .boxLR
   (см. _v3_one_track*.php). Coverage подтвердил 0 использований. */
/* =====================================================================
   3.5. Music tracks — physical 4-row layout
   ---------------------------------------------------------------------
   Music-партиал рендерится внутри `<div class="trackList longTracks">`,
   sound — без `longTracks`. Префикс `.longTracks` даёт specificity 0,4,1
   (vs sound 0,3,1) — точечно переопределяет только music-карточки.

   Layout (физические rows в grid):
     row 1: title (full-width)
     row 2: meta-полоска (size · dl · bps · share-mini)
     row 3: player — play | wave | time | ★ | mp3
     row 4: description (full-width, 2-line clamp)
   ===================================================================== */
/* NOTE про !important: десктоп-grid-overrides для sound-варианта (`.zg-v3
   .onetrack.accordion`) живут одним общим правилом и жёстко зажимают
   `grid-template-columns/rows` + `height`. `.longTracks`-селектор формально
   перевешивает (0,4,0 vs 0,3,0), но в живом cascade браузер для именно
   `grid-template-columns` и `height` упорно берёт sound-значения (rows/areas
   — забирает music). Стабильно воспроизводится; причина не выяснена.
   !important на спорных свойствах решает.

   Layout (3 row):
     row 1: titlewrap = flex space-between { title слева | meta+share справа }
     row 2: player = play | wave | time | ★ | mp3
     row 3: description (2-line clamp, text-align left) */
/* Концепция: text-block (title+description) → divider-space → player-row.
   Title не кричит размером, а присутствует. Description под title — единый
   читаемый блок. Player отделён от текста явным вертикальным gap'ом. */
.zg-v3 .longTracks .onetrack.accordion {
  display: grid;
  /* Compact controls: play 36, time 36, mp3 auto. ★ перенесён в top-right
     рядом с share через position:absolute (см. .zgcat-fav ниже). */
  grid-template-columns: 36px minmax(0, 1fr) 36px auto !important; /* col 4: mp3 */
  grid-template-rows: auto auto auto;
  grid-template-areas: "title title title title" "desc  desc  desc  desc" "play  wave  time  mp3";
  height: auto !important;
  min-height: 0 !important;
  max-height: none !important;
  column-gap: 10px;
  row-gap: 0;
  padding: 22px !important; /* симметрично — top/bottom одинаковые */
  align-items: center;
  transition: border-color 0.15s, box-shadow 0.15s, padding 0.15s;
  position: relative;
}

/* Compact режим для music: убираем description + stats-текст, оставляем
   share+fav кнопки. Уплотняем vertical padding и gap. */
body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion {
  padding: 13px 22px 15px !important; /* bottom +2px компенсирует оптику */
  /* Удаляем desc-row из grid template — иначе остаётся пустая дорожка. */
  grid-template-rows: auto auto !important;
  grid-template-areas: "title title title title" "play  wave  time  mp3" !important;
}

/* Compact: меньше жирка title, иконки share/fav уменьшены до 24px. */
body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion .waveTitle {
  font-size: 14.5px !important;
}

body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion .zgcat-share-mini,
body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion .zgcat-fav {
  width: 24px !important;
  height: 24px !important;
}

body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion .zgcat-share-mini-icon,
body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion .zgcat-fav-icon {
  width: 12px !important;
  height: 12px !important;
}

/* Sound compact rule (line 727) прячет всю trackmeta — для music нам нужны
   share+fav внутри trackmeta. Возвращаем display:flex. */
body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion .zgcat-trackmeta {
  display: flex !important;
}

/* Прячем только description и stats-текст/icons. Share-mini и fav buttons
   остаются (они с классами, не матчатся под :not([class])). */
body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion .zgcat-trackdesc,
body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion .zgcat-trackmeta-icon,
body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion .zgcat-trackmeta > span:not([class]) {
  display: none !important;
}

/* Compact: теги тоже скрываем — карточка фокусируется на title + player. */
body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion .zgcat-track-tags {
  display: none !important;
}

/* Admin-метки (track-id + delete) тоже прячем в compact — у них своё место. */
body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion .zgcat-track-admin {
  display: none !important;
}

/* В compact меньше gap'а title→player. */
body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion .playWrap,
body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion .waveWrap,
body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion .waveTime,
body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion .dwdWrap {
  margin-top: 8px !important;
}

/* Sound compact ставит height:64px на .onetrack.accordion (line 722) — для music
   с titlewrap+player это мало, ломает layout. Возвращаем auto. */
body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion {
  height: auto !important;
  min-height: 0 !important;
  max-height: none !important;
}

/* Card-to-card gap — карточки больше не слипаются. */
.zg-v3 .longTracks .onetrack.accordion + .onetrack.accordion {
  margin-top: 8px;
}

/* Hover: subtle lift — даёт ощущение «карточка интерактивна» без агрессии. */
.zg-v3 .longTracks .onetrack.accordion:hover {
  border-color: rgba(46, 88, 239, 0.3);
  box-shadow: 0 6px 20px -8px rgba(15, 23, 42, 0.1), 0 1px 2px rgba(15, 23, 42, 0.04);
}

/* Row 1 — titlewrap: title слева, stats+share справа. align baseline для
   чистого выравнивания цифр со строкой title. !important на grid-area:
   sound base ставит grid-column:2 + grid-row:1; longhand-cascade ломается
   (см. NOTE про cascade-аномалию выше) — без !important не побеждает. */
.zg-v3 .longTracks .onetrack.accordion .zgcat-titlewrap {
  grid-area: title !important;
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: baseline;
  gap: 20px;
  min-width: 0;
  width: 100%;
  margin: 0;
}

/* Title — средний вес, разумный размер. Не bold-shock, но явно главный.
   !important на font-size/weight/height/max-height/line-clamp/display:
   sound base правило `.waveTitle` для них значения через cascade-аномалию
   побеждает music selector 0,5,0 — без !important music не применяется. */
.zg-v3 .longTracks .onetrack.accordion .waveTitle {
  font-family: inherit;
  font-style: normal;
  /* 16/400 — title regular для music (раньше было 17/600). Category-prefix
     (`.zgcat-search-row-cat`) media-weight 500, так получается reverse-
     contrast: категория слегка жирнее title'а. !important нужен потому что
     site sound-base `.waveTitle` через cascade-аномалию побеждает music-
     specific selector с specificity 0,5,0. */
  font-size: 16px !important;
  font-weight: 400 !important;
  line-height: 1.35 !important;
  color: var(--zgcat-ink, #0f172a);
  letter-spacing: -0.005em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  height: auto !important;
  max-height: none !important;
  -webkit-line-clamp: 1 !important;
  line-clamp: 1 !important;
  display: block !important;
  max-width: 100%;
  flex: 1 1 auto;
  min-width: 0;
}

/* Stats meta — обычный inline flex без фона. Stats и кнопки разделены gap'ом. */
.zg-v3 .longTracks .onetrack.accordion .zgcat-trackmeta {
  font-size: 11px;
  font-weight: 400;
  color: var(--zgcat-muted);
  flex: 0 0 auto;
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 6px;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  padding: 0;
  background: transparent;
  border-radius: 0;
  height: auto;
}

.zg-v3 .longTracks .onetrack.accordion .zgcat-trackmeta .zgcat-trackmeta-icon {
  width: 12px;
  height: 12px;
  opacity: 0.75;
}

/* TEMP до деплоя нового PHP: прячем sep'ы которые приходят из старого
   _v3_one_track_music_admin.php (с точками-разделителями). Новый PHP их
   не выводит. После деплоя — удалить это правило. */
.zg-v3 .longTracks .onetrack.accordion .zgcat-trackmeta-sep {
  display: none !important;
}

/* Share-mini — отдельная кнопка-плашка с серым фоном. Specificity 0,5,0 vs
   sound 0,4,0 — music выигрывает без !important. */
.zg-v3 .longTracks .onetrack.accordion .zgcat-share-mini {
  width: 28px;
  height: 28px;
  margin-left: 6px;
  background: #f1f5f9;
  border-radius: 7px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  cursor: pointer;
  opacity: 0.85;
  transition: opacity 0.15s, background 0.15s;
}

.zg-v3 .longTracks .onetrack.accordion .zgcat-share-mini:hover {
  opacity: 1;
  background: #e2e8f0;
}

.zg-v3 .longTracks .onetrack.accordion .zgcat-share-mini-icon {
  width: 14px;
  height: 14px;
}

/* Row 2 — description ПЛОТНО под title (text-block continuum). Lighter color,
   не отвлекает но передаёт смысл. .zgcat-trackdesc — music-only элемент,
   sound его не рендерит, конфликтов нет.
   .onetrack.accordion должен быть position:relative для anchor абсолютного tip'а. */
.zg-v3 .longTracks .onetrack.accordion .zgcat-trackdesc {
  grid-area: desc;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.55;
  color: #6c7390;
  margin: 4px 0 0;
  padding: 0;
  border: 0;
  text-align: left;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
  word-break: break-word;
  max-width: 100%;
}

/* Hover-tooltip для обрезанного desc — тот же паттерн что у .zgcat-title-tip:
   PHP рендерит `<div class="zgcat-trackdesc-tip" data-show="0">{full text}</div>`
   рядом с trackdesc, JS detectTitleTruncation ставит data-show="1" если
   обрезано. CSS показывает на hover самой trackdesc.
   :has() — для матча parent .onetrack которое содержит truncated desc. */
/* Tip позиционирован В ТОЧНОСТЬ как desc (grid-area:desc + top:4px чтобы
   совпадало с margin-top:4px у desc) — первые 2 строки tip pixel-identical
   к видимым строкам desc. Появление = «desc развернулся вниз», без сдвига.
   Отличие только: opaque background (перекрывает player-row под собой).
   Никаких border/padding/borderRadius — они бы сместили текст. */
.zg-v3 .longTracks .onetrack.accordion .zgcat-trackdesc-tip {
  grid-area: desc;
  position: absolute;
  top: 4px; /* match desc { margin-top: 4px } — pixel alignment */
  left: 0;
  right: 0;
  z-index: 100;
  background: #fff;
  border: 0;
  border-radius: 0;
  padding: 0;
  margin: 0;
  font-size: 14px; /* match desc — pixel alignment первых 2 строк */
  font-weight: 400; /* match desc */
  line-height: 1.55; /* match desc */
  color: #6c7390; /* match desc */
  text-align: left;
  white-space: normal;
  word-break: break-word;
  /* subtle тень снизу — намёк на «расширение», но не визуальный shift */
  box-shadow: 0 6px 12px -6px rgba(15, 23, 42, 0.12);
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.12s ease, visibility 0s linear 0.12s;
}

.zg-v3 .longTracks .onetrack.accordion .zgcat-trackdesc[data-truncated="1"]:hover ~ .zgcat-trackdesc-tip[data-show="1"] {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.12s ease, visibility 0s;
  transition-delay: 0.08s;
}

/* Row 3 — player. Compact: 36px play, wave full width, 36px time, auto mp3.
   margin-top 18px = визуальный divider от text-block. */
/* playWrap/playBu — !important на размерах ОБЯЗАТЕЛЬНО: sound base ставит
   width/height/min/max 40px и `aspect-ratio: 1/1` без important; без них
   music 36px не выигрывает cascade (cascade-аномалия с .longTracks-префиксом
   на shorthand-properties). border-radius — без important, sound тоже 50%. */
.zg-v3 .longTracks .onetrack.accordion .playWrap {
  grid-area: play !important;
  margin-top: 18px;
  width: 36px !important;
  height: 36px !important;
  min-width: 36px !important;
  min-height: 36px !important;
  max-width: 36px !important;
  max-height: 36px !important;
  border-radius: 50%;
}

.zg-v3 .longTracks .onetrack.accordion .playBu {
  width: 36px !important;
  height: 36px !important;
  min-width: 36px !important;
  min-height: 36px !important;
  max-width: 36px !important;
  max-height: 36px !important;
  aspect-ratio: 1/1 !important;
  border-radius: 8px;
}

.zg-v3 .longTracks .onetrack.accordion .waveWrap {
  grid-area: wave !important;
  min-width: 0;
  margin-top: 18px;
}

/* Time — отдельная col рядом с wave. !important на grid-area: cascade-аномалия. */
.zg-v3 .longTracks .onetrack.accordion .waveTime {
  grid-area: time !important;
  margin-top: 18px;
  font-size: 12px;
  font-weight: 500;
  color: var(--zgcat-muted);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  justify-self: end;
  text-align: right;
}

.zg-v3 .longTracks .onetrack.accordion .dwdWrap {
  grid-area: mp3 !important;
  margin-top: 18px;
}

/* MP3-кнопка чуть компактнее в music-варианте. */
.zg-v3 .longTracks .onetrack.accordion .dwdButtn[data-type=mp3] {
  padding: 6px 12px;
  font-size: 12.5px;
}

/* ★ favorite — отдельная кнопка-плашка, аналог share. Specificity 0,5,0
   победит sound 0,4,0 без !important. grid-area: unset нужен — sound ставит
   grid-column: 5; в music ★ flex-ребёнок trackmeta, не grid-item. */
.zg-v3 .longTracks .onetrack.accordion .zgcat-fav {
  grid-area: unset;
  margin: 0;
  width: 28px;
  height: 28px;
  background: #f1f5f9;
  border-radius: 7px;
  opacity: 0.85;
  transition: opacity 0.15s, background 0.15s;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  cursor: pointer;
  padding: 0;
}

.zg-v3 .longTracks .onetrack.accordion .zgcat-fav:hover {
  opacity: 1;
  background: #e2e8f0;
}

.zg-v3 .longTracks .onetrack.accordion .zgcat-fav-icon {
  width: 14px;
  height: 14px;
}

/* ★ active state (.s1) — заливаем blue */
.zg-v3 .longTracks .onetrack.accordion:has(.addList.s1) .zgcat-fav {
  opacity: 1;
}

.zg-v3 .longTracks .onetrack.accordion:has(.addList.s1) .zgcat-fav .zgcat-fav-icon {
  background-color: var(--zgcat-primary, #2e58ef);
}

/* Скрытые легаси элементы — убрать из grid sizing.
   Music: описание уже в row 4 (.zgcat-trackdesc), .podrobno-блок не нужен,
   .boxLR (toggle chevron) не нужен. Прячем display:none. */
.zg-v3 .longTracks .onetrack.accordion > .podrobno,
.zg-v3 .longTracks .onetrack.accordion > .boxLR,
.zg-v3 .longTracks .onetrack.accordion > .ya-share2 {
  display: none !important;
}

/* =====================================================================
   3.6. Music tracks — адаптивные правила
   ---------------------------------------------------------------------
   Брейкпоинты: 1100 (laptop с сайдбаром), 768 (planshet/phone), 480 (phone).
   Стратегия: уменьшаем padding/font/control-sizes последовательно;
   stats text/icons прячем рано (≤1100), share+fav кнопки остаются всегда
   как primary actions.
   ===================================================================== */
@media (max-width: 1100px) {
  .zg-v3 .longTracks .onetrack.accordion {
    padding: 18px !important;
  }
  .zg-v3 .longTracks .onetrack.accordion .waveTitle {
    font-size: 14.5px !important;
  }
  /* Stats text и icons прячем — на узком они теснят title. Кнопки share+fav
     остаются (с собственными классами, под :not([class]) не попадают). */
  .zg-v3 .longTracks .onetrack.accordion .zgcat-trackmeta-icon,
  .zg-v3 .longTracks .onetrack.accordion .zgcat-trackmeta > span:not([class]) {
    display: none;
  }
}
@media (max-width: 768px) {
  .zg-v3 .longTracks .onetrack.accordion {
    padding: 14px !important;
    column-gap: 10px;
  }
  .zg-v3 .longTracks .onetrack.accordion .waveTitle {
    font-size: 14px !important;
  }
  .zg-v3 .longTracks .onetrack.accordion .zgcat-trackdesc {
    font-size: 12.5px;
    line-height: 1.45;
    margin-top: 3px;
  }
  /* Меньше play + share + fav для phone-screen */
  .zg-v3 .longTracks .onetrack.accordion .playWrap,
  .zg-v3 .longTracks .onetrack.accordion .playBu {
    width: 32px !important;
    height: 32px !important;
    min-width: 32px !important;
    min-height: 32px !important;
    max-width: 32px !important;
    max-height: 32px !important;
  }
  .zg-v3 .longTracks .onetrack.accordion .zgcat-share-mini,
  .zg-v3 .longTracks .onetrack.accordion .zgcat-fav {
    width: 24px;
    height: 24px;
  }
  .zg-v3 .longTracks .onetrack.accordion .zgcat-share-mini-icon,
  .zg-v3 .longTracks .onetrack.accordion .zgcat-fav-icon {
    width: 12px;
    height: 12px;
  }
  /* Меньше gap text-block → player */
  .zg-v3 .longTracks .onetrack.accordion .playWrap,
  .zg-v3 .longTracks .onetrack.accordion .waveWrap,
  .zg-v3 .longTracks .onetrack.accordion .waveTime,
  .zg-v3 .longTracks .onetrack.accordion .dwdWrap {
    margin-top: 12px;
  }
  /* Compact play col */
  .zg-v3 .longTracks .onetrack.accordion {
    grid-template-columns: 32px minmax(0, 1fr) 32px auto !important;
  }
}
/* ≤520: phone — time переезжает absolute в title-row перед share+★.
   Снизу mp3 и три-точки = единый элемент (верхние углы у mp3, нижние у ⋯). */
@media (max-width: 520px) {
  .zg-v3 .longTracks .onetrack.accordion {
    padding: 12px !important;
    grid-template-columns: 32px minmax(0, 1fr) auto !important;
    grid-template-areas: "title title title" "desc  desc  desc" "play  wave  ctrl" !important;
    column-gap: 8px;
    position: relative; /* anchor для absolute waveTime */
  }
  .zg-v3 .longTracks .onetrack.accordion .waveTitle {
    font-size: 13.5px !important;
  }
  /* На ≤520 в title-row плавают разноразмерные элементы (title 13.5, time 11.5,
     buttons 24×24). Baseline-alignment даёт визуальный jiggle. Переходим
     на center — все 4 элемента центруются по горизонтали-середине строки. */
  .zg-v3 .longTracks .onetrack.accordion .zgcat-titlewrap {
    align-items: center;
  }
  .zg-v3 .longTracks .onetrack.accordion .zgcat-trackdesc {
    font-size: 12.5px;
    margin-top: 3px;
    /* 2-line clamp оставляем как в default — desc важен для понимания трека. */
  }
  /* Time → absolute в title-row, ровно справа (share+★ перенеслись на строку
     с trackmeta под title — место для них не резервируем). */
  .zg-v3 .longTracks .onetrack.accordion .waveTime {
    grid-area: unset !important;
    position: absolute;
    top: 12px; /* match padding-top карточки */
    right: 12px; /* flush right — match padding-right карточки */
    height: 24px;
    align-self: flex-start;
    display: flex;
    align-items: center; /* vertical center text внутри 24px box */
    margin: 0 !important;
    font-size: 11.5px !important;
    line-height: 1;
    color: var(--zgcat-muted);
    font-variant-numeric: tabular-nums;
    z-index: 2;
    pointer-events: none;
  }
  /* rightWavebox — теперь содержит только dwdWrap (time absolute, ушёл).
     overflow: visible — обязательно: legacy `_grids.scss` ставит overflow:hidden
     на .rightWavebox, что для sound безвредно (там display:contents — box не
     создаётся), а для music ≤520 (где мы переключаем на display:flex) клиппит
     .zgcat-formats-popup (он position:absolute внутри .dwdWrap и должен выйти
     за пределы 62×50 box rightWavebox). */
  .zg-v3 .longTracks .onetrack.accordion .rightWavebox {
    display: flex !important;
    flex-direction: column !important;
    align-items: stretch;
    justify-content: center;
    grid-area: ctrl !important;
    margin-top: 12px;
    height: 50px;
    gap: 0;
    overflow: visible !important;
  }
  /* Popup wav/m4r/ogg — на ≤520 сдвигаем right на -4px, чтобы правый край
     popup'а совпал с правым краем .waveTime следующего трека (12px от карточки).
     Иначе из-под popup'а торчит ~4px полоска цифр времени соседа. */
  .zg-v3 .longTracks .onetrack.accordion .dwdWrap .zgcat-formats-popup {
    right: -4px;
  }
  /* dwdWrap — flex-col, mp3 и ⋯ касаются (gap:0), читаются как один блок. */
  .zg-v3 .longTracks .onetrack.accordion .dwdWrap {
    grid-area: unset !important;
    margin-top: 0;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    flex: 1 1 auto;
  }
  /* mp3 — верхние углы скруглены, нижние плоские (соединение с ⋯).
     border-bottom: 0 — на стыке только chev'а border-top.
     border-right-width: 1px — отменяем дефолтный split-button override (где
     mp3 теряет правый border т.к. соседствует с chev). flex: 1 — кнопка
     заполняет всё свободное пространство rightWavebox (50px минус 14px chev). */
  .zg-v3 .longTracks .onetrack.accordion .dwdWrap .dwdButtn[data-type=mp3] {
    padding: 4px 10px;
    font-size: 11px;
    line-height: 1.2;
    text-align: center;
    justify-content: center;
    border-radius: var(--zgcat-radius-sm) var(--zgcat-radius-sm) 0 0;
    border-bottom: 0;
    border-right-width: 1px;
    flex: 1 1 auto;
  }
  /* ⋯ кнопка — нижние углы скруглены, верхний border общий с mp3.
     Three-dot icon вместо chevron — современнее, читается как «ещё». */
  .zg-v3 .longTracks .onetrack.accordion .zgcat-formats-toggle {
    width: 100%;
    height: 14px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 0 0 var(--zgcat-radius-sm) var(--zgcat-radius-sm);
    border: 1px solid var(--zgcat-border);
  }
  .zg-v3 .longTracks .onetrack.accordion .zgcat-formats-caret {
    width: 12px;
    height: 12px;
    -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='5' cy='12' r='2'/><circle cx='12' cy='12' r='2'/><circle cx='19' cy='12' r='2'/></svg>") center/contain no-repeat;
    mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='5' cy='12' r='2'/><circle cx='12' cy='12' r='2'/><circle cx='19' cy='12' r='2'/></svg>") center/contain no-repeat;
    transform: none; /* убираем rotate — это не chevron больше */
  }
  .zg-v3 .longTracks .onetrack.accordion .dwdWrap.is-open .zgcat-formats-caret {
    transform: none;
  }
  /* text-block → player gap чуть меньше */
  .zg-v3 .longTracks .onetrack.accordion .playWrap,
  .zg-v3 .longTracks .onetrack.accordion .waveWrap {
    margin-top: 12px;
  }
  /* COMPACT + MOBILE row 1: title (shrink/ellipsis) | 02:32 | share | ★
     waveTime absolute right:70px (= padding 12 + share 24 + gap + ★ 24).
     trackmeta inline в углу right:12px (margin-right:0 = к самому краю).
     Таймер визуально сидит ЛЕВЕЕ share+★. */
  body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion .zgcat-titlewrap {
    flex-wrap: nowrap;
    align-items: center;
    gap: 8px;
  }
  body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion .zgcat-titlewrap .waveTitle {
    flex: 0 1 auto;
    flex-basis: auto;
    padding-right: 0;
    min-width: 0;
  }
  body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion .zgcat-trackmeta {
    flex: 0 0 auto;
    margin-left: auto;
    margin-right: 0; /* trackmeta (share + ★) сидит у самого правого края */
  }
  body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion .waveTime {
    right: 86px; /* gap до share+★ кластера; 12pad + 54buttons + 20gap = 86 */
    top: 12px;
    margin-top: 0 !important; /* override compact-rule margin-top: 8 которая смещала вниз */
    height: 24px;
    display: flex;
    align-items: center;
  }
  /* COMPACT + MOBILE row 2: play | wave | mp3 — mp3 НЕ должна уезжать на row 3.
     Compact-grid имеет 4 cols "play wave time mp3" — переключаем rightWavebox
     с дефолтного `grid-area: ctrl` (3-col mobile-grid) на `mp3` (compact-grid). */
  body[data-zg-density=compact] .zg-v3 .longTracks .onetrack.accordion .rightWavebox {
    grid-area: mp3 !important;
  }
}
/* =====================================================================
   4. Responsive — 1250 / 768 / 400
   ---------------------------------------------------------------------
   Wave — главный элемент. На широком десктопе (>1250) — оставляем как есть.
   С ≤1250 включается compact: wave переезжает на row 1 во всю ширину,
   контролы (play+title+time+★+mp3) — на row 2 под ней. Meta-строка
   (size/dl/bps · share-mini) скрывается. Это освобождает максимум места
   под визуально крупную звуковую волну.
   На ≤768 — phone-stack (то же что compact + меньшие paddings/scrolling
   sort/duration rows + density off).
   ===================================================================== */
/* ---- ≤1250px : compact wave-first layout.
   Срабатывает рано (1250 а не 1040) потому что на 1071-1250 при наличии
   сайдбара контента остаётся ~700-900px и в десктоп-grid с title+wave+controls
   wave получает мизерную полосу. Лучше отдать ей всю ширину карточки. ---- */
@media (max-width: 1250px) {
  .zg-v3 {
    padding: 18px 14px 22px;
  }
  .zg-v3 h1 {
    font-size: 23px;
  }
  .zg-v3__desc {
    font-size: 13.5px;
    margin-bottom: 14px;
  }
  .zg-v3__desc p {
    max-width: 100%;
  }
  /* desc-meta (Обновлено / Скачано) — на узких в normal flow, строкой под текстом.
     На десктопе она absolute top-right; на ≤1040 текст использует всю ширину
     и meta наезжала бы — поэтому возвращаем её в поток. */
  .zg-v3 .zgcat-desc-meta {
    position: static;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    text-align: left;
    margin-top: 8px;
    gap: 4px 12px;
    padding-right: 36px; /* место для chevron-кнопки развернуть */
  }
  .zg-v3__panel {
    padding: 11px 13px;
  }
  .zg-v3__sorts .zg-v3__btn {
    font-size: 12.5px;
    padding: 4px 10px;
  }
  /* Density toggle бессмыслен на узких — у нас итак 2-row compact layout.
     Раньше скрывали ВСЁ .zgcat-sort-controls (включая trigger «База»),
     теперь оставляем trigger видимым на всех ширинах. */
  .zg-v3__sorts .zgcat-sort-controls {
    display: inline-flex;
  }
  /* Если из cookie прилетел density="compact" — игнорируем сжатие высоты,
     иначе wave на row 2 наложится на следующую карточку. */
  body[data-zg-density=compact] .zg-v3 .onetrack.accordion {
    height: auto;
    min-height: 0;
    max-height: none;
  }
  body[data-zg-density=compact] .zg-v3 .onetrack.accordion .zgcat-trackmeta {
    display: none;
  }
  .zg-v3__filters.zgcat-rows {
    column-gap: 12px;
    row-gap: 10px;
  }
  .zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration] .zg-v3__btn,
  .zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration] .zgcat-chip-all {
    padding: 5px 11px;
    font-size: 12.5px;
  }
  /* Match fdrop-toggle на ≤1250 — duration и dropdown одного размера. */
  .zg-v3__filters.zgcat-rows .zgcat-fdrop-toggle {
    padding: 5px 11px;
    font-size: 12.5px;
  }
  .zg-v3__filters.zgcat-rows .zgcat-row[data-row=secondary] {
    position: static;
    transform: none;
    top: auto;
    right: auto;
    justify-self: stretch;
    flex-wrap: wrap;
  }
  .zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration] {
    padding-right: 0;
  }
  /* Groups chip-strip — только padding/margin тут.
     Кол-во колонок остаётся base (var(--zg-cols, 4)) на 920-1040 (контент
     достаточен для 3-4 chips в ряд). Переход в 2 кол — отдельный media ниже. */
  body[data-zg-sort=groups] .zg-v3__groups {
    margin: 0 -13px 6px;
    padding: 8px 13px;
  }
  .zg-v3__section > h2 {
    font-size: 16px;
    gap: 10px;
  }
  .zg-v3__section > h2::before {
    font-size: 14px;
  }
  .zg-v3__section > h2 .zgcat-section-count {
    font-size: 11px;
  }
  /* Track row — play+wave на одной строке, заголовок и controls — над ней:
     row 1: title (1fr) | time | ★ | mp3-split  — meta-полоска заголовка
     row 2: play | wave (1fr)                    — главная "плеер"-строка
     Так wave получает всю ширину минус play, и play визуально "живёт" с wave
     как единая управляющая зона. */
  /* Layout B:
       Row 1: Title (col 1/3 от лев. края)  ★    │  00:12
       Row 2:  ▶  │  ≈≈ wave ≈≈≈                 │  ↓ MP3
     Title начинается с самого левого края, под ним play. */
  .zg-v3 .onetrack.accordion {
    grid-template-columns: 40px auto auto minmax(0, 1fr) auto; /* col 5: time row 1 / mp3 row 2 */
    grid-template-rows: auto auto;
    gap: 8px 8px; /* row-gap 8 — wave чуть ниже от заголовка */
    padding: 4px 10px 8px; /* top меньше bottom: title прибит к верху */
    height: auto;
    min-height: 0;
    max-height: none;
    align-items: center;
  }
  /* Row 1 — title с col 1 от лев. края, ★ inline, time правый край */
  .zg-v3 .onetrack.accordion .zgcat-titlewrap {
    grid-column: 1/3;
    grid-row: 1;
    min-width: 0;
    align-self: center;
  }
  .zg-v3 .onetrack.accordion .zgcat-fav {
    grid-column: 3;
    grid-row: 1;
    align-self: center;
  }
  .zg-v3 .onetrack.accordion .waveTime {
    grid-column: 5;
    grid-row: 1;
    justify-self: end;
    align-self: center;
  }
  /* Row 2 */
  .zg-v3 .onetrack.accordion .playWrap {
    grid-column: 1;
    grid-row: 2;
  }
  .zg-v3 .onetrack.accordion .waveWrap {
    grid-column: 2/5;
    grid-row: 2;
  }
  .zg-v3 .onetrack.accordion .dwdWrap {
    grid-column: 5;
    grid-row: 2;
    justify-self: end;
    align-self: center;
  }
  /* Title — single line ellipsis */
  .zg-v3 .onetrack.accordion .zgcat-titlewrap {
    align-self: center;
  }
  .zg-v3 .onetrack.accordion .waveTitle {
    font-size: 13px;
    height: auto;
    max-height: none;
    line-height: 1.3;
    -webkit-line-clamp: 1;
    line-clamp: 1;
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
    display: block;
  }
  /* Meta-строка скрыта */
  .zg-v3 .onetrack.accordion .zgcat-trackmeta {
    display: none;
  }
  /* Контролы — mini */
  .zg-v3 .onetrack.accordion .playWrap,
  .zg-v3 .onetrack.accordion .playBu {
    width: 36px;
    height: 36px;
    min-width: 36px;
    max-width: 36px;
    min-height: 36px;
    max-height: 36px;
  }
  .zg-v3 .onetrack.accordion .waveTime {
    font-size: 11.5px;
    padding: 0;
    align-self: center;
  }
  .zg-v3 .onetrack.accordion .zgcat-fav {
    width: 22px;
    height: 22px;
  }
  .zg-v3 .onetrack.accordion .dwdButtn[data-type=mp3] {
    padding: 5px 10px;
    font-size: 12px;
    min-width: 0;
  }
  .zg-v3 .onetrack.accordion .zgcat-formats-toggle {
    width: 20px;
  }
  .zg-v3 .onetrack.accordion .zgcat-title-tip {
    left: -6px;
    right: -6px;
  }
}
/* ---- ≤920px : контент с сайдбаром ~580px — переключаем chip-strip групп
   на 2 колонки. Длинные названия и так с ellipsis. ---- */
@media (max-width: 920px) {
  body[data-zg-sort=groups] .zg-v3__groups {
    grid-template-columns: repeat(2, 1fr);
  }
}
/* ---- ≤768px : phone — full stack, density off, scrolling sort/duration ---- */
@media (max-width: 768px) {
  .zg-v3 {
    padding: 8px 12px 22px;
  }
  .zg-v3 h1 {
    font-size: 21px;
    margin: 2px 0 8px;
  }
  .zg-v3__crumbs .breadcrumb {
    font-size: 12px;
  }
  .zg-v3__desc {
    font-size: 13px;
    padding-right: 36px;
    margin-bottom: 12px;
  }
  .zg-v3__desc::after,
  .zg-v3 .zgcat-desc-toggle {
    width: 26px;
    height: 26px;
    bottom: 6px;
  }
  /* Mobile: clamp first paragraph to 4 lines when collapsed.
     Длинный одиночный абзац (например на /category/geroicheskaya/) занимал
     8+ строк и сильно сдвигал контент вниз. Разворот по chevron возвращает
     полный текст. max-width:80% c desktop отменяем — на узком экране это
     режет 1-2 строки текста под пустоту справа от chevron'а. */
  .zg-v3__desc p:first-of-type {
    max-width: none;
  }
  .zg-v3__desc:not(.is-open) p:first-of-type {
    display: -webkit-box;
    -webkit-line-clamp: 4;
    line-clamp: 4;
    -webkit-box-orient: vertical;
    overflow: hidden;
    /* Точная отсечка по line-height (без сабпиксельного bleed 5-й строки).
       Без max-height webkit резал по визуальной box-height, и часть глифов
       5-й строки просвечивала под ellipsis. */
    max-height: 4lh;
  }
  .zg-v3 .zgcat-desc-meta {
    display: none;
  }
  /* Mobile: density toggle бесполезен — на ≤768 layout треков один и тот же
     независимо от comfort/compact, а в filter-row он сваливается на 2-ю строку.
     Прячем и в обычной панели, и в sticky-баре (и для category, и для search). */
  .zg-v3 .zg-v3__panel .zgcat-density,
  .zg-v3 .zg-v3__filters .zgcat-density,
  body.zg-extras-panel-sticky .zg-v3__panel .zgcat-density {
    display: none !important;
  }
  .zg-v3 .zgcat-meta-strip {
    font-size: 11px;
    gap: 6px;
    margin-bottom: 10px;
  }
  .zg-v3 .zgcat-meta-strip .zgcat-meta-dot {
    margin: 0 1px;
  }
  /* Panel — stack rows, tighten padding */
  .zg-v3__panel {
    padding: 10px 12px;
    gap: 10px;
  }
  .zg-v3__filters.zgcat-rows {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
  }
  .zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration],
  .zg-v3__filters.zgcat-rows .zgcat-row[data-row=secondary] {
    grid-column: auto;
    width: 100%;
    justify-self: stretch;
  }
  .zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration] {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    margin: 0 -12px;
    padding: 2px 12px;
    gap: 5px;
  }
  .zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration]::-webkit-scrollbar {
    display: none;
  }
  .zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration] .zg-v3__btn,
  .zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration] .zgcat-chip-all {
    flex-shrink: 0;
    padding: 5px 11px;
    font-size: 12px;
  }
  /* Reset desktop absolute positioning of secondary row for mobile stacking */
  .zg-v3__filters.zgcat-rows .zgcat-row[data-row=secondary] {
    position: static;
    transform: none;
    top: auto;
    right: auto;
    justify-self: start;
    justify-content: flex-start;
    flex-wrap: wrap;
    gap: 6px;
  }
  .zg-v3__filters.zgcat-rows .zgcat-fdrop-toggle {
    /* Match duration chips на этом брейке (см. .zg-v3__btn выше: 5/11, 12px). */
    padding: 5px 11px;
    font-size: 12px;
  }
  .zg-v3__filters.zgcat-rows .zgcat-fdrop-chips {
    left: 0;
    right: auto;
    min-width: max-content;
  }
  /* Sort row: tabs horizontally scroll, density hides */
  .zg-v3__sorts {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    margin: 0 -12px;
    padding: 0 12px;
    gap: 4px;
  }
  .zg-v3__sorts::-webkit-scrollbar {
    display: none;
  }
  .zg-v3__sorts .zg-v3__btn {
    flex-shrink: 0;
    font-size: 12.5px;
  }
  .zg-v3__sorts .zgcat-sort-controls {
    display: inline-flex;
  }
  /* Groups: 2-column grid, tighter chips */
  body[data-zg-sort=groups] .zg-v3__groups {
    grid-template-columns: repeat(2, 1fr);
    gap: 5px;
    margin: 0 -12px 6px;
    padding: 7px 12px;
  }
  .zg-v3__groups > .zg-v3__btn {
    font-size: 11.5px;
    padding: 4px 8px;
  }
  .zg-v3__groups > .zg-v3__btn::before {
    font-size: 9.5px;
  }
  /* H2 — compact */
  .zg-v3__section > h2 {
    font-size: 15px;
    gap: 8px;
    margin-bottom: 8px;
  }
  .zg-v3__section > h2::before {
    font-size: 13px;
  }
  .zg-v3__section > h2 .zgcat-section-count {
    font-size: 10.5px;
    padding-left: 8px;
  }
  .zg-v3__section {
    margin-bottom: 22px;
  }
  /* Track list — tighter rhythm */
  .zg-v3__list.trackList {
    gap: 6px;
    margin-top: 6px;
  }
  /* Track row — same as ≤1250: play+wave на одной линии row 2,
     controls (title/time/★/mp3) — row 1 над plaay+wave */
  /* Layout B (как ≤1250): title с col 1, ★ inline, ↓ MP3 / 00:12 справа */
  .zg-v3 .onetrack.accordion {
    grid-template-columns: 36px auto auto minmax(0, 1fr) auto; /* col 5: time row 1 / mp3 row 2 */
    grid-template-rows: auto auto;
    column-gap: 8px;
    row-gap: 8px;
    padding: 4px 10px 10px; /* top меньше: title ближе к верху */
    height: auto;
    min-height: 0;
    max-height: none;
    border-radius: 8px;
  }
  .zg-v3 .onetrack.accordion .zgcat-titlewrap {
    grid-column: 1/3;
    grid-row: 1;
    min-width: 0;
    align-self: center;
  }
  .zg-v3 .onetrack.accordion .zgcat-fav {
    grid-column: 3;
    grid-row: 1;
    align-self: center;
  }
  .zg-v3 .onetrack.accordion .waveTime {
    grid-column: 5;
    grid-row: 1;
    justify-self: end;
    align-self: center;
  }
  .zg-v3 .onetrack.accordion .playWrap {
    grid-column: 1;
    grid-row: 2;
  }
  .zg-v3 .onetrack.accordion .waveWrap {
    grid-column: 2/5;
    grid-row: 2;
    align-self: center;
  }
  .zg-v3 .onetrack.accordion .dwdWrap {
    grid-column: 5;
    grid-row: 2;
    justify-self: end;
    align-self: center;
  }
  .zg-v3 .onetrack.accordion .waveTime {
    font-size: 11.5px;
    padding: 0;
    align-self: center;
  }
  .zg-v3 .onetrack.accordion .waveTitle {
    font-size: 13px;
  }
  .zg-v3 .onetrack.accordion .zgcat-fav {
    width: 26px;
    height: 26px;
  }
  .zg-v3 .onetrack.accordion .dwdButtn[data-type=mp3] {
    padding: 6px 10px;
    font-size: 11.5px;
  }
  .zg-v3 .onetrack.accordion .zgcat-formats-toggle {
    width: 20px;
  }
  body[data-zg-density=compact] .onetrack.accordion {
    height: auto;
    min-height: 0;
    max-height: none;
  }
  /* Title hover tooltip — keep within card padding */
  .zg-v3 .onetrack.accordion .zgcat-title-tip {
    left: -4px;
    right: -4px;
    top: -4px;
  }
  /* Hide hover-expansion fallback on touch (no hover) */
  .zg-v3 .onetrack.accordion .zgcat-titlewrap:not(:has(.zgcat-title-tip)):hover .waveTitle {
    position: static;
    background: transparent;
    border: 0;
    box-shadow: none;
    padding: 0;
  }
}
/* ---- ≤480px : small phone (sidebar collapsed) — tightest ---- */
@media (max-width: 400px) {
  .zg-v3 {
    padding: 6px 10px 20px;
  }
  .zg-v3 h1 {
    font-size: 19px;
  }
  .zg-v3__panel {
    padding: 8px 10px;
  }
  .zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration] {
    margin: 0 -10px;
    padding: 2px 10px;
  }
  .zg-v3__sorts {
    margin: 0 -10px;
    padding: 0 10px;
  }
  /* Group chip-strip: на 400px остаётся 2 кол. С 1 кол получается слишком
     длинная простыня. Длинные названия и так с ellipsis. */
  body[data-zg-sort=groups] .zg-v3__groups {
    grid-template-columns: repeat(2, 1fr);
    margin: 0 -10px 6px;
    padding: 7px 10px;
    gap: 4px;
  }
  .zg-v3__groups > .zg-v3__btn {
    padding: 5px 9px;
  }
  .zg-v3 .onetrack.accordion {
    column-gap: 8px;
    padding: 8px 10px;
    grid-template-columns: 36px minmax(0, 1fr) auto auto;
  }
  .zg-v3 .onetrack.accordion .playBu,
  .zg-v3 .onetrack.accordion .playWrap {
    width: 36px;
    height: 36px;
    min-width: 36px;
    min-height: 36px;
    max-width: 36px;
    max-height: 36px;
  }
  .zg-v3 .onetrack.accordion .zgcat-fav {
    width: 28px;
    height: 28px;
  }
  .zg-v3 .onetrack.accordion .dwdButtn[data-type=mp3] {
    padding: 5px 8px;
    font-size: 11px;
  }
  .zg-v3 .onetrack.accordion .dwdButtn[data-type=mp3]::before {
    width: 11px;
    height: 11px;
  }
}
/* admin mobile media query moved to files/zg-v3-admin.css */
/* Local integration: legacy _v2_one_track may output .waveTitle directly, without .zgcat-titlewrap. */
.zg-v3 .onetrack.accordion > .waveTitle {
  grid-column: 2;
  grid-row: 1;
  align-self: center;
}

/* =============================================================================
   v9 EXTRAS — sticky-bar + popup catalog + density-в-filters + sidebar off-screen
   =============================================================================
   Добавлено 2026-05-07 (v9 deploy). Ранее лежало в отдельном category-extras-prod.css,
   теперь интегрировано в основной _category_v3.scss → v3_category.css при компиляции.
   AI-инспектор: подробное описание см. memory/reference_category_extras_v9_deploy.md
   ============================================================================= */
/* === Скрываем .zg-v3-admin секцию (админ-панель сверху) на category v9 ====== */
/* Render оставляем (для DevTools / debug админам), но visually прячем —
   v9 layout не предусматривает админ-блок над panel.

   ВАЖНО: при ЗАДАЧА 10 (admin-popup) admin-section живёт ВНУТРИ
   .zg-v3-admin-popup. Это правило скрывало бы её и в popup'е (popup-wrapper
   тоже внутри .zg-v3). Override ниже возвращает видимость внутри popup'а. */
.zg-v3 .zg-v3-admin {
  display: none;
}

.zg-v3 .zg-v3-admin-popup .zg-v3-admin {
  display: block;
}

/* === Music tags filter dropdown ===========================================
   На music-категориях (/category/y2k-2000/, /category/muz/*) вместо .Группы
   рендерится .zgcat-fdrop-music-tags с поиском внутри + список топ-50 тегов.
   Широкий popup чтобы вместить много тегов: ~5-6 колонок tag-style чипов,
   compact density, max-height + scroll, стилизация input'а search. */
/* Music-tags / search-cats dropdown: LEFT-anchored к toggle (left:0).
   Узкий 280px с одной колонкой — единообразно с Громкость/Битрейт/BPM.
   Раньше был широкий 720px с 5-колоночной сеткой — выпадало из стиля. */
.zg-v3 .zgcat-fdrop-music-tags .zgcat-fdrop-chips,
.zg-v3 .zgcat-fdrop-search-cats .zgcat-fdrop-chips {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: auto;
  min-width: 0;
  width: 280px;
  max-width: 280px;
  padding: 12px;
  gap: 8px;
}

/* Header row: search input + Clear button. Multi-select dropdown — Clear
   виден только когда есть selections (PHP toggles `hidden` атрибут). */
.zg-v3 .zgcat-music-tag-head {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 0;
  /* Head на всю ширину chips-контейнера. Layout: Clear (когда есть selections,
     header-action right-aligned ABOVE search) → Search (full width).
     Если Clear hidden — только Search строкой. */
  flex: 1 1 100%;
  width: 100%;
}

.zg-v3 .zgcat-music-tag-head .zgcat-music-tag-search {
  align-self: stretch;
  width: 100%;
}

/* Standalone Clear button скрыт — очистка через × в toggle badge'е. */
.zg-v3 .zgcat-music-tag-clear {
  display: none;
}

.zg-v3 .zgcat-music-tag-list {
  flex: 1 1 100%;
  width: 100%;
}

.zg-v3 .zgcat-music-tag-search {
  width: 100%;
  height: 34px;
  margin: 0;
  padding: 6px 12px;
  border: 1px solid var(--zgcat-border, #e2e8f0);
  border-radius: var(--br, 8px);
  background: #fff;
  color: var(--zgcat-ink, #0f172a);
  font: inherit;
  font-size: 13px;
  line-height: 1.2;
  outline: none;
  box-sizing: border-box;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.zg-v3 .zgcat-music-tag-search::placeholder {
  color: #94a3b8;
}
.zg-v3 .zgcat-music-tag-search:focus {
  border-color: var(--pr-blue, #2e58ef);
  box-shadow: 0 0 0 3px rgba(46, 88, 239, 0.1);
}

/* Counter-badge: count + ×, весь badge кликабелен для сброса.
   Применяется ко ВСЕМ multi-select фильтрам (Теги, Категории, BPM,
   Длительность) — единый визуальный язык. Раньше селектор был привязан
   к .zgcat-fdrop-music-tags, поэтому BPM/Duration отрисовывались без
   синего pill'а с белым ×. */
.zg-v3 .zgcat-fdrop .zgcat-fdrop-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 18px;
  padding: 0 6px;
  margin-right: 6px;
  background: var(--pr-blue, #2e58ef);
  color: #fff;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  transition: background 0.12s;
}
.zg-v3 .zgcat-fdrop .zgcat-fdrop-badge:hover {
  background: var(--zgcat-primary-hov, #1a40c9);
}

.zg-v3 .zgcat-fdrop-badge-count {
  pointer-events: none;
}

.zg-v3 .zgcat-fdrop-badge-clear {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: 5px;
  padding-left: 5px;
  border-left: 1px solid rgba(255, 255, 255, 0.32);
  color: rgba(255, 255, 255, 0.92);
  pointer-events: none;
}
.zg-v3 .zgcat-fdrop-badge-clear svg {
  display: block;
  width: 8px;
  height: 8px;
  flex-shrink: 0;
}
.zg-v3 .zgcat-fdrop-badge-clear svg.zg-spinner {
  width: 10px;
  height: 10px;
  animation: zg-fdrop-spin 0.7s linear infinite;
}

/* During async clear: dim the pill slightly so it looks "in progress" */
.zg-v3 .zgcat-fdrop-badge.is-busy {
  cursor: progress;
  opacity: 0.85;
}

@keyframes zg-fdrop-spin {
  to {
    transform: rotate(360deg);
  }
}
/* Когда выбраны теги — иконка тега скрыта, badge встаёт на её место. */
.zg-v3 .zgcat-fdrop-music-tags.is-active .zgcat-fdrop-icon-tag {
  display: none;
}

/* Список тегов — одна колонка с вертикальным скроллом. Единообразно с другими
   фильтрами (Громкость, BPM, Битрейт). Раньше grid auto-fill ставил 5 колонок
   при широком popup — выпадало из стиля. */
.zg-v3 .zgcat-music-tag-list {
  display: grid;
  grid-template-columns: 1fr;
  grid-auto-rows: auto;
  align-content: flex-start;
  gap: 3px;
  width: 100%;
  /* Не более 35vh — popup не должен занимать половину экрана. На большом
     экране (vh=900) = 315px, на vh=600 = 210px. Min 200px чтобы и на мелких
     экранах хотя бы 5-6 строк было видно. */
  max-height: min(35vh, 360px);
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: #cbd5e1 transparent;
  padding: 2px;
}
.zg-v3 .zgcat-music-tag-list::-webkit-scrollbar {
  width: 6px;
}
.zg-v3 .zgcat-music-tag-list::-webkit-scrollbar-thumb {
  background: #cbd5e1;
  border-radius: 3px;
}

/* Все теги: одна строка, прямоугольная форма. display:block (НЕ flex) обязательно
   для работы ::first-letter ниже. capitalize не подходит — он капитализирует
   КАЖДОЕ слово («Акустическая Гитара»), нужна только ПЕРВАЯ буква
   («Акустическая гитара»).
   Специфичность 0,5,1 — нужно перебить
   `.zg-v3__filters.zgcat-rows .zgcat-fdrop-chips .zg-v3__btn { display: inline-flex }`
   (0,4,1). Цепляем `.zg-v3__filters.zgcat-rows` (две класса на ОДНОМ элементе)
   + `.zgcat-fdrop-chips` → 0,5,1. */
/* Стили теговых кнопок = клон Громкость/Битрейт chip-стиля (line ~1003):
   font 11.5px, padding 4px 9px, line-height 1.3, soft-blue active.
   Отличия от Громкость: display:block (нужен для ::first-letter) вместо
   inline-flex; width:100% (одна колонка), text-align:left. */
.zg-v3 .zg-v3__filters.zgcat-rows .zgcat-fdrop-chips .zgcat-music-tag-list .zg-v3__btn,
.zg-v3 .zgcat-music-tag-list .zg-v3__btn {
  display: block;
  width: 100%;
  height: unset;
  padding: 4px 9px;
  font-size: 11.5px;
  font-weight: 500;
  line-height: 1.3;
  text-align: left;
  text-transform: none;
  text-decoration: none;
  border-radius: var(--zgcat-radius-sm, 6px);
  background: var(--zgcat-surface, #fff);
  color: var(--zgcat-muted, #6c7390);
  border: 1px solid var(--zgcat-border, #e2e8f0);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  box-sizing: border-box;
  cursor: pointer;
  transition: all 0.12s;
  letter-spacing: 0;
}
.zg-v3 .zg-v3__filters.zgcat-rows .zgcat-fdrop-chips .zgcat-music-tag-list .zg-v3__btn::first-letter,
.zg-v3 .zgcat-music-tag-list .zg-v3__btn::first-letter {
  text-transform: uppercase;
}
.zg-v3 .zg-v3__filters.zgcat-rows .zgcat-fdrop-chips .zgcat-music-tag-list .zg-v3__btn:hover,
.zg-v3 .zgcat-music-tag-list .zg-v3__btn:hover {
  color: var(--zgcat-primary, #2e58ef);
  border-color: rgba(46, 88, 239, 0.3);
  background: #fff;
}
.zg-v3 .zg-v3__filters.zgcat-rows .zgcat-fdrop-chips .zgcat-music-tag-list .zg-v3__btn.is-active,
.zg-v3 .zgcat-music-tag-list .zg-v3__btn.is-active {
  background: var(--zgcat-primary-soft, rgba(46, 88, 239, 0.07));
  color: var(--zgcat-primary, #2e58ef);
  font-weight: 600;
  border-color: rgba(46, 88, 239, 0.25);
}

/* «Все» chip — без рамки/фона в неактивном состоянии (как в Громкость).
   Цепочка `.zg-v3 .zg-v3__filters.zgcat-rows .zgcat-fdrop-chips .zgcat-chip-all`
   нужна для тех же specificity-войн что и выше. */
.zg-v3 .zg-v3__filters.zgcat-rows .zgcat-fdrop-chips .zgcat-music-tag-list .zgcat-chip-all,
.zg-v3 .zgcat-music-tag-list .zgcat-chip-all {
  border-color: transparent;
  background: transparent;
}

/* Title-row (music card): title content-fit (natural width), может сжаться
   до 150px минимум (длинные названия → ellipsis). Tags + trackmeta прижаты
   вправо как cluster: tags margin-left: auto, trackmeta сразу после tags. */
.zg-v3 .longTracks .onetrack.accordion .zgcat-titlewrap {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.zg-v3 .longTracks .onetrack.accordion .zgcat-titlewrap .waveTitle {
  flex: 0 1 auto;
  width: auto; /* перебиваем prod's .waveTitle { width: 90% } */
  min-width: 150px; /* минимум 150px — дальше ellipsis */
  max-width: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: block !important;
  -webkit-line-clamp: unset !important;
  -webkit-box-orient: unset !important;
}

.zg-v3 .longTracks .onetrack.accordion .zgcat-titlewrap .zgcat-track-tags {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 4px;
  flex: 0 0 auto;
  margin: 0 0 0 auto; /* пустота слева → tags+trackmeta cluster справа */
  padding: 0;
  position: relative; /* anchor для popup-меню */
}

.zg-v3 .longTracks .onetrack.accordion .zgcat-titlewrap .zgcat-trackmeta {
  flex: 0 0 auto; /* сразу после tags, без gap'а большего чем titlewrap.gap */
  margin-left: 0; /* убираем legacy margin-left: auto от старых правил */
}

.zg-v3 .longTracks .zgcat-track-tags > a {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 20px;
  padding: 0 6px;
  background: transparent;
  color: #64748b;
  border: 1px solid #e2e8f0;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 500;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  box-sizing: border-box;
  transition: border-color 0.12s, color 0.12s, background 0.12s;
  /* Иконка-ярлычок ТОЛЬКО у первого тега — служит маркером группы.
     У остальных chip'ов только текст (меньше повторов, легче read). */
}
.zg-v3 .longTracks .zgcat-track-tags > a:first-of-type::before {
  content: "";
  flex: 0 0 auto;
  width: 9px;
  height: 9px;
  background-color: #94a3b8;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'><path d='M21.41 11.58 12.83 3a2 2 0 0 0-1.41-.58H4a2 2 0 0 0-2 2v7.42c0 .53.21 1.04.58 1.41l8.59 8.59a2 2 0 0 0 2.83 0l7.42-7.42a2 2 0 0 0 0-2.83zM6.5 7A1.5 1.5 0 1 1 8 5.5 1.5 1.5 0 0 1 6.5 7z'/></svg>") no-repeat center/contain;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'><path d='M21.41 11.58 12.83 3a2 2 0 0 0-1.41-.58H4a2 2 0 0 0-2 2v7.42c0 .53.21 1.04.58 1.41l8.59 8.59a2 2 0 0 0 2.83 0l7.42-7.42a2 2 0 0 0 0-2.83zM6.5 7A1.5 1.5 0 1 1 8 5.5 1.5 1.5 0 0 1 6.5 7z'/></svg>") no-repeat center/contain;
  transition: background-color 0.12s;
}
.zg-v3 .longTracks .zgcat-track-tags > a:hover {
  border-color: var(--pr-blue, #2e58ef);
  color: var(--pr-blue, #2e58ef);
  background: rgba(46, 88, 239, 0.04);
}
.zg-v3 .longTracks .zgcat-track-tags > a:hover:first-of-type::before {
  background-color: var(--pr-blue, #2e58ef);
}
.zg-v3 .longTracks .zgcat-track-tags > a.is-extra {
  display: none;
}

/* «…» chip — subtle indicator. Hover/tap → floating popup.
   Same height/border-radius как обычные tag-chip, серый цвет (не яркий). */
.zg-v3 .longTracks .zgcat-track-tags-more {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 20px;
  padding: 0 8px 4px;
  background: transparent;
  border: 1px solid #e2e8f0;
  border-radius: 4px;
  color: #94a3b8;
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.08em; /* шире gap между точками — читается как «…» */
  cursor: pointer;
  box-sizing: border-box;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.zg-v3 .longTracks .zgcat-track-tags-more:hover {
  background: rgba(46, 88, 239, 0.04);
  border-color: var(--pr-blue, #2e58ef);
  color: var(--pr-blue, #2e58ef);
}

/* Скрыть legacy `.is-extra` теги (prod до деплоя нового partial'а). */
.zg-v3 .longTracks .zgcat-track-tags > a.is-extra {
  display: none;
}

/* «Inline second row» — popup прижат прямо под «…» chip без gap'а, чипы
   выглядят как продолжение основного ряда. Touches button bottom → нет потери
   hover. Right-anchored к «…», открывается ниже и влево.
   Селектор включает .onetrack.accordion — нужно чтобы перебить hard-reset
   правило (position: static на всех потомках .onetrack.accordion, line ~1121). */
.zg-v3 .longTracks .onetrack.accordion .zgcat-track-tags-popup {
  position: absolute;
  top: 100%;
  right: 0;
  left: auto;
  z-index: 100;
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  justify-content: flex-end;
  gap: 4px;
  margin-top: 4px;
  padding: 4px;
  /* bg бампнули с 0.95 до 0.98 alpha — визуально неотличимо без blur'а,
     но без paint-cost от backdrop-filter (cumulative на 50 popup'ах). */
  background: rgba(255, 255, 255, 0.98);
  border: 1px solid var(--zgcat-border, #e2e8f0);
  border-radius: 6px;
  box-shadow: 0 4px 12px -2px rgba(15, 23, 42, 0.08);
  min-width: 0;
  max-width: 320px;
  width: max-content;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-2px);
  pointer-events: none;
  transition: opacity 0.15s ease, transform 0.15s ease, visibility 0s linear 0.2s;
  /* Bridge между button и popup — закрывает 4px gap чтобы cursor не терял hover. */
}
.zg-v3 .longTracks .onetrack.accordion .zgcat-track-tags-popup::before {
  content: "";
  position: absolute;
  top: -6px;
  left: 0;
  right: 0;
  height: 6px;
}

/* Trigger popup на hover — ТОЛЬКО на устройствах с реальной мышью (desktop).
   На touch (mobile) hover sticky → click-toggle не закрывает popup.
   `:not(.is-popup-suppressed)` — блокирует hover после click-close
   до тех пор пока курсор не покинет контейнер (mouseleave снимает класс). */
@media (hover: hover) {
  .zg-v3 .longTracks .onetrack.accordion .zgcat-track-tags:not(.is-popup-suppressed):hover .zgcat-track-tags-popup,
  .zg-v3 .longTracks .onetrack.accordion .zgcat-track-tags:not(.is-popup-suppressed) .zgcat-track-tags-popup:hover {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    pointer-events: auto;
    transition: opacity 0.15s ease, transform 0.15s ease, visibility 0s linear 0s;
  }
}
/* Click-toggle (.is-popup-open) работает на ЛЮБЫХ устройствах. */
.zg-v3 .longTracks .onetrack.accordion .zgcat-track-tags.is-popup-open .zgcat-track-tags-popup {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  pointer-events: auto;
  transition: opacity 0.15s ease, transform 0.15s ease, visibility 0s linear 0s;
}

.zg-v3 .longTracks .zgcat-track-tags-more:hover ~ .zgcat-track-tags-popup,
.zg-v3 .longTracks .zgcat-track-tags-popup:hover,
.zg-v3 .longTracks .zgcat-track-tags.is-popup-open .zgcat-track-tags-popup {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  pointer-events: auto;
  transition: opacity 0.18s ease, transform 0.18s ease, visibility 0s linear 0s;
}

/* Tag chip внутри popup — compact pill style. */
.zg-v3 .longTracks .zgcat-track-tags-popup a {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 8px;
  background: transparent;
  color: #475569;
  border: 1px solid #e2e8f0;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 500;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  box-sizing: border-box;
  transition: border-color 0.12s, color 0.12s, background 0.12s;
}
.zg-v3 .longTracks .zgcat-track-tags-popup a:hover {
  border-color: var(--pr-blue, #2e58ef);
  color: var(--pr-blue, #2e58ef);
  background: rgba(46, 88, 239, 0.04);
}

/* MOBILE: density (icon-toggle 2-button group) всегда прижата вправо в panel —
   на любых ширинах. Ставится через margin-left: auto когда parent flex. */
@media (max-width: 768px) {
  .zg-v3 .zg-v3__panel .zgcat-density,
  .zg-v3 .zg-v3__filters .zgcat-density {
    margin-left: auto !important;
  }
}
/* ≤480 для music категорий: убираем Битрейт и Громкость из panel — на узких
   экранах они занимают много места и не критичны (можно через "Все"). */
@media (max-width: 480px) {
  .zg-v3[data-v3-is-music="1"] .zg-v3__panel .zgcat-fdrop[data-fdrop=bitrate],
  .zg-v3[data-v3-is-music="1"] .zg-v3__panel .zgcat-fdrop[data-fdrop=loudness],
  .zg-v3[data-v3-is-music="1"] .zg-v3__filters .zgcat-fdrop[data-fdrop=bitrate],
  .zg-v3[data-v3-is-music="1"] .zg-v3__filters .zgcat-fdrop[data-fdrop=loudness] {
    display: none !important;
  }
}
/* Filter chips (duration ranges, Теги, Громкость, Битрейт) растут и равномерно
   заполняют ширину строки — никаких пустот справа. На всех ширинах. */
/* Раньше .zgcat-fdrop был flex: 1 1 0 (растягивался во всю ширину row) —
   теперь после прогерского refactor фильтры должны быть content-fit, чтобы
   уместить Длительность/Теги/BPM/Громкость/Битрейт в одну row без растяжения.
   .zg-v3__btn / .zgcat-chip-all внутри duration-row старого формата всё
   ещё могут существовать на других страницах — keep flex:1 для них. */
.zg-v3 .zg-v3__filters.zgcat-rows .zgcat-row > .zg-v3__btn,
.zg-v3 .zg-v3__filters.zgcat-rows .zgcat-row > .zgcat-chip-all {
  flex: 1 1 0;
  min-width: 0;
}

.zg-v3 .zg-v3__filters.zgcat-rows .zgcat-row > .zgcat-fdrop {
  flex: 0 0 auto;
}

/* Density — компактная, не растягивается. */
.zg-v3 .zg-v3__filters.zgcat-rows .zgcat-density {
  flex: 0 0 auto;
}

/* Mobile (≤768): override scroll-strip rule на duration-row (line 2769) которая
   ставит margin/padding negative + flex-wrap:nowrap → запрещает 100% fill.
   Возвращаем wrap + flush margins, чтобы chips заполнили строку полностью. */
@media (max-width: 768px) {
  .zg-v3 .zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration] {
    flex-wrap: wrap;
    overflow: visible;
    margin: 0;
    padding: 0;
  }
  .zg-v3 .zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration] > .zg-v3__btn,
  .zg-v3 .zg-v3__filters.zgcat-rows .zgcat-row[data-row=duration] > .zgcat-chip-all {
    flex: 1 1 0;
    min-width: 0;
  }
}
/* TABLET (520-768): titlewrap может wrap'аться (safety при overflow), но
   до 520px tags ОСТАЮТСЯ выровнены ВПРАВО (рядом с trackmeta) — на этих
   ширинах места достаточно. */
@media (max-width: 768px) {
  .zg-v3 .longTracks .onetrack.accordion .zgcat-titlewrap {
    flex-wrap: wrap;
  }
}
/* MOBILE/NARROW (≤700): title — full-width row 1 c absolute waveTime справа.
   Tags wrap на свою row → выравниваем ВЛЕВО + popup открывается ВПРАВО от «…».
   Брейк 700 покрывает диапазон где tags+meta уже не помещаются в title-row.
   Не применяется в compact-режиме — там title делит row с share+★ buttons,
   tags+desc скрыты. */
@media (max-width: 700px) {
  body:not([data-zg-density=compact]) .zg-v3 .longTracks .onetrack.accordion .zgcat-titlewrap .waveTitle {
    flex-basis: 100%;
    padding-right: 56px;
    box-sizing: border-box;
  }
  body:not([data-zg-density=compact]) .zg-v3 .longTracks .onetrack.accordion .zgcat-titlewrap .zgcat-track-tags {
    margin: 0 auto 0 0;
  }
  body:not([data-zg-density=compact]) .zg-v3 .longTracks .onetrack.accordion .zgcat-track-tags-popup {
    left: 0;
    right: auto;
    justify-content: flex-start;
    max-width: calc(100vw - 32px);
    min-width: 180px;
  }
}
/* === ЗАДАЧА 1. catv3 sidebar — VISIBLE footer-каталог =====================
   _V3leftMenu_admin.php рендерит:
   <div class="zg-v3-cat-sidebar"><ul class="sblist">...</ul></div>
   Раньше скрывали off-screen (grey-hat SEO). Теперь — visible footer-блок:
   юзер прокрутил вниз → видит структуру каталога (UX), Google индексирует
   как обычный контент (white-hat SEO). При клике на «База звуков» JS
   копирует sblist в popup-modal — same content, два места показа.

   ВАЖНО про layout: .dashBoard.catv3 — flex row (legacy) с двумя детьми:
   .oh (контент) и .zg-v3-cat-sidebar. Без правок sidebar встаёт правой
   колонкой ~250px. Чтобы он стал «футером» — flex-wrap: wrap на родителе
   + 100%-ширина обоим детям → sidebar естественно падает под .oh.
   AI-инспектор: см. memory/reference_category_extras_v9_deploy.md */
body.zg-extras-no-fixed-header .dashBoard.catv3 {
  flex-wrap: wrap;
}

body.zg-extras-no-fixed-header .dashBoard.catv3 > .oh,
body.zg-extras-no-fixed-header .dashBoard.catv3 > .zg-v3-cat-sidebar {
  flex: 0 0 100%;
  min-width: 0;
}

.zg-v3-cat-sidebar {
  margin: 32px auto 24px;
  max-width: 1200px;
  padding: 24px 24px 28px;
  background: #fbfcfe;
  border: 1px solid var(--border-card, #dfe7f3);
  border-radius: 10px;
}

.zg-v3-cat-sidebar::before {
  content: "Все категории";
  display: block;
  font-family: inherit;
  font-weight: 600;
  font-size: 15px;
  line-height: 1.3;
  color: #0f172a;
  margin-bottom: 16px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--border-card, #dfe7f3);
}

.zg-v3-cat-sidebar .sblist {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 4px 12px;
}

.zg-v3-cat-sidebar .sblist li {
  margin: 0;
  padding: 0;
  list-style: none;
}

.zg-v3-cat-sidebar .sblist li a {
  display: block;
  padding: 6px 10px;
  border-radius: 6px;
  color: #475569;
  font-family: inherit;
  font-weight: 400;
  font-size: 13px;
  line-height: 1.4;
  text-decoration: none;
  transition: background 0.12s, color 0.12s;
}

.zg-v3-cat-sidebar .sblist li a:hover {
  background: var(--l-blue, #eef4f9);
  color: var(--pr-blue, #2e58ef);
}

.zg-v3-cat-sidebar .sblist li.favorite a {
  color: var(--pr-blue, #2e58ef);
  font-weight: 600;
}

.zg-v3-cat-sidebar .sblist li.favorite a::before {
  content: "★ ";
  margin-right: 2px;
}

/* === ЗАДАЧА 2. .oh центрируется, .vv3 без outline ========================== */
/* Legacy `.contnr.morda.nofixbox .oh { width: calc(100%-300px); outline: ... }`
   (specificity 0,4,0) — мешает на category v9. Override через body class
   .zg-extras-no-fixed-header (выставляется JS-ом сразу после загрузки страницы)
   + matching legacy chain даёт 0,5,0 → выигрываем cascade. */
body.zg-extras-no-fixed-header .contnr.morda.nofixbox .oh,
body.zg-extras-no-fixed-header .oh {
  max-width: 1200px;
  margin-left: auto;
  margin-right: auto;
  float: none;
  width: 100%;
  outline: 0;
  /* `.oh` имеет site-default overflow:hidden — обрезает любые абсолютно-
     позиционированные dropdown'ы (теги, BPM, музыкальные категории) когда они
     вылазят за нижний край контейнера. На category/search-page снимаем. */
  overflow: unset;
}

body.zg-extras-no-fixed-header .vv3,
body.zg-extras-no-fixed-header .dashBoard.vv3 {
  outline: 0;
  border: 0;
  box-shadow: none;
}

body.zg-extras-no-fixed-header .contnr.morda.nofixbox {
  margin-top: 0;
}

/* === ЗАДАЧА 3. Header сайта un-stick на /category/ ========================== */
/* .site-header на проде имеет position: sticky; top: 0; z-index: 999.
   На category /v3 хотим скроллируемый header. JS добавляет body class.

   ВАЖНО про stacking-context: site-default `position: sticky` создавал
   stacking context на header. При переключении на `position: static` —
   stacking context теряется (z-index у static игнорируется). Тогда
   stacking context от `.fixtop` (z=99999 внутри header) "поднимается"
   к body root и в некоторых case'ах page content (.zg-v3__desc и др.)
   рендерится визуально ВЫШЕ dropdown'а topnav.
   Решение: `position: relative; z-index: 9999` — relative + non-auto z
   восстанавливает stacking context на header, dropdown всегда сверху.
   Z=9999 (а не 99) чтобы header оставался поверх .leftSupermenu.expandMenu
   overlay (z=99) на мобиле — иначе крестик-кнопка перекрывается оверлеем
   и юзер не может закрыть меню. */
body.zg-extras-no-fixed-header header.site-header {
  position: relative;
  top: auto;
  z-index: 9999;
}

/* === ЗАДАЧА 4. Static popup-trigger «База звуков» в sort-controls =========== */
/* Trigger вставляет PHP-ом в .zgcat-sort-controls (см. v3_showTag.php).
   Базовый стиль (общий для static + sticky варианта): */
.zg-extras-popup-trigger {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  font-weight: 600;
  color: var(--pr-blue, #2e58ef);
  background: #fff;
  border: 1px solid var(--pr-blue, #2e58ef);
  border-radius: 6px;
  padding: 5px 12px;
  text-decoration: none;
  cursor: pointer;
  line-height: 1.3;
  margin: 0;
  white-space: nowrap;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}

.zg-extras-popup-trigger:hover {
  background: var(--l-blue, #eef4f9);
  color: var(--pr-blue, #2e58ef);
  text-decoration: none;
}

.zg-extras-popup-trigger .zg-extras-icon {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  display: inline-block;
  vertical-align: middle;
  color: var(--pr-blue, #2e58ef);
}

/* Static-вариант (в .zgcat-sort-controls правый край sort-row):
   off-white фон + outer-ring blue glow + divider слева. */
.zgcat-sort-controls .zg-extras-static-trigger {
  position: relative;
  margin-left: 14px;
  background: #f8fafc;
  box-shadow: 0 0 0 1px rgba(46, 88, 239, 0.1);
}

.zgcat-sort-controls .zg-extras-static-trigger:hover {
  box-shadow: 0 0 0 1px rgba(46, 88, 239, 0.18);
}

.zgcat-sort-controls .zg-extras-static-trigger::before {
  content: none;
}

/* "Категории" chip-trigger в sort-row (search-page) — повторяет визуал
   chip-кнопок из .zg-v3__filters.zgcat-rows. Чужой родительский
   контекст (.zg-v3__sorts вместо .zg-v3__filters) → базовые chip-стили
   .zgcat-fdrop-toggle / -icon / -caret не подхватываются, иначе срабатывают
   правила .zg-v3__sorts .zg-v3__btn и кнопка выходит сиренево-filled.
   Этот блок реплицирует chip-look по точечному селектору. */
.zg-v3__sorts .zgcat-sort-controls .zgcat-fdrop-search-cats {
  position: relative;
}
.zg-v3__sorts .zgcat-sort-controls .zgcat-fdrop-search-cats .zgcat-fdrop-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px;
  font-size: 13px;
  font-weight: 500;
  color: var(--zgcat-muted);
  background: #f8fafc;
  border: 1px solid var(--zgcat-border);
  border-radius: var(--zgcat-radius-sm);
  cursor: pointer;
  line-height: 1.3;
  white-space: nowrap;
  transition: all 0.15s;
  font-family: inherit;
  letter-spacing: 0;
  text-transform: none;
}
.zg-v3__sorts .zgcat-sort-controls .zgcat-fdrop-search-cats .zgcat-fdrop-toggle:hover {
  color: var(--zgcat-text);
  border-color: var(--zgcat-muted-3);
  background: #f1f5f9;
}
.zg-v3__sorts .zgcat-sort-controls .zgcat-fdrop-search-cats.is-open .zgcat-fdrop-toggle, .zg-v3__sorts .zgcat-sort-controls .zgcat-fdrop-search-cats.is-active .zgcat-fdrop-toggle {
  color: var(--zgcat-primary);
  background: var(--zgcat-primary-soft);
  border-color: rgba(46, 88, 239, 0.25);
  font-weight: 600;
}
.zg-v3__sorts .zgcat-sort-controls .zgcat-fdrop-search-cats .zgcat-fdrop-icon {
  display: inline-block;
  width: 12px;
  height: 12px;
  background-color: currentColor;
  flex-shrink: 0;
}
.zg-v3__sorts .zgcat-sort-controls .zgcat-fdrop-search-cats .zgcat-fdrop-icon-grid {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='3' width='8' height='8' rx='1.5'/><rect x='13' y='3' width='8' height='8' rx='1.5'/><rect x='3' y='13' width='8' height='8' rx='1.5'/><rect x='13' y='13' width='8' height='8' rx='1.5'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='3' width='8' height='8' rx='1.5'/><rect x='13' y='3' width='8' height='8' rx='1.5'/><rect x='3' y='13' width='8' height='8' rx='1.5'/><rect x='13' y='13' width='8' height='8' rx='1.5'/></svg>") center/contain no-repeat;
}
.zg-v3__sorts .zgcat-sort-controls .zgcat-fdrop-search-cats .zgcat-fdrop-caret {
  display: inline-block;
  width: 8px;
  height: 8px;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>") center/contain no-repeat;
  transition: transform 0.18s ease;
  opacity: 0.55;
  flex-shrink: 0;
}
.zg-v3__sorts .zgcat-sort-controls .zgcat-fdrop-search-cats.is-open .zgcat-fdrop-caret {
  transform: rotate(180deg);
  opacity: 1;
}

/* ≤768: «Выбор категорий» не влезает рядом с тремя sort-кнопками. Всегда
   меняем label на короткое «Категории» через ::after-trick. Без :not() —
   проще и надёжнее, при выборе там и так уже «Категории» (с сервера). */
@media (max-width: 768px) {
  .zg-v3__sorts .zgcat-sort-controls .zgcat-fdrop-search-cats .zgcat-fdrop-text {
    font-size: 0;
  }
  .zg-v3__sorts .zgcat-sort-controls .zgcat-fdrop-search-cats .zgcat-fdrop-text::after {
    content: "Категории";
    font-size: 13px;
    font-weight: 500;
  }
}
/* ≤520: прячем badge "N ×" на «Категории» — для indicator'а выбора
   достаточно .is-active рамки/тинта. «Новые» / «В тренде» остаются — юзер
   хочет видеть все три sort-варианта на любой ширине (см. также loudness). */
@media (max-width: 520px) {
  .zg-v3__sorts .zgcat-sort-controls .zgcat-fdrop-search-cats .zgcat-fdrop-badge {
    display: none;
  }
}
/* ≤700: компактим триггеры — убираем иконку + сокращаем label
   до «Музыка»/«Звуки» (без префикса «База») чтобы всё влезло.
   Бамп 520→700: на 521-700 «База музыки» уже плохо влезала в sticky-бар
   рядом с density+тегами. Использован существующий брейкпоинт 700
   (тот же, что управляет mobile-stack tag-popup) — без новых ширин. */
@media (max-width: 700px) {
  /* Static trigger в sort-row + sticky trigger в panel — убираем иконку. */
  .zgcat-sort-controls .zg-extras-static-trigger .zg-extras-icon,
  body.zg-extras-panel-sticky .zg-extras-sticky-trigger .zg-extras-icon {
    display: none;
  }
  /* Скрываем нативный текст, показываем короткий через ::after.
     gap: 0 — иначе flex gap между текстом (0px) и ::after создаёт лишний
     отступ слева. */
  .zgcat-sort-controls .zg-extras-static-trigger,
  body.zg-extras-panel-sticky .zg-extras-sticky-trigger {
    font-size: 0;
    padding-left: 10px;
    padding-right: 10px;
    gap: 0;
  }
  .zg-v3[data-v3-is-music="1"] .zgcat-sort-controls .zg-extras-static-trigger::after,
  body.zg-extras-panel-sticky .zg-v3[data-v3-is-music="1"] .zg-extras-sticky-trigger::after,
  body.zg-extras-panel-sticky .zg-extras-sticky-trigger::after {
    content: "Музыка";
    font-size: 12px;
    font-weight: 500;
  }
  .zg-v3:not([data-v3-is-music="1"]) .zgcat-sort-controls .zg-extras-static-trigger::after,
  body.zg-extras-panel-sticky .zg-v3:not([data-v3-is-music="1"]) .zg-extras-sticky-trigger::after {
    content: "Звуки";
    font-size: 12px;
    font-weight: 500;
  }
  /* «Теги» toggle — иконку тега тоже убираем для компактности.
     В sticky панели — поднимаем специфичность чтобы перебить любые
     site-CSS правила. */
  .zg-v3 .zgcat-fdrop-music-tags .zgcat-fdrop-icon-tag,
  body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop-icon,
  body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop-icon-tag {
    display: none !important;
  }
  /* Sticky-bar toggles: только Sort (Популярные) — 100px min-width как anchor.
     BPM, Duration, Density, Trigger («Музыка») — sizing to content. Узкие кнопки
     дают больше воздуха в 355px панель на iPhone 12 Pro (390px). */
  body.zg-extras-panel-sticky .zg-v3__panel .zg-extras-sort-dropdown .zgcat-fdrop-toggle {
    min-width: 100px;
  }
  body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop-toggle {
    overflow: hidden;
  }
  body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop-toggle .zgcat-fdrop-text {
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
  }
  /* Все sticky-toggles — одинаковая min-height чтобы icon-кнопка (clock)
     совпадала по вертикали с текстовыми. 13px * 1.4 + 6+6 padding + 2 border
     = ~34px. */
  body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop-toggle,
  body.zg-extras-panel-sticky .zg-v3__panel .zg-extras-sticky-trigger {
    min-height: 34px;
    box-sizing: border-box;
  }
}
/* === ЗАДАЧА 5. Sticky-bar при scroll ======================================== */
/* Активируется body class .zg-extras-panel-sticky (выставляет JS bbox-hysteresis).
   Логика ON: scrollY > original panel.bottom; OFF: scrollY < panel.top.
   Placeholder в потоке заменяет panel когда тот fixed (без прыжка контента). */
/* Белая полоса 100vw — высота берётся из CSS-переменной --zg-extras-sticky-h
   (JS измеряет panel.height после ON и устанавливает). pointer-events:none
   чтобы scroll-wheel прошёл сквозь и не блокировался. */
body.zg-extras-panel-sticky::before {
  content: "";
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: var(--zg-extras-sticky-h, 56px);
  box-sizing: border-box;
  background: #fff;
  /* Линия снизу через box-shadow (offset 0,1 blur 0 spread 0) — она живёт
     в y=[h, h+1], сразу ПОД элементом. Border-bottom внутри height ломал
     выравнивание с панелью когда --h был чуть рассинхронизирован. С
     box-shadow панель и ::before кончаются на одной y, линия одна на
     обе зоны. */
  box-shadow: 0 1px 0 0 #c6d4e8, 0 6px 14px -8px rgba(15, 23, 42, 0.14);
  z-index: 49;
  pointer-events: none;
}

/* Панель в sticky-mode: position:fixed, ограничена 1200 по центру,
   #f8fafc фон отделяет от белой полосы 100vw, боковые границы без скруглений.

   Visual lift: drop-shadow + утолщённые side-rails + bottom border.
   Без них бар «сливается» со светлым контентом ниже на белом фоне страницы.
   Side-rail 2px #c6d4e8 — это вторичный border-token (--border-card-2), он
   уже используется в фильтрах для divider'ов; отделяет 1200-карту от 100vw
   шапки/контента. Bottom 1px замыкает прямоугольник. Shadow двухуровневый:
   ambient (16px blur, низкая alpha) + key (6px blur) — стандарт material-2dp. */
body.zg-extras-panel-sticky .zg-v3__panel {
  /* Центруем через round(down, calc(...)) — whole-pixel offset БЕЗ transform.
     Transform → GPU compositor layer → текст рендерится по-другому →
     постоянный блюр до hover-paint. CSS round() (Values 4, supported
     Chrome 124+/FF 118+/Safari 16.4+) даёт floor-integer, никогда не
     даст half-pixel независимо от viewport-ширины. */
  /* --zg-sb-w кормится JS-ом из _v3_extras_inline.php (innerWidth -
     documentElement.clientWidth). 100vw включает scrollbar (~15px на Windows),
     а контент `.oh` центрирован в clientWidth → без вычитания scrollbar панель
     сидит на 7.5px правее карточек. Fallback 0px = безопасный no-op для macOS
     с overlay-скроллбаром. */
  position: fixed;
  top: 0;
  left: max(0px, round(down, (100vw - var(--zg-sb-w, 0px) - 1200px) / 2, 1px));
  width: min(1200px, 100vw - var(--zg-sb-w, 0px));
  /* Mobile inset: контент `.oh` имеет 10px горизонтальный padding на узких
     экранах → trackcards выровнены с x=10, sticky-bar при `left:0; width:100vw`
     визуально съезжает влево относительно карточек. Зажимаем sticky на те же
     10px. Mid-range планшеты (≤920) — 12px чтобы не было резкого скачка. */
}
@media (max-width: 1199px) and (min-width: 769px) {
  body.zg-extras-panel-sticky .zg-v3__panel {
    left: 12px !important;
    width: calc(100vw - var(--zg-sb-w, 0px) - 24px) !important;
  }
}
body.zg-extras-panel-sticky .zg-v3__panel {
  /* На mobile (≤768) `width:calc(100% - 20px)` — `100%` для position:fixed
     резолвится в documentElement.clientWidth (без scrollbar), что естественно
     устраняет правый дрифт без зависимости от JS-feeded `--zg-sb-w`. Более
     надёжно чем `100vw - sb`: не зависит от тайминга загрузки JS. */
}
@media (max-width: 768px) {
  body.zg-extras-panel-sticky .zg-v3__panel {
    left: 10px !important;
    width: calc(100% - 20px) !important;
  }
}
body.zg-extras-panel-sticky .zg-v3__panel {
  z-index: 50;
  background: #f8fafc;
  border: 0;
  border-left: 1px solid var(--border-card, #dfe7f3);
  border-right: 1px solid var(--border-card, #dfe7f3);
  /* Без border-bottom — линию полной шириной рисует ::before через box-shadow
     (0 1px 0 #c6d4e8). Панель кончается на той же y что и ::before, линия
     box-shadow выходит из-под обоих сразу — без удвоения. */
  border-radius: 0;
  box-shadow: 0 6px 16px -6px rgba(15, 23, 42, 0.18), 0 2px 4px -2px rgba(15, 23, 42, 0.06);
  padding: 12px 14px;
  margin: 0;
  animation: zg-extras-panel-slide 180ms ease-out;
  /* Grid 8-col: [sort-dd][duration][type/tag][bpm][loudness][spacer 1fr][density][trigger] */
  display: grid;
  grid-template-columns: auto auto auto auto auto minmax(0, 1fr) auto auto;
  grid-template-rows: auto;
  align-items: center;
  gap: 0 10px;
}

@keyframes zg-extras-panel-slide {
  /* X-центровка через left:round() (см. правило панели). Анимация только
     по Y. translateY(0) = identity = panel не остаётся в GPU layer. */
  from {
    transform: translateY(-100%);
    opacity: 0;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}
/* Грид-position для каждого item. grid-row:1 явно — иначе density wraps. */
body.zg-extras-panel-sticky .zg-v3__panel .zg-extras-sort-dropdown {
  grid-column: 1;
  grid-row: 1;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop[data-fdrop=duration] {
  grid-column: 2;
  grid-row: 1;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop[data-fdrop=type],
body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop[data-fdrop=music_tag] {
  grid-column: 3;
  grid-row: 1;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop[data-fdrop=bpm] {
  grid-column: 4;
  grid-row: 1;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop[data-fdrop=loudness] {
  grid-column: 5;
  grid-row: 1;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-density {
  grid-column: 7;
  grid-row: 1;
}

body.zg-extras-panel-sticky .zg-v3__panel .zg-extras-sticky-trigger {
  grid-column: 8;
  grid-row: 1;
}

/* col 6 — 1fr spacer. */
/* В sticky оригинальные .zg-v3__sorts pills остаются ВИДИМЫМИ — юзер хочет
   видеть Популярные / Новые / В тренде прямо в sticky-баре, без вложенного
   dropdown. Заменяем sort-dropdown (его прячем ниже) на инлайн-пилюли.
   Внутри `.zg-v3__sorts` сидит ещё и `.zgcat-sort-controls` (правый кластер
   с «Категории» / «База звуков» и др. триггерами) — его прячем, чтобы в
   sticky остались только sort-пилюли. */
body.zg-extras-panel-sticky .zg-v3__panel .zg-v3__sorts {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex-wrap: nowrap;
  grid-column: 1;
  grid-row: 1;
  padding: 0;
  margin: 0;
  background: transparent;
  border: 0;
}

body.zg-extras-panel-sticky .zg-v3__panel .zg-v3__sorts .zgcat-sort-controls {
  display: none;
}

body.zg-extras-panel-sticky .zg-v3__panel .zg-v3__sorts .zg-v3__btn {
  padding: 5px 10px;
  font-size: 12.5px;
}

/* Sort-dropdown больше не нужен в sticky — пилюли отображаются напрямую. */
body.zg-extras-panel-sticky .zg-v3__panel .zg-extras-sort-dropdown {
  display: none;
}

/* Bitrate в sticky скрываем (редкий filter — для него юзер скроллит вверх). */
body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop[data-fdrop=bitrate] {
  display: none;
}

/* Прочие panel-children (.zg-v3__groups, etc.) скрыты в sticky.
   :not() chain — `.zg-v3__sorts` теперь VISIBLE (sort-пилюли), `.zg-extras-sort-dropdown` тоже включаем чтобы он не наследовал display:none от catch-all. */
body.zg-extras-panel-sticky .zg-v3__panel > *:not(.zg-v3__filters):not(.zg-v3__sorts):not(.zgcat-density):not(.zg-extras-sticky-trigger):not(.zg-extras-sort-dropdown):not(.zgcat-fdrop) {
  display: none;
}

/* В sticky filters-обёртка пустая (JS вытащил dropdowns в panel напрямую) —
   скрываем её, иначе border-left от старой стилистики рисует ложный
   divider слева от density. */
body.zg-extras-panel-sticky .zg-v3__panel .zg-v3__filters {
  display: none;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-duration-heading {
  display: none;
}

body.zg-extras-panel-sticky .zg-v3__panel [data-row=duration] .zg-v3__btn {
  padding: 3px 8px;
  font-size: 11.5px;
}

/* Скрываем secondary row внутри filters в sticky (он содержит type/loudness/bitrate
   которые мы переносим в panel отдельными колонками). */
body.zg-extras-panel-sticky .zg-v3__panel .zg-v3__filters [data-row=secondary] {
  display: none;
}

/* Density в sticky — col 5, без divider, без бордеров обертки. */
body.zg-extras-panel-sticky .zg-v3__panel .zgcat-density {
  margin: 0;
  padding: 0;
  border: 0;
  display: inline-flex;
  flex-direction: row;
  flex-wrap: nowrap;
  background: transparent;
  gap: 6px;
  align-items: center;
}

/* Trigger («База музыки/звуков») — справа в sticky. Divider убран
   (раньше был ::before linкой высотой 70% между density и trigger). */
body.zg-extras-panel-sticky .zg-v3__panel .zg-extras-sticky-trigger {
  position: relative;
}

/* === ЗАДАЧА 6. Sticky sort-dropdown («По группам ▾») ======================== */
/* DOM-структура site `.zgcat-fdrop` — сайт-JS-обработчик в v3_showTag.php
   обрабатывает click на `.zgcat-fdrop-toggle` (toggle .is-open класс).
   Site CSS `.zg-v3__filters.zgcat-rows .zgcat-fdrop` scoped к filters —
   не доступен в sticky panel. Дублируем стили scoped к sticky.
   Дополнительная стилизация sort-dropdown (col 1): белый фон + синяя рамка
   (отличается от Громкость/Тип-droprops с off-white фоном). */
/* sticky-trigger «База звуков» создаётся JS-ом в panel и должен быть СКРЫТ
   в normal mode (его дубликат уже есть в .zgcat-sort-controls справа sort-row).
   В sticky-mode body class активирует — JS делает append last child. */
.zg-v3__panel .zg-extras-sticky-trigger {
  display: none;
}

body.zg-extras-panel-sticky .zg-v3__panel .zg-extras-sticky-trigger {
  display: inline-flex;
}

/* SEARCH-page exception: sticky-bar уже содержит
   `.zgcat-fdrop[data-fdrop="category"]` chip — он живёт в `.zg-v3__sorts >
   .zgcat-sort-controls` (sort-row), которые JS перемещает в panel целиком.
   Nested chip collapsed (grid-panel «видит» только direct children).
   JS-injected `.zg-extras-sticky-trigger` <a> с inline 24×24 SVG из 4
   rect'ов получается 52×52 (не указан размер на <svg>) и читается как
   `[][]` placeholders → дубль.

   Fix CSS-only:
   - hide дубль sticky-trigger на search-page
   - `display: contents` на `.zg-v3__sorts` + `.zgcat-sort-controls` чтобы
     их дети стали direct grid-children panel'а
   - category chip получает явный `grid-column: 6` (loudness:5, density:7) */
body.zg-extras-panel-sticky .zg-v3-search.zg-v3 .zg-extras-sticky-trigger,
body.zg-extras-panel-sticky .zg-v3-search .zg-extras-sticky-trigger {
  display: none !important;
}

body.zg-extras-panel-sticky .zg-v3-search .zg-v3__panel > .zg-v3__sorts,
body.zg-extras-panel-sticky .zg-v3-search .zg-v3__panel > .zg-v3__sorts > .zgcat-sort-controls {
  display: contents !important;
}

/* sort-row's view-mode buttons leak into grid через display:contents.
   В sticky они не нужны (sort = sort-dropdown в col 1). */
body.zg-extras-panel-sticky .zg-v3-search .zg-v3__panel .zg-v3__sorts .zg-v3__btn,
body.zg-extras-panel-sticky .zg-v3-search .zg-v3__panel .zgcat-sort-controls .zg-v3__btn {
  display: none !important;
}

/* Desktop sticky grid: `auto auto auto auto auto 1fr auto auto`
   (col6=spacer, col7=density, col8=trigger). На search trigger скрыт;
   category chip встаёт в его слот (col 8 → justify-self:end).
   ВАЖНО: только desktop (≥521). На mobile (≤520) site CSS ставит chip
   в col 4 — мы не мешаем, иначе chip уезжает в col 8 implicit grid. */
@media (min-width: 521px) {
  body.zg-extras-panel-sticky .zg-v3-search .zg-v3__panel .zgcat-fdrop[data-fdrop=category] {
    grid-column: 8;
    grid-row: 1;
    justify-self: end;
    min-width: 0;
  }
}
/* sort-dropdown — виден только в sticky, белый фон + синяя рамка для
   контраста с Громкость/Битрейт/Тип droprops (у них off-white).
   .zg-extras-dur-dropdown — аналогично, виден ТОЛЬКО при ≤920 sticky
   (см. @media block ниже). На широких экранах duration-chips живут
   inline-row внутри .zg-v3__filters, dropdown скрыт. */
.zg-extras-sort-dropdown,
.zg-extras-dur-dropdown {
  display: none;
}

/* Высокая specificity (0,3,1) для дублирующего hide — иначе rule 3251
   `body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop { display: inline-flex }`
   перебивает базовое .zg-extras-dur-dropdown { display: none } (0,1,0)
   и dropdown показывается даже на широких экранах вместе с inline-presets. */
body.zg-extras-panel-sticky .zg-v3__panel .zg-extras-dur-dropdown {
  display: none;
}

body.zg-extras-panel-sticky .zg-extras-sort-dropdown {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  position: relative;
  margin: 0;
}

/* Sort-trigger в sticky-bar — белый фон + контрастная рамка, чтобы плашка
   читалась отдельно от sticky-bar (а не сливалась soft-blue фоном). Site
   .zgcat-fdrop.is-open / .zg-v3__btn.is-active могут давать тинт-фон —
   override через явный селектор + .is-open вариант. */
body.zg-extras-panel-sticky .zg-extras-sort-dropdown .zgcat-fdrop-toggle,
body.zg-extras-panel-sticky .zg-extras-sort-dropdown.is-open .zgcat-fdrop-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--pr-blue, #2e58ef);
  background: #fff;
  border: 1.5px solid var(--pr-blue, #2e58ef);
  border-radius: 6px;
  cursor: pointer;
  line-height: 1.3;
  white-space: nowrap;
  transition: background 0.15s, box-shadow 0.15s;
  font-family: inherit;
  box-shadow: 0 1px 0 rgba(46, 88, 239, 0.06);
}

body.zg-extras-panel-sticky .zg-extras-sort-dropdown .zgcat-fdrop-toggle:hover {
  background: var(--l-blue, #eef4f9);
  box-shadow: 0 2px 6px -2px rgba(46, 88, 239, 0.25);
}

body.zg-extras-panel-sticky .zg-extras-sort-dropdown .zgcat-fdrop-caret {
  display: inline-block;
  width: 8px;
  height: 8px;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>") center/contain no-repeat;
  transition: transform 0.18s ease;
  opacity: 0.55;
  flex-shrink: 0;
}

body.zg-extras-panel-sticky .zg-extras-sort-dropdown.is-open .zgcat-fdrop-caret {
  transform: rotate(180deg);
  opacity: 1;
}

body.zg-extras-panel-sticky .zg-extras-sort-dropdown .zgcat-fdrop-chips {
  position: absolute;
  top: calc(100% + 6px);
  /* Right-anchor: popup растёт ВЛЕВО от правого края trigger'а — на узких
     экранах не уезжает за viewport справа, и визуально prefer-left как
     просил юзер ("давай влево чтобы в мобилке было отпозиционировано"). */
  left: auto;
  right: 0;
  min-width: max-content;
  display: flex;
  flex-direction: column;
  /* stretch — иначе items центрируются (rule 3315 set align-items: center
     каскадно). В column-flex stretch = full-width, items text-align: left. */
  align-items: stretch;
  gap: 1px;
  padding: 4px;
  background: #fff;
  border: 1px solid var(--border-card, #dfe7f3);
  border-radius: 6px;
  box-shadow: 0 8px 18px -8px rgba(15, 23, 42, 0.18);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity 0.18s ease, transform 0.18s ease, visibility 0s linear 0.18s;
  pointer-events: none;
  z-index: 100;
  white-space: nowrap;
}

body.zg-extras-panel-sticky .zg-extras-sort-dropdown.is-open .zgcat-fdrop-chips {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  pointer-events: auto;
  transition: opacity 0.18s ease, transform 0.22s cubic-bezier(0.2, 0.8, 0.3, 1), visibility 0s;
}

/* Explicit selector с .zgcat-fdrop-chips parent — выше specificity (0,5,1)
   чтобы выиграть у любых site `.zg-v3__btn` правил вне зависимости от
   их селекторов. Также явный margin: 0, height: auto, min-height: 0 —
   чтобы убрать все user-agent / inheritance крупные дефолты. */
body.zg-extras-panel-sticky .zg-v3__panel .zg-extras-sort-dropdown .zgcat-fdrop-chips .zg-extras-sort-item,
body.zg-extras-panel-sticky .zg-v3__panel .zg-extras-sort-dropdown .zgcat-fdrop-chips .zg-v3__btn {
  display: block;
  margin: 0;
  padding: 4px 12px;
  height: auto;
  min-height: 0;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: #475569;
  font-family: inherit;
  font-weight: 500;
  font-size: 11.5px;
  line-height: 1.2;
  letter-spacing: 0;
  text-align: left;
  text-decoration: none;
  text-transform: none;
  white-space: nowrap;
  cursor: pointer;
  box-sizing: border-box;
  transition: background 0.1s, color 0.1s;
}

body.zg-extras-panel-sticky .zg-v3__panel .zg-extras-sort-dropdown .zgcat-fdrop-chips .zg-extras-sort-item:hover,
body.zg-extras-panel-sticky .zg-v3__panel .zg-extras-sort-dropdown .zgcat-fdrop-chips .zg-v3__btn:hover {
  background: var(--l-blue, #eef4f9);
  color: var(--pr-blue, #2e58ef);
}

body.zg-extras-panel-sticky .zg-v3__panel .zg-extras-sort-dropdown .zgcat-fdrop-chips .zg-extras-sort-item.is-active,
body.zg-extras-panel-sticky .zg-v3__panel .zg-extras-sort-dropdown .zgcat-fdrop-chips .zg-v3__btn.is-active {
  color: var(--pr-blue, #2e58ef);
  font-weight: 600;
  background: rgba(46, 88, 239, 0.06);
}

/* === ЗАДАЧА 7. .zgcat-fdrop (type/loudness) внутри sticky panel ============= */
/* Site rules scoped к .zg-v3__filters.zgcat-rows. В sticky filters содержит
   только duration row, а type/loudness переносим в panel колонками 3/4.
   Дублируем стили scoped к sticky panel. */
/* :not(.zg-extras-dur-dropdown) — dur-dropdown управляется собственными
   правилами (display: none по умолчанию + inline-flex в @media ≤920).
   Без exclusion это правило (specificity 0,3,1) перебивало бы базовое
   .zg-extras-dur-dropdown { display: none } и dropdown показывался бы
   на широких экранах вместе с inline-presets. */
body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop:not(.zg-extras-dur-dropdown):not(.zg-extras-sort-dropdown) {
  display: inline-flex;
  align-items: center;
  position: relative;
}

/* Toggle-стиль для type/loudness/bitrate в sticky panel.
   :not(.zg-extras-sort-dropdown) — sort-dropdown имеет свои правила выше
   (белый фон + контрастная синяя рамка ВСЕГДА) и не должна получать
   off-white фон или blue-tint при .is-open. */
body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop:not(.zg-extras-sort-dropdown) .zgcat-fdrop-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--zgcat-muted, #6c7390);
  background: #f8fafc;
  border: 1px solid var(--zgcat-border, #dfe7f3);
  border-radius: 6px;
  cursor: pointer;
  line-height: 1.3;
  white-space: nowrap;
  transition: all 0.15s;
  font-family: inherit;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop:not(.zg-extras-sort-dropdown) .zgcat-fdrop-toggle:hover {
  color: #0f172a;
  border-color: #c6d4e8;
  background: #f1f5f9;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop:not(.zg-extras-sort-dropdown).is-open .zgcat-fdrop-toggle,
body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop:not(.zg-extras-sort-dropdown).is-active .zgcat-fdrop-toggle {
  color: var(--pr-blue, #2e58ef);
  background: var(--l-blue, #eef4f9);
  border-color: rgba(46, 88, 239, 0.25);
  font-weight: 600;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop-icon {
  display: inline-block;
  width: 12px;
  height: 12px;
  background-color: currentColor;
  flex-shrink: 0;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop-icon-grid {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='3' width='8' height='8' rx='1.5'/><rect x='13' y='3' width='8' height='8' rx='1.5'/><rect x='3' y='13' width='8' height='8' rx='1.5'/><rect x='13' y='13' width='8' height='8' rx='1.5'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='3' width='8' height='8' rx='1.5'/><rect x='13' y='3' width='8' height='8' rx='1.5'/><rect x='3' y='13' width='8' height='8' rx='1.5'/><rect x='13' y='13' width='8' height='8' rx='1.5'/></svg>") center/contain no-repeat;
}

/* Tag icon — в sticky-mode dropdown переехал в .zg-v3__panel,
   родительский селектор `.zg-v3__filters.zgcat-rows` не матчится,
   из-за чего mask отваливался и иконка рендерилась квадратом. */
body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop-icon-tag {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z'/><circle cx='7' cy='7' r='1.5' fill='black'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z'/><circle cx='7' cy='7' r='1.5' fill='black'/></svg>") center/contain no-repeat;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop-caret {
  display: inline-block;
  width: 8px;
  height: 8px;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>") center/contain no-repeat;
  transition: transform 0.18s ease;
  opacity: 0.55;
  flex-shrink: 0;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop.is-open .zgcat-fdrop-caret {
  transform: rotate(180deg);
  opacity: 1;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop-chips {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  left: auto;
  min-width: 100%;
  display: inline-flex;
  flex-wrap: wrap;
  /* flex-start (= start) — пиннит items к левому краю в column-flex
     popup'ах (sort/loudness/bitrate/type все column-mode в sticky).
     Раньше было center → items центрировались по cross-axis. */
  align-items: flex-start;
  gap: 4px;
  padding: 6px;
  background: #fff;
  border: 1px solid var(--border-card, #dfe7f3);
  border-radius: 8px;
  box-shadow: 0 6px 18px -4px rgba(15, 23, 42, 0.12);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity 0.18s, transform 0.18s, visibility 0s linear 0.18s;
  pointer-events: none;
  z-index: 100;
  white-space: nowrap;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop.is-open .zgcat-fdrop-chips {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  pointer-events: auto;
  transition: opacity 0.18s, transform 0.22s cubic-bezier(0.2, 0.8, 0.3, 1), visibility 0s;
}

/* === Items внутри fdrop-popup в sticky-mode (loudness / bitrate / type) ====
   Site CSS на `.zg-v3__filters.zgcat-rows .zgcat-fdrop-chips .zg-v3__btn`
   не достигает их в sticky (они физически перенесены в .zg-v3__panel
   напрямую). Без этого блока items рендерятся сырыми underline-ссылками.

   Стиль: column-list (как sort-dropdown «Популярные»), без border-чипов.
   Узкая полоса фильтрового popup'а (~110px width родителя) с border-чипами
   wraps в кучу строк → выглядит «гигантскими плитками». Column-list даёт
   чистый компактный list-меню, как нативный <select>. */
body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop:not(.zg-extras-sort-dropdown):not(.zgcat-fdrop-music-tags) .zgcat-fdrop-chips {
  flex-direction: column;
  align-items: stretch;
  flex-wrap: nowrap;
  gap: 1px;
  padding: 4px;
  min-width: max-content;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop:not(.zg-extras-sort-dropdown):not(.zgcat-fdrop-music-tags) .zgcat-fdrop-chips .zg-v3__btn,
body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop:not(.zg-extras-sort-dropdown):not(.zgcat-fdrop-music-tags) .zgcat-fdrop-chips .zgcat-chip-all {
  display: block;
  margin: 0;
  padding: 4px 12px;
  height: auto;
  min-height: 0;
  width: auto;
  min-width: 0;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: #475569;
  font-family: inherit;
  font-weight: 500;
  font-size: 11.5px;
  line-height: 1.2;
  letter-spacing: 0;
  text-align: left;
  text-decoration: none;
  text-transform: none;
  white-space: nowrap;
  cursor: pointer;
  box-sizing: border-box;
  transition: background 0.1s, color 0.1s;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop:not(.zg-extras-sort-dropdown):not(.zgcat-fdrop-music-tags) .zgcat-fdrop-chips .zg-v3__btn:hover,
body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop:not(.zg-extras-sort-dropdown):not(.zgcat-fdrop-music-tags) .zgcat-fdrop-chips .zgcat-chip-all:hover {
  background: var(--l-blue, #eef4f9);
  color: var(--pr-blue, #2e58ef);
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop:not(.zg-extras-sort-dropdown):not(.zgcat-fdrop-music-tags) .zgcat-fdrop-chips .zg-v3__btn.is-active,
body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop:not(.zg-extras-sort-dropdown):not(.zgcat-fdrop-music-tags) .zgcat-fdrop-chips .zgcat-chip-all.is-active {
  background: rgba(46, 88, 239, 0.07);
  color: var(--pr-blue, #2e58ef);
  font-weight: 600;
}

/* === Music-tags dropdown в STICKY — переопределяем как vertical-list =========
   В sticky-bar dropdown «Теги» должен выпадать как у Sort/Duration: узкий
   compact список вертикально. Override wide-grid стилей с line 3030 (min-width
   540 + flex-wrap) для sticky-context. */
body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop-music-tags .zgcat-fdrop-chips {
  min-width: 0;
  width: 240px;
  max-width: calc(100vw - 32px);
  flex-direction: column;
  flex-wrap: nowrap;
  padding: 10px;
  gap: 8px;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-music-tag-head {
  flex-direction: row;
  flex-wrap: nowrap;
  margin: 0;
  gap: 6px;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-music-tag-search {
  flex: 1 1 100%;
  width: 100%;
  height: 30px;
  padding: 4px 10px;
  font-size: 12px;
  margin: 0;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-music-tag-list {
  /* В sticky popup узкий — column-list вместо grid (base default — grid
     auto-fill). display: flex !important чтобы перебить base display:grid
     (specificity 0,3,1 vs base 0,2,0 + источник позже — побеждает source-
     order, но для display сменили формально для надёжности). */
  display: flex !important;
  grid-template-columns: none;
  flex-direction: column;
  flex-wrap: nowrap;
  align-items: stretch;
  gap: 1px;
  width: 100%;
  max-height: 280px;
  padding: 4px;
}

/* В sticky chip-style идентичен non-sticky. После :not(.zgcat-fdrop-music-tags)
   на общем sticky-fdrop-chips правиле — конкурирующих !important нет, наследует
   base без хака. Selector specificity 0,4,1 > base 0,3,0 → выигрывает чисто. */
body.zg-extras-panel-sticky .zg-v3__panel .zgcat-music-tag-list .zg-v3__btn {
  /* Размер согласован с loudness/bitrate sticky chips. Display: block (не
     flex!) — flex-item с одним text-узлом коллапсирует до 0 content высоты
     в flex-column контейнере (flex-shrink) → текст clipped. Block layout
     корректно занимает высоту по line-height. */
  display: block;
  width: 100%;
  flex-shrink: 0;
  padding: 4px 12px;
  font-size: 11.5px;
  font-weight: 500;
  line-height: 1.3;
  text-transform: capitalize;
  text-decoration: none;
  text-align: left;
  border-radius: 4px;
  background: #fff;
  color: #475569;
  border: 1px solid #e7ecf3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  box-sizing: border-box;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-music-tag-list .zg-v3__btn:hover {
  background: var(--l-blue, #eef4f9);
  color: var(--pr-blue, #2e58ef);
  border-color: rgba(46, 88, 239, 0.25);
  transform: none;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-music-tag-list .zg-v3__btn.is-active {
  background: var(--pr-blue, #2e58ef);
  border-color: var(--pr-blue, #2e58ef);
  color: #fff;
}

/* === Sticky-bar @ ≤920px — компактный режим ================================
   На узких экранах в sticky слишком много рядом стоящих элементов:
   sort + duration-row + groups + loudness + density + trigger.
   Скрываем groups, loudness и duration-row, заменяем последний на
   .zg-extras-dur-dropdown (JS-built, см. v3_showTag.php inline-script).
   Bitrate уже скрыт в sticky (см. правило ранее). Остаётся минимальный
   набор: sort | duration-dd | density | trigger. */
@media (max-width: 920px) {
  /* Перестраиваем grid: 5 колонок. На ≤520 все равномерные minmax(0, 1fr) —
     тогда длинные слова в toggle'ах (Популярные) ужмутся через ellipsis,
     ничего не вылезет за viewport. */
  body.zg-extras-panel-sticky .zg-v3__panel {
    grid-template-columns: auto auto 1fr auto auto;
  }
  /* MUSIC pages: BPM остаётся в sticky → нужна доп. колонка между фильтрами
     и spacer'ом, иначе BPM и density сидят в одной (col 4) и накладываются. */
  body.zg-extras-panel-sticky .zg-v3[data-v3-is-music="1"] .zg-v3__panel,
  body.zg-extras-panel-sticky.zg-v3[data-v3-is-music="1"] .zg-v3__panel {
    grid-template-columns: auto auto auto 1fr auto auto;
  }
}
@media (max-width: 920px) and (max-width: 520px) {
  body.zg-extras-panel-sticky .zg-v3__panel {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr) auto auto;
    gap: 0 6px;
    padding: 10px 10px;
  }
  /* MUSIC ≤520: 4 равномерных + density (скрыт ниже) + trigger. BPM в col 4. */
  body.zg-extras-panel-sticky .zg-v3[data-v3-is-music="1"] .zg-v3__panel,
  body.zg-extras-panel-sticky.zg-v3[data-v3-is-music="1"] .zg-v3__panel {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr) auto;
  }
}
@media (max-width: 920px) {
  /* Прячем оригинальные filters, type, loudness — duration-dropdown их
     заменяет UX-wise (длительность через dropdown), а type/loudness юзер
     может найти через скролл вверх к non-sticky panel. */
  body.zg-extras-panel-sticky .zg-v3__panel .zg-v3__filters,
  body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop[data-fdrop=type],
  body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop[data-fdrop=loudness] {
    display: none;
  }
  /* MUSIC: BPM перевешиваем в col 3 (между tags и spacer'ом). На music-pages
     music_tag («Теги») висит в col 3 базово, но в sticky его уже скрывает
     основной набор — а BPM наоборот нужен для music. Берём col 3 под BPM. */
  body.zg-extras-panel-sticky .zg-v3[data-v3-is-music="1"] .zg-v3__panel .zgcat-fdrop[data-fdrop=bpm],
  body.zg-extras-panel-sticky.zg-v3[data-v3-is-music="1"] .zg-v3__panel .zgcat-fdrop[data-fdrop=bpm] {
    grid-column: 3;
    grid-row: 1;
  }
  body.zg-extras-panel-sticky .zg-v3[data-v3-is-music="1"] .zg-v3__panel .zgcat-density,
  body.zg-extras-panel-sticky.zg-v3[data-v3-is-music="1"] .zg-v3__panel .zgcat-density {
    grid-column: 5;
  }
  body.zg-extras-panel-sticky .zg-v3[data-v3-is-music="1"] .zg-v3__panel .zg-extras-sticky-trigger,
  body.zg-extras-panel-sticky.zg-v3[data-v3-is-music="1"] .zg-v3__panel .zg-extras-sticky-trigger {
    grid-column: 6;
  }
  /* Music-tag «Теги» — он есть в filters wrapper, который уже hidden выше.
     Если site вытащил его в panel напрямую (как BPM) — тоже скрываем в sticky
     на узком, чтобы BPM был основным music-фильтром на mobile. */
  body.zg-extras-panel-sticky .zg-v3[data-v3-is-music="1"] .zg-v3__panel .zgcat-fdrop[data-fdrop=music_tag],
  body.zg-extras-panel-sticky.zg-v3[data-v3-is-music="1"] .zg-v3__panel .zgcat-fdrop[data-fdrop=music_tag] {
    display: none;
  }
  /* Показываем duration-dropdown (column-flex как sort, но off-white toggle
     вместо blue-border — он filter, не primary-action). */
  body.zg-extras-panel-sticky .zg-v3__panel .zg-extras-dur-dropdown {
    display: inline-flex;
    align-items: center;
    position: relative;
    margin: 0;
    grid-column: 2;
    grid-row: 1;
  }
  /* Density / trigger перемещаются в col 4 / 5 (грид-по-position). */
  body.zg-extras-panel-sticky .zg-v3__panel .zgcat-density {
    grid-column: 4;
  }
  body.zg-extras-panel-sticky .zg-v3__panel .zg-extras-sticky-trigger {
    grid-column: 5;
  }
  /* Toggle стиль duration-dropdown — высота должна совпадать с sort-dropdown
     «Популярные ▾» (рядом стоят, juxtaposed). Поэтому padding 5px 12px и
     border 1.5px — те же что у sort. Цвет border — серый (filter), не blue
     (filter-trigger ≠ primary). */
  body.zg-extras-panel-sticky .zg-v3__panel .zg-extras-dur-dropdown .zgcat-fdrop-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 12px;
    font-family: inherit;
    font-weight: 600;
    font-size: 12.5px;
    line-height: 1.3;
    color: var(--zgcat-muted, #6c7390);
    background: #f8fafc;
    border: 1.5px solid var(--zgcat-border, #dfe7f3);
    border-radius: 6px;
    cursor: pointer;
    white-space: nowrap;
    transition: all 0.15s;
  }
  body.zg-extras-panel-sticky .zg-v3__panel .zg-extras-dur-dropdown .zgcat-fdrop-toggle:hover {
    color: #0f172a;
    border-color: #c6d4e8;
    background: #f1f5f9;
  }
  body.zg-extras-panel-sticky .zg-v3__panel .zg-extras-dur-dropdown.is-open .zgcat-fdrop-toggle,
  body.zg-extras-panel-sticky .zg-v3__panel .zg-extras-dur-dropdown.is-active .zgcat-fdrop-toggle {
    color: var(--pr-blue, #2e58ef);
    background: var(--l-blue, #eef4f9);
    border-color: rgba(46, 88, 239, 0.25);
  }
}
/* ≤580: «Длительность» в sticky-toggle меняется на SVG-иконку часов
   (экономит ~80px). На search-page при 539px без этого «Длительность» забирает
   слишком много места рядом с BPM + Категории. Threshold поднят с 480 до 580. */
@media (max-width: 580px) {
  body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop[data-fdrop=duration] .zgcat-fdrop-toggle .zgcat-fdrop-text {
    /* Скрываем native текст «Длительность», заменяя SVG-иконкой через ::before. */
    font-size: 0;
    width: 14px;
    height: 14px;
    display: inline-block;
    background-color: currentColor;
    -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='9'/><polyline points='12 7 12 12 15.5 14'/></svg>") center/contain no-repeat;
    mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='9'/><polyline points='12 7 12 12 15.5 14'/></svg>") center/contain no-repeat;
  }
}
/* Самые узкие экраны — density тоже прячем. !important нужен — site base CSS
   ставит repeat(6,minmax(0,1fr)) и выигрывает без него.
   Category: 5-col layout (sort | BPM | Duration | 1fr | trigger).
   Search:   4-col layout (sort | BPM | 1fr | Категории) — Duration скрыт ниже. */
@media (max-width: 520px) {
  body.zg-extras-panel-sticky .zg-v3:not(.zg-v3-search) .zg-v3__panel {
    grid-template-columns: auto auto auto 1fr auto !important;
  }
  body.zg-extras-panel-sticky .zg-v3-search .zg-v3__panel {
    grid-template-columns: auto auto 1fr auto !important;
  }
  body.zg-extras-panel-sticky .zg-v3__panel .zgcat-density {
    display: none;
  }
  /* Category: BPM в col2, Duration в col3, trigger в col5. */
  body.zg-extras-panel-sticky .zg-v3:not(.zg-v3-search) .zg-v3__panel .zgcat-fdrop-bpm,
  body.zg-extras-panel-sticky .zg-v3:not(.zg-v3-search) .zg-v3__panel .zgcat-fdrop[data-fdrop=bpm] {
    display: inline-flex !important;
    grid-column: 2 !important;
    grid-row: 1 !important;
  }
  body.zg-extras-panel-sticky .zg-v3:not(.zg-v3-search) .zg-v3__panel .zgcat-fdrop-duration {
    grid-column: 3 !important;
    grid-row: 1 !important;
  }
  body.zg-extras-panel-sticky .zg-v3:not(.zg-v3-search) .zg-v3__panel .zg-extras-sticky-trigger {
    grid-column: 5 !important;
    grid-row: 1 !important;
    justify-self: end;
  }
}
/* Search-page sticky ≤520: 4-col layout (sort | BPM | 1fr | Категории).
   Site rule на line 3993 ставит BPM в col 4 безусловно — переопределяем на col 2.
   Категории chip явно в col 4 (последняя auto-колонка в нашем 4-col grid).
   Также hide Duration, density, и popup-anchor для sort-dropdown. */
@media (max-width: 520px) {
  body.zg-extras-panel-sticky .zg-v3-search .zg-v3__panel .zgcat-fdrop[data-fdrop=bpm] {
    grid-column: 2 !important;
    grid-row: 1 !important;
  }
  body.zg-extras-panel-sticky .zg-v3-search .zg-v3__panel .zgcat-fdrop[data-fdrop=category] {
    grid-column: 4 !important;
    grid-row: 1 !important;
    justify-self: end;
  }
  body.zg-extras-panel-sticky .zg-v3-search .zg-v3__panel .zgcat-fdrop-duration,
  body.zg-extras-panel-sticky .zg-v3-search .zg-v3__panel .zg-extras-dur-dropdown,
  body.zg-extras-panel-sticky .zg-v3-search .zg-v3__panel .zgcat-density {
    display: none !important;
  }
  body.zg-extras-panel-sticky .zg-extras-sort-dropdown .zgcat-fdrop-chips {
    left: 0 !important;
    right: auto !important;
  }
}
/* ≤380px на category: «Музыка»-trigger прячем — фильтры (Sort + BPM + Duration)
   важнее, а Музыка это popup со списком ссылок на категории. На 390px (iPhone 12
   Pro) trigger ещё влезает естественным размером без min-width. */
@media (max-width: 380px) {
  body.zg-extras-panel-sticky .zg-v3:not(.zg-v3-search) .zg-v3__panel .zg-extras-sticky-trigger {
    display: none !important;
  }
  body.zg-extras-panel-sticky .zg-v3:not(.zg-v3-search) .zg-v3__panel {
    grid-template-columns: auto auto auto 1fr !important;
  }
}
/* BPM toggle: white-space:nowrap чтобы «BPM» не переносилось в sticky. */
body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop[data-fdrop=bpm] .zgcat-fdrop-toggle {
  white-space: nowrap;
}

/* ≤380px sticky ALL (category + search): сжимаем padding чтобы кнопки влезли
   в ~286–310px. На category: sort + dur-dd + trigger. На search: sort + BPM + Категории. */
@media (max-width: 380px) {
  body.zg-extras-panel-sticky .zg-v3__panel .zgcat-fdrop-toggle,
  body.zg-extras-panel-sticky .zg-extras-sort-dropdown .zgcat-fdrop-toggle {
    padding-left: 8px !important;
    padding-right: 8px !important;
  }
}
/* === ЗАДАЧА 9. H1 + поиск в одной строке =================================
   Render: v3_showTag.php
     <div.zg-v3__h1-row>
       <h1>...</h1>
       <?php renderPartial('_v2_search_form') ?>  → <div.box1.srchbox><form>...</form></div>
     </div>
   Layout:
     Desktop ≥980: h1 слева (flex 1 1 auto), search справа (max-width 440).
     Mobile <980:  flex-direction: column + order:-1 на search → поиск ВЫШЕ h1
                   (mobile UX — поиск первое что доступно при touch-навигации).
   Партиал site-генерируется legacy CSS (.box1.srchbox), нужен hard reset
   margin/padding/background/box-shadow + max-height чтобы убрать legacy
   высоту-распирающий filler внутри. */
.zg-v3__h1-row {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  margin: 0 0 8px;
  width: 100%;
}
.zg-v3__h1-row > h1 {
  margin: 0;
  /* flex-basis: 0 — h1 стартует с 0 ширины и grow'ит до оставшегося места.
     С `flex: 1 1 auto` h1 (display: block) получает basis = 100% (browser
     default for block) → ест всю строку, search wrap'ом уходит вниз.
     С basis: 0 у обоих flex:grow=1 разделяют пространство, search ограничен
     max-width: 440 → h1 получает остаток (~700px на десктопе). */
  flex: 1 1 0;
  min-width: 0;
  width: auto;
  /* h1 ВСЕГДА одна строка с ellipsis на десктопе. При :focus-within search
     расширяется → h1 пожимается. Без nowrap+ellipsis на blur transition
     (0.3s обратно к 280) во время сжатия h1 кратковременно становится
     2-строчным, row прыгает по высоте. На мобиле (column-stack) wrap
     восстанавливается ниже в @media. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.zg-v3__h1-row .box1.srchbox {
  flex: 1 1 280px;
  max-width: 280px;
  min-width: 220px;
  width: auto;
  margin: 0;
  padding: 0;
  background: transparent;
  border: 0;
  box-shadow: none;
  display: flex;
  align-items: stretch;
  gap: 6px;
  flex-wrap: nowrap;
  height: auto;
  min-height: 0;
  max-height: 38px;
  /* При клике в input search раздвигается влево с 280→440px,
     h1 синхронно пожимается. focus-within ловит фокус на любом
     вложенном input/button. */
  transition: max-width 0.3s ease;
}
.zg-v3__h1-row .box1.srchbox:focus-within {
  max-width: 440px;
}
.zg-v3__h1-row .box1.srchbox form {
  margin: 0;
  padding: 0;
  background: transparent;
  border: 0;
  box-shadow: none;
  display: flex;
  align-items: stretch;
  gap: 6px;
  flex-wrap: nowrap;
  width: 100%;
  height: auto;
  min-height: 0;
  max-height: 38px;
}
.zg-v3__h1-row form input[type=search],
.zg-v3__h1-row form input[name=s] {
  flex: 1 1 auto;
  min-width: 0;
  width: auto;
  height: 38px;
  padding: 6px 12px;
  border: 1px solid var(--border-card, #dfe7f3);
  border-radius: 8px;
  font-family: inherit;
  font-weight: 400;
  font-size: 14px;
  line-height: 1.3;
  background: #fff;
  color: #0f172a;
  outline: none;
  box-shadow: none;
}
.zg-v3__h1-row form input[type=search]:focus,
.zg-v3__h1-row form input[name=s]:focus {
  border-color: var(--pr-blue, #2e58ef);
}
.zg-v3__h1-row form button {
  flex: 0 0 auto;
  height: 38px;
  padding: 0 16px;
  background: var(--pr-blue, #2e58ef);
  color: #fff;
  border: 0;
  border-radius: 8px;
  cursor: pointer;
  font-family: inherit;
  font-weight: 600;
  font-size: 13px;
  line-height: 1;
  box-shadow: none;
  transition: background 0.15s;
}
.zg-v3__h1-row form button:hover {
  background: #2347c9;
}

/* Music на десктопе: H1 может занимать 2 строки вместо ellipsis.
   У музыкальных категорий нет search-формы в h1-row → нет focus-within
   transition → row не прыгает при wrap'е. Sound-категории сохраняют
   ellipsis (там есть search). */
.zg-v3[data-v3-is-music="1"] .zg-v3__h1-row > h1 {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  line-height: 1.2;
}

/* Узкие экраны: column-stack + поиск ВЫШЕ h1 через flex order. */
@media (max-width: 980px) {
  .zg-v3__h1-row {
    flex-direction: column;
    align-items: stretch;
  }
  .zg-v3__h1-row > h1,
  .zg-v3__h1-row .box1.srchbox {
    max-width: none;
    width: 100%;
  }
  .zg-v3__h1-row .box1.srchbox {
    order: -1;
  }
  /* На мобиле возвращаем wrap для h1 (не нужен ellipsis, есть полная ширина). */
  .zg-v3__h1-row > h1 {
    order: 1;
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
  }
}
/* Search-page (.zg-v3-search) — search-форма всегда ПОД H1 на всю ширину
   и крупнее (44px height, 16px padding). Юзер видит query в H1 + canonical
   input. Chip-«ключ ×» удалён из meta-strip — теперь native × в input
   делает ту же работу через `search`-event handler в v3_search.php. */
.zg-v3-search .zg-v3__h1-row {
  flex-direction: column;
  align-items: stretch;
}

.zg-v3-search .zg-v3__h1-row > h1 {
  order: 0;
  max-width: none;
  width: 100%;
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
}

.zg-v3-search .zg-v3__h1-row .box1.srchbox {
  order: 1; /* search ПОД h1, не выше как на /category/ */
  max-width: none;
  width: 100%;
  min-width: 0;
  max-height: 48px; /* legacy site CSS пытается распёрить srchbox
   до ~280px filler-paddings; cap'аем явно */
  transition: none;
}

.zg-v3-search .zg-v3__h1-row .box1.srchbox:focus-within {
  max-width: none; /* full-width не expand'ится — он уже 100% */
  max-height: 48px;
}

.zg-v3-search .zg-v3__h1-row .box1.srchbox form {
  max-height: 48px;
  gap: 8px;
  position: relative;
}

.zg-v3-search .zg-v3__h1-row form input[type=search],
.zg-v3-search .zg-v3__h1-row form input[name=s] {
  height: 48px;
  padding: 10px 16px;
  font-size: 15px;
  border-radius: 10px;
}

.zg-v3-search .zg-v3__h1-row form button {
  height: 48px;
  padding: 0 24px;
  font-size: 14px;
  border-radius: 10px;
}

/* Custom clear-× — JS-ом инжектится ВНУТРИ wrapper'а вокруг input
   (.zg-search-input-wrap → position:relative). × позиционируется
   absolute поверх правого края input area: квадрат с brand-radius
   --br (8px), лёгкий серый фон, без border. Native ::-webkit-search-
   cancel-button скрыт (CSS), input получает padding-right чтобы текст
   не залезал под кнопку. */
.zg-v3-search .zg-v3__h1-row form .zg-search-input-wrap {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
}

.zg-v3-search .zg-v3__h1-row form .zg-search-input-wrap input[type=search],
.zg-v3-search .zg-v3__h1-row form .zg-search-input-wrap input[name=s] {
  width: 100%;
  padding-right: 48px; /* зарезервировать место под × */
}

.zg-v3-search .zg-v3__h1-row form .zg-search-clear {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  width: 36px;
  height: 36px;
  padding: 0;
  margin: 0;
  background: #f1f5f9;
  color: #64748b;
  border: 0;
  border-radius: var(--br, 8px);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
  box-shadow: none;
  font-size: 0;
}

.zg-v3-search .zg-v3__h1-row form .zg-search-clear:hover {
  background: #e2e8f0;
  color: #0f172a;
}

.zg-v3-search .zg-v3__h1-row form .zg-search-clear svg {
  display: block;
  width: 12px;
  height: 12px;
}

/* Specificity-override: our `.zg-search-clear { display: inline-flex }`
   beats the default `[hidden] { display: none }` (specificity 0,4,0 vs
   0,0,1). Without this the button stays visible even when input.value is
   empty (its `hidden` attribute is set but ignored). */
.zg-v3-search .zg-v3__h1-row form .zg-search-clear[hidden] {
  display: none;
}

/* Skрытие нативной ::-webkit-search-cancel-button — она дублировала бы
   наш custom × и съедала бы клик. */
.zg-v3-search .zg-v3__h1-row form input[type=search]::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
  display: none;
}

/* Busy-state (применяется JS-ом при clear): dim form + spinner внутри × */
.zg-v3-search .zg-v3__h1-row form.is-busy {
  opacity: 0.85;
  pointer-events: none;
}

.zg-v3-search .zg-v3__h1-row form .zg-search-clear .zg-spinner {
  width: 16px;
  height: 16px;
  animation: zg-fdrop-spin 0.7s linear infinite;
}

/* === ЗАДАЧА 10. Админ-popup для категории =================================
   Trigger «⚙ Админка» в .zgcat-meta-strip (admin-only, PHP-условие).
   Click → JS добавляет .is-open на .zg-v3-admin-popup → modal-overlay
   с backdrop. Внутри уже существующий <section.zg-v3-admin> со всеми
   действиями (редактирование группы/треки/исключения). zg-v3-admin.css
   уже стилизует контент панели — здесь только overlay-обёртка. */
/* Trigger «Админка» — оформлена как обычный inline-span в meta-strip
   (рядом с «бесплатно», «mp3 · wav» и т.д.). Без рамки/фона/padding —
   только icon + text, серый цвет meta-strip. Hover — синий, чтобы
   подсказать что это интерактивный элемент. */
.zgcat-meta-admin {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0;
  margin: 0;
  font-family: inherit;
  font-size: inherit;
  font-weight: inherit;
  line-height: inherit;
  color: inherit;
  background: transparent;
  border: 0;
  border-radius: 0;
  cursor: pointer;
  transition: color 0.12s;
}

.zgcat-meta-admin:hover {
  color: var(--pr-blue, #2e58ef);
}

.zgcat-meta-admin-icon {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
  display: inline-block;
  vertical-align: -2px;
}

.zg-v3-admin-popup {
  display: none;
  position: fixed;
  inset: 0;
  /* Solid darker overlay вместо blur(2px) — admin-only modal, blur незаметен. */
  background: rgba(15, 23, 42, 0.62);
  z-index: 100000;
  padding: 32px 16px;
  overflow-y: auto;
  align-items: flex-start;
  justify-content: center;
}

.zg-v3-admin-popup.is-open {
  display: flex;
}

/* Inner panel — контент админки. Существующий .zg-v3-admin styled через
   zg-v3-admin.css (`.zg-v3-admin__details` etc). Тут только override
   margin/width для centered-modal placement. */
.zg-v3-admin-popup .zg-v3-admin {
  width: 100%;
  max-width: 980px;
  margin: 0;
  pointer-events: auto;
  /* zg-v3-admin.css ставит margin: 16px 0 18px !important — обнулим */
}

.zg-v3-admin-popup .zg-v3-admin.zg-v3-admin {
  margin-top: 0;
  margin-bottom: 0;
}

@media (max-width: 640px) {
  .zg-v3-admin-popup {
    padding: 12px 8px;
  }
}
/* === ЗАДАЧА 8. Density-кнопки везде с light border ========================== */
/* Site CSS `.z-v3__sorts .zgcat-density-btn { border: 1px solid transparent }`
   делает невидимый бордер у inactive кнопок. На v9 layout density переехала
   в filters secondary и sticky panel — везде хотим видимую light-рамку. */
/* Density в .zg-v3__filters [data-row="secondary"] (нормальный режим v9). */
.zg-v3 .zg-v3__filters [data-row=secondary] .zgcat-density {
  display: inline-flex;
  align-items: center;
  background: transparent;
  gap: 6px;
  margin-left: 8px;
  position: relative;
}

/* Divider слева от density удалён по запросу — в music не нужен,
   density уезжает вправо через margin-left:auto, между ним и фильтрами
   уже есть естественный gap. */
.zg-v3 .zg-v3__filters [data-row=secondary] .zgcat-density::before {
  display: none;
}

.zg-v3 .zg-v3__filters [data-row=secondary] .zgcat-density-btn {
  width: 30px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid var(--border-card, #dfe7f3); /* light border всегда видна */
  padding: 0;
  cursor: pointer;
  color: var(--zgcat-muted-2, #6c7390);
  border-radius: 6px;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}

.zg-v3 .zg-v3__filters [data-row=secondary] .zgcat-density-btn:hover {
  color: #0f172a;
  background: #f1f5f9;
}

.zg-v3 .zg-v3__filters [data-row=secondary] .zgcat-density-btn.is-active {
  color: var(--pr-blue, #2e58ef);
  background: var(--l-blue, #eef4f9);
  border-color: rgba(46, 88, 239, 0.2);
}

.zg-v3 .zg-v3__filters [data-row=secondary] .zgcat-density-icon {
  display: inline-block;
  width: 14px;
  height: 14px;
  background-color: currentColor;
}

.zg-v3 .zg-v3__filters [data-row=secondary] .zgcat-density-icon-comfort {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='5' width='18' height='3.5' rx='1'/><rect x='3' y='10.5' width='18' height='3.5' rx='1'/><rect x='3' y='16' width='18' height='3.5' rx='1'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='5' width='18' height='3.5' rx='1'/><rect x='3' y='10.5' width='18' height='3.5' rx='1'/><rect x='3' y='16' width='18' height='3.5' rx='1'/></svg>") center/contain no-repeat;
}

.zg-v3 .zg-v3__filters [data-row=secondary] .zgcat-density-icon-compact {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='4' width='18' height='2' rx='1'/><rect x='3' y='8' width='18' height='2' rx='1'/><rect x='3' y='12' width='18' height='2' rx='1'/><rect x='3' y='16' width='18' height='2' rx='1'/><rect x='3' y='20' width='18' height='2' rx='1'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='4' width='18' height='2' rx='1'/><rect x='3' y='8' width='18' height='2' rx='1'/><rect x='3' y='12' width='18' height='2' rx='1'/><rect x='3' y='16' width='18' height='2' rx='1'/><rect x='3' y='20' width='18' height='2' rx='1'/></svg>") center/contain no-repeat;
}

/* Density в sticky panel — то же стили + flex enforce. */
body.zg-extras-panel-sticky .zg-v3__panel .zgcat-density-btn {
  width: 30px;
  height: 28px;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid var(--border-card, #dfe7f3);
  padding: 0;
  cursor: pointer;
  color: var(--zgcat-muted-2, #6c7390);
  border-radius: var(--zgcat-radius-sm, 6px);
  transition: background 0.15s, color 0.15s, border-color 0.15s;
  font-family: inherit;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-density-btn:hover {
  color: #0f172a;
  background: #f1f5f9;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-density-btn.is-active {
  color: var(--pr-blue, #2e58ef);
  background: var(--l-blue, #eef4f9);
  border-color: rgba(46, 88, 239, 0.2);
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-density-icon {
  display: inline-block;
  width: 14px;
  height: 14px;
  background-color: currentColor;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-density-icon-comfort {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='5' width='18' height='3.5' rx='1'/><rect x='3' y='10.5' width='18' height='3.5' rx='1'/><rect x='3' y='16' width='18' height='3.5' rx='1'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='5' width='18' height='3.5' rx='1'/><rect x='3' y='10.5' width='18' height='3.5' rx='1'/><rect x='3' y='16' width='18' height='3.5' rx='1'/></svg>") center/contain no-repeat;
}

body.zg-extras-panel-sticky .zg-v3__panel .zgcat-density-icon-compact {
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='4' width='18' height='2' rx='1'/><rect x='3' y='8' width='18' height='2' rx='1'/><rect x='3' y='12' width='18' height='2' rx='1'/><rect x='3' y='16' width='18' height='2' rx='1'/><rect x='3' y='20' width='18' height='2' rx='1'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='4' width='18' height='2' rx='1'/><rect x='3' y='8' width='18' height='2' rx='1'/><rect x='3' y='12' width='18' height='2' rx='1'/><rect x='3' y='16' width='18' height='2' rx='1'/><rect x='3' y='20' width='18' height='2' rx='1'/></svg>") center/contain no-repeat;
}

/* === Popup overlay со списком всех категорий ============================== */
/* Открывается JS-ом по клику на любой .zg-extras-popup-trigger.
   Содержимое (.zg-extras-cats-grid) populates JS-ом из .leftSupermenu .sblist a
   при первом open (lazy build). */
.zg-extras-cats-overlay {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, 0.55);
  /* z-index намеренно >2^31-2: перекрывает Яндекс/AdFox sticky-баннеры
     внизу, которые сидят в своём stacking-context с z=999999+. */
  z-index: 2147483646;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 60px 16px;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.18s ease, visibility 0s linear 0.18s;
  overflow-y: auto;
}

.zg-extras-cats-overlay.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.18s ease, visibility 0s linear 0s;
}

.zg-extras-cats-modal {
  background: #fff;
  border-radius: 14px;
  width: min(960px, 100%);
  max-height: calc(100vh - 120px);
  display: flex;
  flex-direction: column;
  box-shadow: 0 30px 60px -20px rgba(15, 23, 42, 0.4);
  overflow: hidden;
  transform: translateY(-12px);
  transition: transform 0.2s ease;
}

.zg-extras-cats-overlay.is-open .zg-extras-cats-modal {
  transform: translateY(0);
}

/* Страховка: пока попап открыт — прячем известные fixed/sticky-баннеры
   внизу. Z-index 2147483646 на оверлее должен покрывать всё, но Яндекс
   RTB/AdFox иногда инжектится в top-layer или iframe вне нашего DOM-
   контекста — в этом случае хайдим класс на <body>. */
body.zg-extras-cats-open .fixadbar,
body.zg-extras-cats-open .fixedAd,
body.zg-extras-cats-open #fixad,
body.zg-extras-cats-open .fixad,
body.zg-extras-cats-open [class*=adfox],
body.zg-extras-cats-open [id*=adfox],
body.zg-extras-cats-open .yandex_rtb,
body.zg-extras-cats-open [id^=yandex_rtb],
body.zg-extras-cats-open ins.adsbygoogle,
body.zg-extras-cats-open .zg-ad-fixed,
body.zg-extras-cats-open .zg-bottombar,
body.zg-extras-cats-open .zg-fixed-bottom,
body.zg-extras-cats-open [data-ad-fixed],
body.zg-extras-catfilter-open .fixadbar,
body.zg-extras-catfilter-open .fixedAd,
body.zg-extras-catfilter-open #fixad,
body.zg-extras-catfilter-open .fixad,
body.zg-extras-catfilter-open [class*=adfox],
body.zg-extras-catfilter-open [id*=adfox],
body.zg-extras-catfilter-open .yandex_rtb,
body.zg-extras-catfilter-open [id^=yandex_rtb],
body.zg-extras-catfilter-open ins.adsbygoogle,
body.zg-extras-catfilter-open .zg-ad-fixed,
body.zg-extras-catfilter-open .zg-bottombar,
body.zg-extras-catfilter-open .zg-fixed-bottom,
body.zg-extras-catfilter-open [data-ad-fixed] {
  visibility: hidden !important;
}

.zg-extras-cats-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 18px 20px;
  border-bottom: 1px solid var(--border-card, #dfe7f3);
  flex-shrink: 0;
}

.zg-extras-cats-title {
  font-family: inherit;
  font-weight: 600;
  font-size: 16px;
  line-height: 1.3;
  color: #0f172a;
  margin: 0;
  flex: 1;
}

.zg-extras-cats-close {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  border: 0;
  background: transparent;
  color: #64748b;
  font-family: inherit;
  font-weight: 400;
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  /* Centered glyph: flex-center чтобы × сидел ровно по центру кнопки. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}

.zg-extras-cats-close:hover {
  background: #f1f5f9;
  color: #0f172a;
}

.zg-extras-cats-body {
  flex: 1;
  overflow-y: auto;
  padding: 12px 20px 20px;
}

.zg-extras-cats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 6px 10px;
}

.zg-extras-cat-link {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-radius: 6px;
  color: #0f172a;
  font-family: inherit;
  font-weight: 400;
  font-size: 13px;
  line-height: 1.35;
  text-decoration: none;
  transition: background 0.12s;
  min-width: 0;
}

.zg-extras-cat-link:hover {
  background: var(--l-blue, #eef4f9);
  color: var(--pr-blue, #2e58ef);
}

.zg-extras-cat-link.is-current {
  background: var(--l-blue, #eef4f9);
  color: var(--pr-blue, #2e58ef);
  font-weight: 600;
}

.zg-extras-cat-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.zg-extras-cat-count {
  flex-shrink: 0;
  font-size: 11px;
  color: #94a3b8;
  font-variant-numeric: tabular-nums;
}

.zg-extras-cat-link:hover .zg-extras-cat-count {
  color: var(--pr-blue, #2e58ef);
}

.zg-extras-cats-foot {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 14px 20px 18px;
  border-top: 1px solid var(--border-card, #dfe7f3);
  flex-shrink: 0;
  background: #f8fafc;
}

.zg-extras-cats-foot-link {
  flex: 1 1 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 14px;
  border-radius: var(--br, 8px);
  border: 1px solid var(--pr-blue, #2e58ef);
  background: #fff;
  color: var(--pr-blue, #2e58ef);
  font-family: inherit;
  font-weight: 500;
  font-size: 13px;
  line-height: 1.3;
  text-decoration: none;
  transition: background 0.12s, color 0.12s;
  min-width: 0;
  text-align: center;
}

.zg-extras-cats-foot-link:hover {
  background: var(--pr-blue, #2e58ef);
  color: #fff;
}

@media (max-width: 520px) {
  .zg-extras-cats-foot {
    flex-direction: column;
    padding: 12px 16px 16px;
  }
  .zg-extras-cats-foot-link {
    width: 100%;
  }
}
/* === ЗАДАЧА 4. «Похожие звуки» — карточки с image+overlay+pattern ==========
   Render: v3_showTag.php секция .zg-v3-related (только когда $MainCat=root,
   т.е. $tag — субкатегория). До 8 карточек (ровный 4×2 grid на десктопе),
   full-width (растягивается на всю ширину .oh).
   HTML каждой карточки:
     <li.zg-v3-related__item>
       <a.zg-v3-related__link href="...">
         <div.zg-v3-related__thumb>
           <img.zg-v3-related__img loading="lazy" />  ← если image_thumb есть
           <span.zg-v3-related__overlay/>      ← цветной градиент (8 палитр), opacity 0.62
           <span.zg-v3-related__pattern/>      ← decorative dots+corner-glow (mix-blend overlay)
           <span.zg-v3-related__count>N звуков</>  ← rounded 6px pill bottom-left
           <span.zg-v3-related__icon><svg/></>  ← music-icon bottom-right
         </>
         <span.zg-v3-related__name>Имя</>
       </a>
     </li>
   image_thumb рендерится как <img loading="lazy"> (нативный lazy + alt-text
   для SEO). overlay тонирует image в цвет палитры (визуально объединяет
   разнородные картинки). pattern — субтильный декор (dots + corner glow).
   Hover: image scales 1.06 через transform на __img (overlay не двигается),
   overlay opacity drops, card lifts. */
.zg-v3-related {
  margin: 32px 0 24px;
}
.zg-v3-related__title {
  margin: 0 0 16px;
  font-family: inherit;
  font-weight: 700;
  font-size: 22px;
  line-height: 1.25;
  color: #0f172a;
}
.zg-v3-related {
  /* Fixed 4 cols + min-width: 0 даёт grid-track усадку до 0 (иначе img
     не даёт ужаться). Full-width — grid растягивается на всю ширину
     родителя (никаких max-width ограничений). */
}
.zg-v3-related__grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px;
  width: 100%;
}
.zg-v3-related__item {
  list-style: none;
  margin: 0;
  padding: 0;
  min-width: 0;
}
.zg-v3-related__link {
  display: block;
  text-decoration: none;
  color: inherit;
}
.zg-v3-related__thumb {
  position: relative;
  aspect-ratio: 4/3;
  border-radius: 14px;
  overflow: hidden;
  background-color: #2e58ef;
  isolation: isolate;
  transition: box-shadow 0.25s, transform 0.25s;
}
.zg-v3-related {
  /* Native lazy через <img loading="lazy"> вместо background-image —
     браузер сам подгружает только когда thumb близко к viewport. */
}
.zg-v3-related__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  z-index: 0;
  transition: transform 0.5s ease;
  will-change: transform;
}
.zg-v3-related__overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, #2e58ef 0%, #6e8eff 100%);
  opacity: 0.62;
  pointer-events: none;
  z-index: 1;
  transition: opacity 0.25s;
}
.zg-v3-related {
  /* Decorative pattern: corner glow + dots grid. mix-blend-mode: overlay
     даёт adaptive прозрачность поверх любой палитры (на тёмных частях
     дотс ярче, на светлых — мягче). */
}
.zg-v3-related__pattern {
  position: absolute;
  inset: 0;
  background-image: radial-gradient(circle at 80% 18%, rgba(255, 255, 255, 0.28) 0%, transparent 38%), radial-gradient(circle at 18% 82%, rgba(255, 255, 255, 0.14) 0%, transparent 50%), radial-gradient(circle, rgba(255, 255, 255, 0.22) 1px, transparent 1.5px);
  background-size: 100% 100%, 100% 100%, 18px 18px;
  background-position: 0 0, 0 0, 0 0;
  opacity: 0.55;
  mix-blend-mode: overlay;
  pointer-events: none;
  z-index: 2;
}
.zg-v3-related {
  /* Border-radius 6px — в стиле сайта (--br: 8px база, count чуть меньше
     карточки). Никаких 999px-pill, у нас не таких суперкруглых элементов. */
}
.zg-v3-related__count {
  position: absolute;
  left: 12px;
  bottom: 12px;
  padding: 4px 10px;
  border-radius: 6px;
  /* Solid bump вместо blur — на каждой related-карточке (8 на страницу)
     blur сложился бы по paint-cost. */
  background: rgba(15, 23, 42, 0.72);
  color: #fff;
  font-family: inherit;
  font-weight: 600;
  font-size: 11px;
  line-height: 1;
  letter-spacing: 0.01em;
  z-index: 3;
}
.zg-v3-related__icon {
  position: absolute;
  right: 10px;
  bottom: 10px;
  width: 32px;
  height: 32px;
  border-radius: 8px;
  /* bg ↑ с 0.22 на 0.32 чтобы компенсировать потерю blur(3px) glassy-эффекта.
     Без backdrop-filter — paint-cost кратно меньше на 8 related-карточках. */
  background: rgba(255, 255, 255, 0.32);
  color: #fff;
  display: grid;
  place-items: center;
  z-index: 3;
  transition: background 0.2s;
}
.zg-v3-related__icon svg {
  width: 16px;
  height: 16px;
}
.zg-v3-related__name {
  display: block;
  margin-top: 10px;
  font-family: inherit;
  font-weight: 600;
  font-size: 14px;
  line-height: 1.3;
  color: #0f172a;
  transition: color 0.12s;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.zg-v3-related__link:hover .zg-v3-related__thumb {
  transform: translateY(-3px);
  box-shadow: 0 12px 28px rgba(15, 23, 42, 0.16);
}
.zg-v3-related__link:hover .zg-v3-related__img {
  transform: scale(1.06);
}
.zg-v3-related__link:hover .zg-v3-related__overlay {
  opacity: 0.46;
}
.zg-v3-related__link:hover .zg-v3-related__icon {
  background: rgba(255, 255, 255, 0.32);
}
.zg-v3-related__link:hover .zg-v3-related__name {
  color: var(--pr-blue, #2e58ef);
}
.zg-v3-related__link:focus-visible .zg-v3-related__thumb {
  outline: 2px solid var(--pr-blue, #2e58ef);
  outline-offset: 3px;
}
.zg-v3-related {
  /* 8 палитр-градиентов через :nth-child(8n+i). При 8 карточках получаем
     ровно 8 разных тонов; на 4×2-сетке диагональ выглядит сбалансированно. */
}
.zg-v3-related__item:nth-child(8n+1) .zg-v3-related__overlay {
  background: linear-gradient(135deg, #2e58ef 0%, #6e8eff 100%);
}
.zg-v3-related__item:nth-child(8n+2) .zg-v3-related__overlay {
  background: linear-gradient(135deg, #7c3aed 0%, #a78bfa 100%);
}
.zg-v3-related__item:nth-child(8n+3) .zg-v3-related__overlay {
  background: linear-gradient(135deg, #1e40af 0%, #4f86ff 100%);
}
.zg-v3-related__item:nth-child(8n+4) .zg-v3-related__overlay {
  background: linear-gradient(135deg, #0891b2 0%, #22d3ee 100%);
}
.zg-v3-related__item:nth-child(8n+5) .zg-v3-related__overlay {
  background: linear-gradient(135deg, #c026d3 0%, #e879f9 100%);
}
.zg-v3-related__item:nth-child(8n+6) .zg-v3-related__overlay {
  background: linear-gradient(135deg, #16a34a 0%, #4ade80 100%);
}
.zg-v3-related__item:nth-child(8n+7) .zg-v3-related__overlay {
  background: linear-gradient(135deg, #ea580c 0%, #fb923c 100%);
}
.zg-v3-related__item:nth-child(8n) .zg-v3-related__overlay {
  background: linear-gradient(135deg, #0f766e 0%, #14b8a6 100%);
}

/* === Адаптация по экранам =================================================
   ≥1024px: 4 cols (4×2) — десктоп
   768-1023: 3 cols (3×3 — последний ряд 2) — планшеты landscape
   520-767:  3 cols compact — планшеты portrait
   <520:     2 cols (2×4) — мобилки */
@media (max-width: 1023px) {
  .zg-v3-related__grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 14px;
  }
}
@media (max-width: 767px) {
  .zg-v3-related {
    margin: 24px 0 16px;
  }
  .zg-v3-related__title {
    font-size: 19px;
    margin: 0 0 12px;
  }
  .zg-v3-related__grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 12px;
  }
  .zg-v3-related__count {
    left: 10px;
    bottom: 10px;
    padding: 3px 8px;
    font-size: 10px;
  }
  .zg-v3-related__icon {
    right: 8px;
    bottom: 8px;
    width: 28px;
    height: 28px;
  }
  .zg-v3-related__icon svg {
    width: 14px;
    height: 14px;
  }
  .zg-v3-related__name {
    margin-top: 8px;
    font-size: 13px;
  }
}
@media (max-width: 519px) {
  .zg-v3-related__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
  }
  .zg-v3-related__pattern {
    background-size: 100% 100%, 100% 100%, 14px 14px;
  }
  .zg-v3-related__thumb {
    border-radius: 12px;
  }
}
/* Самые узкие экраны — 1 колонка. 4:3 тут даёт слишком высокую карточку
   (~280px), поэтому переключаемся на 16:9 (~210px) — компактнее. */
@media (max-width: 380px) {
  .zg-v3-related__grid {
    grid-template-columns: 1fr;
    gap: 12px;
  }
  .zg-v3-related__thumb {
    aspect-ratio: 16/9;
    border-radius: 10px;
  }
}
/* При открытом popup-каталоге прячем footer-sidebar — иначе оба видны
   одновременно (популярная путаница: «откуда два каталога?»).
   visibility вместо display:none — Google не считает это hidden-content. */
body.zg-extras-cats-open .zg-v3-cat-sidebar {
  visibility: hidden;
}

/* === Track cover-art на круглой play-button (music cards) =================
   Сейчас один статичный placeholder (base64-inline JPEG, 1.8KB) применяется
   на все music-карточки. Программер позже сделает per-track cover из БД —
   достаточно будет ставить inline `style="--zg-cover: url(/path/img.jpg)"`
   на конкретную карточку, оно перебьёт placeholder через CSS-var fallback.

   Layout-trade-offs:
   - col1 в music-grid расширяется с 36/32 до 46/40 чтобы button (46×46
     desktop, 40×40 ≤520) полностью вписался в col без overflow.
   - playWrap-обёртка тоже бампается до 46/40 (иначе flex-center внутри
     wrap'а сдвигает button — overflow по бокам).
   - column-gap синхронизирован с card.padding-left → гармония отступов.
   - margin: 0 на playBu — убирает site-base margin-right:7 (перекос
     left vs right при flex-center).
   ============================================================================ */
.zg-v3 .longTracks .onetrack.accordion .playBu {
  /* width/height/min/max — base music rule (line ~2232) ставит !important
     с 36px, нужно сохранить !important чтобы перебить. */
  border-radius: 8px;
  width: 46px !important;
  height: 46px !important;
  min-width: 46px !important;
  min-height: 46px !important;
  max-width: 46px !important;
  max-height: 46px !important;
  position: relative;
  overflow: hidden;
  margin: 0;
  /* Cover-стратегия для прода:
     1. По дефолту все music playBu получают placeholder из external file
        `../img/track-cover-placeholder.jpg` (относительно CSS-файла →
        /design/img/track-cover-placeholder.jpg на проде).
     2. Per-track override через inline-style на PHP-уровне: программер
        ставит на конкретный .playBu `style="--zg-cover: url(/path/123.jpg)"`
        — CSS-var перебивает placeholder fallback. */
  background-image: var(--zg-cover, url("../img/track-cover-placeholder.jpg"));
  background-size: cover;
  background-position: center;
  background-color: #1c2b39;
  color: #fff;
}

/* Очень лёгкий tint над cover'ом — практически не затемняет обложку,
   но даёт минимальный контрастный фон под play-icon. Контраст самого
   icon'а обеспечивается drop-shadow ниже (см. ::before). */
.zg-v3 .longTracks .onetrack.accordion .playBu::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(15, 23, 42, 0.12);
  border-radius: inherit;
  pointer-events: none;
  transition: background 0.18s;
  z-index: 0;
}

/* Triangle с закруглёнными углами через SVG-mask. Solid-white + drop-shadow
   filter — icon хорошо читается на любом фоне (светлый cover тоже): тень
   рисуется только в области масочного пути, не fillit'ит весь box. */
.zg-v3 .longTracks .onetrack.accordion .playBu::before {
  position: relative;
  z-index: 1;
  width: 18px;
  height: 18px;
  border: 0;
  margin-left: 2px;
  background-color: #fff;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.45));
  transition: background-color 0.15s, filter 0.15s;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M8 5 L8 19 L19 12 Z' fill='black' stroke='black' stroke-width='3.5' stroke-linejoin='round' stroke-linecap='round'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M8 5 L8 19 L19 12 Z' fill='black' stroke='black' stroke-width='3.5' stroke-linejoin='round' stroke-linecap='round'/></svg>") center/contain no-repeat;
}

/* Hover overlay — обложка ещё чище, минимальный tint. */
.zg-v3 .longTracks .onetrack.accordion:hover .playBu::after,
.zg-v3 .longTracks .onetrack.accordion .playBu:hover::after {
  background: rgba(15, 23, 42, 0.04);
}

/* Hover icon — solid white. */
.zg-v3 .longTracks .onetrack.accordion:hover .playBu::before,
.zg-v3 .longTracks .onetrack.accordion .playBu:hover::before {
  background-color: #fff;
}

/* Hover/Pause: keep cover bg видимым (site base правило `:hover .playBu`
   на line ~1414 перебрасывает на blue без !important — наша source-order-late
   rule побеждает без !important). */
.zg-v3 .longTracks .onetrack.accordion:hover .playBu,
.zg-v3 .longTracks .onetrack.accordion .playBu:hover,
.zg-v3 .longTracks .onetrack.accordion .playBu.Pause {
  background-color: #1c2b39;
  color: #fff;
}

/* Pause-state — два rounded-rect через SVG-mask. */
.zg-v3 .longTracks .onetrack.accordion .playBu.Pause::before {
  position: relative;
  z-index: 1;
  width: 16px;
  height: 16px;
  margin-left: 0;
  background-color: rgba(255, 255, 255, 0.78);
  transition: background-color 0.15s;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='6' y='5' width='4' height='14' rx='1.5'/><rect x='14' y='5' width='4' height='14' rx='1.5'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='6' y='5' width='4' height='14' rx='1.5'/><rect x='14' y='5' width='4' height='14' rx='1.5'/></svg>") center/contain no-repeat;
  animation: zgcat-cover-icon-pop 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes zgcat-cover-icon-pop {
  0% {
    transform: scale(0.55);
    opacity: 0;
  }
  55% {
    transform: scale(1.15);
    opacity: 1;
  }
  100% {
    transform: scale(1);
    opacity: 1;
  }
}
.zg-v3 .longTracks .onetrack.accordion:hover .playBu.Pause::before,
.zg-v3 .longTracks .onetrack.accordion .playBu.Pause:hover::before {
  background-color: #fff;
}

/* Loading spinner — облегчённая палитра поверх обложки. */
.zg-v3 .longTracks .onetrack.accordion .playBu.loading::before {
  width: 16px;
  height: 16px;
  margin: 0;
  border: 2px solid rgba(255, 255, 255, 0.35);
  border-top-color: #fff;
  border-radius: 50%;
  background-color: transparent;
  -webkit-mask: none;
  mask: none;
  animation: zgcat-playBu-spin 0.7s linear infinite;
}

/* Layout: ≥521 расширяем col1 + playWrap до 46, gap=22. На ≤768 gap=14.
   Применяется ко ВСЕМ music-карточкам (.longTracks эксклюзивно music) —
   без ожидания data-cover. Без этого layout «прыгает» при догрузке cover'а. */
@media (min-width: 521px) {
  .zg-v3 .longTracks .onetrack.accordion {
    /* grid-template-columns с !important в base music (line ~1957) — keep.
       column-gap в base без !important — source-order побеждает. */
    grid-template-columns: 46px minmax(0, 1fr) 36px auto !important;
    column-gap: 22px;
  }
  .zg-v3 .longTracks .onetrack.accordion .playWrap {
    /* width/height/min/max с !important в base playWrap (line ~2221). */
    width: 46px !important;
    height: 46px !important;
    min-width: 46px !important;
    min-height: 46px !important;
    max-width: 46px !important;
    max-height: 46px !important;
  }
}
@media (max-width: 768px) and (min-width: 521px) {
  .zg-v3 .longTracks .onetrack.accordion {
    column-gap: 14px;
  }
}
/* ≤520: button 40×40, padding 16, col1=40, gap=16. */
@media (max-width: 520px) {
  .zg-v3 .longTracks .onetrack.accordion {
    /* padding и grid-template-columns с !important в base @520 (line ~2385). */
    padding: 16px !important;
    grid-template-columns: 40px minmax(0, 1fr) auto !important;
    column-gap: 16px;
  }
  .zg-v3 .longTracks .onetrack.accordion .playWrap,
  .zg-v3 .longTracks .onetrack.accordion .playBu {
    /* width/height с !important в base @520 (line ~2350). */
    width: 40px !important;
    height: 40px !important;
    min-width: 40px !important;
    min-height: 40px !important;
    max-width: 40px !important;
    max-height: 40px !important;
  }
  .zg-v3 .longTracks .onetrack.accordion .playBu::before {
    width: 16px;
    height: 16px;
  }
  .zg-v3 .longTracks .onetrack.accordion .playBu.Pause::before {
    width: 12px;
    height: 13px;
  }
}
/* =====================================================================
   Категории popup overlay — search-page (kind=music).
   JS живёт в _v3_extras_inline.php (setupCategoryFilterPopup).
   3 view modes: groups (по группам), alpha (А-Я), popular.
   Tokens: --pr-blue #2e58ef, --br:8px, --border-card #dfe7f3, --l-blue #eef4f9.
   ===================================================================== */
.zg-extras-catfilter-overlay {
  position: fixed;
  inset: 0;
  /* Solid dark wash, no backdrop-filter blur — blur is paint-heavy at
     fullscreen size (whole viewport is reblurred every frame) and the
     0.55 alpha already gives enough visual separation from the page. */
  background: rgba(15, 23, 42, 0.55);
  /* Above site-header (z-index 9999) and any ad/megatop overlays. Topnav
     dropdowns sit at z-index 9999-99999; we want to fully cover them when
     picking categories. */
  z-index: 100001;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.zg-extras-catfilter-overlay.is-open {
  display: flex;
}

.zg-extras-catfilter-modal {
  background: #fff;
  border-radius: 12px;
  border: 1px solid var(--border-card, #dfe7f3);
  box-shadow: 0 30px 60px -20px rgba(15, 23, 42, 0.35);
  width: min(960px, 100%);
  max-height: calc(100vh - 48px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* ── HEAD (3 rows: title+actions / search / tabs) ───────────────── */
.zg-extras-catfilter-head {
  flex-shrink: 0;
  border-bottom: 1px solid var(--border-card, #dfe7f3);
  background: #fff;
}

.zg-extras-catfilter-head-top {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px 8px;
}

.zg-extras-catfilter-title {
  margin: 0;
  flex: 1 1 auto;
  min-width: 0; /* allow ellipsis-shrink past content size on narrow */
  font-size: 16px;
  font-weight: 700;
  color: #0f172a;
  letter-spacing: -0.01em;
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.zg-extras-catfilter-total {
  color: #94a3b8;
  font-family: ui-monospace, "JetBrains Mono", "SF Mono", Menlo, Consolas, monospace;
  font-size: 13px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
}

.zg-extras-catfilter-head-actions {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  margin-left: auto; /* force-dock to right edge even if title is tiny */
}

.zg-extras-catfilter-counter {
  font-weight: 500;
  font-size: 12.5px;
  line-height: 1.3;
  color: #6c7390;
  margin-right: 4px;
  white-space: nowrap;
}

.zg-extras-catfilter-counter [data-cf-count] {
  color: var(--pr-blue, #2e58ef);
  font-family: ui-monospace, "JetBrains Mono", "SF Mono", Menlo, Consolas, monospace;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* Search row — full-width input under title */
.zg-extras-catfilter-head-search {
  padding: 0 16px 8px;
}

.zg-extras-catfilter-search {
  display: block;
  width: 100%;
  padding: 7px 12px;
  border-radius: var(--br, 8px);
  border: 1px solid var(--border-card, #dfe7f3);
  background: #f8fafc;
  font-size: 13.5px;
  line-height: 1.3;
  color: #0f172a;
  transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
  box-sizing: border-box;
}

.zg-extras-catfilter-search:focus {
  outline: none;
  border-color: var(--pr-blue, #2e58ef);
  background: #fff;
  box-shadow: 0 0 0 3px rgba(46, 88, 239, 0.1);
}

.zg-extras-catfilter-search::placeholder {
  color: #94a3b8;
}

.zg-extras-catfilter-close {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border-card, #dfe7f3);
  border-radius: var(--br, 8px);
  background: #fff;
  color: #6c7390;
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
  line-height: 0;
}

.zg-extras-catfilter-close svg {
  display: block;
  width: 12px;
  height: 12px;
}

.zg-extras-catfilter-close:hover {
  background: #f1f5f9;
  color: #0f172a;
  border-color: #c6d4e8;
}

/* TABS row — view-tabs on the left, density toggle on the right (both are
   "how the body looks" affordances). pill-style mirrors clone-ui stepper. */
.zg-extras-catfilter-tabs {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 0 16px 10px;
}

.zg-extras-catfilter-tabs-list {
  display: flex;
  gap: 2px;
  flex-wrap: wrap;
  min-width: 0;
}

.zg-extras-catfilter-tab {
  padding: 6px 14px;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: #6c7390;
  font-weight: 600;
  font-size: 13px;
  line-height: 1.3;
  cursor: pointer;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}

.zg-extras-catfilter-tab:hover {
  background: #f8fafc;
  color: #0f172a;
}

.zg-extras-catfilter-tab.is-active {
  background: rgba(46, 88, 239, 0.07);
  border-color: rgba(46, 88, 239, 0.18);
  color: var(--pr-blue, #2e58ef);
}

/* ── BODY (scrollable) ─────────────────────────────────────────── */
/* No top padding here — sticky alpha-jump needs to flush against
   the scroll container's top edge. Top-spacing lives inside first content
   child or inside sticky-bar's own padding instead. */
.zg-extras-catfilter-body {
  flex: 1 1 auto;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 0 18px 14px;
  scrollbar-width: thin;
  scrollbar-color: #cbd5e1 #f1f5f9;
}

.zg-extras-catfilter-body::-webkit-scrollbar {
  width: 8px;
}

.zg-extras-catfilter-body::-webkit-scrollbar-thumb {
  background: #cbd5e1;
  border-radius: 4px;
}

.zg-extras-catfilter-body::-webkit-scrollbar-track {
  background: transparent;
}

/* GROUPS view */
/* Two-level hierarchy via TYPE+CASE contrast:
   Level 1 (group)    — 18px 800 dark sentence-case + hairline underline
                        + brand-blue count pill (looks like a SECTION).
   Level 2 (subgroup) — 11px 700 UPPERCASE muted slate-500 letter-spacing
                        (looks like a LABEL, not a competing heading).
   Combined: the two never read as the same weight class — user can
   instantly tell which is the parent. */
.zg-extras-catfilter-group {
  padding: 18px 0 8px;
}

.zg-extras-catfilter-group:first-child {
  padding-top: 12px;
}

.zg-extras-catfilter-group + .zg-extras-catfilter-group {
  margin-top: 6px;
}

.zg-extras-catfilter-group.is-hidden {
  display: none;
}

.zg-extras-catfilter-group-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 14px;
  padding-bottom: 10px;
  border-bottom: 1px solid #e2e8f0;
}

.zg-extras-catfilter-group-title {
  margin: 0;
  font-weight: 800;
  font-size: 18px;
  line-height: 1.2;
  color: #0f172a;
  letter-spacing: -0.015em;
}

/* Group count pill: hidden — общие цифры рядом с заголовком группы
   не помогают выбрать категорию, только шумят. JS всё ещё пушит span,
   CSS прячет. */
.zg-extras-catfilter-group-count {
  display: none;
}

.zg-extras-catfilter-subgroup {
  display: block;
  padding: 0 0 18px; /* bigger gap between subgroups → breathing room */
  border-top: 0;
}

.zg-extras-catfilter-subgroup + .zg-extras-catfilter-subgroup {
  padding-top: 4px;
}

.zg-extras-catfilter-subgroup:last-child {
  padding-bottom: 4px;
}

.zg-extras-catfilter-subgroup.is-hidden {
  display: none;
}

.zg-extras-catfilter-subgroup-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 4px 0 14px; /* "больше воздуха ниже подзаголовка" */
}

.zg-extras-catfilter-subgroup-title {
  margin: 0;
  font-weight: 700;
  font-size: 11px;
  line-height: 1.2;
  color: #64748b; /* slate-500 muted, NOT blue, NOT dark */
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

/* Subgroup count (число справа от лейбла подгруппы): hidden — общие
   цифры сбивают, не несут actionable инфо в picker context. JS их всё
   ещё рендерит, CSS прячет. */
.zg-extras-catfilter-subgroup-count {
  display: none;
}

/* Flat business-group: no head — chips occupy full width directly. */
.zg-extras-catfilter-subgroup--untitled {
  padding-top: 4px;
}

.zg-extras-catfilter-subgroup--untitled .zg-extras-catfilter-subgroup-head {
  display: none;
}

/* ALPHA view */
/* Sticky alpha jump-bar — sticks flush at scroll container top.
   Negative left/right margins extend it edge-to-edge across body's
   horizontal padding so the bottom border-line spans the full width. */
.zg-extras-catfilter-alpha-jump {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  padding: 10px 18px 10px;
  margin: 0 -18px 10px;
  position: sticky;
  top: 0;
  background: #fff;
  z-index: 2;
  border-bottom: 1px solid #f1f5f9;
}

.zg-extras-catfilter-alpha-letter {
  min-width: 24px;
  height: 24px;
  padding: 0 6px;
  display: inline-grid;
  place-items: center;
  border-radius: 5px;
  color: var(--pr-blue, #2e58ef);
  font-weight: 600;
  font-size: 12px;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.12s;
}

.zg-extras-catfilter-alpha-letter:hover {
  background: var(--l-blue, #eef4f9);
}

.zg-extras-catfilter-alpha-letter.is-disabled {
  color: #cbd5e1;
  pointer-events: none;
  cursor: default;
}

.zg-extras-catfilter-letter-head {
  margin: 12px 0 4px;
  font-weight: 700;
  font-size: 13px;
  line-height: 1.2;
  color: var(--pr-blue, #2e58ef);
  scroll-margin-top: 50px;
}

.zg-extras-catfilter-letter-head:first-child {
  margin-top: 0;
}

.zg-extras-catfilter-letter-head.is-hidden {
  display: none;
}

/* Popular view (flat chip list) — body has no top padding, so this
   flat-container needs its own top spacing. Alpha view's sticky jump-bar
   handles its own. */
.zg-extras-catfilter-flat {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 5px 6px;
  align-items: flex-start;
  padding-top: 12px;
}

.zg-extras-catfilter-body[data-view=alpha] .zg-extras-catfilter-flat {
  padding-top: 0;
}

/* Letter headers span full width of the flat grid (alpha view). */
.zg-extras-catfilter-flat .zg-extras-catfilter-letter-head {
  grid-column: 1/-1;
  margin-bottom: 2px;
  margin-top: 8px;
}

.zg-extras-catfilter-flat .zg-extras-catfilter-letter-head:first-child {
  margin-top: 0;
}

/* CHIPS — equal-width grid columns. Long names ellipsis-truncate;
   count badge always visible at right. minmax(200px, 1fr) — крупнее start,
   на desktop ~3-4 колонки в subgroup; на phone стек 2 кол + ниже @media. */
.zg-extras-catfilter-chips {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 5px 6px;
}

/* Chip — Zvukogram brand-style rectangular (rounded 8px, matches
   `.zg-v3__btn`). Internal grid: name (1fr ellipsis) + count (auto).
   Chip fills its grid cell — equal column width across all chips. */
.zg-extras-catfilter-chip {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 6px;
  padding: 5px 11px;
  border: 1px solid var(--border-card, #dfe7f3);
  border-radius: var(--br, 8px);
  background: #fff;
  color: #2c2c2c;
  font-weight: 400;
  font-size: 13px;
  line-height: 1.3;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
  text-align: left;
  width: 100%;
  min-width: 0;
}

.zg-extras-catfilter-chip-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.zg-extras-catfilter-chip.is-hidden {
  display: none;
}

.zg-extras-catfilter-chip:hover {
  border-color: rgba(46, 88, 239, 0.25);
  color: var(--pr-blue, #2e58ef);
  background: var(--l-blue, #eef4f9);
}

.zg-extras-catfilter-chip[aria-pressed=true] {
  background: var(--pr-blue, #2e58ef);
  border-color: var(--pr-blue, #2e58ef);
  color: #fff;
  font-weight: 600;
}

/* Per-chip count (число справа от имени категории внутри chip): hidden.
   Просто общие цифры — не помогают выбрать, только шумят. Chip layout
   возвращается на single-column (grid-template-columns: 1fr) — без
   правой колонки под count. JS-state hover/aria-pressed для count тоже
   больше не нужен, элемента нет. */
.zg-extras-catfilter-chip-count {
  display: none;
}

.zg-extras-catfilter-chip {
  grid-template-columns: 1fr;
}

/* ── DENSITY TOGGLE (blocks / list) ─────────────────────────────── */
.zg-extras-catfilter-density {
  display: inline-flex;
  gap: 2px;
  margin-right: 4px;
}

.zg-extras-catfilter-density-btn {
  width: 28px;
  height: 28px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border-card, #dfe7f3);
  border-radius: var(--br, 8px);
  background: #fff;
  color: #94a3b8;
  cursor: pointer;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}

.zg-extras-catfilter-density-btn:hover {
  color: #0f172a;
  border-color: #c6d4e8;
  background: #f8fafc;
}

.zg-extras-catfilter-density-btn.is-active {
  color: var(--pr-blue, #2e58ef);
  background: var(--l-blue, #eef4f9);
  border-color: rgba(46, 88, 239, 0.25);
}

.zg-extras-catfilter-density-btn svg {
  display: block;
}

/* LIST DENSITY — chips become full-width single-column rows. Applied via
   [data-density="list"] on body. Both `.chips` (groups view) and `.flat`
   (alpha/popular views) get the single-column treatment. */
.zg-extras-catfilter-body[data-density=list] .zg-extras-catfilter-chips,
.zg-extras-catfilter-body[data-density=list] .zg-extras-catfilter-flat {
  grid-template-columns: 1fr;
}

.zg-extras-catfilter-body[data-density=list] .zg-extras-catfilter-chip {
  padding: 7px 12px;
  font-size: 13.5px;
}

/* In list mode the subgroup-title column also tighter (chips much taller). */
.zg-extras-catfilter-body[data-density=list] .zg-extras-catfilter-subgroup {
  grid-template-columns: 132px 1fr;
}

/* ── ACTION BUTTONS (now in HEAD top-right, not foot) ──────────── */
.zg-extras-catfilter-reset,
.zg-extras-catfilter-apply {
  padding: 6px 14px;
  border-radius: var(--br, 8px);
  font-weight: 600;
  font-size: 12.5px;
  line-height: 1.3;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
  white-space: nowrap;
}

.zg-extras-catfilter-reset {
  border: 1px solid var(--border-card, #dfe7f3);
  background: #fff;
  color: #6c7390;
}

.zg-extras-catfilter-reset:hover {
  border-color: #c6d4e8;
  color: #0f172a;
  background: #f1f5f9;
}

.zg-extras-catfilter-apply {
  border: 1px solid var(--pr-blue, #2e58ef);
  background: var(--pr-blue, #2e58ef);
  color: #fff;
}

.zg-extras-catfilter-apply:hover {
  background: #1a40c9;
  border-color: #1a40c9;
}

/* ── RESPONSIVE — three breakpoints ─────────────────────────────
   ≤960: tablet (modal still windowed but tighter)
   ≤720: phone landscape — modal fullscreen, narrower chips, subgroup-label
         stacks above chips (no more 132px sidebar — saves space)
   ≤480: phone portrait — counter/reset label compacted, tabs shrink
   ───────────────────────────────────────────────────────────── */
@media (max-width: 960px) {
  .zg-extras-catfilter-overlay {
    padding: 16px;
  }
  .zg-extras-catfilter-chips,
  .zg-extras-catfilter-flat {
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  }
}
@media (max-width: 720px) {
  .zg-extras-catfilter-overlay {
    padding: 0;
  }
  .zg-extras-catfilter-modal {
    width: 100%;
    max-height: 100vh;
    height: 100vh;
    border-radius: 0;
    border: 0;
  }
  .zg-extras-catfilter-head-top {
    padding: 10px 14px 6px;
    gap: 8px;
  }
  .zg-extras-catfilter-title {
    font-size: 15px;
  }
  .zg-extras-catfilter-head-actions {
    gap: 5px;
    flex-wrap: nowrap;
  }
  /* On narrow phone hide counter text — save space; close × stays at right. */
  .zg-extras-catfilter-counter {
    display: none;
  }
  .zg-extras-catfilter-head-search {
    padding: 0 14px 6px;
  }
  .zg-extras-catfilter-tabs {
    padding: 0 14px 8px;
    flex-wrap: wrap; /* tabs + density may wrap to 2 rows on small */
    gap: 6px 8px;
  }
  .zg-extras-catfilter-tabs-list {
    flex: 1 1 auto;
    overflow-x: auto;
    flex-wrap: nowrap;
  }
  .zg-extras-catfilter-tab {
    flex-shrink: 0;
  }
  .zg-extras-catfilter-body {
    padding: 0 14px 12px;
  }
  /* Subgroup title moves ABOVE chips (stacked) on phone — saves the 132px
     sidebar real-estate so chip-grid has full width. */
  .zg-extras-catfilter-subgroup {
    grid-template-columns: 1fr;
    gap: 4px 0;
  }
  .zg-extras-catfilter-subgroup-title {
    margin-top: 2px;
  }
  .zg-extras-catfilter-chips,
  .zg-extras-catfilter-flat {
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 5px;
  }
  /* Alpha jump-bar: tighter on phone */
  .zg-extras-catfilter-alpha-jump {
    margin: 0 -14px 10px;
    padding: 8px 14px;
  }
  .zg-extras-catfilter-alpha-letter {
    min-width: 22px;
    height: 22px;
    font-size: 11.5px;
  }
}
@media (max-width: 480px) {
  .zg-extras-catfilter-head-top {
    gap: 6px;
    padding: 9px 12px 4px;
  }
  /* Hide Reset's text label, keep just X SVG instead — saves space. Or
     just compact padding. Going with compact padding (keeps explicit label
     readable even on narrow). */
  .zg-extras-catfilter-reset,
  .zg-extras-catfilter-apply {
    padding: 5px 10px;
    font-size: 12px;
  }
  .zg-extras-catfilter-close {
    width: 26px;
    height: 26px;
  }
  .zg-extras-catfilter-head-search {
    padding: 0 12px 4px;
  }
  .zg-extras-catfilter-search {
    padding: 7px 10px;
    font-size: 13px;
  }
  .zg-extras-catfilter-tabs {
    padding: 0 12px 6px;
  }
  .zg-extras-catfilter-tab {
    padding: 5px 11px;
    font-size: 12px;
  }
  .zg-extras-catfilter-density-btn {
    width: 26px;
    height: 26px;
  }
  .zg-extras-catfilter-body {
    padding: 0 12px 12px;
  }
  .zg-extras-catfilter-group {
    padding: 8px 0 2px;
  }
  .zg-extras-catfilter-group:first-child {
    padding-top: 10px;
  }
  .zg-extras-catfilter-group-title {
    font-size: 14px;
  }
  .zg-extras-catfilter-chips,
  .zg-extras-catfilter-flat {
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  }
  .zg-extras-catfilter-body[data-density=list] .zg-extras-catfilter-chip {
    padding: 6px 10px;
    font-size: 13px;
  }
}
/* ≤380: ultra-narrow phones — 2 columns guaranteed */
@media (max-width: 380px) {
  .zg-extras-catfilter-chips,
  .zg-extras-catfilter-flat {
    grid-template-columns: repeat(2, 1fr);
  }
  .zg-extras-catfilter-chip {
    font-size: 12.5px;
    padding: 4px 8px;
  }
}
/* Search-result track-row: matched category as a chip in description-row.
   Source link (.zgcat-search-row-cat) lives inline inside .waveTitle from
   server; v3_search.php JS relocates it into .zgcat-trackdesc and gives it
   this class. Brand-blue text on light-blue tint + tiny grid icon — same
   visual language as the «Категории» trigger in sort-row. */
.zg-v3-search .zg-track-cat-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 1px 8px;
  background: var(--l-blue, #eef4f9);
  color: var(--pr-blue, #2e58ef);
  border-radius: 2px;
  font-size: 11.5px;
  font-weight: 500;
  text-decoration: none;
  line-height: 1.5;
  vertical-align: baseline;
  white-space: nowrap;
  transition: background 0.15s;
}

.zg-v3-search .zg-track-cat-chip:hover {
  background: #dce8f7;
  text-decoration: none;
  color: var(--pr-blue, #2e58ef);
}

.zg-v3-search .zg-track-cat-chip-icon {
  display: inline-block;
  width: 11px;
  height: 11px;
  flex-shrink: 0;
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='3' width='8' height='8' rx='1.5'/><rect x='13' y='3' width='8' height='8' rx='1.5'/><rect x='3' y='13' width='8' height='8' rx='1.5'/><rect x='13' y='13' width='8' height='8' rx='1.5'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='3' width='8' height='8' rx='1.5'/><rect x='13' y='3' width='8' height='8' rx='1.5'/><rect x='3' y='13' width='8' height='8' rx='1.5'/><rect x='13' y='13' width='8' height='8' rx='1.5'/></svg>") center/contain no-repeat;
}

.zg-v3-search .zg-track-cat-chip-sep {
  color: #94a3b8;
  margin: 0 6px;
}

/* Search-page (both music + sound): inline category prefix in .waveTitle
   reads as a breadcrumb path («Спокойная › Задумчивая Классика»). Category
   link is muted by default (secondary metadata), brand-blue on hover. The
   «/» separator is replaced visually with «›» — clearer hierarchy signal.
   Unified visual language across kind=music and kind=sound. */
.zg-v3-search .waveTitle .zgcat-search-row-cat {
  /* Slate-600 solid muted link (не serый-выгоревший как slate-500, а
     читаемый darker neutral). Brand-blue только на hover. Title stays
     bold-dark primary, категория — secondary text-link.

     overflow: clip (НЕ hidden) — CSS-spec gotcha: inline-block с
     overflow:hidden создаёт BFC и baseline переезжает на margin-bottom
     edge → category «пляшет» относительно текста title'а. `clip` не
     создаёт BFC, baseline остаётся normal text baseline → category и
     title на одной оси. */
  color: #475569;
  text-decoration: none;
  font-weight: 500;
  transition: color 0.15s;
  display: inline-block;
  overflow: clip;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: baseline;
  /* max-width зависит от kind:
     - music: до 200px (music-карточки шире, есть длинные «Сказочная»)
     - sound: до 110px (sound-карточки уже, длинные срезаются жёстче) */
  max-width: 110px;
}

.zg-v3-search[data-v3-is-music="1"] .waveTitle .zgcat-search-row-cat {
  max-width: 200px;
}

@media (max-width: 600px) {
  .zg-v3-search .waveTitle .zgcat-search-row-cat {
    max-width: 100px;
  }
  .zg-v3-search[data-v3-is-music="1"] .waveTitle .zgcat-search-row-cat {
    max-width: 140px;
  }
}
.zg-v3-search .waveTitle .zgcat-search-row-cat:hover {
  color: var(--pr-blue, #2e58ef);
  text-decoration: underline;
}

/* Category inline-link выравнивается по line-height родителя — без этого
   inline-block имеет свой line-height и baseline «пляшет» относительно
   текста title'а. Музыкальный title уже регулируется через base rule
   `.zg-v3 .longTracks .onetrack.accordion .waveTitle` выше (16/400). */
.zg-v3 .longTracks .onetrack.accordion .waveTitle .zgcat-search-row-cat {
  line-height: inherit;
  font-size: inherit;
}

/* Chevron — brand-blue accent. Категория сама muted slate-600, а brand-
   colour приходит только на разделителе. Получается «brand-blue hint»
   без перегрузки самой ссылки. */
.zg-v3-search .waveTitle .zgcat-search-row-sep {
  font-size: 0;
  color: var(--pr-blue, #2e58ef);
  margin: 0 6px;
}

.zg-v3-search .waveTitle .zgcat-search-row-sep::after {
  content: "›"; /* › single right angle quotation mark */
  font-size: 14px;
  font-weight: 600;
  vertical-align: baseline;
}

/* ──────────────────────────────────────────────────────────────────────────
   Filter loading/pending/error states (redesigned 2026-05-19 — было в
   draft-варианте от прогера, не в дизайн-коде сайта).

   Что было не так в исходной версии:
   • Gradient `#2f58ff → #17b8a6` (бирюза вне палитры сайта).
   • `.is-filtering ::before` через position:sticky + height:2px + margin -2px
     даёт sub-pixel layout shift в момент первого toggle.
   • `opacity: .72` на tracks — слишком жёстко, текст плохо читается.
   • Glow `box-shadow: 0 0 0 2px ... 0 8px 18px ...` чужд сайту: site uses
     crisp borders + tinted backgrounds, не shadow-glow (см. /clone/ pattern).
   • `filter: saturate(1.05)` — незаметно, шум.
   • Error: inset red glow — снова не паттерн site'а.

   Текущая реализация:
   • Loading indicator — position:absolute на нижней кромке `.zg-v3__panel`,
     внутри border-radius, НЕТ смещения треков.
   • Indeterminate-slide через background-position 35%-wide highlight.
   • Pending controls — soft-blue tint (`rgba(46,88,239,0.07)`) + crisp
     blue border, паттерн с /clone/ activeStep.
   • Error — solid 1px red bottom-border + лёгкий tint.
   • Tokens: --pr-blue, --br, --border-card.
   • Respect `prefers-reduced-motion`.
────────────────────────────────────────────────────────────────────────── */
.zg-v3.is-filtering .zg-v3__tracks {
  /* убираем dim — треки остаются читаемыми, индикатор живёт на панели */
  opacity: 1;
}

/* Kill proger's sticky gradient bar (legacy, иначе layout shift) */
.zg-v3.is-filtering .zg-v3__tracks::before {
  content: none;
}

/* Контейнер для индикатора — на самой панели фильтров. */
.zg-v3 .zg-v3__panel {
  position: relative;
}

.zg-v3.is-filtering .zg-v3__panel::after {
  content: "";
  position: absolute;
  left: 12px; /* отступ от закруглённых углов панели */
  right: 12px;
  bottom: -1px; /* aligned с нижней кромкой — без layout shift */
  height: 2px;
  background-color: transparent;
  /* Только движущаяся «волна» — без статичного track-слоя, чтобы не было
     видимой ровной линии по всей ширине. Очень мягкие fade-in/fade-out зоны
     (transparent → 0.08 → 0.45 → 0.95 → 0.45 → 0.08 → transparent) — пик
     яркости только в самом центре, эффект «выходит из прозрачки и уходит в
     прозрачку». */
  background-image: linear-gradient(90deg, transparent 0%, rgba(46, 88, 239, 0.08) 25%, rgba(46, 88, 239, 0.45) 42%, rgba(46, 88, 239, 0.95) 50%, rgba(46, 88, 239, 0.45) 58%, rgba(46, 88, 239, 0.08) 75%, transparent 100%);
  background-repeat: no-repeat;
  background-size: 50% 100%;
  background-position: -50% 0;
  /* Mask клипит концы бара — даже если highlight подходит к краю, он
     растворяется до полной прозрачности (нет hard cut'а у скруглений). */
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 10%, #000 90%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0%, #000 10%, #000 90%, transparent 100%);
  animation: zg-v3-progress-indeterminate 1.4s cubic-bezier(0.45, 0.05, 0.55, 0.95) infinite;
  pointer-events: none;
  border-radius: 999px;
  z-index: 5;
}

/* Pending — site-pattern soft-blue tint + crisp blue border, без glow */
.zg-v3 [data-v3-control].is-pending,
.zg-v3 [data-v3-filter-clear].is-pending,
.zg-v3 [data-v3-duration-badge-clear].is-pending,
.zg-v3 [data-v3-music-tag-clear].is-pending,
.zg-v3 [data-v3-music-tag-badge-clear].is-pending {
  pointer-events: none;
  box-shadow: none;
  background-color: rgba(46, 88, 239, 0.07);
  border-color: var(--pr-blue, #2e58ef);
  color: var(--pr-blue, #2e58ef);
  opacity: 0.85;
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease, opacity 0.15s ease;
}

/* Сбрасываем saturate-фильтр прогера */
.zg-v3 [data-v3-control].is-pending {
  filter: none;
}

/* Error — solid red bottom border + soft tint, без shadow-glow */
.zg-v3.is-load-error .zg-v3__panel {
  box-shadow: none;
  border-bottom: 1px solid #dc2626;
  background-color: rgba(220, 38, 38, 0.025);
}

@keyframes zg-v3-progress-indeterminate {
  0% {
    background-position: -50% 0;
  }
  100% {
    background-position: 150% 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  .zg-v3.is-filtering .zg-v3__panel::after {
    animation: none;
    background-image: none;
    background-color: rgba(46, 88, 239, 0.35);
  }
}
