    /* ── Self-hosted шрифты (variable woff2, latin + latin-ext) ──────────────
       Google Fonts нестабилен в РФ и добавлял render-blocking запрос + 2 preconnect.
       Кириллица у обоих семейств отсутствует и в Google Fonts — рендерится системным
       фолбэком (ui-sans-serif), поведение не изменилось. Файлы в fonts/, кэшируются SW. */
    @font-face {
      font-family: 'DM Sans'; font-style: normal; font-weight: 400 700;
      font-display: swap; src: url('fonts/dmsans-latin.woff2') format('woff2');
      unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
    }
    @font-face {
      font-family: 'DM Sans'; font-style: normal; font-weight: 400 700;
      font-display: swap; src: url('fonts/dmsans-latin-ext.woff2') format('woff2');
      unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
    }
    @font-face {
      font-family: 'Space Grotesk'; font-style: normal; font-weight: 500 700;
      font-display: swap; src: url('fonts/spacegrotesk-latin.woff2') format('woff2');
      unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
    }
    @font-face {
      font-family: 'Space Grotesk'; font-style: normal; font-weight: 500 700;
      font-display: swap; src: url('fonts/spacegrotesk-latin-ext.woff2') format('woff2');
      unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
    }
    /* Кириллица: DM Sans и Space Grotesk кириллицы НЕ содержат, поэтому для символов
       U+0400–045F русский текст берёт совместимый по геометрии Manrope (self-host, тот же
       вес-диапазон). Латиница и ЦИФРЫ остаются в брендовых шрифтах — цифр в этом диапазоне
       нет, значит tabular-nums сумм по-прежнему рендерит Space Grotesk. */
    @font-face {
      font-family: 'DM Sans'; font-style: normal; font-weight: 400 700;
      font-display: swap; src: url('fonts/manrope-cyrillic.woff2') format('woff2');
      unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
    }
    @font-face {
      font-family: 'Space Grotesk'; font-style: normal; font-weight: 500 700;
      font-display: swap; src: url('fonts/manrope-cyrillic.woff2') format('woff2');
      unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
    }

    :root {
      --font-ui:      'DM Sans', ui-sans-serif, sans-serif;
      --font-display: 'Space Grotesk', ui-sans-serif, sans-serif;
      --font-mono:    'JetBrains Mono', ui-monospace, monospace;

      /* Backgrounds — subtle violet tint for brand warmth */
      --bg:    #0c0c12;
      --bg2:   #13131b;
      --panel: rgba(19, 19, 27, .97);
      --panel2: rgba(26, 26, 36, .96);

      --text:  #f4f4f8;
      --muted: #8a8a9e;
      --hint:  #3e3e52;
      --line:  rgba(255, 255, 255, .08);
      --empty-bg: rgba(255, 255, 255, .015);

      --primary:    #6c00ff;
      --primary2:   #9b4dff;
      /* Тот же акцент в виде тройки каналов — для подсветок и свечения:
         rgb(var(--primary-rgb) / .12). Раньше на этих местах стояло 168 хардкодов
         rgba(124,58,237,…) / rgba(108,0,255,…), из-за чего подсветки не следовали ни
         за темой, ни тем более за выбранной схемой. Держать в синхроне с --primary. */
      --primary-rgb: 108 0 255;
      --primary-bg: rgb(var(--primary-rgb) / .09);
      /* Текст поверх фиолетовой подсветки (--primary-bg и rgba(124,58,237,.1x)).
         Отдельно от --primary2: тот в светлой теме светлее основного и на тинте
         даёт 3.1:1 — ниже AA. Замер: tests/suites/a11y.js «активные элементы». */
      --primary-on-tint: #b18aff;
      /* Акцентный текст (ссылки-действия, суммы, подписи). Отдельно от --primary2:
         тот светлее основного и как текст на белой панели даёт 3.6:1. */
      --primary-text: #b18aff;

      --blue:   #2563eb;
      --cyan:   #0891b2;
      --green:  #16a34a;
      --yellow: #f6bd3a;
      --orange: #ea580c;
      --red:    #f22822;
      /* Фиксированный фиолетовый для этапов воронки. Отдельно от --primary: тот
         следует за выбранной цветовой схемой, а этап «В работе» должен оставаться
         отличимым от «Сдано» и в зелёной схеме. См. CRM_STATUS_COLOR в app.js. */
      --violet: #8b5cf6;

      /* Status text — readable variant of each hue, dark-theme default.
         Цель — 5-8:1, а не «чем больше, тем лучше». Прежние значения (200-я ступень
         Tailwind: #fca5a5/#86efac/#93c5fd/#fdba74) давали 9-13:1 при пороге AA 4.5 —
         запас вдвое, купленный обесцвечиванием: «Расходы» и «Долг клиентов» на
         дашборде выходили бледно-розовыми, а не красными. Ниже — насыщенные оттенки
         с двукратным запасом над AA. Замер: tests/suites/a11y.js. */
      --text-success: #22c55e;
      --text-danger:  #ff5147;
      --text-info:    #57a5ff;
      --text-warning: #ff9d3d;

      /* Darker hue shade — hover-darken for solid buttons, same in both themes */
      --green-strong:  #15803d;
      --blue-strong:   #1d4ed8;
      --yellow-strong: #e6af2e;
      /* Бренд Premium Stock (stock.adervis.ru) — ЧУЖАЯ палитра: живёт только в
         карточке продукта в боковом меню (.side-promo) и одинакова в обеих темах.
         У продукта своя тёмная айдентика, и карточка несёт её, а не нашу. Золото
         совпадает с --yellow тёмной темы, но в светлой --yellow темнее — отсюда
         отдельные токены, не зависящие от темы. */
      --stock-ink:       #141414;
      --stock-gold:      #f6bd3a;
      --stock-gold-deep: #c8901f;
      /* Красной ступени в семье НЕ БЫЛО — и ровно поэтому три места держали свой
         #b91c1c / #d41e18 мимо токенов: затемнить красную кнопку было нечем.
         Значение то же, что уже стояло в двух из трёх. */
      --red-strong:    #b91c1c;

      /* Текст цветной «капсулы» (категории пакетов, база знаний, активные фильтры).
         Фон капсулы — полупрозрачный тинт, поэтому реальный контраст зависит от того,
         что под ним: светлый оттенок читается на тёмном и почти исчезает на белом.
         Раньше эти цвета были захардкожены в компонентах и в светлой теме давали
         до 2:1 — держим их здесь по одному на оттенок и переопределяем для светлой.
         Значения проверяются тестом «цветные капсулы читаемы в обеих темах». */
      --tint-violet: #c4b5fd;
      --tint-blue:   #93c5fd;
      --tint-green:  #86efac;
      --tint-orange: #fdba74;
      --tint-cyan:   #67e8f9;
      --tint-pink:   #f9a8d4;
      --tint-amber:  #fcd34d;
      --tint-indigo: #a5b4fc;
      --tint-red:    #fca5a5;
      --tint-slate:  #cbd5e1;
      /* Фоны капсул — альфа-тинты, работают в обеих темах, поэтому не дублируются */
      --tint-violet-bg: rgb(var(--primary-rgb) / .12);
      --tint-blue-bg:   rgba(37, 99, 235, .12);
      --tint-green-bg:  rgba(22, 163, 74, .14);
      --tint-orange-bg: rgba(234, 88, 12, .12);
      --tint-cyan-bg:   rgba(8, 145, 178, .12);
      --tint-pink-bg:   rgba(219, 39, 119, .12);
      --tint-amber-bg:  rgba(202, 138, 4, .14);
      --tint-indigo-bg: rgba(67, 56, 202, .12);

      /* VK brand blue — фиксированный фирменный цвет VK, одинаков в обеих темах */
      --vk-blue:       #0077ff;
      --vk-blue-hover: #005fcc;

      /* Text on a saturated/colored background (buttons, badges) — same in both themes */
      --on-color: #fff;

      /* Elevation scale — use instead of a single heavy --shadow */
      --elev-0: 0 1px 2px rgba(0,0,0,.2);
      --elev-1: 0 2px 8px rgba(0,0,0,.28), 0 1px 3px rgba(0,0,0,.18);
      --elev-2: 0 8px 24px rgba(0,0,0,.36), 0 2px 8px rgba(0,0,0,.22);
      --elev-3: 0 20px 56px rgba(0,0,0,.52), 0 6px 18px rgba(0,0,0,.28);
      --shadow: var(--elev-2);

      /* ── Градиенты и свечение ──────────────────────────────────────────────
         Приём из современных дашбордов: акцент не плоской заливкой, а мягким
         переходом со свечением по краю. Держим ТОКЕНАМИ и строим из
         --primary-rgb, чтобы градиент следовал и за темой, и за выбранной
         схемой: захардкоженный фиолетовый в «зелёной» схеме выглядел бы чужим,
         ровно как это было со 168 хардкодами rgba(124,58,237,…) до --primary-rgb.

           grad-hero  — крупная акцентная карточка (главная метрика, промо);
           grad-soft  — лёгкая подложка под плитку/строку под курсором;
           grad-btn   — заливка главной кнопки;
           glow-sm    — свечение под элементом (кнопка, карточка при наведении);
           glow-md    — заметное свечение у акцентной карточки и точки графика.

         Радиальная часть grad-hero сдвинута вправо-вверх (85% 0%): свет падает
         из угла, как в референсах, и не спорит с текстом, который идёт слева. */
      --grad-hero:
        radial-gradient(120% 150% at 85% 0%, rgb(var(--primary-rgb) / .55) 0%, rgb(var(--primary-rgb) / .14) 48%, transparent 100%),
        linear-gradient(135deg, rgb(var(--primary-rgb) / .30) 0%, rgb(var(--primary-rgb) / .06) 100%);
      --grad-soft: linear-gradient(180deg, rgb(var(--primary-rgb) / .10) 0%, rgb(var(--primary-rgb) / .02) 100%);
      --grad-btn: linear-gradient(135deg, var(--primary2) 0%, var(--primary) 100%);
      --glow-sm: 0 6px 20px -8px rgb(var(--primary-rgb) / .55);
      --glow-md: 0 10px 34px -10px rgb(var(--primary-rgb) / .70);

      /* ── Скругления ────────────────────────────────────────────────────────
         Замер по живому DOM (08.08.2026): на экранах встречалось ДЕСЯТЬ разных
         радиусов, в самом файле — восемнадцать значений от 2 до 999px, включая
         пары вроде 6/7/9 и 10/11, различить которые глазом нельзя, а вот
         несобранность интерфейса они дают.

         Шкала ровно на пять шагов, как у теней и длительностей:
           r-xs  — мелочь внутри строк (чипы, иконки, бейджи);
           r-sm  — кнопки, поля ввода, вложенные блоки;
           r-md  — плитки и карточки внутри панели;
           r-lg  — панели, модальные окна;
           r-xl  — крупные карточки, hero-блоки;
           r-pill — всё, что должно быть капсулой.

         Значения 14/18/20/24 намеренно оставлены как есть в нескольких местах:
         они подобраны под вложенность (внутренний радиус = внешний − отступ), и
         сведение их к шкале дало бы видимый «нотч» на скруглении. */
      /* Фокус-кольцо одним токеном: до этого кольцо рисовалось двумя разными
         цветами — глобальное правило брало --primary, а семь локальных (календарь,
         калькулятор) — --primary2. При переходе табом по одной странице подсветка
         меняла оттенок, хотя это ОДНО состояние: «сюда сейчас попадёт нажатие».
         Толщина и отступ тоже здесь — менять их поштучно негде. */
      --ring: var(--primary2);
      --ring-w: 2px;

      /* ── Высоты элементов управления ───────────────────────────────────────
         Замер по живому DOM (08.08.2026): на четырнадцати экранах встретилось
         ШЕСТНАДЦАТЬ разных высот кнопок, из них 27 кнопок ниже 32px — включая
         «Создать», «Открыть» и «Создать КП» в чеклисте первых шагов (27px). Это
         одновременно и неопрятно, и трудно попасть.

         Три шага: h-sm — компактные (фильтры, чипы, кнопки в строках таблиц),
         h-md — обычные кнопки и поля ввода, h-lg — главные действия и всё, до
         чего дотягиваются пальцем.

         Задаём МИНИМУМ, а не фиксированную высоту: кнопка с длинной подписью в
         две строки должна вырастать, а не обрезаться. Поэтому крупные кнопки
         (42–46px) остаются как есть — разнобой снизу опаснее. */
      --h-sm: 32px;
      --h-md: 36px;
      --h-lg: 44px;

      /* Шкала из DESIGN.md §5: 4 бейдж/чип · 8 инпут/мелкая кнопка · 12 вторичная
         карточка · 16 основная карточка и модалка · 24 нижний лист и крупные
         standalone-панели (экран входа, страница брифа, портал клиента).
         --r-md: 10px — единственное значение вне спеки, оставлено под мелкие
         внутренние элементы, которых в DESIGN.md нет. */
      --r-xs:   4px;
      --r-sm:   8px;
      --r-md:  10px;
      --r-lg:  12px;
      --r-xl:  16px;
      --r-2xl: 24px;
      --r-pill: 999px;

      /* ── Движение ──────────────────────────────────────────────────────────
         До этих токенов длительности расползлись по файлу десятью значениями
         (.12/.14/.15/.16/.18/.2/.22/.3/.35s) и пятью кривыми, из-за чего соседние
         элементы на одном экране двигались с разной скоростью. Три шага хватает:
           dur-1 — отклик на нажатие/наведение, глазом не читается как анимация;
           dur-2 — смена состояния (подсветка, раскрытие);
           dur-3 — появление слоя (модалка, шторка, всплывашка).
         ease-out для входа, ease-in для ухода, spring — только там, где нужен
         «отскок» (кружок схемы, счётчик), иначе интерфейс начинает пружинить весь.
         Уважение к prefers-reduced-motion — глобальным правилом ниже, не здесь. */
      --dur-1: .12s;
      --dur-2: .2s;
      --dur-3: .32s;
      --ease-out:    cubic-bezier(.16, 1, .3, 1);
      --ease-in:     cubic-bezier(.4, 0, 1, 1);
      --ease-spring: cubic-bezier(.34, 1.56, .64, 1);

      --input:  rgba(19, 19, 27, .95);

      /* Z-index scale */
      --z-sticky:  40;
      --z-topbar:  50;
      --z-dropdown: 200;
      --z-notif:   300;
      --z-modal:   500;
      --z-search:  600;
      --z-toast:   700;
      --z-tooltip: 800;
      --z-tour:    850;
      --z-auth:    999;
    }

    [data-theme="light"] {
      --bg: #f0f2f5;
      --bg2: #ffffff;
      --panel: rgba(255, 255, 255, .98);
      --panel2: rgba(244, 246, 249, .98);
      --text: #1a2332;
      --muted: #4a5568;
      --hint: #94a3b8;
      --line: rgba(30, 41, 59, .14);
      --empty-bg: rgba(30, 41, 59, .02);
      --input: #ffffff;
      --shadow: 0 2px 12px rgba(0, 0, 0, .09);

      /* Высоты для СВЕТЛОЙ темы. Раньше их не было вовсе: :root задаёт тени под
         тёмный фон (до 36% и 52% чёрного), и светлая тема брала их как есть.
         Замер: карточка в покое имела rgba(0,0,0,.09), а под курсором получала
         rgba(0,0,0,.36) с размытием 24px — тень темнела вчетверо и вырастала
         вдвое, читаясь грязным пятном под каждой карточкой.

         На светлом фоне тень работает иначе: её задача — приподнять край, а не
         затемнить область. Поэтому меньше прозрачности, меньше размытие и
         синеватый оттенок подложки (30,41,59) вместо чистого чёрного — чистый
         чёрный на белом выглядит грязным, а не глубоким. */
      --elev-0: 0 1px 2px rgba(30, 41, 59, .06);
      --elev-1: 0 2px 8px rgba(30, 41, 59, .08), 0 1px 2px rgba(30, 41, 59, .05);
      --elev-2: 0 6px 18px rgba(30, 41, 59, .10), 0 2px 6px rgba(30, 41, 59, .06);
      --elev-3: 0 16px 40px rgba(30, 41, 59, .16), 0 4px 12px rgba(30, 41, 59, .08);
      --primary: #5b4ee8;
      --primary2: #7c6fef;
      --primary-rgb: 91 78 232;
      --primary-bg: rgb(var(--primary-rgb) / .09);
      /* on-tint темнее, чем primary-text, и это не опечатка: подсветки местами
         накладываются друг на друга (активный пункт каталога + счётчик внутри него),
         и суммарный тинт заметно темнее одиночного. На нём #6d28d9 даёт 4.33:1.
         Ловится тестом «текст в интерфейсе: контраст отвечает WCAG AA». */
      --primary-on-tint: #5b21b6;
      --primary-text: #6d28d9;
      --blue: #2563eb;
      --cyan: #0891b2;
      --green: #16a34a;
      --yellow: #ca8a04;
      --orange: #ea580c;
      --red: #dc2626;
      --violet: #7c3aed;

      /* Светлая тема: та же цель 5-6:1. Прежние значения уходили в другую крайность —
         #991b1b (7.4:1) читается кирпично-бурым, а не красным. Плюс здесь закрыт
         скрытый промах: --text-success был var(--green-strong) = #15803d, и на фоне
         страницы --bg #f0f2f5 это 4.47:1 — ниже AA. Пробник a11y ловил его только на
         белых панелях (5.02:1), поэтому тест молчал. */
      --text-success: #14783c;
      --text-danger:  #c81e1e;
      --text-info:    var(--blue-strong);
      --text-warning: #b8400c;

      /* Тёмные варианты тех же оттенков: на белом читаются 5-8:1 вместо 2-4:1.
         У зелёного, оранжевого и янтарного взята 800-я ступень, а не 700-я:
         на 700-й замер давал 4,29-4,47:1 — формально ниже порога AA 4,5:1. */
      --tint-violet: #6d28d9;
      --tint-blue:   #1d4ed8;
      --tint-green:  #166534;
      --tint-orange: #9a3412;
      --tint-cyan:   #155e75;
      --tint-pink:   #be185d;
      --tint-amber:  #854d0e;
      --tint-indigo: #4338ca;
      --tint-red:    #991b1b;
      --tint-slate:  #475569;

      /* Градиенты и свечение в светлой теме — вдвое тише. Те же значения, что в
         тёмной, на белой панели читаются как грязное пятно: там свет ложится НА
         тёмное, здесь — на светлое, и глазу нужен куда меньший перепад. */
      --grad-hero:
        radial-gradient(120% 150% at 85% 0%, rgb(var(--primary-rgb) / .28) 0%, rgb(var(--primary-rgb) / .08) 48%, transparent 100%),
        linear-gradient(135deg, rgb(var(--primary-rgb) / .16) 0%, rgb(var(--primary-rgb) / .03) 100%);
      --grad-soft: linear-gradient(180deg, rgb(var(--primary-rgb) / .07) 0%, rgb(var(--primary-rgb) / .015) 100%);
      --glow-sm: 0 6px 18px -10px rgb(var(--primary-rgb) / .45);
      --glow-md: 0 10px 28px -12px rgb(var(--primary-rgb) / .50);
    }

    /* ── Цветовые схемы (акцент) ─────────────────────────────────────────────
       Настройки → Оформление. Схема меняет ТОЛЬКО акцент; фоны, текст и статусные
       цвета (успех/ошибка) общие — иначе «зелёная» схема сделала бы неотличимыми
       акцент и признак успеха, а «красная» — акцент и ошибку. По той же причине в
       наборе нет красной схемы: она конфликтует с --text-danger.

       Специфичность: [data-accent=…] и [data-theme=light] равны (0,1,0), поэтому
       светлые значения задаются парой селекторов (0,2,0) — она выигрывает
       независимо от порядка правил в файле.

       Каждое значение проверено на AA: --primary держит ≥4.5:1 с белой подписью
       кнопки, --primary-text — ≥4.5:1 на всех трёх фонах своей темы.
       Сторож: tests/suites/a11y.js «цветовые схемы читаемы в обеих темах». */
    [data-accent="indigo"] {
      --primary: #1f6feb; --primary2: #4d9dff; --primary-rgb: 31 111 235;
      --primary-text: #6ba8ff; --primary-on-tint: #6ba8ff;
    }
    [data-theme="light"][data-accent="indigo"] {
      --primary: #1d4ed8; --primary2: #3b82f6; --primary-rgb: 29 78 216;
      --primary-text: #1d4ed8; --primary-on-tint: #1e40af;
    }

    [data-accent="emerald"] {
      --primary: #0a7a43; --primary2: #16b364; --primary-rgb: 10 122 67;
      --primary-text: #22c55e; --primary-on-tint: #22c55e;
    }
    [data-theme="light"][data-accent="emerald"] {
      --primary: #047857; --primary2: #10b981; --primary-rgb: 4 120 87;
      --primary-text: #14783c; --primary-on-tint: #14532d;
    }

    [data-accent="amber"] {
      --primary: #b45309; --primary2: #e08c0b; --primary-rgb: 180 83 9;
      --primary-text: #f0b429; --primary-on-tint: #f0b429;
    }
    [data-theme="light"][data-accent="amber"] {
      --primary: #b45309; --primary2: #d97706; --primary-rgb: 180 83 9;
      --primary-text: #9a4c07; --primary-on-tint: #713f12;
    }

    [data-accent="teal"] {
      --primary: #0d7f8f; --primary2: #14b8c4; --primary-rgb: 13 127 143;
      --primary-text: #22d3ee; --primary-on-tint: #22d3ee;
    }
    [data-theme="light"][data-accent="teal"] {
      --primary: #0f766e; --primary2: #0d9488; --primary-rgb: 15 118 110;
      --primary-text: #0e6b64; --primary-on-tint: #115e59;
    }

    [data-accent="graphite"] {
      --primary: #4b5563; --primary2: #7c8a9e; --primary-rgb: 75 85 99;
      --primary-text: #9aa8bc; --primary-on-tint: #9aa8bc;
    }
    [data-theme="light"][data-accent="graphite"] {
      --primary: #475569; --primary2: #64748b; --primary-rgb: 71 85 105;
      --primary-text: #3f4c5f; --primary-on-tint: #3f4c5f;
    }

    /* Сине-стальная. Отличается от «Синей» (indigo) тем, что уходит не в чистый
       синий, а в холодный сине-серый: с ним градиенты и свечение дают ту самую
       «ночную» подачу аналитических панелей, ради которой схему и заводили.
       От «Графитовой» отличается наличием цвета — та полностью нейтральна. */
    [data-accent="midnight"] {
      --primary: #3352d1; --primary2: #6d8bff; --primary-rgb: 51 82 209;
      --primary-text: #8fa8ff; --primary-on-tint: #8fa8ff;
    }
    [data-theme="light"][data-accent="midnight"] {
      --primary: #2f49b8; --primary2: #4d6ae0; --primary-rgb: 47 73 184;
      --primary-text: #2a4099; --primary-on-tint: #24377f;
    }

    [data-theme="light"] body {
      background:
        radial-gradient(circle at top left, rgb(var(--primary-rgb) / .06), transparent 40%),
        linear-gradient(160deg, #f0f2f5 0%, #e6e9ee 100%);
    }

    [data-theme="light"] .topbar {
      background: rgba(255, 255, 255, .96);
      border-bottom-color: rgba(30, 41, 59, .12);
    }

    * {
      box-sizing: border-box;
    }

    /* Единый видимый focus-индикатор для клавиатурной навигации (WCAG 2.1 AA §2.4.7).
       :focus-visible (не :focus) — рамка появляется только при Tab, не при клике мышью.
       Работает глобально, поверх любых локальных outline:none (input/select/textarea и т.п.) */
    :focus-visible {
      outline: var(--ring-w) solid var(--ring);
      outline-offset: 2px;
      border-radius: var(--r-xs);
    }

    @media (prefers-reduced-motion: reduce) {
      *, *::before, *::after {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
      }
    }

    html,
    body {
      min-height: 100%;
    }

    body {
      margin: 0;
      overflow-x: hidden;
      font-family: var(--font-ui);
      background:
        radial-gradient(circle at top left, rgb(var(--primary-rgb) / .12), transparent 40%),
        linear-gradient(180deg, var(--bg) 0%, var(--bg2) 100%);
      color: var(--text);
    }

    button,
    input,
    textarea,
    select {
      font: inherit;
    }

    button {
      cursor: pointer;
    }

    .app {
      display: flex;
      min-height: 100vh;
    }

    /* ── Sidebar ───────────────────────────────────────── */
    .app-sidebar {
      width: 224px;
      flex: 0 0 224px;
      position: sticky;
      top: 0;
      height: 100vh;
      display: flex;
      flex-direction: column;
      background: var(--bg2);
      border-right: 1px solid var(--line);
      overflow: hidden;
      z-index: var(--z-sticky);
      scrollbar-width: none;
      transition: width var(--dur-3) var(--ease-out),
                  flex-basis var(--dur-3) var(--ease-out);
    }
    .app-sidebar::-webkit-scrollbar { display: none; }
    .app-sidebar:empty { display: none; }

    /* Sidebar labels — fade+clip instead of display:none so they can transition */
    .sidebar-label,
    .sidebar-brand-name,
    .sidebar-brand-sub,
    .sidebar-project-hint,
    .sidebar-badge {
      overflow: hidden;
      flex-shrink: 1;
      max-width: 200px;
      opacity: 1;
      /* fade slightly after width starts expanding; hide before width finishes collapsing */
      transition: max-width var(--dur-3) var(--ease-out),
                  opacity var(--dur-2) var(--ease-out);
    }

    /* ── Collapsed sidebar ─────────────────────────────── */
    .app-sidebar.collapsed { width: 64px; flex: 0 0 64px; }

    .app-sidebar.collapsed .sidebar-label,
    .app-sidebar.collapsed .sidebar-brand-name,
    .app-sidebar.collapsed .sidebar-brand-sub,
    .app-sidebar.collapsed .sidebar-project-hint,
    .app-sidebar.collapsed .sidebar-badge {
      max-width: 0;
      opacity: 0;
      pointer-events: none;
    }

    .app-sidebar.collapsed .sidebar-header {
      padding: 14px 16px;
    }
    .app-sidebar.collapsed .sidebar-brand { gap: 0; }
    .app-sidebar.collapsed .sidebar-nav { padding: 6px; }
    .app-sidebar.collapsed .sidebar-nav-item {
      padding: 10px 18px;
      gap: 0;
      border-radius: var(--r-md);
    }
    .app-sidebar.collapsed .sidebar-nav-item svg {
      color: var(--text);
      opacity: .6;
    }
    .app-sidebar.collapsed .sidebar-nav-item.active svg { opacity: 1; color: var(--primary-text); }
    .app-sidebar.collapsed .sidebar-nav-item:hover svg { opacity: 1; }

    .app-sidebar.collapsed .sidebar-footer { padding: 6px; }
    .app-sidebar.collapsed .sidebar-footer .sidebar-nav-item {
      padding: 10px 18px;
    }

    /* CSS tooltip при наведении в collapsed */
    .app-sidebar.collapsed .sidebar-nav-item { position: relative; }
    .app-sidebar.collapsed .sidebar-nav-item[title]::after {
      content: attr(title);
      position: fixed;
      left: 72px;
      background: var(--panel);
      color: var(--text);
      font-size: 12px; font-weight: 600;
      padding: 6px 12px;
      border-radius: var(--r-sm);
      border: 1px solid var(--line);
      white-space: nowrap;
      pointer-events: none;
      opacity: 0;
      /* Второе значение — ЗАДЕРЖКА, а не длительность: подсказка не выскакивает
         сразу, пока курсор просто проезжает мимо. Токен сюда не подставлять. */
      transition: opacity var(--dur-1) .06s;
      z-index: var(--z-tooltip);
      box-shadow: var(--elev-2);
    }
    .app-sidebar.collapsed .sidebar-nav-item[title]:hover::after { opacity: 1; }

    /* ── Sidebar header ─────────────────────────────────── */
    .sidebar-header {
      display: flex; align-items: center; justify-content: space-between;
      padding: 16px 14px 12px; flex-shrink: 0;
      transition: padding var(--dur-3) var(--ease-out);
    }
    .sidebar-brand {
      display: flex; align-items: center; gap: 10px; cursor: pointer; min-width: 0; flex: 1;
      -webkit-user-select: none; user-select: none;
      transition: gap var(--dur-3) var(--ease-out);
    }
    .sidebar-collapse-btn { display: none; }

    /* Текущий проект под Смета */
    .sidebar-project-hint {
      display: block; font-size: 12px; color: var(--primary-text);
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
      font-weight: 600; margin-top: 1px;
    }

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

    .app-main {
      flex: 1;
      min-width: 0;
      display: flex;
      flex-direction: column;
      min-height: 100vh;
    }

    .sidebar-brand .logo {
      width: 34px; height: 34px; border-radius: var(--r-md); flex-shrink: 0;
    }
    .sidebar-brand-name { font-size: 13px; font-weight: 900; letter-spacing: .2px; line-height: 1.2; white-space: nowrap; }
    .sidebar-brand-sub { font-size: 12px; color: var(--muted); margin-top: 2px; white-space: nowrap; }

    .sidebar-nav {
      padding: 10px 8px;
      flex: 1;
      display: flex;
      flex-direction: column;
      gap: 1px;
      transition: padding var(--dur-3) var(--ease-out);
      /* Список может прокручиваться, а шапка и подвал (профиль, «Выйти») — нет.
         Меню высотой ровно в экран с overflow:hidden, и без этих двух строк
         на невысоком экране НИЗ МЕНЮ СРЕЗАЛСЯ: замер 11.09.2026 — на 700px
         «Выйти» уходил за край на 9px, на 600px на 109px, а у суперадмина
         подвал ещё выше на строку «Admin Panel». Ноутбук 1366×768 даёт в
         браузере как раз 650–700px. Полоса прокрутки скрыта, как у самого меню. */
      min-height: 0;
      overflow-y: auto;
      scrollbar-width: none;
    }
    .sidebar-nav::-webkit-scrollbar { display: none; }

    .sidebar-nav-config {
      position: fixed;
      width: 208px;
      background: var(--panel);
      border: 1px solid var(--line);
      border-radius: var(--r-lg);
      padding: 10px;
      box-shadow: 0 16px 48px rgba(0,0,0,.4);
      z-index: var(--z-modal);
      animation: catalogQtyPop .16s cubic-bezier(.16,1,.3,1);
    }
    .sidebar-nav-config-title {
      font-size: 12px;
      font-weight: 700;
      color: var(--muted);
      text-transform: uppercase;
      letter-spacing: .06em;
      padding: 2px 6px 8px;
    }
    .sidebar-nav-config-row {
      display: flex;
      align-items: center;
      gap: 8px;
      padding: 6px;
      border-radius: var(--r-sm);
      cursor: grab;
    }
    .sidebar-nav-config-row:hover { background: var(--panel2); }
    .sidebar-nav-config-label {
      flex: 1;
      font-size: 13px;
      font-weight: 600;
      color: var(--text);
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .sidebar-nav-switch {
      flex: 0 0 auto;
      width: 34px;
      height: 20px;
      border-radius: var(--r-pill);
      border: none;
      background: var(--line);
      position: relative;
      cursor: pointer;
      transition: background var(--dur-1);
    }
    .sidebar-nav-switch::after {
      content: "";
      position: absolute;
      top: 2px; left: 2px;
      width: 16px; height: 16px;
      border-radius: 50%;
      background: var(--on-color);
      transition: transform var(--dur-1);
    }
    .sidebar-nav-switch.on { background: var(--primary); }
    .sidebar-nav-switch.on::after { transform: translateX(14px); }

    /* ── PWA INSTALL TOAST ───────────────────────────── */
    .pwa-install-toast {
      position: fixed;
      left: 50%;
      bottom: 24px;
      transform: translateX(-50%);
      z-index: var(--z-toast);
      display: flex;
      align-items: center;
      gap: 12px;
      max-width: min(440px, calc(100vw - 32px));
      background: var(--panel);
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      padding: 12px 14px;
      box-shadow: 0 20px 56px rgba(0,0,0,.45);
      animation: pwaToastSlideUp .25s cubic-bezier(.16,1,.3,1);
    }
    @keyframes pwaToastSlideUp {
      from { opacity: 0; transform: translateX(-50%) translateY(16px); }
      to   { opacity: 1; transform: translateX(-50%) translateY(0); }
    }
    .pwa-install-toast-icon { width: 36px; height: 36px; border-radius: var(--r-sm); flex-shrink: 0; }
    .pwa-install-toast-text { display: flex; flex-direction: column; gap: 1px; flex: 1; min-width: 0; }
    .pwa-install-toast-text strong { font-size: 13px; }
    .pwa-install-toast-text span { font-size: 12px; color: var(--muted); }
    .pwa-install-toast-close {
      flex: 0 0 auto;
      background: none; border: none; color: var(--muted);
      font-size: 18px; line-height: 1; cursor: pointer; padding: 2px 4px;
    }
    .pwa-install-toast-close:hover { color: var(--text); }
    @media (max-width: 480px) {
      .pwa-install-toast { left: 16px; right: 16px; bottom: 76px; transform: none; max-width: none; }
      @keyframes pwaToastSlideUp { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: translateY(0); } }
    }

    .sidebar-nav-item {
      display: flex;
      align-items: center;
      gap: 11px;
      padding: 9px 12px;
      border-radius: var(--r-md);
      border: 1px solid transparent;
      background: transparent;
      color: var(--muted);
      font-size: 13px;
      font-weight: 600;
      cursor: pointer;
      width: 100%;
      text-align: left;
      transition: background var(--dur-1), color var(--dur-1), border-color var(--dur-1),
                  padding var(--dur-3) var(--ease-out),
                  gap var(--dur-3) var(--ease-out);
      white-space: nowrap;
    }
    .sidebar-nav-item svg { flex-shrink: 0; opacity: .65; transition: opacity var(--dur-1), color var(--dur-3) var(--ease-out); }
    .sidebar-nav-item:hover { background: var(--primary-bg); color: var(--text); border-color: rgb(var(--primary-rgb) / .14); }
    .sidebar-nav-item:hover svg { opacity: 1; }
    .sidebar-nav-item.active {
      background: rgb(var(--primary-rgb) / .14);
      color: var(--primary-on-tint);
      border-color: rgb(var(--primary-rgb) / .26);
    }
    .sidebar-nav-item.active svg { opacity: 1; color: var(--primary-text); }

    .sidebar-divider { height: 1px; background: var(--line); margin: 6px 10px; }

    .sidebar-section-label {
      font-size: 12px;
      font-weight: 700;
      color: var(--muted);
      text-transform: uppercase;
      letter-spacing: .08em;
      padding: 10px 12px 4px;
      opacity: .7;
    }

    .sidebar-footer {
      border-top: 1px solid var(--line);
      padding: 12px 10px;
      flex-shrink: 0;
      transition: padding var(--dur-3) var(--ease-out);
    }

    /* ── Профиль + выход одной строкой ───────────────────────────────────────
       Просьба владельца 11.09.2026: «Выйти» отдельной строкой занимал место.
       Теперь это значок справа от профиля. Вплотную к имени промахнуться легко,
       а выход не переспрашивает — поэтому два нажатия, и оба видны глазом:
         покой   — приглушённый значок двери;
         наведение — краснеет, стрелка «выходит» из двери на 2px;
         1-е нажатие — раскрывается в красную пилюлю «Выйти?» (ширина текста
           едет от 0, имя слева уступает место и уходит в многоточие);
         2-е нажатие — выход. Через 3 с или по уходу фокуса — схлопывается. */
    .sidebar-user-row { display: flex; align-items: center; gap: 4px; }
    .sidebar-user-row .sidebar-user-main { flex: 1 1 auto; min-width: 0; }
    .sidebar-logout {
      flex: 0 0 auto;
      height: 32px; min-width: 32px; padding: 0 8px;
      display: inline-flex; align-items: center; justify-content: center; gap: 0;
      border: none; border-radius: var(--r-md);
      background: transparent; color: var(--muted);
      font: inherit; cursor: pointer;
      transition: background var(--dur-2) var(--ease-out), color var(--dur-2) var(--ease-out),
                  gap var(--dur-3) var(--ease-out);
    }
    .sidebar-logout-text {
      max-width: 0; opacity: 0; overflow: hidden; white-space: nowrap;
      font-size: 12.5px; font-weight: 700;
      transition: max-width var(--dur-3) var(--ease-out), opacity var(--dur-2) var(--ease-out);
    }
    .sidebar-logout-arrow { transition: transform var(--dur-2) var(--ease-out); }
    .sidebar-logout:hover {
      color: var(--text-danger);
      background: color-mix(in srgb, var(--red) 12%, transparent);
    }
    .sidebar-logout:hover .sidebar-logout-arrow { transform: translateX(2px); }
    .sidebar-logout.armed {
      gap: 6px;
      background: var(--red); color: var(--on-color);
    }
    .sidebar-logout.armed .sidebar-logout-text { max-width: 64px; opacity: 1; }
    .sidebar-logout.armed .sidebar-logout-arrow { transform: translateX(2px); }
    /* Свёрнутое меню — 64px: строка встаёт столбиком, пилюле раскрыться некуда,
       поэтому взведённое состояние там — просто красный значок. */
    .app-sidebar.collapsed .sidebar-user-row { flex-direction: column; gap: 2px; }
    .app-sidebar.collapsed .sidebar-logout-text { display: none; }
    @media (prefers-reduced-motion: reduce) {
      .sidebar-logout, .sidebar-logout-text, .sidebar-logout-arrow { transition: none; }
    }

    /* ── Карточка своего продукта в боковом меню ─────────────────────────────
       Просьба владельца 11.09.2026: рекламировать в пустом месте меню свои
       другие продукты. Сейчас один — Premium Stock (stock.adervis.ru).

       Меню высотой ровно в экран и с overflow:hidden — прокрутки в нём НЕТ.
       Карточка, которой не хватило места, вытолкнула бы за край «Выйти» и
       профиль. Поэтому слот занимает только ОСТАВШЕЕСЯ место (flex 1 1 0), а
       сама карточка решает по его высоте: полная, короткая или никакой.

       Тон — тон самого продукта: «надёжный, прямой, быстрый», без кислотных
       градиентов и мигания (PRODUCT.md Premium Stock). Своя тёмная айдентика
       в обеих темах: это карточка продукта, а не наш интерфейс. */
    .side-promo-slot {
      flex: 1 1 0; min-height: 0;
      container: sidepromo / size;
      display: flex; flex-direction: column; justify-content: flex-end;
      /* Без своих отступов: слот обязан сжиматься ДО НУЛЯ. Первая версия держала
         padding-top: 12px — сжаться ниже он не давал и сдвигал «Выйти» на 13px
         вниз как раз там, где места и так нет. Отступ сверху — у обёртки, и
         исчезает вместе с ней. -1px гасит зазор gap:1px, который списку
         достаётся от лишнего ребёнка. */
      margin-top: -1px;
    }
    .app-sidebar.collapsed .side-promo-slot { display: none; }
    /* Крестик привязан к обёртке карточки, а не к слоту: карточка прижата к низу
       слота, и при высоком слоте крестик висел бы далеко над ней. */
    .side-promo-wrap { position: relative; margin-top: 12px; }
    .side-promo {
      display: flex; flex-direction: column; gap: 8px;
      padding: 14px;
      border-radius: var(--r-xl);
      /* Сплошной фон ОТДЕЛЬНОЙ строкой: браузер без color-mix отбросит
         следующее объявление целиком, вместе с --stock-ink внутри, — и белый
         текст остался бы на светлом меню невидимым. */
      background: var(--stock-ink);
      background:
        radial-gradient(120% 70% at 100% 0%, color-mix(in srgb, var(--stock-gold) 16%, transparent), transparent 62%),
        var(--stock-ink);
      border: 1px solid color-mix(in srgb, var(--stock-gold) 22%, transparent);
      color: var(--on-color);
      text-decoration: none;
      transition: border-color var(--dur-2) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
    }
    .side-promo:hover {
      border-color: color-mix(in srgb, var(--stock-gold) 50%, transparent);
      box-shadow: 0 8px 24px color-mix(in srgb, var(--stock-gold) 14%, transparent);
    }
    /* Постоянное движение карточки (владелец 28.09.2026: «баннер adervis stock
       должен быть с анимацией постоянной»). Три спокойных слоя: золотое
       свечение медленно дрейфует, по карточке раз в несколько секунд проходит
       блик, кнопка мягко «дышит». Всё — ПОД текстом (слой наложения карточки и
       z-index:-1) и на transform/opacity: композитор двигает готовые слои,
       перерисовки нет. */
    .side-promo { position: relative; overflow: hidden; isolation: isolate; }
    .side-promo::before {
      content: ""; position: absolute; z-index: -1; pointer-events: none;
      width: 150%; height: 150%; top: -55%; left: -25%;
      background: radial-gradient(closest-side, color-mix(in srgb, var(--stock-gold) 24%, transparent), transparent);
      animation: stockGlow 9s ease-in-out infinite alternate;
    }
    @keyframes stockGlow {
      0%   { transform: translate(28%, -6%) scale(.8); }
      50%  { transform: translate(-12%, 18%) scale(1); }
      100% { transform: translate(18%, 42%) scale(.9); }
    }
    .side-promo::after {
      content: ""; position: absolute; z-index: -1; pointer-events: none;
      top: 0; bottom: 0; left: -60%; width: 45%;
      background: linear-gradient(105deg, transparent,
        color-mix(in srgb, var(--on-color) 11%, transparent) 50%, transparent);
      animation: stockShine 5.6s ease-in-out infinite;
    }
    @keyframes stockShine {
      0%, 55% { transform: translateX(0); }
      85%, 100% { transform: translateX(420%); }
    }
    @keyframes stockCta {
      0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--stock-gold) 0%, transparent); }
      50%      { box-shadow: 0 0 0 5px color-mix(in srgb, var(--stock-gold) 18%, transparent),
                             0 6px 18px -6px color-mix(in srgb, var(--stock-gold) 60%, transparent); }
    }
    /* Фокус — наш, а не золотой: кольцо одно на всё приложение (сторож в assets),
       иначе при переходе табом подсветка меняла бы оттенок на этой карточке. */
    .side-promo:focus-visible { outline: var(--ring-w) solid var(--ring); outline-offset: 2px; }
    .side-promo-logo { height: 26px; width: auto; align-self: flex-start; display: block; }
    .side-promo-title {
      font-family: "Space Grotesk", "DM Sans", system-ui, sans-serif;
      font-size: 15px; font-weight: 700; line-height: 1.25;
      text-wrap: balance;
    }
    .side-promo-text {
      font-size: 12px; line-height: 1.4;
      color: color-mix(in srgb, var(--on-color) 62%, transparent);
    }
    /* Сменяющиеся пары «заголовок + строка» (владелец 02.10.2026: «чтобы читали»).
       Пары лежат в ОДНОЙ ячейке сетки: высота карточки = самая длинная пара, и
       при смене ничего не прыгает. Ключевые кадры — под 4 пары по 5 с
       (SIDE_PROMO_SLIDES / SIDE_PROMO_SLIDE_MS в app.js): 25% цикла на пару,
       вход и уход по 0,5 с. Фаза приходит из app.js от часов — перерисовка
       меню смену не обнуляет. Наведение или фокус — пауза, чтобы дочитать. */
    .side-promo-msgs { display: grid; }
    .side-promo-msg { grid-area: 1 / 1; align-self: start; display: flex; flex-direction: column; gap: 8px; }
    .side-promo-msgs.is-rotating .side-promo-msg {
      animation: promoMsg 20s var(--ease-out) infinite both;
      animation-delay: calc(var(--promo-phase, 0ms) + var(--i, 0) * 5s);
    }
    .side-promo:hover .side-promo-msg,
    .side-promo:focus-visible .side-promo-msg { animation-play-state: paused; }
    @keyframes promoMsg {
      0%        { opacity: 0; transform: translateY(8px); }
      2.5%, 22.5% { opacity: 1; transform: none; }
      25%, 100% { opacity: 0; transform: translateY(-8px); }
    }
    .side-promo-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 2px; }
    .side-promo-price {
      font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums;
      color: var(--stock-gold); white-space: nowrap;
    }
    /* Кнопка — того же роста и формы, что все .btn.small (правило владельца
       «стилистика кнопок одинаковая»), только в цветах продукта. Это span, а не
       button: вся карточка уже ссылка, кнопка внутри ссылки — невалидная разметка.
       Селектор на класс сильнее, чем кажется нужным: .btn.small той же силы
       стоит в файле НИЖЕ и перекрашивал кнопку в серый (снимок 11.09.2026). */
    .side-promo .btn.side-promo-cta,
    .side-promo:hover .btn.side-promo-cta {
      background: linear-gradient(90deg, var(--stock-gold), var(--stock-gold-deep));
      border-color: transparent;
      color: var(--stock-ink);
      font-weight: 700;
      animation: stockCta 2.8s ease-in-out infinite;
    }
    /* «Уменьшить движение» — анимации карточки выключаем ЯВНО. Общее правило
       проекта ускоряет их до 0,01 мс, а бесконечная анимация на такой
       скорости превратилась бы в мерцание. */
    @media (prefers-reduced-motion: reduce) {
      .side-promo::before, .side-promo::after,
      .side-promo .btn.side-promo-cta,
      .side-promo-msgs.is-rotating .side-promo-msg { animation: none; }
      /* без смены — только первая пара, остальные не должны лечь поверх */
      .side-promo-msg:not(:first-child) { visibility: hidden; }
    }
    .side-promo-close {
      position: absolute; top: 8px; right: 8px;
      width: 26px; height: 26px; border: none; border-radius: var(--r-sm);
      display: grid; place-items: center;
      background: none; cursor: pointer;
      color: color-mix(in srgb, var(--on-color) 50%, transparent);
    }
    .side-promo-close:hover {
      color: var(--on-color);
      background: color-mix(in srgb, var(--on-color) 10%, transparent);
    }
    /* Места мало — короткая карточка: знак, цена, кнопка. Меньше — никакой:
       обрезанная реклама хуже отсутствующей. Пороги из замера 11.09.2026:
       полная карточка 204px + 12px отступа = 216 (берём 224, с запасом на
       перенос строки), короткая 100 + 12 = 112. Сменится текст — перемерить. */
    /* Та же карточка в листе «Разделы» на телефоне — в самом низу, после
       «Настроить меню». Компактно: без абзаца описания (лист и так длинный). */
    .side-promo-wrap--sheet { margin: 14px 4px 4px; }
    .side-promo-wrap--sheet .side-promo-text { display: none; }
    @container sidepromo (max-height: 223px) {
      .side-promo-msgs { display: none; }
    }
    @container sidepromo (max-height: 111px) {
      .side-promo-wrap { display: none; }
    }
    .sidebar-user-btn {
      display: flex;
      align-items: center;
      gap: 10px;
      width: 100%;
      padding: 8px 8px;
      border-radius: var(--r-md);
      border: 1px solid transparent;
      background: transparent;
      cursor: pointer;
      text-align: left;
      transition: background var(--dur-1);
    }
    .sidebar-user-btn:hover { background: var(--primary-bg); border-color: rgb(var(--primary-rgb) / .12); }
    .sidebar-user-avatar {
      width: 30px; height: 30px; border-radius: 50%;
      background: var(--primary);
      display: grid; place-items: center;
      font-size: 12px; font-weight: 700; color: var(--on-color); flex-shrink: 0;
    }
    .sidebar-user-name { font-size: 12px; font-weight: 700; color: var(--text); line-height: 1.2; }
    .sidebar-user-sub { font-size: 12px; color: var(--muted); margin-top: 1px; }

    /* ── Topbar icon buttons ───────────────────────────────── */
    /* Значки шапки — в собственных «стеклянных» кружках, а не просто иконки на
       фоне. Без рамки они висели в пустоте и не читались как кнопки: единственным
       признаком нажимаемости был курсор, которого на телефоне нет. Рамка едва
       заметна (6% линии), поэтому шапка не превращается в ряд коробок. */
    .topbar-icon-btn {
      width: 34px; height: 34px; padding: 0;
      display: grid; place-items: center;
      border: 1px solid var(--line);
      background: var(--panel2);
      border-radius: var(--r-md);
      color: var(--muted);
      cursor: pointer;
      transition: background var(--dur-1), color var(--dur-1),
                  border-color var(--dur-1), box-shadow var(--dur-1);
      flex-shrink: 0;
    }
    .topbar-icon-btn:hover {
      background: var(--primary-bg);
      color: var(--text);
      border-color: rgb(var(--primary-rgb) / .35);
      box-shadow: var(--glow-sm);
    }
    .topbar-utils { display: flex; align-items: center; gap: 2px; }
    .topbar-page-title { display: none; }
    .topbar-page-title h2 { font-size: 17px; font-weight: 800; margin: 0; letter-spacing: -.3px; }
    .topbar-page-title p { font-size: 12px; color: var(--muted); margin: 1px 0 0; }

    /* ── Topbar add button — круглая кнопка + ─────────────── */
    .topbar-add-btn {
      width: 38px; height: 38px; padding: 0;
      border-radius: 50%; border: none;
      background: var(--primary);
      color: var(--on-color); font-size: 22px; font-weight: 300; line-height: 1;
      display: grid; place-items: center;
      cursor: pointer; transition: background var(--dur-1), transform var(--dur-1), box-shadow var(--dur-1);
      box-shadow: 0 4px 14px rgb(var(--primary-rgb) / .35);
      flex-shrink: 0;
    }
    .topbar-add-btn:hover { background: var(--primary2); transform: scale(1.07); box-shadow: 0 6px 20px rgb(var(--primary-rgb) / .45); }
    .topbar-add-label, .topbar-add-chevron { display: none !important; }
    .topbar-add-icon { display: none; }

    /* ── Topbar: десктоп и мобильные ─────────────────────────
       Справа всегда: [тема] [поиск] [уведомления] [+] [аватар]
       Слева на мобильных: лого + горизонтальный нав
       Слева на десктопе: пусто (sidebar держит навигацию)
    ────────────────────────────────────────────────────────── */
    @media (min-width: 769px) {
      .topbar {
        display: flex !important;
        align-items: center;
        padding: 0 20px !important;
        height: 62px;
        gap: 6px;
      }
      /* На десктопе скрываем мобильные элементы */
      .topbar .brand,
      .topbar .burger-btn,
      .topbar .nav { display: none !important; }
      .topbar-page-title { display: none !important; }
      /* Весь .top-actions прижат вправо */
      .top-actions {
        margin-left: auto;
        display: flex;
        align-items: center;
        gap: 6px;
        flex-shrink: 0;
      }
      .topbar-icon-btn { width: 38px; height: 38px; }
    }

    @media (max-width: 768px) {
      .topbar-page-title { display: none !important; }
      /* На мобиле автосохранение не нужно в шапке */
      .autosave-indicator { display: none; }
    }

    /* Sidebar footer tools (theme + settings) */
    .sidebar-footer-tools {
      display: flex; align-items: center; gap: 4px;
      padding: 8px 10px 4px;
    }
    .sidebar-tool-btn {
      width: 32px; height: 32px; padding: 0;
      display: grid; place-items: center;
      border: 1px solid transparent; background: transparent;
      border-radius: var(--r-sm); color: var(--muted); cursor: pointer;
      transition: background var(--dur-1), color var(--dur-1), border-color var(--dur-1); flex-shrink: 0;
    }
    .sidebar-tool-btn:hover {
      background: var(--primary-bg); color: var(--text);
      border-color: rgb(var(--primary-rgb) / .18);
    }
    .sidebar-logout-btn {
      width: 30px; height: 30px; padding: 0;
      display: grid; place-items: center;
      border: 1px solid var(--line); background: transparent;
      border-radius: var(--r-sm); color: var(--muted); cursor: pointer;
      font-size: 16px; transition: var(--dur-1); flex-shrink: 0;
    }
    .sidebar-logout-btn:hover { background: rgba(220,38,38,.1); color: var(--text-danger); border-color: rgba(220,38,38,.3); }

    /* Hide sidebar + show mobile nav on mobile */
    @media (max-width: 768px) {
      .app-sidebar { display: none !important; }
      .app { display: block; }
      .app-main { min-height: 100vh; }
    }

    /* brief/portal/calc mode — no sidebar */
    body.brief-mode .app-sidebar,
    body.portal-mode .app-sidebar,
    body.calc-mode .app-sidebar { display: none !important; }
    body.brief-mode .app,
    body.portal-mode .app,
    body.calc-mode .app { display: block; }

    .topbar {
      position: sticky;
      top: 0;
      z-index: var(--z-topbar);
      display: grid;
      grid-template-columns: auto minmax(0, 1fr) auto;
      align-items: center;
      gap: 14px;
      padding: 14px 22px;
      border-bottom: 1px solid var(--line);
      background: rgba(12, 12, 18, .88);
      -webkit-backdrop-filter: blur(20px) saturate(160%);
      backdrop-filter: blur(20px) saturate(160%);
    }



    .brand {
      display: flex;
      align-items: center;
      gap: 12px;
      min-width: 215px;
    }

    .logo {
      width: 42px;
      height: 42px;
      display: grid;
      place-items: center;
      border-radius: var(--r-lg);
      background: var(--primary);
      box-shadow: 0 12px 36px rgb(var(--primary-rgb) / .35);
      color: var(--on-color);
      font-weight: 900;
      flex: 0 0 auto;
      overflow: hidden;
    }

    .logo img {
      width: 100%;
      height: 100%;
      object-fit: contain;
      display: block;
    }

    .logo span {
      display: none;
      line-height: 1;
    }

    .brand h1 {
      margin: 0;
      font-size: 16px;
      line-height: 1.1;
      letter-spacing: .2px;
    }

    .brand p {
      margin: 3px 0 0;
      color: var(--muted);
      font-size: 12px;
    }

    .nav {
      display: flex;
      align-items: center;
      justify-content: flex-start;
      gap: 4px;
      overflow-x: auto;
      scrollbar-width: none;
      padding: 2px 0;
    }

    .nav::-webkit-scrollbar {
      display: none;
    }

    .nav button,
    /* Иконка внутри кнопки НЕ сжимается. Кнопка — inline-flex, и SVG в ней обычный
       flex-элемент: при длинной подписи в узкой кнопке он ужимался до нуля и
       пропадал молча. Замер на 390px: у «Открыть каталог» иконка была 0×15px, у
       «Выбрать пакет» — 11×15 вместо 15×15. Тем же объяснялся пропавший «+» на
       кнопке «Своя позиция» в каталоге: элемент в разметке есть, на экране его нет. */
    .btn > svg { flex: 0 0 auto; }

    .btn {
      border: 1px solid var(--line);
      background: rgba(15, 23, 42, .45);
      color: var(--text);
      border-radius: var(--r-lg);
      /* 7px по вертикали — как у .btn.small: разницу между обычной и мелкой
         кнопкой держат кегль и горизонтальный отступ, а не рост. С 9px обычная
         кнопка выходила на 38–42px против 32–34 у всех остальных и читалась как
         «большая» (замечание владельца 04.09.2026). На телефоне ниже по файлу
         действует min-height:44px — цель касания не страдает. */
      padding: 7px 14px;
      min-height: 34px;
      transition: background var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out);
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 7px;
      white-space: nowrap;
      text-decoration: none;
      /* Кегль ЗАДАН, а не унаследован. Без этой строки высота кнопки зависела от
         того, куда её положили: в панели с font-size 13px выходило 34px, а там,
         где наследовались базовые 16px, — 42px. Замер по 14 разделам: 218 кнопок
         на 32px, 138 на 34px и хвост из 42/40/38/37 — вот эти «большие кнопки»
         владелец и видел. Разницы задумано не было, она набежала наследованием.

         13.5px — на полпункта крупнее .btn.small (13px): обычная кнопка должна
         читаться чуть весомее мелкой, но не выбиваться из ряда. */
      font-size: 13.5px;
    }

    .nav button {
      padding: 7px 10px;
      font-size: 12px;
      font-weight: 600;
      border-radius: var(--r-md);
    }

    .nav-icon-btn svg {
      opacity: .7;
      flex: 0 0 auto;
    }
    .nav-icon-btn:hover svg, .nav-icon-btn.active svg { opacity: 1; }

    [data-theme="light"] .nav button,
    [data-theme="light"] .btn {
      background: rgba(255, 255, 255, .78);
    }

    .nav button:hover,
    .btn:hover {
      transform: translateY(-1px);
      border-color: rgb(var(--primary-rgb) / .55);
      background: rgb(var(--primary-rgb) / .1);
    }

    .nav button.active {
      background: var(--primary);
      border-color: transparent;
      color: var(--on-color);
    }

    /* Deal bar gets extra top margin to breathe */
    .deal-bar { margin-top: 4px; }

    .top-actions {
      display: flex;
      align-items: center;
      gap: 8px;
      justify-content: flex-end;
      min-width: 175px;
    }

    .container {
      width: min(1520px, calc(100% - 32px));
      margin: 22px auto 42px;
    }

    .layout {
      display: grid;
      grid-template-columns: minmax(0, 1fr) 360px;
      gap: 18px;
      align-items: start;
    }
    /* Грид-элемент по умолчанию имеет min-width:auto и раздувается под самый широкий
       неразрывный кусок внутри — на телефоне колонка сметы становилась 397px при
       ширине сетки 374 и утаскивала страницу вбок. Тот же корень, что у обрезки
       текста через ellipsis: без min-width:0 грид не даёт содержимому сжаться. */
    .layout > * { min-width: 0; }

    /* Правая колонка 360px — под сводку сметы. Но .layout рисуют и без неё:
       вкладка «Финансы» в сделке кладёт внутрь одну только .panel, и колонка
       вместе с гэпом простаивала — замер на окне 1250px: сетка 932px, панель
       554px, справа 378px пустоты. Из десяти вкладок сделки и четырнадцати
       разделов это единственное такое место, но правило общее: одинокий ребёнок
       занимает обе колонки, и резерв под сводку появляется только когда сводка
       есть. Через :only-child, а не :has(), — в проекте :has() пока не водится. */
    .layout > :only-child { grid-column: 1 / -1; }

    .panel,
    .summary,
    .item,
    .package-card,
    .project-card,
    .client-card,
    .task-card,
    .finance-card,
    .team-card,
    .calendar-card,
    .crm-card,
    .proposal-preview {
      background: var(--panel);
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      box-shadow: var(--shadow);
    }

    .panel {
      padding: 18px;
      min-width: 0; /* без этого .panel как грид-ячейка раздувается под min-content контента (напр. .fin-summary-grid) и ломает мобильную раскладку */
    }

    .summary {
      position: sticky;
      top: 86px;
      padding: 18px;
    }

    .section-title {
      display: flex;
      justify-content: space-between;
      align-items: flex-start;
      gap: 16px;
      margin-bottom: 16px;
    }

    h1,
    h2,
    h3 {
      margin-top: 0;
      line-height: 1.15;
    }

    h1 {
      margin-bottom: 6px;
      font-size: 30px;
      letter-spacing: -.035em;
    }

    h2 {
      font-size: 22px;
      letter-spacing: -.025em;
    }

    h3 {
      margin-bottom: 8px;
      font-size: 17px;
      letter-spacing: -.015em;
    }

    p {
      color: var(--muted);
      line-height: 1.45;
    }

    .grid {
      display: grid;
      gap: 14px;
    }

    .grid.two {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .grid.three {
      grid-template-columns: repeat(3, minmax(0, 1fr));
    }
    /* Карточки сделок — адаптивная сетка: 4 в ряд на широком экране, дальше
       автоматически 3 / 2 / 1 по ширине (минимум карточки 320px). */
    .grid.three.deal-cards-grid {
      gap: 18px;
      grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    }

    .grid.four {
      grid-template-columns: repeat(4, minmax(0, 1fr));
    }

    .grid.five {
      grid-template-columns: repeat(5, minmax(0, 1fr));
    }

    .field {
      display: grid;
      gap: 6px;
    }

    /* Элемент сетки по умолчанию имеет min-width:auto и не сжимается ниже
       min-content своего содержимого. min-content у <select> — это его САМАЯ
       ДЛИННАЯ опция: в редакторе договора список сделок («Демо: рекламный ролик
       для бренда · Бренд «Вкус»») раздувал ячейку до 373px в окне 390px, и вбок
       ехала вся страница. Ширину задаёт колонка сетки, содержимое обязано в неё
       укладываться — поэтому min-width:0 ставим на САМ элемент сетки, а не на
       обёртки внутри него (то же правило, что и при обрезании текста
       многоточием). На дорожки minmax(...) это не влияет: их размер задаёт
       grid-template-columns, а не min-width детей. */
    .grid > *,
    .field > * {
      min-width: 0;
    }

    .field label {
      color: var(--muted);
      font-size: 12px;
      font-weight: 600;
    }

    input,
    select,
    textarea {
      width: 100%;
      border: 1px solid var(--line);
      border-radius: var(--r-sm);
      padding: 10px 12px;
      background: var(--input);
      color: var(--text);
      outline: none;
    }
    /* Поля — той же высоты, что обычная кнопка: в формах они стоят в один ряд,
       и разница в 2–3px читается как «съехало». textarea исключена намеренно —
       её высоту задаёт содержимое. */
    input:not([type="checkbox"]):not([type="radio"]),
    select {
      min-height: var(--h-md);
    }

    /* Чекбоксы и радио под правило выше не подходят: width:100% растягивал их на
       всю строку. В подписи КП на клиентском портале это ломало вёрстку — квадрат
       становился 266px и выталкивал текст согласия за пределы карточки, давая
       горизонтальную прокрутку на телефоне (442px при окне 390). Правило общее,
       поэтому чинит и все будущие чекбоксы, а не только этот. */
    input[type="checkbox"],
    input[type="radio"] {
      width: 18px;
      height: 18px;
      flex: 0 0 auto;
      padding: 0;
      border-radius: 0;
      background: none;
      border: none;
      accent-color: var(--primary);
      cursor: pointer;
    }
    input[type="radio"] { border-radius: 50%; }

    /* Кастомный select — убираем браузерную стрелку, ставим свой шеврон */
    select {
      -webkit-appearance: none;
      appearance: none;
      padding-right: 34px;
      cursor: pointer;
      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%237a7a8a' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
      background-repeat: no-repeat;
      background-position: right 10px center;
      background-size: 15px;
      transition: border-color var(--dur-1), box-shadow var(--dur-1);
    }
    [data-theme="light"] select {
      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%234a5568' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
    }
    select:hover { border-color: rgb(var(--primary-rgb) / .35); }

    /* Тема браузера для нативного dropdown popup */
    [data-theme="dark"] select { color-scheme: dark; }
    [data-theme="light"] select { color-scheme: light; }

    /* Явные цвета для option — работает в Firefox и Chromium */
    [data-theme="dark"] select option {
      background-color: #1e1e2e;
      color: #f0f0f0;
    }
    [data-theme="light"] select option {
      background-color: #ffffff;
      color: #1a2332;
    }

    /* Иконка календаря/часов в date/time inputs — следует теме браузера */
    [data-theme="dark"] input[type="date"],
    [data-theme="dark"] input[type="time"],
    [data-theme="dark"] input[type="datetime-local"],
    [data-theme="dark"] input[type="month"],
    [data-theme="dark"] input[type="week"] {
      color-scheme: dark;
    }
    [data-theme="light"] input[type="date"],
    [data-theme="light"] input[type="time"],
    [data-theme="light"] input[type="datetime-local"],
    [data-theme="light"] input[type="month"],
    [data-theme="light"] input[type="week"] {
      color-scheme: light;
    }

    textarea {
      min-height: 92px;
      resize: vertical;
    }

    input:focus,
    select:focus,
    textarea:focus {
      border-color: rgb(var(--primary-rgb) / .65);
      box-shadow: 0 0 0 4px rgb(var(--primary-rgb) / .14);
    }

    input.input-error {
      border-color: var(--text-danger);
      box-shadow: 0 0 0 3px rgba(220, 38, 38, .15);
    }

    .field-error-msg {
      font-size: 12px;
      color: var(--text-danger);
      margin-top: 2px;
    }

    .toolbar {
      display: flex;
      gap: 8px;
      flex-wrap: wrap;
      align-items: center;
    }

    /* Главная кнопка — градиентом и со свечением. Плоская заливка --primary
       выглядела «наклейкой» рядом со стеклянными панелями; переход светлее→темнее
       по диагонали даёт объём, а мягкая тень акцентного тона отрывает кнопку от
       фона. Цвет тот же: градиент собран из --primary2 → --primary, поэтому идёт
       за выбранной схемой. Контраст подписи не меняется — на светлом конце
       градиента лежит --primary2, тот же, что был у :hover. */
    .btn.primary {
      background-image: var(--grad-btn);
      background-color: var(--primary);
      border-color: transparent;
      color: var(--on-color);
      box-shadow: var(--glow-sm);
    }
    .btn.primary:hover {
      background-color: var(--primary2);
      box-shadow: var(--glow-md);
    }
    .btn.primary:active { box-shadow: var(--glow-sm); }

    .btn.blue {
      background: var(--blue);
      border-color: transparent;
      color: var(--on-color);
    }
    .btn.blue:hover { background: var(--blue-strong); }

    .btn.green {
      background: var(--green);
      border-color: transparent;
      color: var(--on-color);
    }
    .btn.green:hover { background: var(--green-strong); }
    /* .btn.yellow удалён 11.09.2026: ни разметка, ни сборка класса на лету его
       не используют (жёлтое в app.js — фон карточек статистики и точка маржи
       .health-dot), а цвет текста в нём был единственным #1a1a1a мимо токенов. */

    .btn.danger {
      background: var(--red);
      border-color: transparent;
      color: var(--on-color);
    }
    .btn.danger:hover { background: var(--red-strong); }

    /* Разрушительное действие рядом с главным: цвет предупреждает, но вес не
       спорит с призывом. Заливка остаётся у .danger — в окнах-подтверждениях
       удаление и есть главное действие. */
    .btn.danger-quiet {
      background: transparent;
      border-color: rgba(220, 38, 38, .35);
      color: var(--text-danger);
    }
    .btn.danger-quiet:hover { background: rgba(220, 38, 38, .1); border-color: rgba(220, 38, 38, .55); }

    .btn.small {
      padding: 7px 10px;
      font-size: 13px;
      min-height: var(--h-sm);
    }

    .btn.full {
      width: 100%;
    }

    /* Отключённое состояние — одно правило на всё приложение.
       До этого каждый случай оформлялся по-своему: в тарифах inline-стилем прямо
       в разметке, у пяти компонентов (сохранение в модалке, стрелки онбординга,
       отправка брифа) — собственные :disabled в CSS, а у остальных мест с
       атрибутом disabled не было ничего вовсе. Кнопка выглядела рабочей, человек
       жал и не получал отклика — худший вид молчания в интерфейсе.
       Курсор not-allowed и приглушение дают ответ до нажатия. */
    .btn:disabled,
    button:disabled,
    input:disabled,
    select:disabled,
    textarea:disabled {
      opacity: .5;
      cursor: not-allowed;
    }
    /* Наведение на отключённую кнопку не должно её «оживлять». */
    .btn:disabled:hover {
      transform: none;
      border-color: var(--line);
    }

    .tabs {
      display: flex;
      gap: 8px;
      flex-wrap: wrap;
      margin: 14px 0 16px;
    }

    /* Строка поиска — над лентой, вровень с карточками (02.10.2026). Раньше
       поле тянулось через весь каталог над колонкой разделов: 760px пустого
       поля на запрос в одно слово. Теперь поле компактное, выбор и вид — у
       правого края той же строки. */
    .catalog-toolbar {
      display: flex;
      align-items: center;
      gap: 10px;
      margin: 0 0 14px;
      flex-wrap: wrap;
    }
    .catalog-search-wrap {
      position: relative;
      flex: 1 1 240px;
      max-width: 360px;
      min-width: 200px;
      display: flex;
      align-items: center;
    }
    .catalog-toolbar-tools { display: flex; align-items: center; gap: 8px; margin-left: auto; }
    .catalog-toolbar .catalog-view-switch { margin: 0; }
    /* Подсказка «/» внутри поля — пока поле пустое и не в фокусе; на касании
       клавиатуры нет, и подсказка там не нужна. */
    .catalog-search-kbd {
      position: absolute; right: 10px;
      min-width: 20px; height: 20px; padding: 0 6px;
      border: 1px solid var(--line); border-radius: var(--r-sm);
      background: var(--panel2); color: var(--muted);
      font: inherit; font-size: 11px; font-weight: 700; line-height: 18px; text-align: center;
      pointer-events: none;
    }
    .catalog-search-wrap:focus-within .catalog-search-kbd,
    .catalog-search-input:not(:placeholder-shown) + .catalog-search-kbd { display: none; }
    @media (hover: none) { .catalog-search-kbd { display: none; } }

    /* «Все» блоками разделов: шапка блока прилипает, пока листаешь раздел. */
    .catalog-block + .catalog-block { margin-top: 22px; }
    .catalog-block-head {
      position: sticky; top: 62px; z-index: 2;
      display: flex; align-items: center; gap: 10px;
      padding: 10px 0; margin-bottom: 8px;
      background: var(--panel);
      /* Линия снизу: прилипшая белая шапка сливалась с белой карточкой, и
         срезанная её краем строка читалась как текст СКВОЗЬ шапку (замер 02.10). */
      box-shadow: 0 1px 0 var(--line);
    }
    .catalog-block-ico {
      flex: 0 0 30px; width: 30px; height: 30px;
      display: inline-flex; align-items: center; justify-content: center;
      border-radius: var(--r-md);
      background: color-mix(in srgb, currentColor 14%, transparent);
    }
    .catalog-block-head h3 { margin: 0; font-size: 16px; line-height: 1.25; }
    .catalog-block-count { font-size: 12px; font-weight: 700; color: var(--muted); font-variant-numeric: tabular-nums; }
    .catalog-block-open {
      margin-left: auto;
      display: inline-flex; align-items: center; gap: 4px;
      height: 30px; padding: 0 10px;
      border: 0; border-radius: var(--r-md);
      background: none; color: var(--muted);
      font: inherit; font-size: 12px; font-weight: 650;
      cursor: pointer;
      transition: background var(--dur-1), color var(--dur-1);
    }
    .catalog-block-open:hover { color: var(--primary-text); background: rgb(var(--primary-rgb) / .1); }
    .catalog-block-open:focus-visible { outline: var(--ring-w) solid var(--ring); outline-offset: 2px; }
    @media (hover: none) and (pointer: coarse) { .catalog-block-open { height: 44px; } }

    /* Широкий экран: строка поиска и колонка разделов прилипают под верхней
       полосой (62px). Колонка раньше прилипала на 16px — и уезжала верхом ПОД
       полосу. Шапка блока — под строкой поиска: 62 + 12 + 43 (поле) + 12. */
    @media (min-width: 769px) {
      .catalog-body-main > .catalog-toolbar {
        position: sticky; top: 62px; z-index: 3;
        padding: 12px 0; margin: -12px 0 2px;
        background: var(--panel);
        /* фон на 6px шире строки: иначе по краям выглядывали скруглённые углы
           и тени карточек, проезжающих под ней */
        box-shadow: 0 0 0 6px var(--panel);
      }
      .catalog-block-head { top: 128px; }
      .catalog-found-count.is-dup,
      .catalog-section-head.is-dup-head { display: none; }
    }
    .catalog-search-wrap svg {
      position: absolute;
      left: 12px;
      color: var(--muted);
      pointer-events: none;
    }
    .catalog-search-input {
      width: 100%;
      padding-left: 36px;
    }
    .catalog-toolbar-select {
      flex: 0 0 auto;
      width: auto;
      min-width: 130px;
    }
    .catalog-found-count {
      flex: 0 0 auto;
      font-size: 12px;
      font-weight: 700;
      color: var(--muted);
      background: var(--panel2);
      border: 1px solid var(--line);
      border-radius: var(--r-pill);
      padding: 7px 14px;
      white-space: nowrap;
    }

    .catalog-body {
      display: flex;
      align-items: flex-start;
      gap: 18px;
      margin-top: 16px;
    }
    .catalog-cat-sidebar {
      flex: 0 0 168px;
      display: flex;
      flex-direction: column;
      gap: 2px;
      position: sticky;
      top: 74px;   /* под верхней полосой (62px), вровень с прилипшей строкой поиска */
    }
    .catalog-cat-group {
      display: flex;
      flex-direction: column;
      gap: 2px;
    }
    .catalog-cat-divider {
      height: 1px;
      background: var(--line);
      margin: 10px 4px;
    }
    .catalog-cat-item {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 8px;
      width: 100%;
      padding: 8px 12px;
      border: none;
      border-radius: var(--r-sm);
      background: transparent;
      color: var(--muted);
      font-size: 13px;
      font-weight: 600;
      text-align: left;
      cursor: pointer;
      transition: background var(--dur-1), color var(--dur-1);
      white-space: nowrap;
    }
    .catalog-cat-item:hover { background: var(--panel2); color: var(--text); }
    .catalog-cat-item.active { background: rgb(var(--primary-rgb) / .14); color: var(--primary-on-tint); font-weight: 700; }
    .catalog-cat-count {
      flex: 0 0 auto;
      font-size: 12px;
      font-weight: 700;
      color: var(--muted);
      background: var(--panel2);
      border-radius: var(--r-pill);
      padding: 1px 7px;
    }
    .catalog-cat-item.active .catalog-cat-count { background: rgb(var(--primary-rgb) / .22); color: var(--primary-on-tint); }
    /* «Расходы» отделены линией, но окрашены как все разделы. Красная строка с
       красной подсветкой при выборе читалась как ошибка, а не как раздел; цвет
       раздела и так несёт значок. */
    .catalog-cat-item.danger {
      margin-top: 10px;
      padding-top: 10px;
      border-top: 1px solid var(--line);
      /* Верх квадратный: пункт отделён линией и визуально к ней прижат. Низ — как у
         базового .catalog-cat-item (--r-sm), раньше стояло 9px без причины. */
      border-radius: 0 0 var(--r-sm) var(--r-sm);
    }
    .catalog-cat-label { display: flex; align-items: center; gap: 6px; min-width: 0; }
    .catalog-cat-chev { flex: 0 0 12px; width: 12px; display: inline-flex; opacity: .55; }
    .catalog-cat-chev svg { transition: transform var(--dur-1); }
    .catalog-cat-item[data-open="1"] .catalog-cat-chev svg { transform: rotate(90deg); }
    .catalog-cat-ico { display: inline-flex; flex-shrink: 0; }
    .catalog-cat-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .catalog-cat-nums { display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto; }
    .catalog-cat-picked {
      display: inline-flex; align-items: center; gap: 2px;
      font-size: 11px; font-weight: 750; font-variant-numeric: tabular-nums;
      color: var(--primary-on-tint); background: rgb(var(--primary-rgb) / .16);
      border-radius: var(--r-pill); padding: 1px 6px 1px 4px;
    }
    .catalog-cat-item:not(.active) .catalog-cat-count { opacity: .7; }
    /* Раздел, который сейчас на экране в «Все» (_catalogSpy), — тоньше, чем
       выбранный: выбран «Все», а это лишь «вы здесь». */
    .catalog-cat-item.is-here:not(.active) { color: var(--text); background: rgb(var(--primary-rgb) / .07); box-shadow: inset 2px 0 0 var(--primary); }
    /* правило выше сильнее общего :hover — без своего наведения подсвеченный
       раздел переставал откликаться на мышь (сторож «кликабельное отзывается») */
    .catalog-cat-item.is-here:not(.active):hover { background: rgb(var(--primary-rgb) / .13); }
    /* При поиске: в разделе ничего не найдено */
    .catalog-cat-item .catalog-cat-count.is-zero { opacity: .35; }

    /* Шапка над карточками (app.js renderCatalogSectionHead). */
    .catalog-section-head {
      display: flex; align-items: center; gap: 12px;
      margin: 0 0 12px; min-width: 0;
    }
    .catalog-section-ico {
      flex: 0 0 34px; width: 34px; height: 34px;
      display: inline-flex; align-items: center; justify-content: center;
      border-radius: var(--r-md);
      background: color-mix(in srgb, currentColor 14%, transparent);
    }
    .catalog-section-text { flex: 1 1 auto; min-width: 0; }
    .catalog-section-text h2 {
      margin: 0; font-size: 16px; line-height: 1.25;
      display: flex; align-items: center; flex-wrap: wrap; gap: 2px 6px;
    }
    .catalog-section-crumb { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); font-weight: 600; }
    .catalog-section-crumb svg { opacity: .6; }
    .catalog-section-text p { margin: 2px 0 0; font-size: 12px; color: var(--muted); }
    .catalog-section-head .catalog-found-count { background: none; border: 0; padding: 0; }
    .catalog-cat-add {
      margin-top: 10px;
      padding-top: 10px;
      border-top: 1px solid var(--line);
      justify-content: flex-start;
      color: var(--primary-text);
    }
    .catalog-cat-add:hover { background: rgb(var(--primary-rgb) / .1); color: var(--primary-on-tint); }

    /* Настройка списка разделов — служебная строка внизу самого списка, как
       «Настроить меню» у главного бокового меню. Приглушённая намеренно: она не
       выбирает раздел и не должна спорить за внимание с теми, кто выбирает. */
    .catalog-cat-config {
      margin-top: 10px;
      padding-top: 10px;
      border-top: 1px solid var(--line);
      justify-content: flex-start;
      /* Одного var(--muted) НЕ хватает: им же покрашены и сами разделы (замер дал
         обоим rgb(74,85,104)), поэтому служебная строка сливалась с пунктами.
         Приглушаем сверх того — но умеренно: контраст к панели проверен замером
         и остаётся выше порога AA (см. тест в наборе responsive). */
      color: var(--muted);
      /* .85, а не «на глаз потише»: замер по эффективному цвету дал 5,11:1 против
         7,53:1 у разделов — тише, но в целевом коридоре 5–8:1. Пробовал .72 —
         3,74:1, ниже порога AA; .8 даёт 4,52:1, то есть впритык, и любая правка
         темы уронила бы под порог. */
      opacity: .85;
      font-weight: 500;
    }
    .catalog-cat-config:hover { background: var(--panel2); color: var(--text); opacity: 1; }
    .catalog-body-main { flex: 1; min-width: 0; }

    .catalog-grid {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
      gap: 12px;
      align-items: start;
    }

    /* Второй уровень каталога. На десктопе — вложенный столбик под своей группой. */
    .catalog-subgroups { margin: 2px 0 6px 10px; display: flex; flex-direction: column; gap: 2px; }
    .catalog-subgroup { font-size: 12px; min-height: 36px; opacity: .85; }

    /* ── Выбор места своей позиции: разделы плашками, под выбранным — подразделы.
       Цвет раздела приходит переменной --chip-c из CATALOG_GROUPS (те же токены,
       что у значков левой колонки), поэтому «Оборудование» узнаётся по синему и в
       колонке, и здесь. Подпись — как у полей формы (.field label), но не <label>:
       связывать её не с чем, это группа кнопок. */
    .place-caption { display: block; color: var(--muted); font-size: 12px; font-weight: 600; margin-bottom: 8px; }

    /* Свёрнутый выбор — строка размером с поле ввода: значок раздела в плашке
       его цвета, «Раздел › Подраздел», справа «Изменить». Раскрывается без
       перерисовки окна (togglePlacePicker). */
    .place-summary {
      display: flex; align-items: center; gap: 10px; width: 100%;
      min-height: 42px; padding: 6px 12px 6px 6px;
      border: 1px solid var(--line); border-radius: var(--r-md);
      background: var(--input); color: var(--text);
      font: inherit; font-size: 14px; font-weight: 600; text-align: left; cursor: pointer;
      transition: border-color var(--dur-1);
    }
    .place-summary:hover,
    .place-field.is-open .place-summary { border-color: color-mix(in srgb, var(--chip-c) 55%, transparent); }
    .place-summary-ico {
      flex: 0 0 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;
      border-radius: var(--r-sm); color: var(--chip-c);
      background: color-mix(in srgb, var(--chip-c) 16%, transparent);
    }
    .place-summary-text { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 6px; white-space: nowrap; }
    .place-summary-group { flex: 0 0 auto; }
    .place-summary-sub { min-width: 0; overflow: hidden; text-overflow: ellipsis; font-weight: 500; }
    .place-summary-sep { display: inline-flex; flex: 0 0 auto; color: var(--muted); }
    .place-summary-act { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 4px; font-size: 12px; color: var(--primary-text); }
    .place-summary-act svg { transform: rotate(90deg); transition: transform var(--dur-1); }
    .place-field.is-open .place-summary-act svg { transform: rotate(-90deg); }
    .place-field:not(.is-open) .when-open,
    .place-field.is-open .when-closed,
    .place-field:not(.is-open) .place-panel { display: none; }
    .place-panel { margin-top: 8px; }

    .place-groups { display: flex; flex-wrap: wrap; gap: 6px; }
    .place-chip {
      display: inline-flex; align-items: center; gap: 6px;
      min-height: 30px; padding: 4px 10px 4px 8px;
      border: 1px solid var(--line); border-radius: var(--r-pill);
      background: var(--panel2); color: var(--muted);
      font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer;
      transition: background var(--dur-1), border-color var(--dur-1), color var(--dur-1);
    }
    .place-chip svg { color: var(--chip-c); flex: 0 0 auto; }
    .place-chip:hover { color: var(--text); border-color: color-mix(in srgb, var(--chip-c) 55%, transparent); }
    .place-chip.active {
      color: var(--text);
      border-color: var(--chip-c);
      background: color-mix(in srgb, var(--chip-c) 16%, transparent);
    }
    /* Подразделы — вложенной подложкой в цвете выбранного раздела: читается как
       «внутри раздела», а не как второй независимый выбор. */
    .place-subs {
      display: flex; flex-wrap: wrap; gap: 4px;
      margin-top: 8px; padding: 6px;
      border-radius: var(--r-lg);
      background: color-mix(in srgb, var(--chip-c) 7%, transparent);
      border: 1px solid color-mix(in srgb, var(--chip-c) 22%, transparent);
    }
    .place-sub {
      min-height: 28px; padding: 3px 10px;
      border: 1px solid transparent; border-radius: var(--r-pill);
      background: transparent; color: var(--muted);
      font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer;
      transition: background var(--dur-1), color var(--dur-1);
    }
    .place-sub:hover { color: var(--text); background: color-mix(in srgb, var(--chip-c) 12%, transparent); }
    /* Выбранный подраздел — подкрашенный фон с рамкой, а не сплошная заливка с
       белым текстом: у «Подготовки» цвет раздела жёлтый, и белое на жёлтом не
       прочитать. Текст остаётся цветом текста темы, контраст не зависит от того,
       какой раздел выбран. */
    .place-sub.active {
      color: var(--text);
      background: color-mix(in srgb, var(--chip-c) 26%, transparent);
      border-color: var(--chip-c);
    }
    /* Палец: плашки и подразделы — не ниже 44px. */
    @media (hover: none) and (pointer: coarse) {
      .place-chip, .place-sub, .place-summary { min-height: 44px; }
    }

    /* Значок своего подраздела — маленькая звезда в цвете акцента, как у своих
       разделов в колонке: «это завели вы», а не встроенное. */
    .place-sub-own { display: inline-flex; color: var(--primary-text); margin-right: 4px; flex: 0 0 auto; }

    /* ── «Настроить разделы»: раздел раскрывается и показывает свои подразделы.
       Переключатели справа в одну колонку и у разделов, и у подразделов — их
       сравнивают глазами сверху вниз. */
    .cfg-group { border-bottom: 1px solid var(--line); }
    .cfg-row { display: flex; align-items: center; gap: 10px; min-height: 44px; }
    .cfg-expand {
      flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 10px;
      min-height: 44px; padding: 0; border: none; background: none;
      color: var(--text); font: inherit; text-align: left; cursor: pointer;
    }
    .cfg-expand:hover .cfg-label { color: var(--primary-text); }
    .cfg-chev { display: inline-block; width: 10px; font-size: 9px; opacity: .55; transition: transform var(--dur-1); }
    .cfg-chev.open { transform: rotate(90deg); }
    .cfg-label { flex: 1 1 auto; min-width: 0; font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .cfg-count { flex: 0 0 auto; font-size: 12px; }
    .cfg-subs { margin: 0 0 10px 20px; padding-left: 12px; border-left: 2px solid var(--line); }
    .cfg-sub-row .cfg-label { font-weight: 500; color: var(--muted); }
    .cfg-remove {
      flex: 0 0 auto; min-width: 44px; min-height: 44px;
      display: inline-flex; align-items: center; justify-content: center;
      background: none; border: none; color: var(--muted); cursor: pointer; border-radius: var(--r-sm);
    }
    .cfg-remove:hover { color: var(--text-danger); background: rgba(220,38,38,.08); }
    .cfg-add-sub { margin-top: 6px; }


    /* Строка раздела: сама кнопка раздела и плюс «своя позиция здесь» рядом.
       Плюс — отдельная кнопка, а не часть кнопки раздела: кнопка в кнопке
       недопустима. Специфичность у правила для кнопки внутри строки выше, чем у
       мобильного `.catalog-cat-item { width: 100% }`, — иначе на телефоне раздел
       занимал бы всю ширину и выталкивал плюс за край листа. */
    .catalog-cat-row { display: flex; align-items: center; gap: 2px; min-width: 0; }
    .catalog-cat-row > .catalog-cat-item { flex: 1 1 auto; width: auto; min-width: 0; }
    /* Плюс появляется, только когда курсор над строкой (решение владельца
       15.09.2026): двадцать плюсов в колонке спорили с самими разделами.
       Прячем прозрачностью, а не display: место в строке остаётся за плюсом, и
       счётчик не прыгает влево-вправо, пока курсор едет по списку. С клавиатуры
       плюс проявляется, как только фокус попадает в строку, — иначе на невидимую
       кнопку можно было бы попасть табом вслепую. Именно :focus-visible, а не
       :focus-within: после клика мышью кнопка раздела держит фокус, и плюс
       оставался бы висеть на строке, с которой курсор уже ушёл. */
    .catalog-row-add {
      flex: 0 0 auto;
      width: 26px; height: 26px; padding: 0;
      display: inline-flex; align-items: center; justify-content: center;
      border: none; border-radius: var(--r-sm);
      background: transparent; color: var(--muted);
      opacity: 0; cursor: pointer;
      transition: opacity var(--dur-1), background var(--dur-1), color var(--dur-1);
    }
    .catalog-cat-row:hover > .catalog-row-add,
    .catalog-cat-row:has(:focus-visible) > .catalog-row-add { opacity: 1; }
    /* Сам плюс в фокусе с клавиатуры — виден СРАЗУ, без плавного проявления: в
       миг, когда на него пришёл таб, он ещё прозрачный, и фокус на мгновение
       стоит на невидимом. Сторож обхода табом в a11y это и поймал. */
    .catalog-row-add:focus-visible { opacity: 1; transition: none; }
    .catalog-row-add:hover { opacity: 1; background: rgb(var(--primary-rgb) / .14); color: var(--primary-on-tint); }
    /* На пальце плюс сам 44×44, а не значок 26px с невидимой областью вокруг:
       строки в листе разделов и так 46px высотой, место есть, а честный размер
       кнопки не перекрывает соседнюю строку своим ореолом. И здесь плюс виден
       ВСЕГДА: наведения на телефоне нет, и спрятанный до наведения плюс там не
       появился бы никогда. */
    @media (hover: none) and (pointer: coarse) {
      .catalog-row-add { width: 44px; height: 44px; opacity: .8; }
    }
    /* У «Расходов» линия-разделитель и отступ сверху переезжают с кнопки на всю
       строку — иначе плюс центрировался бы по кнопке вместе с её отступом и
       стоял ниже названия. */
    .catalog-cat-row.danger { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line); }
    .catalog-cat-row.danger > .catalog-cat-item.danger { margin-top: 0; padding-top: 8px; border-top: 0; border-radius: var(--r-sm); }

    /* Кнопка выбора раздела и шапка листа — только на телефоне: на десктопе
       список и так стоит слева целиком, открывать и закрывать нечего. */
    .catalog-nav-trigger { display: none; }
    .catalog-nav-sheet-head { display: none; }
    .catalog-nav-backdrop { display: none; }

    @media (max-width: 768px) {
      .catalog-body { flex-direction: column; }

      /* РАНЬШЕ здесь боковое меню разворачивалось в горизонтальную ленту
         (flex-direction: row + overflow-x: auto). Замер на 390px: 1968px
         содержимого при окне 348px — 5,7 экрана, из 14 пунктов видно 4, и ни
         одного РАЗДЕЛА среди них. То есть свойство, ради которого меню и
         существует (видеть все разделы сразу), на телефоне пропадало целиком,
         а разделы приходилось искать свайпом вслепую.

         Теперь список открывается листом снизу и показан ВЕРТИКАЛЬНО — в той же
         форме, что и на десктопе. Разметка одна и та же, меняется только подача. */
      .catalog-nav-trigger {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 10px;
        width: 100%;
        min-height: 48px;
        padding: 8px 14px;
        margin-bottom: 10px;
        border: 1px solid var(--line);
        border-radius: var(--r-lg);
        background: var(--panel2);
        color: var(--text);
        cursor: pointer;
        text-align: left;
      }
      .catalog-nav-trigger-label { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
      .catalog-nav-trigger-label strong {
        font-size: 15px; font-weight: 700;
        overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
      }
      .catalog-nav-trigger-chevron { display: inline-flex; color: var(--muted); flex-shrink: 0; }

      /* Слой модальный, а не «чуть выше страницы»: нижняя панель навигации
         прибита на z-index 110 и перекрывала последний раздел списка
         («Расходы» уходил под неё — замер этого не видел, поймал скриншот).
         Лист — обычная модалка, поэтому живёт на общем --z-modal и накрывает
         панель целиком, как остальные модалки приложения. */
      .catalog-nav-backdrop {
        display: block;
        position: fixed; inset: 0;
        z-index: calc(var(--z-modal) - 1);
        background: rgba(0, 0, 0, .45);
      }
      .catalog-cat-sidebar.is-open .catalog-nav-sheet-head { display: flex; }

      /* Сам список: скрыт, пока лист не открыт. */
      .catalog-cat-sidebar { display: none; }

      .catalog-cat-sidebar.is-open {
        display: flex;
        flex-direction: column;      /* как на десктопе — вертикальный список */
        position: fixed;
        z-index: var(--z-modal);
        /* top: auto ОБЯЗАТЕЛЕН: в базовом правиле у меню position:sticky с top:16px,
           и при fixed это значение продолжает действовать — лист прилипал к верху
           экрана вместо низа (видно на скриншоте до правки). */
        left: 0; right: 0; bottom: 0; top: auto;
        width: auto;
        /* 88vh, а не 82: с добавлением служебной строки списку нужно 739px, и на
           82vh (692px) последний пункт не влезал. Лист всё равно прокручивается,
           но на обычном телефоне теперь помещается целиком. */
        max-height: 88vh;
        overflow-y: auto;
        overflow-x: hidden;
        padding: 0 14px calc(14px + env(safe-area-inset-bottom, 0px));
        /* Непрозрачная подложка под --panel — как у .modal-box: сквозь лист
           тенью читался каталог за ним. */
        background-color: var(--bg2);
        background-image: linear-gradient(var(--panel), var(--panel));
        border-top: 1px solid var(--line);
        border-radius: var(--r-2xl) var(--r-2xl) 0 0;
        box-shadow: 0 -12px 40px rgba(0, 0, 0, .28);
        /* Маску краёв (она была подсказкой «тут скроллится вбок») убираем:
           горизонтальной прокрутки больше нет, а вертикальную она бы обрезала. */
        -webkit-mask-image: none;
        mask-image: none;
      }

      /* Шапка листа липнет к верху — при длинном списке кнопка «закрыть»
         не должна уезжать за пределы видимости. */
      .catalog-nav-sheet-head {
        position: sticky; top: 0; z-index: 1;
        display: flex; align-items: center; justify-content: space-between;
        gap: 10px;
        margin: 0 -14px 6px;
        padding: 14px 14px 10px;
        background: var(--panel);
        border-bottom: 1px solid var(--line);
        font-weight: 700;
      }
      /* Полоска-«ручка» листа, как в системных шторках. */
      .catalog-nav-sheet-head::before {
        content: ""; position: absolute; top: 6px; left: 50%;
        width: 36px; height: 4px; margin-left: -18px;
        border-radius: var(--r-pill); background: var(--line);
      }
      /* Всё, что ниже, раньше выпрямляло список в ленту (flex-direction: row,
         подгруппы через display:contents, разделитель на бок). Ленты больше нет —
         внутри листа список идёт столбиком, как на десктопе. */
      .catalog-cat-group { flex-direction: column; }
      .catalog-cat-item { width: 100%; min-height: 46px; }
      .catalog-cat-divider { width: auto; height: 1px; margin: 8px 0; }
      .catalog-toolbar-select { min-width: 0; flex: 1 1 45%; }

      /* Подгруппы — вложенный столбик со сдвигом, как на десктопе: в вертикальном
         списке вложенность читается отступом и не мешает никому. */
      .catalog-subgroups { display: flex; flex-direction: column; gap: 2px; margin: 2px 0 6px 14px; }
      .catalog-subgroup { font-size: 13px; min-height: 42px; opacity: .9; }
    }

    .tab {
      border: 1px solid var(--line);
      background: var(--panel2);
      color: var(--text);
      border-radius: var(--r-pill);
      padding: 9px 12px;
    }

    .tab.active {
      background: var(--primary);
      color: var(--on-color);
      border-color: transparent;
    }
    /* Отклик на наведение у неактивной вкладки. Его не было вовсе — вкладки
       «Базы знаний» и мастера сделки выглядели надписями, а не кнопками
       (обход 11.09.2026: «кликается, но не отзывается»). */
    .tab { transition: background var(--dur-1), border-color var(--dur-1); }
    .tab:not(.active):hover { border-color: rgb(var(--primary-rgb) / .45); background: var(--primary-bg); }

    /* Фильтры типов в календаре — раньше целиком инлайном, и инлайн перебивал
       любое :hover. Вид прежний, плюс отклик. */
    .cal-type-chip {
      padding: 4px 10px; font-size: 12px; font-weight: 750;
      border-radius: var(--r-pill); border: 1px solid var(--line);
      background: transparent; color: var(--muted); cursor: pointer;
      transition: border-color var(--dur-1), color var(--dur-1), background var(--dur-1);
    }
    .cal-type-chip:not(.active):hover { border-color: rgb(var(--primary-rgb) / .45); color: var(--text); }
    .cal-type-chip.active {
      border-color: var(--primary);
      background: rgb(var(--primary-rgb) / .15);
      color: var(--primary-on-tint);
    }

    /* Строка таблицы, которая открывает запись по нажатию (операции в финансах):
       подсветка строки, как у строк клиентов в дашборде. */
    tr.u-pointer td { transition: background var(--dur-1); }
    tr.u-pointer:hover td { background: var(--primary-bg); }

    .list {
      display: grid;
      gap: 12px;
    }

    .item,
    .package-card,
    .project-card,
    .client-card,
    .task-card,
    .finance-card,
    .team-card,
    .calendar-card,
    .crm-card {
      padding: 16px;
      position: relative;
    }
    /* Выбор этапа — во всю ширину карточки. Нативный select — inline-block, и
       обёртка кастомной выпадашки брала ширину по содержимому: подпись «КП
       отправлено» давала 145px там, где всё остальное в карточке 176px, и
       правый край получался рваным. */
    .crm-card > .task-mini-select,
    .crm-card > .uu-select-wrap { display: block; width: 100%; }

    /* Календарь сделки на телефоне — ПЛОТНЫЙ СПИСОК, а не столбик карточек.
       Общее мобильное правило разворачивает .line-head в колонку, и каждое
       событие занимало 143–161px: название, под ним тип, под ним дата — три
       этажа ради двух слов и числа. Владелец так и сказал: «показать удобным
       списком, а не большими окнами».
       Для календаря колонку отменяем: слева название и подпись, справа тип и
       дата — всё в одну строку. Тексты сжимаются многоточием, поэтому длинное
       название строку не ломает. */
    @media (max-width: 640px) {
      /* min-width:0 на САМОЙ карточке и на строке обязателен: элемент сетки не
         сжимается ниже min-content, и карточка растягивалась по длинному
         названию — замер дал 437px при окне 390px, из-за чего мобильный браузер
         раздувал область просмотра (window.innerWidth показывал те же 437).
         Та же грабля, что уже записана про многоточие в ячейках сетки. */
      .calendar-card { padding: 11px 13px; min-width: 0; }
      .calendar-card .line-head {
        flex-direction: row;
        align-items: center;
        gap: 10px;
        min-width: 0;
      }
      .calendar-card .line-head > div:first-child { min-width: 0; flex: 1; }
      .calendar-card .line-head h3 {
        font-size: 14px; margin: 0 0 1px;
        overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
      }
      .calendar-card .line-head p {
        font-size: 12px; margin: 0;
        overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
      }
      .calendar-card .line-head > div:last-child { flex: 0 0 auto; align-items: flex-end; }
    }

    /* Текст только для экранного диктора (статус «Активный» на карточке
       клиента виден точкой, а озвучен словом). */
    .sr-only {
      position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
      overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
    }

    /* ── Карточка клиента (app.js renderClients, плитка) — компактная:
       кружок с инициалами, имя и компания, внизу сделки, деньги, контакты. */
    .grid.clients-grid { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 10px; }
    .clients-grid .client-card { padding: 12px 14px; display: flex; flex-direction: column; gap: 10px; min-width: 0; }
    /* Строкой и на телефоне: общее мобильное правило ставит .line-head в
       столбец, и кружок уезжал над именем (карточка 150–210px). */
    .clients-grid .client-card .client-card-head { display: flex; flex-direction: row; align-items: center; gap: 10px; min-width: 0; }
    .client-avatar {
      position: relative; flex: 0 0 38px; width: 38px; height: 38px;
      display: grid; place-items: center; border-radius: var(--r-pill);
      font-size: 13px; font-weight: 800; letter-spacing: .02em;
      color: var(--av); background: color-mix(in srgb, var(--av) 16%, transparent);
    }
    .client-avatar-dot {
      position: absolute; right: -1px; bottom: -1px; width: 11px; height: 11px;
      border-radius: var(--r-pill); background: var(--green); border: 2px solid var(--panel);
    }
    .client-card-id { flex: 1 1 auto; min-width: 0; }
    .client-card-id h3 { margin: 0; font-size: 14px; line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .client-card-id p { margin: 2px 0 0; font-size: 12px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .client-card-status { flex: 0 0 auto; font-size: 11px; padding: 2px 8px; }
    /* Строка ценности клиента на карточке: сделки · оплачено · долг · контакты */
    .client-card-stats {
      display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px;
      font-size: 12px; font-weight: 700;
      color: var(--muted); font-variant-numeric: tabular-nums;
    }
    .client-deals-link {
      display: inline-flex; align-items: center; gap: 2px;
      padding: 3px 8px; margin-left: -8px; border: 0; border-radius: var(--r-sm);
      background: transparent; color: var(--text); font: inherit; font-weight: 700; cursor: pointer;
      transition: background var(--dur-1), color var(--dur-1);
    }
    .client-deals-link:hover, .client-deals-link:focus-visible { background: rgb(var(--primary-rgb) / .12); color: var(--primary-text); }
    .client-deals-link svg { opacity: .6; }
    .client-card-none { font-weight: 600; }
    .client-card-paid { color: var(--text-success); }
    .client-card-debt { color: var(--text-warning); }
    /* В шапке карточки, справа: шапку по высоте задаёт кружок (38px), поэтому
       значок у всех карточек стоит в одной точке. */
    .client-card-contacts { flex: 0 0 auto; display: inline-flex; gap: 4px; }
    .client-contact {
      width: 28px; height: 28px; display: grid; place-items: center;
      border-radius: var(--r-pill); border: 1px solid var(--line); color: var(--muted);
      transition: color var(--dur-1), border-color var(--dur-1), background var(--dur-1);
    }
    .client-contact:hover, .client-contact:focus-visible { color: var(--primary-text); border-color: rgb(var(--primary-rgb) / .5); background: rgb(var(--primary-rgb) / .1); }
    /* Палец: значки и «N сделок» — с невидимой областью касания 44px, сама
       карточка от этого не растёт. */
    @media (hover: none) and (pointer: coarse) {
      .client-contact, .client-deals-link { position: relative; }
      .client-contact::after { content: ""; position: absolute; inset: -8px; }
      .client-deals-link::after { content: ""; position: absolute; inset: -11px -4px; }
    }
    .client-card-note { margin: 0; font-size: 12px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    /* Доля оплаты — только у должника (app.js): тонкая дорожка, фон-подсказка. */
    .client-pay-track {
      display: block; height: 3px; margin-top: -4px;
      border-radius: var(--r-pill); background: var(--line); overflow: hidden;
    }
    .client-pay-fill { display: block; height: 100%; border-radius: var(--r-pill); background: var(--primary); }

    .item.selected {
      border-color: rgba(22, 163, 74, .55);
    }

    .item.optional {
      border-style: dashed;
      border-color: rgba(202, 138, 4, .75);
    }

    .item.hidden-item {
      opacity: .72;
      border-style: dashed;
    }

    .item {
      transition: opacity var(--dur-2) var(--ease-out), transform var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out);
    }

    .item.dragging {
      opacity: .35;
      transform: scale(.97);
    }

    .item.drag-over {
      position: relative;
    }

    .item.drag-over::before {
      content: "";
      position: absolute;
      left: 12px; right: 12px; top: -7px;
      height: 3px;
      border-radius: var(--r-pill);
      background: var(--primary);
      box-shadow: 0 0 10px rgb(var(--primary-rgb) / .65);
      animation: dragIndicatorPulse 1s ease-in-out infinite;
    }

    @keyframes dragIndicatorPulse {
      0%, 100% { opacity: .7; }
      50% { opacity: 1; }
    }

    @media (prefers-reduced-motion: reduce) {
      .item.drag-over::before { animation: none; }
    }

    .item-top,
    .line-head {
      display: flex;
      justify-content: space-between;
      gap: 18px;
      align-items: flex-start;
    }

    .price {
      font-size: 23px;
      font-weight: 700;
      font-family: var(--font-display);
      font-variant-numeric: tabular-nums;
      letter-spacing: -0.02em;
      white-space: nowrap;
    }

    .price-editor {
      width: 190px;
      display: grid;
      gap: 8px;
      text-align: right;
    }

    .price-editor input {
      text-align: right;
    }

    .badges {
      display: flex;
      gap: 7px;
      flex-wrap: wrap;
      margin-top: 10px;
    }

    .badge,
    .status-pill {
      display: inline-flex;
      align-items: center;
      gap: 5px;
      border: 1px solid var(--line);
      border-radius: var(--r-pill);
      padding: 5px 8px;
      color: var(--muted);
      font-size: 12px;
      background: rgba(15, 23, 42, .35);
      opacity: .92;
    }

    /* Тот же тёмно-синий тинт на белом фоне даёт плотную серую плашку, и --muted
       поверх неё падает до 3.2:1. В светлой теме капсуле нужна своя, лёгкая
       заливка — замер держит тест «бейджи и капсулы читаемы в обеих темах». */
    [data-theme="light"] .badge {
      background: rgba(15, 23, 42, .06);
      opacity: 1;
    }

    .status-pill {
      color: var(--text);
      font-weight: 700;
      background: rgb(var(--primary-rgb) / .16);
    }

    /* Цвета через --tint-*: на полупрозрачном фоне капсулы читаемость зависит от темы,
       см. тест «цветные капсулы читаемы в обеих темах». */
    .status-pill.green {
      color: var(--tint-green);
    }

    .status-pill.yellow {
      color: var(--tint-amber);
    }

    /* Статусы клиента. Раньше все пять рисовались одной капсулой, и колонка
       статуса ничего не сообщала — приходилось читать каждое слово. Цвета
       берём из семейства --tint-*: они определены для обеих тем, а фон капсулы
       полупрозрачный, и на нём читаемость от темы зависит (см. тест «цветные
       капсулы читаемы в обеих темах»). */
    .status-pill.info {
      color: var(--tint-blue);
      background: var(--tint-blue-bg);
    }
    .status-pill.green { background: var(--tint-green-bg); }
    .status-pill.yellow { background: var(--tint-amber-bg); }
    /* VIP — акцентный тон продукта: это «наш главный клиент», и он же цвет
       главного действия. Фон уже акцентный у базовой капсулы, меняем только
       текст. */
    .status-pill.accent {
      color: var(--primary-on-tint);
    }

    /* Архив — нейтральный серый: не активный статус, но и не ошибка.
       Был var(--muted) — на сером тинте в тёмной теме давал 4,09:1. */
    .status-pill.archived {
      color: var(--tint-slate);
      background: rgba(138, 138, 158, .16);
    }
    /* Бейдж «В архиве · вернуть» на карточке */
    .deal-archived-badge {
      color: var(--muted);
      background: rgba(138, 138, 158, .12);
      border: 1px solid var(--line);
      cursor: pointer;
    }
    /* Чип «Архив» в воронке */
    .funnel-stage-archived h3 { color: var(--muted); }
    .funnel-stage-archived.active { border-color: rgba(138, 138, 158, .5); }

    /* Переопределения для светлой темы больше не нужны — их роль взяли --tint-*
       (прежние значения давали 3,94:1 и 3,87:1, то есть ниже AA). */

    .summary-line {
      display: flex;
      justify-content: space-between;
      gap: 12px;
      padding: 10px 0;
      border-bottom: 1px solid var(--line);
    }
    /* Скидка клиенту — кнопкой в ряду кнопок сметы и окошком под ней
       (app.js renderEstimateDiscountButton); в «Итогах» — только строка с суммой. */
    .estimate-discount { position: relative; display: inline-flex; }
    .estimate-discount-btn.is-set {
      color: var(--text-success); border-color: color-mix(in srgb, var(--green) 45%, var(--line));
      background: color-mix(in srgb, var(--green) 10%, transparent);
    }
    .estimate-discount-pop {
      position: absolute; top: calc(100% + 6px); left: 0; z-index: var(--z-dropdown);
      width: 232px; padding: 12px; display: flex; flex-direction: column; gap: 8px;
      background: var(--bg2); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--elev-3);
    }
    .estimate-discount-row { display: flex; align-items: center; gap: 6px; }
    .estimate-discount-hint { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
    .estimate-discount-clear {
      align-self: flex-start; display: inline-flex; align-items: center; gap: 4px;
      padding: 4px 6px; margin-left: -6px; border: 0; border-radius: var(--r-sm); background: transparent;
      color: var(--muted); font: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
    }
    .estimate-discount-clear:hover, .estimate-discount-clear:focus-visible { color: var(--text-danger); background: color-mix(in srgb, var(--red) 8%, transparent); }
    .summary-discount-input {
      flex: 1 1 auto; min-width: 0; height: 34px; padding: 4px 10px; text-align: right;
      font: inherit; font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums;
      border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--input); color: var(--text);
    }
    .summary-discount-input:focus { outline: var(--ring-w) solid var(--ring); outline-offset: 1px; }
    .summary-discount-unit { display: inline-flex; flex: 0 0 auto; border: 1px solid var(--line); border-radius: var(--r-sm); overflow: hidden; }
    .summary-discount-unit button {
      width: 32px; height: 32px; border: 0; background: transparent; color: var(--muted);
      font: inherit; font-size: 13px; font-weight: 800; cursor: pointer;
    }
    .summary-discount-unit button:hover { color: var(--text); }
    .summary-discount-unit button.active { background: rgb(var(--primary-rgb) / .18); color: var(--primary-on-tint); }
    .summary-discount-sum { color: var(--text-success); font-variant-numeric: tabular-nums; white-space: nowrap; }
    /* Пустая смета — одна строка вместо столбика нулей (app.js renderSummary). */
    .summary-empty-note { margin: 14px 0 0; padding-top: 14px; border-top: 1px solid var(--line); font-size: 13px; color: var(--muted); line-height: 1.45; }
    /* «Очистить смету» — тихая ссылка под кнопками: красной становится при наведении. */
    .summary-clear-link {
      display: flex; align-items: center; justify-content: center; gap: 6px; width: 100%;
      margin-top: 10px; padding: 6px; border: 0; border-radius: var(--r-sm); background: transparent;
      color: var(--muted); font: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
      transition: color var(--dur-1), background var(--dur-1);
    }
    .summary-clear-link:hover, .summary-clear-link:focus-visible { color: var(--text-danger); background: color-mix(in srgb, var(--red) 8%, transparent); }
    @media (hover: none) and (pointer: coarse) {
      .summary-discount-unit button, .estimate-discount-clear { position: relative; }
      .summary-clear-link { min-height: 44px; }
      .summary-discount-unit button::after, .estimate-discount-clear::after {
        content: ""; position: absolute; inset: -10px -4px;
      }
    }
    /* Суммы в «Итогах сметы» — тем же строем, что и в плитках: правый край
       столбца не должен ездить от того, есть ли в числе единица. */
    .summary-line strong { font-variant-numeric: tabular-nums; }

    .summary-line strong {
      text-align: right;
    }

    .summary-total {
      margin-top: 16px;
      padding: 16px 18px;
      border-radius: var(--r-xl);
      background: rgb(var(--primary-rgb) / .1);
      border: 1px solid rgb(var(--primary-rgb) / .2);
      display: grid;
      gap: 3px;
    }

    .summary-total strong {
      font-size: 28px;
      font-family: var(--font-display);
      font-variant-numeric: tabular-nums;
      letter-spacing: -.03em;
    }

    .empty {
      padding: 40px 24px;
      border: 1px dashed var(--line);
      border-radius: var(--r-xl);
      color: var(--muted);
      text-align: center;
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 8px;
      background: var(--empty-bg);
    }
    .empty strong { color: var(--text); font-size: 15px; font-weight: 700; display: block; }
    .empty p { margin: 0; font-size: 13px; line-height: 1.5; max-width: 46ch; }

    .empty__icon {
      display: flex;
      align-items: center;
      justify-content: center;
      width: 44px;
      height: 44px;
      margin-bottom: 4px;
      border-radius: 50%;
      background: var(--primary-bg);
      color: var(--primary-on-tint);
    }
    .empty__actions {
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      gap: 8px;
      margin-top: 8px;
    }

    /* Компактный вариант — внутри колонок канбана, списков и боковых блоков */
    .empty--sm { padding: 16px 12px; gap: 6px; }
    .empty--sm .empty__icon { width: 30px; height: 30px; margin-bottom: 0; }
    .empty--sm .empty__icon svg { width: 15px; height: 15px; }
    .empty--sm strong { font-size: 13px; }
    .empty--sm p { font-size: 12px; }
    .empty--sm .empty__actions { margin-top: 4px; }

    /* Кнопки пустого состояния на телефоне: во всю ширину блока и ровными рядами.
       Раньше они жались по центру и переносились «хвостом» — на экране пустой
       сметы третья кнопка («Изменить бюджет») уезжала на свою строку и висела
       посередине. Та же логика, что у панелей разделов: flex-basis 0 заставляет
       ряд заполняться целиком, min-width держит подписи от переноса по буквам. */
    @media (max-width: 640px) {
      .empty__actions {
        width: 100%;
        gap: 8px;
      }
      .empty__actions .btn {
        flex: 1 1 0;
        min-width: 140px;
      }
    }

    .calc-box {
      padding: 14px;
      margin-top: 14px;
      background: var(--panel2);
      box-shadow: none;
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
    }

    .mini-note {
      font-size: 12px;
      color: var(--muted);
    }

    .line-name-input {
      font-size: 16px;
      font-weight: 700;
      background: transparent;
      border: 1px solid transparent;
      padding: 2px 6px;
      border-radius: var(--r-sm);
      width: 100%;
      display: block;
      color: var(--text);
      letter-spacing: -.01em;
    }
    .line-name-input { text-overflow: ellipsis; }
    .line-name-input::placeholder { color: var(--muted); opacity: 1; }
    .line-name-input:hover { border-color: var(--line); }
    .line-name-input:focus {
      background: var(--input);
      border-color: rgb(var(--primary-rgb) / .65);
      box-shadow: 0 0 0 4px rgb(var(--primary-rgb) / .14);
    }

    .line-desc-input {
      min-height: 36px;
      font-size: 13px;
      color: var(--muted);
      background: transparent;
      resize: none;
      border: 1px solid transparent;
      padding: 2px 6px;
      border-radius: var(--r-sm);
      width: 100%;
      display: block;
      margin-top: 2px;
      line-height: 1.45;
    }

    .line-desc-input:hover {
      border-color: var(--line);
    }

    .line-desc-input:focus {
      background: var(--input);
      border-color: rgb(var(--primary-rgb) / .65);
      color: var(--text);
      box-shadow: 0 0 0 4px rgb(var(--primary-rgb) / .14);
    }

    .home-clients-grid {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
      gap: 10px;
      margin-top: 10px;
    }

    .home-client-card {
      padding: 12px 14px;
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      background: var(--panel2);
      cursor: pointer;
      transition: border-color var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out);
    }

    .home-client-card:hover {
      border-color: rgb(var(--primary-rgb) / .55);
      transform: translateY(-1px);
    }

    .home-client-card h3 {
      margin: 0 0 3px;
      font-size: 14px;
    }

    .home-client-card p {
      margin: 0;
      font-size: 12px;
    }

    /* ── РУЧКА ПЕРЕТАСКИВАНИЯ — ОДНА НА ВСЁ ПРИЛОЖЕНИЕ ──────────────────────
       Было четыре разных вида одного жеста: здесь — пустой квадрат 26×26 с
       рамкой и БЕЗ содержимого (читался как невыбранный чекбокс), в настройке
       меню — текстовый глиф ⠿ (рисуется шрифтом ОС, у каждого свой), в боковом
       списке сделок — свой инлайновый SVG, а карточки канбана таскались без
       ручки вовсе. Теперь разметка одна (dragHandleHtml), рисунок один
       (ICON_PATHS.drag), а место задаёт модификатор.

       Рамки нет намеренно: коробка вокруг точек и превращала ручку в подобие
       чекбокса. Опознаётся она точками и курсором-ладонью. */
    .drag-handle {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 22px;
      height: 26px;
      flex: 0 0 auto;
      color: var(--muted);
      cursor: grab;
      user-select: none;
      -webkit-user-select: none;
      /* Иначе браузер на телефоне забирает вертикальное движение себе под
         прокрутку, и перетаскивание не начинается. */
      touch-action: none;
      border-radius: var(--r-sm);
      transition: color var(--dur-1) var(--ease-out), background var(--dur-1) var(--ease-out);
    }
    .drag-handle svg { display: block; }
    .drag-handle:hover { color: var(--text); background: rgb(var(--primary-rgb) / .10); }
    .drag-handle:active { cursor: grabbing; background: rgb(var(--primary-rgb) / .18); }

    /* Ручка в углу карточки: не встаёт в поток, поэтому ширина названия и суммы
       не зависит от того, наведён курсор или нет. */
    .drag-handle--corner {
      position: absolute;
      top: 4px; right: 4px;
    }

    /* Карточки воронки и задач таскаются целиком (HTML5 drag), ручки у них не было
       вовсе — то, что карточку можно перенести, узнавалось случайно. Показываем ту
       же ручку по наведению как ПОДСКАЗКУ: тянуть по-прежнему можно за всю
       карточку, поэтому ручка не перехватывает указатель. */
    .crm-card, .task-card { position: relative; }
    .crm-card .drag-handle--corner,
    .task-card .drag-handle--corner {
      opacity: 0;
      pointer-events: none;
      transition: opacity var(--dur-1);
    }
    .crm-card:hover .drag-handle--corner,
    .task-card:hover .drag-handle--corner { opacity: .5; }
    /* У карточки задачи в правом верхнем углу уже стоит крестик «Удалить», и
       ручка-подсказка садилась ровно на него: два значка вплотную читались как
       две кнопки, и в маленькую цель легко промахнуться. Ручке — низ карточки,
       там пусто; крестик наверху остаётся один. */
    .task-card .drag-handle--corner { top: auto; bottom: 6px; right: 8px; }
    /* Наведения на телефоне нет — там подсказка видна всегда, приглушённо. */
    @media (hover: none) {
      .crm-card .drag-handle--corner,
      .task-card .drag-handle--corner { opacity: .32; }
    }

    .proposal-preview {
      margin-top: 16px;
      padding: 24px;
      color: #111827;
      background: #fff;
      overflow-x: auto;
    }

    .proposal-preview p {
      color: #374151;
    }
    /* Итог документа — главное число для заказчика, а набран был как обычная
       строка таблицы: на экране правило было только в печатной версии. */
    .proposal-preview .proposal-total td {
      font-size: 17px; font-weight: 800; color: #000;
      padding-top: 12px; border-top: 2px solid #d1d5db;
    }
    .proposal-preview .proposal-total td:last-child { white-space: nowrap; font-variant-numeric: tabular-nums; }
    /* «К оплате сейчас» — следующий шаг клиента, поэтому подсвечен. */
    .proposal-preview .proposal-advance td {
      font-size: 14px; font-weight: 700; color: #111827; background: #f3f4f6;
    }
    .proposal-preview .proposal-advance td:last-child { white-space: nowrap; font-variant-numeric: tabular-nums; }
    /* Этап помечен своим цветом — как в смете и каталоге. */
    .proposal-preview h2.proposal-stage { border-left: 3px solid #6b7280; padding-left: 10px; }

    .proposal-preview table,
    #printArea table {
      width: 100%;
      border-collapse: collapse;
      margin: 12px 0 20px;
    }

    .proposal-preview th,
    .proposal-preview td,
    #printArea th,
    #printArea td {
      border-bottom: 1px solid #e5e7eb;
      padding: 10px;
      text-align: left;
      vertical-align: top;
    }

    .toast {
      position: fixed;
      right: 18px;
      bottom: 18px;
      z-index: var(--z-toast);
      padding: 12px 16px;
      border: 1px solid var(--line);
      border-radius: var(--r-lg);
      background: var(--panel);
      color: var(--text);
      box-shadow: var(--elev-3);
      font-size: 13px;
      font-weight: 600;
      max-width: calc(100vw - 36px);
      opacity: 0;
      transform: translateY(8px) scale(.97);
      pointer-events: none;
      transition: opacity var(--dur-2) var(--ease-out), transform var(--dur-2) var(--ease-out);
    }

    .toast.show {
      opacity: 1;
      transform: translateY(0) scale(1);
    }

    @media (max-width: 768px) {
      .toast {
        right: 12px;
        left: 12px;
        bottom: calc(72px + env(safe-area-inset-bottom, 12px));
        text-align: center;
      }
    }

    mark {
      background: rgba(250, 204, 21, .55);
      color: inherit;
      padding: 0 2px;
      border-radius: var(--r-xs);
    }

    .hidden-bar {
      padding: 14px;
      border-radius: var(--r-xl);
      background: rgba(202, 138, 4, .13);
      border: 1px solid rgba(202, 138, 4, .38);
      margin-bottom: 14px;
    }

    .catalog-section-divider {
      display: flex;
      align-items: center;
      gap: 10px;
      margin: 20px 0 12px;
      font-size: 12px;
      font-weight: 750;
      color: var(--muted);
      text-transform: uppercase;
      letter-spacing: .04em;
    }

    .catalog-section-divider::after {
      content: "";
      flex: 1;
      height: 1px;
      background: var(--line);
    }

    .pill-count {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      min-width: 22px;
      height: 22px;
      padding: 0 7px;
      border-radius: var(--r-pill);
      background: rgb(var(--primary-rgb) / .22);
      color: var(--text);
      font-size: 12px;
      font-weight: 700;
    }

    .metric-grid {
      display: grid;
      grid-template-columns: repeat(3, minmax(0, 1fr));
      gap: 8px;
      margin-top: 12px;
    }

    .metric {
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      padding: 10px;
      background: rgba(255, 255, 255, .035);
    }

    .metric span {
      display: block;
      color: var(--muted);
      font-size: 12px;
      font-weight: 700;
    }

    .metric strong {
      display: block;
      margin-top: 4px;
      font-size: 15px;
    }

    .kanban {
      display: grid;
      grid-template-columns: repeat(4, minmax(0, 1fr));
      gap: 14px;
    }

    /* Воронка CRM шире экрана (десять этапов), последняя колонка обрезается по краю
       панели. Прокрутка тут неизбежна, задача — сделать её понятной:
       - scroll-snap: свайп/колесо доводит колонку до края, а не бросает на полпути;
       - постоянная полоса прокрутки на десктопе (::-webkit-scrollbar) + отступ под неё.
       СОЗНАТЕЛЬНО без scrollbar-width/scrollbar-color: с Chrome 121 наличие
       стандартного свойства отключает ::-webkit-scrollbar целиком.
       ВНИМАНИЕ при проверке: в headless-Chromium полоса всегда оверлейная и не
       занимает места (проверено на изолированной странице: offsetHeight -
       clientHeight = 0 и со стилем, и без него) — скриншотом это не подтвердить,
       только живым десктопным браузером. На телефоне полоса оверлейная всегда,
       там работает только snap. */
    .kanban-scroll-x {
      /* Колонок столько, сколько этапов: число приходит из app.js переменной
         --kanban-cols. Раньше оно ставилось инлайновым grid-template-columns и
         перебивало мобильные правила — доска не сворачивалась в столбик никогда. */
      /* 240, а не 220: при 220px внутри карточки оставалось 160px, и две кнопки
         («Открыть» 74px + «КП-ссылка» 90px + зазор) не помещались в строку —
         каждая карточка на доске несла ряд из одной кнопки и второй под ней.
         Доска и так прокручивается вбок, лишние 20px на колонку ничего не
         ломают, а карточка становится на 34px короче. */
      grid-template-columns: repeat(var(--kanban-cols, 4), minmax(240px, 1fr));
      overflow-x: auto;
      padding-bottom: 10px;
      scroll-snap-type: x proximity;
    }
    .kanban-scroll-x > .kanban-col { scroll-snap-align: start; }
    .kanban-scroll-x::-webkit-scrollbar { height: 8px; }
    .kanban-scroll-x::-webkit-scrollbar-track { background: transparent; }
    .kanban-scroll-x::-webkit-scrollbar-thumb {
      background: var(--hint); border-radius: var(--r-pill);
    }
    .kanban-scroll-x::-webkit-scrollbar-thumb:hover { background: var(--muted); }

    .kanban-col {
      background: var(--panel2);
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      padding: 14px;
      min-height: 220px;
      max-height: calc(100vh - 200px);
      overflow-y: auto;
      scrollbar-width: thin;
    }

    /* Только ЗАГОЛОВОК КОЛОНКИ. Селектор был потомковым (.kanban-col h3) и
       ловил ещё и <h3> каждой карточки внутри колонки: название сделки получало
       серую подложку, отступ снизу и — что хуже — position:sticky, то есть
       прилипало при прокрутке колонки. */
    .kanban-col > h3 {
      display: flex;
      justify-content: space-between;
      align-items: center;
      gap: 8px;
      position: sticky;
      top: 0;
      background: var(--panel2);
      z-index: 1;
      padding-bottom: 10px;
      margin-bottom: 0;
    }
    /* Название колонки и счётчик — одной строкой. Без этого «На согласовании»
       занимало всю ширину, и «0» уезжал под текст: соседние колонки стояли
       заголовками в одну строку, а эта — в две, и ряд визуально разъезжался.
       Длинное имя ужимается многоточием, счётчик и «+» не сжимаются никогда. */
    .kanban-col > h3 > span:first-child {
      display: inline-flex; align-items: center; gap: 6px; min-width: 0;
    }
    /* Многоточие — только на названии: если обрезать весь блок, оно съедает
       счётчик, ради которого в заголовок и смотрят. */
    .kanban-col-name { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .kanban-col > h3 .pill-count { flex: 0 0 auto; }
    .kanban-col > h3 > .btn { flex: 0 0 auto; }

    /* Этапы воронки CRM. Раньше десять колонок были одинаково серыми: на какой
       стадии сделка, читалось только по подписи, и глазом воронка не собиралась.
       Цвет живёт в --status-color (ставится инлайном из CRM_STATUS_COLOR) и красит
       подчёркивание заголовка и счётчик — сами карточки не трогаем, иначе доска
       превращается в радугу. Это НЕ текст, порог контраста здесь 3:1. */
    .kanban-col-status > h3 {
      border-bottom: 2px solid var(--status-color, var(--line));
    }
    /* Счётчик получает рамку, а не цветной текст: внутри он лежит на фиолетовом
       тинте --primary-bg, и перекраска текста в цвет этапа роняла бы контраст
       (жёлтый «Согласование» на тинте — заведомо ниже AA). Рамка — не текст,
       ей достаточно 3:1. */
    .kanban-col-status > h3 .pill-count {
      border: 1.5px solid var(--status-color, var(--line));
      background: transparent;
    }
    /* Колонка, где сделок нет, не должна кричать цветом наравне с рабочими */
    .kanban-col-status.is-empty > h3 { border-bottom-color: var(--line); }
    .kanban-col-status.is-empty > h3 .pill-count { border-color: var(--line); color: var(--muted); }

    .task-card,
    .finance-card,
    .team-card,
    .calendar-card,
    .crm-card {
      box-shadow: none;
      background: rgba(15, 23, 42, .44);
    }

    [data-theme="light"] .task-card,
    [data-theme="light"] .finance-card,
    [data-theme="light"] .team-card,
    [data-theme="light"] .calendar-card,
    [data-theme="light"] .crm-card {
      background: rgba(255, 255, 255, .74);
    }

    .client-mode .nav,
    .client-mode .client-hidden {
      display: none !important;
    }

    .client-mode .layout {
      grid-template-columns: 1fr;
    }

    .client-mode .summary {
      display: none !important;
    }

    .client-mode .proposal-preview {
      margin-top: 0;
    }

    .estimate-toolbar {
      display: flex;
      gap: 8px;
      flex-wrap: wrap;
      margin-bottom: 14px;
    }

    .estimate-toolbar .btn {
      padding: 8px 12px;
    }

    .estimate-stage {
      padding: 16px;
      margin-top: 16px;
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      background: rgba(255, 255, 255, .03);
    }

    .estimate-stage-head {
      display: flex;
      justify-content: space-between;
      gap: 14px;
      align-items: flex-start;
      margin-bottom: 12px;
    }

    .estimate-stage-title {
      display: grid;
      gap: 4px;
    }

    .estimate-stage-title h2 {
      margin-bottom: 0;
      font-size: 20px;
    }

    .estimate-stage-meta {
      color: var(--muted);
      font-size: 12px;
    }

    .estimate-stage-tools {
      display: flex;
      gap: 8px;
      flex-wrap: wrap;
      justify-content: flex-end;
    }

    .line-collapsed-note {
      padding: 10px 12px;
      margin-top: 10px;
      border: 1px dashed var(--line);
      border-radius: var(--r-lg);
      color: var(--muted);
      font-size: 13px;
      background: rgba(255, 255, 255, .03);
    }

    .catalog-price-input {
      max-width: 190px;
    }
    .catalog-price-input::-webkit-outer-spin-button,
    .catalog-price-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
    .catalog-price-input { appearance: textfield; }

    .catalog-qty-stepper {
      display: flex;
      align-items: center;
      gap: 2px;
      border: none;
      border-radius: var(--r-pill);
      background: rgb(var(--primary-rgb) / .1);
      padding: 0 2px;
      flex: 0 0 auto;
    }
    .catalog-qty-btn {
      width: 26px;
      height: 32px;
      border: none;
      border-radius: var(--r-pill);
      background: transparent;
      color: var(--primary-text);
      font-size: 16px;
      font-weight: 700;
      line-height: 1;
      cursor: pointer;
      transition: background var(--dur-1);
    }
    .catalog-qty-btn:hover { background: rgb(var(--primary-rgb) / .22); }
    .catalog-qty-btn:active { background: rgb(var(--primary-rgb) / .32); }

    .catalog-qty-value {
      min-width: 20px;
      text-align: center;
      color: var(--text);
      font-size: 13px;
      font-weight: 700;
      font-variant-numeric: tabular-nums;
    }

    @keyframes catalogQtyPop {
      from { opacity: 0; transform: scale(.85); }
      to   { opacity: 1; transform: scale(1); }
    }
    .catalog-qty-pop { animation: catalogQtyPop .18s cubic-bezier(.16,1,.3,1); }
    .catalog-add-btn { animation: catalogQtyPop .18s cubic-bezier(.16,1,.3,1); }

    /* Убираем спиннеры у числовых полей в строках сметы (цена, дни, кол-во) */
    input[data-scope="line"][data-key="price"]::-webkit-inner-spin-button,
    input[data-scope="line"][data-key="price"]::-webkit-outer-spin-button,
    input[data-scope="line"][data-key="days"]::-webkit-inner-spin-button,
    input[data-scope="line"][data-key="days"]::-webkit-outer-spin-button,
    input[data-scope="line"][data-key="qty"]::-webkit-inner-spin-button,
    input[data-scope="line"][data-key="qty"]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
    input[data-scope="line"][data-key="price"],
    input[data-scope="line"][data-key="days"],
    input[data-scope="line"][data-key="qty"] { appearance: textfield; }

    .proposal-brand {
      display: flex;
      align-items: center;
      gap: 14px;
      margin-bottom: 14px;
    }

    .proposal-brand img {
      width: 54px;
      height: 54px;
      object-fit: contain;
    }

    .proposal-brand h1 {
      margin: 0;
    }

    .proposal-brand p {
      margin: 3px 0 0;
    }

    .estimate-line-actions {
      display: flex;
      gap: 8px;
      flex-wrap: wrap;
      justify-content: flex-end;
    }

    /* «Как считать: За ролик · Сменами · По часам» — сегмент над полями строки. */
    .bill-mode { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin-top: 14px; }
    .bill-mode-lbl { font-size: 12px; font-weight: 700; color: var(--muted); }
    .bill-mode-seg {
      display: inline-flex; gap: 2px; padding: 3px; border-radius: var(--r-pill);
      border: 1px solid var(--line); background: var(--panel2);
    }
    .bill-mode-btn {
      height: 30px; padding: 0 14px; border: 0; border-radius: var(--r-pill); cursor: pointer;
      background: transparent; color: var(--muted); font: inherit; font-size: 12.5px; font-weight: 650; white-space: nowrap;
      transition: background var(--dur-2) var(--ease-out), color var(--dur-2) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
    }
    .bill-mode-btn:hover:not(.is-active) { color: var(--text); }
    .bill-mode-btn.is-active { background: var(--primary); color: var(--on-color); box-shadow: 0 4px 14px -6px rgb(var(--primary-rgb) / .7); }
    @media (hover: none) and (pointer: coarse) {
      .bill-mode-btn { position: relative; }
      .bill-mode-btn::after { content: ""; position: absolute; left: 0; right: 0; top: 50%; height: 44px; transform: translateY(-50%); }
    }
    /* Число, которое считается по датам, — не поле ввода, а итог. */
    input.is-by-dates { background: var(--panel2); color: var(--muted); cursor: default; }

    /* «Смены / дни по датам» — список дат вместо N одинаковых строк. */
    .work-dates {
      margin-top: 12px; padding: 12px 14px; border-radius: var(--r-lg);
      border: 1px solid var(--line); background: var(--panel2);
    }
    .work-dates.is-empty { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; border-style: dashed; }
    .work-dates-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
    .work-dates-title { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 700; }
    .work-dates-sum { font-size: 12.5px; font-weight: 700; color: var(--primary-text); font-variant-numeric: tabular-nums; }
    .work-dates-list { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
    .work-date {
      display: inline-flex; align-items: center; gap: 2px; padding: 2px 4px 2px 2px;
      border: 1px solid var(--line); border-radius: var(--r-md); background: var(--panel);
      transition: border-color var(--dur-2);
    }
    .work-date:hover { border-color: rgb(var(--primary-rgb) / .45); }
    .work-date input[type="date"] {
      width: auto; min-width: 0; height: 30px; padding: 0 6px; border: 0; background: transparent;
      font-size: 12.5px; font-variant-numeric: tabular-nums; color: var(--text);
    }
    .work-date-x {
      width: 24px; height: 24px; border: 0; border-radius: 50%; background: transparent; color: var(--muted);
      display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
      transition: background var(--dur-1), color var(--dur-1);
    }
    .work-date-x:hover { background: color-mix(in srgb, var(--red) 14%, transparent); color: var(--text-danger); }
    .work-date.is-new { animation: workDateIn .45s var(--ease-spring); border-color: rgb(var(--primary-rgb) / .6); }
    @keyframes workDateIn { from { opacity: 0; transform: translateY(4px) scale(.92); } }
    .work-date-add { display: inline-flex; align-items: center; gap: 4px; }
    .work-dates-clear {
      margin-top: 10px; padding: 0; border: 0; background: none; cursor: pointer;
      font-size: 12px; color: var(--muted); text-decoration: underline; text-underline-offset: 3px;
    }
    .work-dates-clear:hover { color: var(--text); }
    @media (hover: none) and (pointer: coarse) {
      .work-date-x, .work-dates-clear { position: relative; }
      .work-date-x::after, .work-dates-clear::after { content: ""; position: absolute; left: 50%; top: 50%; width: 44px; height: 44px; transform: translate(-50%, -50%); }
    }
    @media (prefers-reduced-motion: reduce) { .work-date.is-new { animation: none; } }

    /* Дата работы по позиции под её названием — видна и в свёрнутой строке. */
    .line-date-meta {
      display: inline-flex; align-items: center; gap: 5px; margin: 2px 0 2px 2px;
      font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums;
    }
    .line-total-note {
      color: var(--muted);
      font-size: 12px;
      margin-top: -2px;
    }

    .quick-actions-box {
      padding: 12px;
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      background: rgba(255, 255, 255, .035);
      margin-top: 14px;
    }

    .quick-actions-box h3 {
      margin-bottom: 8px;
      font-size: 15px;
    }

    #printArea {
      display: none;
    }

    @media print {
      body {
        background: #fff !important;
      }

      .topbar,
      .mobile-bottom-nav,
      .toast,
      .toast.show,
      #toast,
      #printArea,
      .no-print,
      .section-title.no-print {
        display: none !important;
        visibility: hidden !important;
      }

      .container {
        width: 100% !important;
        margin: 0 !important;
      }

      .panel {
        padding: 0 !important;
        border: 0 !important;
        box-shadow: none !important;
      }
    }

    /* ── MEDIUM SCREENS (1161–1600px): icon-only nav ────────────────────── */
    @media (min-width: 1161px) and (max-width: 1600px) {
      .nav-label { display: none; }
      .nav button { padding: 9px; gap: 0; justify-content: center; min-width: 34px; }
      .brand p { display: none; }
      .autosave-indicator { display: none; }
      #undoBtn, #redoBtn { display: none; }
      .admin-indicator {
        max-width: 130px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        font-size: 12px;
      }
    }

    @media (max-width: 1160px) {
      /* Топбар: 3 колонки с бургером вместо nav-строки */
      .topbar {
        grid-template-columns: auto 1fr auto;
        align-items: center;
        padding: 10px 16px;
        gap: 10px;
      }

      .nav { display: none; }
      .burger-btn { display: inline-flex; }
      .brand p { display: none; }
      .brand { min-width: 0; }
      .top-actions { gap: 4px; min-width: 0; }
      .autosave-indicator { display: none; }
      #undoBtn, #redoBtn { display: none; }
      .admin-indicator {
        max-width: 130px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        font-size: 12px;
      }

      .layout,
      .kanban {
        grid-template-columns: 1fr;
      }

      .summary {
        position: static;
      }

      .grid.five,
      .grid.four,
      .grid.three,
      .grid.two {
        grid-template-columns: 1fr;
      }

      .brand,
      .top-actions {
        min-width: 0;
      }
    }

    @media (max-width: 768px) {
      .container {
        width: calc(100% - 16px);
      }

      .topbar {
        grid-template-columns: auto 1fr auto;
        padding: 10px 14px;
        gap: 10px;
      }

      .nav { display: none; }

      .burger-btn { display: inline-flex; }

      .brand { min-width: 0; }
      .brand h1 { font-size: 14px; }
      .brand p { display: none; }

      .top-actions { gap: 4px; min-width: 0; }
      .autosave-indicator { display: none; }
      /* #searchBtn СОЗНАТЕЛЬНО не скрыт: на телефоне это единственный способ найти
         сделку среди сотни, не листая списки (замер 03.08.2026 — кнопки не было). */
      #themeBtn, #clientModeBtn, .help-btn, .global-add-wrap, #settingsIconBtn, #undoBtn, #redoBtn { display: none; }
      /* Profile page mobile fixes */
      .plan-card { padding: 14px; }
      .plan-card .plan-price { font-size: 22px; }
      .section-title h1 { font-size: 20px; }
      /* Auth gate mobile */
      .auth-gate-box { padding: 24px 18px; max-width: 100%; margin: 8px; }
      /* Admin indicator compact on mobile */
      .admin-indicator { max-width: 90px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; padding: 4px 6px; }


      .item-top,
      .section-title,
      .line-head,
      .estimate-stage-head {
        flex-direction: column;
      }

      /* Сумма и её подпись — ОДНО значение, и на телефоне они разъезжались по
         разным краям: блок растягивался на всю ширину с text-align: left, а сама
         сумма оставалась прибитой вправо своим justify-content. Получалось «В
         итоге» слева, «9 000 ₽» справа, и между ними пустая строка.
         Теперь это одна строка: подпись слева, значение справа. */
      .price-editor {
        width: 100%;
        display: flex;
        align-items: baseline;
        justify-content: flex-end;
        flex-wrap: wrap;
        gap: 4px 10px;
        text-align: left;
      }
      .price-editor > .line-total-note {
        order: -1;
        margin-right: auto;
        margin-top: 0;
      }
      /* Маржа — своя строка: это отдельная мысль, а не подпись к сумме. */
      .price-editor > .line-margin-note { flex: 1 1 100%; }

      .price-editor input {
        text-align: left;
      }

      h1 {
        font-size: 22px;
      }

      .metric-grid {
        grid-template-columns: 1fr;
      }

      .grid.three,
      .grid.two {
        grid-template-columns: 1fr;
      }

      .deal-card-footer {
        flex-wrap: wrap;
        gap: 6px;
      }

      /* Compact topbar on mobile */
      .presence-area { display: none; }
      .admin-indicator { max-width: 90px; font-size: 12px; padding: 3px 7px; }
      .profile-avatar-btn { width: 28px; height: 28px; font-size: 12px; }

      /* Profile page mobile: тарифы — один столбец, карточки на всю ширину.
         Раньше был горизонтальный скролл с обрезанными по краям карточками —
         на узких экранах выглядело как визуальная ошибка. */
      .grid.five { grid-template-columns: 1fr; }
      .plan-card { padding: 16px; }

      /* Section title on mobile */
      .section-title { gap: 10px; }
      .section-title h1 { font-size: 20px; }

      /* Deal bar compact */
      .deal-compact-row { flex-direction: column; gap: 8px; }
      .deal-stats-row { flex-wrap: wrap; gap: 6px; }
      .deal-nav-group { flex-wrap: wrap; gap: 6px; }
      .next-action-btn { font-size: 12px; padding: 6px 10px; }
    }

    /* ── GLOBAL SEARCH ──────────────────────────── */
    .search-modal {
      position: fixed; inset: 0; z-index: var(--z-search);
      background: rgba(7,11,20,.65);
      -webkit-backdrop-filter: blur(10px);
      backdrop-filter: blur(10px);
      display: flex; align-items: flex-start; justify-content: center;
      padding-top: clamp(48px, 10vh, 100px);
      animation: fadeIn .15s ease;
    }
    .search-box {
      width: min(620px, calc(100vw - 32px)); background: var(--panel);
      border-radius: var(--r-xl); border: 1px solid rgba(255,255,255,.1);
      box-shadow: var(--elev-3);
      overflow: hidden;
      animation: slideUp .18s cubic-bezier(0.16, 1, 0.3, 1);
    }
    [data-theme="light"] .search-modal { background: rgba(0,0,0,.35); }
    [data-theme="light"] .search-box { border-color: var(--line); }
    .search-result {
      display: flex; align-items: center; gap: 12px;
      padding: 10px 16px; cursor: pointer;
      transition: background var(--dur-1);
    }
    .search-result:hover { background: rgb(var(--primary-rgb) / .1); }
    .search-result-icon { width: 32px; height: 32px; border-radius: var(--r-sm); flex: 0 0 32px; display: flex; align-items: center; justify-content: center; font-size: 15px; }
    .search-result-name { font-size: 13px; font-weight: 600; }
    .search-result-sub { font-size: 12px; color: var(--muted); margin-top: 1px; }
    .search-section { padding: 8px 16px 4px; font-size: 12px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }

    /* ── PROFILE DROPDOWN ───────────────────────── */
    .profile-dd-wrap { position: relative; }
    .profile-dd {
      position: absolute; top: calc(100% + 10px); right: 0;
      width: 230px; background: var(--panel); border: 1px solid var(--line);
      border-radius: var(--r-xl); box-shadow: var(--elev-3);
      z-index: var(--z-notif); overflow: hidden; display: none;
    }
    .profile-dd.open { display: block; animation: fadeIn .15s ease; }
    .pd-head {
      display: flex; align-items: center; gap: 10px;
      padding: 14px 16px 12px; border-bottom: 1px solid var(--line);
    }
    .pd-avatar {
      width: 38px; height: 38px; border-radius: 50%; flex: 0 0 38px;
      background: var(--primary);
      display: flex; align-items: center; justify-content: center;
      font-size: 15px; font-weight: 900; color: var(--on-color); overflow: hidden;
    }
    .pd-avatar img { width: 100%; height: 100%; object-fit: cover; }
    .pd-email { font-size: 12px; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .pd-sub { font-size: 12px; color: var(--muted); margin-top: 1px; }
    .pd-sep { height: 1px; background: var(--line); margin: 4px 0; }
    .pd-item {
      display: flex; align-items: center; gap: 10px;
      width: 100%; padding: 9px 16px; border: none; background: none;
      color: var(--text); font-size: 13px; font-weight: 700; cursor: pointer;
      text-align: left; transition: background var(--dur-1), color var(--dur-1);
    }
    .pd-item:hover { background: rgb(var(--primary-rgb) / .08); color: var(--primary-on-tint); }
    .pd-item.danger { color: var(--text-danger); }
    .pd-item.danger:hover { background: rgba(220,38,38,.07); color: var(--text-danger); }
    .pd-item-icon { width: 18px; text-align: center; opacity: .7; font-size: 14px; flex: 0 0 18px; }

    /* ── HELP DROPDOWN ───────────────────────────── */
    .help-dd-wrap { position: relative; }
    .help-dd-wrap--hidden { display: none; }
    .help-dd {
      position: absolute; top: calc(100% + 10px); right: 0;
      width: 240px; background: var(--panel); border: 1px solid var(--line);
      border-radius: var(--r-xl); box-shadow: 0 20px 60px rgba(0,0,0,.45);
      z-index: 300; overflow: hidden; display: none; padding: 6px 0;
    }
    .help-dd.open { display: block; animation: fadeIn .15s ease; }
    .help-dd-section { padding: 6px 14px 2px; font-size: 12px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; }
    .help-dd-item {
      display: flex; align-items: center; gap: 10px;
      width: 100%; padding: 8px 16px; border: none; background: none;
      color: var(--text); font-size: 13px; font-weight: 700; cursor: pointer;
      text-align: left; transition: var(--dur-1); text-decoration: none;
    }
    .help-dd-item:hover { background: rgb(var(--primary-rgb) / .08); color: var(--primary-on-tint); }
    .help-dd-item .hdi-sub { font-size: 12px; font-weight: 500; color: var(--muted); margin-top: 1px; }
    .help-dd-item-icon { width: 20px; height: 20px; border-radius: var(--r-sm); display: flex; align-items: center; justify-content: center; font-size: 13px; flex: 0 0 20px; }

    /* ── CURRENCY DROPDOWN ───────────────────────── */
    /* width: на телефоне поле стояло уже соседних (по ширине текста). */
    .currency-select-wrap { position: relative; width: 100%; }
    /* Тот же вид, что у остальных выпадающих списков (.uu-select-btn): у кнопки
       браузер по умолчанию центрирует текст, и «₽ — Российский рубль» стоял по
       центру поля, когда все соседние поля формы выровнены влево. Заодно
       сведены отступы, радиус и шрифт — раньше валюта была крупнее и круглее
       остальных полей той же формы. */
    .currency-select-btn {
      display: flex; align-items: center; justify-content: space-between; gap: 8px;
      width: 100%; padding: 9px 12px; border: 1px solid var(--line);
      border-radius: var(--r-md); background: var(--input); color: var(--text);
      font-size: 13px; font-weight: 600; cursor: pointer; text-align: left;
      transition: border-color var(--dur-1); line-height: 1.3;
    }
    .currency-select-btn:hover { border-color: rgb(var(--primary-rgb) / .5); }
    /* Длинное название валюты (напр. «Казахстанский тенге») переносилось на 2 строки —
       кнопка становилась выше соседнего поля «Описание» и ломала выравнивание сетки.
       Одна строка с многоточием — высота всегда одинаковая. */
    .currency-select-btn { min-height: 42px; }
    .currency-select-btn span:first-child {
      flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    .currency-select-btn span:last-child { flex-shrink: 0; }
    .currency-select-dd {
      position: absolute; top: calc(100% + 4px); left: 0; right: 0;
      /* Непрозрачно: --panel полупрозрачен, и под списком читались строки
         следующей карточки (валюта теперь не последняя на вкладке). */
      background: linear-gradient(var(--panel), var(--panel)), var(--bg); border: 1px solid var(--line); border-radius: var(--r-lg);
      box-shadow: 0 12px 40px rgba(0,0,0,.3); z-index: 200; overflow: hidden;
      display: none;
    }
    .currency-select-dd.open { display: block; animation: fadeIn .12s ease; }
    .currency-opt {
      display: flex; align-items: center; gap: 10px;
      padding: 10px 14px; cursor: pointer; transition: var(--dur-1);
      font-size: 13px; border: none; background: none; color: var(--text);
      width: 100%; text-align: left;
    }
    .currency-opt:hover { background: rgb(var(--primary-rgb) / .08); }
    .currency-opt.active { background: rgb(var(--primary-rgb) / .12); color: var(--primary-on-tint); font-weight: 600; }
    .currency-sym { font-size: 15px; font-weight: 900; width: 22px; text-align: center; opacity: .8; }

    /* ── Универсальный кастом-дропдаун (заменяет нативные select на десктопе) ── */
    .uu-select-wrap { position: relative; }
    .uu-select-wrap > select.uu-done { position: absolute; opacity: 0; pointer-events: none; width: 1px; height: 1px; margin: 0; padding: 0; border: 0; }
    .uu-select-btn {
      display: flex; align-items: center; justify-content: space-between; gap: 8px;
      width: 100%; padding: 9px 12px; border: 1px solid var(--line);
      border-radius: var(--r-md); background: var(--input); color: var(--text);
      font-size: 13px; font-weight: 600; cursor: pointer; text-align: left;
      transition: border-color var(--dur-1); line-height: 1.3;
    }
    .uu-select-btn:hover { border-color: rgb(var(--primary-rgb) / .5); }
    .uu-select-btn.uu-open { border-color: var(--primary); }
    .uu-select-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
    .uu-select-chev { flex-shrink: 0; opacity: .55; transition: transform var(--dur-1); display: grid; place-items: center; }
    .uu-select-btn.uu-open .uu-select-chev { transform: rotate(180deg); }
    .uu-select-dd {
      position: fixed; z-index: 3000; background: var(--panel);
      border: 1px solid var(--line); border-radius: var(--r-lg); overflow-y: auto;
      box-shadow: 0 12px 40px rgba(0,0,0,.35); padding: 4px;
      opacity: 0; transform: translateY(-4px); transition: opacity var(--dur-1), transform var(--dur-1);
      scrollbar-width: thin; overscroll-behavior: contain;
    }
    .uu-select-dd.open { opacity: 1; transform: translateY(0); }
    /* Строка поиска — только у длинных списков (см. UU_SEARCH_MIN_OPTIONS). Sticky
       поверх опций внутри того же скролл-контейнера, не отдельная область. */
    .uu-select-search {
      position: sticky; top: 0; z-index: 1; width: 100%;
      padding: 8px 10px; margin-bottom: 4px; box-sizing: border-box;
      border: none; border-bottom: 1px solid var(--line); border-radius: var(--r-sm) var(--r-sm) 0 0;
      background: var(--panel); color: var(--text); font-size: 13px; font: inherit;
    }
    .uu-select-search:focus { outline: none; background: var(--panel2); }
    .uu-select-search::placeholder { color: var(--muted); }
    .uu-select-opt {
      display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
      padding: 9px 12px; border: none; background: none; color: var(--text);
      font-size: 13px; cursor: pointer; border-radius: var(--r-sm); transition: background var(--dur-1);
      white-space: normal; overflow-wrap: break-word; line-height: 1.35;
    }
    .uu-select-opt:hover { background: rgb(var(--primary-rgb) / .1); }
    .uu-select-opt.active { background: rgb(var(--primary-rgb) / .16); color: var(--primary-on-tint); font-weight: 700; }
    .uu-select-opt.uu-opt-hidden { display: none; }

    /* ── MOBILE BOTTOM NAV ──────────────────────── */
    .mobile-bottom-nav {
      display: none;
      position: fixed; bottom: 0; left: 0; right: 0; z-index: 110;
      background: rgba(10,10,18,.96);
      border-top: 1px solid var(--line);
      -webkit-backdrop-filter: blur(20px) saturate(180%);
      backdrop-filter: blur(20px) saturate(180%);
      padding: 6px
              max(4px, env(safe-area-inset-right, 4px))
              max(8px, env(safe-area-inset-bottom, 8px))
              max(4px, env(safe-area-inset-left, 4px));
      gap: 0;
      align-items: center;
    }
    [data-theme="light"] .mobile-bottom-nav { background: rgba(255,255,255,.96); }
    .mbn-item {
      flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
      gap: 3px; padding: 6px 2px 4px; border: none; background: none;
      color: var(--muted); font-size: 12px; font-weight: 700; cursor: pointer;
      transition: color var(--dur-1), transform var(--dur-1); min-width: 44px; min-height: 44px; overflow: hidden; position: relative;
    }
    /* Точка-индикатор под активным пунктом убрана: цвета и подсветки иконки уже
       достаточно, чтобы понять, где находишься, а точка добавляла третий сигнал
       об одном и том же и читалась как мусор под подписью. */
    .mbn-item.active { color: var(--primary-text); }
    .mbn-item:active { transform: scale(.9); }
    .mbn-item svg { flex: 0 0 auto; display: block; }
    .mbn-item.active svg { filter: drop-shadow(0 0 4px rgb(var(--primary-rgb) / .5)); }
    .mbn-add {
      flex: 0 0 52px; width: 52px; height: 52px; border-radius: 50%;
      background: var(--primary);
      color: var(--on-color); font-size: 26px; font-weight: 300; line-height: 1;
      box-shadow: 0 4px 20px rgb(var(--primary-rgb) / .5);
      display: flex; align-items: center; justify-content: center;
      border: none; cursor: pointer; margin-top: -18px; transition: background var(--dur-1), box-shadow var(--dur-1);
      flex-shrink: 0; position: relative; z-index: 1;
    }
    .mbn-add:hover { background: var(--primary2); box-shadow: 0 6px 24px rgb(var(--primary-rgb) / .65); }
    .mbn-add:active { transform: scale(.9); }
    @media (max-width: 768px) {
      .mobile-bottom-nav { display: flex; }
      .container { padding-bottom: 88px; }
    }

    /* ── ЛИСТ РАЗДЕЛОВ НА ТЕЛЕФОНЕ ──────────────────────────────────────────
       Такой же лист снизу, как у разделов каталога: на телефоне это уже принятый
       в приложении способ показать длинный список выбора. Слой модальный —
       нижняя панель прибита на z-index 110 и накрыла бы последние пункты
       (на разделах каталога этот же промах ловил именно скриншот, замер молчал). */
    .mobile-nav-backdrop {
      position: fixed; inset: 0;
      z-index: calc(var(--z-modal) - 1);
      background: rgba(0, 0, 0, .45);
    }
    .mobile-nav-sheet {
      position: fixed;
      /* top: auto ОБЯЗАТЕЛЕН при sticky→fixed — иначе унаследованное top прилепит
         лист к верху экрана вместо низа. */
      left: 0; right: 0; bottom: 0; top: auto;
      z-index: var(--z-modal);
      display: flex; flex-direction: column;
      max-height: 86vh; overflow-y: auto; overflow-x: hidden;
      padding: 0 12px calc(12px + env(safe-area-inset-bottom, 0px));
      background: var(--panel);
      border-top: 1px solid var(--line);
      border-radius: var(--r-2xl) var(--r-2xl) 0 0;
      box-shadow: 0 -12px 40px rgba(0, 0, 0, .28);
      animation: sheetUp .28s cubic-bezier(.32, .72, 0, 1);
    }
    .mobile-nav-sheet-head {
      position: sticky; top: 0; z-index: 1;
      display: flex; align-items: center; justify-content: space-between; gap: 10px;
      margin: 0 -12px 6px; padding: 16px 12px 10px;
      background: var(--panel);
      border-bottom: 1px solid var(--line);
      font-weight: 800;
    }
    .mobile-nav-sheet-head::before {
      content: ""; position: absolute; top: 6px; left: 50%;
      width: 36px; height: 4px; margin-left: -18px;
      border-radius: var(--r-pill); background: var(--line);
    }
    /* Кнопка «назад» в шапке листа. Заголовок между ней и «Закрыть» — растягиваем
       его, чтобы «Закрыть» осталась прижатой к правому краю. */
    .mobile-nav-sheet-head > span { flex: 1 1 auto; min-width: 0; }
    .mobile-nav-sheet-back {
      display: inline-flex; align-items: center; justify-content: center;
      flex: 0 0 auto; width: 36px; height: 36px; margin-left: -6px;
      color: var(--muted); background: none;
      border: 1px solid var(--line); border-radius: var(--r-md);
      cursor: pointer;
    }
    .mobile-nav-sheet-back:hover { color: var(--text); border-color: var(--primary2); }
    .mobile-nav-sheet-list { display: flex; flex-direction: column; gap: 2px; padding: 2px 0; }
    /* Разметка строк — та же, что в боковом меню (renderNavListHtml), поэтому
       здесь только размер под палец: сайдбар рассчитан на курсор.
       44px — минимум по WCAG и ровно столько, чтобы все 12 разделов и строка
       настройки поместились на экране телефона без прокрутки: список выбора,
       который надо прокручивать, чтобы увидеть, что в нём есть, — половина
       той же беды, что была до листа. */
    .mobile-nav-sheet-list .sidebar-nav-item {
      width: 100%; min-height: 44px;
      gap: 12px; padding: 9px 14px;
      font-size: 15px;
      border-radius: var(--r-md);
    }
    .mobile-nav-sheet-list .sidebar-nav-item svg { width: 18px; height: 18px; }
    .mobile-nav-sheet-list .sidebar-label { font-size: 15px; }
    .mobile-nav-sheet-list .sidebar-project-hint { font-size: 12px; }
    .mobile-nav-sheet-config {
      display: flex; align-items: center; justify-content: center; gap: 8px;
      width: 100%; min-height: 46px; margin-top: 8px; padding: 12px;
      font: inherit; font-size: 13px; font-weight: 700;
      color: var(--muted); background: none;
      border: 1px dashed var(--line); border-radius: var(--r-md);
      cursor: pointer;
    }
    .mobile-nav-sheet-config:hover { color: var(--text); border-color: var(--primary2); }
    /* Настройка меню ВНУТРИ листа. Разметка строк общая с поповером десктопа
       (navConfigRowsHtml), здесь только размер под палец. */
    .mobile-nav-sheet-config-list { padding: 2px 0; }
    .mobile-nav-sheet-config-list .sidebar-nav-config-row { min-height: 46px; }
    .mobile-nav-sheet-config-list .sidebar-nav-config-label { font-size: 15px; }
    /* У вкладок сделки иконок нет — без этого правила подпись висела бы с отступом
       под несуществующую иконку. */
    .mobile-nav-sheet-list .mobile-tab-row { gap: 0; }

    /* ── WELCOME SCREEN ─────────────────────────── */
    .welcome-screen {
      text-align: center;
      padding: 60px 24px 80px;
      max-width: 740px;
      margin: 0 auto;
    }
    .welcome-screen h1 { font-size: 36px; margin-bottom: 10px; }
    .welcome-screen > p { font-size: 16px; color: var(--muted); }
    .welcome-steps {
      display: grid;
      grid-template-columns: repeat(4, 1fr);
      gap: 14px;
      margin: 36px 0 40px;
      text-align: left;
    }
    .welcome-step {
      padding: 20px 16px;
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      background: var(--panel2);
    }
    .welcome-step-num {
      width: 32px; height: 32px;
      border-radius: 50%;
      background: var(--primary);
      color: var(--on-color);
      display: flex; align-items: center; justify-content: center;
      font-weight: 700; font-size: 14px;
      margin-bottom: 12px;
      flex-shrink: 0;
    }
    .welcome-step h3 { font-size: 14px; margin-bottom: 5px; }
    .welcome-step p { font-size: 12px; margin: 0; }
    @media (max-width: 768px) {
      .welcome-steps { grid-template-columns: 1fr 1fr; }
    }

    /* ── WIZARD ─────────────────────────────────── */
    .wizard-wrap {
      max-width: 860px;
      margin: 0 auto;
    }
    /* Мастер «Новая сделка» окном (кнопка «+» над сделками). Окно шире
       обычного (480px): на шаге «Проект» стоит выбор пакета сеткой. Панель
       мастера внутри окна — без своей рамки и тени: окно уже и есть панель. */
    .modal-box.wizard-modal-box { width: min(820px, calc(100vw - 32px)); padding: 0; }
    /* Пакет, выбранный заранее кнопкой «В смету», — видно, с чем создастся сделка. */
    .wizard-preset-pkg {
      display: inline-flex; align-items: center; gap: 8px; margin-top: 16px;
      padding: 8px 12px; border-radius: var(--r-md);
      background: rgb(var(--primary-rgb) / .1); border: 1px solid rgb(var(--primary-rgb) / .3);
      color: var(--primary-text); font-size: 13px;
    }
    .wizard-preset-pkg b { color: var(--text); }
    .wizard-wrap--modal {
      max-width: none; margin: 0; border: none; box-shadow: none; background: none;
      padding: 26px 28px 24px;
    }
    @media (max-width: 768px) { .wizard-wrap--modal { padding: 18px 16px; } }
    /* «+» над сделками — в ряд с чипами тегов, той же высоты. */
    .deal-add-btn {
      width: 36px; height: 36px; flex: 0 0 auto; display: inline-grid; place-items: center;
      border-radius: var(--r-md); border: 1px solid rgb(var(--primary-rgb) / .45);
      background: rgb(var(--primary-rgb) / .1); color: var(--primary-text); cursor: pointer;
      transition: background var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out),
        transform var(--dur-2) var(--ease-spring), box-shadow var(--dur-2) var(--ease-out);
    }
    .deal-add-btn:hover {
      background: var(--primary); border-color: var(--primary); color: var(--on-color);
      transform: translateY(-1px); box-shadow: var(--glow-sm);
    }
    .deal-add-btn:active { transform: none; }
    @media (hover: none) and (pointer: coarse) { .deal-add-btn { width: 44px; height: 44px; } }
    .wizard-progress {
      display: flex;
      align-items: flex-start;
      justify-content: center;
      padding: 0 60px 28px;
    }
    .wstep {
      display: flex; flex-direction: column; align-items: center;
      gap: 8px; min-width: 90px;
    }
    .wstep-num {
      width: 40px; height: 40px; border-radius: 50%;
      display: flex; align-items: center; justify-content: center;
      font-weight: 900; font-size: 15px;
      border: 2px solid var(--line);
      color: var(--muted);
      background: var(--panel);
      transition: var(--dur-2); position: relative; z-index: 1;
    }
    .wstep.active .wstep-num {
      background: var(--primary);
      border-color: transparent; color: var(--on-color);
      box-shadow: 0 0 0 5px rgb(var(--primary-rgb) / .15);
    }
    .wstep.done .wstep-num {
      background: var(--green); border-color: transparent; color: var(--on-color);
    }
    .wstep-label { font-size: 12px; font-weight: 700; color: var(--muted); text-align: center; }
    .wstep.active .wstep-label { color: var(--text); }
    .wstep.done .wstep-label { color: var(--text-success); }
    [data-theme="light"] .wstep.done .wstep-label { color: var(--text-success); }
    .wstep-line {
      flex: 1; height: 2px; background: var(--line);
      margin-top: 19px; min-width: 50px; transition: background var(--dur-2);
    }
    .wstep-line.done { background: var(--green); }
    .wizard-body {
      padding: 28px;
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      background: var(--panel);
    }
    .wizard-pkg-grid {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
      gap: 12px;
      margin-top: 14px;
    }
    .wizard-pkg-card {
      padding: 14px 16px;
      border: 2px solid var(--line);
      border-radius: var(--r-xl);
      cursor: pointer;
      transition: var(--dur-1) var(--ease-out);
      background: var(--panel2);
    }
    .wizard-pkg-card:hover {
      border-color: rgb(var(--primary-rgb) / .7);
      background: rgb(var(--primary-rgb) / .08);
      transform: translateY(-2px);
      box-shadow: 0 8px 24px rgb(var(--primary-rgb) / .18);
    }
    .wizard-pkg-card:active { transform: translateY(0); }
    .wizard-pkg-card h3 { font-size: 14px; margin-bottom: 4px; }
    .wizard-pkg-card p { font-size: 12px; margin: 0; }
    .wizard-pkg-card .pkg-price { font-weight: 900; font-size: 15px; margin-top: 8px; color: var(--primary-text); }
    .wizard-pkg-search {
      flex: 0 1 auto;
      max-width: 420px;
      margin-bottom: 12px;
    }
    .wizard-pkg-search .catalog-search-input { min-height: 44px; }
    .wizard-pkg-empty {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 12px;
      flex-wrap: wrap;
      padding: 14px 16px;
      border: 1px dashed var(--line);
      border-radius: var(--r-xl);
      background: var(--panel2);
      color: var(--muted);
      font-size: 13px;
    }

    /* ── PACKAGE CARD ─────────────────────────────────── */
    .package-card {
      position: relative; overflow: hidden; padding: 18px 18px 16px;
      display: flex; flex-direction: column;
      border: 1px solid var(--line); transition: border-color var(--dur-2), box-shadow var(--dur-2);
    }
    /* Через токен, а не своей тенью: рукописная 0 12px 32px rgba(0,0,0,.22)
       не знала про светлую тему и ложилась там тёмным пятном. */
    .package-card:hover { border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary), var(--elev-2); transform: translateY(-2px); }
    /* Открытое «⋮» не должно резаться краем карточки (overflow: hidden) и
       прятаться под карточкой следующего ряда: подъём при наведении создаёт
       свой слой, и соседи ниже по разметке рисуются поверх меню. */
    .package-card:has(.deal-ctx-menu[style*="block"]) { overflow: visible; z-index: 5; }

    /* Category identity is carried by .pkg-cat-badge — no decorative stripe needed */
    .package-card[data-cat="social"]    { border-top-color: rgb(var(--primary-rgb) / .45); }
    .package-card[data-cat="interview"] { border-top-color: rgba(37, 99, 235, .45); }
    .package-card[data-cat="business"]  { border-top-color: rgba(5, 150, 105, .45); }
    .package-card[data-cat="events"]    { border-top-color: rgba(234, 88, 12, .45); }
    .package-card[data-cat="ai"]        { border-top-color: rgba(8, 145, 178, .45); }
    .package-card[data-cat="graphic"]   { border-top-color: rgba(219, 39, 119, .45); }
    .package-card[data-cat="photo"]     { border-top-color: rgba(217, 119, 6, .45); }
    .package-card[data-cat="corporate"] { border-top-color: rgba(67, 56, 202, .45); }

    .pkg-card-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; margin-bottom: 10px; }
    .pkg-card-name { margin: 0 0 5px; font-size: 15px; font-weight: 900; line-height: 1.3; }
    /* Две строки под описание пакета всегда: у соседей по ряду оно бывает в одну
       строку и в две, и тогда цена, «ДЛЯ: …» и весь состав съезжают друг
       относительно друга — три карточки одного уровня читаются как разные
       блоки. Резерв ровняет их по цене, по которой пакеты и сравнивают. */
    .pkg-card-desc {
      margin: 0 0 12px; font-size: 12px; color: var(--muted); line-height: 1.5;
      min-height: calc(2 * 1.5em);
      display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
    }
    .pkg-card-price {
      font-family: 'Space Grotesk', sans-serif; font-weight: 900; font-size: 22px;
      font-variant-numeric: tabular-nums; color: var(--text); margin-bottom: 6px; letter-spacing: -.02em;
    }
    /* Подпись под ценой пакета: число посчитано за один съёмочный день. Стоит
       вплотную к цене (отрицательный отступ гасит margin самой цены), иначе читается
       как отдельная строка карточки, а не уточнение к сумме. */
    .pkg-card-price-note {
      margin-top: -4px; margin-bottom: 8px;
      font-size: 12px; font-weight: 600; color: var(--muted);
    }
    /* Капс снят 04.09.2026 по тому же правилу, что и на карточках сделок, но
       случай тут хуже: там капсом были слова-подписи («Бюджет», «Долг»), а здесь
       ЦЕЛАЯ ФРАЗА из поля пакета — до 62 знаков вида «ДЛЯ: АКТИВНЫЕ БРЕНДЫ,
       ПРОДАКШН-АГЕНТСТВА, МАРКЕТИНГОВЫЕ ОТДЕЛЫ». Капс убивает различие букв по
       высоте, и такая строка читается по слогам.

       Замер по всему приложению (12 разделов): это была ЕДИНСТВЕННАЯ фраза
       капсом на экране — остальные правила с uppercase стоят на словах-
       разделителях, где капс уместен. */
    .pkg-card-for { font-size: 12px; color: var(--muted); margin-bottom: 12px; font-weight: 600; }
    .pkg-card-items { flex: 1; margin-bottom: 14px; }
    .pkg-item-line { font-size: 12px; padding: 3px 0; color: var(--muted); border-bottom: 1px solid var(--line); }
    .pkg-item-line:last-child { border-bottom: none; }
    /* Сегмент-переключатель раздела «Услуги»: Каталог | Пакеты. Отделён визуально от
       нижележащих табов-фильтров (пилюля-контейнер, а не подчёркивание). */
    .seg-switch {
      display: inline-flex;
      gap: 4px;
      padding: 4px;
      margin: 0 0 20px;
      background: var(--bg);
      border: 1px solid var(--line);
      border-radius: var(--r-lg);
    }
    .seg-switch button {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 8px;
      min-height: 40px;
      padding: 8px 18px;
      border: none;
      background: none;
      border-radius: var(--r-md);
      font: inherit;
      font-weight: 600;
      font-size: 14px;
      color: var(--muted);
      cursor: pointer;
      transition: background var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
    }
    .seg-switch button svg { opacity: .7; }
    .seg-switch button:hover { color: var(--text); }
    .seg-switch button.active {
      background: var(--bg2);
      color: var(--text);
      box-shadow: 0 1px 3px rgba(0, 0, 0, .12);
    }
    .seg-switch button.active svg { opacity: 1; color: var(--primary); }
    @media (max-width: 640px) {
      .seg-switch { display: flex; width: 100%; }
      .seg-switch button { flex: 1; min-height: 46px; }
    }
    /* Вариант с бОльшим числом вкладок (Настройки: 4-5 пунктов) — перенос строк
       вместо сжатия всех кнопок в одну узкую линию. */
    .seg-switch.wrap { flex-wrap: wrap; }
    /* Вкладка-ссылка (напр. «Admin Panel» в Настройках) — не переключает контент, а уводит
       на отдельную страницу. Красный акцент как у пункта Admin Panel в сайдбаре — визуально
       предупреждает, что это не обычная вкладка. */
    .seg-switch-link { color: var(--text-danger) !important; opacity: .75; }
    .seg-switch-link:hover { opacity: 1; }
    .seg-switch-link svg { opacity: 1 !important; }
    /* Вкладки Настроек — цвет иконки по теме раздела, тот же акцент, что у
       iconBadge() в заголовке контента внутри вкладки (не рассинхронизировано). */
    .seg-switch button.seg-switch-yellow.active svg { color: var(--yellow); }
    .seg-switch button.seg-switch-blue.active svg { color: var(--blue); }
    .seg-switch button.seg-switch-green.active svg { color: var(--text-success); }

    /* Карточка «Установить ADERVIS CRM» (Настройки → Интеграции). Три флекс-элемента
       (иконка/текст/кнопка) на узком экране раньше не переносились на новую строку —
       текст с flex:1 просто сжимался почти до нуля вместо переноса, кнопка вылезала
       рядом. Ниже 640px — колонка, кнопка на всю ширину. */
    .pwa-install-head { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
    @media (max-width: 640px) {
      .pwa-install-head { flex-direction: column; align-items: stretch; }
      .pwa-install-cta { width: 100%; }
      .pwa-install-cta .btn { width: 100%; }
    }

    /* ── Вкладка «Данные» ────────────────────────────────────────────────────
       Карточка занимает ячейку целиком, описанию отведены две строки, действие
       прижато к низу. Высоты НЕ подгоняем: ровный ряд держится строением, иначе
       карточки поплывут от текста разной длины — тот же разбор, что у каталога,
       пакетов и шаблонов договоров (31.08). */
    .data-card {
      display: flex; flex-direction: column; gap: 8px; height: 100%;
      padding: 14px; border-radius: var(--r-lg);
      background: var(--panel); border: 1px solid var(--line);
      transition: border-color .15s ease, transform .15s ease;
    }
    .data-card:hover { border-color: rgb(var(--primary-rgb) / .45); transform: translateY(-1px); }
    .data-card__title { font-size: 13px; font-weight: 750; line-height: 1.35; }
    .data-card__note {
      font-size: 12px; color: var(--muted); line-height: 1.5;
      min-height: 2.6em; /* резерв под две строки — ровно ради ровного ряда */
    }
    /* Кнопка НЕ на всю ширину карточки: сетку держит сама карточка, а залитая
       плита во всю ячейку кричит там, где нужно обычное действие — три таких
       подряд (фиолетовая и две зелёные) читались рядом цветных полос. */
    .data-card__act { margin-top: auto; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
    /* width:auto перебивает общее правило для полей ввода: месяц во всю ширину
       карточки — ровно то, что раздражало на прежнем экране. */
    .data-card input[type="month"] {
      width: auto; max-width: 190px; padding: 7px 10px; font-size: 13px;
      border: 1px solid var(--line); border-radius: var(--r-sm);
      background: var(--panel2); color: var(--text);
    }
    @media (max-width: 1160px) {
      /* В одну колонку карточка широкая, и кнопка у левого края теряется. */
      .data-card__act .btn { flex: 1 1 auto; justify-content: center; }
    }

    /* Плитка факта: значок, число, подпись. Числа моноширинные — колонка цифр
       должна читаться сверху вниз, а не прыгать. */
    .data-stat {
      display: flex; align-items: center; gap: 11px;
      padding: 12px 14px; border-radius: var(--r-lg);
      background: var(--panel); border: 1px solid var(--line);
    }
    .data-stat__num {
      font-family: var(--font-display); font-weight: 800; font-size: 18px;
      font-variant-numeric: tabular-nums; line-height: 1.15;
    }
    .data-stat__lbl { font-size: 12px; color: var(--muted); line-height: 1.35; }
    @media (max-width: 640px) {
      .seg-switch.wrap button { flex: 1 1 calc(50% - 4px); min-width: 0; }
    }

    .pkg-note { font-size: 12px; color: var(--muted); font-style: italic; background: var(--panel2); padding: 6px 10px; border-radius: var(--r-sm); margin: 0 0 12px; line-height: 1.4; }
    /* wrap обязателен: в ряду четыре элемента — «В смету», «Ссылка клиенту» и две
       иконки (звезда, скрыть). На 320px они перестали помещаться, и подпись
       вылезала за край карточки на 3px — поймал сторож «подпись не наезжает на
       соседа». Переносим, а не ужимаем: ужатая кнопка теряет цель касания. */
    .pkg-card-actions { display: flex; gap: 8px; margin-top: auto; flex-wrap: wrap; }
    /* Иконки не тянутся при переносе — растянутая звезда выглядит поломкой. */
    .pkg-card-actions .catalog-action-btn { flex: 0 0 auto; }

    .pkg-cat-badge {
      display: inline-flex; align-items: center; gap: 4px;
      font-size: 12px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
      padding: 2px 8px; border-radius: var(--r-pill);
    }
    .pkg-cat-badge[data-cat="social"]    { background: var(--tint-violet-bg); color: var(--tint-violet); }
    .pkg-cat-badge[data-cat="interview"] { background: var(--tint-blue-bg);   color: var(--tint-blue); }
    .pkg-cat-badge[data-cat="business"]  { background: var(--tint-green-bg);  color: var(--tint-green); }
    .pkg-cat-badge[data-cat="events"]    { background: var(--tint-orange-bg); color: var(--tint-orange); }
    .pkg-cat-badge[data-cat="ai"]        { background: var(--tint-cyan-bg);   color: var(--tint-cyan); }
    .pkg-cat-badge[data-cat="graphic"]   { background: var(--tint-pink-bg);   color: var(--tint-pink); }
    .pkg-cat-badge[data-cat="photo"]     { background: var(--tint-amber-bg);  color: var(--tint-amber); }
    .pkg-cat-badge[data-cat="corporate"] { background: var(--tint-indigo-bg); color: var(--tint-indigo); }
    /* Точечные переопределения цвета для светлой темы удалены: их роль взяли
       --tint-* (значения для светлой темы лежат в [data-theme="light"] рядом с
       остальными переменными). Прежние значения не проходили AA — business 4,29:1,
       events 4,47:1, photo 4,38:1 — и их было легко забыть при добавлении категории. */

    /* Заголовок группы пакетов — строем шапки раздела каталога
       (.catalog-section-head), см. pkgGroupHeader в app.js. */
    .pkg-group-header {
      grid-column: 1 / -1;
      margin: 8px 0 12px;
    }
    .pkg-group-header:first-child { margin-top: 0; }

    .client-select-search {
      position: relative; display: flex; align-items: center; gap: 8px;
      padding: 0 12px; margin-top: 10px;
      border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--panel2);
    }
    .client-select-search svg { flex: 0 0 auto; color: var(--muted); }
    .client-select-search input {
      flex: 1 1 auto; width: auto; min-width: 0; border: none; background: none;
      padding: 11px 0; font-size: 14px; color: var(--text);
    }
    .client-select-search input:focus { outline: none; }
    .client-select-search:focus-within { border-color: rgb(var(--primary-rgb) / .6); }
    .client-select-search-clear {
      flex: 0 0 auto; width: 24px; height: 24px; display: flex; align-items: center;
      justify-content: center; border: none; background: none; color: var(--muted);
      cursor: pointer; border-radius: var(--r-sm);
    }
    .client-select-search-clear:hover { background: var(--panel); color: var(--text); }

    .client-select-list {
      display: grid; gap: 8px; max-height: 280px; overflow-y: auto;
      scrollbar-width: thin; margin-top: 10px;
    }
    .client-select-item {
      padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--r-lg);
      cursor: pointer; display: flex; gap: 12px;
      align-items: center; background: var(--panel2); transition: var(--dur-1);
    }
    .client-select-item:hover, .client-select-item.selected {
      border-color: rgb(var(--primary-rgb) / .6); background: rgb(var(--primary-rgb) / .1);
    }
    /* Место под галочку держим всегда — иначе выбор клиента дёргает всю строку вбок. */
    .client-select-check {
      flex: 0 0 16px; height: 16px; display: flex; align-items: center;
      justify-content: center; color: var(--primary-text);
    }
    .client-select-main { flex: 1 1 auto; min-width: 0; display: grid; gap: 2px; }
    .client-select-main strong { font-size: 14px; }
    .client-select-main small { font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .client-select-meta {
      flex: 0 0 auto; display: grid; gap: 2px; text-align: right;
      font-size: 12px; color: var(--muted);
    }
    .client-select-sum { font-weight: 700; color: var(--text-success); font-variant-numeric: tabular-nums; }
    @media (max-width: 560px) {
      .client-select-meta { font-size: 11px; }
    }

    /* ── КЛИЕНТЫ СПИСКОМ ─────────────────────────── */
    .client-list-row {
      display: flex; align-items: center; gap: 12px; padding: 11px 16px;
      border-bottom: 1px solid var(--line); cursor: pointer; transition: background var(--dur-1);
    }
    .client-list-row:last-child { border-bottom: none; }
    /* Шапка таблицы клиентов: те же классы колонок, что у строк, поэтому ширины
       совпадают сами. Не кликается и не подсвечивается — это подпись, а не
       строка данных. */
    .client-list-head {
      cursor: default; background: var(--panel2);
      font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
      color: var(--muted);
    }
    .client-list-head:hover { background: var(--panel2); }
    .client-list-head .client-list-name,
    .client-list-head .client-list-company,
    .client-list-head .client-list-deals,
    .client-list-head .client-list-paid,
    .client-list-head .client-list-debt {
      font-size: 11px; font-weight: 800; color: var(--muted);
    }
    .client-list-row:hover { background: var(--panel2); }
    .client-list-row .status-pill { flex: 0 0 90px; text-align: center; justify-content: center; }
    .client-list-name { font-size: 14px; font-weight: 600; flex: 1 1 150px; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .client-list-company { font-size: 12px; color: var(--muted); flex: 0 0 150px; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .client-list-deals { font-size: 12px; color: var(--muted); flex: 0 0 84px; text-align: right; }
    .client-list-paid { font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; flex: 0 0 100px; text-align: right; color: var(--text-success); }
    .client-list-debt { font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; flex: 0 0 100px; text-align: right; color: var(--text-warning); }
    /* Ширину держим постоянной, даже когда кнопки нет: иначе строка без сделок
       растягивается и колонки соседних строк разъезжаются по горизонтали. */
    .client-list-actions { flex: 0 0 112px; display: flex; gap: 6px; justify-content: flex-end; }
    @media (max-width: 900px) {
      .client-list-company { display: none; }
    }
    /* На узком экране смысл списка — плотность. Имя и статус держим в одной строке,
       деньги переносим во вторую и только если они есть; пустые ячейки схлопываются
       (flex: 0 0 auto), поэтому клиент без сделок занимает ровно одну строку. */
    @media (max-width: 680px) {
      .client-list-row { flex-wrap: wrap; gap: 2px 10px; padding: 10px 14px; }
      .client-list-name { order: 1; flex: 1 1 auto; }
      .client-list-row .status-pill { order: 2; flex: 0 0 auto; font-size: 11px; }
      .client-list-deals { order: 3; flex: 0 0 auto; text-align: left; }
      .client-list-paid, .client-list-debt { order: 4; flex: 0 0 auto; text-align: left; font-size: 12px; }
      .client-list-actions { order: 5; flex: 0 0 auto; margin-left: auto; }
    }

    /* ── DEAL VIEW ───────────────────────────────── */
    .deal-bar {
      padding: 16px 18px 0;
      background: var(--panel);
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      box-shadow: var(--shadow);
      margin-bottom: 14px;
    }
    .deal-bar-top {
      display: flex; justify-content: space-between;
      align-items: flex-start; gap: 16px; margin-bottom: 14px;
    }
    .deal-bar-title h2 { margin: 0; font-size: 20px; }
    .deal-bar-title p { margin: 4px 0 0; font-size: 13px; }
    .deal-bar-meta {
      display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
    }
    .deal-stats {
      display: flex; gap: 14px; align-items: center;
    }
    .deal-stat { text-align: right; }
    .deal-stat span { display: block; font-size: 12px; color: var(--muted); }
    .deal-stat strong { font-size: 17px; font-weight: 900; }
    .deal-pay-bar {
      height: 4px; background: var(--line); border-radius: var(--r-pill);
      overflow: hidden; margin-top: 1px; width: 100%;
    }
    .deal-pay-fill {
      height: 100%; border-radius: var(--r-pill);
      background: var(--green);
      transition: width var(--dur-3) var(--ease-out);
    }
    /* Ряд «вкладки + кнопка действий». Кнопка ОБЯЗАНА жить вне .deal-tabs: там
       overflow-x: auto, а он обрезает выпадающее меню — оно просто не показалось бы
       целиком. Заодно кнопка не уезжает вместе с прокруткой вкладок и всегда под
       рукой. Отрицательные поля переехали сюда с .deal-tabs, чтобы вкладки
       по-прежнему уходили под края шапки, а кнопка стояла внутри её отступов. */
    .deal-tabs-row {
      display: flex; align-items: stretch; gap: 4px;
      margin: 0 -18px 0 -18px; padding-right: 18px;
      border-top: 1px solid var(--line);
    }
    .deal-tabs-row > .deal-tabs { flex: 1 1 auto; min-width: 0; margin: 0; border-top: none; }
    .deal-tabs-menu { flex: 0 0 auto; display: flex; align-items: center; padding-top: 2px; }
    .deal-tabs {
      display: flex; gap: 4px; border-top: 1px solid var(--line);
      padding-top: 2px; margin: 0 -18px; padding-left: 18px;
      overflow-x: auto; scrollbar-width: none;
      /* Fade-подсказка на краях — намёк, что табы можно проскроллить дальше */
      -webkit-mask-image: linear-gradient(to right, transparent 0, black 18px, black calc(100% - 18px), transparent 100%);
      mask-image: linear-gradient(to right, transparent 0, black 18px, black calc(100% - 18px), transparent 100%);
    }
    .deal-tabs::-webkit-scrollbar { display: none; }
    /* Кнопка-триггер вместо ленты вкладок. На десктопе не нужна — там лента
       помещается целиком; включается медиазапросом ниже. */
    .deal-tabs-trigger { display: none; }
    .deal-tab {
      border: none; background: transparent;
      color: var(--muted); font-size: 13px; font-weight: 600;
      padding: 10px 14px; cursor: pointer; white-space: nowrap;
      border-bottom: 2px solid transparent;
      transition: var(--dur-1); border-radius: 0;
    }
    .deal-tab:hover { color: var(--text); }
    .deal-tab.active {
      color: var(--primary-text);
      border-bottom-color: var(--primary-text);
    }

    /* ── CRM HOME ─────────────────────────────────── */
    /* ── DASHBOARD ───────────────────────────────── */
    .db-header {
      display: flex; align-items: flex-start; justify-content: space-between;
      gap: 12px; flex-wrap: wrap; margin-bottom: 16px; padding: 0 4px;
    }
    .db-greeting { margin: 0 0 4px; font-size: 22px; font-weight: 900; letter-spacing: -.02em; }
    .db-date { margin: 0; font-size: 13px; color: var(--muted); }
    /* Дата словами справа — «воскресенье, 28 сентября». Прописная первая
       буква через ::first-letter: toLocaleDateString отдаёт день недели
       строчным. */
    .db-header-date {
      font-size: 13px; font-weight: 600; color: var(--muted);
      padding-top: 8px; white-space: nowrap;
    }
    .db-header-date::first-letter { text-transform: uppercase; }
    /* «Сегодня»: что горит. Чипы — кнопки: каждый ведёт туда, где это делают. */
    .db-today { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
    .db-today-chip {
      display: inline-flex; align-items: center; gap: 7px;
      padding: 6px 12px 6px 10px; min-height: 32px;
      border-radius: var(--r-pill); border: 1px solid var(--line);
      background: var(--panel); color: var(--text);
      font: inherit; font-size: 13px; cursor: pointer;
      transition: border-color var(--dur-2) var(--ease-out), background var(--dur-2) var(--ease-out),
        transform var(--dur-2) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
    }
    .db-today-chip svg { color: var(--primary-text); flex: 0 0 auto; }
    .db-today-chip b { font-weight: 700; font-variant-numeric: tabular-nums; }
    .db-today-chip:hover {
      border-color: rgb(var(--primary-rgb) / .55);
      background: rgb(var(--primary-rgb) / .08);
      transform: translateY(-1px);
      box-shadow: 0 6px 18px -8px rgb(var(--primary-rgb) / .55);
    }
    .db-today-chip:active { transform: none; }
    .db-today-chip.is-deadline {
      border-color: color-mix(in srgb, var(--red) 35%, transparent);
      background: color-mix(in srgb, var(--red) 8%, transparent);
    }
    .db-today-chip.is-deadline svg { color: var(--text-danger); }
    .db-today-chip.is-deadline:hover {
      border-color: color-mix(in srgb, var(--red) 60%, transparent);
      box-shadow: 0 6px 18px -8px color-mix(in srgb, var(--red) 60%, transparent);
    }
    .db-today-chip.is-overdue svg { color: var(--text-warning); }
    .db-today-calm {
      display: inline-flex; align-items: center; gap: 6px;
      font-size: 13px; color: var(--muted);
    }
    .db-today-calm svg { color: var(--text-success); }
    @media (hover: none) and (pointer: coarse) { .db-today-chip { min-height: 44px; } }
    @media (max-width: 640px) { .db-header-date { display: none; } }

    .db-kpi-grid {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: 10px; margin-bottom: 14px;
    }
    .db-kpi {
      display: flex; align-items: center; gap: 12px;
      background: var(--panel2); border: 1px solid var(--line);
      border-radius: var(--r-xl); padding: 14px; transition: var(--dur-1);
    }
    .db-kpi:hover { border-color: rgb(var(--primary-rgb) / .35); }
    .db-kpi-warn { border-color: rgba(234,88,12,.3) !important; }
    .db-kpi-icon {
      width: 40px; height: 40px; border-radius: var(--r-lg); flex-shrink: 0;
      display: flex; align-items: center; justify-content: center; font-size: 18px;
    }
    .db-kpi-body { flex: 1; min-width: 0; }
    .db-kpi-label { font-size: 12px; color: var(--muted); font-weight: 600; text-transform: uppercase; letter-spacing: .04em; }
    .db-kpi-value { font-size: 20px; font-weight: 900; letter-spacing: -.02em; margin: 2px 0; font-variant-numeric: tabular-nums; }
    .db-kpi-delta { font-size: 12px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .db-kpi-delta.pos { color: var(--text-success); }
    .db-kpi-delta.neg { color: var(--text-danger); }
    .db-kpi-delta.neu { color: var(--muted); }

    @media (max-width: 900px) {
      .db-kpi-grid { grid-template-columns: repeat(2, 1fr); }
    }
    @media (max-width: 480px) {
      .db-kpi-grid { grid-template-columns: 1fr 1fr; gap: 8px; }
      .db-kpi { padding: 10px; gap: 8px; }
      .db-kpi-icon { width: 32px; height: 32px; font-size: 14px; border-radius: var(--r-sm); }
      .db-kpi-value { font-size: 16px; }
      .db-kpi-label { font-size: 12px; }
      .db-greeting { font-size: 18px; }
    }

    /* ── Dashboard stat card grid ──────────────────────────── */
    /* ── Compact stat strip (горизонтальная полоса метрик) ─── */
    /* Сетка, а не flex-wrap: при переносе flex растягивал оставшиеся плитки на всю
       ширину, и «Прогноз 30 дн» во втором ряду выходил вчетверо шире соседей по
       первому. auto-fill (не auto-fit) — чтобы пустые колонки второго ряда не
       схлопывались и плитки во всех рядах были одной ширины. */
    .db-stat-row {
      display: grid;
      /* 150px — измеренный минимум плитки: самая длинная подпись 96px + 26px на
         иконку с зазором + 28px внутренних отступов. Плиток девять, то есть один
         ряд физически возможен только от ~1400px контейнера; ниже раскладка
         переносится, и это правильнее обрезанных подписей. */
      /* auto-fit, а не auto-fill. Плиток девять; auto-fill на экране от ~1600px
         создаёт ДЕСЯТЬ дорожек и оставляет последнюю пустой — справа от
         «Дедлайны / 7 дн» зияла дыра в целую плитку (владелец на неё и указал).
         auto-fit схлопывает лишнюю дорожку и отдаёт место плиткам: 152 → 169px.

         В v214 от auto-fit отказались, опасаясь, что одинокая плитка последнего
         ряда растянется на всю ширину. Проверено замером на семи ширинах: этого
         не происходит — при переносе все дорожки заняты первым рядом и схлопывать
         нечего, одинокая плитка занимает ровно одну дорожку (1380px: 168px, как и
         при auto-fill). На 560px auto-fit даже лучше: три колонки вместо двух. */
      /* ПЕРЕСЧИТАНО: плиток стало ДЕСЯТЬ, а не девять — десятую добавили позже, и
         auto-fit снова начал оставлять дыру. Замер: на 1440px ряды 9+1 и 1251px
         пустоты во второй строке, на 1280px — 8+2 и 935px, на 1024px — 6+4 и 331px.
         Ровно тот случай, на который владелец указывал в финансах.

         Десять делится нацело только на 2 и 5, поэтому колонок ровно столько: пять
         на широком экране (два ряда по пять), два на узком (пять рядов по два).
         Промежуточные 3 и 4 дают 3+3+3+1 и 4+4+2 — то есть ту же дыру.

         Порог берём с САМОЙ СЕТКИ через container query, а не с окна: ширина панели
         от ширины окна не зависит (см. финансы — тот же приём и та же причина).
         790px = 5×150px минимума плитки плюс внутренние отступы. */
      container-type: inline-size;
      /* Ширину задают ДЕТИ через span, а не сама сетка через template-columns:
         элемент не может опрашивать себя как контейнер — container query работает
         только для потомков. Ровно этот промах у меня и вышел с первого раза:
         правило на .db-stat-row внутри @container не срабатывало никогда, и на
         1440px оставалось два столбца вместо пяти. */
      grid-template-columns: repeat(10, 1fr);
      background: var(--panel2); border: 1px solid var(--line);
      border-radius: var(--r-xl); margin-bottom: 14px; overflow: hidden;
    }
    .db-stat-row > .db-stat { grid-column: span 5; }
    @container (min-width: 790px) {
      .db-stat-row > .db-stat { grid-column: span 2; }
    }
    .db-stat {
      /* 150px, а не 110: самая длинная подпись («Дедлайны / 7 дн») требует 96px,
         плюс 20px иконки, 6px зазора и 28px внутренних отступов. На 110px восемь
         плиток вставали в один ряд и ШЕСТЬ подписей из восьми резались многоточием
         (замер пробником на 1180px). Теперь ряд переносится, а не обрезается. */
      flex: 1; min-width: 150px;
      padding: 10px 14px;
      transition: background var(--dur-1) var(--ease-out);

      /* Разделители — ВНЕШНЕЙ тенью слева и сверху, а не рамкой.
         Замер (04.08): полоса переносится уже с 1280px, а на 390px это ПЯТЬ рядов
         вплотную — горизонтальной линии не было ни одной, ряды сливались в стену
         цифр. Прежняя `border-right` рисовала только вертикали и вдобавок оставляла
         висячую черту на правом краю переносимого ряда.

         Почему тень, а не `gap: 1px` с подложкой цвета линии: сетка auto-fill
         оставляет в последнем ряду ПУСТЫЕ дорожки (на 1280px — семь), и подложка
         проступила бы в них серым блоком. Тень же рисуется только у реальных
         плиток. А `overflow:hidden` контейнера срезает её у первого ряда и первого
         столбца — линии остаются строго между плитками, по краям их нет. */
      --stat-sep: -1px 0 0 var(--line), 0 -1px 0 var(--line);
      box-shadow: var(--stat-sep);
    }
    /* Акцентная плитка. Подложка-градиент вместо ровной заливки и чуть более
       крупное число: полоса из десяти одинаковых плиток не давала глазу точки
       входа. Разделители (--stat-sep) сохраняем — без них у плитки пропадали бы
       линии, которыми она отделена от соседей. */
    /* Подложка ТИХАЯ (--grad-soft, а не --grad-hero). Первая версия ставила сюда
       hero-градиент, и плотный фиолет съедал зелёную сумму прибыли: цвет числа
       здесь несёт смысл (доход/убыток), спорить с ним подложке нельзя. Проверка
       контраста этого НЕ поймала бы — она читает background-color, а градиент
       живёт в background-image. */
    .db-stat--hero {
      background-image: var(--grad-soft);
      box-shadow: var(--stat-sep), inset 0 0 0 1px rgb(var(--primary-rgb) / .28);
    }
    .db-stat--hero .db-stat-value { font-size: 19px; }
    .db-stat--hero[onclick]:hover {
      background-image: var(--grad-soft);
      box-shadow: var(--stat-sep), inset 0 0 0 1px rgb(var(--primary-rgb) / .45), inset 0 -2px 0 var(--primary);
    }
    .db-stat[onclick] { cursor: pointer; }
    /* Плитки ведут в раздел, но выглядели как обычный текст: единственным намёком
       был курсор, которого на телефоне нет. Подчёркивание акцентом + подпись в
       полный цвет — сигнал «сюда можно нажать», не занимающий места. */
    /* Наведение ДОБАВЛЯЕТ подчёркивание к разделителям, а не заменяет их: без
       var(--stat-sep) в этих правилах линии вокруг плитки пропадали под курсором. */
    .db-stat[onclick]:hover {
      background-color: var(--panel);
      /* Лёгкая акцентная подложка сверху вниз вместо ровной заливки: плитка под
         курсором «подсвечивается», а не просто меняет серый на серый. */
      background-image: var(--grad-soft);
      box-shadow: var(--stat-sep), inset 0 -2px 0 var(--primary);
    }
    .db-stat[onclick]:hover .db-stat-label { color: var(--text); }
    .db-stat[onclick]:active { background: var(--panel2); }
    .db-stat-warn { background: rgba(234,88,12,.05); }
    /* Иконка + подпись в одну строку (раньше иконка — своим рядом сверху, подпись
       КАПСОМ под ней — на 9 плитках в ряд читалось как "стена из капса"). Иконка
       меньше и служит просто маркером перед подписью, не отдельным акцентом. */
    .db-stat-top { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; min-width: 0; }
    /* 26/14 — домашний размер значка-бейджа: столько по умолчанию даёт iconBadge()
       и таким он стоит по всему приложению. Плитка KPI была единственным местом
       мимо стандарта: кружок 20 со значком 11px, то есть символ МЕЛЬЧЕ соседней
       подписи (12px) — на таком размере он читается уже не как знак, а как цветное
       пятнышко. Полоса из-за этого выросла на 12px на две строки: приемлемая цена
       за то, чтобы значок работал знаком. */
    .db-stat-icon {
      width: 26px; height: 26px; border-radius: var(--r-sm);
      display: flex; align-items: center; justify-content: center;
      flex: 0 0 auto;
    }
    .db-stat-icon svg { width: 14px; height: 14px; display: block; }
    .db-stat-label {
      font-size: 12px; color: var(--muted); font-weight: 600;
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
    }
    .db-stat-value {
      font-size: 16.5px; font-weight: 800; letter-spacing: -.01em;
      font-variant-numeric: tabular-nums; line-height: 1.2; margin-bottom: 2px;
    }
    /* Сумма слева, мини-график справа. Ряд, а не два этажа: плитка и так низкая,
       а форма нужна ГЛАЗУ рядом с числом, а не под ним. График сжимается первым
       (flex: 0 1 auto) и пропадает на узкой плитке — число важнее формы. */
    .db-stat-value-row {
      display: flex; align-items: center; justify-content: space-between;
      gap: 8px; margin-bottom: 2px; min-width: 0;
    }
    /* Строка значения — одной высоты у ВСЕХ плиток, с мини-графиком и без.
       Иначе ряд плиток читался лесенкой: где справа спарклайн (26px) или
       полукруг, число и подпись под ним стояли ниже, чем у соседа без графика
       (скриншот владельца 28.09: «↑ 290%» на 144px, «по активным сделкам» на
       138px в одном ряду). */
    .db-stat-value-row,
    .db-stat > .db-stat-value { min-height: 28px; }
    .db-stat > .db-stat-value { display: flex; align-items: center; }
    .db-stat-value-row .db-stat-value { margin-bottom: 0; min-width: 0; }
    .db-stat-value-row .spark { flex: 0 1 auto; min-width: 0; opacity: .9; }
    .db-stat[onclick]:hover .spark { opacity: 1; }
    /* Ниже 1100px в плитке остаётся ~120px под содержимое: график и сумма
       начинают спорить за место, и первым уходит график. */
    @media (max-width: 1100px) { .db-stat-value-row .spark { display: none; } }
    /* tabular-nums: в подписи плитки бывают суммы («20 750 ₽ из 41 500 ₽»), а
       DESIGN.md §5 требует табличных цифр везде, где деньги. Сторож в a11y это
       и поймал на новой плитке «Собираемость». */
    .db-stat-delta { font-size: 11.5px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-variant-numeric: tabular-nums; }
    .db-stat-delta.pos { color: var(--text-success); }
    .db-stat-delta.neg { color: var(--text-danger); }
    .db-stat-delta.neu { color: var(--muted); }
    .db-stat[onclick]:hover { box-shadow: var(--stat-sep), inset 0 0 0 1px rgb(var(--primary-rgb) / .25); }

    @media (max-width: 900px) { .db-stat { min-width: 45%; padding: 9px 12px; } .db-stat-value { font-size: 15px; } }
    @media (max-width: 480px) { .db-stat { min-width: 50%; } }

    /* ── Analytics 2-column ───────────────────────────────── */
    .db-analytics-header {
      display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center;
      gap: 6px 0; margin-bottom: 8px;
    }
    .db-analytics-legend { display: flex; gap: 12px; font-size: 12px; }
    .db-analytics-body { display: block; }

    /* ── Деньги по месяцам + топ клиентов: ДВЕ панели рядом ──────────────────
       Раньше это была одна коробка с вертикальной чертой посередине: правая
       половина шла без заголовка, легенда графика жалась в верхний угол над ней,
       и блок читался кучей. Правая колонка фиксированной ширины — список имён с
       суммами не выигрывает от растягивания, а график выигрывает. Порог 1040px:
       ниже него на график остаётся меньше 620px, и столбцы месяцев начинают
       сливаться — тогда панели встают друг под друга. */
    /* stretch, а не start: панели заканчивались на разной высоте, и ряд выглядел
       собранным на скорую руку. Теперь обе тянутся до нижней кромки ряда, а
       содержимое внутри распределяется своими подвалами. */
    .db-analytics-row {
      display: grid; grid-template-columns: minmax(0, 1fr) 300px;
      gap: 14px; margin-bottom: 14px; align-items: stretch;
    }
    .db-money-panel { display: flex; flex-direction: column; }
    /* График занимает всё, что осталось между шапкой и подвалом. */
    .db-money-panel .db-analytics-body { flex: 1 1 auto; display: flex; align-items: center; }
    .db-money-panel .db-analytics-body > svg { width: 100%; }
    .db-money-foot {
      display: flex; align-items: center; justify-content: space-between; gap: 10px;
      margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line);
    }
    @media (max-width: 1040px) { .db-analytics-row { grid-template-columns: 1fr; } }
    .db-money-panel, .db-clients-panel { margin-bottom: 0; }
    .db-clients-panel { display: flex; flex-direction: column; }
    /* Значок у заголовка раздела. Тот же квадрат с акцентным тинтом, что и у
       панелей аналитики, — чтобы значки на странице читались как одна семья, а
       не как случайные картинки. inline-flex и выравнивание по центру: без них
       svg садится на базовую линию и заголовок «подпрыгивает». */
    /* Заголовок со значком — флекс по БАЗОВОЙ линии, а значку отдельно центр.
       Текст и счётчик рядом («Клиенты 18») остаются на общей строке, как в
       обычном тексте, а квадрат значка совмещается с серединой строки.

       Замер 13.09.2026 по трём скриншотам владельца: при «vertical-align: -6px»
       значок стоял на 9px НИЖЕ центра текста — и одинаково во всех девяти
       разделах. Подгонка числом обречена: inline-flex ставит на базовую линию
       свой нижний край, поэтому каждый новый кегль заголовка ломает её заново.
       Общий флекс по центру чинил значок, но поднимал счётчик на 10px над
       строкой; этот вариант даёт 1px по значку и −5px по счётчику, то есть оба
       элемента там, где им место. */
    h1:has(> .h1-ico) { display: flex; align-items: baseline; flex-wrap: wrap; }
    h1:has(> .h1-ico) > .h1-ico { align-self: center; }
    .h1-ico {
      width: 28px; height: 28px; border-radius: var(--r-sm); flex: 0 0 auto;
      background: var(--primary-bg); color: var(--primary-text);
      display: inline-flex; align-items: center; justify-content: center;
      margin-right: 10px;
    }
    @media (max-width: 640px) {
      .h1-ico { width: 24px; height: 24px; margin-right: 8px; }
    }

    .db-panel-ico {
      width: 22px; height: 22px; border-radius: var(--r-sm); flex: 0 0 auto;
      background: var(--primary-bg); color: var(--primary-text);
      display: inline-flex; align-items: center; justify-content: center;
    }
    .db-analytics-range { font-size: 12px; color: var(--muted); white-space: nowrap; }
    /* Шапка обеих панелей ряда — одной высоты (по капсулам навигации) и с
       воздухом под собой. Навигация периода стояла вплотную к суммам (8px),
       и «В среднем / мес» читалось подписью к переключателю (владелец 28.09). */
    .db-panel-title { font-size: 15px; font-weight: 700; letter-spacing: -.01em; white-space: nowrap; }
    .db-money-panel > .db-analytics-header,
    .db-clients-panel > .db-analytics-header { min-height: 34px; margin-bottom: 18px; }

    /* Три числа периода подписанной строкой. Прежняя легенда «▋ сумма ▋ сумма
       = сумма» объясняла цвет столбцов, но не говорила, что это за суммы. */
    /* Три суммы жались к левому краю и занимали треть ширины — оставшиеся две
       трети над графиком пустовали, и шапка панели выглядела оборванной. Теперь
       это ряд на всю ширину с разделителями: он держит верх панели и повторяет
       ритм колонок графика под ним. */
    .db-money-sums {
      display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
      margin: 4px 0 6px; border-bottom: 1px solid var(--line); padding-bottom: 14px;
    }
    .db-money-sum {
      display: flex; flex-direction: column; gap: 3px; min-width: 0;
      padding-left: 18px; border-left: 1px solid var(--line);
    }
    .db-money-sum:first-child { padding-left: 0; border-left: none; }
    /* Ниже 1100px четыре колонки дают по 130px на число с длинной подписью —
       «В среднем / мес» переносится и ломает ряд. Тогда два по два. */
    @media (max-width: 1100px) {
      .db-money-sums { grid-template-columns: 1fr 1fr; gap: 10px 0; }
      .db-money-sum:nth-child(3) { padding-left: 0; border-left: none; }
    }
    .db-money-sum b {
      /* 18px, не 22: над панелью стоят плитки главной (16,5–19px), и суммы
         второстепенного блока были громче главного ряда (владелец 01.10.2026). */
      font-family: var(--font-display); font-size: 18px; font-weight: 700; line-height: 1.15;
      font-variant-numeric: tabular-nums; letter-spacing: -.01em; white-space: nowrap;
    }
    /* Подпись — обычным регистром (владелец 30.09.2026: заглавные «ДОХОД / РАСХОД»
       кричали и спорили с числами). */
    .db-money-lbl { font-size: 12.5px; color: var(--muted); font-weight: 600; }
    .db-money-trend { display: flex; align-items: center; gap: 6px; min-height: 18px; margin-top: 2px; font-size: 11.5px; color: var(--muted); }
    .db-money-trend .db-delta { margin-left: 0; }
    .db-delta-note { font-size: 11.5px; color: var(--muted); white-space: nowrap; }
    /* Маржа под прибылью: мини-шкала доли дохода, оставшейся после расходов. */
    .db-money-margin {
      display: inline-flex; align-items: center; gap: 7px; margin-top: 3px;
      font-size: 11.5px; font-weight: 600; color: var(--muted);
      font-variant-numeric: tabular-nums; white-space: nowrap;
    }
    .db-money-margin-track {
      width: 54px; height: 4px; border-radius: var(--r-pill);
      background: color-mix(in srgb, var(--red) 30%, transparent); overflow: hidden; flex: 0 0 auto;
    }
    .db-money-margin-fill {
      display: block; height: 100%; border-radius: var(--r-pill); background: var(--green);
      transform-origin: left center;
    }
    /* Наведение на сумму гасит столбцы ДРУГОЙ серии — видно, как эта сумма
       разложена по месяцам. Сама ячейка суммы при этом не меняется: фон под
       курсором читался коробкой вокруг числа (владелец 28.09: «было лучше»). */
    .db-bar, .db-chart-col text.is-rev, .db-chart-col text.is-exp { transition: opacity var(--dur-3) var(--ease-out); }
    .db-money-panel:has(.db-money-sum[data-series="rev"]:hover) .db-bar.is-exp,
    .db-money-panel:has(.db-money-sum[data-series="rev"]:hover) .db-chart-col text.is-exp,
    .db-money-panel:has(.db-money-sum[data-series="exp"]:hover) .db-bar.is-rev,
    .db-money-panel:has(.db-money-sum[data-series="exp"]:hover) .db-chart-col text.is-rev { opacity: .14; }

    /* Кольцо с суммой периода по центру. Размер кольца — часть смысла, поэтому
       оно не сжимается; цифра внутри абсолютным слоем, чтобы не съезжать. */
    .db-clients-donut {
      position: relative; display: flex; align-items: center; justify-content: center;
      padding: 4px 0 12px;
    }
    .db-clients-donut-center {
      position: absolute; inset: 0; display: flex; flex-direction: column;
      align-items: center; justify-content: center; gap: 1px; pointer-events: none;
      padding-bottom: 8px;
    }
    .db-clients-donut-center b { font-size: 14px; font-variant-numeric: tabular-nums; }
    /* Наведение на клиента в списке: его сегмент кольца толще и ярче,
       остальные гаснут, в центре — его доля и имя вместо общего дохода.
       Правила по номеру сегмента (data-seg) — сопоставить строку и дугу
       одним CSS можно только так; клиентов в топе не больше пяти плюс
       «Остальные». */
    .donut-seg { transition: opacity var(--dur-3) var(--ease-out), stroke-width var(--dur-3) var(--ease-out); }
    /* Утолщённый сегмент выходит за рамку кольца на пару пикселей — не резать. */
    .db-clients-donut .donut { overflow: visible; }
    .db-clients-donut-center { transition: opacity var(--dur-2) var(--ease-out), transform var(--dur-3) var(--ease-out); }
    .db-clients-donut-center.is-alt { opacity: 0; transform: scale(.94); }
    .db-clients-donut-name {
      max-width: 92px; font-size: 12.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .db-clients-panel:has(.db-client-row[data-seg]:hover) .db-clients-donut-center[data-seg="all"] { opacity: 0; transform: scale(.94); }
    .db-clients-panel:has(.db-client-row[data-seg="0"]:hover) .donut-seg:not([data-seg="0"]),
    .db-clients-panel:has(.db-client-row[data-seg="1"]:hover) .donut-seg:not([data-seg="1"]),
    .db-clients-panel:has(.db-client-row[data-seg="2"]:hover) .donut-seg:not([data-seg="2"]),
    .db-clients-panel:has(.db-client-row[data-seg="3"]:hover) .donut-seg:not([data-seg="3"]),
    .db-clients-panel:has(.db-client-row[data-seg="4"]:hover) .donut-seg:not([data-seg="4"]),
    .db-clients-panel:has(.db-client-row[data-seg="5"]:hover) .donut-seg:not([data-seg="5"]),
    .db-clients-panel:has(.db-client-row[data-seg="6"]:hover) .donut-seg:not([data-seg="6"]) { opacity: .18; }
    .db-clients-panel:has(.db-client-row[data-seg="0"]:hover) .donut-seg[data-seg="0"],
    .db-clients-panel:has(.db-client-row[data-seg="1"]:hover) .donut-seg[data-seg="1"],
    .db-clients-panel:has(.db-client-row[data-seg="2"]:hover) .donut-seg[data-seg="2"],
    .db-clients-panel:has(.db-client-row[data-seg="3"]:hover) .donut-seg[data-seg="3"],
    .db-clients-panel:has(.db-client-row[data-seg="4"]:hover) .donut-seg[data-seg="4"],
    .db-clients-panel:has(.db-client-row[data-seg="5"]:hover) .donut-seg[data-seg="5"],
    .db-clients-panel:has(.db-client-row[data-seg="6"]:hover) .donut-seg[data-seg="6"] { stroke-width: 19; }
    .db-clients-panel:has(.db-client-row[data-seg="0"]:hover) .db-clients-donut-center[data-seg="0"],
    .db-clients-panel:has(.db-client-row[data-seg="1"]:hover) .db-clients-donut-center[data-seg="1"],
    .db-clients-panel:has(.db-client-row[data-seg="2"]:hover) .db-clients-donut-center[data-seg="2"],
    .db-clients-panel:has(.db-client-row[data-seg="3"]:hover) .db-clients-donut-center[data-seg="3"],
    .db-clients-panel:has(.db-client-row[data-seg="4"]:hover) .db-clients-donut-center[data-seg="4"],
    .db-clients-panel:has(.db-client-row[data-seg="5"]:hover) .db-clients-donut-center[data-seg="5"],
    .db-clients-panel:has(.db-client-row[data-seg="6"]:hover) .db-clients-donut-center[data-seg="6"] { opacity: 1; transform: none; }

    .db-clients-list { display: flex; flex-direction: column; gap: 2px; }
    /* Строка ведёт в карточку клиента. До этого справа не нажималось ничего:
       список выглядел итогом, хотя за каждым именем стоят сделки. */
    .db-client-row {
      display: flex; align-items: center; gap: 8px; min-width: 0; width: 100%;
      padding: 6px 8px; border: none; background: transparent; color: inherit;
      font: inherit; text-align: left; border-radius: var(--r-sm);
    }
    .db-client-row.is-clickable { cursor: pointer; transition: background var(--dur-1); }
    /* На пальце строка должна быть не ниже 44px. Расширенная зона через ::after
       здесь НЕ годится: строки идут вплотную, и невидимые области перекрыли бы
       соседей — попадёшь в одного клиента, откроется другой. Растим саму строку. */
    @media (hover: none) and (pointer: coarse) {
      .db-client-row { min-height: var(--h-lg); padding: 8px; }
    }
    .db-client-row.is-clickable:hover { background: var(--primary-bg); }
    .db-client-row.is-clickable:active { background: var(--panel2); }
    .db-top-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
    .db-top-name {
      font-size: 12.5px; min-width: 0; flex: 1 1 auto;
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .db-client-share {
      font-size: 11.5px; color: var(--muted); font-variant-numeric: tabular-nums;
      flex: 0 0 auto; min-width: 30px; text-align: right;
    }
    .db-top-sum { font-size: 12.5px; font-weight: 700; font-variant-numeric: tabular-nums; flex: 0 0 auto; }
    /* «Остальные» — служебная строка, не клиент: приглушена и не притворяется
       нажимаемой. */
    .db-client-row--rest .db-top-name,
    .db-client-row--rest .db-top-sum { color: var(--muted); }

    .db-clients-foot {
      display: flex; align-items: center; justify-content: space-between; gap: 10px;
      margin-top: auto; padding-top: 10px; border-top: 1px solid var(--line);
    }
    /* Подпись подвала — обычным регистром, не капсом: «И ЕЩЁ 1 КЛИЕНТ» кричит
       ровно там, где сообщается мелочь. Капс оставлен подписям метрик. */
    .db-clients-foot-note { font-size: 11.5px; color: var(--muted); }

    /* Сводка вместо графика на молодом аккаунте: шесть колонок, из которых пять
       пустых, ничего не показывают и занимают 230px. */
    .db-analytics-early { padding: 4px 2px 2px; }
    .db-analytics-early-nums {
      display: flex; flex-wrap: wrap; gap: 8px 28px;
    }
    .db-analytics-early-nums > div { display: flex; flex-direction: column; gap: 2px; }
    .db-analytics-early-nums b { font-size: 18px; font-variant-numeric: tabular-nums; }

    /* Навигация графика — справа одной группой: «Сейчас» (когда ушли назад),
       длина периода и капсула «‹ период ›». Прежние стрелки 22px стояли ПОСЛЕ
       метки периода и читались значками, а не управлением. Группа прижата
       вправо и растёт влево — появление «Сейчас» не двигает стрелки. */
    .db-chart-nav {
      display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap;
      gap: 8px; margin-left: auto;
    }
    .db-chart-span, .db-chart-pager {
      display: inline-flex; align-items: center; gap: 2px; padding: 2px;
      border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--panel2);
    }
    .db-chart-span-btn {
      height: 26px; padding: 0 10px; border: 0; border-radius: var(--r-pill);
      background: transparent; color: var(--muted); cursor: pointer;
      font: inherit; font-size: 12px; font-weight: 650; white-space: nowrap;
      transition: background var(--dur-2) var(--ease-out), color var(--dur-2) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
    }
    .db-chart-span-btn:hover:not(.is-active) { color: var(--text); }
    .db-chart-span-btn.is-active { background: var(--panel); color: var(--text); box-shadow: var(--elev-1); cursor: default; }
    .db-chart-range {
      min-width: 128px; padding: 0 4px; text-align: center;
      font-size: 12.5px; font-weight: 650; font-variant-numeric: tabular-nums; white-space: nowrap;
    }
    .db-chart-nav-btn {
      width: 26px; height: 26px; border-radius: var(--r-pill); border: 0;
      background: transparent; color: var(--muted); cursor: pointer;
      display: flex; align-items: center; justify-content: center; flex: 0 0 auto;
      transition: background var(--dur-1), color var(--dur-1);
    }
    .db-chart-nav-btn svg { transition: transform var(--dur-2) var(--ease-out); }
    .db-chart-nav-btn.is-prev svg { transform: scaleX(-1); }
    .db-chart-nav-btn:hover:not(:disabled) { background: var(--primary-bg); color: var(--primary-text); }
    /* Шеврон подаётся в сторону листания — нажатие видно рукой. */
    .db-chart-nav-btn:hover:not(:disabled) svg { transform: translateX(2px); }
    .db-chart-nav-btn.is-prev:hover:not(:disabled) svg { transform: scaleX(-1) translateX(2px); }
    .db-chart-nav-btn:disabled { opacity: .3; cursor: default; }
    .db-chart-now {
      height: 32px; padding: 0 12px; border-radius: var(--r-pill);
      border: 1px solid rgb(var(--primary-rgb) / .45); background: var(--primary-bg); color: var(--primary-text);
      font: inherit; font-size: 12px; font-weight: 700; cursor: pointer;
      animation: dbNowIn var(--dur-3) var(--ease-spring);
      transition: border-color var(--dur-1), background var(--dur-1);
    }
    .db-chart-now:hover { border-color: rgb(var(--primary-rgb) / .8); }
    @keyframes dbNowIn { from { opacity: 0; transform: translateX(8px) scale(.9); } }
    /* На пальце: кнопки периода и «Сейчас» дотягивают зону касания до 44px
       по высоте на всю свою ширину (стрелки — в общем списке ниже). */
    @media (hover: none) and (pointer: coarse) {
      .db-chart-span-btn, .db-chart-now { position: relative; }
      .db-chart-span-btn::after, .db-chart-now::after {
        content: ""; position: absolute; left: 0; right: 0; top: 50%;
        height: 44px; transform: translateY(-50%);
      }
    }
    @media (max-width: 640px) {
      .db-money-panel .db-analytics-header { row-gap: 10px; }
      .db-money-panel .db-chart-nav { width: 100%; justify-content: flex-start; }
      .db-money-panel .db-chart-pager { flex: 1 1 auto; justify-content: space-between; }
      .db-chart-range { min-width: 0; }
      .db-chart-now { order: 1; }
    }

    /* ── Профиль ─────────────────────────────────────────────────────────
       Шапка: аватар с именем и карточка подписки в одном ряду; ниже
       карточки одной семьи (панель, рамка --line, скругление --r-xl,
       наведение — фиолетовое свечение, как у карточек на главной). */
    .pf-hero {
      display: grid; grid-template-columns: minmax(0, 1fr) 340px;
      gap: 16px; margin-bottom: 16px;
    }
    .pf-id {
      display: flex; align-items: center; gap: 22px; padding: 22px;
      border: 1px solid var(--line); border-radius: var(--r-xl);
      background: radial-gradient(120% 150% at 0% 0%, rgb(var(--primary-rgb) / .16), transparent 55%), var(--panel2);
    }
    .pf-avatar {
      position: relative; flex: 0 0 auto; width: 88px; height: 88px; border-radius: 50%;
      display: flex; align-items: center; justify-content: center; cursor: pointer;
      background: linear-gradient(135deg, var(--primary), color-mix(in srgb, var(--primary) 50%, var(--blue)));
      color: var(--on-color); font-family: var(--font-display); font-size: 34px; font-weight: 700;
      box-shadow: 0 0 0 4px var(--panel2), 0 0 0 5px rgb(var(--primary-rgb) / .45), 0 12px 30px -6px rgb(var(--primary-rgb) / .45);
      transition: box-shadow var(--dur-3) var(--ease-out), transform var(--dur-3) var(--ease-out);
    }
    .pf-avatar:hover { transform: scale(1.03); box-shadow: 0 0 0 4px var(--panel2), 0 0 0 6px rgb(var(--primary-rgb) / .7), 0 14px 36px -6px rgb(var(--primary-rgb) / .6); }
    .pf-avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; display: block; }
    /* С клавиатуры фокусируется сам аватар (tabindex, Enter/пробел открывают
       выбор файла), поле файла скрыто: Tab на невидимом — фокуса не видно. */
    .pf-avatar:focus-visible { outline: var(--ring-w) solid var(--ring); outline-offset: 6px; }
    .pf-avatar-edit {
      position: absolute; right: -2px; bottom: -2px; width: 30px; height: 30px; border-radius: 50%;
      display: flex; align-items: center; justify-content: center;
      background: var(--panel); border: 1px solid var(--line); color: var(--text); box-shadow: var(--elev-1);
      transition: transform var(--dur-2) var(--ease-spring), color var(--dur-2);
    }
    .pf-avatar:hover .pf-avatar-edit { transform: scale(1.12); color: var(--primary-text); }
    .pf-id-fields { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
    .pf-field { margin: 0; }
    .pf-field input { font-size: 16px; font-weight: 650; }
    .pf-email { display: flex; align-items: center; gap: 6px; min-width: 0; font-size: 13px; color: var(--muted); }
    .pf-email span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .pf-avatar-del { align-self: flex-start; }

    /* Карточка подписки: цвет — по состоянию (--pf-c), шкала — сколько
       осталось от периода. Скоро конец — оранжевая, истекла — красная. */
    .pf-plan {
      --pf-c: var(--primary);
      display: flex; flex-direction: column; gap: 10px; padding: 18px 20px;
      border: 1px solid color-mix(in srgb, var(--pf-c) 35%, var(--line)); border-radius: var(--r-xl);
      background: linear-gradient(160deg, color-mix(in srgb, var(--pf-c) 16%, transparent), transparent 70%), var(--panel2);
    }
    .pf-plan.is-active { --pf-c: var(--green); }
    .pf-plan.is-soon { --pf-c: var(--orange); }
    .pf-plan.is-expired { --pf-c: var(--red); }
    .pf-plan-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
    .pf-plan-chip {
      display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: var(--r-pill);
      background: color-mix(in srgb, var(--pf-c) 16%, transparent);
      color: color-mix(in srgb, var(--pf-c) 65%, var(--text)); font-size: 12.5px; font-weight: 700;
    }
    .pf-plan-days { font-size: 13px; color: var(--muted); white-space: nowrap; }
    .pf-plan-days b {
      font-family: var(--font-display); font-size: 26px; font-weight: 700; color: var(--text);
      font-variant-numeric: tabular-nums; margin-right: 2px;
    }
    .pf-plan-track {
      height: 6px; border-radius: var(--r-pill); overflow: hidden;
      background: color-mix(in srgb, var(--pf-c) 14%, var(--line));
    }
    .pf-plan-track > span {
      display: block; height: 100%; border-radius: var(--r-pill);
      background: linear-gradient(90deg, color-mix(in srgb, var(--pf-c) 55%, transparent), var(--pf-c));
    }
    .pf-plan-note { font-size: 12.5px; color: var(--muted); line-height: 1.45; }
    .pf-plan-btn { align-self: flex-start; margin-top: auto; }

    .pf-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; margin-bottom: 16px; }
    .pf-card {
      padding: 18px 20px; border: 1px solid var(--line); border-radius: var(--r-xl); background: var(--panel2);
      transition: border-color var(--dur-2) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
    }
    .pf-card:hover {
      border-color: rgb(var(--primary-rgb) / .35);
      box-shadow: 0 12px 32px -14px rgb(var(--primary-rgb) / .45);
    }
    .pf-ref { margin-bottom: 16px; }
    .pf-card-head { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
    .pf-card-head h2 { margin: 0; font-size: 15px; }
    .pf-card-head p { margin: 2px 0 0; font-size: 12.5px; color: var(--muted); }
    .pf-head-btn { margin-left: auto; flex: 0 0 auto; }
    .pf-card-actions { display: flex; flex-wrap: wrap; gap: 8px; }
    .pf-text { margin: 0 0 10px; font-size: 13px; line-height: 1.55; color: var(--muted); }
    .pf-copy { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
    .pf-copy code {
      flex: 1 1 auto; min-width: 0; padding: 8px 12px; font-size: 12px; color: var(--muted);
      border: 1px dashed var(--line); border-radius: var(--r-sm); background: var(--panel);
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .pf-copy .btn { flex: 0 0 auto; white-space: nowrap; }
    .pf-ico-blue, .pf-ico-green, .pf-ico-primary { display: inline-flex; }
    .pf-ico-blue { color: var(--blue); }
    .pf-ico-green { color: var(--text-success); }
    .pf-ico-primary { color: var(--primary-text); }
    .pf-online-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 10px; }
    .pf-online {
      display: inline-flex; align-items: center; gap: 6px; padding: 2px 10px; border-radius: var(--r-pill); font-size: 12px;
      background: color-mix(in srgb, var(--green) 12%, transparent);
      border: 1px solid color-mix(in srgb, var(--green) 30%, transparent);
    }
    .pf-online::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--green); }

    .pf-features { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
    .pf-feature {
      display: flex; align-items: flex-start; gap: 10px; padding: 12px;
      border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--panel);
      transition: transform var(--dur-2) var(--ease-out), border-color var(--dur-2);
    }
    .pf-feature:hover { transform: translateY(-2px); border-color: rgb(var(--primary-rgb) / .35); }
    .pf-feature-ico {
      width: 32px; height: 32px; flex: 0 0 auto; border-radius: var(--r-md);
      display: flex; align-items: center; justify-content: center;
      background: var(--primary-bg); color: var(--primary-text);
    }
    .pf-feature-title { font-size: 13px; font-weight: 700; margin-bottom: 2px; display: block; }
    /* Недоступное на тарифе — приглушённо, с тарифом, где оно есть; ведёт в «Тарифы». */
    .pf-feature.is-locked {
      width: 100%; text-align: left; font: inherit; color: inherit; cursor: pointer;
      background: transparent; border-style: dashed;
    }
    .pf-feature.is-locked .pf-feature-ico { background: var(--panel2); color: var(--muted); }
    .pf-feature.is-locked .pf-feature-title { color: var(--muted); }
    .pf-feature.is-locked:hover { border-color: rgb(var(--primary-rgb) / .45); }
    .pf-feature.is-locked:hover .pf-feature-need { color: var(--primary-text); }
    .pf-feature-need { display: block; font-weight: 600; }
    .pf-included-all {
      display: flex; align-items: flex-start; gap: 6px; margin: 12px 0 0;
      font-size: 12.5px; color: var(--muted); line-height: 1.5;
    }
    .pf-included-all svg { flex: 0 0 auto; margin-top: 2px; color: var(--text-success); }
    @media (max-width: 1100px) { .pf-features { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
    @media (max-width: 900px) {
      .pf-hero, .pf-grid { grid-template-columns: 1fr; }
    }
    @media (max-width: 560px) {
      .pf-id { flex-direction: column; align-items: stretch; text-align: left; padding: 18px; gap: 16px; }
      .pf-avatar { align-self: center; }
      .pf-copy { flex-wrap: wrap; }
      .pf-copy code { flex-basis: 100%; }
      /* Восемь пунктов в столбик — полтора экрана телефона. Две колонки
         без пояснений: название со значком и так говорит, что входит. */
      .pf-feature { align-items: center; padding: 10px; }
      .pf-feature .u-meta:not(.pf-feature-need) { display: none; }
      .pf-card-head { flex-wrap: wrap; }
      .pf-head-btn { margin-left: 0; }
    }
    @media (prefers-reduced-motion: reduce) {
      .pf-avatar, .pf-avatar-edit, .pf-feature { transition: none; }
      .pf-avatar:hover, .pf-feature:hover, .pf-avatar:hover .pf-avatar-edit { transform: none; }
    }

    /* ── Настройки ───────────────────────────────────────────────────────
       Вложенные панели всех вкладок — одной карточкой: рамка, --r-xl,
       фиолетовое свечение на наведении, заголовок 16px. Раньше заголовок
       внутри вкладки («Оплата аванса в КП») брал размер h2 по умолчанию и
       спорил с заголовком самой страницы. */
    .set-page > .seg-switch { margin-bottom: 18px; }
    .set-page .panel {
      border: 1px solid var(--line); border-radius: var(--r-xl); padding: 18px 20px;
      margin-bottom: 16px;
      transition: border-color var(--dur-2) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
    }
    .set-page .panel:hover {
      border-color: rgb(var(--primary-rgb) / .3);
      box-shadow: 0 12px 32px -16px rgb(var(--primary-rgb) / .45);
    }
    .set-page .panel .panel:hover { box-shadow: none; }
    .set-page .panel h2 { font-size: 16px; letter-spacing: -.005em; }
    .set-card { background: var(--panel2); }
    .set-card-head { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
    .set-card-head h2 { margin: 0; font-size: 15px; }
    .set-card-head p { margin: 2px 0 0; font-size: 12.5px; color: var(--muted); }
    .set-currency-field { max-width: 420px; }
    .set-currency-field .field { margin: 0; }
    .set-card .field { margin-bottom: 0; }
    .set-card .grid.three, .set-card .grid.two { row-gap: 14px; }

    /* Логотип — превью слева от полей: видно, есть он или нет. */
    .set-logo-row { display: flex; align-items: flex-start; gap: 16px; }
    .set-logo-fields { flex: 1 1 auto; min-width: 0; }
    .set-logo-preview {
      flex: 0 0 auto; width: 72px; height: 72px; margin-top: 20px; border-radius: var(--r-lg);
      display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
      border: 1px dashed var(--line); background: var(--panel); color: var(--muted); overflow: hidden;
    }
    .set-logo-empty { display: flex; flex-direction: column; align-items: center; gap: 2px; }
    .set-logo-empty small { font-size: 10.5px; }
    /* Картинка есть — заглушку прячем; не загрузилась — наоборот. */
    .set-logo-preview.has-img:not(.is-broken) .set-logo-empty { display: none; }
    .set-logo-preview.is-broken img { display: none; }
    .set-logo-preview.is-broken { color: var(--text-warning); }
    .set-logo-preview.has-img:not(.is-broken) { border-style: solid; padding: 8px; background: var(--on-color); }
    .set-logo-preview img { max-width: 100%; max-height: 100%; object-fit: contain; display: block; }
    .set-logo-input { display: flex; gap: 8px; align-items: stretch; }
    .set-logo-input input[type="text"], .set-logo-input > input { flex: 1 1 auto; min-width: 0; }
    .set-logo-upload { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; cursor: pointer; white-space: nowrap; }
    /* С клавиатуры фокусируется сама кнопка (tabindex), поле файла скрыто. */
    .set-logo-upload:focus-visible { outline: var(--ring-w) solid var(--ring); outline-offset: 2px; }
    @media (max-width: 640px) {
      .set-page .panel { padding: 16px; }
      .set-logo-row { flex-direction: column; }
      .set-logo-preview { margin-top: 0; }
    }

    /* Изменение к предыдущему такому же периоду — капсулой у подписи суммы.
       Цвет по смыслу, а не по знаку: рост расхода — красный. */
    .db-delta {
      display: inline-flex; align-items: center; margin-left: 4px; padding: 1px 6px;
      border-radius: var(--r-pill); background: var(--panel2); color: var(--muted);
      font-size: 10.5px; font-weight: 700; letter-spacing: 0; text-transform: none;
      font-variant-numeric: tabular-nums; vertical-align: 1px; cursor: help;
    }
    .db-delta.is-good { color: var(--text-success); background: color-mix(in srgb, var(--green) 14%, transparent); }
    .db-delta.is-bad { color: var(--text-danger); background: color-mix(in srgb, var(--red) 14%, transparent); }

    /* Текущий месяц — подпись акцентом: он ещё не закончился, и его
       невысокий столбец не должен читаться провалом. */
    .db-chart-col.is-now .db-chart-mlabel { fill: var(--primary-text); font-weight: 700; }

    /* Листание: страница перерисована, и столбцы съезжают на одну колонку
       (--gw задаёт svg) — график ПРОЕЗЖАЕТ месяц, а не мигает. Новая колонка
       с края проявляется. Смена длины периода — столбцы вырастают заново. */
    .db-money-panel.is-slide-next .db-chart-col { animation: dbColFromRight .5s var(--ease-out); }
    .db-money-panel.is-slide-prev .db-chart-col { animation: dbColFromLeft .5s var(--ease-out); }
    .db-money-panel.is-slide-next .db-chart-col:last-of-type { animation-name: dbColFromRight, homeFade; }
    .db-money-panel.is-slide-prev .db-chart-col:first-of-type { animation-name: dbColFromLeft, homeFade; }
    @keyframes dbColFromRight { from { transform: translateX(var(--gw, 0px)); } }
    @keyframes dbColFromLeft { from { transform: translateX(calc(var(--gw, 0px) * -1)); } }
    .db-money-panel.is-regrow .db-bar {
      transform-box: fill-box; transform-origin: 50% 100%;
      animation: barGrow .55s var(--ease-out) both;
      animation-delay: calc(var(--i, 0) * 25ms);
    }
    .db-money-panel.is-regrow .db-chart-col text:not(.db-chart-mlabel) {
      animation: homeFade .35s ease both; animation-delay: .4s;
    }
    @media (prefers-reduced-motion: reduce) {
      .db-chart-now, .db-money-panel .db-chart-col, .db-money-panel .db-bar,
      .db-money-panel .db-chart-col text { animation: none; }
    }

    /* Наведение/тап по колонке месяца — hover-подсветка + курсор, клик уводит в «Финансы» */
    /* Подсветка — вертикальный градиент от основания к нулю вверху (заливка
       задаётся в разметке, `url(#…Hl)`), а не ровная фиолетовая плашка на всю
       высоту поля. Плашка поднималась выше самого высокого столбца и читалась
       отдельным блоком, будто в графике что-то выделено насовсем; свет от базы
       поднимает колонку и заканчивается там же, где данные. */
    .db-chart-col { cursor: pointer; }
    .db-chart-hit-bg { opacity: 0; transition: opacity var(--dur-1); }
    .db-chart-col:hover .db-chart-hit-bg { opacity: 1; }
    /* Подпись месяца под курсором выходит из полутени: колонка подсвечена, а её
       имя оставалось таким же тусклым, как у соседей. */
    .db-chart-mlabel { transition: fill var(--dur-1); }
    .db-chart-col:hover .db-chart-mlabel { fill: var(--text); }

    /* Кастомный тултип графика — вместо нативного SVG <title> */
    .db-chart-tooltip {
      position: fixed; z-index: var(--z-tooltip); pointer-events: none;
      background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-md);
      box-shadow: var(--elev-2); padding: 10px 12px; font-size: 12.5px;
      min-width: 170px;
    }
    .db-tip-title { font-weight: 700; margin-bottom: 6px; }
    .db-tip-row {
      display: flex; align-items: center; gap: 6px; line-height: 1.7;
      font-variant-numeric: tabular-nums;
    }
    .db-tip-row b { margin-left: auto; }
    .db-tip-dot { width: 8px; height: 8px; border-radius: var(--r-xs); flex: 0 0 auto; }
    .db-tip-sep { height: 1px; background: var(--line); margin: 5px 0; }

    @media (max-width: 768px) {
      .db-stat { min-width: 45%; padding: 10px 12px; }
      .db-stat-value { font-size: 15px; }
      /* .db-analytics-top больше нет: топ клиентов уехал в свою панель, а
         одноколоночная раскладка задана порогом 1040px на .db-analytics-row. */
    }
    @media (max-width: 480px) {
      .db-stat { min-width: 50%; }
    }

    /* ── Deal list view ──────────────────────────────────────── */
    .deal-list-row {
      display: flex; align-items: center; gap: 10px; padding: 10px 14px;
      border-bottom: 1px solid var(--line); transition: background var(--dur-1);
      cursor: pointer; min-width: 0;
    }
    .deal-list-row:last-child { border-bottom: none; }
    .deal-list-row:hover { background: var(--panel2); }
    .deal-list-row.current { background: rgb(var(--primary-rgb) / .06); }
    /* Та же ловушка, что в колонке сделок: у .current и :hover одинаковая
       специфичность, .current объявлен позже — и открытая сделка переставала
       отзываться на курсор, хотя все соседние строки подсвечивались. */
    .deal-list-row.current:hover { background: rgb(var(--primary-rgb) / .12); }
    .deal-list-row .status-pill { flex: 0 0 108px; text-align: center; justify-content: center; }
    .deal-list-name { font-size: 14px; font-weight: 600; flex: 1 1 160px; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .deal-list-client { font-size: 12px; color: var(--muted); flex: 0 0 140px; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .deal-list-budget { font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; flex: 0 0 96px; text-align: right; }
    .deal-list-deadline { font-size: 12px; color: var(--muted); flex: 0 0 80px; text-align: right; white-space: nowrap; }
    .deal-list-actions { display: flex; gap: 6px; flex: 0 0 230px; align-items: center; justify-content: flex-end; }
    /* ── Тулбар сделок (теги-фильтры + поиск/сортировка/вид) ──
       Без обёртки-панели — плоская строка контролов, теги слева, поиск/сортировка/
       вид справа (см. правку 25.07: панель в рамке смотрелась как одна большая пилюля). */
    .deal-toolbar {
      display: flex; align-items: flex-start; gap: 14px; flex-wrap: wrap; justify-content: space-between;
      margin-bottom: 14px;
    }
    /* align-items:flex-start на родителе — если тегов много и они переносятся в
       несколько строк, поиск/сортировка/вид остаются прижаты к первой строке
       тегов, а не «плавают» по центру всей высоты блока тегов. */
    .deal-toolbar-tags { display: flex; align-items: center; gap: 6px 8px; flex-wrap: wrap; flex: 1; min-width: 180px; }
    /* Единый масштаб контролов тулбара — 36px высотой у чипа тега, поля поиска,
       селекта сортировки и группы кнопок вида (см. правку 25.07: раньше чип тега
       был 24px, поиск ~38px — разный «вес» на глаз). */
    .deal-tag-chip {
      height: 36px; box-sizing: border-box; display: inline-flex; align-items: center;
      border: 1px solid var(--line); background: var(--panel2); color: var(--muted);
      font-size: 12.5px; font-weight: 600; padding: 0 14px; border-radius: var(--r-pill);
      cursor: pointer; white-space: nowrap; transition: border-color var(--dur-1), color var(--dur-1), background var(--dur-1);
    }
    .deal-tag-chip:hover { border-color: rgb(var(--primary-rgb) / .5); color: var(--text); }
    .deal-tag-chip.active { background: var(--primary); border-color: var(--primary); color: var(--on-color); }
    /* «+N» — попап с остальными тегами, когда их больше, чем помещается в ряд. */
    .tag-overflow-wrap { position: relative; }
    .tag-overflow-btn { color: var(--primary-text); border-style: dashed; }
    .tag-overflow-panel {
      position: absolute; top: calc(100% + 6px); left: 0;
      min-width: 180px; max-height: 260px; overflow-y: auto;
      display: flex; flex-direction: column; gap: 2px;
      background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-lg);
      padding: 5px; box-shadow: var(--elev-3); z-index: var(--z-dropdown);
      animation: fadeSlideDown .14s cubic-bezier(0.16, 1, 0.3, 1);
    }
    .tag-overflow-row {
      display: block; width: 100%; text-align: left;
      padding: 7px 10px; border: none; background: transparent; border-radius: var(--r-sm);
      color: var(--text); font-size: 12.5px; font-weight: 600; cursor: pointer;
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .tag-overflow-row:hover { background: rgb(var(--primary-rgb) / .1); }
    .deal-toolbar-controls { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; flex: 0 0 auto; margin-left: auto; }
    .deal-search-wrap { position: relative; display: flex; align-items: center; }
    .deal-search-wrap svg { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--muted); pointer-events: none; }
    .deal-search-input { width: 300px; height: 36px; box-sizing: border-box; padding: 0 12px 0 32px; font-size: 13px; }
    .deal-view-toggle { display: flex; gap: 2px; padding: 2px; background: var(--panel2); border: 1px solid var(--line); border-radius: var(--r-sm); }
    .deal-view-btn {
      background: transparent; border: none; border-radius: var(--r-sm);
      width: 32px; height: 32px; display: flex; align-items: center; justify-content: center;
      cursor: pointer; color: var(--muted); transition: background var(--dur-1), color var(--dur-1);
    }
    .deal-view-btn svg { display: block; }
    .deal-view-btn:hover { color: var(--text); background: rgb(var(--primary-rgb) / .1); }
    .deal-view-btn.active { background: var(--primary); color: var(--on-color); }
    @media (max-width: 640px) {
      .deal-search-input { width: 100%; }
      .deal-toolbar-controls { width: 100%; }

      /* Поиску — ОТДЕЛЬНАЯ СТРОКА во всю ширину. Раньше он делил строку с
         сортировкой и переключателем вида, а у сортировки ширина 190px была
         вписана прямо в разметку (инлайн-стиль перебивает CSS) — поэтому поиску
         доставалось ровно 62px: видно лупу и больше ничего, ни подсказки, ни
         набранного текста. Замер: 62 против 190 у сортировки и 106 у переключателя.
         Инлайн-ширина убрана из app.js, ширина задаётся здесь. */
      .deal-search-wrap { flex: 1 0 100%; min-width: 0; }
      /* Правило вешаем на ОБЁРТКУ, и только здесь оправдан !important.
         enhanceSelects() копирует инлайн-ширину селекта на обёртку (а без неё
         ставит width:100%) — тоже ИНЛАЙНОМ, а инлайн не перебить ни одним
         селектором. Убрать инлайн из разметки нельзя: он и есть источник
         десктопных 190px. Отсюда единственный доступный способ сузить обёртку
         на телефоне.
         flex-basis именно 0, а не auto: при auto обёртка просит ширину своего
         содержимого (те же ~190px), не влезает рядом с переключателем вида и
         уносится на третью строку. С нулевой базой она делит остаток строки. */
      .deal-toolbar-controls .uu-select-wrap { flex: 1 1 0; width: auto !important; min-width: 0; }
      .deal-sort-select { width: 100%; }
      .deal-view-toggle { flex: 0 0 auto; }
    }

    .gantt-grid { display: flex; flex-direction: column; gap: 2px; }
    .gantt-row { display: flex; align-items: center; gap: 10px; min-height: 34px; }
    .gantt-row-label { flex: 0 0 190px; min-width: 0; display: flex; flex-direction: column; justify-content: center; }
    .gantt-row-name { font-size: 12.5px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .gantt-row-sub { font-size: 11px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .gantt-row-track { position: relative; flex: 1; min-width: 0; height: 22px; background: var(--panel2); border-radius: var(--r-sm); }
    .gantt-bar { position: absolute; top: 3px; height: 16px; border-radius: var(--r-xs); cursor: pointer; opacity: .88; transition: opacity var(--dur-1), transform var(--dur-1); }
    .gantt-bar:hover { opacity: 1; transform: translateY(-1px); }
    .gantt-header .gantt-row-track { background: none; height: 18px; }
    .gantt-months { border-bottom: 1px solid var(--line); }
    .gantt-month-label { position: absolute; top: 0; font-size: 10.5px; color: var(--muted); font-weight: 700; text-transform: capitalize; white-space: nowrap; }
    .gantt-today-line { position: absolute; top: 0; bottom: -4px; width: 2px; background: var(--red); opacity: .55; }
    @media (max-width: 640px) {
      .gantt-row-label { flex: 0 0 110px; }
      .gantt-row-sub { display: none; }
    }
    .deal-sort-select {
      /* background-color, НЕ background: шорткод сбрасывает background-repeat к
         "repeat" (по умолчанию), и SVG-шеврон из базового select{} плиткой
         повторяется по всей ширине текста на мобиле (нативный select). */
      background-color: var(--panel2); border: 1px solid var(--line); border-radius: var(--r-sm);
      /* padding — тоже шорткод, перебивает базовый padding-right:34px (место под
         шеврон) — возвращаем его отдельно, иначе текст наезжает на иконку. */
      height: 36px; box-sizing: border-box;
      padding: 0 10px; padding-right: 34px; font-size: 12.5px; color: var(--text); cursor: pointer;
      font-weight: 600; transition: border-color var(--dur-1);
    }
    .deal-sort-select:hover { border-color: rgb(var(--primary-rgb) / .5); }
    .deal-sort-select:focus { outline: none; border-color: var(--primary); }
    @media (max-width: 900px) {
      /* На планшете actions-колонка — auto, не фиксированная */
      .deal-list-actions { flex: 0 0 auto; }
      .deal-list-client { flex: 0 0 110px; }
    }
    @media (max-width: 640px) {
      .deal-list-client { display: none; }
      .deal-list-deadline { display: none; }
      /* На мобиле actions — минимальные: только кнопка действия, без Ред. */
      .deal-list-actions { flex: 0 0 auto; gap: 4px; }
      .deal-list-actions .btn.small { display: none; }
      .deal-list-budget { flex: 0 0 72px; }
      .deal-list-name { font-size: 13px; }
      /* Панель — меньше паддинг на мобиле */
      .panel { padding: 12px; }
    }

    .crm-home-funnel {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(98px, 1fr));
      gap: 6px; margin-bottom: 4px;
    }
    .funnel-stage {
      padding: 8px 10px;
      border: 1px solid var(--line);
      border-radius: var(--r-md); background: var(--panel2);
      cursor: pointer; transition: var(--dur-1);
    }
    .funnel-stage:hover { border-color: rgb(var(--primary-rgb) / .5); transform: translateY(-1px); }
    /* Выбранный этап воронки — акцентная карточка с градиентом и свечением, а не
       ровный тинт: на ленте из одиннадцати одинаковых плиток «где я сейчас»
       читалось только по чуть более светлому фону. */
    .funnel-stage.active {
      border-color: var(--primary-on-tint);
      background-color: rgb(var(--primary-rgb) / .12);
      background-image: var(--grad-soft);
      box-shadow: var(--glow-sm);
    }
    .funnel-stage.active:hover { box-shadow: var(--glow-md); }
    /* Этап без сделок остаётся на месте, но приглушён. Прятать его нельзя: семь
       нулей подряд — это и есть сообщение «сверху воронки никто не заходит», и
       убрать его значило бы убрать сигнал. А вот кричать наравне с занятыми
       этапами ему незачем — глаз должен находить те, где сделки есть. */
    .funnel-stage--empty { opacity: .5; }
    .funnel-stage--empty:hover { opacity: .8; }
    /* Ноль не должен кричать так же, как число. Прозрачности .5 не хватало:
       вес 900 и кегль 19 держат внимание сами по себе, и лента из одиннадцати
       этапов читалась стеной «0 0 0 0 0 0», в которой занятые этапы приходилось
       искать. Пустому этапу оставляем место (семь нулей подряд — это и есть
       сообщение «сверху воронки никто не заходит»), но снимаем вес: сначала
       глаз находит, где сделки есть.
       Снимаем ВЕС и кегль, а не цвет: плитка уже под opacity .5, и приглушение
       цветом поверх прозрачности увело бы ноль в нечитаемые ~2:1. */
    .funnel-stage h3 { font-size: 11px; color: var(--muted); margin: 0 0 2px; }
    .funnel-stage .fs-count { font-size: 19px; font-weight: 900; line-height: 1.1; }
    .funnel-stage--empty .fs-count { font-size: 16px; font-weight: 600; }
    /* 11.5px, а не 10.5: это ДЕНЬГИ по этапу, ради которых воронку и открывают.
       Замер показал, что суммы и на 11.5px помещаются в плитку 98px без обрезки. */
    .funnel-stage .fs-amount { font-size: 11.5px; color: var(--muted); margin-top: 1px; }
    /* Цвет этапа — тонкой полоской сверху у ЗАНЯТОЙ плитки (тот же цвет, что
       полоса статуса на карточке сделки ниже) и рамкой под курсором. Лента
       остаётся прежней ровной сеткой: две переделки 28.09.2026 (плитки разной
       ширины, затем полоса-воронка) владелец отклонил — «изначально было
       лучше». Полоска — внутренней тенью: не меняет ни размеров, ни раскладки. */
    .funnel-stage.is-filled:not(.active) {
      box-shadow: inset 0 2px 0 color-mix(in srgb, var(--st-color, var(--primary)) 75%, transparent);
    }
    .funnel-stage.is-filled:not(.active):hover {
      border-color: color-mix(in srgb, var(--st-color, var(--primary)) 55%, transparent);
      box-shadow: inset 0 2px 0 var(--st-color, var(--primary)),
        0 8px 20px -12px color-mix(in srgb, var(--st-color, var(--primary)) 70%, transparent);
    }
    /* Наведение: плитка «всплывает» — чуть крупнее и выше, поверх ленты, с
       лёгкой пружиной. Соседи стоят на месте: раздвигающуюся плитку, которая
       сжимала соседей и резала их названия, владелец 28.09.2026 отклонил («не
       нравится»). Увеличение (6%) меньше зазора между плитками, поэтому
       всплывшая соседей не перекрывает. Только с настоящим курсором. */
    @media (hover: hover) and (pointer: fine) {
      .crm-home-funnel > .funnel-stage {
        position: relative;
        transition: transform .32s var(--ease-spring), box-shadow var(--dur-3) var(--ease-out),
          border-color var(--dur-2) var(--ease-out), opacity var(--dur-2), background-color var(--dur-2);
      }
      .crm-home-funnel > .funnel-stage:hover,
      .crm-home-funnel > .funnel-stage:focus-visible {
        transform: translateY(-3px) scale(1.06);
        z-index: 1;
      }
      /* Пустому этапу — всплытие скромнее: внимание к тем, где есть сделки. */
      .crm-home-funnel > .funnel-stage--empty:hover { transform: translateY(-2px) scale(1.03); }
      .crm-home-funnel > .funnel-stage:not(.is-filled):not(.active):hover {
        box-shadow: var(--elev-2);
      }
      .crm-home-funnel > .funnel-stage:active { transform: translateY(-1px) scale(1.02); }
    }
    @media (max-width: 640px) {
      /* Горизонтальная свайп-полоса вместо ломаного grid (auto-fit давал
         неровные строки: 1/2/3 плитки на разных строках). */
      .crm-home-funnel {
        display: flex;
        gap: 6px;
        overflow-x: auto;
        margin: 0 -12px 4px;
        padding: 0 12px 4px;
        -webkit-overflow-scrolling: touch;
        /* Fade-подсказка на краях — та же, что у .deal-tabs/.deal-stage-progress */
        -webkit-mask-image: linear-gradient(to right, transparent 0, black 12px, black calc(100% - 12px), transparent 100%);
        mask-image: linear-gradient(to right, transparent 0, black 12px, black calc(100% - 12px), transparent 100%);
      }
      .funnel-stage { flex: 0 0 100px; }
      .funnel-stage h3 { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    }
    .deal-card {
      position: relative;
      /* Колонка — чтобы полоса оплаты прижималась к низу (margin-top:auto) и
         выравнивала низ ряда сама, без подгонки высот. */
      display: flex; flex-direction: column;
      min-width: 0; /* grid-элемент по умолчанию min-width:auto — без этого nowrap-текст
                       в .deal-card-name раздувал бы колонку грида шире вьюпорта */
      padding: 13px 15px 13px 18px; cursor: pointer;
      transition: border-color var(--dur-1), transform var(--dur-1), box-shadow var(--dur-1);
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      --card-fill: var(--panel);
      /* Полоска статуса слева — цвет из CRM_STATUS_COLOR (--status-color, инлайн-стиль),
         залита градиентом в фоне САМОЙ карточки, а не отдельным ::before. Два отдельно
         скруглённых слоя (рамка + свой ::before со своим border-radius) на суб-пикселе
         никогда не совпадают идеально — вылезал нотч на скруглении. Фон стрижётся тем
         же border-radius, что и рамка, — рассинхрона быть не может физически. */
      background: linear-gradient(to right, var(--status-color, var(--line)) 0 4px, transparent 4px 100%), var(--card-fill);
      display: flex; flex-direction: column; gap: 9px;
    }
    /* Правило наведения для .deal-card живёт ОДНО — ниже, вместе с остальными
       карточками (.project-card / .client-card / .package-card). Здесь раньше
       стояла его копия с другой тенью; тот же селектор объявлен позже, поэтому
       эта копия не действовала вовсе, а правку в ней было не увидеть на экране. */
    /* Выделение карточки. Было: рамка + свечение box-shadow наружу. Свечение
       расплывалось ореолом и спорило с цветной полосой статуса слева, а на
       светлой теме читалось грязным пятном. Теперь кольцо INSET — оно ложится
       вплотную к рамке изнутри, поэтому щели между двумя линиями не возникает
       физически, — плюс лёгкая заливка и мягкая тень вниз для объёма.
       Заметно, но не светится. */
    .deal-card.current {
      border-color: var(--text-success);
      box-shadow: inset 0 0 0 1px var(--green), 0 4px 18px rgba(22,163,74,.13);
      --card-fill: rgba(22,163,74,.05);
    }
    .deal-card.deal-card-selected {
      border-color: var(--primary);
      box-shadow: inset 0 0 0 1px var(--primary), 0 4px 18px rgb(var(--primary-rgb) / .15);
      --card-fill: rgb(var(--primary-rgb) / .07);
    }
    /* Обе пометки разом: приоритет у выбора — это то, что человек делает СЕЙЧАС,
       а «текущая» остаётся видна по зелёному уголку-лейблу. */
    .deal-card.current.deal-card-selected {
      border-color: var(--primary);
      box-shadow: inset 0 0 0 1px var(--primary), 0 4px 18px rgb(var(--primary-rgb) / .15);
    }
    /* ── Перетаскивание карточек сделок ──────────────────────────────────────
       Карточка берётся В ЛЮБОМ МЕСТЕ, на её месте остаётся серое пятно, а сама
       она едет за указателем. Ручки нет: она занимала место рядом с кнопкой
       действий и требовала прицеливаться. Клик по карточке при этом сохранён —
       перенос начинается только после смещения на 6px (порог в dealPointerDown).

       Курсор grab на всей карточке был бы враньём: он обещает «тащи», а одиночный
       клик выбирает активную сделку. Поэтому обычный pointer, а «взято» показываем
       уже в процессе — курсором grabbing на всём документе. */
    .deal-card-draggable { cursor: pointer; touch-action: manipulation; }
    body.is-dragging-card, body.is-dragging-card * { cursor: grabbing !important; }
    /* Во время переноса гасим выделение текста и подсветку по наведению —
       иначе под летящим клоном «загораются» карточки, над которыми он проходит. */
    body.is-dragging-card { -webkit-user-select: none; user-select: none; }
    body.is-dragging-card .deal-card:hover { transform: none; }

    /* Пятно на месте карточки: та же геометрия, но без содержимого — видно
       ровно тот слот, куда она вернётся или встанет соседняя. */
    .deal-card-placeholder {
      background: var(--panel2) !important;
      border-style: dashed;
      border-color: var(--line) !important;
      box-shadow: none !important;
      transform: none !important;
    }
    .deal-card-placeholder > * { visibility: hidden; }

    /* Клон, который едет за указателем. pointer-events:none обязателен — иначе
       он сам оказывается элементом под курсором, и цель вставки не находится. */
    .deal-card-flying {
      position: fixed !important;
      margin: 0 !important;
      z-index: 90;
      pointer-events: none;
      opacity: .95;
      box-shadow: 0 18px 42px rgba(0,0,0,.32);
      border-color: var(--primary) !important;
      transform: rotate(1.5deg);
    }

    /* Наведение приподнимает карточку, а не раздувает вокруг неё ореол.
       Было 0 10px 30px при 18–20% — против 0 4px 18px в покое: тень росла почти
       вдвое и расплывалась цветным пятном под соседние карточки. Теперь шаг
       короткий (18 → 22px) и того же порядка, что у остальных карточек. */
    .deal-card:hover.current { box-shadow: inset 0 0 0 1px var(--green), 0 6px 22px rgba(22,163,74,.16); }
    .deal-card:hover.deal-card-selected { box-shadow: inset 0 0 0 1px var(--primary), 0 6px 22px rgb(var(--primary-rgb) / .18); }
    /* Уголок-лейбл слева, под названием — без фона-капсулы, вне потока
       (position:absolute), поэтому не спорит с заголовком и не давит на межстрочный
       интервал. Место под него зарезервировано padding-bottom на .deal-card-head
       для ВСЕХ карточек одинаково — блок с цифрами не съезжает у карточек без пометки. */
    .deal-card-current-badge {
      position: absolute; left: 0; bottom: 0;
      display: inline-flex; align-items: center; gap: 3px;
      font-size: 10.5px; font-weight: 700; letter-spacing: .01em; color: var(--text-success);
    }
    .deal-card-client { font-size: 12px; color: var(--muted); font-weight: 600; margin-top: 2px; }
    /* Название — до ДВУХ строк, а не одна с многоточием. Сделки здесь зовутся
       «Клиент — что делаем» («Антон Патокин — Магазин спойлеров»), и в колонке
       шириной 330px одна строка обрывалась ровно на второй половине, то есть на
       предмете работы: карточка показывала, чей это проект, и умалчивала, какой.
       Ряд от этого не «пляшет»: карточки лежат в grid и тянутся до общей высоты,
       а полоса оплаты прижата к низу (margin-top:auto). */
    .deal-card-name {
      font-size: 15px; font-weight: 750; line-height: 1.3; letter-spacing: -.01em;
      display: -webkit-box; -webkit-box-orient: vertical;
      -webkit-line-clamp: 2; line-clamp: 2;
      overflow: hidden; min-width: 0;
    }

    /* ── DEAL CARD (redesign 04.07.2026 — воздух, мягче вес, чёткая иерархия) ── */
    /* Меню пинится в правый угол, имя занимает почти всю ширину; пилюли статуса —
       отдельной строкой под именем, чтобы не отжимать заголовок в узкую колонку. */
    /* align-items:center, а не flex-start — иначе кнопка-меню (32px, точки по
       центру своей высоты) визуально «съезжает» ниже текста заголовка (~20px). */
    .deal-card-head { display: flex; align-items: center; gap: 6px; position: relative; padding-bottom: 13px; }
    .deal-card-head-main { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 4px; }
    .deal-card-sub { font-size: 12px; color: var(--muted); font-weight: 500; line-height: 1.3; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    /* Раньше — жирный текст с ⚠-эмодзи; при массовом импорте (много непривязанных
       сделок сразу) это читалось как шум на каждой карточке. Иконка вместо эмодзи +
       обычный (не bold) вес — сигнал остаётся, но не кричит; подробности в title. */
    .deal-card-sub.unlinked { color: var(--yellow); }
    .deal-card-sub.unlinked svg { flex: 0 0 auto; vertical-align: -1px; margin-right: 1px; }
    /* Статус + маржа + клиент в одну строку под именем (компактная шапка) */
    .deal-card-statusline { display: flex; align-items: center; gap: 6px; row-gap: 4px; flex-wrap: wrap; }
    /* Раньше статус дублировался: цветная полоска слева (::before) + пилюля с тем же
       смыслом внутри карточки — два одинаковых по весу акцента. Полоска остаётся
       единственным цветовым сигналом, текст статуса — просто подпись без капсулы. */
    .deal-card-status-plain { font-size: 11px; font-weight: 600; color: var(--muted); white-space: nowrap; }
    /* --line — цвет РАМОК, на карточке он практически невидим: первая версия
       разделителя просто не читалась. Берём цвет текста второго плана и гасим
       прозрачностью — разделитель должен отделять, а не исчезать. */
    .deal-card-dot { color: var(--muted); opacity: .6; font-size: 11px; flex: 0 0 auto; }
    .deal-card-pills { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; }
    .deal-card-menu-wrap { flex: 0 0 auto; position: relative; }

    /* Смета/Оплачено (+Расходы/Прибыль) сгруппированы в мягкую вставку с
       вертикальным разделителем — числа читаются как единый финансовый блок. */
    /* Вставку держит ФОН, а не рамка: рамка внутри рамки карточки давала два
       контура в двух миллиметрах друг от друга — на четырёх карточках в ряду это
       восемь линий на экран, и блок с деньгами выглядел вставленной таблицей, а
       не частью карточки. Подложка --panel2 отделяет его сама. */
    .deal-card-stats {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 7px 0;
      padding: 9px 12px;
      background: var(--panel2);
      border-radius: var(--r-lg);
    }
    .deal-card-stat { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
    .deal-card-stat:nth-child(odd) { padding-right: 13px; }
    .deal-card-stat:nth-child(even) { padding-left: 13px; border-left: 1px solid var(--line); }
    /* Капс вразрядку убран 04.09.2026 (вопрос владельца: «нужно ли писать текст
       заглавными?»). Не нужно: КАПС + разрядка — самая тяжёлая для чтения
       комбинация, и прошлая правка это уже знала — она поднимала кегль и вес
       ИМЕННО чтобы капс не проваливался. Сняли капс — компенсировать нечего:
       подпись возвращается к обычному весу и уходит на второй план, а вперёд
       выходит число, ради которого карточку и читают. Это и есть «иерархия
       через типографику, а не через декор» (PRODUCT.md). */
    .deal-card-stat .lbl { font-size: 11.5px; color: var(--muted); font-weight: 550; letter-spacing: 0; }
    .deal-card-stat .val { font-size: 15.5px; font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: -.01em; line-height: 1.15; }

    /* ── Полоса оплаты ────────────────────────────────────────────────────────
       Замер 04.09.2026: в ряду карточки тянутся до высоты самой высокой, и у той,
       что без дедлайна, снизу оставалось 39px пустоты против 14px у соседки.
       Пустоту владелец и назвал: «когда карточка не выделена, там всегда пустота».

       Заполняем не воздухом и не «нет дедлайна», а ДЕЛОМ: доля оплаты есть у
       каждой сделки, читается мгновенно и отвечает на главный вопрос к карточке —
       сколько денег уже пришло. Полоса одинаковой высоты на всех карточках,
       поэтому низ ряда выравнивается сам, без подгонки высот. */
    .deal-card-progress {
      margin-top: auto; padding-top: 11px;
      display: flex; align-items: center; gap: 8px;
    }
    .deal-card-progress-track {
      flex: 1; height: 4px; border-radius: var(--r-pill);
      background: var(--line); overflow: hidden;
    }
    .deal-card-progress-fill { display: block; height: 100%; border-radius: var(--r-pill); }
    .deal-card-progress-num {
      font-size: 11px; font-weight: 700; color: var(--muted);
      font-variant-numeric: tabular-nums; flex: 0 0 auto; min-width: 30px; text-align: right;
    }

    .deal-card-meta { display: flex; flex-direction: column; gap: 4px; }
    .deal-card-deadline { font-size: 11.5px; font-weight: 650; display: flex; align-items: center; gap: 5px; }
    .deal-card-note { font-size: 11.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
    .deal-card-tags { display: flex; flex-wrap: wrap; gap: 4px; }
    .deal-card-tag { font-size: 11px; background: rgb(var(--primary-rgb) / .12); border-radius: var(--r-pill); padding: 1px 8px; color: var(--primary-on-tint); cursor: pointer; }

    /* ── РАЗДЕЛ «ЗАДАЧИ» (все задачи + свободные) ────────── */
    .chip {
      padding: 6px 13px; border-radius: var(--r-pill); border: 1px solid var(--line);
      min-height: var(--h-sm);
      background: transparent; color: var(--muted); font-size: 13px; font-weight: 600;
      cursor: pointer; white-space: nowrap; transition: background var(--dur-1), border-color var(--dur-1), color var(--dur-1);
    }
    .chip:hover { border-color: rgb(var(--primary-rgb) / .5); color: var(--text); }
    .chip.active { background: rgb(var(--primary-rgb) / .15); border-color: var(--primary); color: var(--primary-on-tint); }

    /* Три счётчика с однозначным числом растягивались на 460px каждый — плитка
       в полстраницы ради нуля. Ширину ограничиваем, остаток строки оставляем
       пустым; на телефоне 1fr-колонки те же, что и были. */
    .gtask-stats {
      display: grid; grid-template-columns: repeat(4, minmax(0, 210px));
      justify-content: start; gap: 10px; margin-bottom: 16px;
    }
    @media (max-width: 820px) { .gtask-stats { grid-template-columns: repeat(4, 1fr); } }
    /* Телефон: один ряд из трёх плиток, как раньше. «Всего» уходит — число
       и так стоит у колонок и во вкладках, — значки тоже: на трети экрана
       они съедали место у подписи. */
    @media (max-width: 560px) {
      .gtask-stats { grid-template-columns: repeat(3, 1fr); gap: 8px; }
      /* Через .gtask-stats: базовое `.gtask-stat { display:flex }` стоит в файле
         НИЖЕ этого места и при равной силе перебивало бы скрытие. */
      .gtask-stats .gtask-stat--total, .gtask-stats .gtask-stat-ico { display: none; }
      .gtask-stats .gtask-stat { padding: 10px; }
    }

    /* Тот же приём для рядов-счётчиков на общей сетке (.grid three): плитка с
       одним числом не должна занимать треть страницы. */
    .grid.three.tiles-compact { grid-template-columns: repeat(3, minmax(0, 240px)); justify-content: start; }
    @media (max-width: 820px) { .grid.three.tiles-compact { grid-template-columns: repeat(3, 1fr); } }
    /* Плитка: значок в плашке своего тона, подпись и число. Тон приходит
       модификатором только когда число не ноль (app.js renderGlobalTasks). */
    .gtask-stat {
      --tone: var(--primary-text);
      display: flex; align-items: center; gap: 10px; min-width: 0;
      padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--panel2);
    }
    .gtask-stat.is-warn    { --tone: var(--text-warning); }
    .gtask-stat.is-danger  { --tone: var(--text-danger); border-color: color-mix(in srgb, var(--red) 35%, var(--line)); }
    .gtask-stat.is-success { --tone: var(--text-success); }
    .gtask-stat.is-zero    { --tone: var(--muted); }
    .gtask-stat-ico {
      flex: 0 0 30px; height: 30px; display: grid; place-items: center;
      border-radius: var(--r-md); color: var(--tone);
      background: color-mix(in srgb, var(--tone) 14%, transparent);
    }
    .gtask-stat-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
    .gtask-stat .lbl { font-size: 11px; color: var(--muted); font-weight: 600; letter-spacing: .04em; text-transform: uppercase; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .gtask-stat .val { font-size: 20px; line-height: 1.1; font-weight: 800; font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
    .gtask-stat:is(.is-warn, .is-danger, .is-success) .val { color: var(--tone); }
    .gtask-stat.is-zero .val { color: var(--muted); }

    .gtask-filters { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 14px; }
    /* Раскладка строки фильтров: поиск гибкий, чипсы статусов уходят на свою
       строку (их и так прижимает горизонтальным скроллом), а справа — селект
       проекта, «Своя задача» и переключатель вида. Раньше поиск занимал строку
       целиком, и селект с кнопкой оказывались на втором и третьем уровнях: три
       ряда управления над списком из одной задачи. */
    .gtask-search { flex: 1 1 240px; max-width: 420px; }
    .gtask-chips { flex: 1 1 100%; order: 5; }
    /* Переключатель вида — к правому краю строки, а не вплотную к поиску: рядом
       с полем он читается как его часть («искать списком?»), а справа стоит там
       же, где такие же переключатели в «Сделках» и «Клиентах». Между ними
       остаётся воздух, в который на широком экране попадают чипсы статусов. */
    .gtask-filters .deal-view-toggle { margin-left: auto; }
    /* Ряд «Показать ещё» — тот же вид, что у списка сделок и каталога (там он собран
       инлайном). Новым спискам даём класс, чтобы не плодить четвёртую копию стиля. */
    .show-more-row { display: flex; justify-content: center; margin-top: 16px; }
    /* Полоса выбора типа демо-сделки. Живёт только у новичка, поэтому оформлена
       подсказкой, а не панелью: рамка пунктиром и приглушённый фон — чтобы её не
       спутали с настоящими данными и не искали потом, куда она делась. */
    .demo-kind-strip {
      border: 1px dashed var(--line-strong, var(--line));
      background: var(--panel2);
      border-radius: var(--r-xl);
      padding: 14px 16px;
      margin-bottom: 14px;
      display: flex;
      flex-direction: column;
      gap: 10px;
    }
    .demo-kind-head { display: flex; flex-wrap: wrap; gap: 4px 8px; align-items: baseline; font-size: 13px; }
    .demo-kind-head span { color: var(--muted); }
    .demo-kind-chips { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
    /* Оговорка «цифры из примера» в «Финансах». Тот же пунктир, что у полосы демо
       на главной: одно состояние продукта — один визуальный язык, чтобы человек
       узнал подсказку, а не принял её за новый блок данных. */
    .demo-money-note {
      border: 1px dashed var(--line-strong, var(--line));
      background: var(--panel2);
      border-radius: var(--r-xl);
      padding: 12px 14px;
      margin-bottom: 14px;
      display: flex;
      flex-wrap: wrap;
      gap: 4px 8px;
      align-items: baseline;
      font-size: 13px;
    }
    .demo-money-note span { color: var(--muted); }
    .crm-board-toolbar {
      display: flex;
      align-items: center;
      gap: 10px;
      flex-wrap: wrap;
      margin-bottom: 14px;
    }
    .crm-board-search { flex: 1 1 240px; max-width: 380px; }
    .crm-board-search .catalog-search-input { min-height: 44px; }
    .gtask-search .catalog-search-input,
    .contract-search .catalog-search-input { min-height: 44px; }
    .contract-search { flex: 1 1 220px; max-width: 340px; }
    .gtask-chips { display: flex; gap: 7px; flex-wrap: wrap; }
    .gtask-project-select { padding: 7px 12px; border-radius: var(--r-md); font-size: 13px; }
    /* Ширину в строке фильтров задаёт ОБЁРТКА кастомного дропдауна, а не сам
       select: enhanceSelects прячет нативный (1×1px, opacity 0) и рисует свою
       кнопку. Правил на .gtask-project-select тут не хватало никогда — они
       применялись к невидимому элементу, поэтому селект и растягивал строку,
       уводя «Свою задачу» и переключатель вида на третий ряд. */
    .gtask-filters .uu-select-wrap { flex: 0 1 220px; min-width: 160px; }
    /* «Своя задача» стояла залитой primary-кнопкой в шапке раздела: спорила по весу
       с заголовком «Задачи» и дублировала глобальный «+» в топбаре. Место действия —
       рядом со списком и фильтрами, где человек и решает добавить задачу. */
    .gtask-add { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; white-space: nowrap; }

    .gtask-list { display: flex; flex-direction: column; gap: 8px; }
    .gtask-row {
      display: flex; align-items: center; gap: 12px;
      padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--r-lg);
      background: var(--panel); cursor: pointer;
      transition: border-color var(--dur-1), transform var(--dur-1), box-shadow var(--dur-1);
    }
    .gtask-row:hover { border-color: rgb(var(--primary-rgb) / .5); box-shadow: 0 4px 16px rgb(var(--primary-rgb) / .08); }
    .gtask-row.done { opacity: .6; }
    .gtask-row.done .gtask-title { text-decoration: line-through; color: var(--muted); }

    .gtask-check {
      width: 24px; height: 24px; flex: 0 0 auto; border-radius: var(--r-sm);
      border: 2px solid var(--line); background: transparent; cursor: pointer;
      display: grid; place-items: center; color: var(--on-color); font-size: 14px; font-weight: 800;
      transition: background var(--dur-1), border-color var(--dur-1);
    }
    .gtask-check:hover { border-color: var(--text-success); }
    .gtask-check.checked { background: var(--green); border-color: var(--text-success); }

    .gtask-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
    .gtask-title { font-size: 14px; font-weight: 700; line-height: 1.35; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .gtask-meta { display: flex; flex-wrap: wrap; gap: 4px 12px; font-size: 12px; color: var(--muted); font-weight: 500; }
    .gtask-project { background: var(--panel2); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 1px 9px; font-weight: 600; }
    .gtask-project.personal { background: rgb(var(--primary-rgb) / .12); border-color: rgb(var(--primary-rgb) / .3); color: var(--primary-on-tint); }
    .gtask-del {
      width: 30px; height: 30px; flex: 0 0 auto; border-radius: var(--r-sm);
      border: 1px solid transparent; background: transparent; color: var(--muted);
      cursor: pointer; font-size: 16px; line-height: 1; transition: background var(--dur-1), color var(--dur-1), border-color var(--dur-1);
    }
    .gtask-del:hover { background: rgba(220,38,38,.1); color: var(--text-danger); border-color: rgba(220,38,38,.3); }

    /* ── Единая иконочная кнопка удаления (корзина) ─────── */
    .icon-del-btn {
      width: 32px; height: 32px; flex: 0 0 auto; padding: 0;
      display: grid; place-items: center;
      border: 1px solid var(--line); background: transparent; color: var(--muted);
      border-radius: var(--r-sm); cursor: pointer;
      transition: background var(--dur-1), color var(--dur-1), border-color var(--dur-1);
    }
    .icon-del-btn:hover { background: rgba(220,38,38,.1); color: var(--text-danger); border-color: rgba(220,38,38,.35); }
    .icon-del-btn svg { display: block; }
    .deal-dup-btn {
      width: 24px; height: 24px; padding: 0;
      border: 1px solid transparent;
      background: none; border-radius: var(--r-sm);
      cursor: pointer; color: var(--muted);
      display: grid; place-items: center;
      opacity: 0; transition: opacity var(--dur-1), color var(--dur-1), border-color var(--dur-1), background var(--dur-1);
      flex-shrink: 0;
    }
    .deal-card:hover .deal-dup-btn { opacity: 1; }
    .deal-dup-btn:hover { color: var(--text); border-color: var(--line); background: var(--panel2); }
    /* Действие прижато к низу карточки + тонкий разделитель — во всех карточках
       ряда кнопка встаёт на одну линию, пустота снизу читается как воздух, а не «дырка». */
    .deal-card-footer {
      display: flex; justify-content: space-between;
      align-items: center; gap: 8px;
      margin-top: auto;
      padding-top: 11px;
      border-top: 1px solid var(--line);
    }
    /* Всегда видимая (не hover-only) — без неё единственный способ перейти в смету был
       клик по всей карточке, а hover-only кнопка статуса поверх него перехватывала клик
       через stopPropagation: пользователь целился в карточку, попадал в неё и не понимал,
       почему «ничего не происходит». */
    .deal-open-btn {
      padding: 6px 13px; border-radius: var(--r-pill); border: 1px solid var(--line);
      background: transparent; color: var(--primary-text); font-size: 12px; font-weight: 600;
      cursor: pointer; white-space: nowrap; transition: border-color var(--dur-1), background var(--dur-1);
    }
    .deal-open-btn:hover { border-color: var(--primary); background: rgb(var(--primary-rgb) / .08); }
    .next-action-btn {
      padding: 6px 13px; border-radius: var(--r-pill); border: none;
      background: var(--primary);
      color: var(--on-color); font-size: 12px; font-weight: 600; cursor: pointer;
      white-space: nowrap; transition: background var(--dur-1), transform var(--dur-1);
    }
    .next-action-btn:hover { background: var(--primary2); transform: translateY(-1px); }
    /* В карточке (не в компактной строке списка) — только на hover, меньше шума */
    .deal-card .next-action-btn { opacity: 0; transition: opacity var(--dur-1), background var(--dur-1), transform var(--dur-1); }
    .deal-card:hover .next-action-btn { opacity: 1; }
    /* В СПИСКЕ кнопка тихая: контур и акцентный текст, заливка приходит вместе с
       наведением на строку. Шесть сделок подряд — шесть залитых фиолетовых
       кнопок, и правило «акцент на один главный элемент экрана» (DESIGN.md §3)
       переставало работать: кричали все, значит никто. Прятать кнопку совсем,
       как на карточке, здесь нельзя — список читают с клавиатуры и на планшете,
       где hover может не наступить вовсе. */
    .deal-list-row .next-action-btn {
      background: transparent; color: var(--primary-text);
      border: 1px solid rgb(var(--primary-rgb) / .45);
      transition: background var(--dur-1), color var(--dur-1), border-color var(--dur-1);
    }
    .deal-list-row:hover .next-action-btn,
    .deal-list-row .next-action-btn:hover,
    .deal-list-row .next-action-btn:focus-visible {
      background: var(--primary); color: var(--on-color); border-color: var(--primary);
    }
    .health-dot {
      width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto;
    }
    .health-dot.green { background: var(--green); }
    .health-dot.yellow { background: var(--yellow); }
    .health-dot.red { background: var(--red); }
    .health-dot.grey { background: var(--muted); }

    /* ── DEAL CONTEXT MENU ───────────────────────── */
    .deal-menu-btn {
      width: 32px; height: 32px; padding: 6px;
      display: grid; place-items: center;
      border: 1px solid transparent; background: transparent;
      border-radius: var(--r-sm); color: var(--muted);
      cursor: pointer; flex-shrink: 0;
      /* touch area расширен через padding, визуальный размер через иконку */
      min-width: 32px; min-height: 32px;
      opacity: 0;
      transition: opacity var(--dur-1), background var(--dur-1), border-color var(--dur-1), color var(--dur-1);
    }
    .deal-card:hover .deal-menu-btn { opacity: 1; }
    /* На плитке кнопка проявляется по наведению — там их десятки, и постоянные
       троеточия рябили бы в списке. В шапке открытой сделки она одна и является
       единственным входом в действия над сделкой, поэтому видна всегда.
       Без этого правила кнопка наследовала opacity: 0 и была КЛИКАБЕЛЬНА, НО
       НЕВИДИМА: замер геометрии и elementFromPoint показывали её на месте. */
    .deal-tabs-menu .deal-menu-btn { opacity: 1; }
    .deal-menu-btn:hover {
      background: var(--panel2); border-color: var(--line); color: var(--text);
    }
    /* Удаление проекта доступно только в архиве — кнопка рядом с «вернуть». */
    .deal-del-btn {
      display: inline-flex; align-items: center; gap: 5px;
      padding: 5px 11px; border-radius: var(--r-pill);
      border: 1px solid rgba(220,38,38,.28);
      background: rgba(220,38,38,.08); color: var(--text-danger);
      font-size: 12px; font-weight: 600; cursor: pointer; white-space: nowrap;
      transition: background var(--dur-1), border-color var(--dur-1);
    }
    .deal-del-btn:hover { background: rgba(220,38,38,.16); border-color: rgba(220,38,38,.5); }
    .deal-del-btn svg { display: block; }
    .deal-ctx-menu {
      position: absolute; top: calc(100% + 6px); right: 0;
      min-width: 192px;
      background: var(--panel);
      border: 1px solid var(--line);
      border-radius: var(--r-lg);
      padding: 5px;
      box-shadow: var(--elev-3);
      z-index: var(--z-dropdown);
      animation: fadeSlideDown .14s cubic-bezier(0.16, 1, 0.3, 1);
    }
    /* Раскрытие вверх у нижнего края окна — класс ставит toggleDealMenu по замеру.
       top:auto обязателен: при заданных сразу top и bottom элемент растягивается
       между ними, и меню разъезжается по высоте вместо того, чтобы подняться. */
    .deal-ctx-menu.dcm-up { top: auto; bottom: calc(100% + 6px); }
    /* Пока меню открыто, карточка лежит поверх соседей. Без этого z-index:200 у
       самого меню не помогает: карточка под курсором приподнята через transform,
       а transform создаёт стековый контекст — меню не может выйти за его пределы,
       и следующая карточка (позже в DOM) закрывает нижние пункты. Замер: «В архив»
       перекрыт .deal-card-head соседней карточки при свободном месте в окне. */
    /* Карточка с открытым меню — над соседями. Правило было привязано к
       .deal-card, поэтому в каталоге не работало вовсе. Класс ставит только код
       открытия меню, так что общий селектор безопасен. position задаём здесь же:
       без него z-index ни на что не влияет, а не у всех носителей меню он есть. */
    .deal-card-menu-open { position: relative; z-index: 5; }
    /* Подъём под курсором на время открытого меню отменяем: transform создаёт
       стековый контекст и запирает меню внутри карточки — та самая причина, по
       которой оно и пряталось. */
    .item--catalog.deal-card-menu-open:hover { transform: none; }
    .dcm-item {
      display: flex; align-items: center; gap: 9px;
      width: 100%; padding: 8px 10px;
      border: none; background: transparent;
      color: var(--text); font-size: 13px; font-weight: 600;
      border-radius: var(--r-sm); cursor: pointer; text-align: left;
      transition: background var(--dur-1), color var(--dur-1);
    }
    .dcm-item:hover { background: rgba(255,255,255,.06); }
    [data-theme="light"] .dcm-item:hover { background: rgba(0,0,0,.05); }
    .dcm-item svg { flex-shrink: 0; opacity: .7; }
    .dcm-item:hover svg { opacity: 1; }
    .dcm-green { color: var(--text-success); }
    .dcm-green:hover { background: rgba(22,163,74,.12) !important; color: var(--text-success); }
    .dcm-danger { color: var(--text-danger); }
    .dcm-danger:hover { background: rgba(242,40,34,.1) !important; }
    /* Цветные иконки действий — тот же приём, что и у .dcm-green: цвет держит и
       текст, и иконку (fill="currentColor"), чтобы пункты меню читались с первого
       взгляда, не только по подписи. */
    .dcm-purple { color: var(--primary-text); }
    .dcm-purple:hover { background: rgb(var(--primary-rgb) / .12) !important; color: var(--primary-on-tint); }
    .dcm-blue { color: var(--blue); }
    .dcm-blue:hover { background: rgba(37,99,235,.12) !important; color: var(--blue); }
    .dcm-cyan { color: var(--tint-cyan); }
    .dcm-cyan:hover { background: rgba(8,145,178,.12) !important; color: var(--tint-cyan); }
    .dcm-sep { height: 1px; background: var(--line); margin: 4px 5px; }
    [data-theme="light"] .dcm-green { color: var(--text-success); }
    [data-theme="light"] .dcm-item:hover { background: rgba(0,0,0,.05); }

    /* ── SCROLL TO TOP ───────────────────────────── */
    .scroll-top-btn {
      position: fixed;
      bottom: 28px; right: 20px;
      width: 36px; height: 36px;
      border-radius: 50%;
      background: rgb(var(--primary-rgb) / .82);
      -webkit-backdrop-filter: blur(10px);
      backdrop-filter: blur(10px);
      color: var(--on-color); border: none; cursor: pointer;
      font-size: 14px; font-weight: 900;
      box-shadow: 0 4px 18px rgb(var(--primary-rgb) / .38);
      opacity: 0; transform: translateY(12px) scale(.9);
      transition: opacity var(--dur-2) var(--ease-out), transform var(--dur-2) var(--ease-out);
      z-index: 200;
      display: flex; align-items: center; justify-content: center;
      line-height: 1;
    }
    .scroll-top-btn.visible {
      opacity: 1; transform: translateY(0) scale(1);
    }
    .scroll-top-btn:hover {
      background: var(--primary2);
      transform: translateY(-2px) scale(1.08);
      box-shadow: 0 8px 28px rgb(var(--primary-rgb) / .55);
    }
    @media (max-width: 768px) {
      .scroll-top-btn { bottom: 88px; right: 14px; }
    }

    /* ── ESTIMATE STAGE VISUAL ────────────────────── */
    .estimate-stage {
      padding: 0 !important;
      margin-top: 20px;
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      background: transparent;
      overflow: hidden;
    }
    .stage-header {
      display: flex;
      justify-content: space-between;
      align-items: flex-start;
      /* Перенос обязателен: на телефоне название этапа, шестизначная сумма и кнопка
         «Свернуть» в одну строку не помещаются, и раньше сумма с кнопкой просто
         уезжали ЗА КРАЙ карточки — кнопка оказывалась обрезанной. Замер на 390px
         до правки: строка шире карточки на 5px, кнопка срезана на 4px. */
      flex-wrap: wrap;
      gap: 10px 16px;
      padding: 14px 18px;
      border-bottom: 1px solid var(--line);
      background: rgba(255,255,255,.04);
    }
    [data-theme="light"] .stage-header {
      background: rgba(0,0,0,.025);
    }
    /* min-width: 0 — то самое, без чего flex-элемент отказывается сжиматься ниже
       ширины своего содержимого и выталкивает соседа наружу (min-width по умолчанию
       auto, а не 0). Тот же корень, что у обрезанных названий в гридах. */
    .stage-header-left {
      display: flex; align-items: center; gap: 12px;
      min-width: 0; flex: 1 1 200px;
    }
    .stage-header-text { min-width: 0; }
    .stage-header-right {
      display: flex; align-items: center; gap: 12px;
      flex: 0 0 auto; margin-left: auto;
    }
    .stage-color-bar {
      width: 5px;
      border-radius: var(--r-pill);
      align-self: stretch;
      min-height: 32px;
      flex: 0 0 auto;
    }
    .stage-header h2 {
      margin: 0; font-size: 18px; letter-spacing: -.02em;
      display: flex; align-items: center; gap: 8px;
    }
    /* Значок этапа — в его же цвете, подложка того же цвета на 16%. Размер
       меньше значка раздела (24 против 28): это не заголовок страницы, а метка
       группы внутри неё, и она не должна спорить с ним по весу. */
    .stage-h2-ico {
      width: 24px; height: 24px; border-radius: var(--r-sm); flex: 0 0 auto;
      display: inline-flex; align-items: center; justify-content: center;
    }
    .stage-header-meta {
      font-size: 12px; color: var(--muted); margin-top: 3px;
    }
    .stage-body {
      padding: 16px;
    }
    /* Свёрнутый этап — один заголовок, он же переключатель (app.js).
       Строки «Этап свернут — …» под ним больше нет. */
    .stage-header--toggle { cursor: pointer; transition: background var(--dur-1); }
    .stage-header--toggle:hover { background: rgb(var(--primary-rgb) / .05); }
    .stage-header--toggle:hover .stage-collapse-btn { border-color: rgb(var(--primary-rgb) / .5); }

    /* ── CLIENT DETAIL ─────────────────────────────── */
    .client-detail-header {
      padding: 20px;
      background: var(--panel2);
      border-radius: var(--r-xl);
      border: 1px solid var(--line);
      margin-bottom: 16px;
    }
    .client-detail-top {
      display: flex; justify-content: space-between;
      align-items: flex-start; gap: 16px; margin-bottom: 16px;
    }
    .client-detail-top h1 { margin: 0; font-size: 26px; }
    .client-projects-list {
      display: grid; gap: 10px; margin-top: 14px;
    }
    .client-project-row {
      display: flex; justify-content: space-between; align-items: center;
      padding: 12px 16px; border: 1px solid var(--line); border-radius: var(--r-lg);
      background: var(--panel); gap: 14px;
      cursor: pointer; transition: var(--dur-1);
    }
    .client-project-row:hover { border-color: rgb(var(--primary-rgb) / .5); }
    .client-project-row h4 { margin: 0 0 3px; font-size: 14px; }
    .client-project-row p { margin: 0; font-size: 12px; color: var(--muted); }

    /* ── FINANCE TABLE (Otask style) ────────────── */
    .fin-table-wrap {
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      overflow-x: auto;
      overflow-y: hidden;
      margin-top: 10px;
    }

    /* ── Операции на телефоне: список, а не таблица ──────────────────────────
       Замер на 390px: таблица 741px при доступных 348 — вдвое шире экрана. На
       виду оставались «Дата · Проект · Описание», а СУММА, ради которой в список
       и заходят, уезжала за правый край; итоги внизу тоже. Листать вбок, чтобы
       узнать, сколько денег, — это не список операций.

       Строка становится сеткой из двух колонок и трёх этажей:
         описание   сумма      ← главное, крупно
         проект     дата       ← мелко, приглушённо
         категория             ← бейдж
       Тип операции («Поступление»/«Расход») скрыт: знак и цвет суммы говорят то
       же самое, а место он занимал как полноценная колонка. */
    @media (max-width: 640px) {
      .fin-table--ops { display: block; width: 100%; }
      .fin-table--ops thead { display: none; }
      .fin-table--ops tbody, .fin-table--ops tfoot { display: block; }
      .fin-table--ops tbody tr {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-areas: "desc sum" "proj date" "cat cat";
        gap: 2px 10px; padding: 11px 12px;
        border-bottom: 1px solid var(--line);
      }
      .fin-table--ops tbody tr:last-child { border-bottom: none; }
      .fin-table--ops tbody td {
        display: block; border: none; padding: 0; max-width: none !important;
      }
      .fin-table--ops tbody td:nth-child(1) { grid-area: date; text-align: right; font-size: 11.5px; }
      .fin-table--ops tbody td:nth-child(2) { grid-area: proj; font-size: 11.5px !important; font-weight: 600 !important; color: var(--muted); }
      .fin-table--ops tbody td:nth-child(3) {
        grid-area: desc; font-size: 13.5px; font-weight: 600;
        display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
      }
      .fin-table--ops tbody td:nth-child(4) { grid-area: cat; margin-top: 3px; }
      .fin-table--ops tbody td:nth-child(5) { display: none; }
      .fin-table--ops tbody td:nth-child(6) { grid-area: sum; text-align: right; font-size: 14px; font-weight: 800; }
      /* Пустая строка «Нет транзакций» занимает всю ширину, а не одну колонку. */
      .fin-table--ops tbody tr > td[colspan] { grid-area: unset; grid-column: 1 / -1; text-align: center; }
      /* Итоги: подпись слева, сумма справа — тем же ритмом, что и строки. */
      .fin-table--ops tfoot tr {
        display: flex; justify-content: space-between; gap: 10px;
        padding: 9px 12px;
      }
      .fin-table--ops tfoot td { display: block; border: none; padding: 0; }
      .fin-table--ops tfoot td:first-child { text-align: left; }
    }
    .fin-table {
      width: 100%; border-collapse: collapse;
    }
    .fin-table th {
      text-align: left; padding: 10px 14px;
      font-size: 12px; font-weight: 700;
      color: var(--muted); letter-spacing: .05em; text-transform: uppercase;
      background: var(--panel2);
      border-bottom: 1px solid var(--line);
      white-space: nowrap;
    }
    .fin-table td {
      padding: 11px 14px;
      font-size: 13px;
      border-bottom: 1px solid var(--line);
      vertical-align: middle;
    }
    .fin-table tr:last-child td { border-bottom: none; }
    .fin-table tbody tr:hover td { background: rgba(255,255,255,.05); }
    .fin-table tbody tr:hover .amount-cell { text-decoration: underline dotted; }
    [data-theme="light"] .fin-table tbody tr:hover td { background: rgba(0,0,0,.04); }
    .fin-table .amount-cell { font-weight: 900; font-size: 14px; text-align: right; white-space: nowrap; font-family: var(--font-display); font-variant-numeric: tabular-nums; }
    .fin-table .amount-cell.income { color: var(--text-success); }
    .fin-table .amount-cell.expense { color: var(--text-danger); }
    [data-theme="light"] .fin-table .amount-cell.income { color: var(--text-success); }
    [data-theme="light"] .fin-table .amount-cell.expense { color: var(--text-danger); }
    .fin-table .type-badge {
      display: inline-flex; align-items: center; gap: 5px;
      padding: 4px 9px; border-radius: var(--r-pill); font-size: 12px; font-weight: 700;
    }
    .fin-table .type-badge.income { background: rgba(22,163,74,.14); color: var(--text-success); }
    .fin-table .type-badge.expense { background: rgba(220,38,38,.12); color: var(--text-danger); }
    [data-theme="light"] .fin-table .type-badge.income { color: var(--tint-green); }
    [data-theme="light"] .fin-table .type-badge.expense { color: var(--text-danger); }
    .fin-table-footer td {
      background: var(--panel2); font-weight: 900;
      font-size: 13px; border-top: 2px solid var(--line);
    }
    .fin-action-bar {
      display: flex; gap: 10px; align-items: center;
      margin-bottom: 14px; flex-wrap: wrap;
    }
    /* Ширина НАТИВНОГО select идёт от самого длинного варианта списка. На
       телефоне список из сорока с лишним проектов остаётся нативным (кастомную
       выпадашку там не показываем — см. UU_TOUCH_MAX_OPTIONS), и название сделки
       «Реклама – База отдыха "Раздолье – Троица"» растягивало фильтр на всю
       строку: поиск уезжал вниз, ряд ломался. Замер на 390px с 60 сделками:
       фильтр 348px из 348. Ставим потолок — подпись обрезается многоточием, а
       полное имя видно в раскрытом списке. Пол в 120px не даёт схлопнуться до
       одного шеврона. Правило нужно ОБОИМ видам: кастомной обёртке и нативному
       селекту, иначе чинится только десктоп. */
    .fin-action-bar .uu-select-wrap,
    .fin-action-bar select {
      flex: 1 1 140px; min-width: 120px; max-width: 260px;
      text-overflow: ellipsis;
    }
    .btn-income {
      display: inline-flex; align-items: center; gap: 7px;
      padding: 10px 18px; border-radius: var(--r-pill); border: none;
      background: var(--green); color: var(--on-color); font-weight: 700; font-size: 14px;
      cursor: pointer; transition: var(--dur-1);
    }
    .btn-income:hover { background: var(--green-strong); }
    .btn-expense {
      display: inline-flex; align-items: center; gap: 7px;
      padding: 10px 18px; border-radius: var(--r-pill); border: none;
      background: var(--red); color: var(--on-color); font-weight: 700; font-size: 14px;
      cursor: pointer; transition: var(--dur-1);
    }
    .btn-expense:hover { background: var(--red-strong); }

    .fin-quick-add {
      display: flex; gap: 8px; flex-wrap: wrap;
      margin-bottom: 12px;
    }
    .fin-quick-half {
      display: contents; /* убираем блок-обёртку — кнопки теперь напрямую */
    }
    /* Тинт и рамка — как у остальных кнопок продукта. Сплошная заливка зелёным
       и красным читалась светофором: два самых ярких пятна на экране стояли на
       обычных действиях, а не на том, что требует внимания. */
    .fin-quick-btn {
      display: inline-flex; align-items: center; gap: 6px;
      padding: 8px 16px; border-radius: var(--r-pill);
      font-size: 13px; font-weight: 700; cursor: pointer;
      transition: background var(--dur-1), border-color var(--dur-1);
      white-space: nowrap; flex: 0 0 auto;
    }
    .fin-quick-btn.income {
      background: var(--tint-green-bg); border: 1px solid color-mix(in srgb, var(--green) 35%, transparent);
      color: var(--text-success);
    }
    .fin-quick-btn.income:hover { background: color-mix(in srgb, var(--green) 18%, transparent); border-color: var(--green); }
    .fin-quick-btn.expense {
      /* Смесью, а не токеном: --tint-red-bg в наборе нет, и объявление с ним
         браузер отбросил бы молча вместе со всей строкой. */
      background: color-mix(in srgb, var(--red) 9%, transparent); border: 1px solid color-mix(in srgb, var(--red) 30%, transparent);
      color: var(--text-danger);
    }
    .fin-quick-btn.expense:hover { background: color-mix(in srgb, var(--red) 15%, transparent); border-color: var(--red); }
    /* Строка чипов состава себестоимости. */
    .fin-chips { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin: -6px 0 14px; }
    .fin-chips-label { font-size: 12px; font-weight: 700; color: var(--muted); }
    /* Сегменты «Все / Поступления / Расходы» — тем же строем, что вкладки продукта. */
    .fin-seg { display: inline-flex; gap: 4px; padding: 3px; border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--panel2); }
    .fin-seg-btn {
      padding: 6px 12px; border: 0; border-radius: var(--r-pill); background: none;
      font: inherit; font-size: 12px; font-weight: 750; color: var(--muted); cursor: pointer;
      transition: background var(--dur-1), color var(--dur-1);
    }
    .fin-seg-btn:hover { color: var(--text); background: rgb(var(--primary-rgb) / .08); }
    .fin-seg-btn.active { background: var(--primary); color: var(--on-color); }
    @media (hover: none) and (pointer: coarse) {
      .fin-seg-btn { min-height: 38px; }
      .fin-quick-btn { min-height: 44px; }
    }
    .fin-quick-extra { display: none; }
    .fin-quick-input, .fin-quick-amount { display: none; }

    /* ── ESTIMATE IN DEAL: clear tab context ───── */
    .deal-bar .deal-tab.active {
      color: var(--primary-on-tint);
      background: rgb(var(--primary-rgb) / .12);
      border-radius: var(--r-sm) var(--r-sm) 0 0;
      border-bottom-color: var(--primary-on-tint);
    }
    .estimate-section-title {
      display: flex; justify-content: space-between;
      align-items: center; gap: 14px; margin-bottom: 16px;
    }
    .estimate-section-title h2 { margin: 0; font-size: 18px; }

    /* ── PAGE ANIMATIONS ─────────────────────────── */
    /* Появление контента. Раньше здесь жил отдельный keyframes pageFadeIn, покадрово
       совпадавший с viewFadeIn ниже, но с другой длительностью (.22s против .18s) —
       два имени на одну анимацию, и при смене раздела они наслаивались. Оставлен один
       viewFadeIn: первый кадр после загрузки и смена раздела теперь идут одинаково. */
    #appContent { animation: viewFadeIn var(--dur-2) var(--ease-out); }

    .deal-card, .project-card, .client-card, .item, .package-card {
      transition: transform var(--dur-2) var(--ease-out),
                  border-color var(--dur-2) var(--ease-out),
                  box-shadow var(--dur-2) var(--ease-out);
    }
    /* Подъём карточки при наведении был только у .deal-card, хотя переход объявлен
       для пяти видов сразу — остальные держали transform, но никуда не двигались. */
    .deal-card:hover, .project-card:hover, .client-card:hover, .package-card:hover {
      transform: translateY(-2px);
      box-shadow: var(--elev-2);
      border-color: rgb(var(--primary-rgb) / .3);
    }
    .btn, .nav button, .tab, .deal-tab, .next-action-btn {
      transition: background var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out),
                  color var(--dur-2) var(--ease-out), transform var(--dur-1) var(--ease-out),
                  box-shadow var(--dur-2) var(--ease-out) !important;
    }
    /* Отклик на нажатие. Раньше кнопка поднималась на hover и НИКАК не отвечала на
       клик — на телефоне, где hover нет вовсе, нажатие вообще не подтверждалось. */
    .btn:active, .nav button:active, .tab:active, .deal-tab:active, .next-action-btn:active,
    .icon-btn:active, .topbar-icon-btn:active {
      transform: scale(.96) !important;
    }
    .deal-card:active, .project-card:active, .client-card:active, .package-card:active {
      transform: translateY(-1px) scale(.995);
    }
    .funnel-stage {
      transition: border-color var(--dur-2) var(--ease-out), background var(--dur-2) var(--ease-out),
                  transform var(--dur-1) var(--ease-out);
    }

    /* ── LOGO ANIMATION ──────────────────────────── */
    .logo {
      transition: transform var(--dur-2) var(--ease-out), box-shadow var(--dur-2) var(--ease-out) !important;
      cursor: pointer !important;
    }
    .logo:hover {
      transform: scale(1.07) !important;
      box-shadow: 0 6px 24px rgb(var(--primary-rgb) / .55) !important;
    }

    /* ── DEAL HEADER COMPACT ─────────────────────── */
    .deal-bar { padding: 16px 22px 0; }
    .deal-compact-row {
      display: flex; justify-content: space-between;
      align-items: center; gap: 14px; flex-wrap: wrap;
      margin-bottom: 14px;
      padding-bottom: 12px;
      border-bottom: 1px solid var(--line);
    }
    .deal-nav-group {
      display: flex; align-items: center; gap: 10px;
      flex-wrap: wrap; flex: 1; min-width: 0;
    }
    .deal-nav-group h2 { margin: 0; font-size: 19px; font-weight: 900; }
    .deal-actions-group {
      display: flex; align-items: center; gap: 10px; flex-wrap: wrap; flex: 0 0 auto;
    }
    /* ─── ТАРИФЫ ───────────────────────────────────────────────────────
       Тариф для тех, кто работает один: смета, КП и клиенты. Аналитика с
       графиками на главной — часть «Стандарта», и здесь её просто нет: гасим
       CSS по признаку на <html>, а не условиями внутри renderHome. */
    [data-tier="start"] .db-stat-row,
    [data-tier="start"] .db-analytics-row { display: none; }
    /* Главная тарифа «Старт»: ближайшие съёмки и долг вместо дашборда. Две
       карточки в ряд, на телефоне — одна под другой. */
    .start-home {
      display: grid; grid-template-columns: 1.6fr 1fr; gap: 12px; margin-bottom: 16px;
    }
    .start-home-card {
      display: flex; flex-direction: column; gap: 8px; align-items: stretch;
      padding: 16px 18px; border: 1px solid var(--line); border-radius: var(--r-xl);
      background: var(--panel2); color: var(--text); font: inherit; text-align: left; cursor: pointer;
      transition: border-color .15s, background .15s;
    }
    .start-home-card:hover { border-color: rgb(var(--primary-rgb) / .45); background: var(--panel); }
    .start-home-label {
      font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--muted);
    }
    .start-home-list { display: flex; flex-direction: column; gap: 8px; }
    .start-home-row { display: flex; align-items: baseline; gap: 10px; }
    .start-home-when {
      flex: 0 0 auto; min-width: 86px; font-size: 13px; font-weight: 800; color: var(--primary-text);
    }
    .start-home-name { flex: 1; min-width: 0; font-size: 14px; font-weight: 700; }
    .start-home-sum { flex: 0 0 auto; font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; }
    .start-home-empty { font-size: 12.5px; color: var(--muted); line-height: 1.45; }
    .start-home-debt {
      font-size: 26px; font-weight: 900; font-variant-numeric: tabular-nums; line-height: 1.1;
    }
    .start-home-debt.is-owed { color: var(--text-warning); }
    @media (max-width: 900px) {
      .start-home { grid-template-columns: 1fr; }
    }

    /* Срок оплаты — один переключатель наверху витрины. Тарифов два, сроков
       четыре; восемь карточек в ряд читались бы как восемь продуктов. */
    /* Строка «Ещё N позиций» в каталоге младшего тарифа — приглушённая, как
       «Настроить разделы»: это не раздел, а приглашение. */
    .catalog-cat-more { color: var(--primary-text); }
    .catalog-cat-more:hover { background: rgb(var(--primary-rgb) / .1); }
    .catalog-cat-more .catalog-cat-count { color: var(--muted); font-weight: 600; }
    /* Курсор НЕ inline: inline-стиль перебивает общее правило для :disabled,
       и отключённая кнопка выглядела рабочей. */
    .promo-apply-btn { cursor: pointer; }
    .plan-period-switch {
      display: inline-flex; flex-wrap: wrap; gap: 4px; margin: 0 0 18px;
      padding: 4px; border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--panel2);
    }
    .plan-period-btn {
      display: inline-flex; align-items: center; gap: 6px;
      padding: 8px 14px; border: 0; border-radius: var(--r-pill); background: none;
      font: inherit; font-size: 13px; font-weight: 700; color: var(--muted); cursor: pointer;
      transition: background .15s, color .15s;
    }
    .plan-period-btn:hover { color: var(--text); background: rgb(var(--primary-rgb) / .08); }
    .plan-period-btn.active { background: var(--primary); color: var(--on-color); }
    /* Палец: экран, на котором платят, — не место для целей в 34px. Растим
       саму кнопку, а не невидимый ::after: переключатель стоит в ряд, и
       расширения налезали бы друг на друга. */
    @media (hover: none) and (pointer: coarse) {
      .plan-period-btn { min-height: 44px; padding: 10px 16px; }
      .promo-apply-btn { min-height: 44px; }
    }
    /* На узком экране четыре срока в строку не помещаются, и капсула
       переносилась в две строки, оставаясь капсулой — форма врала о строении.
       Сетка 2×2 с обычными углами честнее и попадается пальцем. */
    @media (max-width: 640px) {
      .plan-period-switch { display: grid; grid-template-columns: 1fr 1fr; width: 100%; border-radius: var(--r-lg); }
      .plan-period-btn { justify-content: center; }
    }
    .plan-period-off { font-size: 11.5px; font-weight: 800; color: var(--text-success); }
    .plan-period-btn.active .plan-period-off { color: var(--on-color); opacity: .85; }
    /* Админка: продление подписки в один клик под полем даты. */
    .admin-sub-quick { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: -2px 0 12px; }
    /* Действующая подписка — строкой над витриной: что сейчас и до когда. */
    .plans-current {
      display: flex; align-items: center; gap: 8px; margin: 0 0 16px;
      padding: 10px 14px; border-radius: var(--r-md); font-size: 13px; font-weight: 600;
      color: var(--text-success); background: color-mix(in srgb, var(--green) 10%, transparent);
      border: 1px solid color-mix(in srgb, var(--green) 30%, transparent);
    }
    .plans-current.is-admin { color: var(--primary-text); background: var(--primary-bg); border-color: rgb(var(--primary-rgb) / .3); }
    .plans-current.is-trial { color: var(--text-warning); background: color-mix(in srgb, var(--yellow) 10%, transparent); border-color: color-mix(in srgb, var(--yellow) 35%, transparent); }
    /* ── Витрина тарифов ─────────────────────────────────────────────────
       Всё по центру одной колонкой (карточки, условия, таблица): карточки
       стояли прижатыми влево на две трети экрана, а таблица — во всю ширину
       (владелец 30.09.2026: «должно выглядеть лучше»). */
    .plans-wrap { max-width: 1120px; margin: 0 auto; }
    .plans-period-row { display: flex; justify-content: center; }
    .plans-page .plan-period-switch { margin: 4px 0 26px; }
    .plans-tier-grid {
      display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px;
      align-items: stretch; margin-bottom: 18px;
    }
    @media (max-width: 900px) { .plans-tier-grid { grid-template-columns: 1fr; max-width: 460px; margin-left: auto; margin-right: auto; } }
    .plans-page .plan-card {
      position: relative; display: flex; flex-direction: column; min-width: 0; text-align: left;
      padding: 24px 22px 20px; border: 1px solid var(--line); border-radius: var(--r-2xl); background: var(--panel2);
      transition: transform var(--dur-3) var(--ease-out), box-shadow var(--dur-3) var(--ease-out), border-color var(--dur-2);
    }
    .plans-page .plan-card:hover { transform: translateY(-3px); border-color: rgb(var(--primary-rgb) / .35); box-shadow: 0 18px 40px -22px rgb(var(--primary-rgb) / .5); }
    .plans-page .plan-card.is-recommended {
      border-color: rgb(var(--primary-rgb) / .7);
      background: linear-gradient(180deg, rgb(var(--primary-rgb) / .12), transparent 55%), var(--panel2);
      box-shadow: 0 20px 44px -24px rgb(var(--primary-rgb) / .65);
    }
    .plans-page .plan-card.is-current {
      border-color: color-mix(in srgb, var(--green) 60%, var(--line));
      background: linear-gradient(180deg, color-mix(in srgb, var(--green) 10%, transparent), transparent 55%), var(--panel2);
    }
    .plan-card-badge {
      position: absolute; top: -11px; left: 22px; display: inline-flex; align-items: center; gap: 4px;
      padding: 3px 10px; border-radius: var(--r-pill); font-size: 11.5px; font-weight: 800; white-space: nowrap;
      background: var(--primary); color: var(--on-color); box-shadow: 0 6px 16px -8px rgb(var(--primary-rgb) / .8);
    }
    .plan-card-badge.is-current { background: var(--green); box-shadow: 0 6px 16px -8px color-mix(in srgb, var(--green) 80%, transparent); }
    .plans-page .plan-card-head { display: flex; flex-direction: column; gap: 2px; margin-bottom: 14px; }
    .plans-page .plan-card-name { font-family: var(--font-display); font-size: 19px; font-weight: 700; letter-spacing: -.01em; }
    .plans-page .plan-card-note { font-size: 12px; color: var(--muted); font-weight: 600; }
    .plans-page .plan-price { margin: 0 0 16px; padding-bottom: 16px; border-bottom: 1px dashed var(--line); font-size: inherit; font-weight: inherit; }
    .plan-price-row { display: flex; align-items: baseline; gap: 6px; }
    .plan-price-num {
      display: inline-block; font-family: var(--font-display); font-size: 34px; font-weight: 700;
      line-height: 1.05; letter-spacing: -.02em; font-variant-numeric: tabular-nums;
    }
    .plan-price-num.is-free, .plan-price-num.is-discount { color: var(--text-success); }
    .plan-price-per { font-size: 13px; color: var(--muted); font-weight: 600; }
    .plan-price-old { display: block; font-size: 13px; color: var(--muted); text-decoration: line-through; }
    .plan-price-note { margin-top: 4px; font-size: 12px; font-weight: 700; color: var(--primary-text); min-height: 16px; }
    .plan-feats { list-style: none; margin: 0 0 18px; padding: 0; display: flex; flex-direction: column; gap: 8px; flex: 1 1 auto; }
    .plan-feats li { display: flex; align-items: flex-start; gap: 8px; font-size: 13px; line-height: 1.4; }
    .plan-feats li svg {
      flex: 0 0 auto; width: 18px; height: 18px; padding: 3px; margin-top: 0; border-radius: 50%;
      color: var(--primary-text); background: var(--primary-bg);
    }
    .plans-page .plan-card.is-current .plan-feats li svg { color: var(--text-success); background: color-mix(in srgb, var(--green) 14%, transparent); }
    .plan-card-btn { width: 100%; justify-content: center; white-space: normal; line-height: 1.25; min-height: 42px; font-weight: 700; }
    /* Вход волной при заходе на экран; цены «переезжают» при смене срока. */
    #appContent.plans-enter .plans-page .plan-card { animation: planIn .55s var(--ease-out) both; animation-delay: calc(var(--i, 0) * 80ms); }
    @keyframes planIn { from { opacity: 0; transform: translateY(14px) scale(.98); } }
    .plans-page.is-period-switch .plan-price-num,
    .plans-page.is-period-switch .plan-price-note { animation: priceIn .4s var(--ease-out) both; }
    @keyframes priceIn { from { opacity: 0; transform: translateY(8px); } }
    @media (prefers-reduced-motion: reduce) {
      .plans-page .plan-card, .plans-page .plan-card:hover { transition: none; transform: none; }
      #appContent.plans-enter .plans-page .plan-card, .plans-page.is-period-switch .plan-price-num,
      .plans-page.is-period-switch .plan-price-note { animation: none; }
    }
    /* Условия — списком со значками, а не абзацем мелким шрифтом. */
    .plans-facts {
      list-style: none; margin: 18px 0 0; padding: 16px 18px; display: grid;
      grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 22px;
      border: 1px solid var(--line); border-radius: var(--r-xl); background: var(--panel2);
    }
    .plans-facts li { display: flex; align-items: flex-start; gap: 8px; font-size: 12.5px; color: var(--muted); line-height: 1.45; }
    .plans-facts li svg { flex: 0 0 auto; margin-top: 2px; color: var(--text-success); }
    @media (max-width: 700px) { .plans-facts { grid-template-columns: 1fr; } }

    /* ── Сравнение тарифов ─────────────────────────────────────────────── */
    .plans-compare-wrap { margin-top: 34px; }
    .plans-compare-title { margin: 0 0 12px; font-size: 17px; font-family: var(--font-display); }
    .plans-compare-scroll { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r-xl); }
    .plans-compare { width: 100%; min-width: 600px; border-collapse: collapse; font-size: 13px; }
    .plans-compare thead th { padding: 14px 10px; border-bottom: 1px solid var(--line); background: var(--panel2); }
    .plans-compare .pc-label-head { text-align: left; padding-left: 16px; font-size: 12px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; min-width: 200px; }
    .plans-compare .pc-col { text-align: center; white-space: nowrap; }
    .pc-col-name { display: block; font-size: 13.5px; font-weight: 800; }
    .pc-col-sub { display: block; margin-top: 2px; font-size: 11.5px; font-weight: 600; color: var(--muted); }
    .plans-compare .is-hl { background: rgb(var(--primary-rgb) / .08); }
    .plans-compare thead .pc-col.is-hl { box-shadow: inset 0 3px 0 var(--primary); }
    .plans-compare .pc-row td { padding: 10px; border-bottom: 1px solid color-mix(in srgb, var(--line) 60%, transparent); }
    .plans-compare .pc-row:hover td { background: rgb(var(--primary-rgb) / .05); }
    .plans-compare .pc-row:hover td.is-hl { background: rgb(var(--primary-rgb) / .12); }
    .plans-compare .pc-label { padding-left: 16px; color: var(--text); }
    .plans-compare .pc-cell { text-align: center; font-variant-numeric: tabular-nums; }
    .plans-compare .pc-group td {
      padding: 14px 16px 6px; font-size: 11.5px; font-weight: 800; color: var(--muted);
      text-transform: uppercase; letter-spacing: .05em; background: transparent;
    }
    .pc-yes {
      display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 50%;
      color: var(--text-success); background: color-mix(in srgb, var(--green) 14%, transparent);
    }
    .pc-no { color: var(--muted); opacity: .6; }
    .pc-inf { font-size: 12px; font-weight: 700; color: var(--text-success); white-space: nowrap; }
    /* Телефон: колонка с названиями стоит на месте, тарифы листаются под ней —
       иначе названия съедали полэкрана и был виден один тариф из четырёх. */
    @media (max-width: 700px) {
      .plans-compare { min-width: 0; font-size: 12.5px; }
      .plans-compare .pc-label-head, .plans-compare .pc-label {
        position: sticky; left: 0; z-index: 1; min-width: 132px; max-width: 150px;
        padding-left: 12px; background: linear-gradient(var(--panel), var(--panel)), var(--bg); box-shadow: 1px 0 0 var(--line);
      }
      .plans-compare .pc-label-head { background: linear-gradient(var(--panel2), var(--panel2)), var(--bg); }
      .plans-compare .pc-row:hover td.pc-label { background: linear-gradient(var(--panel), var(--panel)), var(--bg); }
      .plans-compare .pc-col, .plans-compare .pc-cell { min-width: 84px; padding-left: 6px; padding-right: 6px; }
      .plans-compare .pc-group td { padding-left: 12px; }
      .plans-compare .pc-group td span { position: sticky; left: 12px; }
    }
    .plans-tier-head {
      margin: 0 0 10px; font-size: 12px; font-weight: 800;
      letter-spacing: .04em; text-transform: uppercase; color: var(--muted);
    }
    .upsell-list { display: flex; flex-direction: column; gap: 8px; margin: 0 0 16px; }
    .upsell-point {
      display: flex; align-items: center; gap: 8px;
      font-size: 13px; color: var(--text);
    }
    .upsell-point svg { color: var(--text-success); flex: 0 0 auto; }
    .upsell-foot {
      display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
      padding-top: 12px; border-top: 1px solid var(--line);
    }
    /* Карточка в подвале меню: на «Соло» закрытых разделов в списке нет, и это
       единственное место, которое говорит, куда расти. */
    .sidebar-tier-card {
      display: block; width: 100%; text-align: left;
      margin: 8px 10px 0; padding: 12px; width: auto;
      border: 1px solid rgb(var(--primary-rgb) / .3); border-radius: var(--r-lg);
      background: rgb(var(--primary-rgb) / .07); color: var(--text); cursor: pointer;
      font: inherit; transition: border-color .15s, background .15s;
    }
    .sidebar-tier-card:hover { border-color: rgb(var(--primary-rgb) / .55); background: rgb(var(--primary-rgb) / .12); }
    .sidebar-tier-card b { display: block; font-size: 13px; font-weight: 800; margin-bottom: 2px; }
    .sidebar-tier-card span { display: block; font-size: 11.5px; color: var(--muted); line-height: 1.4; }

    /* Деньги сделки — кнопкой: числа ведут туда, где их заводят («Финансы»). */
    .deal-stats-inline {
      display: flex; flex-direction: column; align-items: stretch; gap: 6px;
      padding: 8px 14px; border-radius: var(--r-lg);
      background: var(--panel2); border: 1px solid var(--line);
      font: inherit; color: inherit; text-align: inherit; cursor: pointer;
      transition: border-color .15s, background .15s;
    }
    .deal-stats-inline:hover { border-color: rgb(var(--primary-rgb) / .45); background: var(--panel); }
    .deal-stats-row { display: flex; align-items: center; gap: 16px; }
    /* Шкала внутри кнопки собрана на <span> — без display:block ширина заливки
       не работает вовсе (строчный элемент её игнорирует). */
    .deal-stats-bar { display: block; height: 4px; margin: 0; }
    .deal-stats-bar > span { display: block; }
    /* Пара «Оплачено / Долг» в «Итогах сметы» — только там, где полосы сверху
       нет: ниже 641px она скрыта. Иначе те же числа стояли бы на экране дважды. */
    @media (min-width: 641px) {
      .summary-pay-block { display: none; }
    }
    .deal-stat-item { display: flex; flex-direction: column; align-items: flex-end; }
    .deal-stat-item span { font-size: 12px; color: var(--muted); font-weight: 600; letter-spacing: .04em; text-transform: uppercase; }
    .deal-stat-item strong { font-size: 15px; font-weight: 900; }
    .deal-stat-sep { width: 1px; height: 28px; background: var(--line); flex: 0 0 auto; }
    .crm-select-pill {
      padding: 5px 10px; border-radius: var(--r-pill); font-size: 12px; font-weight: 600;
      background: rgb(var(--primary-rgb) / .15); border: 1px solid rgb(var(--primary-rgb) / .3); color: var(--text);
      cursor: pointer; max-width: 130px;
    }

    /* ── DEAL SWITCHER ────────────────────────────── */
    .deal-switcher { position: relative; flex: 0 0 auto; }
    .deal-switcher-btn {
      display: flex; align-items: center; gap: 8px;
      padding: 6px 12px 6px 14px; border-radius: var(--r-md);
      background: var(--panel2); border: 1px solid var(--line);
      color: var(--text); font-size: 14px; font-weight: 800;
      cursor: pointer; transition: border-color var(--dur-1), background var(--dur-1);
      max-width: 240px; min-width: 0;
    }
    .deal-switcher-btn:hover { border-color: var(--primary-text); background: var(--panel2); }

    /* Две строки внутри кнопки: подпись «Сделка» мелким и приглушённым, название —
       крупным. Без подписи кнопка читалась как заголовок экрана, а не как выбор. */
    .deal-switcher-btn-icon { flex: 0 0 auto; display: inline-flex; color: var(--muted); }
    .deal-switcher-btn-text {
      display: flex; flex-direction: column; align-items: flex-start;
      gap: 0; min-width: 0; flex: 1;
    }
    .deal-switcher-btn-caption {
      font-size: 11px; font-weight: 600; color: var(--muted);
      line-height: 1.1; letter-spacing: .02em;
    }
    .deal-switcher-btn-label {
      overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
      max-width: 100%; line-height: 1.25;
    }
    .deal-switcher-chevron {
      flex: 0 0 auto; font-size: 12px; color: var(--muted);
      transition: transform var(--dur-1);
    }
    .deal-switcher-chevron.open { transform: rotate(180deg); }
    .deal-switcher-backdrop {
      position: fixed; inset: 0; z-index: var(--z-modal);
      background: rgba(8,9,16,.45);
      animation: fadeIn .18s ease;
    }
    [data-theme="light"] .deal-switcher-backdrop { background: rgba(20,20,24,.25); }
    .deal-switcher-panel {
      position: fixed; top: 0; bottom: 0;
      width: min(320px, 92vw);
      z-index: calc(var(--z-modal) + 1);
      background: var(--panel); border-right: 1px solid var(--line);
      box-shadow: 12px 0 48px rgba(0,0,0,.35);
      display: flex; flex-direction: column;
      animation: dealSwitcherSlideIn .22s cubic-bezier(.16,1,.3,1);
    }
    @keyframes dealSwitcherSlideIn {
      from { opacity: 0; transform: translateX(-16px); }
      to   { opacity: 1; transform: translateX(0); }
    }
    .deal-switcher-panel-header {
      display: flex; align-items: center; gap: 10px;
      padding: 16px 16px 12px; border-bottom: 1px solid var(--line);
      flex: 0 0 auto;
    }
    .deal-switcher-panel-header h3 { margin: 0; font-size: 15px; flex: 1; }
    .deal-switcher-panel-close {
      background: none; border: none; color: var(--muted);
      font-size: 20px; line-height: 1; cursor: pointer; padding: 2px 4px;
    }
    .deal-switcher-panel-close:hover { color: var(--text); }
    .deal-switcher-search-wrap { padding: 10px 16px; border-bottom: 1px solid var(--line); flex: 0 0 auto; }
    .deal-switcher-search {
      width: 100%; padding: 8px 12px; border-radius: var(--r-sm);
      border: 1px solid var(--line); background: var(--input);
      color: var(--text); font-size: 13px;
    }
    .deal-switcher-panel-list { flex: 1 1 auto; overflow-y: auto; padding: 8px; }
    .deal-switcher-panel-empty { padding: 28px 16px; text-align: center; color: var(--muted); font-size: 13px; }
    .deal-switcher-section-label {
      display: flex; align-items: center; justify-content: space-between; width: 100%;
      padding: 10px 8px 4px; font-size: 12px; font-weight: 700; font-family: inherit;
      color: var(--muted); text-transform: uppercase; letter-spacing: .06em;
      background: none; border: none; cursor: pointer; border-radius: var(--r-sm);
    }
    .deal-switcher-section-label:hover { background: var(--panel2); }
    .deal-switcher-section-label:first-child { padding-top: 4px; }
    .deal-switcher-section-label.active-label { color: var(--text-success); }
    /* «Завершённые» — итог работы, а не тревога: красным они читались как
       проблема (владелец 28.09). Подпись приглушённая, галочка — зелёная. */
    .deal-switcher-section-label.completed-label { color: var(--muted); }
    .deal-switcher-section-label.completed-label svg { color: var(--text-success); opacity: 1; }
    .deal-switcher-section-label.archived-label { color: var(--muted); }
    /* Иконка и подпись — одна группа: значок наследует цвет секции. */
    .deal-switcher-section-title {
      display: inline-flex; align-items: center; gap: 7px; min-width: 0;
    }
    .deal-switcher-section-title svg { flex: 0 0 auto; opacity: .85; }
    .deal-switcher-section-chevron { transition: transform var(--dur-1); font-size: 11px; }
    .deal-switcher-section-chevron.open { transform: rotate(180deg); }
    .deal-switcher-item {
      display: flex; flex-direction: column; gap: 5px;
      padding: 12px 14px; border-radius: var(--r-md);
      cursor: pointer; transition: background var(--dur-1), border-color var(--dur-1);
      /* Обводка и своя заливка. Зазора между строками оказалось мало: строки лежали
         прозрачными на фоне колонки, и границу сделки приходилось угадывать по
         цветной полоске слева — где сделка кончилась и началась соседняя, глаз не
         видел. Теперь каждая строка — карточка по правилам DESIGN.md: 1px --line и
         фон --panel, на тон отличный от --panel2 колонки, в обеих темах. */
      background: var(--panel);
      border: 1px solid var(--line);
      /* Зазор между строками. Раньше карточки шли впритык, и при трёх строках
         текста (название, клиент, этап с суммой) список читался сплошной простынёй:
         граница между соседними сделками была видна только по цветной полоске слева.
         Отступ на самом элементе, а не gap у контейнера, — список рисуется в двух
         разных обёртках (.deal-rail-list в колонке и .deal-switcher-panel-list в
         выезжающей панели), и в обеих между строками лежат ещё и заголовки секций. */
      margin-bottom: 6px;
      position: relative;
    }
    /* Подсветки при наведении НЕ БЫЛО ВООБЩЕ: правило красило строку в
       var(--panel2), а сама колонка .deal-rail уже этого цвета — курсор закрашивал
       строку тем же, что под ней. Замер: и в покое, и под курсором строка выходила
       rgba(244,246,249,.98).

       Тинт акцента, а не var(--panel): это тот же язык, что у выбранной строки
       (.active — .13), только слабее, поэтому «навёл» и «выбрано» читаются как
       одно семейство, а не два разных состояния. Тинт работает поверх любого фона,
       значит переживёт и смену цветовой схемы, и обе темы. */
    /* Ручка переноса появляется по наведению на строку, а не висит всегда: иначе
       она спорит за внимание с названием сделки. Сам рисунок и размеры — общие
       (.drag-handle выше), здесь только правило появления.

       На тач-устройствах наведения НЕТ, и ручка была бы не видна никогда —
       поэтому там она показана всегда, приглушённо. */
    .deal-switcher-item .drag-handle--corner {
      opacity: 0;
      pointer-events: none;
      transition: opacity var(--dur-1);
    }
    .deal-switcher-item:hover .drag-handle--corner { opacity: .55; }
    @media (hover: none) {
      .deal-switcher-item .drag-handle--corner { opacity: .38; }
    }
    /* touch-action: none обязателен — иначе браузер на телефоне забирает
       вертикальное движение себе под прокрутку и pointermove до нас не доходит.
       Прокрутку списка это не ломает: перенос стартует только после удержания
       (TOUCH_HOLD_MS), а до него уход пальца отменяет жест — см. dealPointerMove. */
    .deal-switcher-item-draggable { touch-action: none; }
    /* Тинт лежит СЛОЕМ поверх заливки карточки (background-image), а не вместо неё:
       иначе на наведении строка теряла бы фон --panel и переставала читаться
       карточкой ровно в тот момент, когда человек ею занят. */
    .deal-switcher-item:hover {
      background-image: linear-gradient(rgb(var(--primary-rgb) / .07), rgb(var(--primary-rgb) / .07));
      border-color: rgb(var(--primary-rgb) / .35);
    }
    .deal-switcher-item.active {
      background-image: linear-gradient(rgb(var(--primary-rgb) / .13), rgb(var(--primary-rgb) / .13));
      border-color: rgb(var(--primary-rgb) / .5);
    }
    /* Специфичность .active и :hover одинакова, и .active идёт позже — без этого
       правила наведение на выбранную строку не давало бы никакого отклика. */
    .deal-switcher-item.active:hover {
      background-image: linear-gradient(rgb(var(--primary-rgb) / .19), rgb(var(--primary-rgb) / .19));
    }
    /* Полоса слева — цвет ЭТАПА сделки (--st-color из CRM_STATUS_COLOR), тот же,
       что у точки этапа и у полосы на карточках главной. Была зелёной у всех
       активных, а у завершённых — красной. */
    .deal-switcher-item.active-deal { border-left: 3px solid var(--st-color, var(--green)); }
    .deal-switcher-item.completed-deal { border-left: 3px solid color-mix(in srgb, var(--green) 45%, transparent); opacity: .75; }
    .deal-switcher-item.completed-deal:hover { opacity: 1; }
    .deal-switcher-item.archived-deal { border-left: 2px solid var(--muted); opacity: .6; }
    .deal-switcher-item.archived-deal:hover { opacity: 1; }
    /* Имя сделки — ДВЕ строки, а не одна с многоточием.
       Замер на живой колонке (262px): у названия вроде «Реклама – База отдыха
       "Раздолье – Троица"» не влезало 59px, и обрезалось ровно то, что отличает
       сделку от соседней. В боковом списке имя — единственный способ узнать
       карточку, поэтому лучше отдать ему вторую строку, чем ряд одинаковых
       «Реклама – База отды…».

       padding-right — под ручку переноса: она лежит абсолютом в правом верхнем
       углу, и текст уходил ПОД неё. Замер: ручка начиналась на 12px раньше, чем
       кончалось имя, то есть многоточие рисовалось прямо под точками. */
    .deal-switcher-item-name {
      font-size: 13px; font-weight: 800; line-height: 1.3;
      padding-right: 22px;
      display: -webkit-box;
      -webkit-line-clamp: 2;
      -webkit-box-orient: vertical;
      overflow: hidden;
      overflow-wrap: anywhere;
    }
    .deal-switcher-item-sub {
      display: flex; align-items: center; gap: 8px;
      font-size: 12px; color: var(--muted);
    }
    /* Ярусы строки сделки: название → клиент → этап и сумма → срок.
       min-width:0 обязателен на всех — иначе длинное название клиента распирает
       колонку шире её дорожки в гриде. */
    .deal-switcher-item-client {
      font-size: 12px; color: var(--muted); min-width: 0;
      overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    .deal-switcher-item-meta {
      display: flex; align-items: center; justify-content: space-between;
      gap: 10px; font-size: 12px; min-width: 0;
    }
    .deal-switcher-item-stage {
      display: inline-flex; align-items: center; gap: 6px;
      color: var(--muted); min-width: 0;
      overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    /* Точка этапа — тот же язык, что на карточках главной. Пилюля здесь читалась
       как кнопка: форма на краю строки — это язык действия, а не свойства. */
    .deal-switcher-item-stage i {
      width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto;
    }
    .deal-switcher-item-sum {
      font-weight: 800; color: var(--text); flex: 0 0 auto;
      font-variant-numeric: tabular-nums;
    }
    /* Срок стоит в строке статуса, отделённый точкой: «● В работе · 27.08.2026».
       Отдельной четвёртой строкой он висел без подписи и без связи с соседями —
       и добавлял карточке высоты на ровном месте. */
    .deal-switcher-item-date {
      font-size: 11px; font-weight: 700; flex: 0 0 auto;
      display: inline-flex; align-items: center; gap: 4px;
    }
    .deal-switcher-item-date svg { opacity: .8; }
    /* Полоса оплаты внизу строки: сколько из суммы сделки уже получено. */
    .deal-switcher-item-pay {
      display: block; height: 3px; margin-top: 3px; border-radius: var(--r-pill);
      background: var(--line); overflow: hidden;
    }
    .deal-switcher-item-pay > span {
      display: block; height: 100%; border-radius: var(--r-pill); background: var(--green);
    }

    /* ── ANALYTICS DATE FILTER ────────────────────── */
    .analytics-date-bar {
      display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
      margin-bottom: 18px; padding: 12px 16px;
      background: var(--panel2); border-radius: var(--r-lg);
      border: 1px solid var(--line);
    }
    /* Обёртка вокруг самих пилюль-пресетов (без подписи «Период:» и кастомных дат) —
       на мобильном превращается в горизонтальный скролл, см. media-запрос ниже. */
    /* Полосу прокрутки под лентой периодов прячем: на телефоне она рисуется
       серой чертой под чипами и читается как подчёркивание выбранного. */
    .date-preset-row::-webkit-scrollbar { display: none; }
    .date-preset-row {
      scrollbar-width: none; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
    .date-preset-btn {
      padding: 6px 12px; border-radius: var(--r-pill); border: 1px solid var(--line);
      min-height: var(--h-sm);
      background: transparent; color: var(--muted); font-size: 12px; font-weight: 600;
      cursor: pointer; transition: var(--dur-1);
    }
    .date-preset-btn.active {
      background: var(--primary);
      border-color: transparent; color: var(--on-color);
    }
    .date-preset-btn:hover:not(.active) { border-color: rgb(var(--primary-rgb) / .5); color: var(--text); }
    .date-range-inputs {
      display: flex; align-items: center; gap: 6px; margin-left: 8px;
    }
    .date-range-inputs input {
      width: 130px; padding: 5px 10px; font-size: 12px; border-radius: var(--r-md);
    }

    /* ── SVG CHART ────────────────────────────────── */
    .svg-chart-wrap { overflow-x: auto; padding-bottom: 8px; }
    .svg-chart-tooltip {
      position: absolute; background: var(--panel); border: 1px solid var(--line);
      border-radius: var(--r-md); padding: 8px 12px; font-size: 12px;
      pointer-events: none; z-index: 50; white-space: nowrap;
      box-shadow: 0 8px 24px rgba(0,0,0,.3);
    }

    /* ── REAL CALENDAR GRID ─────────────────────── */
    /* ── CALENDAR HEADER ──────────────────────────── */
    .cal-header {
      display: flex; align-items: flex-start; justify-content: space-between;
      gap: 12px; margin-bottom: 16px; flex-wrap: wrap;
    }
    .cal-header-title h1 { margin: 0 0 2px; }
    .cal-header-sub { margin: 0; font-size: 13px; color: var(--muted); }

    /* ── CALENDAR NAV BAR ─────────────────────────── */
    .cal-nav2 {
      display: flex; align-items: center; justify-content: space-between;
      gap: 10px;
      background: var(--panel2); border: 1px solid var(--line);
      border-radius: var(--r-lg); padding: 8px 12px;
      margin-bottom: 10px;
    }
    .cal-nav2-center {
      display: flex; align-items: center; gap: 10px; min-width: 0;
    }
    .cal-nav2-arrow {
      width: 32px; height: 32px; border-radius: var(--r-sm);
      border: 1px solid var(--line); background: transparent;
      color: var(--muted); font-size: 16px; cursor: pointer;
      transition: var(--dur-1); display: flex; align-items: center; justify-content: center;
      flex-shrink: 0;
    }
    .cal-nav2-arrow:hover { border-color: rgb(var(--primary-rgb) / .5); color: var(--primary-on-tint); background: rgb(var(--primary-rgb) / .08); }
    .cal-nav2-month {
      font-size: 18px; font-weight: 900; color: var(--text);
      min-width: 130px; text-align: center; letter-spacing: -.02em;
      font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .cal-nav2-today {
      padding: 7px 14px; border-radius: var(--r-md); font-size: 13px; font-weight: 600;
      background: var(--primary);
      border: none; color: var(--on-color); cursor: pointer; transition: background var(--dur-1); white-space: nowrap;
      flex-shrink: 0;
    }
    .cal-nav2-today:hover { background: var(--primary2); }
    .cal-nav2-today:hover { opacity: .88; }
    @media (max-width: 768px) {
      .cal-nav2 { padding: 7px 8px; gap: 6px; }
      .cal-nav2-center { gap: 4px; }
      .cal-nav2-month { font-size: 15px; min-width: 0; }
      .cal-nav2-today { padding: 7px 12px; font-size: 12px; }
      .cal-header-sub { display: none; }
    }

    /* ── MONTH PILLS (scrollable row) ─────────────── */
    .cal-months-scroll {
      display: flex; flex-wrap: nowrap; gap: 5px; margin-bottom: 14px;
      overflow-x: auto; -webkit-overflow-scrolling: touch; padding: 2px 4px 6px;
      scrollbar-width: none;
      -webkit-mask-image: linear-gradient(to right, transparent 0, #000 14px, #000 calc(100% - 14px), transparent 100%);
      mask-image: linear-gradient(to right, transparent 0, #000 14px, #000 calc(100% - 14px), transparent 100%);
    }
    .cal-months-scroll::-webkit-scrollbar { display: none; }
    .cal-month-pill {
      padding: 5px 12px; border-radius: var(--r-pill); border: 1px solid var(--line);
      background: transparent; color: var(--muted); font-size: 12px; font-weight: 600;
      cursor: pointer; transition: var(--dur-1); white-space: nowrap; flex-shrink: 0;
    }
    .cal-month-pill:hover { border-color: rgb(var(--primary-rgb) / .5); color: var(--text); }
    .cal-month-pill.active {
      background: rgb(var(--primary-rgb) / .18); border-color: var(--primary-on-tint);
      color: var(--primary-on-tint);
    }
    @media (max-width: 768px) {
      .cal-months-scroll { gap: 4px; }
      .cal-month-pill { padding: 5px 10px; font-size: 12px; }
    }
    .cal-weekdays {
      display: grid; grid-template-columns: repeat(7, 1fr);
      gap: 4px; margin-bottom: 4px;
    }
    .cal-weekday {
      text-align: center; font-size: 12px; font-weight: 700;
      color: var(--muted); padding: 6px 0; letter-spacing: .04em;
      text-transform: uppercase;
    }
    .cal-weekday.weekend { color: var(--text-danger); opacity: .7; }
    .cal-grid-wrap {
      display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px;
    }
    .cal-cell {
      min-height: 80px; padding: 7px 8px;
      border: 1px solid var(--line);
      border-radius: var(--r-md); background: var(--panel2);
      cursor: pointer; transition: var(--dur-1); position: relative;
      overflow: hidden;
    }
    .cal-cell:hover { border-color: rgb(var(--primary-rgb) / .45); background: rgb(var(--primary-rgb) / .06); }
    .cal-cell.today {
      border-color: var(--primary-on-tint);
      background: rgb(var(--primary-rgb) / .1);
      box-shadow: 0 0 0 2px rgb(var(--primary-rgb) / .22) inset;
    }
    .cal-cell.other-month { opacity: .32; }
    .cal-cell.selected { border-color: var(--blue); background: rgba(37,99,235,.1); }
    .cal-cell.weekend .cal-day-num { color: var(--text-danger); opacity: .7; }
    .gcal-more { display: block; margin-top: 1px; font-size: 12px; color: var(--muted); white-space: nowrap; }
    @media (max-width: 640px) { .gcal-more-word { display: none; } }

    /* «+ Задача» живёт в самой ячейке — так она знает свой день. Появляется
       по наведению на ячейку; на тач-экране её нет вовсе (см. ниже) — там день
       открывается тапом, и кнопка стоит подписанной в панели дня.

       `pointer-events: none`, пока кнопка невидима, — не украшение, а суть:
       прозрачный элемент продолжает ловить нажатия, а на телефоне ему ещё и
       расширяли область до 44×44 в ячейке 46×56 — то есть НЕВИДИМАЯ кнопка
       перекрывала день целиком. Тап по дню не выбирал его, а молча заводил
       задачу в открытой сделке и уводил с календаря на «Задачи проекта». */
    .cal-day-add {
      position: absolute; top: 4px; right: 4px;
      width: 22px; height: 22px; padding: 0;
      display: grid; place-items: center;
      border: 1px solid transparent; background: transparent;
      border-radius: var(--r-sm); color: var(--muted);
      cursor: pointer; opacity: 0; pointer-events: none;
      transition: opacity var(--dur-1), background var(--dur-1), border-color var(--dur-1), color var(--dur-1);
    }
    .cal-cell:hover .cal-day-add { opacity: 1; pointer-events: auto; }
    .cal-day-add:hover {
      background: var(--primary-bg); border-color: var(--primary-on-tint); color: var(--primary-on-tint);
    }
    .cal-day-add:focus-visible { opacity: 1; pointer-events: auto; outline: var(--ring-w) solid var(--ring); outline-offset: 1px; }
    /* Прошлые/будущие месяцы — не предлагаем ставить туда задачу */
    .cal-cell.other-month .cal-day-add { display: none; }
    .cal-day-num {
      font-size: 13px; font-weight: 900; color: var(--text);
      display: block; margin-bottom: 4px; line-height: 1;
    }
    .cal-cell.today .cal-day-num {
      color: var(--on-color); background: var(--primary); border-radius: 50%;
      width: 22px; height: 22px; display: flex; align-items: center; justify-content: center;
      font-size: 12px;
    }
    .cal-dots { display: flex; flex-wrap: wrap; gap: 3px; margin-top: 2px; }
    .cal-dot-item {
      width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto;
    }
    .cal-dot-item.deadline { background: var(--red); }
    .cal-dot-item.task { background: var(--blue); }
    .cal-dot-item.payment { background: var(--green); }
    .cal-dot-item.expense { background: var(--orange); }
    .cal-dot-item.google { background: var(--primary); }
    .cal-event-label {
      display: block; font-size: 12px; font-weight: 600;
      padding: 2px 4px; border-radius: var(--r-xs); margin-top: 2px;
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.3;
      /* Суммы в ячейках — табличными цифрами: в столбце дней они выстраиваются
         разряд под разряд и читаются как колонка, а не как случайные подписи. */
      font-variant-numeric: tabular-nums;
    }
    .cal-event-label.deadline { background: rgba(220,38,38,.18); color: var(--text-danger); }
    .cal-event-label.task { background: rgba(37,99,235,.18); color: var(--text-info); }
    .cal-event-label.payment { background: rgba(22,163,74,.18); color: var(--text-success); }
    .cal-event-label.expense { background: rgba(234,88,12,.18); color: var(--text-warning); }
    .cal-event-label.google { background: rgb(var(--primary-rgb) / .18); color: var(--primary-on-tint); }
    [data-theme="light"] .cal-event-label.deadline { color: var(--tint-red); }
    [data-theme="light"] .cal-event-label.task { color: var(--text-info); }
    [data-theme="light"] .cal-event-label.payment { color: var(--tint-green); }
    [data-theme="light"] .cal-event-label.expense { color: var(--tint-orange); }
    [data-theme="light"] .cal-event-label.google { color: var(--primary-text); }
    .cal-legend {
      display: flex; flex-wrap: wrap; gap: 12px; margin-top: 16px;
      padding: 12px 16px; border: 1px solid var(--line); border-radius: var(--r-lg);
      background: var(--panel2);
    }
    .cal-legend-item {
      display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted);
    }
    .cal-legend-dot {
      width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto;
    }
    .cal-day-panel {
      margin-top: 16px; padding: 16px;
      border: 1px solid var(--line); border-radius: var(--r-xl); background: var(--panel);
    }
    .cal-day-panel h3 { margin: 0 0 12px; font-size: 16px; }
    .cal-day-event-row {
      display: flex; align-items: center; gap: 10px;
      padding: 10px 0; border-bottom: 1px solid var(--line);
    }
    .cal-day-event-row:last-child { border-bottom: none; }
    .cal-day-event-type {
      width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto;
    }
    .cal-day-event-info { flex: 1; }
    .cal-day-event-info h4 { margin: 0 0 2px; font-size: 14px; }
    .cal-day-event-info p { margin: 0; font-size: 12px; color: var(--muted); }
    @media (max-width: 768px) {
      .cal-cell { min-height: 52px; padding: 5px 4px; }
      .cal-day-num { font-size: 12px; }
      .cal-event-label { display: none; }
    }

    /* ── GLOBAL CALENDAR ─────────────────────────── */
    .gcal-event {
      padding: 10px 14px;
      border-radius: var(--r-lg);
      border: 1px solid var(--line);
      background: var(--panel2);
      display: flex; justify-content: space-between;
      align-items: center; gap: 12px;
      transition: var(--dur-1);
    }
    .gcal-event:hover { border-color: rgb(var(--primary-rgb) / .45); }
    .gcal-event-left h4 { margin: 0 0 2px; font-size: 14px; }
    .gcal-event-left p { margin: 0; font-size: 12px; color: var(--muted); }
    .gcal-date-badge {
      flex: 0 0 auto; text-align: right;
      font-weight: 700; font-size: 13px; white-space: nowrap;
    }
    .gcal-date-overdue { color: var(--text-danger); }
    .gcal-date-soon { color: var(--text-warning); }
    .gcal-date-ok { color: var(--muted); }
    .gcal-group-header {
      font-size: 12px; font-weight: 700; color: var(--muted);
      letter-spacing: .05em; text-transform: uppercase;
      margin: 18px 0 8px;
    }
    .gcal-type-badge {
      display: inline-flex; align-items: center;
      padding: 3px 8px; border-radius: var(--r-pill); font-size: 12px;
      font-weight: 700; flex: 0 0 auto;
    }
    .gcal-type-badge.deadline { background: rgba(220,38,38,.12); color: var(--text-danger); }
    .gcal-type-badge.task { background: rgba(37,99,235,.12); color: var(--text-info); }
    .gcal-type-badge.payment { background: rgba(22,163,74,.12); color: var(--text-success); }
    [data-theme="light"] .gcal-type-badge.deadline { color: var(--text-danger); }
    [data-theme="light"] .gcal-type-badge.task { color: var(--text-info); }
    [data-theme="light"] .gcal-type-badge.payment { color: var(--text-success); }

    /* ── ANALYTICS TABS ──────────────────────────── */
    .analytics-section {
      padding: 18px;
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      background: var(--panel2);
      margin-bottom: 16px;
    }
    .analytics-section h3 { font-size: 14px; color: var(--muted); margin: 0 0 14px; font-weight: 700; }
    /* Строка списка-рейтинга. Сетка, а не флекс: у четырёх списков на странице
       имена, доли и суммы обязаны стоять в одних колонках — при флексе каждая
       строка выравнивалась сама по себе, и правый край «дышал». Имя получает
       190px и режется многоточием (полное — в подсказке), полоска забирает
       остаток, доля и сумма — фиксированные колонки справа. */
    .rank-row {
      display: grid;
      grid-template-columns: minmax(0, 190px) minmax(60px, 1fr) auto auto;
      align-items: center; gap: 12px; padding: 5px 0;
    }
    .rank-name {
      font-size: 13px; min-width: 0;
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .rank-track {
      height: 6px; border-radius: var(--r-pill);
      background: var(--line); overflow: hidden;
    }
    .rank-fill { display: block; height: 100%; border-radius: var(--r-pill); min-width: 2px; transition: width var(--dur-3); }
    .rank-share {
      font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums;
      min-width: 34px; text-align: right;
    }
    .rank-sum {
      font-size: 13px; font-weight: 800; font-variant-numeric: tabular-nums;
      white-space: nowrap; min-width: 96px; text-align: right;
    }
    /* На телефоне колонки не помещаются, и строка разворачивается в два этажа:
       имя и сумма В ОДНОЙ строке (их и сравнивают), полоска — под ними. Первая
       версия отправляла сумму на третий этаж, и каждая позиция занимала три
       строки: десять проектов растягивались на пол-экрана. */
    @media (max-width: 620px) {
      .rank-row {
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-areas: "name sum" "track track";
        gap: 3px 10px; padding: 7px 0;
      }
      .rank-name { grid-area: name; }
      .rank-sum { grid-area: sum; min-width: 0; }
      .rank-track { grid-area: track; }
      .rank-share { display: none; }
    }

    .category-bar-item {
      display: flex; align-items: center; gap: 10px; margin-bottom: 10px;
    }
    .category-bar-fill {
      height: 8px; border-radius: var(--r-pill); min-width: 2px; transition: width var(--dur-3);
    }
    .category-bar-label { font-size: 13px; flex: 1; }
    .category-bar-amount { font-size: 13px; font-weight: 900; white-space: nowrap; }
    .fin-subtab-bar {
      display: flex; gap: 4px; margin-bottom: 18px;
      border-bottom: 1px solid var(--line);
      /* Три вкладки не влезали в 320px (страница разъезжалась на 18px) — скроллим вбок,
         как .deal-tabs/.brief-type-tabs, а не переносим на вторую строку.
         Fade-маску по краям (как у .deal-tabs) СПЕЦИАЛЬНО не ставим: .deal-tabs гасит
         её отрицательным margin с обратным padding, чтобы затухание попадало на отступ,
         а не на текст. У .fin-subtab-bar два разных контейнера (Финансы и вкладка сделки)
         с разными отступами — подгонка была бы хрупкой, а на широком экране маска
         срезала бы первую букву «Аналитики». Подсказка о скролле и так есть: у края
         видно обрезанную вкладку. */
      overflow-x: auto; scrollbar-width: none;
    }
    .fin-subtab-bar::-webkit-scrollbar { display: none; }
    .fin-subtab {
      padding: 10px 16px; border: none; background: transparent;
      min-height: var(--h-sm);
      color: var(--muted); font-size: 13px; font-weight: 600;
      cursor: pointer; border-bottom: 2px solid transparent;
      transition: var(--dur-1); white-space: nowrap;
    }
    .fin-subtab.active {
      color: var(--primary-on-tint); border-bottom-color: var(--primary-on-tint);
      background: rgb(var(--primary-rgb) / .12); border-radius: var(--r-sm) var(--r-sm) 0 0;
    }
    .fin-subtab:hover { color: var(--text); }

    /* ── ВСЕ КП (compact list) ───────────────────── */
    .kp-list { display: flex; flex-direction: column; gap: 6px; }
    .kp-row {
      display: flex; align-items: center; gap: 10px;
      padding: 9px 12px; border: 1px solid var(--line); border-radius: var(--r-lg);
      background: var(--panel); transition: border-color var(--dur-1);
    }
    .kp-row:hover { border-color: var(--primary); }
    /* «Отправить КП» — тихая, пока строку не тронули. Шесть залитых акцентом
       кнопок подряд гасят друг друга; тот же приём уже стоит в списке сделок,
       каталоге, пакетах и админке. */
    .kp-row .btn.kp-send-btn.primary {
      background: transparent; color: var(--primary-text);
      border: 1px solid rgb(var(--primary-rgb) / .45);
      transition: background var(--dur-1), color var(--dur-1), border-color var(--dur-1);
    }
    .kp-row:hover .btn.kp-send-btn.primary,
    .kp-row .btn.kp-send-btn.primary:hover,
    .kp-row .btn.kp-send-btn.primary:focus-visible {
      background: var(--primary); color: var(--on-color); border-color: var(--primary);
    }
    .kp-row-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); flex-shrink: 0; }
    .kp-row-dot.green { background: var(--green); }
    .kp-row-main { flex: 1; min-width: 0; }
    .kp-row-name { font-weight: 700; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .kp-row-sub { font-size: 12px; color: var(--muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .kp-row-actions { display: flex; gap: 4px; flex-shrink: 0; }
    /* Сумма КП — своей колонкой, а не первой среди пяти значений серой строки. */
    .kp-row-sum {
      font-size: 13px; font-weight: 700; color: var(--text);
      font-variant-numeric: tabular-nums; flex-shrink: 0; white-space: nowrap;
      min-width: 96px; text-align: right;
    }
    /* Сколько КП ждёт ответа. После недели — предупреждением. */
    .kp-wait {
      font-size: 11px; font-weight: 700; flex-shrink: 0; white-space: nowrap;
      padding: 3px 9px; border-radius: var(--r-pill);
      background: var(--panel2); color: var(--muted);
    }
    .kp-wait.is-late { background: var(--tint-amber-bg); color: var(--text-warning); }

    /* Поиск + сортировка над списком КП */
    .kp-toolbar {
      display: flex; align-items: center; gap: 12px;
      flex-wrap: wrap; margin-bottom: 12px;
    }
    .kp-search { position: relative; flex: 1; min-width: 180px; }
    .kp-search-icon {
      position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
      color: var(--muted); display: grid; place-items: center; pointer-events: none;
    }
    .kp-search input { width: 100%; padding-left: 30px; }
    .kp-sort { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
    /* Отбор по метке в ряду поиска (админка). Подпись слева, чтобы список не
       читался как «ещё одна сортировка»; ширина по содержимому — меток обычно
       две-три, и растягивать поле не на что. */
    .kp-tag-filter { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
    .kp-tag-filter .u-meta { font-size: 12px; white-space: nowrap; }
    .kp-tag-filter select, .kp-tag-filter .uu-select-wrap { width: auto; min-width: 150px; }

    /* На телефоне четыре действия в один ряд с названием не помещаются —
       строка переносит их вниз, под сумму, вместо горизонтальной прокрутки
       (та же болезнь, что чинили в каталоге). */
    @media (max-width: 560px) {
      .kp-row { flex-wrap: wrap; }
      .kp-row-main { flex: 1 1 100%; order: 1; }
      .kp-row-dot { order: 0; }
      .kp-row .status-pill { order: 2; }
      .kp-row-actions { order: 3; margin-left: auto; }
      /* Строки «Без отправленного КП» несут обычную кнопку вместо ряда иконок —
         без своего order она по умолчанию уезжала выше названия сделки. */
      .kp-row > .btn { order: 3; margin-left: auto; }
      /* Четыре действия подряд пальцем: 32px — промах через раз. Ряд ушёл на
         отдельную строку, место есть (4×44 + зазоры ≈ 194px из ~340 доступных),
         поэтому берём полные 44, а не 40: замер 24.09.2026 показал ровно 40px —
         на один шаг ниже цели касания. Сторож это не ловил: у него в аккаунте
         КП нет, а без них список не рисуется. */
      .kp-row-actions { gap: 6px; }
      .kp-row-actions .icon-btn { width: 44px; height: 44px; }
      /* Сумма встаёт в свою строку рядом с метками, а не прижимается к краю. */
      .kp-row-sum { order: 2; }
      .kp-wait { order: 2; }
    }
    .icon-btn {
      background: none; border: 1px solid var(--line); border-radius: var(--r-sm);
      width: 32px; height: 32px; display: grid; place-items: center;
      color: var(--muted); cursor: pointer; flex-shrink: 0; transition: var(--dur-1);
    }
    .icon-btn:hover { color: var(--primary-text); border-color: var(--primary); }

    /* ── KPI-плитки в аналитике финансов ─────────── */
    .kpi-row { display: grid; grid-template-columns: repeat(6, 1fr); gap: 8px; margin-bottom: 16px; }
    /* Значок и подпись сверху, число снизу — как в плитках главной. Центрирование
       убрано: шесть центрированных блоков подряд не дают глазу вертикали, по
       которой он идёт вниз по колонке. */
    .kpi-tile { padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--panel); }
    .kpi-top { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; min-width: 0; }
    .kpi-ico {
      width: 20px; height: 20px; border-radius: var(--r-xs); flex: 0 0 auto;
      background: var(--primary-bg); color: var(--primary-text);
      display: inline-flex; align-items: center; justify-content: center;
    }
    .kpi-body { display: flex; align-items: center; justify-content: space-between; gap: 6px; min-width: 0; }
    .kpi-val { font-size: 15px; font-weight: 800; font-variant-numeric: tabular-nums; white-space: nowrap; }
    .kpi-lbl {
      font-size: 11px; color: var(--muted); min-width: 0;
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    /* Полукруг уходит первым: на узкой плитке число важнее формы. */
    .kpi-body .spark { flex: 0 1 auto; min-width: 0; }
    @media (max-width: 1280px) { .kpi-body .spark { display: none; } }
    @media (max-width: 768px) { .kpi-row { grid-template-columns: repeat(3, 1fr); } }
    @media (max-width: 480px) { .kpi-row { grid-template-columns: repeat(2, 1fr); } }

    /* ── AUTO-SAVE INDICATOR ─────────────────────── */
    .autosave-indicator {
      font-size: 12px; font-weight: 600;
      color: var(--text-success); opacity: 0;
      transition: opacity var(--dur-3); white-space: nowrap;
      padding: 4px 8px; border-radius: var(--r-pill);
    }
    [data-theme="light"] .autosave-indicator { color: var(--text-success); }
    .autosave-indicator.show { opacity: 1; }
    .autosave-indicator.saving { color: var(--muted); opacity: 1; }
    .autosave-indicator.cloud-error { color: var(--text-danger); opacity: 1; }

    /* ── SKELETON LOADING (shimmer 1.5s, DESIGN.md §7) ── */
    .skeleton {
      position: relative; overflow: hidden;
      background: var(--line); border-radius: var(--r-sm);
    }
    .skeleton::after {
      content: ""; position: absolute; inset: 0;
      background: linear-gradient(90deg, transparent, rgba(148, 163, 184, .14), transparent);
      animation: skeleton-shimmer 1.5s infinite;
    }
    @keyframes skeleton-shimmer {
      from { transform: translateX(-100%); }
      to { transform: translateX(100%); }
    }

    /* Карточки шагов сборки сметы: подсветка как у остальных кликабельных карточек */
    .estimate-step-card:hover {
      border-color: rgb(var(--primary-rgb) / .45) !important;
      transform: translateY(-1px);
    }
    .estimate-step-card:active { transform: translateY(0); }

    /* ── DEAL STAGE PROGRESS ─────────────────────── */
    .deal-stage-progress {
      display: flex; align-items: center;
      padding: 10px 18px 12px; gap: 0;
      overflow-x: auto; scrollbar-width: none;
      /* Fade-подсказка на краях — намёк, что воронку статусов можно проскроллить дальше */
      -webkit-mask-image: linear-gradient(to right, transparent 0, black 18px, black calc(100% - 18px), transparent 100%);
      mask-image: linear-gradient(to right, transparent 0, black 18px, black calc(100% - 18px), transparent 100%);
    }
    .deal-stage-progress::-webkit-scrollbar { display: none; }
    .dsp-step {
      display: flex; align-items: center; flex: 0 0 auto; cursor: pointer;
    }
    .dsp-pill {
      padding: 5px 13px; border-radius: var(--r-pill); font-size: 12px; font-weight: 700;
      border: 1.5px solid var(--line); color: var(--muted); white-space: nowrap;
      background: transparent; transition: all var(--dur-2); -webkit-user-select: none; user-select: none;
    }
    .dsp-step:hover .dsp-pill { border-color: rgb(var(--primary-rgb) / .4); color: var(--text); }
    .dsp-step.done .dsp-pill {
      background: rgba(22,163,74,.1); border-color: rgba(22,163,74,.35);
      color: var(--text-success);
    }
    [data-theme="light"] .dsp-step.done .dsp-pill { color: var(--text-success); background: rgba(22,163,74,.08); }
    .dsp-step.active .dsp-pill {
      background: var(--primary);
      border-color: transparent; color: var(--on-color);
      box-shadow: 0 4px 16px rgb(var(--primary-rgb) / .35);
    }
    .dsp-line {
      height: 2px; min-width: 14px; flex: 1 1 14px;
      background: var(--line); transition: background var(--dur-2);
    }
    /* Сделка в архиве: метка в начале строки этапов, сами этапы приглушены. */
    .dsp-archived {
      flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px;
      padding: 5px 12px; border-radius: var(--r-pill); font-size: 12px; font-weight: 700; white-space: nowrap;
      color: var(--text); background: var(--panel2); border: 1.5px dashed var(--muted);
    }
    .deal-stage-progress.is-archived .dsp-step { opacity: .55; }
    .deal-stage-progress.is-archived .dsp-step:hover { opacity: 1; }
    .dsp-line.done { background: rgba(22,163,74,.5); }

    /* ── GLOBAL FINANCES ─────────────────────────── */
    .gfin-month-bar {
      display: flex; align-items: flex-end; gap: 4px;
      height: 120px; padding: 0 4px;
    }
    .gfin-month-col {
      display: flex; flex-direction: column; align-items: center;
      gap: 2px; flex: 1; min-width: 28px;
    }
    .gfin-bar {
      width: 100%; border-radius: var(--r-xs) var(--r-xs) 0 0; min-height: 2px; transition: var(--dur-2);
    }
    .gfin-bar.income { background: var(--green); }
    .gfin-bar.expense { background: var(--red); opacity: .7; }
    .gfin-month-label {
      font-size: 12px; color: var(--muted); white-space: nowrap; margin-top: 4px;
    }
    .gfin-project-filter {
      display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 12px;
    }

    /* ── FINANCE MODAL ───────────────────────────── */
    .modal-overlay {
      position: fixed; inset: 0;
      background: rgba(0,0,0,.55);
      z-index: var(--z-modal);
      display: flex; align-items: center; justify-content: center;
      -webkit-backdrop-filter: blur(6px);
      backdrop-filter: blur(6px);
      animation: fadeIn var(--dur-2) var(--ease-out);
    }
    @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
    .modal-box {
      /* Под полупрозрачной --panel (.97) — непрозрачная --bg2. Одной --panel не
         хватало: сквозь окно тенью читался каталог за ним («3D-визуализация
         продукта» поверх описания позиции, снимок 15.09.2026). Цвет тот же —
         панель лежит поверх своего же тона, — но насквозь больше не видно. */
      background-color: var(--bg2);
      background-image: linear-gradient(var(--panel), var(--panel));
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      padding: 28px;
      width: min(480px, calc(100vw - 32px));
      max-height: calc(100dvh - 48px);
      overflow-y: auto;
      box-shadow: var(--elev-3);
      animation: modalIn var(--dur-3) var(--ease-out);
    }
    @media (max-width: 768px) {
      .modal-box { padding: 20px 16px; border-radius: var(--r-xl); }
    }

    /* ── Редактор пакета: колонка раздела, а не наложение ─────────────────────
       Владелец (04.09.2026): «не поверх открытое окно, а сбоку как меню». Разница
       не косметическая: наложение ГАСИТ страницу и перехватывает клики, а колонка
       оставляет список пакетов живым — можно листать категории и открыть другой
       пакет, не закрывая редактор.

       Поэтому редактор рисуется внутри renderPackages второй колонкой .layout, а
       не через renderModal(). Разметка и обработчики те же — просто другое место
       в дереве.

       На телефоне колонки нет (там .layout схлопывается в одну), и та же панель
       становится окном поверх: решение владельца — на узком экране колонке негде
       развернуться. */
    .pkg-editor {
      position: sticky; top: 16px;
      max-height: calc(100dvh - 32px);
      display: flex; flex-direction: column;
      background: var(--panel);
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      box-shadow: var(--elev-2);
      overflow: hidden;
    }
    /* Три яруса: шапка и кнопки закреплены, прокручивается только форма между
       ними. Раньше прокручивался весь блок целиком — и уход к «Составу» уносил
       за верхний край заголовок вместе с крестиком, единственным способом
       закрыть панель (замер 05.09.2026: крестик на top −31px). */
    .pkg-editor-inner { display: flex; flex-direction: column; min-height: 0; overflow: hidden; }
    .pkg-editor-head {
      flex: 0 0 auto;
      display: flex; justify-content: space-between; align-items: center;
      gap: 10px; padding: 16px 20px;
      border-bottom: 1px solid var(--line);
    }
    .pkg-editor-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 16px 20px; }
    .pkg-editor-foot {
      flex: 0 0 auto;
      display: flex; gap: 10px; justify-content: flex-end;
      padding: 12px 20px;
      border-top: 1px solid var(--line);
    }

    /* ── Состав пакета ────────────────────────────────────────────────────────
       Владелец (04.09.2026): «сейчас состав не понятно что написано». Причина в
       строении: имя, поле объёма, подпись, сумма и корзина стояли ОДНОЙ строкой,
       и в панели 360px имени оставалось около 120px — «Монтаж короткого Reels /
       Shorts» превращался в «Монтаж короткого Reels / …». Название позиции — самое
       важное в строке, и именно оно обрезалось.

       Разносим на два этажа: сверху имя во всю ширину (до двух строк), снизу
       объём слева и сумма справа. Имени достаётся втрое больше места, а числа
       выстраиваются в колонку и читаются сверху вниз. */
    .pkg-item-row {
      display: flex; flex-direction: column; gap: 5px;
      padding: 9px 0; border-bottom: 1px solid var(--line);
    }
    .pkg-item-row:last-child { border-bottom: none; }
    /* Без поля объёма строке хватает одного этажа. Два завели ради длинных
       названий, но теснило их именно поле объёма: там, где его нет, имени
       остаётся почти вся ширина панели. У «Идеи ролика» второй этаж занимала
       одна сумма — пустая трата высоты, из-за которой список выглядел вдвое
       длиннее, чем есть. */
    .pkg-item-row-flat { flex-direction: row; align-items: center; gap: 10px; }
    .pkg-item-row-flat .pkg-item-name { flex: 1; min-width: 0; }
    .pkg-item-row-flat .pkg-item-meta { flex: 0 0 auto; }
    .pkg-item-name {
      font-size: 13px; line-height: 1.35; color: var(--text);
      /* Две строки — потолок: третья превращает список в простыню, а названий
         длиннее двух строк в каталоге нет. */
      display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
    }
    /* Состав сметы в правой колонке каталога (19.09.2026: «сделать красивее,
       удобнее и функциональнее»). Строки собраны по этапам сметы — точка и
       подпись цветом этапа, подытог справа. Название — кнопка «к позиции в
       смете», крестик убирает строку (с отменой в тосте). Имени разрешено
       переноситься: обрезка названия была главной жалобой на соседний список.
       Место под крестик держится всегда — иначе суммы прыгали бы при наведении. */
    .summary-comp-head {
      margin-top: 14px; display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
      font-size: 12px; font-weight: 700; color: var(--muted);
    }
    .summary-count { font-weight: 600; font-variant-numeric: tabular-nums; }
    .summary-lines {
      margin-top: 8px; padding-top: 6px;
      border-top: 1px solid var(--line);
      display: flex; flex-direction: column; gap: 12px;
      max-height: min(56vh, 520px); overflow-y: auto; overscroll-behavior: contain;
    }
    /* Строки вплотную: линия этапа слева — сплошная, а не пунктир. */
    .summary-stage { display: flex; flex-direction: column; }
    .summary-stage-head {
      display: flex; align-items: center; gap: 8px; padding: 6px 4px 4px 6px;
      font-size: 11.5px; font-weight: 700; letter-spacing: .02em;
      /* Липкий: в прокрученном списке видно, чьи строки под глазами. Фон —
         плотный двойник --panel колонки, иначе строки просвечивали бы.
         top = минус отступ списка: липкость считается от его padding-box. */
      position: sticky; top: -6px; z-index: 2; background: var(--bg2);
      /* 50%, а не 72% как у заголовка этапа в смете: там кегль крупный, тут
         11.5px — жёлтая «Подготовка» на белом при 72% давала ~2.9:1. */
      color: color-mix(in srgb, var(--stage-c) 50%, var(--text));
    }
    .summary-stage-dot { flex: 0 0 auto; width: 8px; height: 8px; border-radius: var(--r-pill); background: var(--stage-c); }
    .summary-stage-name { flex: 1; min-width: 0; }
    .summary-stage-sum { color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
    .summary-line-row {
      display: flex; align-items: flex-start; gap: 6px;
      padding: 4px 4px 4px 8px;
      border-left: 2px solid color-mix(in srgb, var(--stage-c) 40%, transparent);
      border-radius: 0 var(--r-sm) var(--r-sm) 0;
      transition: background .15s;
    }
    .summary-line-row:hover, .summary-line-row:focus-within { background: rgb(var(--primary-rgb) / .06); }
    .summary-line-row.is-new { animation: summary-line-in 1.6s ease-out; }
    @keyframes summary-line-in {
      0%, 35% { background: rgb(var(--primary-rgb) / .18); }
      100% { background: transparent; }
    }
    .summary-line-name {
      flex: 1; min-width: 0; margin: 0; padding: 0; border: 0; background: none;
      font: inherit; font-size: 12.5px; line-height: 1.35; text-align: left; color: var(--text);
      cursor: pointer; transition: color .15s;
    }
    .summary-line-name:hover { color: var(--primary-text); }
    .summary-line-opt {
      display: inline-block; padding: 0 6px; border: 1px solid var(--line); border-radius: var(--r-pill);
      font-size: 10.5px; font-weight: 700; color: var(--muted); vertical-align: 1px;
    }
    .summary-line-sum {
      flex: 0 0 auto; font-size: 12.5px; line-height: 1.35; font-weight: 700;
      font-variant-numeric: tabular-nums; white-space: nowrap;
    }
    .summary-line-row.is-optional .summary-line-sum { color: var(--muted); font-weight: 600; }
    .summary-line-remove {
      position: relative; flex: 0 0 auto; width: 18px; height: 18px;
      display: inline-grid; place-items: center;
      margin: 0; padding: 0; border: 0; border-radius: var(--r-sm); background: none;
      color: var(--muted); cursor: pointer; opacity: 0;
      transition: opacity .15s, color .15s, background .15s;
    }
    .summary-line-row:hover .summary-line-remove, .summary-line-remove:focus-visible { opacity: 1; }
    .summary-line-remove:hover { color: var(--text-danger); background: color-mix(in srgb, var(--red) 12%, transparent); }
    /* Палец: строка 44px, и вся она — цель «к позиции» (::after названия
       растянут на строку). Крестик виден всегда и лежит слоем выше; его цель
       44×44 уходит вправо, в пустые поля колонки, а не на сумму слева. */
    @media (hover: none) and (pointer: coarse) {
      .summary-line-row { position: relative; min-height: 44px; align-items: center; }
      .summary-line-name::after { content: ""; position: absolute; inset: 0; }
      .summary-line-remove { opacity: 1; z-index: 1; }
      .summary-line-remove::after { content: ""; position: absolute; inset: -13px -20px -13px -6px; }
    }
    /* Позиция, к которой пришли из списка каталога, — короткая подсветка рамкой. */
    /* scroll-margin — чтобы начало карточки не уходило под липкую шапку. */
    .item[data-line] { scroll-margin-top: 96px; }
    /* То же у сделок: программная прокрутка к карточке (доводка до списка по
       плитке, возврат к сделке) ставила её к самому верху окна — ПОД
       фиксированный топбар (62px). Меню карточки открывалось от кнопки,
       спрятанной под шапкой, и его пункты оказывались под ней же. Всплыло
       28.09.2026, когда главная стала выше и прокрутке стало куда уходить. */
    [data-deal-id] { scroll-margin-top: 96px; }
    .item.is-located { animation: line-located 1.6s ease-out; }
    @keyframes line-located {
      0%, 40% { box-shadow: 0 0 0 2px var(--primary); }
      100% { box-shadow: 0 0 0 2px transparent; }
    }

    .pkg-item-meta { display: flex; align-items: center; gap: 8px; }
    .pkg-item-meta input[type="number"] { width: 54px; padding: 4px 6px; font-size: 12px; text-align: center; }
    .pkg-item-sum {
      margin-left: auto; font-size: 12.5px; font-weight: 700;
      font-variant-numeric: tabular-nums; white-space: nowrap;
    }

    @media (max-width: 1160px) {
      /* Ниже этого порога .layout уже в одну колонку — редактор становится окном
         поверх, иначе он уехал бы ПОД список, и после нажатия «Редактировать»
         ничего бы не произошло на видимом экране. */
      .pkg-editor {
        position: fixed; inset: 0; z-index: var(--z-modal);
        top: 0; max-height: none; border-radius: 0; border: none;
        background: rgba(0, 0, 0, .55);
        -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
        align-items: center; justify-content: center;
        padding: 16px;
      }
      .pkg-editor-inner {
        width: min(520px, 100%); max-height: calc(100dvh - 32px);
        background: var(--panel);
        border: 1px solid var(--line);
        border-radius: var(--r-xl);
        box-shadow: var(--elev-3);
      }
    }
    /* Прежний slideUp двигал окно на 16px вверх линейным ease. Лёгкий доворот
       масштаба (.97 → 1) читается как «окно вышло вперёд», а не «подъехало снизу»,
       и заодно скрывает рывок на длинных модалках. slideUp оставлен: на нём висят
       другие элементы (дропдауны шапки), их поведение менять не нужно. */
    @keyframes modalIn {
      from { transform: translateY(12px) scale(.97); opacity: 0; }
      to   { transform: translateY(0) scale(1); opacity: 1; }
    }
    @keyframes slideUp { from { transform: translateY(16px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }

    /* Единый диалог подтверждения/ввода (confirmDialog/promptDialog) — замена нативных
       confirm()/prompt(). Наследует .modal-overlay/.modal-box, уже, кнопки справа. */
    .confirm-dialog-box { width: min(400px, calc(100vw - 32px)); }
    .confirm-dialog-title { margin: 0 0 8px; }
    .confirm-dialog-msg {
      color: var(--muted);
      font-size: 14px;
      line-height: 1.5;
      white-space: pre-line;
      margin: 0 0 22px;
    }
    .confirm-dialog-input { width: 100%; margin: 4px 0 22px; }
    /* Пояснение и варианты в диалоге ввода. Поле без подписи заставляет гадать,
       что именно вводят, а повторяющиеся значения (метки, роли) быстрее выбрать,
       чем набрать. Отступ у поля снимаем, когда под ним идут варианты. */
    /* Метка аккаунта в админке: амбассадор, партнёр, свой тестовый. Вид —
       нейтральная пилюля рядом со статусом подписки: это две вещи, по которым
       аккаунт узнают, и спорить за внимание они не должны. */
    /* Бюджет клиента в шапке сметы — кнопка, а не подпись: его называют один
       раз в мастере, а менять и убирать приходится по ходу проекта. Вид как у
       соседних показателей, чтобы шапка не рассыпалась на разнородные блоки. */
    /* Доска задач: та же сетка, что у канбана сделки, — раздел «Задачи» и
       мини-канбан внутри сделки должны читаться как одно место, а не как две
       разные программы. Колонки прокручиваются вбок на узком экране. */
    .gtask-board {
      display: grid; grid-auto-flow: column; grid-auto-columns: minmax(250px, 1fr);
      /* Колонки — по высоте содержимого, как списки в Trello, а не вытянутые
         до самой длинной: пустая колонка не рисует экран пустоты. */
      align-items: start;
      gap: 12px; overflow-x: auto; padding-bottom: 4px;
    }
    .gtask-board-col {
      display: flex; flex-direction: column;
      background: var(--panel2); border: 1px solid var(--line);
      border-radius: var(--r-xl); padding: 10px; min-width: 0;
    }
    /* Колонка несёт цвет статуса (--st-c из TASK_STATUS_COLORS): полоса по
       верхнему краю, точка и счётчик в заголовке. */
    .gtask-board-col { box-shadow: inset 0 3px 0 color-mix(in srgb, var(--st-c, var(--line)) 70%, transparent); }
    .gtask-board-col h3 {
      display: flex; align-items: center; justify-content: space-between; gap: 8px;
      margin: 0 0 10px; font-size: 14px; min-height: 28px;
    }
    .gtask-col-name { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
    .gtask-col-dot { flex: 0 0 8px; width: 8px; height: 8px; border-radius: var(--r-pill); background: var(--st-c, var(--muted)); }
    .gtask-board-col .pill-count { background: color-mix(in srgb, var(--st-c, var(--muted)) 20%, transparent); color: var(--text); }
    /* Быстрое добавление внизу колонки (app.js openTaskQuickAdd) — «Добавить
       карточку» из Trello: кнопка во всю ширину, по нажатию — поле в колонке. */
    .gtask-quick-open {
      display: flex; align-items: center; gap: 6px; width: 100%;
      min-height: 36px; margin-top: 8px; padding: 6px 10px;
      border: 0; border-radius: var(--r-md); background: transparent;
      color: var(--muted); font: inherit; font-size: 13px; font-weight: 600; text-align: left; cursor: pointer;
      transition: background var(--dur-1), color var(--dur-1);
    }
    .gtask-quick-open:hover, .gtask-quick-open:focus-visible { background: rgb(var(--primary-rgb) / .1); color: var(--primary-text); }
    .gtask-quick { margin-top: 8px; display: flex; flex-direction: column; gap: 8px; }
    .gtask-quick-input {
      width: 100%; min-height: 60px; resize: vertical;
      padding: 10px 12px; border-radius: var(--r-lg);
      border: 1px solid rgb(var(--primary-rgb) / .55); background: var(--panel); color: var(--text);
      font: inherit; font-size: 13px; line-height: 1.4; box-shadow: var(--elev-1);
    }
    .gtask-quick-input:focus { outline: var(--ring-w) solid var(--ring); outline-offset: 1px; }
    .gtask-quick-actions { display: flex; align-items: center; gap: 6px; }
    @media (hover: none) and (pointer: coarse) { .gtask-quick-open { min-height: 44px; } }
    .gtask-board-list { display: flex; flex-direction: column; gap: 8px; }
    .gtask-board-empty {
      display: flex; align-items: center; justify-content: center; gap: 6px;
      padding: 18px 8px; text-align: center; font-size: 12px; color: var(--muted);
      border: 1px dashed var(--line); border-radius: var(--r-lg);
    }
    .gtask-card {
      position: relative;
      background: var(--panel); border: 1px solid var(--line);
      border-radius: var(--r-lg); padding: 10px 12px; cursor: pointer;
      display: flex; flex-direction: column; gap: 8px;
      box-shadow: var(--elev-1);
      transition: border-color var(--dur-1), box-shadow var(--dur-1);
    }
    /* Метки над названием — как в Trello: проект, приоритет, «закрыта». */
    .gtask-card-labels { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }
    .gtask-card-labels .gtask-project, .gtask-card-labels .gtask-prio { font-size: 11px; padding: 1px 8px; }
    /* Телефон: колонка почти во всю ширину и листается по одной — как в
       мобильном Trello; край следующей колонки подсказывает, что там ещё есть. */
    @media (max-width: 640px) {
      .gtask-board { grid-auto-columns: 86%; scroll-snap-type: x mandatory; }
      .gtask-board-col { scroll-snap-align: start; }
      /* Во время переноса доска сама едет к краю (_tbStart) — прилипание к
         колонкам возвращало бы её назад на каждом шаге. */
      body.gtask-dragging .gtask-board { scroll-snap-type: none; }
    }
    .gtask-card:hover { border-color: rgb(var(--primary-rgb) / .55); box-shadow: 0 6px 18px -10px rgb(var(--primary-rgb) / .5); }
    /* Приоритет выше обычного — полосой по левому краю: на доске его видно,
       не читая меток. */
    .gtask-card.prio-urgent::before,
    .gtask-card.prio-high::before {
      content: ""; position: absolute; left: -1px; top: 10px; bottom: 10px; width: 3px;
      border-radius: var(--r-pill); background: var(--orange);
    }
    .gtask-card.prio-urgent::before { background: var(--red); }
    .gtask-prio { font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: var(--r-pill); }
    .gtask-prio.is-urgent { color: var(--text-danger); background: color-mix(in srgb, var(--red) 12%, transparent); }
    .gtask-prio.is-high { color: var(--text-warning); background: color-mix(in srgb, var(--orange) 12%, transparent); }
    /* Срок — капсулой со значком; цвет по шкале задачи (taskDueMeta). */
    .gtask-due {
      display: inline-flex; align-items: center; gap: 4px;
      padding: 2px 8px; border-radius: var(--r-pill); border: 1px solid var(--line);
      font-size: 12px; font-weight: 600; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap;
    }
    .gtask-due.is-overdue { color: var(--text-danger); border-color: color-mix(in srgb, var(--red) 35%, transparent); background: color-mix(in srgb, var(--red) 10%, transparent); }
    .gtask-due.is-today { color: var(--text-warning); border-color: color-mix(in srgb, var(--orange) 35%, transparent); background: color-mix(in srgb, var(--orange) 10%, transparent); }
    .gtask-due.is-near { color: var(--text); }
    .gtask-card-end { margin-left: auto; display: inline-flex; align-items: center; gap: 8px; }
    .gtask-comments { display: inline-flex; align-items: center; gap: 3px; font-variant-numeric: tabular-nums; }
    .gtask-assignee {
      width: 22px; height: 22px; display: grid; place-items: center; flex: 0 0 auto;
      border-radius: var(--r-pill); font-size: 11px; font-weight: 800;
      color: var(--primary-on-tint); background: rgb(var(--primary-rgb) / .18);
    }
    .gtask-meta-who { display: inline-flex; align-items: center; gap: 4px; }
    .gtask-status { font-size: 12px; color: var(--st-c); border-color: color-mix(in srgb, var(--st-c) 40%, transparent); }
    .gtask-card.done { opacity: .6; }
    .gtask-card.done .gtask-card-title { text-decoration: line-through; }
    .gtask-card-top { display: flex; align-items: flex-start; gap: 8px; }
    .gtask-card-title { font-size: 13px; font-weight: 600; line-height: 1.35; min-width: 0; overflow-wrap: anywhere; }
    .gtask-card-meta { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; font-size: 12px; color: var(--muted); }
    /* Перетаскивание по доске (app.js _initTaskBoardDrag). Выделение текста и
       меню долгого нажатия на iOS срывали бы захват карточки. */
    .gtask-card[data-drag-id] { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
    .gtask-board-col { transition: background var(--dur-1), border-color var(--dur-1), box-shadow var(--dur-1); }
    .gtask-board-col.dragover {
      border-color: var(--primary-on-tint);
      background: rgb(var(--primary-rgb) / .1);
      box-shadow: 0 0 0 2px rgb(var(--primary-rgb) / .2) inset;
    }
    .gtask-card.is-drag-source { opacity: .35; border-style: dashed; }
    /* Сводка доски сделки (app.js renderTasks): прогресс и просроченное. */
    .gtask-deal-summary { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
    .gtask-deal-progress { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600; color: var(--muted); font-variant-numeric: tabular-nums; }
    .gtask-deal-bar { width: 96px; height: 6px; border-radius: var(--r-pill); background: var(--panel2); border: 1px solid var(--line); overflow: hidden; }
    .gtask-deal-bar > span { display: block; height: 100%; background: var(--green); border-radius: var(--r-pill); }
    /* Линия «встанет сюда» при переносе (app.js _tbTrack). Отрицательные поля
       гасят отступ списка, поэтому карточки под линией не прыгают. */
    .gtask-drop-marker {
      height: 4px; margin: -6px 0; flex: 0 0 auto;
      border-radius: var(--r-pill); background: var(--primary);
      box-shadow: 0 0 0 3px rgb(var(--primary-rgb) / .2);
    }
    .gtask-card-ghost {
      position: fixed; left: 0; top: 0; z-index: var(--z-tooltip);
      margin: 0; pointer-events: none;
      border-color: var(--primary); box-shadow: var(--elev-3);
    }
    body.gtask-dragging, body.gtask-dragging * { cursor: grabbing; }
    .client-budget-chip {
      display: flex; flex-direction: column; gap: 1px; align-items: flex-start;
      padding: 4px 12px; border: 0; border-left: 1px solid var(--line);
      background: none; color: inherit; cursor: pointer; text-align: left; font: inherit;
    }
    .client-budget-chip:hover { background: var(--panel2); }
    .admin-tag-pill {
      padding: 3px 10px; border-radius: var(--r-pill); cursor: pointer;
      font-size: 12px; font-weight: 700; white-space: nowrap;
      background: var(--primary-bg); color: var(--primary-text);
      border: 1px solid rgb(var(--primary-rgb) / .28);
    }
    .admin-tag-pill:hover { border-color: var(--primary); }
    .confirm-dialog-message { margin: 0 0 10px; font-size: 13px; line-height: 1.5; color: var(--muted); }
    .confirm-dialog-suggestions { display: flex; flex-wrap: wrap; gap: 6px; margin: -14px 0 20px; }
    .confirm-dialog-suggestions .chip { font-size: 12px; }
    .confirm-dialog-actions { display: flex; justify-content: flex-end; gap: 10px; flex-wrap: wrap; }
    @media (max-width: 480px) {
      .confirm-dialog-actions { flex-direction: column-reverse; }
      .confirm-dialog-actions .btn { width: 100%; }
    }
    .modal-type-switch {
      display: flex; gap: 6px; margin-bottom: 20px;
    }
    .modal-type-btn {
      padding: 10px 20px; border-radius: var(--r-pill); border: 2px solid var(--line);
      font-size: 14px; font-weight: 700; cursor: pointer; transition: var(--dur-1);
      background: transparent; color: var(--muted);
    }
    .modal-type-btn.income.active {
      background: var(--green); border-color: var(--text-success); color: var(--on-color);
    }
    .modal-type-btn.expense.active {
      background: var(--red); border-color: var(--text-danger); color: var(--on-color);
    }
    .modal-amount-input {
      font-size: 26px; font-weight: 900; border-radius: var(--r-lg);
      padding: 12px 16px; width: 100%;
      font-variant-numeric: tabular-nums;
    }
    .modal-amount-input.invalid { border-color: rgba(220,38,38,.6); }

    /* Стрелки-спиннеры у числовых полей: в тёмной теме браузер рисует их белыми,
       они лезут поверх крупных сумм и не нужны никому — суммы и количества здесь
       вводят с клавиатуры, а не накликивают по единице. */
    input[type="number"]::-webkit-outer-spin-button,
    input[type="number"]::-webkit-inner-spin-button {
      -webkit-appearance: none; appearance: none; margin: 0;
    }
    input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
    .modal-save-btn {
      padding: 12px 28px; border-radius: var(--r-pill); border: none;
      font-size: 15px; font-weight: 900; cursor: pointer; transition: var(--dur-1);
      color: var(--on-color);
    }
    .modal-save-btn.income { background: var(--green); }
    .modal-save-btn.expense { background: var(--red); }
    .modal-save-btn:disabled { opacity: .45; cursor: default; }
    .modal-save-btn:not(:disabled):hover { opacity: .85; transform: translateY(-1px); }

    /* ── COMPACT TASK CARDS ───────────────────────── */
    .task-title-input {
      flex: 1; background: transparent; border: none;
      border-bottom: 1px solid transparent;
      font-size: 14px; font-weight: 600; color: var(--text);
      padding: 2px 0; width: 100%;
    }
    .task-title-input:hover { border-bottom-color: var(--line); }
    .task-title-input:focus { border-bottom-color: rgb(var(--primary-rgb) / .65); outline: none; }
    .task-mini-select {
      /* padding-right — место под шеврон, см. .deal-sort-select */
      padding: 5px 8px; padding-right: 32px; font-size: 12px; border-radius: var(--r-sm);
      /* background-color, не background — см. .deal-sort-select */
      background-color: var(--input); border: 1px solid var(--line);
      color: var(--text); cursor: pointer;
    }
    details summary { -webkit-user-select: none; user-select: none; }
    details summary::-webkit-details-marker { display: none; }

    /* ── GLOBAL QUICK ADD ────────────────────────── */
    .global-add-wrap { position: relative; }
    .global-add-btn {
      width: 36px; height: 36px; border-radius: 50%;
      background: var(--primary); color: var(--on-color); border: none;
      font-size: 22px; font-weight: 900; cursor: pointer;
      display: flex; align-items: center; justify-content: center;
      transition: var(--dur-1); box-shadow: 0 4px 18px rgb(var(--primary-rgb) / .45);
      flex: 0 0 auto; line-height: 1;
    }
    .global-add-btn:hover { background: var(--primary2); transform: scale(1.08); }
    .global-add-menu {
      position: absolute; right: 0; top: calc(100% + 10px);
      background: var(--panel); border: 1px solid var(--line);
      border-radius: var(--r-xl); padding: 8px; min-width: 210px;
      box-shadow: 0 20px 60px rgba(0,0,0,.4); z-index: 300;
      display: none; flex-direction: column; gap: 2px;
    }
    .global-add-menu.open { display: flex; animation: fadeSlideDown .15s cubic-bezier(0.16, 1, 0.3, 1); }
    .global-add-menu-item {
      padding: 11px 14px; border-radius: var(--r-md); border: none;
      background: transparent; color: var(--text); text-align: left;
      font-size: 13px; font-weight: 600; cursor: pointer; transition: var(--dur-1);
      display: flex; align-items: center; gap: 8px;
    }
    .global-add-menu-item:hover { background: rgb(var(--primary-rgb) / .14); }
    .global-add-menu-item .icon {
      width: 26px; height: 26px; border-radius: var(--r-sm);
      display: flex; align-items: center; justify-content: center;
      font-size: 14px; flex: 0 0 auto;
    }
    .global-add-menu-divider {
      height: 1px; background: var(--line); margin: 4px 6px;
    }

    /* ── MOBILE QUICK ADD (FAB-кнопка нижней навигации) ──────────
       Отдельный узел вместо переиспользования #globalAddMenu: тот вложен
       в .topbar, а у неё backdrop-filter — создаёт containing block для
       position:fixed, и поповер вместо места у FAB улетал к верху экрана. */
    .mobile-add-menu {
      display: none;
      position: fixed;
      left: 50%; transform: translateX(-50%);
      bottom: calc(76px + env(safe-area-inset-bottom, 0px));
      background: var(--panel); border: 1px solid var(--line);
      border-radius: var(--r-xl); padding: 8px; width: min(240px, calc(100vw - 32px));
      box-shadow: 0 20px 60px rgba(0,0,0,.4); z-index: 300;
      flex-direction: column; gap: 2px;
    }
    /* Свой keyframe, не общий slideUp: тот анимирует голый translateY и на время
       анимации перебивает translateX(-50%) из базового правила — меню центрируется
       только ПОСЛЕ окончания анимации, а на время самой анимации «уезжает» вправо
       (left:50% без компенсации). Здесь translateX(-50%) держится в обоих кадрах. */
    @keyframes slideUpCentered {
      from { transform: translateX(-50%) translateY(16px); opacity: 0; }
      to   { transform: translateX(-50%) translateY(0); opacity: 1; }
    }
    .mobile-add-menu.open { display: flex; animation: slideUpCentered .15s cubic-bezier(0.16, 1, 0.3, 1); }

    /* ── NAV DEAL INDICATOR ──────────────────────── */
    .nav-deal-indicator {
      display: inline-flex; align-items: center; gap: 6px;
      padding: 8px 12px; border-radius: var(--r-pill);
      background: rgba(22,163,74,.14);
      border: 1px solid rgba(22,163,74,.3);
      font-size: 13px; font-weight: 600;
      color: var(--text-success); white-space: nowrap; cursor: pointer;
      transition: var(--dur-1);
    }
    [data-theme="light"] .nav-deal-indicator { color: var(--text-success); }
    .nav-deal-indicator:hover { background: rgba(22,163,74,.22); }
    .nav-deal-indicator .dot {
      width: 7px; height: 7px; border-radius: 50%;
      background: var(--green); flex: 0 0 auto;
    }

    /* ── QUICK FINANCE ───────────────────────────── */
    .quick-finance-bar {
      display: flex;
      gap: 10px;
      align-items: stretch;
      padding: 14px 0;
      border-top: 1px solid var(--line);
      margin-top: 14px;
      flex-wrap: wrap;
    }
    .quick-fin-form {
      display: flex;
      gap: 8px;
      align-items: center;
      flex: 1;
      min-width: 280px;
      padding: 12px 14px;
      background: var(--panel2);
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
    }
    .quick-fin-form.income { border-color: rgba(22,163,74,.35); }
    .quick-fin-form.expense { border-color: rgba(220,38,38,.25); }
    .quick-fin-label {
      font-size: 12px; font-weight: 700;
      white-space: nowrap; color: var(--muted);
    }
    .quick-fin-form.income .quick-fin-label { color: var(--text-success); }
    .quick-fin-form.expense .quick-fin-label { color: var(--text-danger); }
    [data-theme="light"] .quick-fin-form.income .quick-fin-label { color: var(--text-success); }
    [data-theme="light"] .quick-fin-form.expense .quick-fin-label { color: var(--text-danger); }
    .quick-fin-form input {
      flex: 1; min-width: 80px; padding: 7px 10px;
      border-radius: var(--r-md); font-size: 13px;
    }
    .quick-fin-form input.amount-input { max-width: 100px; }

    /* ── FINANCE PAGE ─────────────────────────────── */
    /* Пять плиток и `repeat(auto-fit, minmax(150px,1fr))` — раскладка, при которой
       последняя ВСЕГДА оставалась одна: пять не делится ни на какое число колонок.
       Замер по живому DOM (финансы сделки): окно 1600 → сетка 766px, ряд из четырёх
       и 583px пустоты справа от «Прибыли»; 1400 → 499px; 860 → 601px; 640 → 305px.
       На телефоне это чинили точечно (правило ниже), но дыра была не только там.

       Раскладка задана явно: 3 + 2. Ряды заполняются целиком на любой ширине и
       заодно читаются как два разных вопроса: сверху деньги клиента (бюджет /
       оплачено / долг), снизу экономика проекта (расход / прибыль).

       Тридцать колонок — не прихоть: это НОК для 3, 2 и 5, а пять плиток в один
       ряд нужны на широкой панели (общая страница «Финансы» — сетка 1477px при
       окне 1500, там 3+2 растягивало плитки до 735px впустую). Гэпы внутри span
       считаются в ширину плитки, поэтому 30 узких колонок дают ровно те же
       размеры, что и 6 широких.

       Порог берётся с САМОЙ СЕТКИ (container query), а не с окна: ширина панели
       от ширины окна не зависит — в сделке при окне 1600px сетка 766px, а на
       общей странице при окне 1100px — 1030px. Медиазапрос тут врал бы.

       Обе сетки экранов «Финансы» — и в сделке, и общая — ровно из пяти плиток.
       Появится шестая — span у 4-й и 5-й придётся пересчитать; сторож на это
       лежит в tests/suites/responsive.js, он меряет пустоту в рядах. */
    .fin-summary-grid {
      container-type: inline-size;
      display: grid;
      grid-template-columns: repeat(30, 1fr);
      gap: 12px;
      margin-bottom: 20px;
    }
    .fin-summary-grid > * { grid-column: span 10; }
    .fin-summary-grid > :nth-child(4),
    .fin-summary-grid > :nth-child(5) { grid-column: span 15; }
    /* Ряд из пяти включается с 920px, а не с 1040. Пять плиток по ~168px ещё
       держат сумму в одну строку (замер: «1 742 100 ₽» при 22px — 132px), зато
       исчезает промежуточная раскладка 3 + 2, где второй ряд шире первого и
       цифры встают на разной сетке — именно её видно на среднем окне.
       Ниже 920px раскладка 3 + 2 остаётся: там пять узких плиток уже рвут
       суммы переносом, а разнобой ширин — меньшее зло, чем «1 742 100 ₽»
       в две строки. */
    @container (min-width: 920px) {
      .fin-summary-grid > *,
      .fin-summary-grid > :nth-child(4),
      .fin-summary-grid > :nth-child(5) { grid-column: span 6; }
    }
    /* Подсказка в модалке расхода: в смете сделки уже заложена себестоимость.
       Тон нейтральный (панель, не жёлтая тревога): расход сверх сметы — обычное
       дело, а не ошибка. Место — сразу под полем суммы, рядом с парной
       подсказкой «Остаток по сделке» у поступления. */
    .fin-linecost-note {
      display: flex; flex-direction: column; gap: 3px;
      margin-top: 8px; padding: 9px 11px;
      background: var(--panel2);
      border: 1px dashed var(--line-strong, var(--line));
      border-radius: var(--r-lg);
      font-size: 12px; line-height: 1.5; color: var(--text);
    }
    .fin-linecost-note strong { font-variant-numeric: tabular-nums; }
    .fin-card {
      padding: 16px;
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      background: var(--panel2);
    }
    /* Сумма слева, мини-график справа — тот же приём, что в плитках главной.
       График сжимается первым и пропадает на узкой карточке: число важнее формы. */
    .fin-amount-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-width: 0; }
    .fin-amount-row .fin-amount { min-width: 0; }
    .fin-amount-row .spark { flex: 0 1 auto; min-width: 0; opacity: .9; }
    .fin-card:hover .spark { opacity: 1; }
    @media (max-width: 1180px) { .fin-amount-row .spark { display: none; } }
    /* Нечётная плитка на телефоне занимает ряд целиком.
       Замер на 390px: сетка даёт две колонки по 168px, плиток пять — последняя
       («Прибыль» в финансах сделки, «Транзакций» на общей странице) висела одна и
       оставляла 201px пустоты справа. Именно на это владелец и указал.
       Условие :nth-child(odd) у :last-child = «последняя осталась без пары»;
       при чётном числе плиток правило не срабатывает вовсе.
       Порог поднят с 500 до 640: раньше выше 500px число колонок задавал auto-fit
       и «нечётная» перестала бы означать «одинокая», а теперь ниже 640px колонок
       ровно две по всей ширине диапазона. На 501–640px дыра оставалась: замер на
       окне 640px давал 2+2+1 и 305px пустоты. */
    @media (max-width: 640px) {
      .fin-summary-grid > :last-child:nth-child(odd) { grid-column: 1 / -1; }
    }
    /* Заодно плотнее: 16px отступов на узком экране съедали высоту зря. */
    @media (max-width: 500px) {
      .fin-summary-grid .fin-card { padding: 13px 14px; }
    }

    .fin-card h3 {
      display: flex; align-items: center; gap: 6px;
      margin: 0 0 8px;
      font-size: 11.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--muted);
    }
    /* «план» — метка при заголовке, а не второй заголовок мелким кеглем. */
    .fin-tag {
      padding: 1px 7px; border-radius: var(--r-pill);
      background: var(--panel); border: 1px solid var(--line);
      font-size: 10.5px; font-weight: 700; letter-spacing: 0; text-transform: none; color: var(--muted);
    }
    /* tabular-nums на суммах: без него цифры разной ширины, и в столбце плиток
       «24 300» и «35 000» стоят с разным правым краем — колонка «дышит» при
       каждом изменении суммы. Деньги в продукте всегда читают столбцом. */
    .fin-card .fin-amount { font-size: 22px; font-weight: 900; font-variant-numeric: tabular-nums; }
    .fin-card .fin-sub { font-variant-numeric: tabular-nums; }
    .fin-card .fin-sub { font-size: 12px; color: var(--muted); margin-top: 4px; }
    /* Смысл карточки показывает ПОЛОСА слева — тот же приём, что у этапов сметы
       и у строк состава. Рамка вокруг всей карточки спорила с рамкой панели и
       давала на экране три вида обводок сразу. */
    .fin-card.income-card,
    .fin-card.expense-card,
    .fin-card.profit-card { position: relative; overflow: hidden; }
    .fin-card.income-card::before,
    .fin-card.expense-card::before,
    .fin-card.profit-card::before {
      content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
    }
    /* 55%, а не полный цвет: полоса метит карточку, а не кричит. Та же мера,
       что у линии этапа в составе сметы. */
    .fin-card.income-card::before { background: color-mix(in srgb, var(--green) 55%, transparent); }
    .fin-card.expense-card::before { background: color-mix(in srgb, var(--red) 55%, transparent); }
    /* border-color через var(--primary2) вместо хардкода rgba(124,58,237) — та фиксированная
       "тёмная" фиолетовая не совпадает ни с --primary тёмной (108,0,255), ни светлой (91,78,232)
       темы и никогда не меняется вместе с темой. Без прозрачности — border-color не обязана
       быть полупрозрачной, а color-mix() не работает в Chrome < 111. */
    .fin-card.profit-card::before { background: color-mix(in srgb, var(--primary) 60%, transparent); }
    .fin-card.u-pointer { cursor: pointer; transition: border-color var(--dur-1), transform var(--dur-1), box-shadow var(--dur-1); }
    .fin-card.u-pointer:hover { border-color: var(--primary-text); transform: translateY(-2px); box-shadow: var(--elev-1); }

    /* Кнопка «Экспорт в Excel» — иконкой */
    .xlsx-icon-btn {
      width: 38px; height: 38px; flex: 0 0 auto; display: grid; place-items: center;
      border: 1px solid rgba(22,163,74,.4); border-radius: var(--r-md);
      background: rgba(22,163,74,.12); color: var(--text-success);
      cursor: pointer; transition: background var(--dur-1), border-color var(--dur-1), transform var(--dur-1);
    }
    .xlsx-icon-btn:hover { background: rgba(22,163,74,.2); border-color: var(--text-success); transform: translateY(-1px); }
    .xlsx-icon-btn svg { display: block; }
    /* Импорт — та же иконка-кнопка, но нейтральная (отличать «загрузку внутрь» от зелёного экспорта) */
    .xlsx-icon-btn.import { border-color: var(--line); background: var(--panel2); color: var(--muted); }
    .xlsx-icon-btn.import:hover { border-color: rgb(var(--primary-rgb) / .5); background: var(--panel2); color: var(--text); }

    /* Прогноз */
    .fin-h3-note { font-size: 12px; color: var(--muted); font-weight: 500; margin-left: 6px; }
    .fin-forecast-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
    .fin-forecast-card {
      padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--panel2);
    }
    /* Значок в одну строку с подписью: он называет величину раньше, чем глаз
       дочитает капс. inline-flex — иначе svg сидит на базовой линии и подпись
       «прыгает» относительно соседних карточек. */
    .fin-forecast-card .fc-lbl {
      font-size: 11px; color: var(--muted); font-weight: 650;
      text-transform: uppercase; letter-spacing: .03em;
      display: flex; align-items: center; gap: 6px;
    }
    .fin-forecast-card .fc-lbl svg { flex: 0 0 auto; color: var(--primary-text); }
    .fin-forecast-card .fc-val { font-size: 21px; font-weight: 900; font-variant-numeric: tabular-nums; letter-spacing: -.01em; margin: 5px 0 2px; }
    .fin-forecast-card .fc-sub { font-size: 12px; color: var(--muted); }
    .fin-trend { font-weight: 700; }
    .fin-trend.up { color: var(--text-success); }
    .fin-trend.down { color: var(--text-danger); }

    /* Инсайты «Что важно» */
    .fin-insights { display: flex; flex-direction: column; }
    .fin-insight {
      display: flex; gap: 10px; align-items: flex-start;
      padding: 9px 0; border-bottom: 1px solid var(--line);
      font-size: 13.5px; line-height: 1.45;
    }
    .fin-insight:last-child { border-bottom: none; }
    .fin-insight-ico {
      flex: 0 0 auto; display: inline-flex; align-items: center;
      height: 20px; color: var(--muted);
    }
    .fin-insight.good .fin-insight-ico { color: var(--text-success); }
    .fin-insight.warn .fin-insight-ico { color: var(--text-warning); }
    .fin-insight-text { color: var(--text); }

    @media (max-width: 900px) { .fin-forecast-grid { grid-template-columns: 1fr 1fr; } }
    /* Ниже 480px карточки прогноза вставали в СТОЛБИК: четыре коротких числа
       занимали целый экран телефона, и до «Что важно» приходилось листать.
       Числа короткие, подписи в две строки помещаются — держим два в ряд до
       самых узких экранов. */
    @media (max-width: 360px) { .fin-forecast-grid { grid-template-columns: 1fr; } }
    .fin-row {
      display: flex; justify-content: space-between;
      align-items: flex-start; gap: 12px;
      padding: 12px 0; border-bottom: 1px solid var(--line);
    }
    .fin-row:last-child { border-bottom: none; }
    .fin-row-info h4 { margin: 0 0 3px; font-size: 14px; }
    .fin-row-info p { margin: 0; font-size: 12px; color: var(--muted); }
    .fin-row-amount { font-weight: 900; font-size: 16px; white-space: nowrap; }
    .fin-row-amount.positive { color: var(--text-success); }
    .fin-row-amount.negative { color: var(--text-danger); }
    .fin-category-badge {
      display: inline-flex; align-items: center; gap: 4px;
      padding: 3px 8px; border-radius: var(--r-pill); font-size: 12px;
      background: rgb(var(--primary-rgb) / .12); color: var(--primary-on-tint);
      margin-top: 4px;
    }
    .fin-section-head {
      display: flex; justify-content: space-between;
      align-items: center; margin: 20px 0 12px; gap: 12px;
    }
    .fin-section-head h2 { margin: 0; font-size: 18px; }

    /* ── CONTRACTS ───────────────────────────────── */
    .contract-card {
      padding: 18px;
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      background: var(--panel);
      cursor: pointer;
      transition: var(--dur-1);
    }
    /* Тоже через токен: 0 12px 36px rgba(0,0,0,.28) — самая тяжёлая рукописная
       тень в проекте, на светлой теме читалась откровенным пятном. */
    .contract-card:hover { border-color: rgb(var(--primary-rgb) / .5); transform: translateY(-2px); box-shadow: var(--elev-2); }
    .contract-card h3 { margin: 0 0 6px; }
    .contract-card p { margin: 0; font-size: 13px; }
    /* С кем и на сколько — первая строка после названия. */
    .contract-card-meta {
      font-size: 12px; color: var(--muted); margin: 0 0 6px;
    }
    .contract-card-meta b { color: var(--text); font-variant-numeric: tabular-nums; font-weight: 700; }
    /* Незаполненные поля — не серая справка, а вход в мастер. */
    .contract-left-badge {
      display: inline-flex; align-items: center; gap: 4px;
      padding: 3px 9px; border-radius: var(--r-pill);
      font-size: 11px; font-weight: 700; cursor: pointer;
      background: var(--tint-amber-bg); color: var(--text-warning);
      border: 1px solid color-mix(in srgb, var(--yellow) 30%, transparent);
      transition: background var(--dur-1), border-color var(--dur-1);
    }
    .contract-left-badge:hover {
      background: color-mix(in srgb, var(--yellow) 18%, transparent);
      border-color: color-mix(in srgb, var(--yellow) 55%, transparent);
    }
    .contract-sub-title {
      font-size: 16px; margin: 0 0 12px; color: var(--muted);
      display: flex; align-items: center; gap: 8px;
    }
    /* Блок шаблонов стоит ПОД своими договорами, когда они есть, — отделяем. */
    .contract-tpl-block {
      margin-top: 28px; padding-top: 20px; border-top: 1px solid var(--line);
    }
    /* Статус договора задаётся классом из CONTRACT_STATUSES (warn/green), но
       таких правил в стилях НЕ БЫЛО: «На подписи» и «Подписан» выглядели ровно
       как «Черновик» — серой меткой. Единственный признак, по которому в списке
       ищут глазами, ничего не показывал. */
    .badge.warn { background: var(--tint-amber-bg); color: var(--text-warning); }
    .badge.green { background: var(--tint-green-bg); color: var(--tint-green); }
    .contract-status-btn {
      padding: 5px 12px; border-radius: var(--r-pill);
      font-size: 12px; font-weight: 700; cursor: pointer;
      border: 1px solid var(--line); background: transparent; color: var(--muted);
      transition: border-color var(--dur-1), background var(--dur-1), color var(--dur-1);
    }
    .contract-status-btn:hover { border-color: rgb(var(--primary-rgb) / .5); color: var(--text); }
    .contract-status-btn.is-on { border-color: transparent; background: var(--panel2); color: var(--text); }
    .contract-status-btn.is-on.warn { background: var(--tint-amber-bg); color: var(--text-warning); }
    .contract-status-btn.is-on.green { background: var(--tint-green-bg); color: var(--tint-green); }
    .contract-cat-filters { display: flex; gap: 4px; flex-wrap: wrap; }
    /* Фильтр-чип: раньше вид задавался инлайново (включая хардкод #fff), а
       класс `.active-filter` в стилях не существовал вовсе. */
    .chip-filter {
      padding: 4px 10px; border-radius: var(--r-pill);
      border: 1px solid var(--line); background: transparent; color: var(--muted);
      font-size: 12px; font-weight: 600; cursor: pointer;
      transition: border-color var(--dur-1), background var(--dur-1), color var(--dur-1);
    }
    .chip-filter:hover { border-color: rgb(var(--primary-rgb) / .5); color: var(--text); }
    .chip-filter.is-on {
      background: var(--primary); border-color: var(--primary); color: var(--on-color);
    }
    .contract-editor {
      width: 100%;
      min-height: 420px;
      font-family: inherit;
      font-size: 14px;
      line-height: 1.6;
      padding: 16px;
      border-radius: var(--r-lg);
      background: var(--input);
      border: 1px solid var(--line);
      color: var(--text);
      resize: vertical;
    }
    .contract-editor:focus { border-color: rgb(var(--primary-rgb) / .65); box-shadow: 0 0 0 4px rgb(var(--primary-rgb) / .14); outline: none; }


    /* ── TASK MODAL ──────────────────────────────── */
    .task-modal-overlay {
      position: fixed; inset: 0;
      background: rgba(0,0,0,.55);
      z-index: var(--z-modal);
      display: flex; align-items: center; justify-content: center;
      -webkit-backdrop-filter: blur(6px);
      backdrop-filter: blur(6px);
      animation: fadeIn .18s ease;
    }
    .task-modal-box {
      background: var(--panel);
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      padding: 24px;
      width: min(520px, calc(100vw - 32px));
      box-shadow: 0 32px 80px rgba(0,0,0,.55);
      animation: slideUp .2s ease;
    }

    /* ── IMPROVED SUMMARY ────────────────────────── */
    /* Строка этапа в «Итогах сметы»: название — доля — сумма, и под ними
       полоска доли во всю ширину. Сетка, а не flex: полоска должна лежать
       ВТОРОЙ строкой на всю ширину, а три подписи — держать свои колонки, чтобы
       проценты и суммы стояли столбиком у соседних этапов. */
    .summary-stage-row {
      display: grid;
      grid-template-columns: 1fr auto auto;
      align-items: baseline;
      column-gap: 10px; row-gap: 4px; padding: 6px 0;
      font-size: 13px;
    }
    .summary-stage-pct {
      font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums;
    }
    .summary-stage-track {
      grid-column: 1 / -1;
      height: 3px; border-radius: var(--r-pill);
      background: var(--line); overflow: hidden;
    }
    .summary-stage-fill { display: block; height: 100%; border-radius: var(--r-pill); }
    .margin-badge {
      display: inline-flex; align-items: center;
      padding: 3px 8px; border-radius: var(--r-pill); font-size: 12px; font-weight: 700;
      border: 1px solid transparent;
    }
    /* Те же --tint-*, что у остальных капсул: прежние var(--text-success)/(--text-danger)
       давали в светлой теме 4,1:1 и 3,64:1 при пороге AA 4,5:1. */
    .margin-badge.good { background: var(--tint-green-bg); color: var(--tint-green); }
    .margin-badge.ok   { background: var(--tint-amber-bg); color: var(--tint-amber); border-color: rgba(246,189,58,.3); }
    .margin-badge.bad  { background: rgba(220,38,38,.18);  color: var(--tint-red); }
    /* Выручки ещё нет — судить не о чем. Красное здесь читается как «у тебя всё
       плохо» на пустой сделке, хотя данных просто нет. Тот же приём, что у
       health-dot на карточках сделок: отсутствие данных нейтрально, не тревожно. */
    .margin-badge.none { background: var(--panel2); color: var(--muted); border-color: var(--line); }

    /* ── ADMIN / SUPABASE ──────────────────────────── */
    .admin-indicator {
      display: inline-flex; align-items: center; gap: 5px;
      font-size: 12px; font-weight: 700; color: var(--muted);
      background: rgba(22,163,74,.12); border: 1px solid rgba(22,163,74,.25);
      border-radius: var(--r-pill); padding: 4px 10px; white-space: nowrap;
    }
    .admin-indicator::before { content: "●"; color: var(--text-success); font-size: 12px; }
    .presence-dot {
      display: inline-flex; align-items: center; justify-content: center;
      width: 26px; height: 26px; border-radius: 50%;
      background: var(--primary);
      color: var(--on-color); font-size: 12px; font-weight: 800; letter-spacing: -.3px;
      border: 2px solid rgba(34,197,94,.6);
      box-shadow: 0 0 0 2px rgba(34,197,94,.2);
      cursor: default; margin-right: 4px; flex-shrink: 0;
    }
    .presence-area { display: inline-flex; align-items: center; gap: 2px; margin-right: 6px; }
    .client-link { cursor: pointer; }
    .admin-modal-box {
      background: var(--panel);
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      box-shadow: var(--shadow);
      padding: 28px;
      width: min(420px, calc(100vw - 32px));
    }
    .supabase-config-box {
      border: 1px solid rgba(139,127,232,.3);
      border-radius: var(--r-xl);
      padding: 18px;
      margin-top: 18px;
      background: rgba(139,127,232,.04);
    }
    .sync-badge {
      display: inline-flex; align-items: center; gap: 4px;
      font-size: 12px; color: var(--text-success);
      background: rgba(22,163,74,.1); border-radius: var(--r-pill);
      padding: 2px 8px;
    }

    /* ── SKELETON LOADING ───────────────────────────── */
    @keyframes skelShimmer {
      0%   { background-position: -600px 0; }
      100% { background-position: 600px 0; }
    }
    .skel {
      border-radius: var(--r-sm);
      background: linear-gradient(90deg, var(--panel2) 25%, rgba(148,163,184,.08) 50%, var(--panel2) 75%);
      background-size: 600px 100%;
      animation: skelShimmer 1.5s infinite linear;
      display: block;
    }
    [data-theme="light"] .skel {
      background: linear-gradient(90deg, rgba(0,0,0,.05) 25%, rgba(0,0,0,.02) 50%, rgba(0,0,0,.05) 75%);
      background-size: 600px 100%;
      animation: skelShimmer 1.5s infinite linear;
    }
    .skel-card {
      border-radius: var(--r-xl);
      background: var(--panel2);
      border: 1px solid var(--line);
      padding: 20px;
    }

    /* ── AUTH GATE ─────────────────────────────────── */
    .auth-gate {
      position: fixed; inset: 0; z-index: var(--z-auth);
      display: flex; align-items: center; justify-content: center;
      background: var(--bg);
      background-image: radial-gradient(circle at top left, rgb(var(--primary-rgb) / .18), transparent 40%),
        radial-gradient(circle at bottom right, rgba(37,99,235,.12), transparent 40%);
      overflow-y: auto; padding: 16px;
    }
    .auth-gate-inner {
      display: flex; align-items: stretch; gap: 48px;
      width: 100%; max-width: 900px; margin: auto;
    }
    .auth-gate-left {
      flex: 1; padding: 20px 0; display: flex; flex-direction: column; justify-content: center;
    }
    .auth-gate-brand {
      display: flex; align-items: center; gap: 14px; margin-bottom: 0;
    }
    .auth-features-list {
      list-style: none; padding: 0; margin: 0 0 28px; display: flex; flex-direction: column; gap: 14px;
    }
    .auth-features-list li {
      display: flex; align-items: flex-start; gap: 12px;
    }
    .auth-feat-icon {
      width: 28px; height: 28px; flex-shrink: 0; margin-top: 1px;
      display: grid; place-items: center;
      background: rgb(var(--primary-rgb) / .12); border-radius: var(--r-sm);
      color: var(--primary-on-tint);
    }
    .auth-features-list li div {
      display: flex; flex-direction: column; gap: 2px;
    }
    .auth-features-list li strong {
      font-size: 14px; font-weight: 700; color: var(--text);
    }
    .auth-features-list li span {
      font-size: 12px; color: var(--muted);
    }
    .auth-stats-row {
      display: flex; gap: 20px; flex-wrap: wrap;
    }
    .auth-stats-row div {
      display: flex; flex-direction: column; gap: 2px;
      background: rgb(var(--primary-rgb) / .08); border: 1px solid rgb(var(--primary-rgb) / .2);
      border-radius: var(--r-lg); padding: 10px 16px;
    }
    .auth-stats-row strong { font-size: 18px; font-weight: 900; color: var(--primary-text); }
    .auth-stats-row span { font-size: 12px; color: var(--muted); }
    .auth-gate-right { flex: 0 0 auto; display: flex; align-items: center; }
    .auth-gate-box {
      background: var(--panel);
      border: 1px solid var(--line);
      border-radius: var(--r-2xl);
      box-shadow: 0 32px 80px rgba(0,0,0,.28);
      padding: 36px 32px;
      width: min(400px, calc(100vw - 32px));
    }
    .auth-gate-logo {
      display: flex; align-items: center; gap: 12px; margin-bottom: 24px;
    }
    .auth-gate-logo .logo { flex: 0 0 auto; }
    .auth-tab-bar {
      display: flex; gap: 0; margin-bottom: 24px;
      background: var(--panel2); border-radius: var(--r-lg); padding: 4px;
    }
    .auth-tab {
      flex: 1; border: none; border-radius: var(--r-sm); padding: 9px;
      font-size: 13px; font-weight: 600; cursor: pointer;
      background: transparent; color: var(--muted); transition: var(--dur-1);
    }
    .auth-tab.active { background: var(--primary); color: var(--on-color); }
    @media (max-width: 768px) {
      .auth-gate-inner { flex-direction: column; gap: 0; }
      .auth-gate-left { display: none; }
      .auth-gate-right { width: 100%; }
      .auth-gate-box { width: 100%; padding: 28px 20px; }
    }

    /* ── SUPPORT PAGE ──────────────────────────────── */
    .support-card {
      display: flex; align-items: center; gap: 16px;
      padding: 16px; border-radius: var(--r-xl);
      background: var(--panel2); border: 1px solid var(--line);
      cursor: pointer; transition: border-color var(--dur-1), transform var(--dur-1);
    }
    .support-card:hover { border-color: var(--primary); transform: translateY(-1px); }

    /* ── NOTIFICATION BELL ─────────────────────────── */
    .notif-btn {
      position: relative;
    }
    .notif-btn:hover { background: rgba(148,163,184,.12); }
    .notif-badge {
      position: absolute; top: 2px; right: 2px;
      background: var(--red); color: var(--on-color);
      font-size: 12px; font-weight: 900; border-radius: var(--r-pill);
      min-width: 15px; height: 15px; display: flex; align-items: center;
      justify-content: center; padding: 0 3px; line-height: 1;
    }
    .notif-popup {
      position: fixed; right: 16px; top: 62px; z-index: 200;
      width: min(380px, calc(100vw - 24px));
      background: var(--panel);
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      box-shadow: 0 20px 60px rgba(0,0,0,.28);
      overflow: hidden;
    }
    .notif-popup-header {
      display: flex; justify-content: space-between; align-items: center;
      padding: 14px 18px 12px; border-bottom: 1px solid var(--line);
    }
    .notif-list { max-height: 420px; overflow-y: auto; }
    .notif-item {
      display: flex; gap: 12px; align-items: flex-start;
      padding: 12px 18px; border-bottom: 1px solid var(--line);
      cursor: pointer; transition: var(--dur-1);
    }
    .notif-item:hover { background: rgba(148,163,184,.06); }
    .notif-item.unread { background: rgb(var(--primary-rgb) / .05); }
    .notif-dot {
      width: 8px; height: 8px; border-radius: 50%;
      background: var(--primary); flex: 0 0 8px; margin-top: 5px;
    }
    .notif-item.unread .notif-dot { background: var(--primary); }
    .notif-item:not(.unread) .notif-dot { background: transparent; }
    .notif-body { font-size: 12px; line-height: 1.45; }
    .notif-body strong { display: block; font-size: 13px; margin-bottom: 2px; }
    .notif-time { font-size: 12px; color: var(--muted); margin-top: 3px; }

    /* ── ESTIMATE NAV TOOLTIP ──────────────────────── */
    .estimate-nav-btn {
      position: relative;
    }
    .estimate-nav-btn.has-project::after {
      content: "";
      position: absolute;
      top: 5px; right: 5px;
      width: 5px; height: 5px;
      border-radius: 50%;
      background: var(--primary);
      box-shadow: 0 0 6px var(--primary);
    }
    .estimate-nav-tooltip-fixed {
      position: fixed;
      z-index: var(--z-tooltip);
      pointer-events: none;
      background: rgba(10, 13, 28, .92);
      -webkit-backdrop-filter: blur(16px);
      backdrop-filter: blur(16px);
      border: 1px solid rgb(var(--primary-rgb) / .5);
      border-radius: var(--r-md);
      padding: 7px 14px 7px 10px;
      display: flex; align-items: center; gap: 8px;
      font-size: 12px; font-weight: 700;
      color: var(--text);
      white-space: nowrap;
      opacity: 0;
      transform: translateY(-6px);
      transition: opacity var(--dur-2) var(--ease-out),
                  transform var(--dur-2) var(--ease-out);
      box-shadow: 0 8px 24px rgba(0,0,0,.35), 0 0 0 1px rgb(var(--primary-rgb) / .12);
    }
    .estimate-nav-tooltip-fixed.visible {
      opacity: 1;
      transform: translateY(0);
    }
    .estimate-nav-tooltip-fixed::before {
      content: "";
      width: 6px; height: 6px;
      border-radius: 50%;
      background: var(--primary);
      flex-shrink: 0;
      box-shadow: 0 0 8px var(--primary);
    }
    [data-theme="light"] .estimate-nav-tooltip-fixed {
      background: rgba(255,255,255,.95);
      box-shadow: 0 8px 24px rgba(0,0,0,.12), 0 0 0 1px rgb(var(--primary-rgb) / .15);
    }

    /* ── ONBOARDING SPOTLIGHT TOUR ───────────────────── */
    .tour-spotlight {
      position: fixed;
      z-index: var(--z-tour);
      border-radius: var(--r-lg);
      box-shadow: 0 0 0 9999px rgba(8, 9, 16, .72);
      pointer-events: none;
      transition: top var(--dur-3) var(--ease-out), left var(--dur-3) var(--ease-out),
                  width var(--dur-3) var(--ease-out), height var(--dur-3) var(--ease-out);
    }
    [data-theme="light"] .tour-spotlight {
      box-shadow: 0 0 0 9999px rgba(20, 20, 24, .55);
    }
    .tour-popover {
      position: fixed;
      z-index: calc(var(--z-tour) + 1);
      /* Было var(--card) — такой переменной нет ни в одной теме, из-за чего
         объявление отбрасывалось и всплывашка тура рисовалась ПРОЗРАЧНОЙ
         поверх затемнения. Тот же баг был у .deal-switcher-btn. */
      background: var(--panel);
      border: 1px solid var(--line);
      border-radius: var(--r-xl);
      padding: 16px 18px;
      width: 270px;
      box-shadow: 0 12px 40px rgba(0,0,0,.4);
      transition: top var(--dur-3) var(--ease-out), left var(--dur-3) var(--ease-out);
    }
    .tour-popover h4 { margin: 0 0 6px; font-size: 14px; }
    .tour-popover p { margin: 0 0 14px; font-size: 13px; color: var(--muted); line-height: 1.5; }
    .tour-popover-footer { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
    .tour-dots { display: flex; gap: 5px; }
    .tour-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--line); transition: all var(--dur-2); }
    .tour-dot.active { background: var(--primary); width: 16px; border-radius: var(--r-xs); }
    .tour-popover-actions { display: flex; gap: 8px; }

    /* ── THEME TOGGLE BUTTON ───────────────────────── */
    .theme-toggle-btn {
      width: 36px; height: 36px; padding: 0;
      border: 1px solid var(--line);
      background: none; border-radius: var(--r-md);
      cursor: pointer; color: var(--text);
      display: grid; place-items: center;
      transition: border-color var(--dur-2), background var(--dur-2), transform var(--dur-2);
      flex: 0 0 auto;
    }
    .theme-toggle-btn:hover {
      background: rgba(148,163,184,.1);
      border-color: var(--primary);
      transform: scale(1.08);
    }
    .theme-toggle-btn:active { transform: scale(.93) rotate(20deg); }

    .theme-icon {
      position: relative; width: 16px; height: 16px;
    }
    .theme-icon .icon-moon,
    .theme-icon .icon-sun {
      position: absolute; top: 0; left: 0;
      transition: opacity var(--dur-3) var(--ease-out),
                  transform var(--dur-3) var(--ease-out);
    }

    /* dark mode (default): moon visible, sun hidden */
    .theme-icon .icon-moon { opacity: 1; transform: rotate(0deg) scale(1); }
    .theme-icon .icon-sun  { opacity: 0; transform: rotate(90deg) scale(0); }

    /* light mode: sun visible, moon hidden */
    [data-theme="light"] .theme-icon .icon-moon { opacity: 0; transform: rotate(-90deg) scale(0); }
    [data-theme="light"] .theme-icon .icon-sun  { opacity: 1; transform: rotate(0deg) scale(1); }

    /* ── HELP BUTTON ─────────────────────────────── */
    .help-btn {
      border: 1px solid var(--line); background: none;
      color: var(--muted); border-radius: 50%;
      width: 30px; height: 30px; font-size: 14px; font-weight: 900;
      cursor: pointer; display: flex; align-items: center; justify-content: center;
      transition: var(--dur-1);
    }
    .help-btn:hover { border-color: var(--primary); color: var(--primary); }

    .profile-avatar-btn {
      width: 34px; height: 34px; border-radius: 50%; border: none; cursor: pointer;
      background: var(--primary);
      color: var(--on-color); font-size: 14px; font-weight: 900;
      display: flex; align-items: center; justify-content: center;
      overflow: hidden; flex: 0 0 34px;
      transition: var(--dur-1); box-shadow: 0 4px 14px rgb(var(--primary-rgb) / .35);
    }
    .profile-avatar-btn:hover { box-shadow: 0 6px 20px rgb(var(--primary-rgb) / .5); transform: scale(1.06); }
    .profile-avatar-btn img { width: 100%; height: 100%; object-fit: cover; display: block; }
    /* Признак «открыт профиль». Было `0 0 0 2px #fff` — в тёмной теме белое кольцо
       читалось, в светлой оно ложилось на светлый топбар и состояние пропадало.
       Теперь: внутренний зазор цветом фона + внешнее кольцо акцентом — видно в обеих. */
    .profile-avatar-btn.active { box-shadow: 0 0 0 2px var(--bg2), 0 0 0 4px var(--primary2), 0 4px 14px rgb(var(--primary-rgb) / .35); }

    /* ── PROFILE PAGE ─────────────────────────────── */
    .plan-card {
      border: 1px solid var(--line); border-radius: var(--r-xl);
      padding: 20px; text-align: center; transition: var(--dur-2); cursor: default;
    }
    .plan-card.popular {
      border-color: var(--primary); background: rgb(var(--primary-rgb) / .06);
    }
    .plan-card .plan-price { font-size: 28px; font-weight: 900; margin: 10px 0 4px; }
    .plan-card .plan-period { font-size: 12px; color: var(--muted); }
    .plan-card .plan-label { font-size: 13px; font-weight: 700; margin-bottom: 6px; }
    .popular-badge {
      display: inline-block; background: var(--primary); color: var(--on-color);
      font-size: 12px; font-weight: 900; border-radius: var(--r-pill);
      padding: 2px 10px; margin-bottom: 8px; text-transform: uppercase;
    }
    .plan-save { font-size: 12px; color: var(--text-success); font-weight: 700; margin-top: 4px; }

    /* ── KNOWLEDGE BASE ────────────────────────────── */
    /* Приведено к общему языку пустых состояний (.empty/.empty__icon): 1px пунктир,
       radius var(--r-xl), круглая иконка на --primary-bg без своей рамки —
       раньше kb-new-card (эта плитка + «+ Новый клиент») была толще/квадратнее. */
    .kb-new-card {
      background: var(--panel2); border: 1px dashed var(--line);
      border-radius: var(--r-xl); padding: 16px; cursor: pointer; transition: var(--dur-1);
      display: flex; flex-direction: column; align-items: center; justify-content: center;
      gap: 10px; min-height: 130px; color: var(--muted);
    }
    .kb-new-card:hover { border-color: var(--primary-on-tint); color: var(--primary-on-tint); background: rgb(var(--primary-rgb) / .06); }
    .kb-new-icon {
      width: 44px; height: 44px; border-radius: 50%;
      background: var(--primary-bg);
      display: flex; align-items: center; justify-content: center;
      font-size: 22px; font-weight: 400; color: var(--primary-on-tint); transition: var(--dur-1);
    }
    .kb-new-card:hover .kb-new-icon { background: rgb(var(--primary-rgb) / .2); }
    .kb-new-label { font-size: 13px; font-weight: 600; }
    /* Поиск и тематики — строкой, как фильтры «Задач». */
    .kb-filters { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-bottom: 18px; }
    .kb-search { flex: 1 1 240px; max-width: 420px; }
    .kb-chips { flex: 1 1 100%; }
    .kb-chip-count { opacity: .6; font-size: 12px; font-variant-numeric: tabular-nums; }
    /* Видеоинструкции (12.10.2026) — «Помощь и обучение» в «Поддержке»: строки
       «обложка + текст» (колонка 680 px), ролик открывается окном-плеером; тот же плеер
       открывается ссылкой из шагов «Начала работы» на главной */
    .howto-list { display: grid; gap: 10px; margin: 0 0 22px; padding: 18px; border: 1px solid var(--line); border-radius: var(--r-xl);
      background: radial-gradient(120% 140% at 0% 100%, rgb(var(--primary-rgb) / .12), transparent 60%), var(--panel2); }
    .howto-list-head { display: grid; gap: 4px; margin-bottom: 4px; }
    .howto-list-head h2 { display: inline-flex; align-items: center; gap: 8px; margin: 0; font-size: 18px; }
    .howto-list-head p { margin: 0; font-size: 13px; color: var(--muted); }
    .howto-row { display: flex; align-items: center; gap: 14px; width: 100%; padding: 8px; border: 1px solid transparent; border-radius: var(--r-lg);
      background: transparent; color: var(--text); text-align: left; font: inherit; cursor: pointer; transition: background var(--dur-1), border-color var(--dur-1); }
    .howto-row:hover { background: rgb(var(--primary-rgb) / .06); border-color: rgb(var(--primary-rgb) / .3); }
    .howto-row:focus-visible { outline: var(--ring-w) solid var(--ring); outline-offset: 2px; }
    .howto-thumb { position: relative; flex: 0 0 200px; aspect-ratio: 16 / 9; border-radius: var(--r-md); overflow: hidden; background: var(--bg);
      box-shadow: 0 0 0 1px rgba(255,255,255,.08); }
    .howto-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
    /* в углу, а не по центру: в центре обложки — название ролика, кнопка закрывала его («Перв▶мета») */
    .howto-play { position: absolute; left: 8px; bottom: 8px; width: 34px; height: 34px; border-radius: var(--r-pill);
      display: grid; place-items: center; background: var(--primary); color: var(--on-color); box-shadow: 0 8px 24px -6px rgb(var(--primary-rgb) / .8);
      transition: transform var(--dur-1); }
    .howto-row:hover .howto-play { transform: scale(1.08); }
    .howto-text { display: grid; gap: 4px; min-width: 0; }
    .howto-text b { font-size: 15px; line-height: 1.3; }
    .howto-kicker { font-size: 12px; font-weight: 700; color: var(--primary-text); font-variant-numeric: tabular-nums; }
    .howto-desc { font-size: 13px; line-height: 1.5; color: var(--muted); }
    /* Настройки → «Вход и пароль» */
    .acc-note { display: flex; gap: 10px; align-items: flex-start; margin: 4px 0 14px; padding: 10px 12px; border-radius: var(--r-lg); font-size: 13px; line-height: 1.5;
      background: rgb(var(--primary-rgb) / .08); border: 1px solid rgb(var(--primary-rgb) / .25); }
    .acc-note > svg { flex: 0 0 auto; margin-top: 2px; color: var(--primary-text); }
    .acc-note.is-ok { background: rgba(22,163,74,.08); border-color: rgba(22,163,74,.3); }
    .acc-note.is-ok > svg { color: var(--text-success); }
    .acc-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 10px 0; border-bottom: 1px solid var(--line); }
    .acc-provs { display: flex; flex-wrap: wrap; gap: 6px; }

    /* Свой выбор даты (datePickerHtml): поле-кнопка с датой словами, быстрые сроки, сетка месяца */
    .dp { display: grid; gap: 10px; }
    .dp-field { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 48px; padding: 8px 12px; border: 1px solid var(--line); border-radius: var(--r-lg);
      background: var(--input); color: var(--text); font: inherit; text-align: left; cursor: pointer; transition: border-color var(--dur-1), box-shadow var(--dur-1); }
    .dp-field > svg { flex: 0 0 auto; color: var(--primary-text); }
    .dp-field:hover, .dp-field.is-open { border-color: rgb(var(--primary-rgb) / .5); }
    .dp-field.is-open { box-shadow: 0 0 0 3px rgb(var(--primary-rgb) / .15); }
    .dp-field:focus-visible { outline: var(--ring-w) solid var(--ring); outline-offset: 2px; }
    .dp-field-text { display: grid; gap: 1px; flex: 1; min-width: 0; font-size: 14px; }
    .dp-field-text b { font-size: 15px; font-weight: 700; }
    .dp-field-text span { font-size: 12px; color: var(--muted); }
    .dp-field.is-empty .dp-field-text { color: var(--muted); }
    .dp-chev { display: grid; color: var(--muted); transform: rotate(90deg); transition: transform var(--dur-1); }
    .dp-field.is-open .dp-chev { transform: rotate(-90deg); }
    .dp-quick { display: flex; flex-wrap: wrap; gap: 6px; }
    .dp-quick .chip { font-size: 12px; padding: 4px 10px; }
    /* календарь открыт — быстрые сроки столбиком справа от него: там было пусто, а строка над
       сеткой удлиняла окно (скриншот владельца 12.10) */
    .dp.is-open { grid-template-columns: minmax(0, 340px) minmax(0, 1fr); align-items: start; }
    .dp.is-open .dp-field { grid-column: 1 / -1; }
    .dp.is-open .dp-cal { grid-column: 1; grid-row: 2; }
    .dp.is-open .dp-quick { grid-column: 2; grid-row: 2; flex-direction: column; align-items: stretch; }
    .dp.is-open .dp-quick .chip { justify-content: flex-start; text-align: left; padding: 7px 12px; }
    @media (max-width: 560px) {
      .dp.is-open { grid-template-columns: minmax(0, 1fr); }
      .dp.is-open .dp-cal, .dp.is-open .dp-quick { grid-column: 1; grid-row: auto; }
      .dp.is-open .dp-quick { flex-direction: row; }
    }
    /* компактно: окно сделки и так длинное (владелец: «всё не вмещается, календарь очень большой») */
    .dp-cal { max-width: 340px; padding: 8px 10px 10px; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--panel); }
    .dp-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }
    .dp-head b { font-size: 14px; }
    .dp-nav { display: grid; place-items: center; width: 28px; height: 28px; border: 1px solid var(--line); border-radius: var(--r-md); background: transparent;
      color: var(--text); cursor: pointer; transition: background var(--dur-1); }
    .dp-nav svg { transform: rotate(180deg); }
    .dp-nav.is-next svg { transform: none; }
    .dp-nav:hover { background: rgb(var(--primary-rgb) / .1); }
    .dp-nav:focus-visible { outline: var(--ring-w) solid var(--ring); outline-offset: 2px; }
    .dp-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 2px; }
    .dp-wd { padding: 2px 0 4px; text-align: center; font-size: 10px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
    .dp-wd.is-wkd { color: var(--text-danger, var(--red)); opacity: .75; }
    .dp-day { height: 30px; border: 1px solid transparent; border-radius: var(--r-sm); background: transparent; color: var(--text); font: inherit; font-size: 13px;
      font-variant-numeric: tabular-nums; cursor: pointer; transition: background var(--dur-1), color var(--dur-1); }
    .dp-day:hover { background: rgb(var(--primary-rgb) / .12); }
    .dp-day:focus-visible { outline: var(--ring-w) solid var(--ring); outline-offset: 1px; }
    .dp-day.is-wkd { color: var(--text-danger, var(--red)); }
    .dp-day.is-past { opacity: .45; }
    .dp-day.is-other { opacity: .3; }
    .dp-day.is-today { border-color: rgb(var(--primary-rgb) / .6); font-weight: 700; }
    .dp-day.is-sel { background: var(--primary); color: var(--on-color); font-weight: 800; opacity: 1; box-shadow: 0 6px 16px -6px rgb(var(--primary-rgb) / .8); }
    /* на телефоне палец — дни выше, но не 44: семь колонок по ~46 px и так дают широкую цель */
    @media (hover: none) and (pointer: coarse) { .dp-cal { max-width: none; } .dp-day { height: 38px; } .dp-nav { width: 36px; height: 36px; } }

    /* Плашка срока в шапке сделки — кнопка: открывает карточку сделки на поле дедлайна */
    .deal-deadline-pill { display: inline-flex; align-items: center; gap: 5px; font-family: inherit; line-height: 1.4; cursor: pointer; position: relative;
      transition: filter var(--dur-1), transform var(--dur-1); }
    .deal-deadline-pill:hover { filter: brightness(1.18); }
    .deal-deadline-pill:active { transform: scale(.97); }
    .deal-deadline-pill:focus-visible { outline: var(--ring-w) solid var(--ring); outline-offset: 2px; }
    @media (hover: none) and (pointer: coarse) { .deal-deadline-pill::after { content: ""; position: absolute; inset: -10px -4px; } }
    .howto-sub { display: inline-flex; align-items: center; gap: 6px; margin-top: 4px; font-size: 12px; font-weight: 700; letter-spacing: .04em;
      text-transform: uppercase; color: var(--muted); }
    /* «Быстрый старт» и «Тур по интерфейсу» — внизу того же раздела, двумя кнопками */
    .howto-more { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-top: 6px; padding-top: 14px; border-top: 1px solid var(--line); }
    .howto-more-btn { display: flex; align-items: center; gap: 12px; padding: 12px; border: 1px solid var(--line); border-radius: var(--r-lg);
      background: var(--panel); color: var(--text); text-align: left; font: inherit; cursor: pointer; transition: background var(--dur-1), border-color var(--dur-1); }
    .howto-more-btn:hover { background: rgb(var(--primary-rgb) / .06); border-color: rgb(var(--primary-rgb) / .3); }
    .howto-more-btn:focus-visible { outline: var(--ring-w) solid var(--ring); outline-offset: 2px; }
    .howto-more-btn > span:last-child { display: grid; gap: 2px; min-width: 0; }
    .howto-more-btn b { font-size: 14px; }
    .howto-modal-box { width: min(960px, calc(100vw - 24px)); padding: 22px; }
    .howto-modal-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; margin-bottom: 14px; }
    .howto-modal-head h2 { margin: 4px 0 0; font-size: 19px; }
    .howto-modal-frame { aspect-ratio: 16 / 9; border-radius: var(--r-lg); overflow: hidden; background: var(--bg); }
    .howto-modal-frame video { display: block; width: 100%; height: 100%; }
    .howto-next { margin-top: 14px; }
    .onb-howto-link { display: inline-flex; align-items: center; gap: 6px; margin-top: 4px; padding: 2px 0; min-height: 28px; border: 0; background: none;
      font: inherit; font-size: 12px; font-weight: 600; color: var(--primary-text); cursor: pointer; border-radius: var(--r-sm); }
    .onb-howto-link:hover { color: var(--text); }
    .onb-howto-link:focus-visible { outline: var(--ring-w) solid var(--ring); outline-offset: 2px; }
    @media (hover: none) and (pointer: coarse) { .onb-howto-link { min-height: 44px; } }
    @media (max-width: 560px) {
      .howto-list { padding: 14px; }
      .howto-row { flex-direction: column; align-items: stretch; }
      .howto-thumb { flex: 0 0 auto; width: 100%; }
      .howto-more { grid-template-columns: minmax(0, 1fr); }
      .howto-modal-box { padding: 14px; }
    }
    .kb-doc-card {
      background: var(--panel2); border: 1px solid var(--line);
      border-radius: var(--r-xl); padding: 16px; cursor: pointer; transition: var(--dur-1);
    }
    .kb-doc-card:hover { border-color: rgb(var(--primary-rgb) / .4); background: rgb(var(--primary-rgb) / .04); }
    .kb-doc-card h3 { margin: 0 0 6px; font-size: 14px; font-weight: 800; }
    .kb-doc-card p { margin: 0; font-size: 12px; }
    .kb-editor { display: flex; flex-direction: column; gap: 12px; }
    .kb-editor textarea { min-height: 320px; font-family: inherit; font-size: 14px; line-height: 1.6; }
    .kb-cat-badge {
      font-size: 12px; font-weight: 700; padding: 2px 8px;
      border-radius: var(--r-pill); display: inline-flex; text-transform: uppercase;
    }
    /* Через --tint-*: одна пара значений на оттенок вместо хардкода + точечных
       переопределений для светлой темы (они всё равно не покрывали guide/client
       и давали 3,0-4,4:1 — замер в тесте контраста капсул). */
    .kb-cat-badge.sales  { background: var(--tint-green-bg);  color: var(--tint-green); }
    .kb-cat-badge.prod   { background: var(--tint-blue-bg);   color: var(--tint-blue); }
    .kb-cat-badge.price  { background: var(--tint-amber-bg);  color: var(--tint-amber); }
    .kb-cat-badge.client { background: var(--tint-violet-bg); color: var(--tint-violet); }
    .kb-cat-badge.guide  { background: var(--tint-cyan-bg);   color: var(--tint-cyan); }

    .kb-cat-list { display: flex; flex-direction: column; gap: 6px; }
    .kb-cat-row {
      display: flex; align-items: center; gap: 10px;
      padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--r-md);
    }

    /* ── BURGER MENU ──────────────────────────────── */
    .burger-btn {
      display: none;
      background: none;
      border: 1px solid var(--line);
      border-radius: var(--r-md);
      padding: 7px 10px;
      color: var(--text);
      cursor: pointer;
      flex: 0 0 auto;
      align-items: center;
      justify-content: center;
      gap: 3px;
      flex-direction: column;
    }
    .burger-btn span {
      display: block;
      width: 18px; height: 2px;
      background: currentColor;
      border-radius: var(--r-xs);
      transition: var(--dur-2) var(--ease-out);
    }
    /* ── ONBOARDING SLIDER ──────────────────────────── */
    .ob-slider-wrap { overflow: hidden; position: relative; }
    .ob-slides {
      display: flex;
      transition: transform var(--dur-3) var(--ease-out);
    }
    .ob-slide {
      min-width: 100%;
      padding: 0 2px;
    }
    /* Мини-иллюстрация слайда онбординга — реальный скриншот соответствующего
       экрана CRM (снят локально через Playwright, см. onboarding/*.png). Если файла
       нет (например, деплой без папки onboarding/) — <img onerror> открывает
       .ob-mock-fallback: лёгкий CSS-мокап теми же цветами/радиусами, что и в
       настоящих карточках/статусах, чтобы не показывать битую картинку. */
    .ob-mock {
      width: 100%; aspect-ratio: 16 / 8; max-height: 46vh;
      border-radius: var(--r-xl); overflow: hidden;
      background: linear-gradient(135deg, rgb(var(--primary-rgb) / .14), rgba(37,99,235,.1));
      border: 1px solid var(--line);
      margin-bottom: 18px;
    }
    .ob-mock img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top; }
    .ob-mock-fallback {
      display: none; width: 100%; height: 100%;
      flex-direction: column; justify-content: center; gap: 14px;
      padding: 20px 24px; box-sizing: border-box;
    }
    .ob-mock-kpis { display: flex; gap: 8px; }
    .ob-mock-kpi { flex: 1; height: 26px; border-radius: var(--r-sm); }
    .ob-mock-bars { display: flex; align-items: flex-end; gap: 6px; height: 56px; }
    .ob-mock-bars span { flex: 1; background: var(--primary2); opacity: .55; border-radius: var(--r-xs) var(--r-xs) 0 0; }
    .ob-mock-kanban { display: flex; gap: 10px; height: 100%; }
    .ob-mock-col { flex: 1; display: flex; flex-direction: column; gap: 7px; }
    .ob-mock-card { display: block; height: 24px; border-radius: var(--r-sm); background: var(--panel); border: 1px solid var(--line); border-left: 3px solid var(--line); }
    .ob-mock-finance { display: flex; flex-direction: column; gap: 9px; }
    .ob-mock-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
    .ob-mock-line { height: 8px; border-radius: var(--r-xs); background: var(--line); }
    .ob-mock-num { font-size: 12.5px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--text); white-space: nowrap; }
    .ob-mock-total { padding-top: 8px; border-top: 1px dashed var(--line); }
    .ob-mock-team { display: flex; gap: 8px; align-items: center; }
    .ob-mock-avatar {
      width: 34px; height: 34px; border-radius: 50%;
      display: grid; place-items: center; flex-shrink: 0;
      color: var(--on-color); font-size: 11px; font-weight: 700;
    }
    .ob-mock-avatar-add { background: var(--panel2); border: 1px dashed var(--line); color: var(--muted); font-size: 16px; }
    .ob-mock-tasks { display: flex; flex-direction: column; gap: 6px; }
    .ob-mock-task { height: 8px; width: 100%; border-radius: var(--r-xs); background: var(--line); }
    .ob-mock-settings { display: flex; align-items: center; gap: 18px; }
    .ob-mock-gear { color: var(--primary-text); opacity: .7; flex-shrink: 0; }
    .ob-mock-docs { flex: 1; display: flex; flex-direction: column; gap: 7px; }
    .ob-mock-doc { height: 20px; border-radius: var(--r-sm); background: var(--panel); border: 1px solid var(--line); }
    .ob-mock-plans { display: flex; gap: 8px; height: 100%; align-items: stretch; }
    .ob-mock-plan { flex: 1; border-radius: var(--r-md); background: var(--panel); border: 1px solid var(--line); }
    .ob-mock-plan-active { background: rgb(var(--primary-rgb) / .14); border-color: var(--primary); }
    .ob-slide h3 { margin: 0 0 8px; font-size: 17px; }
    .ob-slide p { margin: 0; font-size: 14px; line-height: 1.5; }
    .ob-nav {
      display: flex; align-items: center; justify-content: space-between;
      margin-top: 22px; gap: 12px;
    }
    .ob-dots {
      display: flex; gap: 6px; align-items: center;
    }
    .ob-dot {
      width: 7px; height: 7px; border-radius: 50%;
      background: var(--line); transition: var(--dur-2) var(--ease-out); cursor: pointer;
    }
    .ob-dot.active {
      background: var(--primary);
      width: 22px; border-radius: var(--r-xs);
    }
    .ob-arrow {
      background: none; border: 1px solid var(--line);
      border-radius: var(--r-md); padding: 7px 14px;
      color: var(--text); font-size: 16px; cursor: pointer;
      transition: var(--dur-1);
    }
    .ob-arrow:disabled { opacity: .3; cursor: default; }
    .ob-arrow:not(:disabled):hover { border-color: rgb(var(--primary-rgb) / .5); background: rgb(var(--primary-rgb) / .08); }

/* ── MOBILE TOPBAR: show only logo + profile icon ── */
@media (max-width: 768px) {
  .brand h1 { display: none; }
  #adminTopbar { display: none; }
}

/* ── BRIEF MODE (public client form) ───────────────── */
body.brief-mode .topbar { display: none !important; }
body.brief-mode .mobile-bottom-nav { display: none !important; }
body.brief-mode .app { padding-top: 0 !important; min-height: 100vh; display: block; }
body.brief-mode #appContent { padding: 0; }

/* ── CALC MODE (public estimate calculator, ?calc=…) ─── */
body.calc-mode .topbar { display: none !important; }
body.calc-mode .mobile-bottom-nav { display: none !important; }
body.calc-mode .app { padding-top: 0 !important; min-height: 100vh; display: block; }
body.calc-mode #appContent { padding: 0; }

.brief-wrap {
  min-height: 100vh;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
  padding: 40px 16px 60px;
}
.brief-inner { width: 100%; max-width: 580px; }
.brief-logo-row {
  display: flex; align-items: center; gap: 14px; margin-bottom: 32px;
}
.brief-logo-box {
  width: 48px; height: 48px; border-radius: var(--r-xl); flex-shrink: 0;
  background: var(--primary);
  display: grid; place-items: center;
}
/* Первая буква названия студии, когда логотипа нет, — тот же приём, что и в
   клиентском портале (.portal-brand-logo.letter). */
.brief-logo-box.letter {
  color: var(--on-color); font-size: 21px; font-weight: 900; line-height: 1;
}
.brief-card {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-2xl); padding: 36px 32px; box-shadow: var(--shadow);
}
.brief-card h1 { margin: 0 0 8px; font-size: 24px; font-weight: 900; }
.brief-card > p.brief-sub { margin: 0 0 24px; color: var(--muted); font-size: 14px; line-height: 1.55; }
.brief-section-title {
  font-size: 13px; font-weight: 700; color: var(--primary-text);
  padding: 10px 0 8px; margin-top: 12px;
  border-top: 1px solid var(--line);
  letter-spacing: .01em;
}
.brief-section-title:first-of-type { margin-top: 0; border-top: none; }
.brief-fields { display: flex; flex-direction: column; gap: 12px; margin-bottom: 4px; }
.brief-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
/* Согласие на обработку ПД. Вид и размеры — те же, что у такой же галочки в
   публичном калькуляторе (.calc-agree): одна и та же просьба к человеку в двух
   формах продукта не должна выглядеть по-разному. min-height держит цель
   касания: сам чекбокс 18px, но нажимают по всей строке с текстом. */
.brief-agree {
  display: flex; align-items: flex-start; gap: 9px; min-height: 44px;
  margin-top: 16px; padding: 6px 0;
  font-size: 12.5px; line-height: 1.5; color: var(--muted); cursor: pointer;
}
.brief-agree input { width: 18px; height: 18px; margin-top: 1px; flex-shrink: 0; accent-color: var(--primary); }
.brief-agree a { color: var(--primary-text); }
.brief-submit {
  width: 100%; padding: 15px; margin-top: 20px;
  border-radius: var(--r-lg); font-size: 16px; font-weight: 800;
  background: var(--primary);
  color: var(--on-color); border: none; cursor: pointer;
  transition: background var(--dur-2), transform var(--dur-2); letter-spacing: -.01em;
}
.brief-submit:hover:not(:disabled) { background: var(--primary2); }
.brief-submit:hover:not(:disabled) { opacity: .88; transform: translateY(-1px); }
.brief-submit:disabled { opacity: .5; cursor: default; }
.brief-error { color: var(--text-danger); font-size: 13px; margin-top: 10px; }
.brief-success-wrap { text-align: center; padding: 48px 24px; }
/* Кружок с галочкой вместо убранного эмодзи: раньше пустой div на 64px просто
   держал дыру над «Заявка отправлена!» — а это первый экран, который видит
   заказчик после заполнения брифа. */
.brief-success-icon {
  width: 64px; height: 64px; margin: 0 auto 16px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(22, 163, 74, .14); color: var(--text-success);
}
.brief-success-wrap h2 { margin: 0 0 10px; font-size: 26px; font-weight: 900; }
.brief-success-wrap p { color: var(--muted); font-size: 15px; line-height: 1.55; margin: 0; }
.brief-link-box {
  background: rgb(var(--primary-rgb) / .08); border: 1px solid rgb(var(--primary-rgb) / .3);
  border-radius: var(--r-lg); padding: 12px 14px; min-height: 44px;
  display: flex; align-items: center; gap: 10px; margin-top: 16px;
  width: 100%; font: inherit; color: inherit; text-align: left; cursor: pointer;
  transition: background var(--dur-1), border-color var(--dur-1);
}
.brief-link-box:hover { background: rgb(var(--primary-rgb) / .14); border-color: rgb(var(--primary-rgb) / .5); }
.brief-link-hint {
  margin-left: auto; flex: 0 0 auto; display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; font-weight: 700; color: var(--primary-text);
}
/* Контакт заявки — ссылка: нажал и ответил. */
.brief-contact {
  display: inline-flex; align-items: center; gap: 5px;
  color: var(--muted); text-decoration: none; border-bottom: 1px dashed transparent;
}
.brief-contact:hover { color: var(--primary-text); border-bottom-color: currentColor; }
/* Метки заявки: подпись вопроса + ответ клиента. */
.brief-tags { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.brief-tag {
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: 3px 9px; border-radius: var(--r-pill);
  font-size: 12px; font-weight: 700; border: 1px solid transparent;
}
.brief-tag i {
  font-style: normal; font-size: 10.5px; font-weight: 700;
  letter-spacing: .03em; text-transform: uppercase; opacity: .75;
}
.brief-tag--type { background: rgb(var(--primary-rgb) / .1); color: var(--primary-text); }
.brief-tag--budget { background: var(--tint-blue-bg); color: var(--text-info); border-color: color-mix(in srgb, var(--blue) 25%, transparent); }
.brief-tag--deadline { background: var(--tint-amber-bg); color: var(--text-warning); border-color: color-mix(in srgb, var(--yellow) 30%, transparent); }
.brief-link-url {
  flex: 1; min-width: 0; font-size: 12px; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  -webkit-user-select: all; user-select: all; font-family: monospace;
}
.brief-card-item {
  background: var(--panel2); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: 16px; transition: var(--dur-1);
}
.brief-card-item:hover { border-color: rgb(var(--primary-rgb) / .3); }
.brief-card-item.expanded { border-color: rgb(var(--primary-rgb) / .35); }

/* Раскрывающаяся заявка брифа */
.brief-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; border-radius: var(--r-md); }
/* На узком экране шапка заявки переносится: имя и контакты сверху, действия
   строкой ниже. Иначе «Создать сделку» наезжала на почту клиента — а по ней
   как раз и отвечают. */
@media (max-width: 640px) {
  .brief-head { flex-direction: column; align-items: stretch; gap: 10px; }
  .brief-head > div:last-child { justify-content: flex-start; }
  .brief-head .btn.primary { flex: 1; }
}
.brief-expand-btn {
  width: 30px; height: 30px; flex: 0 0 auto; border-radius: var(--r-sm);
  border: 1px solid var(--line); background: var(--panel); color: var(--muted);
  cursor: pointer; display: grid; place-items: center;
  transition: transform var(--dur-2) var(--ease-out), color var(--dur-1), border-color var(--dur-1), background var(--dur-1);
}
.brief-expand-btn:hover { color: var(--primary-on-tint); border-color: var(--primary); background: rgb(var(--primary-rgb) / .08); }
.brief-expand-btn.open { transform: rotate(180deg); color: var(--primary-text); border-color: var(--primary); }
.brief-preview {
  margin: 10px 0 0; font-size: 13px; color: var(--muted); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/* Структурированная заявка брифа (вместо одного блока текста) */
.brief-idea {
  margin-top: 12px; padding: 12px 14px;
  background: rgb(var(--primary-rgb) / .06); border: 1px solid rgb(var(--primary-rgb) / .18);
  border-radius: var(--r-lg);
}
.brief-idea p { margin: 6px 0 0; font-size: 13.5px; color: var(--text); line-height: 1.6; white-space: pre-line; }
/* Сетка полей ТОЛЬКО в карточке заявки CRM. Раньше правило было без скоупа и
   перебивало флекс-раскладку публичной формы (.brief-fields), из-за чего на
   десктопе контакты лепились в 4 колонки, а textarea вставала в пол-ширины. */
.brief-card-item .brief-fields {
  margin-top: 12px; display: grid; grid-template-columns: 1fr 1fr; gap: 10px;
}
.brief-field {
  display: flex; flex-direction: column; gap: 3px; min-width: 0;
  padding: 10px 12px; background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-md);
}
.brief-field.wide { grid-column: 1 / -1; }
.brief-field-label {
  font-size: 11px; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: .04em;
}
.brief-field-value { font-size: 13px; color: var(--text); line-height: 1.5; white-space: pre-line; word-break: break-word; }

/* ── CRM: селектор типа брифа + панель типа ─────────── */
/* Одна строка со скроллом, как .deal-tabs/.crm-home-funnel — не переносится
   на 2-3 строки при 5 типах брифа на узком экране. */
.brief-type-tabs {
  display: flex; gap: 8px; flex-wrap: nowrap; margin-top: 16px;
  overflow-x: auto; scrollbar-width: none;
  -webkit-mask-image: linear-gradient(to right, transparent 0, black 12px, black calc(100% - 12px), transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, black 12px, black calc(100% - 12px), transparent 100%);
}
.brief-type-tabs::-webkit-scrollbar { display: none; }
.brief-type-chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 14px; border-radius: var(--r-pill);
  border: 1px solid var(--line); background: var(--panel2);
  color: var(--muted); font-size: 13px; font-weight: 700;
  cursor: pointer; transition: var(--dur-1); position: relative; white-space: nowrap;
}
.brief-type-chip:hover { border-color: rgb(var(--primary-rgb) / .4); color: var(--text); }
.brief-type-chip.active {
  background: rgb(var(--primary-rgb) / .14); border-color: var(--primary);
  color: var(--primary-on-tint);
}
.brief-type-ic { font-size: 15px; line-height: 1; }
.brief-type-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--primary2); flex-shrink: 0;
}
.brief-type-panel {
  margin-top: 16px; padding: 16px; border-radius: var(--r-xl);
  background: var(--panel2); border: 1px solid var(--line);
}
.brief-type-panel-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
}
.brief-type-panel-title { font-size: 16px; font-weight: 800; }

/* ── Редактор вопросов брифа (модалка) ──────────────── */
.bfe-list { display: flex; flex-direction: column; gap: 10px; }
.bfe-field {
  border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: 12px; background: var(--panel2);
  display: flex; flex-direction: column; gap: 9px;
}
.bfe-field.bfe-section { background: rgb(var(--primary-rgb) / .06); border-color: rgb(var(--primary-rgb) / .22); }
.bfe-field.bfe-off { opacity: .55; }
.bfe-row1 { display: flex; align-items: center; gap: 8px; }
.bfe-row2 { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.bfe-move { display: flex; flex-direction: column; gap: 2px; flex-shrink: 0; }
.bfe-mini {
  width: 22px; height: 17px; padding: 0; line-height: 1;
  border: 1px solid var(--line); background: var(--panel);
  color: var(--muted); border-radius: var(--r-xs); cursor: pointer; font-size: 9px;
}
.bfe-mini:hover:not(:disabled) { color: var(--primary-text); border-color: var(--primary); }
.bfe-mini:disabled { opacity: .35; cursor: default; }
.bfe-label { flex: 1; min-width: 0; margin: 0; }
.bfe-label, .bfe-type, .bfe-options, .bfe-ph { font-size: 13px; }
.bfe-type { width: auto; padding: 7px 30px 7px 10px; }
.bfe-options, .bfe-ph { width: 100%; }
.bfe-chk {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; color: var(--muted); cursor: pointer; white-space: nowrap;
}
.bfe-chk input { width: auto; }
.bfe-tag {
  font-size: 11px; font-weight: 700; color: var(--muted);
  padding: 3px 8px; border-radius: var(--r-sm); background: var(--panel);
  border: 1px solid var(--line); flex-shrink: 0; white-space: nowrap;
}
.bfe-del {
  width: 30px; height: 30px; flex-shrink: 0; display: grid; place-items: center;
  border: 1px solid var(--line); background: var(--panel); color: var(--muted);
  border-radius: var(--r-sm); cursor: pointer; transition: var(--dur-1);
}
.bfe-del:hover { color: var(--text-danger); border-color: rgba(220,38,38,.4); background: rgba(220,38,38,.08); }
/* Тумблер «показывать поле» */
.bfe-switch { position: relative; display: inline-flex; flex-shrink: 0; width: 34px; height: 20px; cursor: pointer; }
.bfe-switch input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.bfe-switch span {
  position: absolute; inset: 0; border-radius: var(--r-pill);
  background: var(--line); transition: background var(--dur-1);
}
.bfe-switch span::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--panel); transition: transform var(--dur-1);
}
.bfe-switch input:checked + span { background: var(--primary); }
.bfe-switch input:checked + span::after { transform: translateX(14px); }

@media (max-width: 640px) {
  .brief-card-item .brief-fields { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {
  .brief-row { grid-template-columns: 1fr; }
  .brief-card { padding: 24px 18px; border-radius: var(--r-xl); }
  .brief-wrap { padding: 24px 12px 48px; }
}

/* ═══════════════════════════════════════════════════════
   AI SPINNER
═══════════════════════════════════════════════════════ */
.ai-spinner {
  display: inline-block;
  width: 14px; height: 14px;
  border: 2px solid rgba(255,255,255,.3);
  border-top-color: var(--on-color);
  border-radius: 50%;
  animation: ai-spin .65s linear infinite;
  vertical-align: middle;
  flex-shrink: 0;
}
@keyframes ai-spin { to { transform: rotate(360deg); } }

/* ═══════════════════════════════════════════════════════
   КЛИЕНТСКИЙ ПОРТАЛ
═══════════════════════════════════════════════════════ */
/* Нижняя мобильная навигация — статический <nav class="mobile-bottom-nav"> из
   index.html, а не .nav, поэтому под правило ниже она не попадала и рисовалась
   поверх клиентского КП: заказчик на телефоне видел «Проекты · Смета · + ·
   Финансы · Ещё» чужого приложения, а панель ещё и перекрывала текст и давала
   горизонтальную прокрутку (442px при окне 390). brief-mode и calc-mode её
   прячут явно (см. ниже), portal-mode — просто забыли. */
body.portal-mode .topbar,
body.portal-mode .mobile-bottom-nav,
body.portal-mode .nav { display: none !important; }

.portal-wrap {
  min-height: 100vh;
  display: flex; flex-direction: column; align-items: center;
  justify-content: flex-start;
  padding: 40px 16px 60px;
  background: var(--bg);
  background-image:
    radial-gradient(circle at top left, rgb(var(--primary-rgb) / .14), transparent 40%),
    radial-gradient(circle at bottom right, rgba(37,99,235,.1), transparent 40%);
}
.portal-inner { width: 100%; max-width: 580px; }
.portal-header {
  display: flex; align-items: center; gap: 14px; margin-bottom: 28px;
}
/* Логотип агентства в шапке КП. Раньше здесь стоял логотип сервиса — заказчик
   чужой студии видел бренд конкурента (см. _portalBrandHtml в app.js). Логотипа у
   агентства может не быть, тогда показываем первую букву названия. */
.portal-brand-logo {
  width: 46px; height: 46px; border-radius: var(--r-lg); flex-shrink: 0;
  display: grid; place-items: center; overflow: hidden;
  background: var(--panel2); border: 1px solid var(--line);
}
.portal-brand-logo img { width: 100%; height: 100%; object-fit: contain; padding: 5px; }
.portal-brand-logo.letter {
  background: var(--primary); border-color: transparent;
  color: var(--on-color); font-size: 20px; font-weight: 900; line-height: 1;
}
.portal-card {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-2xl); padding: 32px 28px; box-shadow: var(--shadow);
}
.portal-status-badge {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 13px; border-radius: var(--r-pill); font-size: 12px; font-weight: 700;
  background: rgba(202,138,4,.1); border: 1px solid rgba(202,138,4,.3);
  color: var(--yellow); margin-bottom: 18px;
}
.portal-status-badge.approved {
  background: rgba(22,163,74,.1); border-color: rgba(22,163,74,.3); color: var(--text-success);
}
.portal-title {
  font-size: 26px; font-weight: 900; letter-spacing: -.035em;
  margin: 0 0 22px; line-height: 1.15;
}
.portal-price-block {
  background: rgb(var(--primary-rgb) / .07); border: 1px solid rgb(var(--primary-rgb) / .18);
  border-radius: var(--r-xl); padding: 16px 20px; margin-bottom: 24px;
  display: flex; justify-content: space-between; align-items: center;
  flex-wrap: wrap; gap: 8px;
}
.portal-price-label { font-size: 13px; color: var(--muted); }
.portal-price-amount { font-size: 26px; font-weight: 900; color: var(--primary-text); }
.portal-section { margin-bottom: 20px; }
.portal-section h3 {
  font-size: 12px; font-weight: 800; color: var(--muted);
  text-transform: uppercase; letter-spacing: .06em; margin: 0 0 8px;
}
.portal-section p { font-size: 14px; line-height: 1.7; color: var(--text); margin: 0; }
.portal-note {
  background: var(--panel2); border-radius: var(--r-lg); padding: 16px 18px;
  border: 1px solid var(--line);
}
.portal-services-list {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 6px;
}
.portal-services-list li {
  font-size: 13px; padding: 6px 10px; border-radius: var(--r-sm);
  background: var(--panel2); border: 1px solid var(--line);
}
.portal-services-list li::before { content: "✓ "; color: var(--text-success); font-weight: 700; }
@media (max-width: 640px) {
  .portal-card { padding: 22px 18px; border-radius: var(--r-xl); }
  .portal-title { font-size: 20px; }
  .portal-price-amount { font-size: 22px; }
}

/* ═══════════════════════════════════════════════════════
   DRAG-AND-DROP КАНБАН
═══════════════════════════════════════════════════════ */
.kanban-col.dragover {
  border-color: var(--primary-on-tint) !important;
  background: rgb(var(--primary-rgb) / .1) !important;
  box-shadow: 0 0 0 2px rgb(var(--primary-rgb) / .2) inset;
  transition: background var(--dur-1), border-color var(--dur-1);
}
.kanban-drop-hint { opacity: .5; font-style: italic; }
.crm-card[draggable="true"],
.task-card[draggable="true"] {
  cursor: grab;
  -webkit-user-select: none; user-select: none;
}
.crm-card[draggable="true"]:active,
.task-card[draggable="true"]:active { cursor: grabbing; }
.crm-card[draggable="true"]:hover,
.task-card[draggable="true"]:hover {
  border-color: rgb(var(--primary-rgb) / .45);
  transform: translateY(-1px);
  box-shadow: 0 6px 18px rgba(0,0,0,.18);
  transition: transform var(--dur-1), box-shadow var(--dur-1), border-color var(--dur-1);
}

/* ═══════════════════════════════════════════════════════
   SWIPE-TO-DELETE
═══════════════════════════════════════════════════════ */
.swipe-wrap {
  position: relative;
  overflow: hidden;
  border-radius: var(--r-xl);
}
.swipe-delete-bg {
  position: absolute;
  right: 0; top: 0; bottom: 0; width: 72px;
  background: var(--red);
  display: flex; align-items: center; justify-content: center;
  font-size: 22px; border-radius: var(--r-xl);
  pointer-events: none;
}
.swipe-wrap .task-card {
  position: relative; z-index: 1;
  background: var(--panel);
}
@media (min-width: 769px) {
  .swipe-delete-bg { display: none; }
}

/* ═══════════════════════════════════════════════════════
   OAUTH / СОЦИАЛЬНЫЙ ВХОД
═══════════════════════════════════════════════════════ */
.oauth-divider {
  display: flex; align-items: center; gap: 10px;
  margin: 18px 0 14px;
}
.oauth-divider::before,
.oauth-divider::after {
  content: ''; flex: 1; border-top: 1px solid var(--line);
}
.oauth-divider span { font-size: 12px; color: var(--muted); white-space: nowrap; }
.oauth-buttons {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px;
}
.oauth-btn {
  display: flex; align-items: center; justify-content: center;
  flex-direction: column; gap: 5px;
  padding: 10px 6px 8px;
  border-radius: var(--r-lg); border: 1px solid var(--line);
  background: var(--panel2); color: var(--text);
  font-size: 12px; font-weight: 700; cursor: pointer;
  transition: var(--dur-1); white-space: nowrap; overflow: hidden;
}
.oauth-btn:hover {
  border-color: rgb(var(--primary-rgb) / .4);
  background: rgb(var(--primary-rgb) / .07);
  transform: translateY(-1px);
}
.oauth-btn svg { flex-shrink: 0; }
[data-theme="light"] .oauth-btn { background: rgba(255,255,255,.9); }
@media (max-width: 380px) {
  .oauth-buttons { grid-template-columns: repeat(2, 1fr); }
  .oauth-btn span { display: none; }
  .oauth-btn { flex-direction: row; padding: 10px; }
}
.oauth-btn.oauth-soon {
  opacity: .38; cursor: not-allowed; position: relative;
}
.oauth-btn.oauth-soon::after {
  content: 'скоро';
  position: absolute; top: 3px; right: 4px;
  font-size: 12px; font-weight: 800; color: var(--muted);
  text-transform: uppercase; letter-spacing: .03em;
}
.oauth-btn.oauth-active:hover { transform: translateY(-2px); box-shadow: 0 4px 14px rgba(0,0,0,.15); }
.oauth-btn.oauth-google-full {
  flex-direction: row; padding: 11px 16px; font-size: 13px; font-weight: 700;
  justify-content: center; gap: 10px;
}
.oauth-btn.oauth-vk {
  background: var(--vk-blue); border-color: var(--vk-blue); color: var(--on-color);
}
.oauth-btn.oauth-vk:hover { background: var(--vk-blue-hover); border-color: var(--vk-blue-hover); }

/* ── Toast close button ─────────────────────────────── */
.toast { display: flex; align-items: center; gap: 2px; }
.toast-msg { flex: 1; }
.toast-close {
  background: none; border: none; color: inherit;
  font-size: 18px; cursor: pointer; padding: 0 0 0 8px;
  line-height: 1; opacity: .65; flex-shrink: 0;
  pointer-events: auto;
}
.toast-close:hover { opacity: 1; }
.toast.show { pointer-events: auto; }
.toast-undo {
  background: none; border: none; color: var(--primary);
  font-size: 13px; font-weight: 700; cursor: pointer; padding: 0 8px;
  flex-shrink: 0; pointer-events: auto; white-space: nowrap;
}
.toast-undo:hover { text-decoration: underline; }

/* ── Появление раздела (render → page-enter) ──────────────────────────
   Блоки страницы поднимаются волной (шаг 35 мс), карточки сеток проявляются
   следом — только прозрачностью, без сдвига, чтобы не складывать движение с
   родителем. Вход (.16,1,.3,1), всё укладывается в ~0,6 с. */
@keyframes pageRise { from { opacity: 0; transform: translateY(8px); } }
@keyframes pageFade { from { opacity: 0; } }
#appContent.page-enter > .panel > *, #appContent.page-enter > :not(.panel) { animation: pageRise .38s cubic-bezier(.16, 1, .3, 1) both; }
#appContent.page-enter > .panel > :nth-child(1), #appContent.page-enter > :not(.panel):nth-child(1) { animation-delay: 0ms; }
#appContent.page-enter > .panel > :nth-child(2), #appContent.page-enter > :not(.panel):nth-child(2) { animation-delay: 35ms; }
#appContent.page-enter > .panel > :nth-child(3), #appContent.page-enter > :not(.panel):nth-child(3) { animation-delay: 70ms; }
#appContent.page-enter > .panel > :nth-child(4), #appContent.page-enter > :not(.panel):nth-child(4) { animation-delay: 105ms; }
#appContent.page-enter > .panel > :nth-child(5), #appContent.page-enter > :not(.panel):nth-child(5) { animation-delay: 140ms; }
#appContent.page-enter > .panel > :nth-child(6), #appContent.page-enter > :not(.panel):nth-child(6) { animation-delay: 175ms; }
#appContent.page-enter > .panel > :nth-child(7), #appContent.page-enter > :not(.panel):nth-child(7) { animation-delay: 210ms; }
#appContent.page-enter :is(.catalog-grid, .grid) > * { animation: pageFade .32s ease both; }
#appContent.page-enter :is(.catalog-grid, .grid) > :nth-child(1) { animation-delay: 160ms; }
#appContent.page-enter :is(.catalog-grid, .grid) > :nth-child(2) { animation-delay: 190ms; }
#appContent.page-enter :is(.catalog-grid, .grid) > :nth-child(3) { animation-delay: 220ms; }
#appContent.page-enter :is(.catalog-grid, .grid) > :nth-child(4) { animation-delay: 250ms; }
#appContent.page-enter :is(.catalog-grid, .grid) > :nth-child(5) { animation-delay: 280ms; }
#appContent.page-enter :is(.catalog-grid, .grid) > :nth-child(6) { animation-delay: 310ms; }
#appContent.page-enter :is(.catalog-grid, .grid) > :nth-child(7) { animation-delay: 340ms; }
#appContent.page-enter :is(.catalog-grid, .grid) > :nth-child(8) { animation-delay: 370ms; }
#appContent.page-enter :is(.catalog-grid, .grid) > :nth-child(9) { animation-delay: 400ms; }
#appContent.page-enter :is(.catalog-grid, .grid) > :nth-child(10) { animation-delay: 430ms; }
#appContent.page-enter :is(.catalog-grid, .grid) > :nth-child(11) { animation-delay: 460ms; }
#appContent.page-enter :is(.catalog-grid, .grid) > :nth-child(12) { animation-delay: 490ms; }
/* Уменьшенное движение: класс page-enter render() не ставит вовсе. */

/* ═══ Движение на главной ═══════════════════════════════════════════════
   Вход — ОДИН раз на заход: класс home-enter ставит render() только при смене
   вида, снимает таймер (app.js, _homeEnterMotion). Любая перерисовка во время
   входа класс снимает — новые узлы встают на место без повторного каскада.

   Порядок задаёт --enter-i (ставит JS по структуре страницы) — и он
   НАСЛЕДУЕТСЯ: линия спарклайна, дуга полукруга, сегменты кольца и столбцы
   графика берут задержку своей плитки или панели и стартуют после неё.

   Только transform / opacity / stroke-*: это не трогает раскладку. Размытие —
   лишь у плиток KPI (их десять), у карточек его нет: на сорока узлах оно
   дорогое. При «уменьшить движение» JS каскад не запускает вовсе, а
   глобальное правило выше гасит остальное. */
/* У появлений НЕТ конечного кадра — намеренно. Конец берётся из собственного
   стиля элемента: пустой этап воронки приглушён до .5, подпись расхода над
   столбцом — до .85. С «to { opacity: 1 }» они на время входа вспыхивали до
   полной яркости и после снятия класса резко падали обратно — скачок. */
@keyframes homeRise     { from { opacity: 0; transform: translateY(14px) scale(.985); } }
@keyframes homeRiseSoft { from { opacity: 0; transform: translateY(10px); filter: blur(6px); } }
@keyframes homeFade     { from { opacity: 0; } }
@keyframes sparkDraw { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
@keyframes gaugeDraw { from { stroke-dashoffset: var(--dash); } to { stroke-dashoffset: 0; } }
@keyframes dotPop {
  0%   { opacity: 0; transform: scale(0); }
  65%  { opacity: 1; transform: scale(1.6); }
  100% { opacity: 1; transform: scale(1); }
}
/* Сегмент кольца растёт от нуля со своей точки. Скруглённый кончик нулевой
   длины рисуется точкой — поэтому на старте сегмент ещё и прозрачен. */
@keyframes donutGrow {
  0%   { stroke-dasharray: 0 var(--dl); opacity: 0; }
  12%  { opacity: 1; }
}
@keyframes barGrow  { from { transform: scaleY(0); } to { transform: scaleY(1); } }
@keyframes barFillX { from { transform: scaleX(0); } to { transform: scaleX(1); } }

#appContent.home-enter [data-enter] {
  animation: homeRise .64s var(--ease-out) both;
  animation-delay: calc(var(--enter-i, 0) * 45ms);
}
#appContent.home-enter .db-stat[data-enter] { animation-name: homeRiseSoft; }

#appContent.home-enter .spark-line {
  stroke-dasharray: 1;
  animation: sparkDraw 1s var(--ease-out) both;
  animation-delay: calc(var(--enter-i, 0) * 45ms + 220ms);
}
#appContent.home-enter .spark-area {
  animation: homeFade .8s ease both;
  animation-delay: calc(var(--enter-i, 0) * 45ms + 560ms);
}
#appContent.home-enter .spark-dot {
  transform-box: fill-box; transform-origin: center;
  animation: dotPop .5s var(--ease-spring) both;
  animation-delay: calc(var(--enter-i, 0) * 45ms + 1050ms);
}
#appContent.home-enter .spark-gauge-fill {
  animation: gaugeDraw 1.15s var(--ease-out) both;
  animation-delay: calc(var(--enter-i, 0) * 45ms + 240ms);
}
#appContent.home-enter .donut-seg {
  animation: donutGrow 1.05s var(--ease-out) both;
  animation-delay: calc(var(--enter-i, 0) * 45ms + 200ms + var(--i, 0) * 90ms);
}
#appContent.home-enter .db-bar {
  transform-box: fill-box; transform-origin: 50% 100%;
  animation: barGrow .8s var(--ease-out) both;
  animation-delay: calc(var(--enter-i, 0) * 45ms + 200ms + var(--i, 0) * 55ms);
}
#appContent.home-enter .deal-card-progress-fill,
#appContent.home-enter .onb-progress-fill,
#appContent.home-enter .db-money-margin-fill {
  transform-origin: left center;
  animation: barFillX .9s var(--ease-out) both;
  animation-delay: calc(var(--enter-i, 0) * 45ms + 260ms);
}
/* Подписи сумм над столбцами — после того, как столбцы выросли. Без этого
   «221к» висело в воздухе над пустой осью первые полсекунды. Подписи месяцев
   (.db-chart-mlabel) — ось, они на месте сразу. */
#appContent.home-enter .db-chart-col text:not(.db-chart-mlabel) {
  animation: homeFade .45s ease both;
  animation-delay: calc(var(--enter-i, 0) * 45ms + 900ms);
}

/* Счётчик в плитке. Настоящее число остаётся в тексте (его читают скринридер,
   копирование и тесты) и на время анимации лишь прозрачно, держа ширину.
   Набегающее значение рисует ::after из data-count-show. text-fill-color, а не
   color: у значений цвет задан инлайном (доход зелёный, долг оранжевый), и
   перебить его без !important нельзя — а заливку текста можно. */
.is-counting { position: relative; -webkit-text-fill-color: transparent; }
.is-counting::after {
  content: attr(data-count-show);
  content: attr(data-count-show) / "";
  position: absolute; left: 0; top: 0;
  -webkit-text-fill-color: currentColor;
  white-space: nowrap; pointer-events: none;
}

/* Свечение за курсором — приём из референсов владельца («градиенты и
   свечение»). Координаты кладёт один делегированный обработчик в app.js
   (_initSpotlight). Только при настоящем курсоре: на сенсорном экране
   свечению не за чем следовать.

   ДВА слоя, и оба не ложатся на содержимое:
   · заливка — ПОД текстом и графикой. Первая версия клала её слоем поверх
     (::after), и пятно подкрашивало фиолетовым суммы, подписи и линии —
     владелец 28.09: «не должно перекрывать весь текст и графику».
   · кромка — светится только рамка в 1px у курсора (маска вырезает всё,
     кроме полосы по краю), поэтому текста она не касается физически.

   Как заливка уходит под содержимое — по-разному, и это не прихоть:
   · у плиток — свой слой наложения (isolation) и ::before с z-index:-1.
     Всплывающего внутри плиток нет, изолировать их безопасно;
   · у панелей и карточек — слоем самого ФОНА. Изолировать их нельзя: у
     панели графика внутри подсказка с position:fixed, у карточки — меню,
     которое выпадает за её край. В изолированном блоке и то и другое
     оказалось бы под соседними панелями и карточками. */
@property --spot-a {
  syntax: "<number>";
  inherits: false;
  initial-value: 0;
}
@media (hover: hover) and (pointer: fine) {
  /* Плитки: заливка слоем под содержимым. */
  .db-stat { position: relative; isolation: isolate; }
  .db-stat::before {
    content: ""; position: absolute; inset: 0; z-index: -1;
    pointer-events: none; opacity: 0;
    background: radial-gradient(220px circle at var(--mx, 50%) var(--my, 50%),
      rgb(var(--primary-rgb) / .16), transparent 65%);
    transition: opacity var(--dur-3) var(--ease-out);
  }
  .db-stat:hover::before { opacity: 1; }

  /* Панели и карточки: заливка — слоем фона. Прозрачность пятна — через
     --spot-a (зарегистрирована выше), иначе градиент в фоне появлялся бы
     рывком: сам background плавно не переходит. */
  .db-analytics-row > .panel {
    position: relative;
    background:
      radial-gradient(420px circle at var(--mx, 50%) var(--my, 50%),
        rgb(var(--primary-rgb) / calc(var(--spot-a) * .09)), transparent 62%),
      var(--panel);
    transition: --spot-a var(--dur-3) var(--ease-out), border-color var(--dur-2) var(--ease-out);
  }
  .db-analytics-row > .panel:hover { --spot-a: 1; }
  /* Полоса статуса повторена из базового правила .deal-card: фон — один
     shorthand, и слой пятна встаёт между полосой и заливкой карточки. */
  .deal-card {
    background:
      linear-gradient(to right, var(--status-color, var(--line)) 0 4px, transparent 4px 100%),
      radial-gradient(300px circle at var(--mx, 50%) var(--my, 50%),
        rgb(var(--primary-rgb) / calc(var(--spot-a) * .11)), transparent 62%),
      var(--card-fill);
    transition: --spot-a var(--dur-3) var(--ease-out),
      border-color var(--dur-1), transform var(--dur-2) var(--ease-out), box-shadow var(--dur-2);
  }
  .deal-card:hover { --spot-a: 1; }

  /* Кромка у курсора. Маска оставляет от слоя только полосу толщиной в
     padding (1px) — светится рамка, а не площадь. */
  .db-stat::after,
  .deal-card::after,
  .db-analytics-row > .panel::after {
    content: ""; position: absolute; inset: 0;
    padding: 1px; pointer-events: none; opacity: 0;
    background: radial-gradient(180px circle at var(--mx, 50%) var(--my, 50%),
      rgb(var(--primary-rgb) / .85), transparent 70%);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
    transition: opacity var(--dur-3) var(--ease-out);
  }
  .db-stat:hover::after,
  .deal-card:hover::after,
  .db-analytics-row > .panel:hover::after { opacity: 1; }
  /* Скругление кромки — токеном хозяина (у плитки ленты его нет вовсе). */
  .deal-card::after,
  .db-analytics-row > .panel::after { border-radius: var(--r-xl); }
  /* У панели кромка — поверх её собственной рамки (inset:-1px), а не внутри:
     иначе светящаяся линия шла бы в пикселе от серой, двумя параллельными. */
  .db-analytics-row > .panel::after,
  .deal-card::after { inset: -1px; }

  /* ── Отклик на наведение ─────────────────────────────────────────────
     Малое действие — малый отклик (microinteractions): ничего не прыгает
     на полэкрана, живёт то, на что смотрят. */
  /* Значок плитки чуть приподнимается — подтверждение «это нажимается». */
  .db-stat-icon { transition: transform var(--dur-2) var(--ease-spring); }
  .db-stat[onclick]:hover .db-stat-icon { transform: translateY(-1px) scale(1.1); }
  /* Спарклайн: линия уверенней, точка на конце — пульсирует кольцом. */
  .db-stat .spark-line { transition: stroke-width var(--dur-2) var(--ease-out); }
  .db-stat:hover .spark-line { stroke-width: 2.2; }
  .db-stat:hover .spark-dot {
    transform-box: fill-box; transform-origin: center;
    animation: sparkPulse 1.4s var(--ease-out) infinite;
  }
  /* Полукруг «Собрано»: дуга чуть толще — читается, что это шкала. */
  .spark-gauge-fill { transition: stroke-width var(--dur-2) var(--ease-out); }
  .db-stat:hover .spark-gauge-fill { stroke-width: 6.5; }
  /* Кольцо «Топ клиентов»: под курсором панель слегка поворачивает кольцо —
     ровно настолько, чтобы было видно: форма живая. */
  .donut { transition: transform .6s var(--ease-out); }
  .db-analytics-row > .panel:hover .donut { transform: rotate(6deg) scale(1.02); }
  /* Карточка сделки: полоса оплаты светлеет — взгляд идёт к тому, сколько
     осталось получить. */
  .deal-card-progress-fill { transition: filter var(--dur-2) var(--ease-out); }
  .deal-card:hover .deal-card-progress-fill { filter: brightness(1.25) saturate(1.1); }
}
@keyframes sparkPulse {
  0%   { transform: scale(1);   opacity: 1; }
  50%  { transform: scale(1.7); opacity: .55; }
  100% { transform: scale(1);   opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .db-stat::before, .db-stat::after,
  .deal-card::after, .db-analytics-row > .panel::after { display: none; }
  .db-analytics-row > .panel:hover, .deal-card:hover { --spot-a: 0; }
  .db-stat:hover .spark-dot { animation: none; }
  .db-analytics-row > .panel:hover .donut,
  .db-stat[onclick]:hover .db-stat-icon { transform: none; }
}

/* ── Offline banner ─────────────────────────────────── */
.offline-banner {
  display: none;
  position: fixed; top: 0; left: 0; right: 0; z-index: var(--z-auth);
  background: var(--red); color: var(--on-color);
  padding: 10px 16px;
  align-items: center; justify-content: center; gap: 8px;
  font-size: 14px; font-weight: 600;
}
.offline-banner svg { vertical-align: -2px; margin-right: 4px; }

/* «Вышла новая версия». Не красная, как «нет сети»: это новость, а не беда.
   На широком экране — снизу по центру (правый нижний угол занимает тост), на
   телефоне — сверху, сразу ПОД верхней панелью (63px, липкая): снизу там тост
   во всю ширину над нижним меню, а поверх панели плашка закрывала поиск.
   Поправки на вырез экрана нет намеренно — у самой панели её тоже нет. */
.update-banner {
  position: fixed; left: 50%; bottom: 18px; transform: translateX(-50%);
  z-index: var(--z-toast);
  display: flex; align-items: center; gap: 10px;
  padding: 6px 6px 6px 16px;
  background: var(--panel); color: var(--text);
  border: 1px solid var(--line); border-radius: var(--r-pill);
  box-shadow: var(--elev-3);
  font-size: 13px; font-weight: 600; white-space: nowrap;
  max-width: calc(100vw - 32px);
}
/* display:flex выше перебил бы атрибут hidden — возвращаем ему силу. */
.update-banner[hidden] { display: none; }
@media (max-width: 768px) {
  .update-banner { top: 72px; bottom: auto; }
}

/* ── Mobile UX — touch targets & table overflow ─────── */
@media (max-width: 640px) {
  .btn { min-height: 44px; }
  /* 44px и у мелких кнопок. Раньше тут стояло 36px — ровно те кнопки, на которые
     жаловался владелец («Выгрузить», «+ Услуги», «Свернуть всё», «Обновить»),
     и мимо них проще промахнуться, чем попасть. Замер до правки: 32–36px по всем
     разделам. Слово small теперь про плотность текста, а не про площадь касания. */
  .btn.small { min-height: 44px; }
  button.deal-tab { min-height: 40px; }

  /* Вкладки сделки: лента → кнопка + лист. Замер на 390px до правки: видно 3
     вкладки из 10, 573px содержимого за краем, и единственным намёком служила
     маска-градиент. «Договор» и «История» находились только слепым свайпом. */
  .deal-tabs { display: none; }
  .deal-tabs-trigger {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    flex: 1 1 auto; min-width: 0;
    min-height: 44px; margin: 2px 0 6px; padding: 10px 14px;
    font: inherit; font-size: 15px; font-weight: 800; text-align: left;
    color: var(--text); background: var(--panel2);
    border: 1px solid var(--line); border-radius: var(--r-md);
    cursor: pointer;
  }
  /* min-width: 0 на самой подписи — иначе длинная («Версии сметы») распирает
     кнопку и выталкивает «⋮» за край строки. */
  .deal-tabs-trigger-label {
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .deal-tabs-trigger svg { flex: 0 0 auto; color: var(--muted); }
  /* Ряд теперь «кнопка + ⋮»: отрицательные поля были нужны ленте, чтобы уходить
     под края шапки, а кнопке они ломают выравнивание с остальным содержимым. */
  .deal-tabs-row { margin-left: 0; margin-right: 0; padding-right: 0; align-items: center; }

  /* Кнопки панели раздела ДЕЛЯТ ряд поровну вместо того, чтобы жаться влево.
     Замер: в «Смете» четыре кнопки вставали в два ряда и оставляли 270px пустоты
     справа, в «Каталоге» — 242px. Это и есть «свободные места справа», из-за
     которых панель выглядит неубранной. flex-basis 0 заставляет ряд заполняться
     целиком, min-width держит подписи от схлопывания в столбик по буквам. */
  /* Селектор с .small ОБЯЗАТЕЛЕН. Раньше здесь стояло `.toolbar > .btn` с
     min-width: 128px, и правило молча проигрывало `.btn.small { min-width: 44px }`
     ниже по файлу: специфичность одинаковая, побеждает то, что позже. В итоге
     flex: 1 1 0 сжимал кнопки до 80px, а подпись «Свернуть всё» требует 92 — и
     ВЫЛЕЗАЛА ЗА ГРАНИЦЫ СВОЕЙ КНОПКИ, налезая на соседнюю («Свернуть всё+ Услуги»
     одной строкой). Замер на 390px: перелив 14px.

     min-width: max-content вместо числа: подпись не вылезет ни при какой длине,
     а ряд по-прежнему заполняется целиком и переносится, когда не влезает. */
  .toolbar > .btn,
  .toolbar > .btn.small {
    flex: 1 1 0;
    min-width: max-content;
    justify-content: center;
  }
  .data-table { display: block; overflow-x: auto; }
  /* .grid.three/.four наследуют grid-template-columns:1fr из более широкого брейкпоинта
     (≤1160px, см. выше по файлу) — раньше здесь стояло принудительное «1fr 1fr» специально
     для телефона, из-за чего и поля формы (Настройки → Компания), и текстовые карточки
     (пакеты/проекты/договора/команда) сжимались в тесные узкие колонки. Одна колонка —
     единый вид везде на телефоне, никаких спец-классов не нужно.
     ИСКЛЮЧЕНИЕ: .deal-cards-grid — у неё свой базовый (без media) grid-template-columns:
     repeat(auto-fill, minmax(320px,1fr)) с более высокой специфичностью (3 класса), который
     побеждает общее правило на ЛЮБой ширине. На ≤320px даже одна колонка minmax(320px)
     не помещается — раздвигает страницу. Принудительно 1fr именно здесь. */
  .grid.three.deal-cards-grid { grid-template-columns: 1fr; }
  /* Раздел «Задачи» на телефоне: селектор проекта на всю ширину под чипами */
  .gtask-project-select { margin-left: 0; width: 100%; }
  .gtask-filters { gap: 8px; }
  /* Переключатель «список / доска» — в строку к поиску. Отдельной строкой он
     занимал ~50px с пустотой слева (обход 29.09.2026): поиск на всю ширину,
     под ним проект и «Своя задача», и уже третьей строкой — две кнопки. */
  /* Основа поиска — вся строка минус переключатель: тогда проект и «Своя
     задача» гарантированно уходят на вторую строку, а не сжимают поиск. */
  .gtask-search { order: 0; flex: 1 1 calc(100% - 120px); min-width: 0; max-width: none; }
  .gtask-filters .deal-view-toggle { order: 1; margin-left: 0; }
  /* Основа 60%, не 0: с нулевой основой селект втискивался в первую строку
     остатком в 17px и вылезал содержимым за правый край. */
  .gtask-filters .uu-select-wrap { order: 2; flex: 1 1 60%; min-width: 0; }
  .gtask-add { order: 3; }
  /* Много пилюль-фильтров (.tabs, .gtask-chips, «Период:» в Финансах) — на телефоне не
     перенос в 2-3 тесных ряда, а горизонтальный скролл в одну строку (тот же приём,
     что у .catalog-cat-sidebar), с fade-подсказкой по краям. Скроллбар НЕ прячем —
     виден стандартный тонкий scrollbar (глобальный ::-webkit-scrollbar ниже по файлу),
     как у .catalog-cat-sidebar — это и есть подсказка «тут можно скроллить». */
  .tabs, .gtask-chips, .date-preset-row {
    flex-wrap: nowrap;
    overflow-x: auto;
    padding-bottom: 4px;
    -webkit-mask-image: linear-gradient(to right, transparent 0, black 14px, black calc(100% - 14px), transparent 100%);
    mask-image: linear-gradient(to right, transparent 0, black 14px, black calc(100% - 14px), transparent 100%);
  }
  .tabs .tab, .gtask-chips .chip, .date-preset-row .date-preset-btn { flex: 0 0 auto; }
  .date-preset-row { flex: 1 1 100%; gap: 8px; }
  /* На тач-экране нет hover — кнопка меню сделки (⋮, единственный доступ к
     Редактировать/Дублировать/Удалить) должна быть видна всегда, а не по наведению. */
  .deal-menu-btn { opacity: 1; }
  /* Тот же случай для кнопки действия («Сдать проект →») в карточке сделки. */
  .deal-card .next-action-btn { opacity: 1; }
  /* А вот «+» в дне календаря на телефоне не показываем ВОВСЕ. Раньше он
     появлялся в выбранном дне, но невидимым висел во всех сорока двух — с
     областью касания 44×44 поверх ячейки 46×56. Из-за этого тап по дню не
     выбирал день, а заводил задачу в открытой сделке и уходил с календаря.
     Первое действие в разделе оказывалось ловушкой.
     На тач-экране день открывается тапом, а «+ Задача» стоит ПОДПИСАННОЙ
     кнопкой в панели дня — там видно и что она делает, и на какое число. */
  .cal-day-add { display: none; }
  .deal-bar .deal-compact-row { flex-wrap: wrap; gap: 8px; }
  .deal-stats-inline { display: none; }
  /* На телефоне колонок две, а не тридцать, поэтому явные span'ы десктопной
     раскладки 3+2 надо снять: span 10 в двухколоночной сетке создаёт недостающие
     колонки неявно и выносит плитку за край панели. Правило про одинокую
     последнюю плитку (@media max-width:640px) специфичнее и переживает этот
     сброс — оно и растягивает её на весь ряд. */
  .fin-summary-grid { grid-template-columns: 1fr 1fr; }
  .fin-summary-grid > *,
  .fin-summary-grid > :nth-child(4),
  .fin-summary-grid > :nth-child(5) { grid-column: auto; }
  /* Доска воронки на телефоне — вертикальный столбик этапов.
     Раньше здесь стояло `flex-direction: column`, но .kanban это ГРИД, и
     свойство не делало ничего; а колонки задавались инлайновым стилем из
     app.js, который перебивал любые правила. Итог: на экране 390px доска
     оставалась шириной 2326px — видно полторы колонки из десяти, а до
     «Завершённых» приходилось листать вбок шесть экранов.

     Горизонтальную прокрутку и snap здесь выключаем: со схлопнутой в один
     столбец сеткой они лишние и мешают вертикальному скроллу страницы. */
  .kanban,
  .kanban-scroll-x {
    grid-template-columns: 1fr;
    overflow-x: visible;
    scroll-snap-type: none;
    padding-bottom: 0;
  }
  .kanban-scroll-x > .kanban-col { scroll-snap-align: none; }

  /* min-height: 220px нужен на ДЕСКТОПЕ, где колонки стоят в ряд и обязаны быть
     одной высоты. В мобильном столбике он превращается в пустое место: замер на
     реальных данных (3 сделки на 2 этапах из 10) — восемь пустых этапов по 220px
     съедали 64% высоты доски, и она вытягивалась на 3.2 экрана прокрутки.

     Пустой этап сворачивается в строку заголовка: название и счётчик «0» видно,
     воронка целиком читается, но места он почти не занимает. Заглушка «Пусто»
     там не нужна — счётчик уже сказал то же самое. */
  .kanban-col { min-width: unset; width: 100%; min-height: 0; }
  .kanban-col-status.is-empty { padding: 10px 14px; }
  .kanban-col-status.is-empty > h3 { padding-bottom: 0; border-bottom: none; }
  .kanban-col-status.is-empty .kanban-drop-hint { display: none; }
  /* Правило про две колонки УБРАНО: сетка базово на 10 дорожек, и дети уже
     занимают по пять — это и есть две плитки в ряду. А переопределение сетки на
     две дорожки ломало счёт: span 5 в сетке из двух упирался в край, и плитка
     занимала ВСЮ ширину. Замер на 390px: 10 плиток в 10 рядов, полоса 882px —
     выше самого экрана. Моя же недоделка из правки «5+5»: базовую сетку поменял,
     а про мобильное переопределение не вспомнил. */
  .toolbar { flex-wrap: wrap; gap: 6px; }
  .admin-modal-box { width: calc(100vw - 32px); padding: 20px; }
  select, input[type="text"], input[type="email"], input[type="password"], input[type="date"], input[type="number"], textarea {
    font-size: 16px;
  }
}

/* ═══════════════════════════════════════════════════════
   МОДАЛКИ КАК BOTTOM-SHEET НА ТЕЛЕФОНЕ (DESIGN.md §9)
   На узких экранах модалка выезжает снизу и прижата к нижней
   кромке — до неё легко дотянуться большим пальцем, закрытие
   свайпом/тапом по фону естественнее, чем по центру экрана.
   Только ≤640px; на планшете/десктопе — центрованная карточка.
═══════════════════════════════════════════════════════ */
@media (max-width: 640px) {
  .modal-overlay,
  .task-modal-overlay {
    align-items: flex-end;
  }
  /* width:100% !important — единственный способ перебить инлайновые
     width:min(...)/max-width:... на конкретных модалках (см. app.js);
     оправданное исключение из «без !important», ограничено мобилой. */
  .modal-box,
  .admin-modal-box,
  .client-modal-box,
  .deal-modal-box,
  .task-modal-box {
    width: 100% !important;
    max-width: 100% !important;
    max-height: 92dvh;
    overflow-y: auto;
    border-radius: var(--r-2xl) var(--r-2xl) 0 0;
    padding-bottom: max(20px, env(safe-area-inset-bottom, 20px));
    animation: sheetUp .28s cubic-bezier(.32, .72, 0, 1);
  }
  /* Грип-хэндл — визуальный намёк «потяни/закрой» */
  .modal-box::before,
  .task-modal-box::before {
    content: "";
    display: block;
    width: 36px; height: 4px;
    margin: -4px auto 14px;
    border-radius: var(--r-pill);
    background: var(--line);
  }
}
@keyframes sheetUp { from { transform: translateY(100%); } to { transform: translateY(0); } }
@media (max-width: 640px) and (prefers-reduced-motion: reduce) {
  .modal-box, .admin-modal-box, .client-modal-box, .deal-modal-box, .task-modal-box { animation: none; }
}

/* ── Estimate line bottom action bar ────────────────── */
.line-action-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid var(--line);
  opacity: 0;
  transition: opacity var(--dur-1);
}
.item:hover .line-action-bar { opacity: 1; }
/* Наведения на телефоне НЕТ, и полоса действий висела там невидимой — занимая
   53px высоты в каждой строке сметы (это и была «пустота» в полкарточки), а
   кнопки «В опции», «Дублировать» и «Удалить позицию» оставались нажимаемыми
   вслепую. Те же грабли, что с ручками переноса: opacity:0 прячет от глаза, но
   не от пальца. */
@media (hover: none) {
  .line-action-bar { opacity: 1; }
}

/* ── Estimate line collapse chevron ─────────────────── */
.line-collapse-btn {
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--panel2);
  color: var(--muted);
  cursor: pointer;
  flex-shrink: 0;
  transition: background var(--dur-1), color var(--dur-1), transform var(--dur-2);
}
.line-collapse-btn:hover { background: rgb(var(--primary-rgb) / .1); color: var(--primary); border-color: var(--primary); }
.line-collapse-btn.collapsed { transform: rotate(180deg); }
.line-collapse-btn.collapsed:hover { transform: rotate(180deg); }

/* Тот же шеврон, что и line-collapse-btn, но на кнопках с текстом (этап / всё
   целиком) — крутится только сама иконка, не вся кнопка, иначе текст перевернётся */
.stage-collapse-btn svg,
.estimate-collapse-all-btn svg {
  transition: transform var(--dur-2);
  flex-shrink: 0;
}
.stage-collapse-btn.collapsed svg,
.estimate-collapse-all-btn.collapsed svg {
  transform: rotate(180deg);
}

.line-details {
  max-height: 2000px;
  opacity: 1;
  overflow: hidden;
  transition: max-height var(--dur-3) var(--ease-out), opacity var(--dur-2) var(--ease-out);
}
.line-details-collapsed {
  max-height: 0;
  opacity: 0;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .line-details { transition: none; }
}

/* ── Catalog action icon buttons ────────────────────── */
.catalog-action-btn {
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: var(--r-pill);
  background: var(--panel2);
  color: var(--muted);
  cursor: pointer;
  transition: background var(--dur-1), color var(--dur-1);
  flex-shrink: 0;
}

.btn.catalog-add-btn {
  border-radius: var(--r-pill);
  padding: 0 18px;
  min-height: 32px;
}

/* Главное действие карточки — тихое; заливку оно получает вместе с наведением
   на свою карточку. В каталоге таких карточек два десятка на экран, в пакетах —
   сорок пять: залитая кнопка в каждой превращала сетку в фиолетовую стену, и
   правило «акцент — на один главный элемент экрана» (DESIGN.md §3) переставало
   работать. Акцентом на этих страницах остаётся кнопка вверху («Своя позиция»,
   «Свой пакет») — то есть ровно одна.
   Прятать до наведения, как кнопку шага на карточке сделки, здесь нельзя: сюда
   приходят именно нажимать, а на телефоне наведения не бывает вовсе. */
.item--catalog .btn.catalog-add-btn.primary,
.package-card .btn.pkg-apply-btn.primary {
  background: transparent;
  color: var(--primary-text);
  border: 1px solid rgb(var(--primary-rgb) / .45);
}
.item--catalog:hover .btn.catalog-add-btn.primary,
.item--catalog .btn.catalog-add-btn.primary:hover,
.item--catalog .btn.catalog-add-btn.primary:focus-visible,
.package-card:hover .btn.pkg-apply-btn.primary,
.package-card .btn.pkg-apply-btn.primary:hover,
.package-card .btn.pkg-apply-btn.primary:focus-visible {
  background: var(--primary);
  color: var(--on-color);
  border-color: var(--primary);
}
.catalog-action-btn:hover {
  background: var(--panel3, rgb(var(--primary-rgb) / .08));
  color: var(--text);
  border-color: var(--primary);
}
.catalog-action-btn.active {
  background: var(--tint-amber-bg);
  color: var(--tint-amber);
  border-color: rgba(251,191,36,.4);
}
.catalog-action-btn.danger:hover {
  background: rgba(220,38,38,.1);
  color: var(--text-danger);
  border-color: rgba(220,38,38,.4);
}

/* ═══════════════════════════════════════════════════════
   LIGHT MODE OVERRIDES (собраны вместе для ясности)
═══════════════════════════════════════════════════════ */
[data-theme="light"] .modal-overlay,
[data-theme="light"] .task-modal-overlay { background: rgba(0,0,0,.3); }


[data-theme="light"] .dsp-step.done .dsp-pill { background: rgba(22,163,74,.1); border-color: rgba(22,163,74,.35); color: var(--text-success); }

/* ═══════════════════════════════════════════════════════
   GLOBAL SCROLLBAR STYLE
═══════════════════════════════════════════════════════ */
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: rgba(255,255,255,.12);
  border-radius: var(--r-xs);
}
::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,.22); }
[data-theme="light"] ::-webkit-scrollbar-thumb { background: rgba(0,0,0,.14); }
[data-theme="light"] ::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,.24); }

/* ═══════════════════════════════════════════════════════
   TOUCH TARGETS — расширенная (невидимая) зона касания 44×44px
   для мелких icon-кнопок, без изменения их визуального размера
   (иначе плотные тулбары карточек/каталога сломались бы). Только
   для touch-устройств — на мыши точность и так достаточна.
═══════════════════════════════════════════════════════ */
@media (hover: none) and (pointer: coarse) {
  /* Список пополнен 03.08.2026 по замеру живого DOM на 390×844 С ЭМУЛЯЦИЕЙ
     КАСАНИЯ. Без hasTouch Chromium сообщает pointer:fine, весь этот блок в
     замер не попадает, и мерилась бы десктопная раскладка в узком окне.
     Добавлены: .logo (42×42) и .profile-avatar-btn (34×34) — они на ВСЕХ 15
     экранах, а из списка выпали, хотя стоят в топбаре рядом с уже учтёнными
     .topbar-icon-btn; .xlsx-icon-btn (38×38) — экспорт/импорт каталога,
     клиентов и финансов. */
  .topbar-icon-btn,
  .profile-avatar-btn,
  .logo,
  .xlsx-icon-btn,
  .deal-menu-btn,
  .line-collapse-btn,
  .catalog-action-btn,
  .db-chart-nav-btn,
  .icon-del-btn,
  .u-modal-close {
    position: relative;
  }
  /* .u-modal-close — общий крестик ВСЕХ окон — в этом списке не было: на телефоне
     его зона касания была 32×32, меньше 44. Всплыло 11.09.2026, когда крестик
     онбординга (он был 44×44 своим инлайном) переводили на общий класс. */
  .u-modal-close::after,
  .side-promo-close::after,
  .topbar-icon-btn::after,
  .profile-avatar-btn::after,
  .logo::after,
  .xlsx-icon-btn::after,
  .deal-menu-btn::after,
  .line-collapse-btn::after,
  .catalog-action-btn::after,
  .db-chart-nav-btn::after,
  .icon-del-btn::after {
    content: "";
    position: absolute;
    top: 50%; left: 50%;
    width: 44px; height: 44px;
    transform: translate(-50%, -50%);
  }

  /* Чипы раздела «Договоры» — фильтр категории (27px) и метка «N полей не
     заполнено» (23px): по ширине они и так больше 44, не хватало высоты.
     Сторож целей касания их не ловил — в его аккаунте договоров нет вовсе,
     а без них ни фильтров, ни меток на экране не появляется. */
  .chip-filter,
  .contract-status-btn,
  .contract-left-badge { position: relative; }
  .chip-filter::after,
  .contract-status-btn::after,
  .contract-left-badge::after {
    content: "";
    position: absolute;
    top: 50%; left: 0; right: 0;
    height: 44px;
    transform: translateY(-50%);
  }

  /* Поля ввода не дотягивали до 44px ОДИН пиксель (43) — цена услуги, поиск по
     каталогу, поля базы знаний и настроек. Дешевле поправить, чем объяснять,
     почему палец промахивается по строке цены. Чекбоксы и переключатели сюда
     не попадают: у них свой размер (18px) и своя область касания через label. */
  input:not([type="checkbox"]):not([type="radio"]),
  textarea,
  select {
    min-height: 44px;
  }

  /* «+» в колонках задач — 30px в ширину при 36 в высоту: узкая цель, а рядом
     заголовок колонки. Кнопки с текстом и так шире 44px, поэтому правило
     трогает только такие узкие. */
  .btn.small {
    min-width: 44px;
  }

  /* uu-select-btn/opt рассчитаны на десктоп-мышь (~35px); на телефоне их
     теперь тоже используем для коротких списков (UU_TOUCH_MAX_OPTIONS в
     app.js) — поднимаем до 44px touch-таргета. */
  .uu-select-btn { min-height: 44px; }
  .uu-select-opt { min-height: 44px; }
}

/* ═══════════════════════════════════════════════════════
   UTILITY CLASSES — вынос самых часто повторяющихся
   inline style="..." из app.js (5+ повторений). Одноразовые/
   контекстные инлайн-стили намеренно не тронуты — не дают
   экономии DRY. Значения — точная копия того, что было
   инлайн, без пересчёта под шкалу отступов.
═══════════════════════════════════════════════════════ */
.u-meta { font-size: 12px; color: var(--muted); }
.mb-14 { margin-bottom: 14px; }
.mt-10 { margin-top: 10px; }
.u-muted { color: var(--muted); }
.mt-12 { margin-top: 12px; }
.mt-14 { margin-top: 14px; }
.m-0 { margin: 0; }
.mb-12 { margin-bottom: 12px; }
.fs-12 { font-size: 12px; }
.u-meta-13 { font-size: 13px; color: var(--muted); }
/* Кнопка-ссылка внутри строки текста («Сметы пока нет · собрать»). Обычная .btn
   здесь разрывала бы фразу на две строки в плитке шириной 150px, а действие тут
   продолжение подписи, а не отдельный элемент управления. */
.u-linkbtn {
  background: none; border: none; padding: 0;
  font: inherit; color: var(--primary-text); font-weight: 700;
  text-decoration: underline; cursor: pointer;
}
.u-linkbtn:hover { color: var(--text); }
.u-pointer { cursor: pointer; }
.ta-right { text-align: right; }
.u-title-20 { margin: 0; font-size: 20px; }
.mt-8 { margin-top: 8px; }
.mb-18 { margin-bottom: 18px; }
.mb-16 { margin-bottom: 16px; }
.u-modal-close {
  background: none; border: none; color: var(--muted); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: var(--r-sm); flex: 0 0 auto;
  transition: background var(--dur-1), color var(--dur-1);
}
.u-modal-close:hover { background: var(--panel2); color: var(--text); }
/* ── Шапка админки ─────────────────────────────────────────────────────────
   Плитка: число, подпись и вторая подпись помельче — она не даёт прочитать
   число неправильно («активных 2» ≠ «платят 2»). Инлайновые стили ушли сюда:
   шесть плиток повторяли одну и ту же простыню. */
.adm-kpi-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
  gap: 10px; margin-bottom: 24px;
}
/* Плитка — кнопка: она ведёт в список с этим же отбором. Значит и вести себя
   должна как кнопка (курсор, отклик на наведение, фокус с клавиатуры), иначе
   получится «кликабельное без признаков кликабельного» — ровно то, что сметали
   по всем разделам 12.09. Текст выравниваем влево: у <button> он по центру. */
.adm-kpi {
  background: var(--panel2); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: 14px 16px;
  text-align: left; font: inherit; color: inherit; cursor: pointer;
  transition: border-color var(--dur-1), background var(--dur-1), transform var(--dur-1);
}
.adm-kpi:hover {
  border-color: rgb(var(--primary-rgb) / .5);
  background: var(--panel);
  transform: translateY(-1px);
}
.adm-kpi:active { transform: none; }
.adm-kpi-val { font-size: 22px; font-weight: 900; font-variant-numeric: tabular-nums; line-height: 1.1; }
.adm-kpi-label { font-size: 12px; color: var(--muted); margin-top: 3px; font-weight: 600; }
.adm-kpi-hint { font-size: 11px; color: var(--muted); opacity: .75; margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Телефон: до первого пользователя уходил ЦЕЛЫЙ экран — 846px на 390×844
   (замер 12.09.2026). Шесть плиток по две в ряд занимали 280px, а под ними
   ещё три ряда управления. Уплотняем плитки и снимаем подпись «Сначала:» —
   сами кнопки сортировки говорят за себя, а ряд перестаёт переноситься.
   Подписи плиток («платят: 0», «за 30 дней: …») НЕ прячем: ради них плитки и
   читают, а место экономится отступами. */
@media (max-width: 560px) {
  /* Три колонки, а не две: шесть плиток вставали тремя рядами. Замер на 390px —
     подписи в плитку шириной 115px помещаются целиком («за 30 дней: 0 ₽» —
     самая длинная), поэтому ряд экономится без потери смысла. */
  .adm-kpi-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin-bottom: 16px; }
  .adm-kpi { padding: 10px 12px; }
  .adm-kpi-val { font-size: 18px; }
  .adm-kpi-label { margin-top: 2px; }
  .kp-sort > .u-meta { display: none; }
}

/* Счётчик найденного — в ряду поиска и сортировки, справа перед «Выбрать». */
.adm-shown { font-size: 12px; margin-left: auto; white-space: nowrap; align-self: center; }

/* Крестик чеклиста «первые шаги» — тот же вид, но крупнее: он стоит не в шапке
   окна, а на карточке среди контента, и по нему целятся пальцем. Размер держит
   тест в наборе a11y (не меньше 36×36); общий крестик 32×32 его бы уронил. */
.u-modal-close.onb-hide-btn { width: 44px; height: 44px; }
.u-subpanel { margin-top: 18px; box-shadow: none; background: var(--panel2); }
.u-label-strong { font-weight: 700; font-size: 14px; color: var(--text); }
.fs-20 { font-size: 20px; }
.u-meta-mt3 { font-size: 12px; color: var(--muted); margin-top: 3px; }
.u-flex1-min0 { flex: 1; min-width: 0; }
.u-flex1 { flex: 1; }
.u-flex-g8 { display: flex; gap: 8px; }
.u-flex-ac-g6 { display: flex; align-items: center; gap: 6px; }
/* Выпадашка рядом с кнопкой: без min-width:0 флекс-элемент не сжимается ниже
   своего содержимого, и сосед выдавливается за край. Замер на карточке сделки:
   поле «Ответственный» шириной 154px, выпадашка занимала 157px, кнопка «+»
   уезжала на 42px за пределы поля — прямо под правую панель. */
.u-flex-ac-g6 > .uu-select-wrap,
.u-flex-ac-g6 > select { flex: 1 1 auto; min-width: 0; }
.u-flex-ac-g6 > button { flex: 0 0 auto; }
.u-subpanel-mb16 { box-shadow: none; background: var(--panel2); margin-bottom: 16px; }

/* ── ПУБЛИЧНЫЙ КАЛЬКУЛЯТОР (?calc=…) — продающий мастер из трёх шагов ─────
   Не таблица сметчика, а витрина: крупные карточки-выборы, цены «от», один
   заметный CTA. Колонка одна на любой ширине — калькулятор живёт в iframe на
   сайте, где ширина непредсказуема, а sticky-панель без своего viewport'а
   всё равно не работает. */
.calc-page { max-width: 940px; margin: 0 auto; padding: 28px 20px 48px; }
.calc-h2 { margin: 0 0 6px; font-size: 21px; line-height: 1.25; font-family: var(--font-display); }
.calc-sub { margin: 0 0 18px; color: var(--muted); font-size: 14px; line-height: 1.5; }

.calc-hero { text-align: center; max-width: 620px; margin: 0 auto 24px; }
.calc-badge {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--primary-on-tint); background: var(--primary-bg);
  border: 1px solid var(--primary-bg); border-radius: var(--r-pill);
  padding: 5px 14px; margin-bottom: 14px;
}
.calc-hero h1 { margin: 0 0 10px; font-size: clamp(26px, 5vw, 34px); line-height: 1.15; font-family: var(--font-display); }
.calc-hero p { margin: 0; color: var(--muted); font-size: 15px; line-height: 1.55; }

/* Шаги: показывают, что вопросов всего три и конец близко */
.calc-steps {
  display: flex; justify-content: center; flex-wrap: wrap; gap: 6px;
  list-style: none; margin: 0 0 24px; padding: 0;
}
.calc-step > button, .calc-step > span {
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 44px; padding: 0 14px; border-radius: var(--r-pill);
  border: 1px solid var(--line); background: transparent;
  color: var(--muted); font: inherit; font-size: 13px; font-weight: 600;
}
.calc-step > button { cursor: pointer; }
.calc-step > button:hover { color: var(--text); border-color: var(--primary-text); }
.calc-step > button:focus-visible { outline: var(--ring-w) solid var(--ring); outline-offset: 2px; }
.calc-step b {
  display: grid; place-items: center; width: 20px; height: 20px; border-radius: 50%;
  background: var(--line); color: var(--text); font-size: 11px; font-weight: 800;
}
.calc-step.on > span { color: var(--text); border-color: var(--primary-on-tint); background: var(--primary-bg); }
.calc-step.on b { background: var(--primary); color: var(--on-color); }
.calc-step.done b { background: var(--green); color: var(--on-color); }
@media (max-width: 560px) {
  .calc-step > button, .calc-step > span { padding: 0 10px; font-size: 12px; }
  /* На узком экране подпись остаётся только у текущего шага — иначе три капсулы
     не влезают в строку. Кнопки «назад» сохраняют aria-label, имя не теряется. */
  .calc-step:not(.on) > * > span { display: none; }
}

/* ── Шаг 1: задача ────────────────────────────────────────────────────── */
.calc-need-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(228px, 1fr)); gap: 12px; }
.calc-need {
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  min-height: 148px; padding: 18px; text-align: left; cursor: pointer;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-xl);
  color: var(--text); font: inherit;
  transition: transform var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out), box-shadow var(--dur-1) var(--ease-out);
}
.calc-need:hover { transform: translateY(-2px); border-color: var(--primary-text); box-shadow: 0 10px 28px var(--primary-bg); }
.calc-need:focus-visible { outline: var(--ring-w) solid var(--ring); outline-offset: 2px; }
.calc-need-icon { font-size: 26px; line-height: 1; margin-bottom: 4px; }
.calc-need-title { font-size: 16px; font-weight: 700; line-height: 1.25; }
.calc-need-sub { font-size: 13px; color: var(--muted); line-height: 1.45; }
.calc-need-from {
  margin-top: auto; padding-top: 10px; font-family: var(--font-display);
  font-size: 15px; font-weight: 700; color: var(--primary-text); font-variant-numeric: tabular-nums;
}

.calc-disclaimer { margin: 20px 0 0; font-size: 13px; line-height: 1.55; color: var(--muted); }

/* ── Шаг 2: уровень ───────────────────────────────────────────────────── */
.calc-tier-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 14px; }
.calc-tier {
  position: relative; display: flex; flex-direction: column; gap: 8px;
  padding: 20px 18px; background: var(--panel);
  border: 1px solid var(--line); border-radius: var(--r-xl);
}
.calc-tier.on { border-color: var(--primary-on-tint); background: var(--primary-bg); }
.calc-tier-flag {
  position: absolute; top: -10px; left: 18px;
  padding: 3px 10px; border-radius: var(--r-pill); background: var(--primary); color: var(--on-color);
  font-size: 11px; font-weight: 700; letter-spacing: .02em;
}
.calc-tier-name { font-size: 17px; font-weight: 800; font-family: var(--font-display); }
.calc-tier-price { font-size: 22px; font-weight: 800; font-family: var(--font-display); font-variant-numeric: tabular-nums; }
.calc-tier-desc { margin: 0; font-size: 13.5px; line-height: 1.5; color: var(--text); }
.calc-tier-for { margin: 0; font-size: 12.5px; line-height: 1.45; color: var(--muted); }
.calc-tier-list { margin: 4px 0 0; padding-left: 18px; display: flex; flex-direction: column; gap: 5px; }
.calc-tier-list li { font-size: 12.5px; line-height: 1.45; color: var(--muted); }
.calc-tier-pick { margin-top: auto; width: 100%; min-height: 44px; }

.calc-nav { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-top: 24px; }
.calc-nav .btn { min-height: 44px; }

/* ── Шаг 3: результат ─────────────────────────────────────────────────── */
.calc-result-card {
  text-align: center; padding: 26px 20px;
  background: var(--panel); border: 1px solid var(--primary2); border-radius: var(--r-xl);
  box-shadow: 0 16px 44px var(--primary-bg);
}
.calc-result-label { font-size: 13px; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; font-weight: 700; }
.calc-result-sum {
  margin: 6px 0 8px; font-family: var(--font-display); font-weight: 800;
  font-size: clamp(34px, 8vw, 48px); line-height: 1.05; font-variant-numeric: tabular-nums;
}
.calc-result-meta { font-size: 14px; color: var(--muted); line-height: 1.5; }
.calc-result-rush { margin-top: 6px; font-size: 13px; color: var(--text-warning); }
.calc-result-cta { margin-top: 18px; min-height: 52px; padding: 0 28px; font-size: 16px; }
.calc-result-sub { margin-top: 8px; font-size: 12.5px; color: var(--muted); }

.calc-details, .calc-included { margin-top: 28px; }
.calc-group { border: 0; margin: 0 0 16px; padding: 0; }
.calc-group legend { padding: 0 0 8px; font-size: 13px; font-weight: 700; color: var(--muted); }
.calc-opts { display: flex; flex-wrap: wrap; gap: 8px; }
.calc-opt, .calc-extra {
  min-height: 44px; padding: 10px 14px; cursor: pointer;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-lg);
  color: var(--text); font: inherit; font-size: 13.5px; text-align: left;
}
.calc-opt:hover, .calc-extra:hover { border-color: var(--primary-text); }
.calc-opt:focus-visible, .calc-extra:focus-visible { outline: var(--ring-w) solid var(--ring); outline-offset: 2px; }
.calc-opt.on, .calc-extra.on { border-color: var(--primary-on-tint); background: var(--primary-bg); font-weight: 700; }

.calc-extras { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 8px; }
.calc-extra { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.calc-extra-name { min-width: 0; }
.calc-extra-price { flex-shrink: 0; font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums; }
.calc-extra.on .calc-extra-price { color: var(--text-success); }

.calc-inc-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 14px; }
.calc-inc-col {
  padding: 16px; background: var(--panel2);
  border: 1px solid var(--line); border-radius: var(--r-xl);
}
.calc-inc-col h3 { margin: 0 0 8px; font-size: 13px; text-transform: uppercase; letter-spacing: .04em; color: var(--primary-text); }
.calc-inc-col ul { margin: 0; padding-left: 16px; display: flex; flex-direction: column; gap: 4px; }
.calc-inc-col li { font-size: 13px; line-height: 1.4; color: var(--text); }
.calc-notes { margin: 14px 0 0; padding-left: 18px; display: flex; flex-direction: column; gap: 5px; }
.calc-notes li { font-size: 13px; line-height: 1.5; color: var(--muted); }

/* ── Заявка ───────────────────────────────────────────────────────────── */
.calc-lead {
  margin-top: 20px; padding-top: 20px; border-top: 1px dashed var(--line);
  display: flex; flex-direction: column; gap: 12px; text-align: left;
}
.calc-lead h3 { margin: 0; font-size: 17px; font-family: var(--font-display); text-align: center; }
.calc-lead.done { align-items: center; text-align: center; }
.calc-lead.done p { margin: 0; font-size: 14px; line-height: 1.55; color: var(--muted); max-width: 420px; }
.calc-lead-contacts { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
.calc-lead-contacts .btn { min-height: 44px; }
.calc-field { display: flex; flex-direction: column; gap: 5px; }
.calc-field span { font-size: 12.5px; font-weight: 600; color: var(--muted); }
.calc-field input, .calc-field textarea {
  width: 100%; min-height: 44px; padding: 11px 13px;
  background: var(--bg2); border: 1px solid var(--line); border-radius: var(--r-lg);
  color: var(--text); font: inherit; font-size: 14px; resize: vertical;
}
.calc-field input:focus-visible, .calc-field textarea:focus-visible { outline: var(--ring-w) solid var(--ring); outline-offset: 1px; }
/* min-height: кликабельная цель здесь — весь label, а сам чекбокс всего 18px;
   в одну строку текст согласия давал 22px тач-таргета вместо 44. */
.calc-agree {
  display: flex; align-items: flex-start; gap: 9px; min-height: 44px; padding: 6px 0;
  font-size: 12.5px; line-height: 1.5; color: var(--muted); cursor: pointer;
}
.calc-agree input { width: 18px; height: 18px; margin-top: 1px; flex-shrink: 0; accent-color: var(--primary); }
.calc-agree a { color: var(--primary-text); }
.calc-lead-err { margin: 0; font-size: 13px; color: var(--text-danger); }
.calc-lead-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.calc-lead-actions .btn { flex: 1; min-width: 140px; min-height: 46px; }

.calc-foot { margin: 28px 0 0; font-size: 12.5px; color: var(--muted); line-height: 1.6; text-align: center; }
.calc-foot a, .calc-link { color: var(--primary-text); font-weight: 700; }
.calc-link { background: none; border: 0; padding: 0; font: inherit; font-weight: 700; cursor: pointer; }
.calc-link:focus-visible { outline: var(--ring-w) solid var(--ring); outline-offset: 2px; }

/* ── Настройки → Оформление ─────────────────────────────────────────────────
   Переключатель темы и сетка цветовых схем. Обе группы — role="radiogroup" из
   обычных <button>: у радиокнопок нужного вида (крупная плитка с иконкой) нет,
   а нативный <input type=radio> здесь пришлось бы прятать и рисовать заново. */
.appearance-modes {
  display: flex; gap: 10px; flex-wrap: wrap;
}
.appearance-mode {
  display: flex; align-items: center; gap: 9px;
  padding: 11px 16px; min-height: 44px;      /* 44px — минимальный тач-таргет */
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--bg2); color: var(--muted);
  font: inherit; font-size: 13.5px; font-weight: 700; cursor: pointer;
  transition: color var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out),
              background var(--dur-2) var(--ease-out), transform var(--dur-1) var(--ease-out);
}
.appearance-mode:hover { color: var(--text); border-color: var(--primary2); }
.appearance-mode:active { transform: scale(.97); }
.appearance-mode.active {
  color: var(--primary-on-tint);
  border-color: rgb(var(--primary-rgb) / .45);
  background: var(--primary-bg);
}
.appearance-mode-ic { display: inline-flex; opacity: .85; }

.accent-grid {
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
.accent-chip {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; min-height: 44px;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--bg2); color: var(--muted);
  font: inherit; font-size: 13.5px; font-weight: 700; cursor: pointer; text-align: left;
  transition: color var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out),
              background var(--dur-2) var(--ease-out), transform var(--dur-1) var(--ease-out);
}
.accent-chip:hover { color: var(--text); border-color: var(--primary2); }
.accent-chip:active { transform: scale(.97); }
.accent-chip.active {
  color: var(--text);
  border-color: rgb(var(--primary-rgb) / .45);
  background: var(--primary-bg);
}
.accent-swatch {
  width: 26px; height: 26px; border-radius: 50%; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--on-color);                     /* галочка на насыщенной заливке */
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.16);
  transition: transform var(--dur-2) var(--ease-spring);
}
.accent-chip:hover .accent-swatch { transform: scale(1.12); }
.accent-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── «Смета»: постоянная колонка со списком сделок ───────────────────────────
   Раньше список сделок жил только в выезжающей панели: чтобы перейти к соседней
   сделке, её приходилось каждый раз раскрывать кнопкой. На широком экране место
   есть — держим колонку открытой всегда. На узком она прячется, и там работает
   прежняя кнопка с панелью (обе разметки в DOM одновременно, поэтому у поля
   поиска в колонке отдельный id — см. renderDealRailHtml). */
.deal-layout {
  display: grid;
  /* Было жёстко 260px: название сделки и имя клиента обрезались почти всегда, а
     этап с датой стояли встык. Растим вместе с экраном, но не бесконечно —
     выше 340px колонка начинает отъедать место у самой сметы. */
  grid-template-columns: clamp(268px, 20vw, 340px) minmax(0, 1fr);
  gap: 18px;
  align-items: start;
}
.deal-layout-main { min-width: 0; }   /* без этого грид растягивается под самую широкую строку сметы */

.deal-rail {
  position: sticky;
  /* 86px, как у .summary справа: шапка sticky и имеет высоту 62px, при top:8px
     колонка уезжала под неё и «Сделки» обрезались. */
  top: 86px;
  display: flex;
  flex-direction: column;
  max-height: calc(100dvh - 104px);
  background: var(--panel2);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  overflow: hidden;
}
.deal-rail-head {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 14px 10px;
  border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
}
.deal-rail-head h3 { margin: 0; font-size: 14px; flex: 1; }
/* Ряд колонки «Сделки»: «+» (новая сделка окном) и поиск длинной строкой. */
.deal-rail-actions { display: flex; align-items: center; gap: 8px; padding: 10px 12px; flex: 0 0 auto; }
.deal-rail-add {
  flex: 0 0 auto; width: 40px; height: 40px; display: inline-grid; place-items: center;
  border-radius: var(--r-md); border: 1px solid rgb(var(--primary-rgb) / .35);
  background: rgb(var(--primary-rgb) / .1); color: var(--primary-text); cursor: pointer;
  transition: background var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out),
    color var(--dur-2), transform var(--dur-2) var(--ease-spring);
}
.deal-rail-add:hover {
  background: var(--primary); border-color: var(--primary); color: var(--on-color); transform: translateY(-1px);
}
.deal-rail-add:active { transform: none; }
.deal-rail-actions .deal-rail-search { flex: 1 1 auto; min-width: 0; width: auto; }
@media (hover: none) and (pointer: coarse) { .deal-rail-add { width: 44px; height: 44px; } }
.deal-rail-list {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 0 8px 10px;
}

/* Кнопка возврата к автосортировке — в шапке колонки, на месте бывшей «Все».
   Видна только когда порядок ручной. Базовый вид берёт у .btn small, здесь только
   иконка в строку с подписью и уплотнение: заголовок «Сделки» рядом, и кнопка не
   должна его перевешивать. */
.deal-rail-order-reset {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 5px 9px; font-size: 12px; white-space: nowrap;
}
.deal-rail-order-reset svg { flex: 0 0 auto; }

/* Ниже 1440px колонка не помещается рядом со сметой — возвращаемся к кнопке.
   Было 1100px, и меню разделов слева не учитывалось: на ноутбуке 1280 смете
   оставалось 360px, позиции резались справа (замер 30.09.2026: 1280 → 360px,
   1366 → 441px). Без колонки: 1280 → ~650px, 1366 → ~730px. */
@media (max-width: 1440px) {
  .deal-layout { display: block; }
  .deal-rail { display: none; }
}
/* На широком экране кнопка-переключатель в шапке сделки не нужна: список и так
   виден слева. Оставляем её только там, где колонка скрыта. */
@media (min-width: 1441px) {
  .deal-switcher { display: none; }
}

/* ── Вкладка «Договор» в сделке: выбор шаблона ───────────────────────────── */
.deal-contract-tpl {
  display: flex; flex-direction: column; gap: 6px; text-align: left;
  padding: 14px 16px; min-height: 44px;
  border: 1px solid var(--line); border-radius: var(--r-xl);
  background: var(--panel2); color: var(--text);
  font: inherit; cursor: pointer;
  transition: border-color var(--dur-2) var(--ease-out), background var(--dur-2) var(--ease-out),
              transform var(--dur-1) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
}
.deal-contract-tpl:hover {
  border-color: rgb(var(--primary-rgb) / .45);
  background: var(--primary-bg);
  transform: translateY(-2px);
  box-shadow: var(--elev-1);
}
.deal-contract-tpl:active { transform: translateY(-1px) scale(.99); }
.deal-contract-tpl-cat {
  font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
  color: var(--primary-text);
}
.deal-contract-tpl-name { font-size: 14px; font-weight: 800; line-height: 1.3; }
.deal-contract-tpl-desc { font-size: 12px; color: var(--muted); line-height: 1.45; }

/* ── Табличные цифры у сумм (DESIGN.md §5) ────────────────────────────────
   Пропорциональные цифры разной ширины: сумма «прыгает» при пересчёте, а в
   списках колонка чисел не выравнивается по разрядам. Для CRM, где деньги —
   главное содержимое, это заметно на каждом экране.

   Замер живого DOM (04.08) нашёл семь видов сумм без табличных цифр, включая
   .fin-amount 22px — самое крупное число в «Финансах».

   Правило намеренно шире одних сумм: у .badge и .kp-row-sub рядом с деньгами
   стоит текст, а на буквы font-variant-numeric не влияет вовсе — разделять
   их отдельными обёртками значило бы плодить разметку ради нуля эффекта. */
.funnel-stage .fs-amount,
.fin-card .fin-amount,
.kp-row-sub,
.db-analytics-legend,
.db-analytics-legend span,
.badge {
  font-variant-numeric: tabular-nums;
}

/* ── Всплывающие поверхности в СВЕТЛОЙ теме ───────────────────────────────
   Дропдауны, меню, подсказки и модалки несли рукописные тени под тёмный фон —
   до rgba(0,0,0,.55) при 80px размытия. Замер: универсальный дропдаун в светлой
   теме давал rgba(0,0,0,.35) при 40px, то есть вдвое тяжелее светлого --elev-3.
   На белом такая тень читается грязным пятном, а не приподнятой поверхностью.

   Раньше это чинили точечно: отдельные правила существовали ровно для двух
   поверхностей из одиннадцати (.profile-dd и .deal-ctx-menu), остальные
   пропустили. Здесь один список и один токен — новую всплывающую поверхность
   достаточно дописать сюда, а не подбирать ей тень заново.

   Тёмная тема не затронута: там рукописные значения остаются как были. */
[data-theme="light"] .uu-select-dd,
[data-theme="light"] .profile-dd,
[data-theme="light"] .deal-ctx-menu,
[data-theme="light"] .help-dd,
[data-theme="light"] .currency-select-dd,
[data-theme="light"] .global-add-menu,
[data-theme="light"] .sidebar-nav-config,
[data-theme="light"] .svg-chart-tooltip,
[data-theme="light"] .estimate-nav-tooltip-fixed,
[data-theme="light"] .tour-popover,
[data-theme="light"] .task-modal-box,
[data-theme="light"] .auth-gate-box,
[data-theme="light"] .deal-card-flying {
  box-shadow: var(--elev-3);
}

/* ═══════════════════════════════════════════════════════════════════════════
   ЦЕЛИ КАСАНИЯ НА ТЕЛЕФОНЕ — ПОСЛЕДНИЙ БЛОК ФАЙЛА, И ЭТО ВАЖНО

   Минимум 44px для телефона задан выше (блок «Mobile UX — touch targets»,
   ~строка 6322), но он стоит В СЕРЕДИНЕ файла, и правила компонентов ниже по
   тексту молча его перебивают: специфичность у них та же, а побеждает то, что
   позже. Так `.btn.catalog-add-btn { min-height: 32px }` (строка ~6584)
   оставлял «Добавить» — самую нажимаемую кнопку продукта — высотой 32px, и это
   же случилось с панелью кнопок сметы («Свернуть всё» вылезала за свою кнопку).

   Замер на 390×844 до правки, обходом 17 разделов: 137 целей меньше 44px.
   Поэтому гарантии касания живут ЗДЕСЬ, в самом конце: любое правило выше по
   файлу они перекрывают, и новый компонент не сможет их отменить, не написав
   отдельное правило ниже этой строки.

   Что НЕ трогаем: плотные сетки, где физический размер задан ячейкой
   (кнопка «+» в дне календаря) — им область касания расширяем невидимым
   ::after, а не высотой.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 640px) {
  .btn,
  .btn.small,
  .chip,
  .tab,
  .fin-subtab,
  .fin-quick-btn,
  .date-preset-btn,
  .deal-view-btn,
  .brief-type-chip,
  .cal-month-pill,
  .cal-nav2-arrow,
  .cal-nav2-today,
  .cal-type-chip,
  .deal-switcher-btn,
  .currency-select-btn {
    min-height: 44px;
  }
  /* Иконочные кнопки без подписи: узкие по ширине так же плохо, как низкие. */
  .deal-view-btn,
  .cal-nav2-arrow {
    min-width: 44px;
  }

  /* Кнопка «+» в клетке дня календаря сюда БОЛЬШЕ НЕ ВХОДИТ: на телефоне её нет
     (правило выше), и расширять область касания у невидимой кнопки — ровно то,
     из-за чего тап по дню улетал в «Задачи проекта». Расширять можно область
     ВИДИМОЙ кнопки; невидимой — нельзя, она тогда крадёт чужие нажатия. */
  /* Шаг количества в строке каталога («−» и «+», 26×32). Поднимать саму кнопку
     нельзя — она стоит внутри компактного степпера рядом с числом, и 44px разорвали
     бы строку позиции. Расширяем невидимую область, как у «+» в календаре.
     Всплыло 17.08, когда демо-сделка стала собираться из пакета: позиций в смете
     стало 21 вместо пяти, и степперы появились там, где раньше их просто не было
     видно — дефект существовал давно, а показался только на объёме. */
  .catalog-qty-btn { position: relative; }
  .catalog-qty-btn::after {
    content: "";
    position: absolute;
    top: 50%; left: 50%;
    width: 44px; height: 44px;
    transform: translate(-50%, -50%);
  }

  /* Кружок «Готово» в строке задачи — 24×24 по рамке: самое частое действие
     исполнителя на съёмке, и до правки палец в него не попадал (замер: ни одна
     из четырёх точек в 20px от центра). Кнопка стоит в плотной строке рядом с
     названием, поднимать её высоту нельзя — расширяем область, как у степпера.
     Здесь это безопасно: кнопка ВИДНА, значит область совпадает с тем, во что
     человек целится. */
  .gtask-check { position: relative; }
  .gtask-check::after {
    content: "";
    position: absolute;
    top: 50%; left: 50%;
    width: 44px; height: 44px;
    transform: translate(-50%, -50%);
  }
}

/* ── ОТКЛИК НА ДОЛГОЕ ДЕЙСТВИЕ ──────────────────────────────────────────────
   Кнопка, запустившая сетевое действие, показывает, что работа идёт. Раньше
   отклика не было: «КП-ссылка» уходила на сервер и слала письмо клиенту, а на
   экране до тоста не менялось ничего — читается как «кнопка не работает», и
   первое желание нажать ещё раз (что и давало дубли КП).

   Круг вращается, но опираться ТОЛЬКО на вращение нельзя: при
   prefers-reduced-motion глобальное правило обнуляет длительность анимаций, и
   спиннер замирает. Поэтому состояние несут ещё и приглушение с курсором
   ожидания — они работают без движения. */
.btn.is-busy,
button.is-busy {
  position: relative;
  cursor: progress;
  opacity: .72;
}
.btn.is-busy::before,
button.is-busy::before {
  content: "";
  display: inline-block;
  width: 13px; height: 13px;
  margin-right: 7px;
  vertical-align: -2px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: btnBusySpin .7s linear infinite;
}
@keyframes btnBusySpin { to { transform: rotate(360deg); } }

/* Триггер выбора типа брифа — на телефоне вместо ленты, которая показывала три
   типа из шести и прятала остальные за краем (см. briefTypeListHtml). */
.brief-type-trigger { display: none; }
@media (max-width: 640px) {
  .brief-type-tabs { display: none; }
  .brief-type-trigger {
    display: flex; align-items: center; gap: 10px; width: 100%;
    min-height: 44px; margin-top: 16px; padding: 10px 14px;
    font: inherit; font-size: 15px; font-weight: 800; text-align: left;
    color: var(--text); background: var(--panel2);
    border: 1px solid var(--line); border-radius: var(--r-md);
    cursor: pointer;
  }
  /* min-width: 0 на подписи — иначе длинное название распирает кнопку и выдавливает
     шеврон за край. */
  .brief-type-trigger-label {
    flex: 1 1 auto; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .brief-type-trigger svg { flex: 0 0 auto; color: var(--muted); }
}

/* Пустые этапы воронки на телефоне скрыты — см. renderCrmHome. На десктопе все
   одиннадцать помещаются, и «ноль в Договоре» там законный сигнал. */
@media (max-width: 640px) {
  .crm-home-funnel .funnel-stage--empty { display: none; }
}

/* ── Админка: карточка пользователя ──────────────────────────────────────────
   Карточка собиралась одним flex-wrap: аватар, имя, статусы и ШЕСТЬ кнопок в
   одном потоке. На телефоне это разваливалось в кашу — кнопки переносились по
   две-три в ряд, последняя (красная «Заблокировать») оставалась сиротой на своей
   строке, и карточка читалась как свалка. Даём явные зоны: кто → в каком
   состоянии → что с ним можно сделать. */
.adm-card { border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden; }
.adm-card-row {
  display: flex; align-items: center; gap: 12px;
  padding: 14px 16px; flex-wrap: wrap;
}
.adm-avatar {
  width: 36px; height: 36px; border-radius: 50%; flex-shrink: 0;
  background: var(--primary); color: var(--on-color);
  display: grid; place-items: center; font-size: 14px; font-weight: 900;
}
/* Цвет аватара — статус подписки: активна, триал, истекла, заблокирован. */
.adm-avatar.is-active { background: var(--green); }
.adm-avatar.is-trial { background: var(--yellow); color: var(--bg); }
.adm-avatar.is-expired, .adm-avatar.is-none { background: var(--panel2); color: var(--muted); border: 1px solid var(--line); }
.adm-avatar.is-blocked { background: color-mix(in srgb, var(--red) 70%, var(--panel2)); }
/* Срок: неделя и меньше — янтарь, прошёл — красный. */
.adm-days { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.adm-days.is-soon { color: var(--text-warning); font-weight: 700; }
.adm-days.is-late { color: var(--text-danger); }
/* «Платежи»: время, пометка «сегодня», номер платежа в ЮKassa. */
.adm-pay-time { margin-top: 2px; font-size: 11.5px; font-variant-numeric: tabular-nums; }
.adm-pay-today {
  display: inline-block; margin-left: 4px; padding: 0 6px; border-radius: var(--r-pill);
  font-size: 10.5px; font-weight: 700; color: var(--text-success);
  background: color-mix(in srgb, var(--green) 14%, transparent);
}
.adm-pay-id {
  display: block; margin-top: 3px; padding: 0; border: 0; background: none; cursor: copy;
  font: 11px var(--font-mono); color: var(--muted);
}
.adm-pay-id:hover { color: var(--primary-text); }
/* Правка подписки — карточкой под строкой. */
.adm-edit { border-top: 1px solid var(--line); padding: 16px; background: var(--panel2); }
.adm-edit-title { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 13px; font-weight: 700; }
.adm-edit-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 10px; margin-bottom: 12px; }
.adm-edit-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding-top: 12px; border-top: 1px dashed var(--line); }
.adm-edit-refund { margin-left: auto; }
.adm-status { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; flex-shrink: 0; }
.adm-actions { display: flex; gap: 6px; flex-wrap: wrap; min-width: 0; align-items: center; }
/* Колонки правой части. Зона статуса растёт по содержимому (у кого-то метка, у
   кого-то только «Истекла»), поэтому без фиксированных ширин ряд действий
   начинался у каждой строки на своей вертикали. Ширины: 300px хватает на самый
   длинный набор состояния («Истекла · PRO · 22 дн. назад · Амбассадор»), 132px —
   на самую длинную главную кнопку («Активировать»). */
@media (min-width: 721px) {
  .adm-status { width: 300px; justify-content: flex-end; }
  .adm-actions { flex-wrap: nowrap; }
  .adm-act-main { flex: 0 0 132px; display: flex; justify-content: flex-end; }
  .adm-act-main .btn { width: 100%; justify-content: center; }
  .adm-act-tools { display: flex; gap: 6px; flex: 0 0 auto; }
}
/* Зелёная «Активировать» была залита в тринадцати строках из семнадцати — экран
   светился одинаково важными кнопками, и глаз не находил ни одной. Контур
   называет действие не тише, а заливку кнопка получает, когда работают с её
   строкой. Тот же приём, что в списке сделок и в каталоге. */
.adm-card-row .btn.green {
  background: transparent; color: var(--text-success); border: 1px solid var(--green);
  transition: background var(--dur-1), color var(--dur-1);
}
.adm-card:hover .adm-card-row .btn.green,
.adm-card-row .btn.green:hover,
.adm-card-row .btn.green:focus-visible {
  background: var(--green); color: var(--on-color);
}
/* Аккаунт без записи агентства: действий нет, и вместо ряда кнопок стоит
   объяснение — почему. Приглушённое, но с предупреждающим знаком: это не
   ошибка интерфейса, а состояние аккаунта. */
.adm-no-profile {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; color: var(--muted);
  padding: 6px 10px; border: 1px dashed var(--line); border-radius: var(--r-pill);
}
.adm-no-profile svg { color: var(--text-warning); flex: 0 0 auto; }

@media (max-width: 720px) {
  /* Три этажа вместо одного потока. Аватар с именем занимают строку целиком,
     статусы идут под ними, действия — последней строкой. */
  .adm-card-row { align-items: flex-start; gap: 10px; padding: 12px 13px; }
  .adm-who { flex: 1 1 100%; min-width: 0; order: 1; }
  .adm-avatar { order: 0; }
  .adm-status { order: 2; flex: 1 1 100%; }
  /* Действия — СЕТКОЙ, а не переносом: при переносе последняя кнопка вставала
     одна и ряд выглядел оборванным. Колонки равной ширины, минимум 44px —
     тач-цель; текстовые кнопки («Активировать», «Возврат») занимают две. */
  /* Ровно ЧЕТЫРЕ колонки, а не auto-fit: при автоподборе выходило пять, и
     семь ячеек (шесть кнопок, одна из них двойная) ложились как 5+2 — вторая
     строка выглядела огрызком. Четыре дают 4+3, ряд читается целым. */
  .adm-actions {
    order: 3; flex: 1 1 100%; display: grid; gap: 6px;
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
  /* На телефоне зоны растворяются: кнопки должны лечь прямо в эту сетку из
     четырёх колонок, а не двумя блоками внутри неё. */
  .adm-act-main, .adm-act-tools { display: contents; }
  .adm-actions .btn { min-height: 44px; padding-left: 6px; padding-right: 6px; justify-content: center; }
  /* Две колонки — только кнопкам с длинной подписью («Активировать»,
     «Возврат»); они помечены классом в разметке. Правило «всё, что не значок»
     раздувало и «+14д», хотя тому хватает одной колонки. */
  .adm-actions .adm-act-wide { grid-column: span 2; }
}

/* Ряды-ленты админки (вкладки, отборы). Раньше они переносились: шесть отборов
   вставали в ТРИ строки и съедали треть экрана до первого пользователя. Лента с
   прокруткой держит одну строку и не прячет порядок — свайп по ней привычен по
   воронке на главной. Полосу прокрутки прячем: она тут шумит. */
.adm-strip {
  flex-wrap: nowrap !important;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-mask-image: linear-gradient(to right, transparent 0, black 10px, black calc(100% - 10px), transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, black 10px, black calc(100% - 10px), transparent 100%);
}
.adm-strip::-webkit-scrollbar { display: none; }
.adm-strip > * { flex: 0 0 auto; }
@media (max-width: 640px) {
  .admin-tab { min-height: 44px; }
  .adm-strip .fin-subtab { min-height: 40px; }
}

/* Форма промокода в админке: на телефоне четыре колонки не помещаются, поле
   «Истекает» схлопывалось до иконки календаря. */
@media (max-width: 640px) {
  .admin-promo-grid { grid-template-columns: 1fr 1fr !important; }
}

/* Состав пакета на телефоне: колонки заданы жёсткой шириной, и строка выходила
   за край — под списком появлялась полоса прокрутки вбок. Ужимаем ровно те
   колонки, которые от этого не теряют смысла. */
@media (max-width: 640px) {
  /* Прежние правила подрезали ширины полей: строка состава была ОДНОЙ линией
     (имя + объём + сумма + корзина) и на 390px выходила за край на 6px. С
     04.09.2026 строка двухэтажная — имя своей строкой, объём и сумма под ним, —
     и подрезать больше нечего: ужимать нечему, всё помещается. */
}

/* Пояснение к разделу на телефоне: читается один раз, а места занимает три строки
   между заголовком и работой. Скрываем только там, где экран узкий. */
@media (max-width: 640px) {
  .hide-on-mobile { display: none !important; }
  /* Фильтр, сортировка и счётчик — в один ряд. С фиксированными 170 и 150px два
     выбора занимали всю строку, и счётчик уезжал на третью. */
  /* Поиск занимает СВОЮ строку целиком, фильтр с сортировкой и счётчик — вторую.
     Без явной строки для поиска он делил ряд с выпадашками, и те сжимались до
     сорока пикселей: от подписей «Без фильтра» и «По названию» оставались одни
     шевроны. Минимальная ширина 110px — та, при которой подпись ещё читается. */
  /* С 02.10.2026 в строке ещё и вид «плитки / список»: поиск делит с ним
     первую строку (поле остаётся ≥200px), обе выпадашки — вторую. Обёртка
     инструментов раскладке не мешает (display: contents), порядок — order. */
  /* Основа «вся строка минус переключатель вида» — рядом с полем больше ничего
     не помещается, выпадашки гарантированно уходят на вторую строку. */
  .catalog-toolbar .catalog-search-wrap { flex: 1 1 calc(100% - 110px); max-width: none; }
  .catalog-toolbar-tools { display: contents; }
  .catalog-toolbar .catalog-view-switch { order: 1; }
  .catalog-toolbar .catalog-toolbar-select,
  .catalog-toolbar .uu-select-wrap { order: 2; }
  .catalog-toolbar .catalog-toolbar-select,
  /* Обёртка кастомной выпадашки ОБЯЗАТЕЛЬНА в списке: enhanceSelects прячет
     нативный select и переносит его ширину на .uu-select-wrap — правило,
     нацеленное только на select, до раскладки не доходит. */
  .catalog-toolbar .uu-select-wrap {
    /* Половина строки, а не 1 1 0: при нулевой основе первая выпадашка влезала
       в строку поиска (замер 480px, 02.10.2026), поле переставало расти, а
       скрытый нативный select вылезал за край на 8px. */
    flex: 1 1 calc(50% - 5px) !important;
    width: auto !important;
    min-width: 110px;
  }
  /* Счётчик уходит на свою строку. 110px на выпадашку — та ширина, при которой
     подпись «ещё читается» в теории; замером на 390px (12.09.2026) вышло «Без
     фил…» и «По назв…»: шеврон и внутренние отступы съедают из этих 110 около
     сорока. Счётчик — справочное число, ему отдельная строка стоит дешевле, чем
     две обрезанные подписи, а выпадашки получают по ~170px и помещаются целиком. */
  /* Счётчика в ряду фильтров больше нет — он в шапке раздела над карточками. */
}

/* ── Карточка каталога: одна высота в ряду и внятное наведение ──────────────
   Высота «плясала»: описание бывает в одну строку и в три, у части позиций есть
   бейдж «Расходы», у части нет — и в ряду под короткой карточкой оставалась
   дыра до низа соседней (скриншот владельца 29.08.2026).

   Лечим не подгонкой высот, а строением карточки: она растягивается на всю
   ячейку сетки, содержимое сверху, ряд действий прижат к низу. Тогда кнопки
   «Добавить» стоят на одной линии во всём ряду — именно по ним глаз и ведёт.
   Описание везде две строки (на телефоне так было и раньше): третья и
   четвёртая не помогают выбрать, а высоту рвут. */
.item--catalog {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.item--catalog > .item-top { flex: 1 1 auto; }
/* Поле цены — одной ширины во всех карточках. Оно объявлено на 190px, но внутри
   флекс-ряда сжималось вслед за названием: у «3D-элементы и рендер» коробка
   выходила 91px, у «Адаптация в вертикальный формат» — 124px, и в одном ряду
   стояли поля разного размера. Ширина 118px — та же, что уже принята на
   телефоне; «за продукт» под ней помещается. */
.item--catalog .price-editor { flex: 0 0 118px; width: 118px; }
.item--catalog .item-top > div:first-child { flex: 1 1 auto; min-width: 0; }
.item--catalog .catalog-price-input { width: 100%; }
/* Цена в карточке каталога — сперва число, и только потом поле ввода. Рамка с
   подложкой стояли вокруг каждой цены, а карточек на экране два десятка: сетка
   получала два десятка тяжёлых прямоугольников, которые спорили с названием
   позиции. Читают эту сетку ради названия и цены, правят цену редко — поэтому
   оправа приходит при наведении на карточку и при фокусе, ровно тогда, когда
   полем собираются пользоваться. Габариты поля не меняются: цель касания
   остаётся прежней, ничего не «прыгает». */
.item--catalog .catalog-price-input {
  background: transparent; border-color: transparent;
  font-size: 16px; font-weight: 700; font-variant-numeric: tabular-nums;
  transition: background var(--dur-1), border-color var(--dur-1);
}
.item--catalog:hover .catalog-price-input,
.item--catalog .catalog-price-input:hover,
.item--catalog .catalog-price-input:focus {
  background: var(--input); border-color: var(--line);
}
/* Заголовок держит две строки всегда. Иначе у соседей по ряду («Аватар
   сообщества» в одну строку, «Адаптация в вертикальный формат» в две) описание
   и бейджи начинались на разной высоте: низ карточек уже выровнен, а середина
   всё равно «плавала». Две строки — потолок для названия услуги в каталоге из
   200 позиций (проверено: длиннее нет), поэтому резерв не создаёт пустоты
   больше одной строки. */
/* Карточка-шаблон (договоры). Растягивается на ячейку сетки, бейдж категории
   прижат к низу — у соседей по ряду он стоит на одной линии независимо от того,
   в одну строку имя шаблона или в две. Наведение — то же, что у карточек
   каталога: подъём и мягкая тень акцентного тона. */
.tpl-card {
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  height: 100%; padding: 14px; text-align: left;
  border: 1px solid var(--line); border-radius: var(--r-xl);
  background: var(--panel2); color: inherit; font: inherit; cursor: pointer;
  transition: transform var(--dur-2), box-shadow var(--dur-2), border-color var(--dur-2);
}
.tpl-card h3 { font-size: 13px; margin: 0; line-height: 1.3; }
.tpl-card p { font-size: 12px; margin: 0; color: var(--muted); line-height: 1.45; }
.tpl-card .badge { margin-top: auto; display: inline-flex; }
.tpl-card:hover {
  transform: translateY(-2px);
  border-color: rgb(var(--primary-rgb) / .55);
  box-shadow: 0 8px 22px -10px rgb(var(--primary-rgb) / .45);
}
.tpl-card:active { transform: translateY(0); }
@media (prefers-reduced-motion: reduce) {
  .tpl-card { transition: none; }
  .tpl-card:hover { transform: none; }
}

.item--catalog .item-top h3 {
  min-height: calc(2 * 1.3em);
  line-height: 1.3;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* Ряд действий — всегда у нижнего края карточки. */
.item--catalog > .no-print:last-child { margin-top: auto; }

/* Верх карточки сеткой: название | цена первой строкой, описание и бейджи во
   всю ширину. Правила выше для .item-top > div:first-child сюда не доходят —
   обёртки у названия больше нет. */
.item--catalog > .item-top.cat-card-top {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: 0 12px; align-items: start; align-content: start;
}
.cat-card-top > h3 { grid-column: 1; grid-row: 1; margin: 0; }
.cat-card-top > p,
.cat-card-top > .badges { grid-column: 1 / -1; }
.item--catalog .cat-card-top > .price-editor {
  grid-column: 2; grid-row: 1;
  display: flex; flex-direction: column; align-items: flex-end; gap: 0;
  width: 118px; text-align: right;
}
.cat-price { display: flex; align-items: center; justify-content: flex-end; gap: 4px; width: 100%; cursor: text; }
/* Поле цены — высотой со строку названия (32px, а не 43): иначе первая строка
   сетки выходила 58px при названии в 44, и между названием и описанием
   зияла пустота. На пальце поле 44px — минимальная цель касания. */
.item--catalog .cat-price .catalog-price-input {
  flex: 1 1 auto; min-width: 0; width: 100%; height: 32px; min-height: 0;
  padding: 2px 4px; text-align: right;
}
.cat-price-cur { font-size: 15px; font-weight: 700; color: var(--muted); }
.cat-price-unit { padding-right: 4px; font-size: 12px; white-space: nowrap; }
.item--catalog .cat-card-top > p { margin-top: 6px; }
/* Своя позиция: метка «Своя» в цвете акцента и подсказка вместо пустого
   описания — строка на месте, карточка той же высоты, что соседние. */
.cat-own-pill { display: inline-flex; align-items: center; gap: 4px; color: var(--primary-on-tint); background: rgb(var(--primary-rgb) / .12); border-color: rgb(var(--primary-rgb) / .3); }
.item--catalog .cat-card-top > p.cat-desc-empty { color: var(--muted); font-style: italic; opacity: .8; }
@media (hover: none) and (pointer: coarse) {
  .item--catalog .cat-price .catalog-price-input { height: 44px; }
}
@media (max-width: 640px) {
  /* 100px вмещают «1 200 000 ₽»; при 118 названию оставалось две строки по
     слову («Аренда / простой…»). */
  .item--catalog .cat-card-top > .price-editor { width: 100px; }
  /* На телефоне раздел уже назван кнопкой «Раздел» над поиском — от шапки
     остаются пояснение и число позиций, одной строкой. */
  .catalog-section-head:not(.pkg-group-header) .catalog-section-ico,
  .catalog-section-head:not(.pkg-group-header) h2 { display: none; }
  .catalog-section-head:not(.pkg-group-header) { margin-bottom: 10px; }
}
/* Шапка раздела, где из действий только «⋮»: на телефоне .section-title встаёт
   столбцом, и одинокая кнопка занимала под заголовком целую строку (60px до
   первого дела). Одна кнопка помещается рядом с заголовком. */
@media (max-width: 768px) {
  .section-title:has(> .toolbar > .deal-card-menu-wrap:only-child),
  .section-title.section-title--inline {
    flex-direction: row; align-items: flex-start; justify-content: space-between;
  }
  .section-title--inline > .toolbar { flex: 0 0 auto; }
}
.item--catalog .item-top p {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* Наведение: карточка чуть приподнимается, рамка берёт акцентный цвет, под ней
   мягкая тень того же тона. Не заливка — заливка спорит с ценой и бейджами,
   а тень поднимает карточку над списком и сразу показывает, что она кликабельна. */
.item--catalog {
  transition: transform var(--dur-2), box-shadow var(--dur-2), border-color var(--dur-2);
}
.item--catalog:hover {
  transform: translateY(-2px);
  border-color: rgb(var(--primary-rgb) / .55);
  box-shadow: 0 8px 22px -10px rgb(var(--primary-rgb) / .45);
}
.item--catalog:active { transform: translateY(0); }
/* Выбранной карточке подъём не нужен: она и так выделена рамкой и фоном, а
   прыжок под курсором мешает целиться в «−/+». */
.item--catalog.selected:hover { transform: none; }
@media (prefers-reduced-motion: reduce) {
  .item--catalog { transition: none; }
  .item--catalog:hover { transform: none; }
}

/* Карточка каталога на телефоне: цена остаётся В ОДНОЙ СТРОКЕ с названием, а не
   уезжает отдельным блоком под описание. Замер до правки: карточка 250px, на
   экран влезало 3,4 услуги — половину высоты занимали поле цены во всю ширину и
   подпись под ним. Строки сметы это правило НЕ трогает: у них свой класс. */
@media (max-width: 640px) {
  .item--catalog .item-top { flex-direction: row; align-items: flex-start; gap: 10px; }
  .item--catalog .item-top > div:first-child { flex: 1 1 auto; min-width: 0; }
  .item--catalog .price-editor {
    flex: 0 0 118px;
    text-align: right;
  }
  .item--catalog .catalog-price-input { width: 100%; text-align: right; }
  /* Описание — две строки. Оно помогает выбрать услугу, но третья и четвёртая
     строки уже не решают: полный текст виден в карточке позиции по нажатию.
     Побочно карточки становятся одной высоты, и список читается ровнее. */
  .item--catalog .item-top p {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
}

/* ── «Финансы»: итог за период и движение денег ─────────────────────────
   Карточка под полосой периода: четыре числа и график «вверх — пришло, вниз —
   ушло». Раньше итог был серой строкой мелким шрифтом в углу полосы
   (владелец 30.09.2026: «доделываем финансы с анимацией и графикой»). */
.fin-period {
  margin: 0 0 14px; padding: 16px 18px 12px;
  border: 1px solid var(--line); border-radius: var(--r-xl); background: var(--panel2);
}
.fin-period-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px 20px; }
.fin-period-stat { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.fin-period-lbl {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11.5px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .04em;
}
.fin-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.fin-dot.is-inc { background: var(--green); }
.fin-dot.is-exp { background: var(--red); }
.fin-period-num {
  font-family: var(--font-display); font-size: 22px; font-weight: 700; line-height: 1.1;
  font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.fin-period-num.is-inc, .fin-period-num.is-pos { color: var(--text-success); }
.fin-period-num.is-exp { color: var(--text-danger); }
.fin-period-chart { margin-top: 14px; }
.fin-flow { display: block; width: 100%; height: 120px; }
.fin-flow-base { stroke: var(--line); stroke-width: 1; }
.fin-flow-hit { fill: rgb(var(--primary-rgb) / .08); opacity: 0; transition: opacity var(--dur-1); }
.fin-flow-col { cursor: pointer; }
.fin-flow-col:hover .fin-flow-hit { opacity: 1; }
.fin-flow-inc { fill: url(#finFlowInc); transition: filter var(--dur-1); }
.fin-flow-exp { fill: url(#finFlowExp); transition: filter var(--dur-1); }
.fin-flow-col:hover .fin-flow-inc { filter: brightness(1.15); }
.fin-flow-col:hover .fin-flow-exp { filter: brightness(1.2); }
.fin-flow-axis { position: relative; height: 16px; margin-top: 6px; }
.fin-flow-axis span {
  position: absolute; top: 0; transform: translateX(-50%); white-space: nowrap;
  font-size: 11px; font-weight: 600; color: var(--muted); font-variant-numeric: tabular-nums;
}
.fin-period-hint { margin-top: 6px; font-size: 11.5px; color: var(--muted); }
@media (max-width: 760px) {
  .fin-period { padding: 14px 14px 10px; }
  .fin-period-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fin-period-num { font-size: 19px; }
}

/* ── «Финансы»: движение ─────────────────────────────────────────────────
   При заходе в раздел — один раз (класс ставит render() только при смене
   вида): плитки волной, суммы набегают (_countUp), линии прорисовываются,
   столбики растут, строки проявляются. При смене вкладки — мягкий въезд её
   содержимого. Строки только проявляются, без сдвига: сдвинутая строка под
   курсором ловила бы клик не той операции. */
#appContent.fin-enter .fin-summary-grid > .fin-card { animation: homeRiseSoft .6s var(--ease-out) both; }
#appContent.fin-enter .fin-summary-grid > .fin-card:nth-child(2) { animation-delay: 50ms; }
#appContent.fin-enter .fin-summary-grid > .fin-card:nth-child(3) { animation-delay: 100ms; }
#appContent.fin-enter .fin-summary-grid > .fin-card:nth-child(4) { animation-delay: 150ms; }
#appContent.fin-enter .fin-summary-grid > .fin-card:nth-child(5) { animation-delay: 200ms; }
#appContent.fin-enter .fin-card .spark-line { stroke-dasharray: 1; animation: sparkDraw 1s var(--ease-out) .3s both; }
#appContent.fin-enter .fin-card .spark-area { animation: homeFade .8s ease .6s both; }
#appContent.fin-enter .fin-card .spark-dot { transform-box: fill-box; transform-origin: center; animation: dotPop .5s var(--ease-spring) 1.1s both; }
#appContent.fin-enter .spark-gauge-fill { animation: gaugeDraw 1.15s var(--ease-out) .3s both; }
#appContent.fin-enter .fin-period { animation: homeRiseSoft .6s var(--ease-out) .18s both; }
#appContent.fin-enter .fin-flow-inc,
#appContent.fin-tab-switch .fin-flow-inc {
  transform-box: fill-box; transform-origin: 50% 100%;
  animation: barGrow .7s var(--ease-out) both; animation-delay: calc(260ms + var(--i, 0) * 18ms);
}
#appContent.fin-enter .fin-flow-exp,
#appContent.fin-tab-switch .fin-flow-exp {
  transform-box: fill-box; transform-origin: 50% 0;
  animation: barGrow .7s var(--ease-out) both; animation-delay: calc(320ms + var(--i, 0) * 18ms);
}
#appContent.fin-enter .fin-table--ops tbody tr { animation: homeFade .45s ease both; animation-delay: calc(300ms + var(--r, 0) * 28ms); }
#appContent.fin-enter .db-bar,
#appContent.fin-tab-switch .db-bar {
  transform-box: fill-box; transform-origin: 50% 100%;
  animation: barGrow .8s var(--ease-out) both; animation-delay: calc(200ms + var(--i, 0) * 45ms);
}
#appContent.fin-enter .rank-fill,
#appContent.fin-tab-switch .rank-fill { transform-origin: left center; animation: barFillX .9s var(--ease-out) .2s both; }
#appContent.fin-tab-switch .fin-subtab-bar ~ * { animation: finTabIn .38s var(--ease-out) both; }
@keyframes finTabIn { from { opacity: 0; transform: translateY(6px); } }
/* Уменьшенное движение: классы fin-enter / fin-tab-switch render() не ставит вовсе. */

/* ── Смета: значок раздела, действия в строке суммы, движение ───────────
   30.09.2026 (владелец: «делай как думаешь будет лучше и красиво с иконками»).
   Действия («В опции», копия, удалить) — значками рядом с суммой, а не
   невидимой полосой в 52px под каждой позицией. */
.item-top .line-ico { flex: 0 0 auto; margin-top: 2px; }
/* Колонка суммы на компьютере шириной 190px: значки в потоке выталкивали
   сумму и стрелку за край карточки. Поэтому они стоят слева от суммы поверх
   пустого места строки (ряд суммы ужат до содержимого), а на телефоне, где
   колонка во всю ширину, — обычным рядом. */
.price-editor > div:first-child { position: relative; justify-self: end; }
.line-quick {
  position: absolute; right: calc(100% + 6px); top: 50%; transform: translateY(-50%);
  display: inline-flex; align-items: center; gap: 4px;
  opacity: 0; transition: opacity var(--dur-1);
}
@media (max-width: 768px) {
  .line-quick { position: static; transform: none; margin-right: 2px; }
}
.item:hover .line-quick, .item:focus-within .line-quick { opacity: 1; }
@media (hover: none) { .line-quick { opacity: 1; } }
.est-head-total {
  font-family: var(--font-display); font-size: 22px; font-weight: 700; line-height: 1.1;
  font-variant-numeric: tabular-nums; color: var(--text);
}
@keyframes estOpen { from { opacity: 0; transform: translateY(-6px); } }
.item.is-leaving { pointer-events: none; }
.bill-mode-btn.is-active.is-fresh { animation: segIn .32s var(--ease-spring) both; }
@keyframes segIn { from { transform: scale(.86); opacity: .5; } }
/* «Монтаж» сводкой: строка выбранного, поля — по «Изменить». */
.calc-fold > .calc-fold-sum {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  list-style: none; cursor: pointer; margin: -2px 0;
}
.calc-fold > .calc-fold-sum::-webkit-details-marker { display: none; }
.calc-fold[open] > .calc-fold-sum { margin-bottom: 12px; }
.calc-fold-title { display: inline-flex; align-items: center; gap: 6px; font-size: 14px; font-weight: 800; color: var(--text); }
.calc-fold-title svg { color: var(--green); }
.calc-fold-chips { display: flex; flex-wrap: wrap; gap: 6px; flex: 1 1 260px; min-width: 0; }
.calc-fold-chips span {
  padding: 3px 9px; border-radius: var(--r-pill); font-size: 12px; font-weight: 650; white-space: nowrap;
  color: var(--muted); background: var(--panel); border: 1px solid var(--line);
}
.calc-fold[open] .calc-fold-chips { display: none; }
.calc-fold-edit {
  display: inline-flex; align-items: center; gap: 4px; margin-left: auto;
  font-size: 12px; font-weight: 700; color: var(--primary-text);
}
.calc-fold-edit svg { transition: transform var(--dur-2); transform: rotate(90deg); }
.calc-fold[open] .calc-fold-edit svg { transform: rotate(-90deg); }
.calc-fold .is-open, .calc-fold[open] .is-closed { display: none; }
.calc-fold[open] .is-open { display: inline; }
.calc-fold > .calc-fold-sum:hover .calc-fold-edit { text-decoration: underline; }
.calc-fold[open] > :not(summary) { animation: estOpen .3s var(--ease-out) both; }
/* Поля строки сметы — одного кегля и высоты. Замер 30.09.2026: поля ввода
   16px / 44px, выпадающие списки рядом — 13px / 37px; строка читалась
   «прыгающей». На телефоне ввод остаётся 16px: меньше — и iPhone приближает
   страницу при фокусе. «Ответственный» делил колонку с «+» и обрезался до
   «— не назна…» — ему две колонки. */
@media (min-width: 769px) {
  .item .line-details .field input:not([type="checkbox"]):not([type="radio"]),
  .item .line-details .field .uu-select-btn {
    font-size: 14px; min-height: 40px; height: 40px;
  }
  .item .line-details .field input:not([type="checkbox"]):not([type="radio"]) { padding-top: 8px; padding-bottom: 8px; }
  .item .line-details .field input[type="number"] { font-variant-numeric: tabular-nums; }
  .item .line-details .grid > .field:has(.catalog-action-btn) { grid-column: span 2; }
}
/* Полоса над сметой: слева главное действие (+ Услуги) и сумма там, где
   «Итогов» рядом нет; справа настройки сметы — позиции, налог, скидка,
   «свернуть всё» значком. */
.est-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 10px 16px; flex-wrap: wrap;
  margin: 0 0 10px; padding: 10px 14px;
  background: var(--panel2); border: 1px solid var(--line); border-radius: var(--r-lg);
}
.est-bar.is-standalone { margin-top: 18px; }
.est-bar-total { min-width: 0; }
.est-bar-sub { font-size: 12px; color: var(--muted); margin-top: 1px; }
.est-bar-icon { width: 34px; padding-left: 0; padding-right: 0; justify-content: center; }
/* Сумма в полосе — третья копия, когда «Итоги сметы» стоят рядом (>1160px). */
@media (min-width: 1161px) {
  body:not(.client-mode) .layout:has(> .summary) .est-bar-total { display: none; }
}
/* Полоса заметнее, добавление — плитками со значком и подсказкой, ряд
   «Сразу в раздел» (владелец 30.09.2026: «+услуги и + пакет не понятно»). */
.est-bar {
  padding: 12px 14px;
  border-color: rgb(var(--primary-rgb) / .25);
  background: linear-gradient(135deg, rgb(var(--primary-rgb) / .09), transparent 60%), var(--panel2);
}
/* «Итоги сметы»: скидка и налог — строками, которые правятся на месте
   (владелец 30.09.2026). Порядок как в корзине: этапы → скидка → налог → итого. */
.summary-adjust { display: flex; flex-direction: column; gap: 6px; margin: 12px 0 14px; }
.summary-adjust .estimate-discount { display: flex; flex-direction: column; gap: 6px; }
.summary-adjust-row {
  display: flex; align-items: center; gap: 8px; width: 100%; min-height: 42px;
  padding: 6px 8px 6px 12px; border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--panel); color: var(--text); font: inherit; font-size: 14px; text-align: left;
}
button.summary-adjust-row { cursor: pointer; transition: border-color var(--dur-1), background var(--dur-1); }
button.summary-adjust-row:hover { border-color: rgb(var(--primary-rgb) / .45); }
.summary-adjust-lbl { display: inline-flex; align-items: center; gap: 7px; margin-right: auto; font-weight: 600; }
.summary-adjust-lbl svg { color: var(--muted); }
.estimate-discount-btn.is-set .summary-adjust-lbl svg { color: var(--text-success); }
.summary-adjust-add { display: inline-flex; align-items: center; gap: 4px; padding-right: 4px; font-size: 12.5px; font-weight: 700; color: var(--primary-text); }
.summary-adjust-row strong { font-family: var(--font-display); font-weight: 700; font-variant-numeric: tabular-nums; padding-right: 4px; }
.summary-tax-row select, .summary-tax-row .uu-select-btn {
  width: auto; max-width: 150px; min-height: 30px; height: 30px; padding: 3px 28px 3px 10px;
  font-size: 12.5px; border-radius: var(--r-sm);
}
/* В колонке «Итогов» окошко раскрывается под строкой, а не поверх налога и итога. */
.summary-adjust .estimate-discount-pop { position: static; width: auto; box-shadow: none; background: var(--panel2); animation: estOpen .24s var(--ease-out) both; }
@media (hover: none) and (pointer: coarse) {
  .summary-adjust-row { min-height: 48px; }
  .summary-tax-row select, .summary-tax-row .uu-select-btn { min-height: 44px; height: 44px; }
}
/* ── Тур: первый слайд — живая сцена (renderWelcomeScene) ────────────────
   Базовые стили = ФИНАЛЬНЫЙ кадр. Анимации живут только под .obx.is-play и
   прячут элементы в «from»; без is-play (повторный показ, уменьшенное
   движение) сцена стоит на итоге. Язык движения один: вход
   cubic-bezier(.16,1,.3,1), перемещение (.65,0,.35,1), без отскоков. */
.ob-mock.is-scene { position: relative; background: linear-gradient(160deg, rgb(var(--primary-rgb) / .16), rgb(var(--primary-rgb) / .03) 55%), var(--panel2); }
.obx {
  --obx-in: cubic-bezier(.16, 1, .3, 1); --obx-move: cubic-bezier(.65, 0, .35, 1);
  position: relative; height: 100%; container-type: size;
  display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr); grid-template-rows: minmax(0, 1fr) auto;
  gap: 14px 56px; padding: 20px 24px 16px; box-sizing: border-box;
}
.obx-est, .obx-client {
  display: flex; flex-direction: column; gap: 7px; min-width: 0;
  padding: 14px; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--panel);
  box-shadow: 0 18px 40px -26px rgb(0 0 0 / .6);
}
.obx-card-head { display: flex; align-items: center; gap: 6px; margin-bottom: 2px; font-size: 12px; font-weight: 700; color: var(--muted); }
.obx-line { display: flex; align-items: center; gap: 9px; font-size: 13px; }
.obx-line > span:not([style]) { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.obx-line b, .obx-total b { font-family: var(--font-display); font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.obx-total { display: flex; align-items: baseline; justify-content: space-between; margin-top: auto; padding-top: 8px; border-top: 1px dashed var(--line); font-size: 13px; color: var(--muted); }
.obx-total b { font-size: 22px; color: var(--text); }
.obx-send {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-height: 34px;
  border-radius: var(--r-md); background: var(--primary); color: var(--on-color); font-size: 13px; font-weight: 700;
  box-shadow: 0 10px 24px -12px rgb(var(--primary-rgb) / .8);
}
.obx-link {
  position: absolute; left: 34%; top: 66%; z-index: 2; opacity: 0;
  display: inline-flex; align-items: center; gap: 5px; padding: 5px 10px; border-radius: var(--r-pill);
  background: var(--primary); color: var(--on-color); font-size: 11.5px; font-weight: 700; white-space: nowrap;
  box-shadow: 0 8px 22px -8px rgb(var(--primary-rgb) / .9);
}
.obx-client { border-color: rgb(var(--primary-rgb) / .45); }
.obx-step {
  display: flex; align-items: center; gap: 8px; min-height: 32px; padding: 0 10px;
  border-radius: var(--r-md); font-size: 12.5px; font-weight: 650;
  color: var(--text-success); background: color-mix(in srgb, var(--green) 10%, transparent);
}
.obx-step.s3 { margin-top: auto; min-height: 40px; color: var(--on-color); background: var(--green); font-weight: 750; }
.obx-pipe {
  grid-column: 1 / -1; position: relative;
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 0;
  padding: 4px; border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--panel);
}
.obx-stage {
  position: relative; z-index: 1; padding: 7px 4px; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font-size: 11.5px; font-weight: 650; color: var(--muted);
}
.obx-stage.is-done, .obx-stage.st1, .obx-stage.st2 { color: var(--text-success); }
.obx-stage.st3 { color: var(--on-color); }
.obx-kp {
  display: flex; align-items: center; gap: 8px; min-height: 34px; padding: 0 10px;
  border: 1px dashed rgb(var(--primary-rgb) / .45); border-radius: var(--r-md);
  background: rgb(var(--primary-rgb) / .08); font-size: 12.5px; font-weight: 650;
}
.obx-kp span { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.obx-kp b { font-family: var(--font-display); font-weight: 700; font-variant-numeric: tabular-nums; }
.obx-kp svg { color: var(--primary-text); }
.obx-cursor {
  position: absolute; top: 4px; bottom: 4px; left: 4px; width: calc((100% - 8px) / 5);
  border-radius: var(--r-pill); background: var(--primary); transform: translateX(300%);
  box-shadow: 0 6px 18px -8px rgb(var(--primary-rgb) / .9);
}
.obx-replay {
  /* В строке заголовка карточки «Клиент» — там справа пусто, а в углу сцены
     кнопка наезжала на рамку карточки. */
  position: absolute; right: 34px; top: 29px; z-index: 3;
  display: inline-flex; align-items: center; gap: 5px; min-height: 26px; padding: 2px 10px;
  border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--panel);
  color: var(--muted); font: inherit; font-size: 11.5px; font-weight: 650; cursor: pointer;
  transition: color var(--dur-1), border-color var(--dur-1);
}
.obx-replay:hover { color: var(--text); border-color: rgb(var(--primary-rgb) / .5); }

/* Таймлайн (с): смета 0–2 · отправка 2 · ссылка 2,2–3 · клиент 3–4,4 · воронка 4,7–5,4 */
.obx.is-play .obx-est { animation: obxRise .6s var(--obx-in) both; }
.obx.is-play .obx-client { animation: obxRise .6s var(--obx-in) .15s both, obxGlow 1s ease 3s both; }
.obx.is-play .obx-line { animation: obxSlide .5s var(--obx-in) both; animation-delay: calc(.4s + var(--i) * .14s); }
.obx.is-play .obx-total { animation: obxFade .4s ease .85s both; }
.obx.is-play .obx-send { animation: obxFade .4s ease 1.1s both, obxPress .45s var(--obx-move) 2s both; }
.obx.is-play .obx-link { animation: obxFly .85s var(--obx-move) 2.2s both; }
.obx.is-play .obx-step { animation: obxSlide .45s var(--obx-in) both; }
.obx.is-play .obx-step.s1 { animation-delay: 3.05s; }
.obx.is-play .obx-step.s2 { animation-delay: 3.55s; }
.obx.is-play .obx-step.s3 { animation: obxPay .6s var(--obx-in) 4.05s both; }
.obx.is-play .obx-pipe { animation: obxFade .5s ease .3s both; }
.obx.is-play .obx-kp { animation: obxRise .5s var(--obx-in) 3s both; }
.obx.is-play .obx-cursor { animation: obxCursor 5.4s linear both; }
.obx.is-play .obx-stage.st1 { animation: obxSt1 5.4s linear both; }
.obx.is-play .obx-stage.st2 { animation: obxSt2 5.4s linear both; }
.obx.is-play .obx-stage.st3 { animation: obxSt3 5.4s linear both; }
.obx.is-play ~ .obx-replay { animation: obxFade .4s ease 5.6s both; }

@keyframes obxRise { from { opacity: 0; transform: translateY(14px); } }
@keyframes obxSlide { from { opacity: 0; transform: translateX(-10px); } }
@keyframes obxFade { from { opacity: 0; } }
@keyframes obxPress { 0%, 100% { transform: none; } 45% { transform: scale(.94); } }
@keyframes obxGlow { 0% { box-shadow: 0 0 0 0 rgb(var(--primary-rgb) / .55); } 100% { box-shadow: 0 0 0 14px rgb(var(--primary-rgb) / 0); } }
/* Ссылка летит дугой от кнопки к карточке клиента: доли от размера сцены (cqw/cqh). */
@keyframes obxFly {
  0%   { opacity: 0; transform: translate(0, 0) scale(.8); }
  12%  { opacity: 1; transform: translate(2cqw, -3cqh) scale(1); }
  55%  { opacity: 1; transform: translate(16cqw, -34cqh); }
  88%  { opacity: 1; transform: translate(26cqw, -44cqh) scale(1); }
  100% { opacity: 0; transform: translate(28cqw, -46cqh) scale(.7); }
}
@keyframes obxPay { from { opacity: 0; transform: translateY(8px); color: var(--text-success); background: color-mix(in srgb, var(--green) 10%, transparent); } }
@keyframes obxCursor {
  0%, 42% { transform: translateX(100%); animation-timing-function: cubic-bezier(.65, 0, .35, 1); }
  48%, 87% { transform: translateX(200%); animation-timing-function: cubic-bezier(.65, 0, .35, 1); }
  98%, 100% { transform: translateX(300%); }
}
@keyframes obxSt1 { 0%, 43% { color: var(--on-color); } 47%, 100% { color: var(--text-success); } }
@keyframes obxSt2 { 0%, 43% { color: var(--muted); } 47%, 88% { color: var(--on-color); } 92%, 100% { color: var(--text-success); } }
@keyframes obxSt3 { 0%, 92% { color: var(--muted); } 96%, 100% { color: var(--on-color); } }

@media (prefers-reduced-motion: reduce) {
  .obx.is-play *, .obx.is-play, .obx.is-play ~ .obx-replay { animation: none; }
}
/* Телефон: сцена в столбик, без полёта ссылки — последовательность и так видна. */
@media (max-width: 600px) {
  .ob-mock.is-scene { aspect-ratio: auto; max-height: none; }
  .obx-replay { position: static; margin: 0 14px 14px auto; display: flex; width: max-content; }
  .obx { container-type: normal; grid-template-columns: 1fr; gap: 10px; padding: 14px; }
  .obx-link { display: none; }
  /* Полные «КП отправлено» / «Предоплата» на 390px резались до «КП отпр…» —
     на телефоне короткие подписи из data-short. */
  .obx-stage[data-short] { font-size: 0; }
  .obx-stage[data-short]::after { content: attr(data-short); font-size: 11px; }
  .obx-stage { font-size: 11px; }
}

/* Поиск по смете — в полосе над сметой. */
.est-search {
  display: inline-flex; align-items: center; gap: 6px; min-height: 34px; padding: 0 10px; margin: 0;
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--panel); color: var(--muted);
  transition: border-color var(--dur-1);
}
.est-search:focus-within { border-color: rgb(var(--primary-rgb) / .6); }
.est-search input { width: 150px; min-height: 0; height: 32px; padding: 0; border: 0; background: transparent; font-size: 13px; color: var(--text); box-shadow: none; }
.est-search input:focus { outline: none; box-shadow: none; }
.est-search input:focus-visible { box-shadow: 0 2px 0 var(--primary); }
.est-search-note {
  display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
  margin: 0 0 10px; padding: 8px 12px; border-radius: var(--r-md);
  background: rgb(var(--primary-rgb) / .08); font-size: 13px; font-weight: 600;
}
@media (max-width: 560px) { .est-search { flex: 1 1 auto; } .est-search input { width: 100%; } }

/* Свёрнутый этап называет состав. */
.stage-peek {
  margin-top: 4px; font-size: 12px; color: var(--muted); line-height: 1.4;
  display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden;
}
.stage-peek span { color: var(--hint); }

/* «Итоги»: аванс под итогом; строки одного размера (Расходы/Прибыль были крупнее этапов). */
.summary-advance { display: block; margin-top: 4px; font-size: 12.5px; font-weight: 600; color: var(--muted); font-variant-numeric: tabular-nums; }
.summary .summary-line { font-size: 14px; }
/* Полоса одной строкой: поиск · «Каталог» с меню разделов · «Пакет» · свернуть всё. */
.est-search { flex: 1 1 220px; }
.est-search input { width: 100%; }
.est-bar-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-left: auto; }
.est-split { position: relative; display: inline-flex; }
.est-split-main { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.est-split-more > summary {
  list-style: none; width: 30px; margin-left: -1px; padding-left: 0; padding-right: 0; justify-content: center;
  border-top-left-radius: 0; border-bottom-left-radius: 0; cursor: pointer;
}
.est-split-more > summary::-webkit-details-marker { display: none; }
.est-split-more > summary svg { transform: rotate(90deg); transition: transform var(--dur-2); }
.est-split-more[open] > summary svg { transform: rotate(-90deg); }
.est-split-menu {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: var(--z-dropdown);
  width: 290px; padding: 6px; display: flex; flex-direction: column; gap: 2px;
  background: var(--bg2); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--elev-3);
  animation: estOpen .2s var(--ease-out) both;
}
.est-split-menu button {
  display: flex; align-items: flex-start; gap: 10px; min-height: 44px; padding: 8px 10px;
  border: 0; border-radius: var(--r-md); background: transparent; color: var(--text); font: inherit; text-align: left; cursor: pointer;
  transition: background var(--dur-1);
}
.est-split-menu button:hover, .est-split-menu button:focus-visible { background: color-mix(in srgb, var(--c) 12%, transparent); }
.est-split-menu button svg { flex: 0 0 auto; margin-top: 2px; color: var(--c); }
.est-split-menu b { display: block; font-size: 13px; font-weight: 700; }
.est-split-menu small { display: block; margin-top: 1px; font-size: 11.5px; color: var(--muted); }
@media (hover: none) and (pointer: coarse) { .est-split-more > summary { width: 44px; } }
@media (max-width: 560px) { .est-bar-actions { margin-left: 0; } .est-split-menu { left: 0; right: auto; } }

/* «Скидка» и «Налог» — обычными строками «Итогов», без рамок-полей: тонкая
   линия снизу, как у соседних строк; нажимаемость — подсветкой при наведении. */
.summary-adjust { gap: 0; margin: 2px 0 12px; }
.summary-adjust-row {
  min-height: 44px; padding: 6px 8px; margin: 0 -8px; width: calc(100% + 16px);
  border: 0; border-radius: var(--r-sm); background: transparent;
  box-shadow: inset 0 -1px 0 var(--line);
}
button.summary-adjust-row:hover { background: rgb(var(--primary-rgb) / .06); border-color: transparent; }
.summary-adjust-lbl { font-weight: 500; }
.estimate-discount-btn.is-set { background: transparent; border-color: transparent; }
.estimate-discount-btn.is-set:hover { background: color-mix(in srgb, var(--green) 8%, transparent); }

/* ── Тур: сцены слайдов 2–6 (ONBOARD_SCENES) ─────────────────────────────
   Тот же язык, что у первого слайда: база = финальный кадр, движение только
   под .obx.is-play. Общая карточка — .obx-card. */
.obx-card {
  display: flex; flex-direction: column; gap: 7px; min-width: 0;
  padding: 14px; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--panel);
  box-shadow: 0 18px 40px -26px rgb(0 0 0 / .6);
}
.obx-av {
  display: inline-grid; place-items: center; flex: 0 0 auto; width: 26px; height: 26px; border-radius: 50%;
  color: var(--on-color); font-size: 10.5px; font-weight: 800;
}
.obx-av.is-ghost { background: transparent; border: 1px dashed var(--line); color: var(--muted); }
.obx.is-play .obx-card { animation: obxRise .55s var(--obx-in) both; animation-delay: calc(var(--i, 0) * .1s); }

/* 2. Воронка: новая сделка переезжает по колонкам до «Предоплаты». */
.obx--kanban { grid-template-columns: 1fr; grid-template-rows: auto minmax(0, 1fr); gap: 12px; }
.obx-kb-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.obx-newbtn {
  display: inline-flex; align-items: center; gap: 5px; padding: 6px 12px; border-radius: var(--r-md);
  background: var(--primary); color: var(--on-color); font-size: 12px; font-weight: 700;
}
.obx-kb { position: relative; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; min-height: 0; }
.obx-kb-col { display: flex; flex-direction: column; gap: 7px; min-width: 0; padding: 8px; border-radius: var(--r-lg); background: var(--panel); border: 1px solid var(--line); }
.obx-kb-h { display: flex; align-items: center; gap: 6px; font-size: 11.5px; font-weight: 700; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.obx-kb-h i { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; }
.obx-kb-slot { height: 48px; flex: 0 0 auto; }
.obx-kb-card, .obx-kb-mover {
  display: flex; flex-direction: column; justify-content: center; gap: 1px; height: 48px; padding: 0 10px;
  border-radius: var(--r-md); background: var(--panel2); border: 1px solid var(--line); min-width: 0;
}
.obx-kb-card b, .obx-kb-mover b { font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.obx-kb-card small, .obx-kb-mover small { font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
/* Путь карточки: слот под заголовком каждой колонки; шаг — ширина колонки + зазор. */
.obx-kb-mover {
  --kb-step: 28px; /* ширина колонки = ширина карточки + 2×(8 поле + 1 рамка) + 10 зазор */
  position: absolute; left: 9px; top: 32px; width: calc((100% - 30px) / 4 - 18px);
  border-color: rgb(var(--primary-rgb) / .55); background: rgb(var(--primary-rgb) / .14);
  transform: translateX(calc(3 * (100% + var(--kb-step))));
}
.obx-kb-done {
  position: absolute; right: 8px; top: 50%; margin-top: -9px; width: 18px; height: 18px; border-radius: 50%;
  display: grid; place-items: center; background: var(--green); color: var(--on-color);
}
.obx.is-play .obx-kb-col { animation: obxRise .5s var(--obx-in) both; animation-delay: calc(var(--i) * .08s); }
.obx.is-play .obx-kb-card { animation: obxFade .4s ease both; animation-delay: calc(.3s + var(--j) * .06s); }
.obx.is-play .obx-newbtn { animation: obxPress .45s var(--obx-move) .75s both; }
.obx.is-play .obx-kb-mover { animation: obxKbMove 3s linear 1s both; }
.obx.is-play .obx-kb-done { animation: obxPop .35s var(--obx-in) 3.95s both; }
@keyframes obxKbMove {
  0%   { opacity: 0; transform: translate(0, -10px) scale(.96); }
  9%   { opacity: 1; transform: translate(0, 0); animation-timing-function: cubic-bezier(.65, 0, .35, 1); }
  26%  { transform: translateX(0); animation-timing-function: cubic-bezier(.65, 0, .35, 1); }
  38%  { transform: translateX(calc(100% + var(--kb-step))); }
  52%  { transform: translateX(calc(100% + var(--kb-step))); animation-timing-function: cubic-bezier(.65, 0, .35, 1); }
  64%  { transform: translateX(calc(2 * (100% + var(--kb-step)))); }
  78%  { transform: translateX(calc(2 * (100% + var(--kb-step)))); animation-timing-function: cubic-bezier(.65, 0, .35, 1); }
  92%, 100% { opacity: 1; transform: translateX(calc(3 * (100% + var(--kb-step)))); }
}
@keyframes obxPop { from { opacity: 0; transform: scale(.4); } }

/* 3. Финансы: плитки набегают, операции въезжают, столбики растут. */
.obx--fin { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); gap: 12px 14px; }
.obx-kpis { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.obx-kpi {
  display: flex; flex-direction: column; gap: 3px; padding: 12px 14px; border-radius: var(--r-lg);
  background: var(--panel); border: 1px solid var(--line); box-shadow: inset 3px 0 0 var(--c);
}
.obx-kpi span { font-size: 11.5px; font-weight: 700; color: var(--muted); }
.obx-kpi b { font-family: var(--font-display); font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--c); white-space: nowrap; }
.obx-txr { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 30px; font-size: 12.5px; border-bottom: 1px solid var(--line); }
.obx-txr:last-child { border-bottom: 0; }
.obx-txr b { font-family: var(--font-display); font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.obx-txr b.is-in { color: var(--text-success); }
.obx-txr b.is-out { color: var(--text-danger); }
.obx-bars-row { flex: 1 1 auto; display: flex; align-items: flex-end; gap: 8px; min-height: 60px; }
.obx-bars-row span {
  flex: 1 1 0; height: var(--h); border-radius: var(--r-xs) var(--r-xs) 0 0;
  background: linear-gradient(180deg, var(--green), color-mix(in srgb, var(--green) 40%, transparent));
  transform-origin: 50% 100%;
}
.obx.is-play .obx-kpi { animation: obxRise .5s var(--obx-in) both; animation-delay: calc(var(--i) * .1s); }
.obx.is-play .obx-txr { animation: obxSlide .45s var(--obx-in) both; animation-delay: calc(1.4s + var(--i) * .25s); }
.obx.is-play .obx-bars-row span { animation: barGrow .7s var(--obx-in) both; animation-delay: calc(2.3s + var(--i) * .09s); }

/* 4. Команда: участники, исполнители у задач, отметки, «Синхронизировано». */
.obx--team { grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr); grid-template-rows: minmax(0, 1fr); gap: 14px; }
.obx-person { display: flex; align-items: center; gap: 10px; min-height: 38px; }
.obx-person b { display: block; font-size: 13px; }
.obx-person small { display: block; font-size: 11.5px; color: var(--muted); }
.obx-person.is-add { opacity: .75; }
.obx-sync {
  margin-left: auto; display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; border-radius: var(--r-pill);
  font-size: 11px; color: var(--text-success); background: color-mix(in srgb, var(--green) 12%, transparent);
}
.obx-task { display: flex; align-items: center; gap: 10px; min-height: 44px; padding: 0 10px; border-radius: var(--r-md); background: var(--panel2); }
.obx-cb {
  display: grid; place-items: center; flex: 0 0 auto; width: 18px; height: 18px; border-radius: var(--r-xs);
  border: 1.5px solid var(--line); color: var(--on-color);
}
.obx-task.is-done .obx-cb { background: var(--green); border-color: var(--green); }
.obx-task-t { flex: 1 1 auto; min-width: 0; }
.obx-task-t b { display: block; font-size: 12.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.obx-task-t small { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; color: var(--muted); }
.obx-task.is-done .obx-task-t b { color: var(--muted); text-decoration: line-through; }
.obx.is-play .obx-person { animation: obxSlide .45s var(--obx-in) both; animation-delay: calc(.3s + var(--i) * .15s); }
.obx.is-play .obx-task { animation: obxSlide .45s var(--obx-in) both; animation-delay: calc(.9s + var(--i) * .15s); }
.obx.is-play .obx-task .obx-av { animation: obxPop .35s var(--obx-in) both; animation-delay: calc(1.8s + var(--i) * .3s); }
.obx.is-play .obx-task.is-done .obx-cb { animation: obxCheck .3s ease both; animation-delay: calc(2.9s + var(--i) * .4s); }
.obx.is-play .obx-task.is-done .obx-task-t b { animation: obxStrike .3s ease both; animation-delay: calc(2.9s + var(--i) * .4s); }
.obx.is-play .obx-sync { animation: obxFade .4s ease 3.8s both; }
@keyframes obxCheck { from { background: transparent; border-color: var(--line); color: transparent; } }
@keyframes obxStrike { from { color: var(--text); text-decoration-color: transparent; } }

/* 5. Настройки: логотип, реквизиты «печатаются», договор сам подставляет данные. */
.obx--set { grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr); grid-template-rows: minmax(0, 1fr); gap: 14px; }
.obx-logo {
  display: grid; place-items: center; width: 44px; height: 44px; border-radius: var(--r-md);
  background: var(--primary); color: var(--on-color); font-family: var(--font-display); font-size: 22px; font-weight: 700;
  box-shadow: 0 8px 20px -10px rgb(var(--primary-rgb) / .9);
}
.obx-field { display: flex; flex-direction: column; gap: 2px; padding: 6px 10px; border-radius: var(--r-md); background: var(--panel2); }
.obx-field small { font-size: 10.5px; color: var(--muted); }
.obx-field b { font-size: 13px; white-space: nowrap; overflow: hidden; }
.obx-doc-l { font-size: 12.5px; line-height: 1.6; }
.obx-doc-sk { height: 7px; border-radius: var(--r-xs); background: var(--line); }
.obx-ph { position: relative; display: inline-block; }
.obx-ph em { font-style: normal; font-weight: 700; color: var(--primary-text); background: rgb(var(--primary-rgb) / .12); padding: 0 4px; border-radius: var(--r-xs); }
.obx-ph s { position: absolute; left: 0; top: 0; text-decoration: none; white-space: nowrap; color: var(--muted); font-family: ui-monospace, monospace; font-size: 11.5px; opacity: 0; }
.obx-stamp {
  margin-top: auto; align-self: flex-start; display: inline-flex; align-items: center; gap: 5px; padding: 5px 10px;
  border-radius: var(--r-pill); background: var(--green); color: var(--on-color); font-size: 12px; font-weight: 700;
}
.obx.is-play .obx-logo { animation: obxPop .45s var(--obx-in) .4s both; }
.obx.is-play .obx-field b { animation: obxType .6s steps(14, end) both; animation-delay: calc(.8s + var(--i) * .5s); }
.obx.is-play .obx-ph em { animation: obxFade .35s ease both; animation-delay: calc(2.6s + var(--i) * .35s); }
.obx.is-play .obx-ph s { animation: obxPhOut 2.95s linear both; animation-delay: calc(var(--i) * .35s); }
.obx.is-play .obx-stamp { animation: obxPop .4s var(--obx-in) 3.9s both; }
@keyframes obxType { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }
@keyframes obxPhOut { 0% { opacity: 0; } 20%, 85% { opacity: 1; } 100% { opacity: 0; } }

/* 6. Тарифы: три карточки, на «Стандарте» «Выбрать» → «Активно». */
.obx--plans { grid-template-columns: 1fr; grid-template-rows: minmax(0, 1fr) auto; gap: 12px; }
.obx-tiers { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; min-height: 0; }
.obx-tier {
  position: relative; display: flex; flex-direction: column; gap: 3px; padding: 16px 14px 14px;
  border: 1px solid var(--line); border-radius: var(--r-xl); background: var(--panel);
}
.obx-tier.is-rec { border-color: rgb(var(--primary-rgb) / .7); background: linear-gradient(180deg, rgb(var(--primary-rgb) / .14), transparent 60%), var(--panel); }
.obx-badge { position: absolute; top: -10px; left: 14px; padding: 2px 9px; border-radius: var(--r-pill); background: var(--primary); color: var(--on-color); font-size: 11px; font-weight: 800; }
.obx-tier-n { font-family: var(--font-display); font-size: 17px; }
.obx-tier > small { font-size: 11.5px; color: var(--muted); }
.obx-tier-p { margin: 8px 0 10px; font-family: var(--font-display); font-size: 24px; font-weight: 700; font-variant-numeric: tabular-nums; }
.obx-tier-p small { font-family: inherit; font-size: 12px; color: var(--muted); font-weight: 600; margin-left: 2px; }
.obx-tier-btn {
  position: relative; margin-top: auto; display: grid; place-items: center; min-height: 34px; border-radius: var(--r-md);
  border: 1px solid var(--line); font-size: 12.5px; font-weight: 700; color: var(--text); overflow: hidden;
}
.obx-tier.is-rec .obx-tier-btn { border-color: transparent; background: var(--green); color: var(--on-color); }
.obx-tier-btn i { font-style: normal; display: inline-flex; align-items: center; gap: 5px; grid-area: 1 / 1; }
.obx-tier-btn .is-pick { opacity: 0; }
.obx-paychips { display: flex; justify-content: center; gap: 8px; flex-wrap: wrap; }
.obx-paychips span {
  display: inline-flex; align-items: center; gap: 5px; padding: 5px 12px; border-radius: var(--r-pill);
  background: var(--panel); border: 1px solid var(--line); font-size: 12px; font-weight: 600; color: var(--muted);
}
.obx-paychips svg { color: var(--primary-text); }
.obx.is-play .obx-tier { animation: obxRise .55s var(--obx-in) both; animation-delay: calc(var(--i) * .12s); }
.obx.is-play .obx-tier.is-rec .obx-tier-btn { animation: obxPayBtn .5s ease 2.2s both, obxPress .45s var(--obx-move) 1.8s both; }
.obx.is-play .obx-tier-btn .is-pick { animation: obxPickOut .5s ease 2.2s both; }
.obx.is-play .obx-tier-btn .is-on { animation: obxFade .35s ease 2.45s both; }
.obx.is-play .obx-paychips span { animation: obxRise .45s var(--obx-in) both; animation-delay: calc(3s + var(--i) * .15s); }
@keyframes obxPayBtn { from { background: var(--primary); color: var(--on-color); } }
@keyframes obxPickOut { from { opacity: 1; } to { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  .obx.is-play .obx-card, .obx.is-play [class^="obx-"], .obx.is-play [class*=" obx-"] { animation: none; }
}
@media (max-width: 600px) {
  .obx--fin, .obx--team, .obx--set { grid-template-columns: 1fr; grid-template-rows: none; }
  .obx-kpis { grid-template-columns: 1fr; }
  .obx-tiers { grid-template-columns: 1fr; }
  .obx-kb { gap: 6px; }
  .obx-kb-col { padding: 6px; }
  .obx-kb-mover { --kb-step: 20px; left: 7px; top: 30px; width: calc((100% - 18px) / 4 - 14px); }
  .obx-kb-card b, .obx-kb-mover b { font-size: 10.5px; }
}

/* Карточки сцен — по содержимому, без пустого низа; отличия тарифов. */
.obx--team, .obx--set { align-items: start; }
.obx-kb { align-items: stretch; align-content: start; grid-auto-rows: min-content; } /* колонки — по самой высокой, без пустоты до низа */
.obx-tier-f { list-style: none; margin: 0 0 12px; padding: 0; display: flex; flex-direction: column; gap: 5px; }
.obx-tier-f li { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); }
.obx-tier-f li svg { flex: 0 0 auto; color: var(--c); }
.obx.is-play .obx-tier-f li { animation: obxFade .35s ease both; animation-delay: calc(.6s + var(--i) * .12s + var(--j) * .08s); }
/* «Итоги сметы» на ноутбуке уже: 360px оставляли смете слишком мало. */
@media (max-width: 1320px) and (min-width: 1161px) {
  .layout { grid-template-columns: minmax(0, 1fr) 320px; }
}

/* Лента этапов на «Проектах» — всегда один ряд на компьютере. Сетка «сколько
   влезет по 98px» на ноутбуке ставила в ряд 11 плиток из 12, и «Архив» уходил
   один на вторую строку (владелец 30.09.2026). Теперь плитки делят ширину
   поровну; длинное название сокращается, полное — в подсказке. Вид и порядок
   плиток прежние (две переделки ленты 28.09 владелец отклонил). */
@media (min-width: 641px) {
  .crm-home-funnel { grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
  .crm-home-funnel > .funnel-stage { min-width: 0; }
  .crm-home-funnel > .funnel-stage h3 { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .crm-home-funnel > .funnel-stage .fs-amount { white-space: nowrap; }
  /* Узкая плитка (ноутбук): сумму не режем многоточием — «1 742 10…» про деньги
     недопустимо; мельче кегль и поля. */
  .crm-home-funnel > .funnel-stage { container-type: inline-size; }
}
@media (min-width: 641px) and (max-width: 1400px) {
  .crm-home-funnel > .funnel-stage { padding-left: 7px; padding-right: 7px; }
}
@container (max-width: 90px) {
  .funnel-stage .fs-amount { font-size: 10.5px; letter-spacing: -.01em; }
  .funnel-stage h3 { font-size: 11px; }
}
/* Поля позиции сметы по карточке, а не по окну: при 1536 с колонкой «Сделки»
   смете остаётся ~580px, и четыре поля в ряд резались («Подгото…», дата
   «30.09.202»). Узкая карточка — поля по два в ряд. */
.estimate-stage article.item { container-type: inline-size; }
@container (max-width: 640px) {
  .line-details .grid.four { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* ── Каталог: подразделы чипами, значок раздела ─────────────────────── */
.catalog-subchips { display: flex; gap: 6px; flex-wrap: wrap; margin: -4px 0 14px; }
.catalog-subchip {
  display: inline-flex; align-items: center; gap: 6px; min-height: 30px; padding: 3px 11px;
  border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--panel);
  color: var(--text); font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer;
  transition: border-color var(--dur-1), background var(--dur-1), color var(--dur-1);
}
.catalog-subchip span { font-size: 11px; font-weight: 700; color: var(--muted); font-variant-numeric: tabular-nums; }
.catalog-subchip:hover { border-color: rgb(var(--primary-rgb) / .5); }
.catalog-subchip.is-on { background: rgb(var(--primary-rgb) / .14); border-color: rgb(var(--primary-rgb) / .6); color: var(--primary-text); }
.catalog-subchip.is-on span { color: var(--primary-text); }
@media (hover: none) and (pointer: coarse) { .catalog-subchip { min-height: 44px; } }
.summary-line-mult { font-size: 11.5px; font-weight: 700; color: var(--primary-text); font-variant-numeric: tabular-nums; }
.cat-card-ico { display: inline-flex; vertical-align: -2px; margin-right: 6px; }
/* Цена на карточке: «45 000 ₽» без дыры — поля и зазор перед знаком сжаты. */
.item--catalog .cat-price { gap: 2px; }
.item--catalog .cat-price .catalog-price-input { padding-right: 1px; }
/* ── Каталог: вид «списком», пустой поиск с созданием своей позиции ─────
   Список собран из той же карточки: позиция — одна строка «значок · название ·
   цена за единицу · действия»; описание — в подсказке (title) и в окне позиции. */
.catalog-view-switch { display: inline-flex; gap: 2px; padding: 2px; margin-left: auto; margin-right: 10px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--panel); }
.catalog-view-switch button {
  display: grid; place-items: center; width: 30px; height: 26px; border: 0; border-radius: var(--r-sm);
  background: transparent; color: var(--muted); cursor: pointer; transition: background var(--dur-1), color var(--dur-1);
}
.catalog-view-switch button:hover { color: var(--text); }
.catalog-view-switch button.is-on { background: rgb(var(--primary-rgb) / .16); color: var(--primary-text); }
@media (hover: none) and (pointer: coarse) { .catalog-view-switch button { width: 44px; height: 44px; } }
.catalog-grid.is-list { grid-template-columns: 1fr; gap: 6px; }
.catalog-grid.is-list > .item--catalog { display: flex; flex-direction: row; align-items: center; gap: 12px; padding: 8px 12px; min-height: 0; text-align: left; }
.catalog-grid.is-list .cat-card-top {
  flex: 1 1 auto; min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 0 12px;
}
.catalog-grid.is-list .cat-card-top h3 { margin: 0; min-height: 0; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block; -webkit-line-clamp: unset; }
.catalog-grid.is-list .cat-card-top > p, .catalog-grid.is-list .cat-card-top > .badges, .catalog-grid.is-list .cat-desc-empty { display: none; }
.catalog-grid.is-list .cat-card-top > .price-editor { display: flex; flex-direction: row; align-items: baseline; gap: 6px; width: auto; }
.catalog-grid.is-list .cat-price { width: 120px; }
.catalog-grid.is-list .cat-price-unit { min-width: 76px; text-align: left; }
/* !important — у ряда действий отступ задан в разметке (style), иначе не перебить. */
.catalog-grid.is-list > .item--catalog > .no-print { margin-top: 0 !important; flex: 0 0 auto; }
.catalog-empty-create {
  grid-column: 1 / -1; display: flex; flex-direction: column; align-items: center; gap: 8px; text-align: center;
  padding: 36px 20px; border: 1px dashed var(--line); border-radius: var(--r-xl); color: var(--muted);
}
.catalog-empty-create b { font-size: 15px; color: var(--text); }
.catalog-empty-create span { font-size: 13px; max-width: 360px; }
.catalog-empty-create .btn { margin-top: 6px; }
@media (max-width: 640px) {
  .catalog-grid.is-list > .item--catalog { flex-wrap: wrap; }
  .catalog-grid.is-list .cat-price-unit { display: none; }
}

/* Колонка разделов каталога — постоянной ширины. Она подстраивалась под
   содержимое: при добавлении позиции у раздела появлялась метка «✓1», колонка
   росла с 227 до 254px и сетка карточек уезжала вправо (владелец 30.09.2026:
   «пусть будет стабильно без сдвигов»). 264px вмещают «Постпродакшн» и оба счётчика. */
@media (min-width: 769px) {
  .catalog-body > .catalog-cat-sidebar { flex: 0 0 264px; width: 264px; min-width: 0; }
  .catalog-cat-sidebar .catalog-cat-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}
/* ── Календарь: сводка периода, ячейки, список по дням (30.09.2026) ───── */
.cal-summary { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; margin: 4px 0 14px; }
.cal-sum-tile {
  --c: var(--primary);
  display: flex; flex-direction: column; gap: 3px; min-width: 0; padding: 12px 14px; text-align: left;
  border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--panel2); color: var(--text);
  font: inherit; cursor: pointer; box-shadow: inset 3px 0 0 var(--c);
  transition: border-color var(--dur-1), background var(--dur-1), transform var(--dur-1);
}
.cal-sum-tile:hover { border-color: color-mix(in srgb, var(--c) 45%, var(--line)); transform: translateY(-1px); }
.cal-sum-tile.is-on { border-color: color-mix(in srgb, var(--c) 70%, transparent); background: color-mix(in srgb, var(--c) 10%, var(--panel2)); }
.cal-sum-tile.is-inc { --c: var(--green); } .cal-sum-tile.is-exp { --c: var(--orange); }
.cal-sum-tile.is-dl { --c: var(--red); } .cal-sum-tile.is-task { --c: var(--blue); }
.cal-sum-lbl { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.cal-sum-lbl i { width: 7px; height: 7px; border-radius: 50%; background: var(--c); }
.cal-sum-tile b { font-family: var(--font-display); font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cal-sum-tile small { font-size: 12px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@media (max-width: 760px) { .cal-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); } .cal-sum-tile b { font-size: 17px; } }

/* Ячейки: прошедшие дни тише, день с дедлайном — красная черта сверху. */
.cal-cell.is-past .cal-day-num { opacity: .5; }
.cal-cell.has-deadline { box-shadow: inset 0 2px 0 var(--red); }

/* Список событий по дням. */
.cal-list { display: flex; flex-direction: column; gap: 6px; }
.cal-list-day { display: flex; align-items: baseline; gap: 8px; margin: 10px 0 2px; padding-left: 2px; }
.cal-list-day:first-child { margin-top: 0; }
.cal-list-day b { font-size: 13.5px; }
.cal-list-day span { font-size: 12px; color: var(--muted); }
.cal-list-day.is-today b, .cal-list-day.is-today span { color: var(--primary-text); }
.cal-list-row {
  display: flex; align-items: center; gap: 10px; min-height: 48px; padding: 8px 12px;
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--panel2); cursor: pointer;
  transition: border-color var(--dur-1), background var(--dur-1);
}
.cal-list-row:hover { border-color: color-mix(in srgb, var(--c) 45%, var(--line)); background: color-mix(in srgb, var(--c) 5%, var(--panel2)); }
.cal-list-row.is-today { border-color: rgb(var(--primary-rgb) / .4); }
.cal-list-row.is-past .cal-list-text { opacity: .6; }
.cal-list-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 8px; background: var(--c); }
.cal-list-text { flex: 1 1 auto; min-width: 0; }
.cal-list-title { font-size: 13px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cal-list-sum { flex: 0 0 auto; font-family: var(--font-display); font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.cal-list-type { flex: 0 0 auto; font-size: 12px; font-weight: 700; white-space: nowrap; }

/* Подсказка слева в полосе, когда искать нечего (пустая смета / бюджет одной суммой). */
.est-bar-hint { flex: 1 1 220px; min-width: 0; font-size: 13px; color: var(--muted); padding-left: 4px; }

/* Колонка «Сделки»: строки одной структуры (01.10.2026). Пустой ярус «клиент ·
   срок» держит высоту, пустая полоса оплаты — прозрачная дорожка того же места.
   Выбор сделки подсвечивается мягко, без скачка. */
.deal-switcher-item-meta--who { min-height: 16px; }
.deal-switcher-item-pay.is-empty { background: transparent; }
.deal-switcher-item { transition: background-color var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out), box-shadow var(--dur-2) var(--ease-out); }
.deal-switcher-item.active { box-shadow: 0 8px 22px -16px rgb(var(--primary-rgb) / .8); }

/* ── Календарь: навигация одной строкой, день справа (01.10.2026) ─────── */
.cal-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px 20px; flex-wrap: wrap; margin-bottom: 14px; }
.cal-top .cal-header-title { min-width: 0; }
.cal-top .cal-header-title h1 { margin: 0; }
.cal-nav3 { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.cal-month-pick { position: relative; }
.cal-month-pick > summary {
  list-style: none; display: inline-flex; align-items: center; gap: 6px; min-height: 34px; padding: 4px 12px;
  border-radius: var(--r-md); cursor: pointer; font-family: var(--font-display); font-size: 16px; font-weight: 700; white-space: nowrap;
  transition: background var(--dur-1);
}
.cal-month-pick > summary::-webkit-details-marker { display: none; }
.cal-month-pick > summary:hover { background: rgb(var(--primary-rgb) / .08); }
.cal-month-pick > summary svg { transform: rotate(90deg); color: var(--muted); transition: transform var(--dur-2); }
.cal-month-pick[open] > summary svg { transform: rotate(-90deg); }
.cal-month-pop {
  position: absolute; top: calc(100% + 6px); left: 50%; transform: translateX(-50%); z-index: var(--z-dropdown);
  width: 244px; padding: 10px; background: var(--bg2); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--elev-3);
  animation: estOpen .2s var(--ease-out) both;
}
.cal-month-pop-year { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.cal-month-pop-year b { font-family: var(--font-display); font-size: 14px; }
.cal-month-pop-year button, .cal-month-pop-grid button {
  border: 1px solid transparent; background: transparent; color: var(--text); font: inherit; cursor: pointer; border-radius: var(--r-sm);
  transition: background var(--dur-1), border-color var(--dur-1);
}
.cal-month-pop-year button { width: 30px; height: 28px; font-size: 16px; }
.cal-month-pop-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; }
.cal-month-pop-grid button { height: 34px; font-size: 13px; font-weight: 600; }
.cal-month-pop button:hover { background: rgb(var(--primary-rgb) / .1); }
.cal-month-pop-grid button.is-now { border-color: rgb(var(--primary-rgb) / .45); }
.cal-month-pop-grid button.is-on { background: var(--primary); color: var(--on-color); }
.cal-mode-seg { display: inline-flex; padding: 2px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--panel2); }
.cal-mode-seg button { min-height: 30px; padding: 3px 12px; border: 0; border-radius: var(--r-sm); background: transparent; color: var(--muted); font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; }
.cal-mode-seg button:hover { color: var(--text); }
.cal-mode-seg button.is-on { background: rgb(var(--primary-rgb) / .16); color: var(--primary-text); }
@media (hover: none) and (pointer: coarse) { .cal-mode-seg button, .cal-month-pop-grid button { min-height: 44px; } .cal-month-pick > summary { min-height: 44px; } .cal-month-pop-year button { width: 44px; height: 44px; } }

.cal-body { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; }
.cal-main { min-width: 0; }
.cal-side { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
/* Две колонки, когда панели календаря хватает места (окно ≥1400 = ~1130px панели
   с меню слева). Контейнерным запросом нельзя: он не меряет сам себя. */
@media (min-width: 1400px) {
  .cal-body { grid-template-columns: minmax(0, 1fr) 320px; align-items: start; }
  .cal-side { position: sticky; top: 86px; }
}
.cal-side-card { padding: 14px; border: 1px solid var(--line); border-radius: var(--r-xl); background: var(--panel2); }
.cal-side .cal-day-panel { margin: 0; }
.cal-side-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
.cal-side-head h3 { margin: 0; font-size: 15px; }
.cal-up-row {
  display: flex; align-items: center; gap: 10px; width: 100%; min-height: 48px; padding: 6px 8px; margin: 0 0 4px;
  border: 1px solid transparent; border-radius: var(--r-md); background: transparent; color: var(--text); font: inherit; text-align: left; cursor: pointer;
  transition: background var(--dur-1), border-color var(--dur-1);
}
.cal-up-row:hover { background: color-mix(in srgb, var(--c) 8%, transparent); border-color: color-mix(in srgb, var(--c) 30%, var(--line)); }
.cal-up-date { flex: 0 0 38px; display: flex; flex-direction: column; align-items: center; padding: 3px 0; border-radius: var(--r-sm); box-shadow: inset 0 3px 0 var(--c); background: var(--panel); }
.cal-up-date b { font-family: var(--font-display); font-size: 15px; line-height: 1.1; }
.cal-up-date small { font-size: 10.5px; color: var(--muted); }
.cal-up-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.cal-up-text b { font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cal-up-text small { font-size: 11.5px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cal-up-sum { flex: 0 0 auto; font-family: var(--font-display); font-size: 13px; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ── Клиенты: итоги плитками, карточка одной раскладки (01.10.2026) ────── */
.clients-summary { margin: 2px 0 16px; }
.cal-sum-tile.is-rep { --c: var(--violet); } /* «Повторные» — хорошее, не красным */
.clients-sort { width: auto; min-height: 34px; padding: 5px 30px 5px 10px; font-size: 13px; border-radius: var(--r-md); }
.client-card-nums { display: grid; grid-template-columns: .8fr 1.2fr 1.2fr; gap: 8px; margin-top: 10px; }
.client-card-nums > span { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.client-card-nums small { font-size: 11px; color: var(--muted); font-weight: 600; }
.client-card-nums b, .client-card-nums .client-deals-link {
  font-family: var(--font-display); font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.client-card-nums .client-deals-link { display: inline-flex; align-items: center; gap: 3px; padding: 0; border: 0; background: none; color: var(--primary-text); cursor: pointer; }
.client-card-nums .client-deals-link { border-radius: var(--r-sm); padding: 0 4px; margin-left: -4px; transition: background var(--dur-1); }
.client-card-nums .client-deals-link:hover { background: rgb(var(--primary-rgb) / .12); }
.client-card-nums .client-card-none { color: var(--muted); font-weight: 600; }
.client-pay-track.is-empty { background: transparent; }

/* ── Задачи: плитки единого вида, список по срокам (01.10.2026) ──────── */
.gtask-stats.cal-summary { margin: 0 0 14px; }
.gtask-stats .cal-sum-tile.is-zero b { color: var(--muted); }
.gtask-group-head { display: flex; align-items: baseline; gap: 8px; margin: 14px 0 6px; padding-left: 2px; }
.gtask-group-head:first-child { margin-top: 0; }
.gtask-group-head b { font-size: 13.5px; }
.gtask-group-head span { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.gtask-group-head.is-overdue b { color: var(--text-danger); }
.gtask-group-head.is-today b { color: var(--text-warning); }
.gtask-group-head.is-done b { color: var(--muted); }
/* Корзина — при наведении на строку (на телефоне видна всегда). */
.gtask-row .icon-del-btn { opacity: 0; transition: opacity var(--dur-1); }
.gtask-row:hover .icon-del-btn, .gtask-row .icon-del-btn:focus-visible { opacity: 1; }
@media (hover: none) { .gtask-row .icon-del-btn { opacity: 1; } }

/* ── Страница клиента: профиль, плитки, сделки слева, контакты справа (01.10.2026) ── */
.client-detail { display: flex; flex-direction: column; gap: 16px; }
.client-hero { padding: 18px 20px; border: 1px solid var(--line); border-radius: var(--r-xl); background: var(--panel); }
.client-back { margin-bottom: 14px; }
.client-hero-main { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.client-avatar--lg { flex: 0 0 64px; width: 64px; height: 64px; font-size: 22px; }
.client-hero-id { flex: 1 1 260px; min-width: 0; }
.client-hero-name { display: flex; align-items: center; gap: 10px; min-width: 0; flex-wrap: wrap; }
.client-hero-name h1 { margin: 0; font-size: 28px; line-height: 1.15; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
.client-hero-id p { margin: 4px 0 0; font-size: 14px; color: var(--muted); }
.client-status-select { appearance: none; -webkit-appearance: none; cursor: pointer; font: inherit; font-size: 12px; font-weight: 700; padding: 4px 12px; border: 0; }
.client-status-select:hover { filter: brightness(1.15); }
.client-hero-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.client-contact-btn { display: inline-flex; align-items: center; gap: 6px; text-decoration: none; color: var(--text); }
.client-contact-btn:hover { background: var(--panel2); color: var(--text); }
.client-detail-stats { margin: 16px 0 0; }
.cal-sum-tile.is-static { cursor: default; }
.cal-sum-tile.is-static:hover { transform: none; border-color: var(--line); }

.client-detail-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; align-items: start; }
@media (min-width: 1100px) { .client-detail-grid { grid-template-columns: minmax(0, 1fr) 390px; } }
.client-deals-head { display: flex; align-items: center; justify-content: space-between; margin: 0 0 10px; }
.client-deals-head h2, .client-side h2 { margin: 0; font-size: 18px; }
.client-deals-head h2 span { margin-left: 6px; font-size: 14px; font-weight: 500; color: var(--muted); }
.client-deals-sep { padding: 12px 4px 4px; font-size: 12px; font-weight: 700; color: var(--muted); }

.client-project-row { display: flex; align-items: center; gap: 14px; box-shadow: inset 3px 0 0 var(--st-color, var(--line)); transition: border-color var(--dur-1), background var(--dur-1), transform var(--dur-1); }
.client-project-row:hover { background: var(--panel2); transform: translateY(-1px); }
.cpr-main { flex: 1 1 auto; min-width: 0; }
.cpr-main h4 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cpr-stage { display: inline-flex; align-items: center; gap: 6px; color: var(--text); font-weight: 600; }
.cpr-stage i { width: 8px; height: 8px; border-radius: 50%; background: var(--st-color, var(--muted)); }
.cpr-money { flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-end; gap: 3px; min-width: 150px; }
.cpr-total { font-family: var(--font-display); font-size: 16px; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.cpr-total.is-none { font-family: inherit; font-size: 13px; font-weight: 600; color: var(--muted); }
.cpr-pay { font-size: 12px; color: var(--muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.cpr-ok { color: var(--text-success, var(--green)); font-weight: 600; }
.cpr-left { color: var(--text-warning, var(--orange)); font-weight: 600; }
.cpr-track { display: block; width: 140px; height: 4px; border-radius: var(--r-pill); background: color-mix(in srgb, var(--orange) 18%, transparent); overflow: hidden; }
.cpr-track > span { display: block; height: 100%; border-radius: var(--r-pill); background: var(--green); }
.cpr-go { flex: 0 0 auto; color: var(--muted); display: inline-flex; }

.client-side { position: relative; display: flex; flex-direction: column; gap: 12px; padding: 18px; border: 1px solid var(--line); border-radius: var(--r-xl); background: var(--panel); }
@media (min-width: 1100px) { .client-side { position: sticky; top: 16px; } }
.client-side-fields { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 10px; }
.client-side-fields > :first-child { grid-column: 1 / -1; }
.client-side .field { margin: 0; }
.client-side-danger { padding-top: 10px; border-top: 1px solid var(--line); }
@media (max-width: 640px) {
  .client-hero { padding: 14px; }
  .client-hero-name h1 { font-size: 22px; }
  .client-avatar--lg { flex-basis: 52px; width: 52px; height: 52px; font-size: 18px; }
  .client-contact-btn span { display: none; }
  .client-side-fields { grid-template-columns: minmax(0, 1fr); }
  .client-project-row { flex-wrap: wrap; gap: 8px 12px; }
  .cpr-main { flex: 1 1 100%; }
  .cpr-money { flex: 1 1 100%; min-width: 0; align-items: flex-start; }
  .cpr-pay { white-space: normal; }
  .cpr-track { width: 100%; }
  .cpr-go { display: none; }
}

/* ── Ролик о продукте: «Тарифный план» и «Поддержка» (01.10.2026) ───────── */
.promo-video { display: flex; gap: 22px; align-items: center; margin: 22px 0; padding: 18px; border: 1px solid var(--line); border-radius: var(--r-xl);
  background: radial-gradient(120% 140% at 0% 100%, rgb(var(--primary-rgb) / .14), transparent 60%), var(--panel2); }
.promo-video.is-support { margin: 0 0 22px; flex-direction: column; align-items: stretch; }
/* В «Поддержке» колонка 680px — рядом с текстом видео выходило 314px; ставим во всю ширину. */
.promo-video.is-support .promo-video-frame { flex: 0 0 auto; width: 100%; max-width: none; }
.promo-video-frame { flex: 0 0 460px; max-width: 52%; aspect-ratio: 16 / 9; border-radius: var(--r-xl); overflow: hidden; background: var(--bg);
  box-shadow: 0 0 0 1px rgba(255,255,255,.08), 0 24px 60px -24px rgb(var(--primary-rgb) / .7); }
.promo-video-frame video { display: block; width: 100%; height: 100%; object-fit: cover; background: var(--bg); }
.promo-video-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 10px; }
.promo-video-kicker { display: inline-flex; align-items: center; gap: 6px; align-self: flex-start; padding: 4px 10px; border-radius: var(--r-pill);
  font-size: 12px; font-weight: 700; color: var(--primary-text); background: rgb(var(--primary-rgb) / .14); }
.promo-video-text h2 { margin: 0; font-size: 22px; line-height: 1.2; }
.promo-video-text p { margin: 0; font-size: 14px; line-height: 1.5; color: var(--muted); }
.promo-video-points { list-style: none; margin: 2px 0 0; padding: 0; display: grid; gap: 8px; }
.promo-video-points li { display: flex; align-items: flex-start; gap: 8px; font-size: 13.5px; }
.promo-video-points li svg { flex: 0 0 auto; margin-top: 3px; color: var(--text-success); }
.promo-video-open { display: inline-flex; align-items: center; gap: 6px; align-self: flex-start; min-height: 32px; font-size: 13px; font-weight: 600;
  color: var(--primary-text); text-decoration: none; border-radius: var(--r-sm); transition: color var(--dur-1), opacity var(--dur-1); }
.promo-video-open:hover { color: var(--text); }
@media (hover: none) and (pointer: coarse) { .promo-video-open { min-height: 44px; } }
@media (max-width: 900px) {
  .promo-video { flex-direction: column; align-items: stretch; padding: 14px; }
  .promo-video-frame { flex: 0 0 auto; width: 100%; max-width: none; }
}

/* ── Экран входа — лендинг (01.10.2026) ─────────────────────────────────
   Прокручиваемая страница поверх приложения: шапка, первый экран с формой,
   ролик, три шага, возможности, цены. Форма — тот же .auth-gate-box. */
.auth-gate.lp { display: block; overflow-y: auto; overflow-x: hidden; padding: 0; -webkit-overflow-scrolling: touch;
  background: radial-gradient(1100px 520px at 75% -10%, rgb(var(--primary-rgb) / .22), transparent 70%), radial-gradient(900px 500px at -10% 30%, rgb(var(--primary-rgb) / .1), transparent 70%), var(--bg); }
.lp-top { position: sticky; top: 0; z-index: 2; backdrop-filter: blur(14px) saturate(1.4); background: color-mix(in srgb, var(--bg) 78%, transparent); border-bottom: 1px solid var(--line); }
.lp-top-in { max-width: 1160px; margin: 0 auto; padding: 12px 24px; display: flex; align-items: center; gap: 10px; }
.lp-brand { display: inline-flex; align-items: center; gap: 10px; margin-right: auto; font-size: 17px; letter-spacing: -.01em; }
.lp-brand b { font-weight: 800; } .lp-brand i { font-style: normal; font-weight: 600; color: var(--muted); }
.lp-logo { width: 34px; height: 34px; border-radius: var(--r-md); background: var(--primary); display: grid; place-items: center; flex: 0 0 auto; }
.lp-logo img { width: 22px; height: 22px; object-fit: contain; }
.lp-nav { display: flex; gap: 2px; margin-right: 8px; }
.lp-nav button { border: 0; background: transparent; color: var(--muted); font: inherit; font-size: 14px; font-weight: 600; padding: 8px 12px; border-radius: var(--r-sm); cursor: pointer; transition: color var(--dur-1), background var(--dur-1); }
.lp-nav button:hover { color: var(--text); background: var(--panel2); }

.lp-hero { max-width: 1160px; margin: 0 auto; padding: 56px 24px 40px; display: grid; grid-template-columns: minmax(0, 1fr) 400px; gap: 56px; align-items: center; }
.lp-kicker { display: inline-flex; padding: 6px 12px; border-radius: var(--r-pill); font-size: 13px; font-weight: 700; color: var(--primary-text); background: rgb(var(--primary-rgb) / .12); }
.lp-hero h1 { margin: 18px 0 16px; font-family: var(--font-display); font-size: clamp(34px, 4.2vw, 54px); line-height: 1.06; letter-spacing: -.03em; text-wrap: balance; }
.lp-acc { background: linear-gradient(180deg, var(--primary-text), var(--primary)); -webkit-background-clip: text; background-clip: text; color: transparent; }
.lp-lead { margin: 0; font-size: 17px; line-height: 1.6; color: var(--muted); max-width: 560px; }
.lp-cta { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 26px; }
.lp-cta-main { padding: 13px 22px; font-size: 15px; }
.lp-cta-video { padding: 13px 18px; font-size: 15px; display: inline-flex; align-items: center; gap: 8px; }
.lp-trust { list-style: none; margin: 22px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px 18px; font-size: 13px; color: var(--muted); }
.lp-trust li { display: inline-flex; align-items: center; gap: 6px; } .lp-trust svg { color: var(--text-success); }
.lp .auth-gate-box { width: 100%; max-width: none; margin: 0; box-shadow: 0 30px 80px -40px rgb(var(--primary-rgb) / .55); }
.lp-auth-title { font-family: var(--font-display); font-size: 22px; font-weight: 700; letter-spacing: -.01em; }
.lp-auth-sub { margin: 4px 0 18px; font-size: 13px; color: var(--muted); }

.lp-sec { max-width: 1160px; margin: 0 auto; padding: 44px 24px; }
.lp-h2 { margin: 0 0 22px; font-family: var(--font-display); font-size: clamp(26px, 3vw, 34px); letter-spacing: -.02em; text-align: center; }
.lp-sub { margin: -10px auto 24px; max-width: 620px; text-align: center; color: var(--muted); font-size: 15px; line-height: 1.55; }
.lp-video-frame { max-width: 960px; margin: 0 auto; aspect-ratio: 16 / 9; border-radius: var(--r-2xl); overflow: hidden; background: var(--bg);
  box-shadow: 0 0 0 1px var(--line), 0 40px 100px -40px rgb(var(--primary-rgb) / .7); }
.lp-video-frame video { display: block; width: 100%; height: 100%; object-fit: cover; background: var(--bg); }

.lp-steps { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; counter-reset: s; }
.lp-steps li, .lp-feat { position: relative; padding: 22px; border: 1px solid var(--line); border-radius: var(--r-xl); background: var(--panel); transition: border-color var(--dur-1), transform var(--dur-1); }
.lp-steps li:hover, .lp-feat:hover { border-color: rgb(var(--primary-rgb) / .45); transform: translateY(-2px); }
.lp-step-n { position: absolute; top: 18px; right: 20px; font-family: var(--font-display); font-size: 34px; font-weight: 700; color: rgb(var(--primary-rgb) / .25); }
.lp-ic { width: 44px; height: 44px; border-radius: var(--r-md); display: grid; place-items: center; color: var(--primary-text); background: rgb(var(--primary-rgb) / .12); }
.lp-steps h3, .lp-feat h3 { margin: 14px 0 6px; font-size: 17px; }
.lp-steps p, .lp-feat p { margin: 0; font-size: 14px; line-height: 1.55; color: var(--muted); }
.lp-feats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }

.lp-plans { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; max-width: 960px; margin: 0 auto; }
.lp-plan { position: relative; padding: 22px; border: 1px solid var(--line); border-radius: var(--r-xl); background: var(--panel); }
.lp-plan.is-rec { border-color: rgb(var(--primary-rgb) / .6); box-shadow: 0 24px 60px -30px rgb(var(--primary-rgb) / .7); }
.lp-plan-badge { position: absolute; top: -11px; left: 22px; padding: 3px 10px; border-radius: var(--r-pill); font-size: 12px; font-weight: 700; color: var(--on-color); background: var(--primary); }
.lp-plan-name { font-size: 18px; font-weight: 800; } .lp-plan-name small { display: block; margin-top: 2px; font-size: 13px; font-weight: 500; color: var(--muted); }
.lp-plan-price { margin: 14px 0 12px; display: flex; align-items: baseline; gap: 6px; }
.lp-plan-price b { font-family: var(--font-display); font-size: 32px; font-variant-numeric: tabular-nums; } .lp-plan-price span { color: var(--muted); font-size: 14px; }
.lp-plan ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.lp-plan li { display: flex; gap: 8px; font-size: 13.5px; line-height: 1.4; } .lp-plan li svg { flex: 0 0 auto; margin-top: 2px; color: var(--text-success); }
.lp-note { margin: 18px 0 0; text-align: center; font-size: 13px; color: var(--muted); }

.lp-final { text-align: center; }
.lp-final h2 { margin: 0 0 10px; font-family: var(--font-display); font-size: clamp(26px, 3vw, 36px); letter-spacing: -.02em; }
.lp-final p { margin: 0 auto 22px; max-width: 520px; color: var(--muted); font-size: 15px; line-height: 1.55; }
.lp-foot { max-width: 1160px; margin: 0 auto; padding: 22px 24px 40px; display: flex; flex-wrap: wrap; gap: 8px 20px; justify-content: center; border-top: 1px solid var(--line); font-size: 13px; color: var(--muted); }
.lp-foot a { color: var(--muted); text-decoration: none; transition: color var(--dur-1); } .lp-foot a:hover { color: var(--text); }

@media (max-width: 980px) {
  .lp-hero { grid-template-columns: minmax(0, 1fr); gap: 28px; padding-top: 32px; }
  .lp .auth-gate-box { max-width: 480px; }
  .lp-nav { display: none; }
  .lp-steps, .lp-feats, .lp-plans { grid-template-columns: minmax(0, 1fr); }
  .lp-plans { max-width: 480px; }
}
@media (max-width: 560px) {
  .lp-top-in { padding: 10px 16px; }
  .lp-top-cta { display: none; }
  .lp-hero, .lp-sec { padding-left: 16px; padding-right: 16px; }
  .lp-hero { padding-top: 24px; }
  .lp-lead { font-size: 15.5px; }
  .lp-cta > .btn { flex: 1 1 100%; justify-content: center; }
  .lp-sec { padding-top: 32px; padding-bottom: 32px; }
  .lp-video-frame { border-radius: var(--r-xl); }
}
@media (hover: none) and (pointer: coarse) { .lp-nav button, .lp-foot a { min-height: 44px; display: inline-flex; align-items: center; } }

/* ── Лендинг входа v2: экран продукта, «Знакомо?», гарантии, вопросы (02.10.2026) ── */
.lp-micro { margin: 12px 0 0; font-size: 13px; color: var(--muted); }
.lp-shot { position: relative; max-width: 1040px; margin: 0 auto; border-radius: var(--r-xl); overflow: visible;
  box-shadow: 0 0 0 1px var(--line), 0 50px 120px -50px rgb(var(--primary-rgb) / .75); background: var(--panel); }
.lp-shot-bar { display: flex; align-items: center; gap: 7px; height: 38px; padding: 0 16px; border-bottom: 1px solid var(--line); border-radius: var(--r-xl) var(--r-xl) 0 0; background: var(--panel2); }
.lp-shot-bar i { width: 11px; height: 11px; border-radius: 50%; background: color-mix(in srgb, var(--muted) 45%, transparent); }
.lp-shot-bar span { margin-left: 12px; font-size: 12.5px; color: var(--muted); }
.lp-shot img, .lp-shot-media { display: block; width: 100%; height: auto; border-radius: 0 0 var(--r-xl) var(--r-xl); background: var(--panel); }
.lp-pin { position: absolute; left: var(--x, 50%); top: var(--y, 50%); transform: translate(-50%, -50%); display: inline-flex; align-items: center; gap: 8px; max-width: 270px;
  padding: 8px 14px 8px 8px; border-radius: var(--r-pill); font-size: 13px; font-weight: 600; line-height: 1.3; color: var(--text);
  background: color-mix(in srgb, var(--panel) 88%, transparent); border: 1px solid rgb(var(--primary-rgb) / .5); backdrop-filter: blur(10px); box-shadow: 0 14px 34px -14px rgb(var(--primary-rgb) / .8); }
.lp-pin b { flex: 0 0 auto; width: 24px; height: 24px; border-radius: 50%; display: grid; place-items: center; font-size: 12px; color: var(--on-color); background: var(--primary); }
.lp-ba { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; max-width: 1000px; margin: 0 auto; }
.lp-ba-col { padding: 24px; border: 1px solid var(--line); border-radius: var(--r-xl); background: var(--panel); }
.lp-ba-col h3 { margin: 0 0 14px; font-size: 18px; }
.lp-ba-col p { display: flex; gap: 10px; margin: 0; padding: 10px 0; font-size: 15px; line-height: 1.45; border-top: 1px solid var(--line); }
.lp-ba-col p:first-of-type { border-top: 0; }
.lp-ba-col p svg { flex: 0 0 auto; margin-top: 3px; }
.lp-ba-col.is-before { color: var(--muted); } .lp-ba-col.is-before h3 { color: var(--muted); } .lp-ba-col.is-before svg { color: var(--text-danger); }
.lp-ba-col.is-after { border-color: rgb(var(--primary-rgb) / .45); box-shadow: 0 24px 60px -34px rgb(var(--primary-rgb) / .7); } .lp-ba-col.is-after svg { color: var(--text-success); }
.lp-plan-btn { width: 100%; margin-top: 18px; justify-content: center; }
.lp-guar { list-style: none; margin: 22px auto 0; padding: 0; max-width: 960px; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.lp-guar li { display: flex; gap: 10px; align-items: flex-start; padding: 14px 16px; border-radius: var(--r-lg); background: var(--panel2); font-size: 13.5px; line-height: 1.45; color: var(--muted); }
.lp-guar li b { color: var(--text); font-weight: 700; } .lp-guar svg { flex: 0 0 auto; margin-top: 2px; color: var(--text-success); }
.lp-faq { max-width: 820px; margin: 0 auto; display: grid; gap: 10px; }
.lp-faq details { border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--panel); transition: border-color var(--dur-1); }
.lp-faq details[open] { border-color: rgb(var(--primary-rgb) / .45); }
.lp-faq summary { list-style: none; display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 20px; min-height: 44px; font-size: 16px; font-weight: 700; cursor: pointer; transition: color var(--dur-1); }
.lp-faq summary:hover { color: var(--primary-text); }
.lp-faq summary::-webkit-details-marker { display: none; }
.lp-faq summary svg { flex: 0 0 auto; color: var(--muted); transform: rotate(90deg); transition: transform var(--dur-1); }
.lp-faq details[open] summary svg { transform: rotate(-90deg); }
.lp-faq details p { margin: 0; padding: 0 20px 18px; font-size: 15px; line-height: 1.6; color: var(--muted); }
@media (max-width: 980px) {
  .lp-pin { position: static; transform: none; max-width: none; margin: 10px 12px 0; }
  .lp-shot { padding-bottom: 12px; overflow: hidden; }
  .lp-ba, .lp-guar { grid-template-columns: minmax(0, 1fr); }
}

/* ═══ ТАЙМИНГ СЪЁМКИ (?timing=…, вкладка сделки «Тайминг») ═══
   Приёмы из референсов владельца (decision-design-from-references-2026-09):
   градиент со светом из угла в шапке, крупные цифры, значки в квадратах,
   свечение у текущего. Цвет типа блока — одна переменная --tm-k на строке:
   от неё красятся точка на линии, значок типа и шаблон в «Добавить блок». */
.tm-wrap { display: grid; gap: 14px; }
.tm-page { max-width: 780px; margin: 0 auto; padding: 20px 16px 48px; }
.tm-k-prep { --tm-k: var(--blue); } .tm-k-shoot { --tm-k: var(--primary); } .tm-k-break { --tm-k: var(--green); }
.tm-k-move { --tm-k: var(--orange); } .tm-k-other { --tm-k: var(--muted); }

/* шапка */
.tm-head { position: relative; display: grid; gap: 14px; padding: 22px; overflow: hidden; background-image: var(--grad-hero); border-color: rgb(var(--primary-rgb) / .28); }
.tm-head-top { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 12px; }
.tm-pills { display: flex; flex-wrap: wrap; gap: 6px; }
.tm-pill { display: inline-flex; align-items: center; gap: 6px; min-height: 28px; padding: 4px 12px; border-radius: var(--r-pill); background: color-mix(in srgb, var(--panel) 75%, transparent); border: 1px solid var(--line); font-size: 12px; font-weight: 600; color: var(--muted); }
.tm-theme { display: grid; place-items: center; width: 36px; height: 36px; padding: 0; border-radius: var(--r-pill); border: 1px solid var(--line); background: color-mix(in srgb, var(--panel) 75%, transparent); color: var(--text); cursor: pointer; transition: border-color var(--dur-1), box-shadow var(--dur-1), color var(--dur-1); }
.tm-theme:hover { border-color: rgb(var(--primary-rgb) / .5); color: var(--primary-text); box-shadow: var(--glow-sm); }
.tm-theme:focus-visible { outline: var(--ring-w) solid var(--ring); outline-offset: 2px; }
.tm-pills { align-items: center; }
.tm-pill.is-admin { background: var(--primary); border-color: transparent; color: var(--on-color); box-shadow: var(--glow-sm); }
.tm-studio { display: flex; align-items: center; gap: 10px; font-size: 14px; font-weight: 700; }
.tm-studio img, .tm-studio-mark { width: 32px; height: 32px; border-radius: var(--r-md); object-fit: cover; }
.tm-studio-mark { display: grid; place-items: center; background: var(--primary); color: var(--on-color); font-family: var(--font-display); font-size: 15px; box-shadow: var(--glow-sm); }
.tm-kicker { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--primary-text); }
.tm-title { margin: -6px 0 0; font-family: var(--font-display); font-size: 30px; line-height: 1.15; letter-spacing: -.01em; overflow-wrap: anywhere; }
.tm-title-in { width: 100%; margin-top: -6px; font-family: var(--font-display); font-size: 22px; font-weight: 700; }
.tm-stats { display: flex; flex-wrap: wrap; gap: 8px; }
.tm-stat { flex: 1 1 0; display: grid; gap: 2px; min-width: 110px; padding: 10px 14px; border-radius: var(--r-lg); background: color-mix(in srgb, var(--panel) 72%, transparent); border: 1px solid var(--line); }
.tm-stat b { white-space: nowrap; font-family: var(--font-display); font-size: 20px; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.tm-stat span { font-size: 12px; color: var(--muted); }
.tm-stat.is-warn b { color: var(--text-warning); }
.tm-meta { display: grid; grid-template-columns: auto minmax(0, 1fr) minmax(0, 1fr); gap: 8px; }
/* у администратора в плитках поля ввода: адрес — отдельной строкой во всю ширину, иначе всё обрезается */
.tm-meta.is-edit { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }
.tm-meta.is-edit .tm-tile:last-child { grid-column: 1 / -1; }
.tm-tile { display: flex; align-items: flex-start; gap: 10px; min-width: 0; padding: 12px; border-radius: var(--r-lg); background: color-mix(in srgb, var(--panel) 80%, transparent); border: 1px solid var(--line); }
.tm-tile-body { display: grid; gap: 4px; min-width: 0; flex: 1; }
.tm-tile-body b { font-size: 15px; line-height: 1.3; overflow-wrap: anywhere; }
.tm-tile-body input { width: 100%; min-width: 0; }
.tm-2in { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 6px; }
.tm-act { display: inline-flex; align-items: center; gap: 6px; justify-self: start; min-height: 32px; margin-top: 2px; padding: 4px 12px; border-radius: var(--r-pill); background: var(--primary-bg); color: var(--primary-on-tint); font-size: 13px; font-weight: 600; text-decoration: none; transition: background var(--dur-1), box-shadow var(--dur-1); }
.tm-act:hover { background: rgb(var(--primary-rgb) / .18); box-shadow: var(--glow-sm); }
.tm-act:focus-visible { outline: var(--ring-w) solid var(--ring); outline-offset: 2px; }
.tm-status[data-s="error"] { color: var(--text-danger); border-color: currentColor; }
.tm-status[data-s="conflict"] { color: var(--text-warning); }
.tm-status[data-s="saved"] { color: var(--text-success); }
.tm-status[data-s="saving"] svg { animation: tmBlink 1s ease-in-out infinite; }
@keyframes tmBlink { 50% { opacity: .3; } }

/* сейчас идёт */
.tm-live { display: grid; gap: 12px; padding: 18px 20px; border-color: rgb(var(--primary-rgb) / .55); box-shadow: var(--glow-md); background-image: var(--grad-soft); }
.tm-live-main { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; }
.tm-live-what { display: grid; gap: 4px; min-width: 0; }
.tm-live-what h2 { margin: 0; font-family: var(--font-display); font-size: 22px; line-height: 1.2; overflow-wrap: anywhere; }
.tm-live-label { display: inline-flex; align-items: center; gap: 8px; font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--text-danger); }
.tm-live-dot { position: relative; flex: 0 0 auto; width: 8px; height: 8px; border-radius: var(--r-pill); background: var(--red); }
.tm-live-dot::after { content: ""; position: absolute; inset: 0; border-radius: var(--r-pill); background: var(--red); animation: tmPing 1.8s cubic-bezier(0, 0, .2, 1) infinite; }
@keyframes tmPing { 75%, 100% { transform: scale(2.6); opacity: 0; } }
.tm-live-left { display: grid; justify-items: end; flex: 0 0 auto; text-align: right; }
.tm-live-left b { font-family: var(--font-display); font-size: 34px; font-weight: 700; line-height: 1; color: var(--primary-text); font-variant-numeric: tabular-nums; }
.tm-live-left span { font-size: 12px; color: var(--muted); }
.tm-live-bar, .tm-card-bar { height: 8px; border-radius: var(--r-pill); background: rgb(var(--primary-rgb) / .12); overflow: hidden; }
.tm-live-bar i, .tm-card-bar i { display: block; height: 100%; border-radius: var(--r-pill); background: linear-gradient(90deg, var(--primary2), var(--primary)); box-shadow: var(--glow-sm); }
.tm-card-bar { height: 4px; margin-top: 4px; }
.tm-live-next { display: flex; align-items: baseline; gap: 10px; padding: 10px 12px; border-radius: var(--r-md); background: var(--panel2); font-size: 14px; }
.tm-live-next > i { flex: 0 0 auto; align-self: center; width: 8px; height: 8px; border-radius: var(--r-pill); background: var(--tm-k); }
.tm-live-next b { font-family: var(--font-display); font-variant-numeric: tabular-nums; }
.tm-live-next span:last-child { min-width: 0; overflow-wrap: anywhere; }
.tm-shift { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.tm-seg { display: inline-flex; padding: 3px; border-radius: var(--r-pill); background: var(--panel2); border: 1px solid var(--line); }
.tm-seg button { min-width: 48px; min-height: 34px; padding: 0 12px; border: 0; border-radius: var(--r-pill); background: transparent; color: var(--text); font: inherit; font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums; cursor: pointer; transition: background var(--dur-1), color var(--dur-1); }
.tm-seg button:hover { background: var(--primary); color: var(--on-color); }
.tm-seg button.is-back { margin-left: 4px; border-left: 1px solid var(--line); border-radius: 0 var(--r-pill) var(--r-pill) 0; color: var(--text-success); }
.tm-seg button.is-back:hover { background: var(--green); color: var(--on-color); }
.tm-seg button:focus-visible { outline: var(--ring-w) solid var(--ring); outline-offset: 1px; }
@media (prefers-reduced-motion: reduce) { .tm-live-dot::after, .tm-status svg { animation: none; } }

/* должности и фильтр */
.tm-roles-panel, .tm-add { display: grid; gap: 12px; }
.tm-add { padding: 16px; border: 1px dashed rgb(var(--primary-rgb) / .4); border-radius: var(--r-xl); background: var(--grad-soft); }
.tm-sec-head { display: flex; align-items: center; gap: 10px; }
.tm-sec-head > div { display: grid; gap: 2px; }
.tm-sec-head b { font-size: 15px; }
.tm-roles { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.tm-role { display: inline-flex; align-items: center; gap: 4px; padding-right: 4px; }
.tm-role-x { display: grid; place-items: center; width: 22px; height: 22px; border: 0; border-radius: var(--r-pill); background: transparent; color: var(--muted); cursor: pointer; }
.tm-role-x:hover { color: var(--text-danger); background: var(--tint-red); }
.tm-role-in { width: 170px; min-height: 34px; padding: 4px 12px; border-radius: var(--r-pill); }
.tm-filter { padding: 2px 0; }
.tm-tpl { display: inline-flex; align-items: center; gap: 6px; }
.tm-tpl i { width: 8px; height: 8px; border-radius: var(--r-pill); background: var(--tm-k); }

/* лента: время · линия с точкой · карточка */
.tm-list { display: grid; }
.tm-row { display: grid; grid-template-columns: 58px 24px minmax(0, 1fr); column-gap: 8px; align-items: stretch; }
.tm-time { display: grid; align-content: start; gap: 2px; padding-top: 12px; text-align: right; font-variant-numeric: tabular-nums; }
.tm-time b { font-family: var(--font-display); font-size: 18px; font-weight: 700; line-height: 1.1; }
.tm-time span { font-size: 12px; color: var(--muted); }
.tm-rail { position: relative; display: flex; justify-content: center; }
.tm-rail::before { content: ""; position: absolute; top: 0; bottom: 0; width: 2px; background: var(--line); }
.tm-row:first-child .tm-rail::before { top: 18px; }
.tm-row:last-child .tm-rail::before { bottom: calc(100% - 18px); }
.tm-rail i { position: relative; display: grid; place-items: center; width: 14px; height: 14px; margin-top: 14px; border-radius: var(--r-pill); background: var(--panel); border: 3px solid var(--tm-k); color: var(--on-color); }
.tm-row.is-past .tm-rail i { width: 16px; height: 16px; border: 0; background: var(--tm-k); }
.tm-row.is-cur .tm-rail i { width: 18px; height: 18px; margin-top: 12px; background: var(--tm-k); box-shadow: 0 0 0 5px rgb(var(--primary-rgb) / .18), var(--glow-sm); }
.tm-card { position: relative; display: grid; gap: 6px; margin-bottom: 10px; padding: 12px 16px; border: 1px solid var(--line); border-radius: var(--r-xl); background: var(--panel); transition: border-color var(--dur-1), box-shadow var(--dur-1), transform var(--dur-1); }
.tm-card[role="button"] { cursor: pointer; }
.tm-card[role="button"]:hover { border-color: rgb(var(--primary-rgb) / .5); box-shadow: var(--glow-sm); }
.tm-card:focus-visible { outline: var(--ring-w) solid var(--ring); outline-offset: 2px; }
.tm-card h3 { margin: 0; font-size: 16px; line-height: 1.35; overflow-wrap: anywhere; }
.tm-card-top { display: flex; align-items: center; gap: 8px; }
.tm-kind { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--tm-k); }
.tm-now { padding: 2px 8px; border-radius: var(--r-pill); background: var(--primary); color: var(--on-color); font-size: 11px; font-weight: 700; }
.tm-dur { margin-left: auto; padding: 2px 8px; border-radius: var(--r-pill); background: var(--panel2); font-size: 12px; font-weight: 600; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.tm-edit-hint { color: var(--hint); }
.tm-chips { display: flex; flex-wrap: wrap; gap: 4px; }
.tm-chip { padding: 2px 10px; border-radius: var(--r-pill); background: var(--panel2); font-size: 12px; color: var(--muted); }
.tm-chip.is-me { background: var(--primary-bg); color: var(--primary-on-tint); font-weight: 600; }
.tm-line { display: flex; align-items: flex-start; gap: 6px; font-size: 13px; color: var(--muted); line-height: 1.45; overflow-wrap: anywhere; white-space: pre-line; }
.tm-line svg { flex: 0 0 auto; margin-top: 3px; }
.tm-line b { color: var(--text); font-weight: 600; }
.tm-line a { color: var(--primary-text); }
.tm-row.is-cur .tm-card { border-color: var(--primary); box-shadow: var(--glow-md); background-image: var(--grad-soft); }
.tm-row.is-cur .tm-time b { color: var(--primary-text); }
.tm-row.is-past .tm-card, .tm-row.is-past .tm-time { opacity: .55; }
.tm-row.is-open .tm-card { border-color: var(--primary); }
.tm-edit { display: grid; gap: 10px; margin: -2px 0 14px 90px; padding: 16px; border: 1px solid rgb(var(--primary-rgb) / .45); border-radius: var(--r-xl); background: var(--primary-bg); }
.tm-edit-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.tm-edit-grid.two { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.tm-edit-grid.four { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.tm-edit-grid.four .field { position: relative; }
.tm-edit-grid.four input, .tm-edit-grid.four select { box-sizing: border-box; width: 100%; height: 48px; }
.tm-hint { position: absolute; right: 34px; bottom: 15px; font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; pointer-events: none; }
.tm-check { display: flex; align-items: center; gap: 8px; min-height: 32px; font-size: 13px; color: var(--muted); cursor: pointer; }
.tm-check input { width: 18px; height: 18px; margin: 0; accent-color: var(--primary); }
.tm-shift-exact { display: inline-flex; align-items: center; gap: 6px; }
.tm-shift-exact input { width: 72px; min-height: 36px; text-align: center; font-weight: 700; font-variant-numeric: tabular-nums; }
.tm-edit .field { margin: 0; }
.tm-edit .toolbar { margin: 0; }

.tm-empty { color: var(--muted); text-align: center; padding: 24px 16px; }
.tm-upsell { display: grid; gap: 14px; justify-items: start; }
.tm-share { display: grid; gap: 10px; }
.tm-share-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.tm-share-row p { margin: 2px 0 0; }
.tm-share-foot { display: flex; justify-content: flex-end; padding-top: 8px; border-top: 1px solid var(--line); }
.tm-foot { text-align: center; font-size: 12px; color: var(--hint); }
.tm-foot a { color: var(--primary-text); }
@media (max-width: 640px) {
  .tm-head { padding: 16px; }
  .tm-title { font-size: 24px; }
  .tm-meta, .tm-edit-grid.two { grid-template-columns: minmax(0, 1fr); }
  .tm-edit-grid, .tm-edit-grid.four { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .tm-stat { min-width: 0; }
  .tm-stat { flex: 1 1 calc(50% - 4px); min-width: 0; }
  .tm-stat b { font-size: 17px; }
  .tm-meta.is-edit { grid-template-columns: minmax(0, 1fr); }
  .tm-live-left b { font-size: 26px; }
  .tm-row { grid-template-columns: 46px 18px minmax(0, 1fr); column-gap: 6px; }
  .tm-time b { font-size: 15px; }
  .tm-card { padding: 12px; }
  .tm-edit { margin-left: 0; }
  .tm-share-row { flex-direction: column; align-items: stretch; }
}
@media print {
  .tm-add, .tm-shift, .tm-share, .tm-pills, .tm-theme, .tm-edit-hint, .tm-roles-panel, .tm-filter, .tm-card-bar { display: none; }
  .tm-head, .tm-live, .tm-row.is-cur .tm-card { background-image: none; box-shadow: none; }
  .tm-row.is-past .tm-card, .tm-row.is-past .tm-time { opacity: 1; }
}

    /* ── Новости CRM: тихая строка по центру шапки (news.js) ─────────
       Не спорит с работой: мелкий приглушённый текст, без заливки,
       точка-маркер в цвете акцента. Только десктоп — на телефоне шапка узкая. */
    .crm-news { display: none; }
    @media (min-width: 769px) {
      .crm-news {
        display: flex; align-items: center; gap: 8px;
        margin-left: auto; min-width: 0; flex: 0 1 auto; max-width: 560px;
        height: 30px; padding: 0 6px 0 12px;
        border: 1px solid transparent; border-radius: var(--r-pill);
        color: var(--muted); font-size: 12.5px; line-height: 1;
        transition: border-color var(--dur-2), background var(--dur-2), opacity .3s;
      }
      .crm-news:hover { border-color: var(--line); background: color-mix(in srgb, var(--panel) 60%, transparent); }
      .crm-news.gone { opacity: 0; }
      .crm-news-dot { width: 6px; height: 6px; border-radius: 50%; flex: none; background: rgb(var(--primary-rgb) / .7); }
      .crm-news-track { flex: 0 1 auto; min-width: 0; overflow: hidden; }
      .crm-news-text {
        display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
        transition: opacity .26s ease, transform .32s cubic-bezier(.22, 1, .36, 1);
      }
      .crm-news-text.out { opacity: 0; transform: translateY(-6px); }
      .crm-news-text.in { opacity: 0; transform: translateY(6px); transition: none; }
      .crm-news-count { flex: none; font-size: 11px; opacity: .6; font-variant-numeric: tabular-nums; }
      .crm-news-count:empty { display: none; }
      .crm-news-close {
        flex: none; width: 22px; height: 22px; padding: 0; display: grid; place-items: center;
        border: 0; border-radius: 50%; background: transparent; color: inherit; cursor: pointer;
        opacity: .55; transition: opacity var(--dur-1), background var(--dur-1);
      }
      .crm-news-close:hover, .crm-news-close:focus-visible { opacity: 1; background: color-mix(in srgb, var(--text) 8%, transparent); }
      .crm-news-close:focus-visible { outline: var(--ring-w) solid var(--ring); outline-offset: 1px; }
    }
    @media (prefers-reduced-motion: reduce) {
      .crm-news, .crm-news-text { transition: none; }
    }
